SysOps: voice-agy-webbuilder-backup — 2026-06-23 10:04 UTC
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
"""Export Intel Cockpit page + API proxy."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Any, Optional
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Query, Request
|
||||
from fastapi.responses import RedirectResponse, StreamingResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from pathlib import Path
|
||||
|
||||
router = APIRouter()
|
||||
BASE = Path(__file__).resolve().parent.parent.parent
|
||||
templates = Jinja2Templates(directory=str(BASE / "templates"))
|
||||
TOOLS = os.getenv("TOOLS_API_URL", "http://tools-api:8700").rstrip("/")
|
||||
|
||||
|
||||
async def _proxy(method: str, path: str, **kwargs) -> Any:
|
||||
url = f"{TOOLS}/export-intel{path}"
|
||||
async with httpx.AsyncClient(timeout=60.0) as client:
|
||||
r = await client.request(method, url, **kwargs)
|
||||
if r.status_code >= 400:
|
||||
return {"error": r.text, "status": r.status_code}
|
||||
if "text/csv" in r.headers.get("content-type", ""):
|
||||
return r
|
||||
return r.json()
|
||||
|
||||
|
||||
@router.get("/foodlinkk")
|
||||
def foodlinkk_home():
|
||||
return RedirectResponse(url="/export-intel", status_code=302)
|
||||
|
||||
|
||||
@router.get("/export-intel")
|
||||
def export_intel_page(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
"export_intel.html",
|
||||
{"request": request, "page_title": "Wereldexport"},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/stats")
|
||||
async def api_stats(country: Optional[str] = None, region: Optional[str] = None):
|
||||
params = {k: v for k, v in {"country": country, "region": region}.items() if v}
|
||||
return await _proxy("GET", "/stats", params=params)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/regions")
|
||||
async def api_regions():
|
||||
return await _proxy("GET", "/regions")
|
||||
|
||||
|
||||
@router.get("/api/export-intel/territories")
|
||||
async def api_territories(region: Optional[str] = None):
|
||||
params = {"region": region} if region else {}
|
||||
return await _proxy("GET", "/territories", params=params)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/entities")
|
||||
async def api_entities(
|
||||
country: Optional[str] = None,
|
||||
region: Optional[str] = None,
|
||||
entity_type: Optional[str] = None,
|
||||
entity_types: Optional[str] = None,
|
||||
q: Optional[str] = None,
|
||||
favorite_only: bool = False,
|
||||
crm_linked: Optional[bool] = None,
|
||||
limit: int = 200,
|
||||
offset: int = 0,
|
||||
):
|
||||
params = {k: v for k, v in {
|
||||
"country": country, "region": region, "entity_type": entity_type,
|
||||
"entity_types": entity_types, "q": q, "limit": limit, "offset": offset,
|
||||
"favorite_only": favorite_only, "crm_linked": crm_linked,
|
||||
}.items() if v is not None and v is not False}
|
||||
return await _proxy("GET", "/entities", params=params)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/entities/{entity_id}")
|
||||
async def api_entity(entity_id: int):
|
||||
return await _proxy("GET", f"/entities/{entity_id}")
|
||||
|
||||
|
||||
@router.get("/api/export-intel/contacts")
|
||||
async def api_contacts(
|
||||
country: Optional[str] = None,
|
||||
entity_type: Optional[str] = None,
|
||||
has_email: Optional[bool] = None,
|
||||
q: Optional[str] = None,
|
||||
limit: int = 200,
|
||||
offset: int = 0,
|
||||
):
|
||||
params: dict[str, Any] = {"limit": limit, "offset": offset}
|
||||
if country:
|
||||
params["country"] = country
|
||||
if entity_type:
|
||||
params["entity_type"] = entity_type
|
||||
if has_email is not None:
|
||||
params["has_email"] = has_email
|
||||
if q:
|
||||
params["q"] = q
|
||||
return await _proxy("GET", "/contacts", params=params)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/contacts/export.csv")
|
||||
async def api_contacts_export(country: Optional[str] = None, entity_type: Optional[str] = None):
|
||||
params = {k: v for k, v in {"country": country, "entity_type": entity_type}.items() if v}
|
||||
url = f"{TOOLS}/export-intel/contacts/export.csv"
|
||||
async with httpx.AsyncClient(timeout=60.0) as client:
|
||||
r = await client.get(url, params=params)
|
||||
return StreamingResponse(
|
||||
iter([r.content]),
|
||||
media_type="text/csv",
|
||||
headers={"Content-Disposition": "attachment; filename=export-intel-contacts.csv"},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/caterers/brands")
|
||||
async def api_caterer_brands():
|
||||
return await _proxy("GET", "/caterers/brands")
|
||||
|
||||
|
||||
@router.get("/api/export-intel/caterers/presence")
|
||||
async def api_caterer_presence(country: Optional[str] = None):
|
||||
params = {"country": country} if country else {}
|
||||
return await _proxy("GET", "/caterers/presence", params=params)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/gov-sources")
|
||||
async def api_gov_sources(country: Optional[str] = None):
|
||||
params = {"country": country} if country else {}
|
||||
return await _proxy("GET", "/gov-sources", params=params)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/map/bundle")
|
||||
async def api_map_bundle(
|
||||
country: Optional[str] = None,
|
||||
region: Optional[str] = None,
|
||||
entity_type: Optional[str] = None,
|
||||
entity_types: Optional[str] = None,
|
||||
q: Optional[str] = None,
|
||||
halal_min: Optional[float] = None,
|
||||
favorite_only: bool = False,
|
||||
crm_linked: Optional[bool] = None,
|
||||
):
|
||||
params = {k: v for k, v in {
|
||||
"country": country, "region": region, "entity_type": entity_type,
|
||||
"entity_types": entity_types, "q": q, "halal_min": halal_min,
|
||||
"favorite_only": favorite_only, "crm_linked": crm_linked,
|
||||
}.items() if v is not None and v is not False}
|
||||
return await _proxy("GET", "/map/bundle", params=params)
|
||||
|
||||
|
||||
@router.post("/api/export-intel/entities/favorites")
|
||||
async def api_set_favorites(request: Request):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/entities/favorites", json=body)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/entities/favorites")
|
||||
async def api_list_favorites(country: Optional[str] = None, region: Optional[str] = None, limit: int = 200):
|
||||
params = {k: v for k, v in {"country": country, "region": region, "limit": limit}.items() if v is not None}
|
||||
return await _proxy("GET", "/entities/favorites", params=params)
|
||||
|
||||
|
||||
@router.post("/api/export-intel/crm/push")
|
||||
async def api_crm_push(request: Request):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/crm/push", json=body)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/crm/pipeline")
|
||||
async def api_crm_pipeline(country: Optional[str] = None, region: Optional[str] = None, limit: int = 100):
|
||||
params = {k: v for k, v in {"country": country, "region": region, "limit": limit}.items() if v is not None}
|
||||
return await _proxy("GET", "/crm/pipeline", params=params)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/halal/markets")
|
||||
async def api_halal_markets(
|
||||
region: Optional[str] = None,
|
||||
country: Optional[str] = None,
|
||||
limit: int = 30,
|
||||
):
|
||||
params = {k: v for k, v in {"region": region, "country": country, "limit": limit}.items() if v is not None}
|
||||
return await _proxy("GET", "/halal/markets", params=params)
|
||||
|
||||
|
||||
@router.get("/api/export-intel/tenders")
|
||||
async def api_tenders(country: Optional[str] = None, status: Optional[str] = "open", limit: int = 100):
|
||||
params = {k: v for k, v in {"country": country, "status": status, "limit": limit}.items() if v is not None}
|
||||
return await _proxy("GET", "/tenders", params=params)
|
||||
|
||||
|
||||
@router.post("/api/export-intel/sync/{kind}")
|
||||
async def api_sync(kind: str, request: Request):
|
||||
body = {}
|
||||
if request.headers.get("content-type", "").startswith("application/json"):
|
||||
try:
|
||||
body = await request.json()
|
||||
except Exception:
|
||||
body = {}
|
||||
if kind not in ("contacts", "caterers", "distributors", "customers", "tenders", "all", "world", "region"):
|
||||
return {"error": "unknown sync kind"}
|
||||
timeout = 7200.0 if kind in ("world", "region", "all") else 600.0
|
||||
async with httpx.AsyncClient(timeout=timeout) as client:
|
||||
url = f"{TOOLS}/export-intel/sync/{kind}"
|
||||
r = await client.post(url, json=body)
|
||||
return r.json()
|
||||
Reference in New Issue
Block a user