0a6e57ac02
2-Pass-Haiku-Klassifikation (konservativ + Re-Confirm jeder Nicht-unternehmen- Einstufung) der Review-Tier-Atome: wer muss die Pflicht erfuellen? - Migration 155: atom_classification.addressee (unternehmen/oeffentliche_stelle/ aufsichtsbefugnis/staat_eu/dritter/meta), additiv, kein CHECK. [migration-approved] - Service: addressee + applicable + is_gov pro Control; include_out_of_scope-Param (Default false -> out-of-scope advisory ausgeblendet, NIE geloescht); out_of_scope_count. Pure Helper addressee_applicable/addressee_is_gov (+ Tests). - Route: optionaler include_out_of_scope-Query (contract-safe, additiv). - Frontend: GOV-Chip (additiv) + "kein Kunden-Pruefaspekt"-Chip + 1-Klick-Toggle zum Einblenden der out-of-scope-Atome. Daten: 40.859 Adressat-Tags auf macmini geladen (81% applicable, 19% advisory, 3.146 GOV). Konservativ: NULL/Unklar = applicable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
2.8 KiB
Python
76 lines
2.8 KiB
Python
"""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"),
|
|
tier: str = Query(
|
|
"core",
|
|
pattern="^(core|all)$",
|
|
description="atom-grain: 'core'=nur validierte Kern-Pflichten (Default), "
|
|
"'all'=alle inkl. 'zur Prüfung'-Stufe",
|
|
),
|
|
include_out_of_scope: bool = Query(
|
|
False,
|
|
description="atom-grain: out-of-scope-Adressaten (Aufsichtsbefugnis/"
|
|
"Mitgliedstaat/Dritter/meta) einblenden (Default: advisory ausgeblendet)",
|
|
),
|
|
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, tier,
|
|
include_out_of_scope,
|
|
)
|