5d60d33db1
Volledige Foodlinkk Command Center uitbreiding met social automatisering, reclamefolder filters, Proxmox monitoring en documentatie.
114 lines
3.9 KiB
Python
114 lines
3.9 KiB
Python
"""Unified live platform feed — events with traceable sources."""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
from typing import Any, Optional
|
|
|
|
from app.db import fetch_all
|
|
|
|
CHANNEL_ROUTES = {
|
|
"dashboard": "/",
|
|
"retail": "/retail",
|
|
"marketing": "/marketing",
|
|
"beurs": "/beurs",
|
|
"agents": "/agents",
|
|
"hermes": "/hermes",
|
|
"browser": "/browser",
|
|
"documents": "/documents",
|
|
"settings": "/settings",
|
|
}
|
|
|
|
|
|
def _resolve_source(row: dict[str, Any]) -> dict[str, Any]:
|
|
meta = row.get("metadata") or {}
|
|
if isinstance(meta, str):
|
|
import json
|
|
try:
|
|
meta = json.loads(meta)
|
|
except Exception:
|
|
meta = {}
|
|
|
|
source_url = meta.get("source_url") or meta.get("url") or meta.get("link")
|
|
source_label = meta.get("source") or meta.get("feed_name")
|
|
|
|
if not source_url:
|
|
channel = row.get("channel") or "dashboard"
|
|
source_url = CHANNEL_ROUTES.get(channel, "/")
|
|
source_label = source_label or f"Foodlinkk · {channel}"
|
|
|
|
if row.get("related_table") == "rss_items" and row.get("related_id"):
|
|
source_url = meta.get("link") or source_url
|
|
|
|
event_type = (row.get("event_type") or "").lower()
|
|
agent = (row.get("agent_name") or "").lower()
|
|
|
|
if event_type in ("briefing", "report"):
|
|
source_url = "/"
|
|
elif event_type in ("sync", "score", "import") and "retail" in agent:
|
|
source_url = "/retail"
|
|
elif event_type == "refresh" and "rss" in agent:
|
|
source_url = "/marketing"
|
|
elif event_type in ("sync",) and "halal" in agent:
|
|
source_url = "/retail"
|
|
elif agent == "herman":
|
|
source_url = "/"
|
|
elif agent in ("marketing", "rss_feeds"):
|
|
source_url = "/marketing"
|
|
elif agent in ("wholesale_scraper", "retail_intel"):
|
|
source_url = "/retail"
|
|
elif agent == "hermes":
|
|
source_url = "/hermes"
|
|
|
|
internal_url = source_url if source_url.startswith("/") else None
|
|
external_url = source_url if source_url and source_url.startswith("http") else None
|
|
|
|
return {
|
|
"source_url": source_url,
|
|
"source_label": source_label or "Foodlinkk platform",
|
|
"internal_url": internal_url,
|
|
"external_url": external_url,
|
|
}
|
|
|
|
|
|
def fetch_platform_events(limit: int = 80, agent: Optional[str] = None) -> list[dict[str, Any]]:
|
|
clauses, params = [], []
|
|
if agent:
|
|
clauses.append("LOWER(agent_name) = %s")
|
|
params.append(agent.lower())
|
|
where = (" WHERE " + " AND ".join(clauses)) if clauses else ""
|
|
rows = fetch_all(
|
|
f"""SELECT id, agent_name, agent_type, event_type, title, body, status,
|
|
channel, metadata, related_table, related_id, created_at
|
|
FROM agent_events{where}
|
|
ORDER BY created_at DESC LIMIT %s""",
|
|
tuple(params + [limit]),
|
|
)
|
|
events = []
|
|
for r in rows:
|
|
item = dict(r)
|
|
if item.get("created_at"):
|
|
item["created_at"] = item["created_at"].isoformat()
|
|
src = _resolve_source(item)
|
|
item.update(src)
|
|
item["click_url"] = src.get("external_url") or src.get("internal_url") or "/agents"
|
|
item["is_external"] = bool(src.get("external_url"))
|
|
events.append(item)
|
|
return events
|
|
|
|
|
|
def platform_stats() -> dict[str, Any]:
|
|
try:
|
|
total = fetch_all("SELECT COUNT(*) AS n FROM agent_events")[0]["n"]
|
|
pending = fetch_all("SELECT COUNT(*) AS n FROM agent_events WHERE status = 'needs_approval'")[0]["n"]
|
|
last_hour = fetch_all(
|
|
"SELECT COUNT(*) AS n FROM agent_events WHERE created_at >= NOW() - INTERVAL '1 hour'"
|
|
)[0]["n"]
|
|
except Exception:
|
|
total = pending = last_hour = 0
|
|
return {
|
|
"total_events": int(total or 0),
|
|
"pending_approvals": int(pending or 0),
|
|
"events_last_hour": int(last_hour or 0),
|
|
"updated_at": datetime.now(timezone.utc).isoformat(),
|
|
}
|