Platform bundle: marketing publish, IT ops, packaging, agents mesh.
Volledige Foodlinkk Command Center uitbreiding met social automatisering, reclamefolder filters, Proxmox monitoring en documentatie.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
"""Halal market opportunity scoring for retail locations."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.db import execute, fetch_all, fetch_one, json_param
|
||||
|
||||
|
||||
def compute_all_scores(limit: int = 5000) -> dict[str, Any]:
|
||||
rows = fetch_all(
|
||||
"""
|
||||
SELECT s.id, s.chain, s.city, s.postcode, s.halal_certified, s.has_halal_section,
|
||||
s.partnership_status, a.population, a.avg_income,
|
||||
(a.religious_composition->>'muslim_proxy_pct')::float AS muslim_pct,
|
||||
(a.ethnic_composition->>'niet_westers_pct')::float AS niet_westers_pct
|
||||
FROM supermarkets s
|
||||
LEFT JOIN area_analysis a ON a.postcode = s.postcode
|
||||
WHERE s.postcode <> '0000AA'
|
||||
LIMIT %s
|
||||
""",
|
||||
(limit,),
|
||||
)
|
||||
computed = 0
|
||||
for r in rows:
|
||||
muslim = float(r.get("muslim_pct") or 0)
|
||||
niet_w = float(r.get("niet_westers_pct") or 0)
|
||||
pop = int(r.get("population") or 0)
|
||||
income = float(r.get("avg_income") or 0)
|
||||
|
||||
halal_gap = 0.0
|
||||
if not r.get("halal_certified") and not r.get("has_halal_section"):
|
||||
halal_gap = min(100, muslim * 1.5 + niet_w * 0.5)
|
||||
elif r.get("has_halal_section") and not r.get("halal_certified"):
|
||||
halal_gap = min(80, muslim * 0.8)
|
||||
|
||||
market_potential = 0.0
|
||||
if pop > 0:
|
||||
market_potential += min(40, pop / 25000)
|
||||
if income > 0:
|
||||
market_potential += min(30, income / 1500)
|
||||
market_potential += min(30, muslim * 0.4)
|
||||
|
||||
partnership_bonus = 15 if r.get("partnership_status") == "active" else 0
|
||||
halal_opp = round(min(100, halal_gap + market_potential * 0.3), 1)
|
||||
market_score = round(min(100, market_potential + partnership_bonus), 1)
|
||||
|
||||
factors = {
|
||||
"muslim_proxy_pct": muslim,
|
||||
"niet_westers_pct": niet_w,
|
||||
"population": pop,
|
||||
"avg_income": income,
|
||||
"halal_gap": round(halal_gap, 1),
|
||||
"has_halal_section": bool(r.get("has_halal_section")),
|
||||
"halal_certified": bool(r.get("halal_certified")),
|
||||
"partnership_status": r.get("partnership_status"),
|
||||
}
|
||||
execute(
|
||||
"""
|
||||
INSERT INTO retail_opportunity_scores (supermarket_id, halal_opportunity_score,
|
||||
market_potential_score, factors, computed_at)
|
||||
VALUES (%s, %s, %s, %s, NOW())
|
||||
ON CONFLICT (supermarket_id) DO UPDATE SET
|
||||
halal_opportunity_score = EXCLUDED.halal_opportunity_score,
|
||||
market_potential_score = EXCLUDED.market_potential_score,
|
||||
factors = EXCLUDED.factors,
|
||||
computed_at = NOW()
|
||||
""",
|
||||
(r["id"], halal_opp, market_score, json_param(factors)),
|
||||
)
|
||||
execute(
|
||||
"UPDATE supermarkets SET halal_opportunity_score = %s WHERE id = %s",
|
||||
(halal_opp, r["id"]),
|
||||
)
|
||||
computed += 1
|
||||
return {"computed": computed}
|
||||
|
||||
|
||||
def top_opportunities(
|
||||
limit: int = 50,
|
||||
min_score: float = 30,
|
||||
chain: str | None = None,
|
||||
province: str | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
clauses = ["ros.halal_opportunity_score >= %s"]
|
||||
params: list[Any] = [min_score]
|
||||
if chain:
|
||||
clauses.append("s.chain ILIKE %s")
|
||||
params.append(f"%{chain}%")
|
||||
if province:
|
||||
clauses.append("s.province ILIKE %s")
|
||||
params.append(f"%{province}%")
|
||||
where = " AND ".join(clauses)
|
||||
return fetch_all(
|
||||
f"""
|
||||
SELECT s.id, s.name, s.chain, s.city, s.province, s.postcode,
|
||||
s.partnership_status, s.halal_certified, s.has_halal_section,
|
||||
ros.halal_opportunity_score, ros.market_potential_score, ros.factors,
|
||||
a.population, a.avg_income,
|
||||
(a.religious_composition->>'muslim_proxy_pct')::float AS muslim_proxy_pct
|
||||
FROM retail_opportunity_scores ros
|
||||
JOIN supermarkets s ON s.id = ros.supermarket_id
|
||||
LEFT JOIN area_analysis a ON a.postcode = s.postcode
|
||||
WHERE {where}
|
||||
ORDER BY ros.halal_opportunity_score DESC
|
||||
LIMIT %s
|
||||
""",
|
||||
tuple(params + [limit]),
|
||||
)
|
||||
Reference in New Issue
Block a user