Add Polaris FastAPI backend: models/schemas, pricing+scoring+order router, importer, Celery tasks, API routers, seed script
This commit is contained in:
65
backend/app/engines/suppliers/base.py
Normal file
65
backend/app/engines/suppliers/base.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""SupplierAdapter abstract base class + registry."""
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
# Registry of adapter_type string -> adapter class. Populated by register_adapter.
|
||||
ADAPTERS = {}
|
||||
|
||||
|
||||
class SupplierAdapter(ABC):
|
||||
"""Normalized interface every supplier feed must implement.
|
||||
|
||||
Product dict shape (used by the importer):
|
||||
{sku, title, description, category, brand, cost, inventory,
|
||||
shipping_cost, shipping_time, image_url}
|
||||
"""
|
||||
|
||||
adapter_type = "base"
|
||||
|
||||
def __init__(self, supplier, data_or_url=""):
|
||||
self.supplier = supplier
|
||||
self.data_or_url = data_or_url
|
||||
|
||||
@abstractmethod
|
||||
def get_products(self):
|
||||
"""Return list of normalized product dicts."""
|
||||
|
||||
@abstractmethod
|
||||
def get_inventory(self):
|
||||
"""Return list of {'supplier_sku': str, 'inventory': int}."""
|
||||
|
||||
@abstractmethod
|
||||
def get_price(self, sku):
|
||||
"""Return current supplier cost for sku (number) or None."""
|
||||
|
||||
@abstractmethod
|
||||
def create_order(self, items):
|
||||
"""Place an order at the supplier; return {'ref': str, ...}."""
|
||||
|
||||
@abstractmethod
|
||||
def get_order_status(self, ref):
|
||||
"""Return status string for a supplier order ref."""
|
||||
|
||||
@abstractmethod
|
||||
def get_tracking(self, ref):
|
||||
"""Return {'tracking': str, 'carrier': str} or None."""
|
||||
|
||||
@abstractmethod
|
||||
def cancel_order(self, ref):
|
||||
"""Cancel a supplier order; return status dict."""
|
||||
|
||||
|
||||
def register_adapter(cls):
|
||||
ADAPTERS[cls.adapter_type] = cls
|
||||
return cls
|
||||
|
||||
|
||||
def build_adapter(supplier, data_or_url=""):
|
||||
cls = ADAPTERS.get(supplier.adapter_type)
|
||||
if cls is None:
|
||||
# lazy-import concrete adapters so their @register_adapter side effects run
|
||||
from app.engines.suppliers import csv_adapter, sample # noqa: F401
|
||||
|
||||
cls = ADAPTERS.get(supplier.adapter_type)
|
||||
if cls is None:
|
||||
raise ValueError(f"unknown adapter_type '{supplier.adapter_type}' for supplier {supplier.name}")
|
||||
return cls(supplier, data_or_url=data_or_url)
|
||||
Reference in New Issue
Block a user