Добавлен статус заказа и общие действия в декораторах
This commit is contained in:
@@ -45,7 +45,7 @@ class MailOrderBot(ConfigManager):
|
||||
|
||||
|
||||
def execute(self):
|
||||
# Получить список айдишников письма
|
||||
#Получить список айдишников письма
|
||||
|
||||
folder = self.config.get("folder")
|
||||
try:
|
||||
|
||||
@@ -14,10 +14,7 @@ class ExcelFileParcer:
|
||||
|
||||
def _parse_file(self, file_bytes):
|
||||
"""Парсит вложение в формате эл таблиц"""
|
||||
try:
|
||||
df = pd.read_excel(file_bytes, sheet_name=self.sheet_name, header=None)
|
||||
except Exception as e:
|
||||
df = None
|
||||
return df
|
||||
|
||||
def set_value(self, sku, manufacturer, column, value):
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
from .processor import TaskProcessor
|
||||
from .message import LogMessage, LogMessageLevel, LogMessageStorage
|
||||
|
||||
from .abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
@@ -1,11 +1,60 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any
|
||||
import logging
|
||||
import functools
|
||||
|
||||
from mail_order_bot.context import Context
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def handle_errors(func):
|
||||
"""
|
||||
Декоратор для обработки ошибок в методе do класса AbstractTask.
|
||||
Оборачивает выполнение метода в try-except, при ошибке устанавливает статус "error",
|
||||
логирует ошибку и пробрасывает исключение дальше.
|
||||
Применяется везде к методу do.
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, attachment) -> None:
|
||||
file_name = attachment.get("name", "неизвестный файл")
|
||||
|
||||
try:
|
||||
# Выполняем метод do
|
||||
return func(self, attachment)
|
||||
|
||||
except Exception as e:
|
||||
# При ошибке устанавливаем статус и логируем
|
||||
if attachment:
|
||||
attachment["status"] = "error"
|
||||
logger.error(f"Ошибка при обработке файла {file_name} на стадии {self.STEP} \n{e}", exc_info=True)
|
||||
# Пробрасываем исключение дальше
|
||||
# raise
|
||||
return wrapper
|
||||
|
||||
|
||||
def pass_if_error(func):
|
||||
"""
|
||||
Декоратор для проверки статуса attachment перед выполнением метода do.
|
||||
Если статус attachment["status"] != "ok", метод не выполняется.
|
||||
Применяется опционально в конкретных классах, где нужна проверка статуса.
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, attachment) -> None:
|
||||
# Проверяем статус перед выполнением
|
||||
if attachment and attachment.get("status") != "ok":
|
||||
file_name = attachment.get("name", "неизвестный файл")
|
||||
logger.warning(f"Пропускаем шаг для файла {file_name}, статус {attachment.get('status')}")
|
||||
return
|
||||
|
||||
# Выполняем метод do
|
||||
return func(self, attachment)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class AbstractTask():
|
||||
RESULT_SECTION = "section"
|
||||
STEP = "Название шага обработки"
|
||||
|
||||
"""
|
||||
Абстрактный базовый класс для всех хэндлеров.
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
from .abcp.api_get_stock import APIGetStock
|
||||
from .abcp._api_get_stock import APIGetStock
|
||||
from .delivery_time.local_store import DeliveryPeriodLocalStore
|
||||
from .delivery_time.from_config import DeliveryPeriodFromConfig
|
||||
from .notifications.test_notifier import TestNotifier
|
||||
|
||||
@@ -24,7 +24,7 @@ class APIGetStock(AbstractTask):
|
||||
self.client_provider = AbcpProvider(login=client_login, password=client_password)
|
||||
|
||||
def do(self, attachment) -> None:
|
||||
|
||||
#
|
||||
order = attachment.get("order", None)
|
||||
for position in order.positions:
|
||||
# Получаем остатки из-под учетной записи клиента
|
||||
@@ -6,7 +6,7 @@
|
||||
"""
|
||||
import logging
|
||||
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
from mail_order_bot.abcp_api.abcp_provider import AbcpProvider
|
||||
from mail_order_bot.credential_provider import CredentialProvider
|
||||
|
||||
@@ -14,14 +14,16 @@ from mail_order_bot.telegram.client import TelegramClient
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SaveOrderToTelegram(AbstractTask):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@pass_if_error
|
||||
@handle_errors
|
||||
def do(self, attachment) -> None:
|
||||
client = TelegramClient()
|
||||
|
||||
try:
|
||||
order = attachment["order"]
|
||||
positions = order.positions
|
||||
message = "\nОбработка заказа {указать название контрагента}\n"
|
||||
@@ -51,9 +53,7 @@ class SaveOrderToTelegram(AbstractTask):
|
||||
document=file,
|
||||
filename="document.xlsx"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Ошибка при отправке инфо по заказу в телеграм")
|
||||
else:
|
||||
|
||||
logger.warning("Инфо по заказу отправлено в телеграм")
|
||||
|
||||
#===============================
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -13,17 +13,9 @@ class DeliveryPeriodFromConfig(AbstractTask):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@pass_if_error
|
||||
@handle_errors
|
||||
def do(self, attachment) -> None:
|
||||
try:
|
||||
delivery_period = self.config.get("delivery_period")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при получении срока доставки из конфига: {e}")
|
||||
|
||||
else:
|
||||
attachment["delivery_period"] = delivery_period
|
||||
logger.warning(f"Срок доставки установлен из конфига - {delivery_period} (ч.)")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Парсер срока доставки из темы письма
|
||||
"""
|
||||
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"""
|
||||
import logging
|
||||
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
from mail_order_bot.email_client.utils import EmailUtils
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from email.utils import formatdate
|
||||
from email import encoders
|
||||
|
||||
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -19,9 +19,10 @@ class EmailReplyTask(AbstractTask):
|
||||
"""Формирует ответ на входящее письмо с запросом на заказ°"""
|
||||
EMAIl = "zosimovaa@yandex.ru" #"noreply@zapchastiya.ru"
|
||||
|
||||
@pass_if_error
|
||||
@handle_errors
|
||||
def do(self, attachment):
|
||||
|
||||
try:
|
||||
email = self.context.data.get("email")
|
||||
|
||||
if not email:
|
||||
@@ -49,11 +50,10 @@ class EmailReplyTask(AbstractTask):
|
||||
self._attach_file(reply_message, attachment)
|
||||
|
||||
self.context.email_client.send_email(reply_message)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при отправке ответа по заказу на email \n{e}")
|
||||
else:
|
||||
|
||||
logger.warning(f"Сформирован ответ на заказ на email")
|
||||
|
||||
|
||||
def _attach_file(self, reply_message, attachment):
|
||||
"""
|
||||
Args:
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import logging
|
||||
import pandas as pd
|
||||
from io import BytesIO
|
||||
|
||||
from mail_order_bot.email_client import EmailUtils
|
||||
#from mail_order_bot.task_processor.handlers.order_position import OrderPosition
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
|
||||
from mail_order_bot.parsers.excel_parcer import ExcelFileParcer
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
from ....parsers.excel_parcer import ExcelFileParcer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -19,20 +14,10 @@ class ExcelExtractor(AbstractTask):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.excel_config = self.config.get("excel", {})
|
||||
|
||||
@pass_if_error
|
||||
@handle_errors
|
||||
def do(self, attachment) -> None:
|
||||
try:
|
||||
file_bytes = BytesIO(attachment['bytes'])
|
||||
excel_file = ExcelFileParcer(file_bytes, self.excel_config)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Не удалось распарсить файл: \n{e}")
|
||||
attachment["excel"] = None
|
||||
|
||||
else:
|
||||
attachment["excel"] = excel_file
|
||||
logger.warning(f"Произведен успешный парсинг файла")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
logger.warning(f"Произведен успешный парсинг файла {attachment.get('name', 'неизвестный файл')}")
|
||||
|
||||
@@ -2,7 +2,7 @@ import logging
|
||||
import pandas as pd
|
||||
from io import BytesIO
|
||||
from mail_order_bot.parsers.order_parcer import OrderParser
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
|
||||
from mail_order_bot.parsers.excel_parcer import ExcelFileParcer
|
||||
|
||||
@@ -10,6 +10,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OrderExtractor(AbstractTask):
|
||||
STEP = "Извлечение заказа"
|
||||
"""
|
||||
Хендлер для каждого вложения считывает эксель файл и сохраняет его контекст
|
||||
"""
|
||||
@@ -17,11 +18,10 @@ class OrderExtractor(AbstractTask):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.excel_config = self.config.get("excel", {})
|
||||
|
||||
|
||||
@pass_if_error
|
||||
@handle_errors
|
||||
def do(self, attachment) -> None:
|
||||
# todo сделать проверку на наличие файла и его тип
|
||||
|
||||
try:
|
||||
delivery_period = attachment.get("delivery_period", 0)
|
||||
mapping = self.excel_config.get("mapping")
|
||||
|
||||
@@ -33,12 +33,9 @@ class OrderExtractor(AbstractTask):
|
||||
order_dataframe = excel_file.get_order_rows()
|
||||
order = order_parcer.parse(order_dataframe)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при парсинге заказа файла: \n{e}")
|
||||
|
||||
else:
|
||||
attachment["order"] = order
|
||||
logger.warning(f"Обработан файл с заказом, извлечено позиций, {len(order.positions)}")
|
||||
|
||||
logger.warning(f"Файл заказа обработан успешно, извлечено {len(order.positions)} позиций")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import logging
|
||||
import pandas as pd
|
||||
from io import BytesIO
|
||||
# from mail_order_bot.task_processor.handlers.order_position import OrderPosition
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
|
||||
|
||||
from mail_order_bot.order.auto_part_position import PositionStatus
|
||||
from mail_order_bot.parsers.excel_parcer import ExcelFileParcer
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,10 +13,10 @@ class UpdateExcelFile(AbstractTask):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.excel_config = self.config.get("excel", {})
|
||||
|
||||
@pass_if_error
|
||||
@handle_errors
|
||||
def do(self, attachment) -> None:
|
||||
# todo сделать проверку на наличие файла и его тип
|
||||
|
||||
try:
|
||||
excel_file = attachment.get("excel")
|
||||
order = attachment.get("order")
|
||||
config = self.context.data.get("config", {})
|
||||
@@ -46,9 +39,5 @@ class UpdateExcelFile(AbstractTask):
|
||||
column = value
|
||||
value = position.order_price
|
||||
excel_file.set_value(sku, manufacturer, column, value)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при правке excel файла: \n{e}")
|
||||
|
||||
else:
|
||||
logger.warning(f"Файл excel успешно обновлен")
|
||||
|
||||
logger.warning(f"Файла {attachment.get('name', 'неизвестный файл')} отредактирован")
|
||||
|
||||
@@ -10,7 +10,7 @@ from mail_order_bot.order.auto_part_position import AutoPartPosition, PositionSt
|
||||
from mail_order_bot.parsers.excel_parcer import ExcelFileParcer
|
||||
from decimal import Decimal
|
||||
|
||||
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||
from mail_order_bot.abcp_api.abcp_provider import AbcpProvider
|
||||
from mail_order_bot.credential_provider import CredentialProvider
|
||||
from mail_order_bot.order.auto_part_order import OrderStatus
|
||||
@@ -35,10 +35,10 @@ class StockSelector(AbstractTask):
|
||||
client_login, client_password = credential_provider.get_system_credentials()
|
||||
self.client_provider = AbcpProvider(login=client_login, password=client_password)
|
||||
|
||||
@pass_if_error
|
||||
@handle_errors
|
||||
def do(self, attachment) -> None:
|
||||
# todo сделать проверку на наличие файла и его тип
|
||||
|
||||
try:
|
||||
order = attachment.get("order", None)
|
||||
delivery_period = attachment.get("delivery_period")
|
||||
for position in order.positions:
|
||||
@@ -63,14 +63,12 @@ class StockSelector(AbstractTask):
|
||||
# Мне не очень нравится управление статусами в этом месте, кажется что лучше это делать внутри AutoPartPosition
|
||||
else:
|
||||
position.status = PositionStatus.STOCK_FAILED
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при выборе позиции со складов: {e}")
|
||||
|
||||
else:
|
||||
logger.warning("Определены оптимальные позиции со складов")
|
||||
|
||||
|
||||
|
||||
|
||||
def get_optimal_stock(self, stock_list, asking_price, asking_quantity, delivery_period):
|
||||
"""Выбирает позицию для заказа"""
|
||||
|
||||
|
||||
@@ -90,7 +90,8 @@ class TaskProcessor:
|
||||
file_name = attachment["name"]
|
||||
logger.warning(f"Начата обработка файла: {file_name} =>")
|
||||
|
||||
attachment["log_messages"] = LogMessageStorage(file_name)
|
||||
#attachment["log_messages"] = LogMessageStorage(file_name)
|
||||
attachment["status"] = "ok"
|
||||
|
||||
# Запустить обработку пайплайна
|
||||
for handler_name in pipeline:
|
||||
|
||||
Reference in New Issue
Block a user