Files
F-r-ldraledigplannerare/backend/app/routers/analytics.py
T
jacobh2 4b802ba0be Give the analytics overview a per-parent, per-year balance series
Each parent projection now carries one entry per calendar year from
birth to the fourth birthday: vacation earned, carried in, planned,
left, carried out and expiring under Semesterlagen's 20-day floor and
5-year carry-over, plus parental-leave days used and left at year end
with the age-4 clamp applied in the birthday year. The overview sums
them into per-year totals. Frontend types, mapper and fixtures follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBRLomdLK9jF7wU7a8AA6H
2026-09-19 19:02:02 +02:00

105 lines
3.6 KiB
Python

"""HTTP routes for analytics / projections."""
from uuid import UUID
from fastapi import APIRouter, HTTPException, status
from app.dependencies import (
ChildRepositoryDep,
ParentalLeaveRepositoryDep,
PersonalDataRepositoryDep,
PlannedLeaveRepositoryDep,
VacationDaysRepositoryDep,
WorkspaceIdDep,
WorkspaceRepositoryDep,
WorkspaceScopeDep,
)
from app.models.analytics import Age4ProjectionResponse, WorkspaceOverviewResponse
from app.services.analytics_service import AnalyticsService
from app.utils.errors import NotFoundError
router = APIRouter(prefix="/api/analytics", tags=["analytics"])
def _make_service(
leave_repo: ParentalLeaveRepositoryDep,
planned_repo: PlannedLeaveRepositoryDep,
child_repo: ChildRepositoryDep,
pd_repo: PersonalDataRepositoryDep,
workspace_repo: WorkspaceRepositoryDep,
vacation_repo: VacationDaysRepositoryDep,
) -> AnalyticsService:
return AnalyticsService(
leave_repo, planned_repo, child_repo, pd_repo, workspace_repo, vacation_repo
)
@router.get("/{workspace_id}/overview", response_model=WorkspaceOverviewResponse)
async def get_overview(
workspace_id: WorkspaceIdDep,
child_id: UUID,
leave_repo: ParentalLeaveRepositoryDep,
planned_repo: PlannedLeaveRepositoryDep,
child_repo: ChildRepositoryDep,
pd_repo: PersonalDataRepositoryDep,
workspace_repo: WorkspaceRepositoryDep,
vacation_repo: VacationDaysRepositoryDep,
) -> WorkspaceOverviewResponse:
service = _make_service(
leave_repo, planned_repo, child_repo, pd_repo, workspace_repo, vacation_repo
)
try:
overview = await service.get_overview(workspace_id, child_id)
except NotFoundError as exc:
raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc)) from exc
return WorkspaceOverviewResponse.from_entity(overview)
@router.get("/{workspace_id}/projection", response_model=WorkspaceOverviewResponse)
async def get_projection(
workspace_id: WorkspaceIdDep,
child_id: UUID,
leave_repo: ParentalLeaveRepositoryDep,
planned_repo: PlannedLeaveRepositoryDep,
child_repo: ChildRepositoryDep,
pd_repo: PersonalDataRepositoryDep,
workspace_repo: WorkspaceRepositoryDep,
vacation_repo: VacationDaysRepositoryDep,
) -> WorkspaceOverviewResponse:
service = _make_service(
leave_repo, planned_repo, child_repo, pd_repo, workspace_repo, vacation_repo
)
try:
overview = await service.get_overview(workspace_id, child_id)
except NotFoundError as exc:
raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc)) from exc
return WorkspaceOverviewResponse.from_entity(overview)
@router.get(
"/parent/{personal_data_uuid}/child/{child_id}/age4", response_model=Age4ProjectionResponse
)
async def get_age4_projection(
personal_data_uuid: UUID,
child_id: UUID,
leave_repo: ParentalLeaveRepositoryDep,
planned_repo: PlannedLeaveRepositoryDep,
child_repo: ChildRepositoryDep,
pd_repo: PersonalDataRepositoryDep,
workspace_repo: WorkspaceRepositoryDep,
vacation_repo: VacationDaysRepositoryDep,
scope: WorkspaceScopeDep,
) -> Age4ProjectionResponse:
await scope.check_member(personal_data_uuid)
await scope.check_child(child_id)
service = _make_service(
leave_repo, planned_repo, child_repo, pd_repo, workspace_repo, vacation_repo
)
try:
proj = await service.get_days_left_at_age_4(
personal_data_uuid, child_id, workspace_id=scope.workspace_id
)
except NotFoundError as exc:
raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc)) from exc
return Age4ProjectionResponse.from_entity(proj)