feat: add governed cross-module reporting

This commit is contained in:
2026-08-02 05:29:38 +02:00
parent eba35edd3c
commit 0abcc2455e
21 changed files with 2497 additions and 38 deletions
+143
View File
@@ -44,12 +44,23 @@ from govoplan_reporting.backend.operations import (
upsert_saved_view,
upsert_schedule,
)
from govoplan_reporting.backend.provider_reports import (
ProviderReportError,
execute_provider_report,
export_provider_execution,
get_provider_execution,
list_provider_exports,
list_provider_reports,
provider_parameter_options,
)
from govoplan_reporting.backend.query_engine import ReportingQueryError
from govoplan_reporting.backend.schemas import (
DefinitionUpdateRequest,
DefinitionWriteRequest,
ImportAssessmentRequest,
PublicationRequest,
ProviderReportExecutionRequest,
ProviderReportExportRequest,
QualityRunRequest,
ReportExecutionRequest,
SavedViewWriteRequest,
@@ -60,6 +71,138 @@ from govoplan_reporting.backend.schemas import (
def create_router(registry: object | None) -> APIRouter:
router = APIRouter(prefix="/reporting", tags=["reporting"])
@router.get("/provider-reports")
def api_list_provider_reports(
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(get_api_principal),
) -> dict[str, object]:
_require(principal, READ_SCOPE)
try:
return list_provider_reports(session, principal, registry=registry)
except (ProviderReportError, TypeError, ValueError) as exc:
raise _error(exc) from exc
@router.get(
"/provider-reports/{provider_id}/{report_id}/parameters/{parameter_key}/options"
)
def api_provider_parameter_options(
provider_id: str,
report_id: str,
parameter_key: str,
query: str = "",
limit: int = Query(default=100, ge=1, le=200),
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(get_api_principal),
) -> dict[str, object]:
_require(principal, READ_SCOPE)
try:
options = provider_parameter_options(
session,
principal,
registry=registry,
provider_id=provider_id,
report_id=report_id,
parameter_key=parameter_key,
query=query,
limit=limit,
)
except (ProviderReportError, PermissionError, LookupError) as exc:
raise _error(exc) from exc
return {"options": [item.to_dict() for item in options]}
@router.post("/provider-reports/{provider_id}/{report_id}/executions")
def api_execute_provider_report(
provider_id: str,
report_id: str,
payload: ProviderReportExecutionRequest,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(get_api_principal),
) -> dict[str, object]:
_require(principal, RUN_SCOPE)
try:
result = execute_provider_report(
session,
principal,
registry=registry,
provider_id=provider_id,
report_id=report_id,
**payload.model_dump(),
)
session.commit()
except (ProviderReportError, PermissionError, LookupError, ValueError) as exc:
session.rollback()
raise _error(exc) from exc
return result
@router.get("/provider-executions/{execution_id}")
def api_get_provider_execution(
execution_id: str,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(get_api_principal),
) -> dict[str, object]:
_require(principal, RUN_SCOPE)
try:
result = get_provider_execution(
session,
principal,
registry=registry,
execution_id=execution_id,
)
except (ProviderReportError, PermissionError, LookupError) as exc:
raise _error(exc) from exc
if result is None:
raise HTTPException(
status_code=404, detail="Provider report execution not found"
)
return result
@router.get("/provider-executions/{execution_id}/exports")
def api_list_provider_exports(
execution_id: str,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(get_api_principal),
) -> dict[str, object]:
_require(principal, RUN_SCOPE)
try:
return {
"exports": list(
list_provider_exports(
session,
principal,
registry=registry,
execution_id=execution_id,
)
)
}
except (ProviderReportError, PermissionError, LookupError) as exc:
raise _error(exc) from exc
@router.post("/provider-executions/{execution_id}/exports")
def api_export_provider_execution(
execution_id: str,
payload: ProviderReportExportRequest,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(get_api_principal),
) -> Response:
_require(principal, RUN_SCOPE)
try:
content, media_type, filename = export_provider_execution(
session,
principal,
registry=registry,
execution_id=execution_id,
**payload.model_dump(),
)
session.commit()
except (ProviderReportError, PermissionError, LookupError) as exc:
session.rollback()
raise _error(exc) from exc
return Response(
content=content,
media_type=media_type,
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
@router.get("/definitions")
def api_list_definitions(
definition_kind: list[str] | None = Query(default=None),