Продолжаем выстраивать архитектуру приложения
This commit is contained in:
@@ -1,60 +1,4 @@
|
||||
# Настройки обработки =================================================================
|
||||
suppliers:
|
||||
# Контрагент A - стандартный формат
|
||||
autostels:
|
||||
sheet_name: "Лист1" # Название листа Excel
|
||||
header_row: 2 # Номер строки с заголовками (0 = первая)
|
||||
|
||||
# Маппинг: внутреннее_поле -> название_колонки_в_Excel
|
||||
mapping:
|
||||
article: "№ Детали"
|
||||
manufacturer: "Производитель"
|
||||
name: "Наименование"
|
||||
price: "Прайс"
|
||||
quantity: "Количество"
|
||||
total: "Сумма"
|
||||
|
||||
# Дополнительные настройки (опционально)
|
||||
options:
|
||||
decimal_separator: ","
|
||||
encoding: "utf-8"
|
||||
|
||||
# Контрагент B - формат с английскими названиями
|
||||
parterra:
|
||||
sheet_name: "TDSheet"
|
||||
header_row: 6 # Заголовки во второй строке
|
||||
|
||||
mapping:
|
||||
article: "Артикул поставщика"
|
||||
manufacturer: "Производитель Поставщика"
|
||||
name: "Номенклатура"
|
||||
price: "Цена"
|
||||
quantity: "Количество (в единицах хранения)"
|
||||
total: "Сумма с НДС"
|
||||
|
||||
options:
|
||||
decimal_separator: ","
|
||||
encoding: "utf-8"
|
||||
#thousand_separator: ","
|
||||
|
||||
# Контрагент C - с запятой как разделителем
|
||||
part-kom:
|
||||
sheet_name: "Лист_1" # Можно указать индекс листа
|
||||
header_row: 5
|
||||
|
||||
mapping:
|
||||
article: "Артикул"
|
||||
manufacturer: "Изготовитель"
|
||||
name: "Наименование товара"
|
||||
price: "Цена"
|
||||
quantity: "Кол-во"
|
||||
total: "Сумма"
|
||||
|
||||
options:
|
||||
#skip_footer_rows: 3
|
||||
decimal_separator: ","
|
||||
|
||||
|
||||
|
||||
# Раздел с общими конфигурационными параметрами ===============================
|
||||
update_interval: 10
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
clients:
|
||||
"todx.ru":
|
||||
- handler: "OrderParser",
|
||||
config:
|
||||
sheet_name: "Лист1"
|
||||
key_value: "Артикул"
|
||||
mapping:
|
||||
name: "Наименование"
|
||||
manufacturer: "Производитель"
|
||||
price: "Цена\nдетали"
|
||||
quantity: "Кол-\nво"
|
||||
total: "Сумма"
|
||||
- handler: "OrderCreator"
|
||||
- handler: "ExcelWriter"
|
||||
config: "output.xlsx"
|
||||
- handler: "EmailSender"
|
||||
config:
|
||||
to: "todx@yandex.ru"
|
||||
- Notifier:
|
||||
- channel: "email"
|
||||
to : "status@zapchastiya.ru"
|
||||
- channel: "telegram"
|
||||
to: "123454323"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from email.message import EmailMessage
|
||||
from typing import List
|
||||
|
||||
|
||||
@@ -13,6 +14,8 @@ class EmailAttachment:
|
||||
@dataclass
|
||||
class EmailMessage:
|
||||
"""Класс для представления электронного письма"""
|
||||
message: EmailMessage
|
||||
attachments: List[EmailAttachment]
|
||||
from_addr: str
|
||||
from_email: str
|
||||
subj: str
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
suppliers:
|
||||
# order@stparts.ru
|
||||
"order@stparts.ru":
|
||||
sheet_name: "TDSheet" # Название листа Excel
|
||||
header_row: 0 # Номер строки с заголовками (0 = первая)
|
||||
|
||||
# Маппинг: внутреннее_поле -> название_колонки_в_Excel
|
||||
mapping:
|
||||
article: "Номер"
|
||||
manufacturer: "Бренд"
|
||||
name: "Описание"
|
||||
price: "Цена"
|
||||
quantity: "Количество"
|
||||
#total: "Сумма"
|
||||
#Вопросы: что за поле "Фактическая_отгрузка"?
|
||||
|
||||
# Дополнительные настройки (опционально)
|
||||
options:
|
||||
decimal_separator: ","
|
||||
encoding: "utf-8"
|
||||
|
||||
# Рай Авто СПб
|
||||
EMPTY-FROM:
|
||||
sheet_name: 0
|
||||
header_row: 2 # Заголовки во второй строке
|
||||
|
||||
mapping:
|
||||
article: "Артикул"
|
||||
manufacturer: "Производитель"
|
||||
name: "Название"
|
||||
price: "Цена"
|
||||
quantity: "Количество"
|
||||
#total: "Сумма с НДС"
|
||||
|
||||
options:
|
||||
decimal_separator: ","
|
||||
encoding: "utf-8"
|
||||
#thousand_separator: ","
|
||||
# Примечание: гемор - нет имейла
|
||||
|
||||
# АвтоТО
|
||||
"order@avtoto.ru":
|
||||
sheet_name: "Заказы" # Можно указать индекс листа
|
||||
header_row: 4
|
||||
|
||||
mapping:
|
||||
article: "Артикул"
|
||||
manufacturer: "Изготовитель"
|
||||
name: "Наименование товара"
|
||||
price: "Цена"
|
||||
quantity: "Кол-во"
|
||||
total: "Сумма"
|
||||
|
||||
options:
|
||||
#skip_footer_rows: 3
|
||||
decimal_separator: ","
|
||||
@@ -1,25 +0,0 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Any
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
@dataclass
|
||||
class OrderPosition:
|
||||
"""
|
||||
Унифицированная модель позиции для заказа.
|
||||
Все контрагенты приводятся к этой структуре.
|
||||
"""
|
||||
article: str # Артикул товара
|
||||
manufacturer: str # Производитель
|
||||
name: str # Наименование
|
||||
price: Decimal # Цена за единицу
|
||||
quantity: int # Количество
|
||||
total: Decimal # Общая сумма
|
||||
additional_attrs: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def __post_init__(self):
|
||||
"""Валидация после инициализации"""
|
||||
if self.quantity < 0:
|
||||
raise ValueError(f"Количество не может быть отрицательным: {self.quantity}")
|
||||
if self.price < 0:
|
||||
raise ValueError(f"Цена не может быть отрицательной: {self.price}")
|
||||
6
src/mail_order_bot/task_handler/handlers/__init__.py
Normal file
6
src/mail_order_bot/task_handler/handlers/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from .abcp_clients.check_avaiability import CheckAvailabilityTest
|
||||
from .abcp_clients.order_creator import InstantOrderTest
|
||||
|
||||
from .excel_parcers.basic_excel_parcer import BasicExcelParser
|
||||
|
||||
from .notifications.test_notifier import TestNotifier
|
||||
@@ -0,0 +1,25 @@
|
||||
import random
|
||||
import logging
|
||||
from mail_order_bot.task_handler.handlers.abstract_task import AbstractTask
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_stock(brand, part_number):
|
||||
return random.randint(0, 10)
|
||||
|
||||
class CheckAvailabilityTest(AbstractTask):
|
||||
|
||||
def do(self) -> None:
|
||||
positions = self.order.positions
|
||||
for position in positions:
|
||||
self._update_stock(position)
|
||||
|
||||
def _update_stock(self, position):
|
||||
# Эмулируем получение данных
|
||||
stock = random.randint(0, 10)
|
||||
price = position.price
|
||||
|
||||
position.stock_price = price
|
||||
position.stock_remaining = stock
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
import requests
|
||||
from ..abstract_task import AbstractTask
|
||||
from mail_order_bot.task_handler.handlers.abstract_task import AbstractTask
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -9,7 +9,7 @@ class InstantOrderTest(AbstractTask):
|
||||
|
||||
def do(self) -> None:
|
||||
|
||||
positions = self.context["positions"]
|
||||
positions = self.order.positions
|
||||
|
||||
message = f"Запрос на создание заказа от {self.context['client']}:\n"
|
||||
message += "\n".join(f"{pos.article}: {pos.name} ({pos.quantity} x {pos.price} = {pos.total})" for pos in positions)
|
||||
@@ -1,15 +1,17 @@
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any
|
||||
|
||||
from ..order.auto_part_order import AutoPartOrder
|
||||
|
||||
class AbstractTask(ABC):
|
||||
RESULT_SECTION = "section"
|
||||
"""
|
||||
Абстрактный базовый класс для всех хэндлеров.
|
||||
"""
|
||||
def __init__(self, config: Dict[str, Any], context: Dict[str, Any],*args, **kwargs) -> None:
|
||||
def __init__(self, config: Dict[str, Any], context: Dict[str, Any], order: AutoPartOrder, *args, **kwargs) -> None:
|
||||
self.config = config
|
||||
self.context = context
|
||||
self.order = order
|
||||
|
||||
@abstractmethod
|
||||
def do(self) -> None:
|
||||
141
src/mail_order_bot/task_handler/handlers/email/send_email.py
Normal file
141
src/mail_order_bot/task_handler/handlers/email/send_email.py
Normal file
@@ -0,0 +1,141 @@
|
||||
|
||||
import smtplib
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.base import MIMEBase
|
||||
from email.utils import formatdate
|
||||
from email import encoders
|
||||
from abc import ABC, abstractmethod
|
||||
import os
|
||||
|
||||
|
||||
class AbstractTask(ABC):
|
||||
"""Базовый класс для задач"""
|
||||
|
||||
def __init__(self, context, config):
|
||||
self.context = context
|
||||
self.config = config
|
||||
|
||||
@abstractmethod
|
||||
def do(self):
|
||||
"""Метод для реализации в подклассах"""
|
||||
pass
|
||||
|
||||
|
||||
class EmailReplyTask(AbstractTask):
|
||||
"""Класс для ответа на электронные письма"""
|
||||
|
||||
def do(self):
|
||||
"""
|
||||
Отправляет ответ на входящее письмо
|
||||
|
||||
Ожидает в self.context:
|
||||
- message: email.message.Message объект входящего письма
|
||||
- attachment: путь к файлу для вложения
|
||||
|
||||
Ожидает в self.config:
|
||||
- reply_to: адрес электронной почты для копии
|
||||
- smtp_host: хост SMTP сервера
|
||||
- smtp_port: порт SMTP сервера
|
||||
- smtp_user: пользователь SMTP
|
||||
- smtp_password: пароль SMTP
|
||||
- from_email: адрес отправителя
|
||||
"""
|
||||
|
||||
incoming_message = self.context.get("message")
|
||||
attachment_path = self.context.get("attacnment")
|
||||
|
||||
if not incoming_message:
|
||||
raise ValueError("В context не найдено письмо (message)")
|
||||
|
||||
# Получаем адрес отправителя входящего письма
|
||||
from_addr = incoming_message.get("From")
|
||||
if not from_addr:
|
||||
raise ValueError("Входящее письмо не содержит адреса отправителя")
|
||||
|
||||
# Создаем ответное письмо
|
||||
reply_message = MIMEMultipart()
|
||||
|
||||
# Заголовки ответного письма
|
||||
reply_message["From"] = self.config.get("from_email", "noreply@example.com")
|
||||
reply_message["To"] = from_addr
|
||||
reply_message["Cc"] = self.config.get("reply_to", "")
|
||||
reply_message["Subject"] = f"Re: {incoming_message.get('Subject', '')}"
|
||||
reply_message["Date"] = formatdate(localtime=True)
|
||||
|
||||
# Тело письма
|
||||
body = "Ваш заказ создан"
|
||||
reply_message.attach(MIMEText(body, "plain", "utf-8"))
|
||||
|
||||
# Добавляем вложение если указан путь к файлу
|
||||
if attachment_path and os.path.isfile(attachment_path):
|
||||
self._attach_file(reply_message, attachment_path)
|
||||
|
||||
# Отправляем письмо
|
||||
self._send_email(reply_message, from_addr)
|
||||
|
||||
def _attach_file(self, message, file_path):
|
||||
"""
|
||||
Добавляет файл в качестве вложения к письму
|
||||
|
||||
Args:
|
||||
message: MIMEMultipart объект
|
||||
file_path: путь к файлу для вложения
|
||||
"""
|
||||
try:
|
||||
with open(file_path, "rb") as attachment:
|
||||
part = MIMEBase("application", "octet-stream")
|
||||
part.set_payload(attachment.read())
|
||||
|
||||
encoders.encode_base64(part)
|
||||
|
||||
file_name = os.path.basename(file_path)
|
||||
part.add_header(
|
||||
"Content-Disposition",
|
||||
f"attachment; filename= {file_name}"
|
||||
)
|
||||
|
||||
message.attach(part)
|
||||
except FileNotFoundError:
|
||||
raise FileNotFoundError(f"Файл не найден: {file_path}")
|
||||
except Exception as e:
|
||||
raise Exception(f"Ошибка при добавлении вложения: {str(e)}")
|
||||
|
||||
def _send_email(self, message, recipient):
|
||||
"""
|
||||
Отправляет письмо через SMTP
|
||||
|
||||
Args:
|
||||
message: MIMEMultipart объект письма
|
||||
recipient: адрес получателя
|
||||
"""
|
||||
try:
|
||||
smtp_host = self.config.get("smtp_host")
|
||||
smtp_port = self.config.get("smtp_port", 587)
|
||||
smtp_user = self.config.get("smtp_user")
|
||||
smtp_password = self.config.get("smtp_password")
|
||||
|
||||
if not all([smtp_host, smtp_user, smtp_password]):
|
||||
raise ValueError("Не указаны параметры SMTP в config")
|
||||
|
||||
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||
server.starttls()
|
||||
server.login(smtp_user, smtp_password)
|
||||
|
||||
# Получаем адреса получателей (основной + копия)
|
||||
recipients = [recipient]
|
||||
reply_to = self.config.get("reply_to")
|
||||
if reply_to:
|
||||
recipients.append(reply_to)
|
||||
|
||||
server.sendmail(
|
||||
self.config.get("from_email"),
|
||||
recipients,
|
||||
message.as_string()
|
||||
)
|
||||
except smtplib.SMTPException as e:
|
||||
raise Exception(f"Ошибка SMTP: {str(e)}")
|
||||
except Exception as e:
|
||||
raise Exception(f"Ошибка при отправке письма: {str(e)}")
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import logging
|
||||
import pandas as pd
|
||||
from typing import Dict, Any, Optional, List
|
||||
from typing import Dict, Any, Optional
|
||||
from decimal import Decimal
|
||||
from io import BytesIO
|
||||
from .order_position import OrderPosition
|
||||
from ..abstract_task import AbstractTask
|
||||
#from mail_order_bot.task_handler.handlers.order_position import OrderPosition
|
||||
from mail_order_bot.task_handler.handlers.abstract_task import AbstractTask
|
||||
|
||||
from ...order.auto_part_position import AutoPartPosition
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -19,7 +21,7 @@ class BasicExcelParser(AbstractTask):
|
||||
def do(self) -> None:
|
||||
|
||||
# todo сделать проверку на наличие файла и его тип
|
||||
file_bytes = BytesIO(self.context.get("attachment")) # self.context.get("attachment") #
|
||||
file_bytes = BytesIO(self.context.get("attachment").content) # self.context.get("attachment") #
|
||||
try:
|
||||
df = self._make_dataframe(file_bytes)
|
||||
# Получаем маппинг колонок из конфигурации
|
||||
@@ -32,6 +34,7 @@ class BasicExcelParser(AbstractTask):
|
||||
position = self._parse_row(row, mapping)
|
||||
if position:
|
||||
positions.append(position)
|
||||
self.order.add_position(position)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка парсинга строки {idx}: {e}, {row}")
|
||||
continue
|
||||
@@ -40,11 +43,12 @@ class BasicExcelParser(AbstractTask):
|
||||
|
||||
self.context[self.RESULT_SECTION] = positions
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при обработке файла: {e}")
|
||||
raise Exception from e
|
||||
|
||||
def _parse_row(self, row: pd.Series, mapping: Dict[str, str]) -> Optional[OrderPosition]:
|
||||
def _parse_row(self, row: pd.Series, mapping: Dict[str, str]) -> Optional[AutoPartPosition]:
|
||||
"""Парсит одну строку Excel в OrderPosition"""
|
||||
|
||||
# Проверяем обязательные поля
|
||||
@@ -69,7 +73,7 @@ class BasicExcelParser(AbstractTask):
|
||||
name = ""
|
||||
|
||||
# Создаем объект позиции
|
||||
position = OrderPosition(
|
||||
position = AutoPartPosition(
|
||||
article=str(row[mapping['article']]).strip(),
|
||||
manufacturer=str(row[mapping.get('manufacturer', "")]).strip(),
|
||||
name=name,
|
||||
@@ -1,9 +1,6 @@
|
||||
import logging
|
||||
import pandas as pd
|
||||
from typing import Dict, Any, Optional, List
|
||||
from decimal import Decimal
|
||||
|
||||
from ..abstract_task import AbstractTask
|
||||
from mail_order_bot.task_handler.handlers.abstract_task import AbstractTask
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
from enum import Enum
|
||||
|
||||
class OrderStatus(Enum):
|
||||
NEW = 1
|
||||
IN_PROGRESS = 2
|
||||
COMPLETED = 3
|
||||
FAILED = 4
|
||||
OPERATOR_HANDLING = 5
|
||||
INVALID = 6
|
||||
|
||||
|
||||
class Order:
|
||||
def __init__(self, context: dict):
|
||||
attachment = context["attachment"]
|
||||
self.context = context
|
||||
24
src/mail_order_bot/task_handler/order/auto_part_order.py
Normal file
24
src/mail_order_bot/task_handler/order/auto_part_order.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from typing import List, Optional
|
||||
from .auto_part_position import AutoPartPosition
|
||||
from .ordet_status import OrderStatus
|
||||
|
||||
class AutoPartOrder:
|
||||
def __init__(self):
|
||||
self.positions: List[AutoPartPosition] = []
|
||||
self.status = OrderStatus.NEW
|
||||
|
||||
def add_position(self, position: AutoPartPosition) -> None:
|
||||
self.positions.append(position)
|
||||
if self.status == OrderStatus.NEW:
|
||||
self.status = OrderStatus.IN_PROGRESS
|
||||
|
||||
def find_positions(self, brand: Optional[str] = None, sku: Optional[str] = None) -> List[AutoPartPosition]:
|
||||
results = self.positions
|
||||
if brand is not None:
|
||||
results = [p for p in results if p.brand == brand]
|
||||
if sku is not None:
|
||||
results = [p for p in results if p.sku == sku]
|
||||
return results
|
||||
|
||||
def __len__(self):
|
||||
return len(self.positions)
|
||||
76
src/mail_order_bot/task_handler/order/auto_part_position.py
Normal file
76
src/mail_order_bot/task_handler/order/auto_part_position.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from typing import List, Optional
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Any
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
@dataclass
|
||||
class AutoPartPosition:
|
||||
"""
|
||||
Унифицированная модель позиции для заказа.
|
||||
Все контрагенты приводятся к этой структуре.
|
||||
"""
|
||||
article: str # Артикул товара
|
||||
manufacturer: str # Производитель
|
||||
name: str # Наименование
|
||||
price: Decimal # Цена за единицу
|
||||
quantity: int # Количество
|
||||
total: Decimal # Общая сумма
|
||||
stock_remaining: int = 0 # Остаток на складе
|
||||
stock_price: Decimal = Decimal('0.0') # Цена на складе
|
||||
additional_attrs: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def __post_init__(self):
|
||||
"""Валидация после инициализации"""
|
||||
if self.quantity < 0:
|
||||
raise ValueError(f"Количество не может быть отрицательным: {self.quantity}")
|
||||
if self.price < 0:
|
||||
raise ValueError(f"Цена не может быть отрицательной: {self.price}")
|
||||
|
||||
|
||||
|
||||
|
||||
class AutoPartPosition2:
|
||||
brand: str
|
||||
sku: str
|
||||
name: str
|
||||
customer_price: float
|
||||
customer_quantity: int
|
||||
supplier_price: float
|
||||
stock_remaining: int
|
||||
|
||||
def __init__(self, brand: str, sku: str, name: str,
|
||||
customer_price: float, customer_quantity: int,
|
||||
supplier_price: float, stock_remaining: int):
|
||||
self.brand = brand
|
||||
self.sku = sku
|
||||
self.name = name
|
||||
self.customer_price = customer_price
|
||||
self.customer_quantity = customer_quantity
|
||||
self.supplier_price = supplier_price
|
||||
self.stock_remaining = stock_remaining
|
||||
|
||||
def customer_cost(self) -> float:
|
||||
return self.customer_price * self.customer_quantity
|
||||
|
||||
def supplier_cost(self) -> float:
|
||||
return self.supplier_price * self.customer_quantity
|
||||
|
||||
def is_available(self) -> bool:
|
||||
return self.stock_remaining >= self.customer_quantity
|
||||
|
||||
def restock(self, amount: int) -> None:
|
||||
if amount < 0:
|
||||
raise ValueError("Restock amount must be non-negative")
|
||||
self.stock_remaining += amount
|
||||
|
||||
def __post_init__(self):
|
||||
if self.customer_price < 0:
|
||||
raise ValueError("Customer price cannot be negative")
|
||||
if self.customer_quantity < 0:
|
||||
raise ValueError("Customer quantity cannot be negative")
|
||||
if self.supplier_price < 0:
|
||||
raise ValueError("Supplier price cannot be negative")
|
||||
if self.stock_remaining < 0:
|
||||
raise ValueError("Stock remaining cannot be negative")
|
||||
|
||||
10
src/mail_order_bot/task_handler/order/ordet_status.py
Normal file
10
src/mail_order_bot/task_handler/order/ordet_status.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class OrderStatus(Enum):
|
||||
NEW = 1
|
||||
IN_PROGRESS = 2
|
||||
FAILED = 4
|
||||
COMPLETED = 3
|
||||
OPERATOR_REQUIRED = 5
|
||||
INVALID = 6
|
||||
@@ -1,33 +1,34 @@
|
||||
from pathlib import Path
|
||||
import os
|
||||
import yaml
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
from pathlib import Path
|
||||
|
||||
from ..task_handler.excel_parsers.basic_excel_parcer import BasicExcelParser
|
||||
from ..task_handler.notifiers.test_notifier import TestNotifier
|
||||
from ..task_handler.abcp_client.OrderCreator import InstantOrderTest
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from .order.auto_part_order import AutoPartOrder
|
||||
from .handlers import *
|
||||
|
||||
class TaskProcessor:
|
||||
def __init__(self, config_path: Path):
|
||||
self.config_path = config_path
|
||||
self.context = dict()
|
||||
self.order = None
|
||||
|
||||
def process(self, client, attachment):
|
||||
config = self._load_config(client)
|
||||
self.context = dict()
|
||||
self.order = AutoPartOrder()
|
||||
|
||||
self.context["client"] = client
|
||||
self.context["attachment"] = attachment.content
|
||||
self.context["attachment"] = attachment
|
||||
|
||||
self.context["status"] = client
|
||||
|
||||
for stage in config["pipeline"]:
|
||||
handler_name = stage["handler"]
|
||||
logger.info(f"Processing handler: {handler_name}")
|
||||
task = globals()[handler_name](stage.get("config", None), self.context)
|
||||
task = globals()[handler_name](stage.get("config", None), self.context, self.order)
|
||||
task.do()
|
||||
|
||||
return self.context
|
||||
|
||||
Reference in New Issue
Block a user