Files
Autoparts-DB/pos/services/webhook_service.py
consultoria-as ea29cc31c0 feat(catalog): supplier catalog cleanup, fuzzy matching, and navigation fixes
- Cleaned 137+ fake engine-displacement models from supplier imports
  (v3/v4 scripts: Chevrolet, Ford, Chrysler, Dodge, Jeep, Nissan, etc.)
- Removed 1,251+ corrupted models (INT. prefixes, year-suffix, torque specs,
  empty names, trailing-year variants)
- Migrated supplier tables to master DB (supplier_catalog,
  supplier_catalog_compat, supplier_catalog_interchange)
- Fixed _get_mye_ids_with_parts() to query supplier_catalog_compat from
  master DB so supplier-only vehicles appear for all tenants
- Added fuzzy model matcher with parenthesis stripping, noise suffix removal,
  compact matching, prefix/substring fallback, model aliases, and ±3 year
  proximity
- Matched compat rows: KEEP GREEN +14,152, KNADIAN +3,021, VAZLO +127,500,
  LUK +477, RAYBESTOS +1,743
- Added KNADIAN catalog importer with year-range expansion and future-year
  filtering
- Added VAZLO catalog importer with position parsing and SKU-in-model cleanup
- Added Keep Green, LUK, Yokomitsu, Raybestos catalog importers
- Cache clearing after cleanups (_classify_cache_*, nexus:mye_ids:*,
  nexus:brand_mye_counts:*)

Final match rates:
- KEEP GREEN: 90.3%
- VAZLO: 93.6%
- YOKOMITSU: 100.0%
- KNADIAN: 57.4%
- LUK: 51.0%
- RAYBESTOS: 55.9%
2026-06-09 07:47:42 +00:00

66 lines
2.2 KiB
Python

"""Webhook dispatch service for dropshipping and external integrations.
Sends POST requests to configured target URLs with retry logic.
Can be called synchronously or enqueued via Celery.
"""
import json
import logging
import requests
import threading
from typing import Optional
logger = logging.getLogger(__name__)
def _send_post(url: str, payload: dict, headers: Optional[dict] = None, timeout: int = 10):
"""Send a POST request and return (success, status_code, response_text)."""
default_headers = {"Content-Type": "application/json"}
if headers:
default_headers.update(headers)
try:
resp = requests.post(url, json=payload, headers=default_headers, timeout=timeout)
success = 200 <= resp.status_code < 300
if not success:
logger.warning("Webhook %s returned %s: %s", url, resp.status_code, resp.text[:200])
return success, resp.status_code, resp.text
except requests.exceptions.Timeout:
logger.warning("Webhook %s timed out after %ss", url, timeout)
return False, 0, "timeout"
except Exception as e:
logger.warning("Webhook %s failed: %s", url, e)
return False, 0, str(e)
def dispatch_webhook_sync(target_url: str, event_type: str, payload: dict, secret: Optional[str] = None):
"""Send webhook synchronously (use inside Celery tasks for async)."""
full_payload = {
"event": event_type,
"data": payload,
}
headers = {}
if secret:
headers["X-Webhook-Secret"] = secret
success, status, body = _send_post(target_url, full_payload, headers=headers)
return {"success": success, "status": status, "body": body[:500]}
def dispatch_webhooks_bulk(target_urls: list[str], event_type: str, payload: dict, secret: Optional[str] = None):
"""Dispatch to multiple URLs concurrently using threads."""
results = []
threads = []
def _send(url):
result = dispatch_webhook_sync(url, event_type, payload, secret=secret)
results.append({"url": url, **result})
for url in target_urls:
t = threading.Thread(target=_send, args=(url,))
t.start()
threads.append(t)
for t in threads:
t.join(timeout=15)
return results