SysOps: deploy-all — 2026-06-09 10:41 UTC

This commit is contained in:
sysops
2026-06-09 10:41:13 +00:00
parent 69fe67cc0e
commit 21ea3a2c81
82 changed files with 8906 additions and 981 deletions
+98 -1
View File
@@ -7,7 +7,7 @@ from fastapi import APIRouter, HTTPException, Query
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from app.db import execute_returning, fetch_all, fetch_one
from app.db import execute, execute_returning, fetch_all, fetch_one
from app.middleware import log_agent_event
from app import retail_scrapers
from app import retail_enrichment
@@ -35,6 +35,49 @@ class CrmLinkIn(BaseModel):
notes: Optional[str] = None
class SupermarketCreateIn(BaseModel):
name: str
chain: str
address: str
postcode: str = "0000AA"
city: str
province: Optional[str] = None
phone: Optional[str] = None
email: Optional[str] = None
website: Optional[str] = None
manager_name: Optional[str] = None
employee_count: Optional[int] = None
store_type: Optional[str] = None
partnership_status: str = "none"
halal_certified: bool = False
has_halal_section: bool = False
halal_certifier: Optional[str] = None
data_source: str = "manual"
class SupermarketPatchIn(BaseModel):
name: Optional[str] = None
chain: Optional[str] = None
address: Optional[str] = None
postcode: Optional[str] = None
city: Optional[str] = None
province: Optional[str] = None
phone: Optional[str] = None
email: Optional[str] = None
website: Optional[str] = None
manager_name: Optional[str] = None
employee_count: Optional[int] = None
store_type: Optional[str] = None
size_m2: Optional[int] = None
partnership_status: Optional[str] = None
halal_certified: Optional[bool] = None
has_halal_section: Optional[bool] = None
halal_certifier: Optional[str] = None
halal_certificate_number: Optional[str] = None
organic_section: Optional[bool] = None
alcohol_section: Optional[bool] = None
STORE_SELECT = """
SELECT s.*,
a.population AS area_population,
@@ -360,6 +403,60 @@ def get_supermarket(store_id: int) -> dict[str, Any]:
return data
@router.post("/supermarkets")
def create_supermarket(payload: SupermarketCreateIn) -> dict[str, Any]:
row = execute_returning(
"""INSERT INTO supermarkets (
name, chain, address, postcode, city, province, phone, email, website,
manager_name, employee_count, store_type, partnership_status,
halal_certified, has_halal_section, halal_certifier, data_source, last_updated
) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,NOW())
RETURNING id""",
(
payload.name.strip(), payload.chain.strip(), payload.address.strip(),
payload.postcode.strip().upper(), payload.city.strip(), payload.province,
payload.phone, payload.email, payload.website, payload.manager_name,
payload.employee_count, payload.store_type, payload.partnership_status,
payload.halal_certified, payload.has_halal_section, payload.halal_certifier,
payload.data_source,
),
)
store_id = int(row["id"])
log_agent_event(
agent_name="retail_crm",
event_type="create",
title=f"Handmatig filiaal toegevoegd: {payload.name} ({payload.chain})",
)
return {"ok": True, "item": _row(fetch_one(f"{STORE_SELECT} WHERE s.id = %s", (store_id,)))}
@router.patch("/supermarkets/{store_id}")
def patch_supermarket(store_id: int, payload: SupermarketPatchIn) -> dict[str, Any]:
existing = fetch_one("SELECT id FROM supermarkets WHERE id = %s", (store_id,))
if not existing:
raise HTTPException(404, "Supermarket not found")
data = payload.model_dump(exclude_unset=True)
if not data:
raise HTTPException(400, "Geen velden om bij te werken")
allowed = set(SupermarketPatchIn.model_fields.keys())
sets: list[str] = []
params: list[Any] = []
for key, val in data.items():
if key not in allowed:
continue
sets.append(f"{key} = %s")
params.append(val)
sets.append("last_updated = NOW()")
params.append(store_id)
execute(f"UPDATE supermarkets SET {', '.join(sets)} WHERE id = %s", tuple(params))
log_agent_event(
agent_name="retail_crm",
event_type="update",
title=f"Supermarkt #{store_id} data aangevuld",
)
return {"ok": True, "item": _row(fetch_one(f"{STORE_SELECT} WHERE s.id = %s", (store_id,)))}
@router.get("/scrape/chains")
def list_scrape_chains() -> dict[str, Any]:
return {"chains": retail_scrapers.list_chains()}