5e43b6b246
Adds floating Herman assistant with voice, Hermes technical KPI dashboard with file explorer, and a wins/kansen analytics pulse with Hermes findings hooks. Includes optional local HTTPS via Caddy for mic permissions (public certs only).
65 lines
2.0 KiB
Python
65 lines
2.0 KiB
Python
from pathlib import Path
|
|
from typing import Any, Optional
|
|
|
|
from fastapi import APIRouter, Body, Query, Request
|
|
from fastapi.responses import JSONResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.services.analytics_data import add_finding, collect_analytics, list_hermes_findings
|
|
|
|
router = APIRouter(prefix="/analytics", tags=["analytics"])
|
|
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
|
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
|
|
|
|
|
|
class FindingIn(BaseModel):
|
|
kind: str = Field(default="insight", description="win|opportunity|attention|insight")
|
|
title: str
|
|
body: str = ""
|
|
score: float | None = None
|
|
source: str = "hermes"
|
|
href: str | None = None
|
|
meta: dict[str, Any] | None = None
|
|
|
|
|
|
@router.get("")
|
|
async def analytics_page(request: Request):
|
|
initial = collect_analytics({})
|
|
return templates.TemplateResponse(
|
|
"analytics.html",
|
|
{"request": request, "page_title": "Analytics", "initial_data": initial},
|
|
)
|
|
|
|
|
|
@router.get("/api/data")
|
|
async def analytics_api(
|
|
chain: Optional[str] = None,
|
|
province: Optional[str] = None,
|
|
stage: Optional[str] = None,
|
|
agent: Optional[str] = None,
|
|
days: int = Query(90, ge=7, le=365),
|
|
):
|
|
filters = {"chain": chain, "province": province, "stage": stage, "agent": agent, "days": days}
|
|
return JSONResponse(collect_analytics(filters))
|
|
|
|
|
|
@router.get("/api/findings")
|
|
async def analytics_findings(limit: int = Query(20, ge=1, le=100)):
|
|
return {"items": list_hermes_findings(limit)}
|
|
|
|
|
|
@router.post("/api/findings")
|
|
async def analytics_findings_create(payload: FindingIn = Body(...)):
|
|
"""Hermes (of admin) kan bevindingen pushen naar het intelligence dashboard."""
|
|
row = add_finding(
|
|
kind=payload.kind,
|
|
title=payload.title,
|
|
body=payload.body,
|
|
score=payload.score,
|
|
source=payload.source,
|
|
href=payload.href,
|
|
meta=payload.meta,
|
|
)
|
|
return {"ok": True, "finding": row}
|