- 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%
66 lines
2.2 KiB
Python
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
|