generated from coulomb/repo-seed
feat: add workplan aliases and legacy meter
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.
This commit is contained in:
129
api/routers/legacy_meter.py
Normal file
129
api/routers/legacy_meter.py
Normal file
@@ -0,0 +1,129 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user