forked from Rockachopa/Timmy-time-dashboard
All `except Exception:` now catch as `except Exception as exc:` with appropriate logging (warning for critical paths, debug for graceful degradation). Added logger setup to 4 files that lacked it: - src/timmy/memory/vector_store.py - src/dashboard/middleware/csrf.py - src/dashboard/middleware/security_headers.py - src/spark/memory.py 31 files changed across timmy core, dashboard, infrastructure, integrations. Zero bare excepts remain. 1340 tests passing.
96 lines
3.3 KiB
Python
96 lines
3.3 KiB
Python
"""Briefing routes — Morning briefing and approval queue.
|
|
|
|
GET /briefing — render the briefing page
|
|
GET /briefing/approvals — HTMX partial: pending approval cards
|
|
POST /briefing/approvals/{id}/approve — approve an item (HTMX)
|
|
POST /briefing/approvals/{id}/reject — reject an item (HTMX)
|
|
"""
|
|
|
|
import logging
|
|
from datetime import UTC, datetime
|
|
|
|
from fastapi import APIRouter, Request
|
|
from fastapi.responses import HTMLResponse, JSONResponse
|
|
|
|
from dashboard.templating import templates
|
|
from timmy import approvals as approval_store
|
|
from timmy.briefing import Briefing
|
|
from timmy.briefing import engine as briefing_engine
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/briefing", tags=["briefing"])
|
|
|
|
|
|
@router.get("", response_class=HTMLResponse)
|
|
async def get_briefing(request: Request):
|
|
"""Return today's briefing page (generated or cached)."""
|
|
try:
|
|
briefing = briefing_engine.get_or_generate()
|
|
except Exception as exc:
|
|
logger.debug("Briefing generation failed: %s", exc)
|
|
logger.exception("Briefing generation failed")
|
|
now = datetime.now(UTC)
|
|
briefing = Briefing(
|
|
generated_at=now,
|
|
summary=(
|
|
"Good morning. The briefing could not be generated right now. "
|
|
"Check that Ollama is running and try again."
|
|
),
|
|
period_start=now,
|
|
period_end=now,
|
|
)
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"briefing.html",
|
|
{"briefing": briefing},
|
|
)
|
|
|
|
|
|
@router.get("/approvals", response_class=HTMLResponse)
|
|
async def get_approvals(request: Request):
|
|
"""Return HTMX partial with all pending approval items."""
|
|
items = approval_store.list_pending()
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"partials/approval_cards.html",
|
|
{"items": items},
|
|
)
|
|
|
|
|
|
@router.post("/approvals/{item_id}/approve", response_class=HTMLResponse)
|
|
async def approve_item(request: Request, item_id: str):
|
|
"""Approve an approval item; return the updated card via HTMX."""
|
|
item = approval_store.approve(item_id)
|
|
if item is None:
|
|
return HTMLResponse("<p class='text-danger'>Item not found.</p>", status_code=404)
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"partials/approval_card_single.html",
|
|
{"item": item},
|
|
)
|
|
|
|
|
|
@router.post("/approvals/{item_id}/reject", response_class=HTMLResponse)
|
|
async def reject_item(request: Request, item_id: str):
|
|
"""Reject an approval item; return the updated card via HTMX."""
|
|
item = approval_store.reject(item_id)
|
|
if item is None:
|
|
return HTMLResponse("<p class='text-danger'>Item not found.</p>", status_code=404)
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"partials/approval_card_single.html",
|
|
{"item": item},
|
|
)
|
|
|
|
|
|
@router.post("/regenerate", response_class=JSONResponse)
|
|
async def regenerate_briefing():
|
|
"""Force-regenerate today's briefing."""
|
|
try:
|
|
briefing = briefing_engine.generate()
|
|
return JSONResponse({"success": True, "generated_at": str(briefing.generated_at)})
|
|
except Exception as exc:
|
|
logger.exception("Failed to regenerate briefing")
|
|
return JSONResponse({"success": False, "error": str(exc)}, status_code=500)
|