Use GEO_INTERNAL_URL for exchange seed
This commit is contained in:
@@ -2,8 +2,11 @@
|
|||||||
Seed Suppliers and Offers for African cocoa belt.
|
Seed Suppliers and Offers for African cocoa belt.
|
||||||
Creates offers via Temporal workflow so they sync to the graph.
|
Creates offers via Temporal workflow so they sync to the graph.
|
||||||
"""
|
"""
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
import uuid
|
import uuid
|
||||||
|
from pathlib import Path
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@@ -65,6 +68,9 @@ SUPPLIER_NAMES = [
|
|||||||
"Dar Coast Commodities", "Maputo Export House",
|
"Dar Coast Commodities", "Maputo Export House",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Default GLEIF Africa LEI dataset path (repo-local)
|
||||||
|
DEFAULT_GLEIF_PATH = "datasets/gleif/africa_lei_companies.csv"
|
||||||
|
|
||||||
# Fixed product catalog (10 items) with realistic prices per ton (USD)
|
# Fixed product catalog (10 items) with realistic prices per ton (USD)
|
||||||
PRODUCT_CATALOG = [
|
PRODUCT_CATALOG = [
|
||||||
{"name": "Cocoa Beans", "category": "Cocoa", "price": Decimal("2450.00")},
|
{"name": "Cocoa Beans", "category": "Cocoa", "price": Decimal("2450.00")},
|
||||||
@@ -138,8 +144,8 @@ class Command(BaseCommand):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--geo-url",
|
"--geo-url",
|
||||||
type=str,
|
type=str,
|
||||||
default="http://geo:8000/graphql/public/",
|
default=None,
|
||||||
help="Geo service GraphQL URL (default: http://geo:8000/graphql/public/)",
|
help="Geo service GraphQL URL (defaults to GEO_INTERNAL_URL env var)",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--odoo-url",
|
"--odoo-url",
|
||||||
@@ -176,6 +182,12 @@ class Command(BaseCommand):
|
|||||||
default=None,
|
default=None,
|
||||||
help="Filter offers by product name (e.g., 'Cocoa Beans')",
|
help="Filter offers by product name (e.g., 'Cocoa Beans')",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--company-csv",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="Path to CSV with real company names (default: datasets/gleif/africa_lei_companies.csv)",
|
||||||
|
)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
if options["clear"]:
|
if options["clear"]:
|
||||||
@@ -194,13 +206,17 @@ class Command(BaseCommand):
|
|||||||
use_bulk = options["bulk"]
|
use_bulk = options["bulk"]
|
||||||
bulk_size = max(1, options["bulk_size"])
|
bulk_size = max(1, options["bulk_size"])
|
||||||
sleep_ms = max(0, options["sleep_ms"])
|
sleep_ms = max(0, options["sleep_ms"])
|
||||||
geo_url = options["geo_url"]
|
geo_url = options["geo_url"] or os.getenv("GEO_INTERNAL_URL") or os.getenv("GEO_URL")
|
||||||
|
if not geo_url:
|
||||||
|
self.stdout.write(self.style.ERROR("Geo URL is not set. Provide --geo-url or GEO_INTERNAL_URL."))
|
||||||
|
return
|
||||||
odoo_url = options["odoo_url"]
|
odoo_url = options["odoo_url"]
|
||||||
product_filter = options["product"]
|
product_filter = options["product"]
|
||||||
ensure_products = options["ensure_products"]
|
ensure_products = options["ensure_products"]
|
||||||
odoo_db = options["odoo_db"]
|
odoo_db = options["odoo_db"]
|
||||||
odoo_user = options["odoo_user"]
|
odoo_user = options["odoo_user"]
|
||||||
odoo_password = options["odoo_password"]
|
odoo_password = options["odoo_password"]
|
||||||
|
company_csv = options["company_csv"]
|
||||||
|
|
||||||
# Fetch products from Odoo
|
# Fetch products from Odoo
|
||||||
self.stdout.write("Fetching products from Odoo...")
|
self.stdout.write("Fetching products from Odoo...")
|
||||||
@@ -233,14 +249,13 @@ class Command(BaseCommand):
|
|||||||
hubs = self._fetch_african_hubs(geo_url)
|
hubs = self._fetch_african_hubs(geo_url)
|
||||||
|
|
||||||
if not hubs:
|
if not hubs:
|
||||||
self.stdout.write(self.style.WARNING(
|
self.stdout.write(self.style.ERROR("No African hubs found from geo service. Aborting seed."))
|
||||||
"No African hubs found. Using default locations."
|
return
|
||||||
))
|
|
||||||
hubs = self._default_african_hubs()
|
|
||||||
|
|
||||||
self.stdout.write(f"Found {len(hubs)} African hubs")
|
self.stdout.write(f"Found {len(hubs)} African hubs")
|
||||||
|
|
||||||
# Create suppliers
|
# Create suppliers
|
||||||
|
self._company_pool = self._load_company_pool(company_csv)
|
||||||
self.stdout.write(f"Creating {suppliers_count} suppliers...")
|
self.stdout.write(f"Creating {suppliers_count} suppliers...")
|
||||||
new_suppliers = self._create_suppliers(suppliers_count, hubs)
|
new_suppliers = self._create_suppliers(suppliers_count, hubs)
|
||||||
self.stdout.write(self.style.SUCCESS(f"Created {len(new_suppliers)} suppliers"))
|
self.stdout.write(self.style.SUCCESS(f"Created {len(new_suppliers)} suppliers"))
|
||||||
@@ -572,15 +587,28 @@ class Command(BaseCommand):
|
|||||||
lat += random.uniform(-0.5, 0.5)
|
lat += random.uniform(-0.5, 0.5)
|
||||||
lng += random.uniform(-0.5, 0.5)
|
lng += random.uniform(-0.5, 0.5)
|
||||||
|
|
||||||
name = self._generate_supplier_name(idx)
|
company = self._pick_company(idx)
|
||||||
|
if company:
|
||||||
|
name = company["name"]
|
||||||
|
company_code = company.get("country_code")
|
||||||
|
mapped_name = self._country_name_from_code(company_code)
|
||||||
|
if mapped_name:
|
||||||
|
country = mapped_name
|
||||||
|
country_code = company_code
|
||||||
|
supplier_uuid = self._stable_uuid("supplier", company.get("lei") or name)
|
||||||
|
team_uuid = self._stable_uuid("team", company.get("lei") or name)
|
||||||
|
else:
|
||||||
|
name = self._generate_supplier_name(idx)
|
||||||
|
supplier_uuid = str(uuid.uuid4())
|
||||||
|
team_uuid = str(uuid.uuid4())
|
||||||
description = (
|
description = (
|
||||||
f"{name} is a reliable supplier based in {country}, "
|
f"{name} is a reliable supplier based in {country}, "
|
||||||
"focused on consistent quality and transparent logistics."
|
"focused on consistent quality and transparent logistics."
|
||||||
)
|
)
|
||||||
|
|
||||||
profile = SupplierProfile.objects.create(
|
profile = SupplierProfile.objects.create(
|
||||||
uuid=str(uuid.uuid4()),
|
uuid=supplier_uuid,
|
||||||
team_uuid=str(uuid.uuid4()),
|
team_uuid=team_uuid,
|
||||||
name=name,
|
name=name,
|
||||||
description=description,
|
description=description,
|
||||||
country=country,
|
country=country,
|
||||||
@@ -600,6 +628,68 @@ class Command(BaseCommand):
|
|||||||
return SUPPLIER_NAMES[index]
|
return SUPPLIER_NAMES[index]
|
||||||
return f"{random.choice(SUPPLIER_NAMES)} Group"
|
return f"{random.choice(SUPPLIER_NAMES)} Group"
|
||||||
|
|
||||||
|
def _find_default_company_csv(self) -> str | None:
|
||||||
|
"""Locate default company CSV in repo (datasets/gleif/africa_lei_companies.csv)."""
|
||||||
|
here = Path(__file__).resolve()
|
||||||
|
for parent in here.parents:
|
||||||
|
candidate = parent / DEFAULT_GLEIF_PATH
|
||||||
|
if candidate.exists():
|
||||||
|
return str(candidate)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _load_company_pool(self, csv_path: str | None) -> list[dict]:
|
||||||
|
"""Load real company names from CSV; returns list of dicts."""
|
||||||
|
path = csv_path or self._find_default_company_csv()
|
||||||
|
if not path or not os.path.exists(path):
|
||||||
|
self.stdout.write(self.style.WARNING("Company CSV not found; using fallback names."))
|
||||||
|
return []
|
||||||
|
|
||||||
|
companies = []
|
||||||
|
seen = set()
|
||||||
|
try:
|
||||||
|
with open(path, newline="", encoding="utf-8") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
name = (row.get("entity_name") or "").strip()
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
if name in seen:
|
||||||
|
continue
|
||||||
|
seen.add(name)
|
||||||
|
companies.append(
|
||||||
|
{
|
||||||
|
"name": name,
|
||||||
|
"lei": (row.get("lei") or "").strip(),
|
||||||
|
"country_code": (row.get("legal_address_country") or row.get("headquarters_country") or "").strip(),
|
||||||
|
"city": (row.get("legal_address_city") or row.get("headquarters_city") or "").strip(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.stdout.write(self.style.WARNING(f"Failed to read company CSV: {e}"))
|
||||||
|
return []
|
||||||
|
|
||||||
|
random.shuffle(companies)
|
||||||
|
self.stdout.write(f"Loaded {len(companies)} company names from CSV")
|
||||||
|
return companies
|
||||||
|
|
||||||
|
def _pick_company(self, index: int) -> dict | None:
|
||||||
|
if not getattr(self, "_company_pool", None):
|
||||||
|
return None
|
||||||
|
if index < len(self._company_pool):
|
||||||
|
return self._company_pool[index]
|
||||||
|
return random.choice(self._company_pool)
|
||||||
|
|
||||||
|
def _stable_uuid(self, prefix: str, value: str) -> str:
|
||||||
|
return str(uuid.uuid5(uuid.NAMESPACE_DNS, f"{prefix}:{value}"))
|
||||||
|
|
||||||
|
def _country_name_from_code(self, code: str | None) -> str | None:
|
||||||
|
if not code:
|
||||||
|
return None
|
||||||
|
for name, country_code, _, _ in AFRICAN_COUNTRIES:
|
||||||
|
if country_code == code:
|
||||||
|
return name
|
||||||
|
return None
|
||||||
|
|
||||||
def _price_for_product(self, product_name: str) -> Decimal:
|
def _price_for_product(self, product_name: str) -> Decimal:
|
||||||
for item in PRODUCT_CATALOG:
|
for item in PRODUCT_CATALOG:
|
||||||
if item["name"].lower() == product_name.lower():
|
if item["name"].lower() == product_name.lower():
|
||||||
|
|||||||
Reference in New Issue
Block a user