"""Use-Case → Controls API — the shared retrieval layer. GET /v1/controls/use-cases — registry + mapped counts GET /v1/controls/use-cases/{use_case}/controls — ranked controls of a topic Consumed by the document specialist agents and the CRA finding-mapper so both draw from ONE controls index instead of separate retrievals. Read-only. """ from __future__ import annotations from typing import Any, Optional from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session from classroom_engine.database import get_db from compliance.api._http_errors import translate_domain_errors from compliance.services.corpus_overview import corpus_overview from compliance.services.use_case_controls import UseCaseControlsService router = APIRouter(prefix="/v1/controls", tags=["use-case-controls"]) def get_use_case_controls_service( db: Session = Depends(get_db), ) -> UseCaseControlsService: return UseCaseControlsService(db) @router.get("/use-cases") async def list_use_cases( svc: UseCaseControlsService = Depends(get_use_case_controls_service), ) -> list[dict[str, Any]]: """All enabled use-cases (topics) with their live mapped-control counts.""" with translate_domain_errors(): return svc.list_use_cases() @router.get("/corpus") async def corpus(db: Session = Depends(get_db)) -> dict[str, Any]: """Korpus-Übersicht: Quell-Dokumente (source_regulation) mit Lizenz-Tier + Atom-Count + gemapptem Use Case, plus den kuratierten Lizenz-Katalog (canonical_control_sources ⋈ licenses) mit Nutzungsrechten.""" with translate_domain_errors(): return corpus_overview(db) @router.get("/use-cases/{use_case}/controls") async def controls_for_use_case( use_case: str, primary_only: bool = Query(False, description="master-grain Fallback: nur Primaerzweck"), sub_topic: Optional[str] = Query(None, description="atom-grain: nur dieses Sub-Thema"), limit: int = Query(50, ge=1, le=200), offset: int = Query(0, ge=0), svc: UseCaseControlsService = Depends(get_use_case_controls_service), ) -> dict[str, Any]: """Controls for a topic. Atom-grain (Haiku: relevant + sub_topic) wenn vorhanden, sonst master-grain Seed.""" with translate_domain_errors(): return svc.controls_for_use_case(use_case, primary_only, limit, offset, sub_topic)