5d60d33db1
Volledige Foodlinkk Command Center uitbreiding met social automatisering, reclamefolder filters, Proxmox monitoring en documentatie.
94 lines
3.1 KiB
Python
94 lines
3.1 KiB
Python
from datetime import date, datetime
|
|
from typing import Any, Optional
|
|
|
|
import httpx
|
|
from fastapi import APIRouter, HTTPException
|
|
|
|
from app.config import settings
|
|
from app.db import execute, fetch_all, fetch_one
|
|
from app.services.briefing import collect_briefing_data, generate_daily_briefing, serialize_stats
|
|
|
|
router = APIRouter(prefix="/api", tags=["api"])
|
|
|
|
|
|
def _stats_payload() -> dict[str, Any]:
|
|
stats: dict[str, Any] = {
|
|
"deals": 0,
|
|
"clients": 0,
|
|
"pending_approvals": 0,
|
|
"pipeline_value": 0,
|
|
}
|
|
try:
|
|
row = fetch_one("SELECT COUNT(*) AS c FROM deals")
|
|
stats["deals"] = int(row["c"]) if row else 0
|
|
except Exception:
|
|
pass
|
|
try:
|
|
row = fetch_one("SELECT COUNT(*) AS c FROM clients")
|
|
stats["clients"] = int(row["c"]) if row else 0
|
|
except Exception:
|
|
pass
|
|
try:
|
|
row = fetch_one("SELECT COUNT(*) AS c FROM agent_events WHERE status = 'needs_approval'")
|
|
stats["pending_approvals"] = int(row["c"]) if row else 0
|
|
except Exception:
|
|
pass
|
|
try:
|
|
row = fetch_one(
|
|
"SELECT COALESCE(SUM(value), 0) AS total FROM deals WHERE stage NOT IN ('won', 'lost')"
|
|
)
|
|
stats["pipeline_value"] = float(row["total"]) if row else 0
|
|
except Exception:
|
|
pass
|
|
return stats
|
|
|
|
|
|
@router.get("/herman/briefing/stats")
|
|
async def herman_briefing_stats():
|
|
"""Live stats from DB — always fresh for dashboard panels."""
|
|
return {"ok": True, "stats": serialize_stats(collect_briefing_data()), "at": datetime.utcnow().isoformat()}
|
|
|
|
|
|
@router.post("/herman/briefing")
|
|
async def herman_briefing():
|
|
try:
|
|
content, stats = await generate_daily_briefing()
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
return {"ok": True, "content": content, "stats": stats, "generated_at": datetime.utcnow().isoformat()}
|
|
|
|
|
|
@router.get("/herman/briefing/latest")
|
|
async def herman_briefing_latest():
|
|
try:
|
|
row = fetch_one(
|
|
"SELECT id, content, generated_by, metadata, created_at FROM daily_briefings ORDER BY created_at DESC LIMIT 1"
|
|
)
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
live_stats = serialize_stats(collect_briefing_data())
|
|
if not row:
|
|
return {"ok": True, "content": None, "stats": live_stats}
|
|
if row.get("created_at") and hasattr(row["created_at"], "isoformat"):
|
|
row["created_at"] = row["created_at"].isoformat()
|
|
return {"ok": True, **row, "stats": live_stats}
|
|
|
|
|
|
@router.get("/events")
|
|
async def list_events(limit: int = 50):
|
|
limit = max(1, min(limit, 200))
|
|
try:
|
|
rows = fetch_all(
|
|
"""
|
|
SELECT id, agent_name, event_type, title, body, status, created_at
|
|
FROM agent_events ORDER BY created_at DESC LIMIT %s
|
|
""",
|
|
(limit,),
|
|
)
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
for row in rows:
|
|
if row.get("created_at"):
|
|
row["created_at"] = row["created_at"].isoformat()
|
|
return {"events": rows}
|