generated from coulomb/repo-seed
Adds preferred workplan REST/event surfaces, legacy-meter telemetry and weekly review summaries, documentation/dashboard terminology updates, dashboard API loading fixes, and close-out sync for STATE-WP-0052 and STATE-WP-0054.
130 lines
4.4 KiB
Python
130 lines
4.4 KiB
Python
import uuid
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from api.database import get_session
|
|
from api.models.legacy_meter import LegacyInterface
|
|
from api.schemas.legacy_meter import (
|
|
LegacyInterfacePatch,
|
|
LegacyInterfaceRead,
|
|
LegacyInterfaceRegister,
|
|
LegacyUsageRecord,
|
|
LegacyUsageSummary,
|
|
LegacyWeeklyReview,
|
|
)
|
|
from api.services.legacy_meter import (
|
|
LegacyUsageIdentity,
|
|
get_legacy_interface_by_key,
|
|
legacy_usage_summary,
|
|
legacy_weekly_review,
|
|
list_legacy_interfaces,
|
|
patch_legacy_interface,
|
|
record_legacy_usage,
|
|
register_legacy_interface,
|
|
)
|
|
|
|
router = APIRouter(prefix="/legacy-meter", tags=["legacy-meter"])
|
|
|
|
|
|
@router.post(
|
|
"/interfaces",
|
|
response_model=LegacyInterfaceRead,
|
|
status_code=status.HTTP_201_CREATED,
|
|
)
|
|
async def register_interface(
|
|
body: LegacyInterfaceRegister,
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> LegacyInterface:
|
|
try:
|
|
return await register_legacy_interface(session, **body.model_dump())
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/interfaces", response_model=list[LegacyInterfaceRead])
|
|
async def list_interfaces(
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> list[LegacyInterface]:
|
|
return await list_legacy_interfaces(session)
|
|
|
|
|
|
@router.get("/interfaces/by-key", response_model=LegacyInterfaceRead)
|
|
async def get_interface_by_key(
|
|
interface_key: str = Query(...),
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> LegacyInterface:
|
|
interface = await get_legacy_interface_by_key(session, interface_key)
|
|
if interface is None:
|
|
raise HTTPException(status_code=404, detail=f"Legacy interface '{interface_key}' not found")
|
|
return interface
|
|
|
|
|
|
@router.patch("/interfaces/{interface_id}", response_model=LegacyInterfaceRead)
|
|
async def patch_interface(
|
|
interface_id: uuid.UUID,
|
|
body: LegacyInterfacePatch,
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> LegacyInterface:
|
|
interface = await session.get(LegacyInterface, interface_id)
|
|
if interface is None:
|
|
raise HTTPException(status_code=404, detail=f"Legacy interface '{interface_id}' not found")
|
|
try:
|
|
return await patch_legacy_interface(
|
|
session,
|
|
interface,
|
|
body.model_dump(exclude_unset=True),
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
|
|
|
|
|
@router.post("/usage", response_model=LegacyInterfaceRead, status_code=status.HTTP_201_CREATED)
|
|
async def record_usage(
|
|
body: LegacyUsageRecord,
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> LegacyInterface:
|
|
try:
|
|
return await record_legacy_usage(
|
|
session,
|
|
interface_key=body.interface_key,
|
|
interface_kind=body.interface_kind,
|
|
replacement_ref=body.replacement_ref,
|
|
owner_component=body.owner_component,
|
|
replacement_verified=body.replacement_verified,
|
|
identity=LegacyUsageIdentity(
|
|
tenant_key=body.tenant_key or "unknown",
|
|
user_key=body.user_key or "unknown",
|
|
component_key=body.component_key or "unknown",
|
|
),
|
|
observed_at=body.observed_at,
|
|
call_count=body.call_count,
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/summary", response_model=LegacyUsageSummary)
|
|
async def usage_summary(
|
|
days: int = Query(7, ge=1, le=366),
|
|
window_start: datetime | None = None,
|
|
window_end: datetime | None = None,
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> LegacyUsageSummary:
|
|
end = window_end or datetime.now(tz=timezone.utc)
|
|
start = window_start or (end - timedelta(days=days))
|
|
return await legacy_usage_summary(session, window_start=start, window_end=end)
|
|
|
|
|
|
@router.get("/weekly-review", response_model=LegacyWeeklyReview)
|
|
async def weekly_review(
|
|
days: int = Query(7, ge=1, le=366),
|
|
window_start: datetime | None = None,
|
|
window_end: datetime | None = None,
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> LegacyWeeklyReview:
|
|
end = window_end or datetime.now(tz=timezone.utc)
|
|
start = window_start or (end - timedelta(days=days))
|
|
return await legacy_weekly_review(session, window_start=start, window_end=end)
|