"""HTTP routes for parental-leave data: manual entry + scraper status. POST /api/parental-leave/manual — manual entry (always available) GET /api/parental-leave/{personal_data_uuid}/{child_id} — latest snapshot GET /api/parental-leave/{personal_data_uuid}/{child_id}/history — all snapshots Scraper endpoints (POST /api/fetch-data/start, GET /api/fetch-data/poll, POST /api/fetch-data/cancel) are stubs returning 501 until the legal gate clears (ARCHITECTURE.md §2.3). """ from uuid import UUID from fastapi import APIRouter, HTTPException, status from starlette.requests import Request from app.config import settings from app.dependencies import CurrentUserDep, ParentalLeaveRepositoryDep from app.main import limiter from app.models.parental_leave import ( ManualFetchRequest, ParentalLeaveFetchResponse, ) from app.services.fetch_data_service import ManualFetchService from app.utils.errors import ValidationError router = APIRouter(prefix="/api/parental-leave", tags=["parental-leave"]) scraper_router = APIRouter(prefix="/api/fetch-data", tags=["fetch-data"]) _SCRAPER_DISABLED = HTTPException( status.HTTP_501_NOT_IMPLEMENTED, "FK scraping is disabled pending legal/ToS + BankID review (§2.3). " "Use /api/parental-leave/manual for data entry.", ) _SCRAPER_NOT_IMPLEMENTED = HTTPException( status.HTTP_501_NOT_IMPLEMENTED, "FK scraper is not yet implemented.", ) @router.post( "/manual", response_model=ParentalLeaveFetchResponse, status_code=status.HTTP_201_CREATED ) async def save_manual_entry( payload: ManualFetchRequest, repo: ParentalLeaveRepositoryDep, _current_user: CurrentUserDep, ) -> ParentalLeaveFetchResponse: service = ManualFetchService(repo) try: fetch = await service.save_manual_entry( personal_data_uuid=payload.personal_data_uuid, workspace_id=payload.workspace_id, child_id=payload.child_id, total_days=payload.total_days, days_used=payload.days_used, reserved_days_total=payload.reserved_days_total, reserved_days_left=payload.reserved_days_left, transferable_days_left=payload.transferable_days_left, dubbeldagar_left=payload.dubbeldagar_left, sjukpenning_total=payload.sjukpenning_total, sjukpenning_used=payload.sjukpenning_used, lagsta_niva_total=payload.lagsta_niva_total, lagsta_niva_used=payload.lagsta_niva_used, ) except ValidationError as exc: raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, str(exc)) from exc return ParentalLeaveFetchResponse.from_entity(fetch) @router.get("/{personal_data_uuid}/{child_id}", response_model=ParentalLeaveFetchResponse | None) async def get_latest( personal_data_uuid: UUID, child_id: UUID, repo: ParentalLeaveRepositoryDep, _current_user: CurrentUserDep, ) -> ParentalLeaveFetchResponse | None: service = ManualFetchService(repo) fetch = await service.get_latest(personal_data_uuid, child_id) return ParentalLeaveFetchResponse.from_entity(fetch) if fetch else None @router.get( "/{personal_data_uuid}/{child_id}/history", response_model=list[ParentalLeaveFetchResponse] ) async def get_history( personal_data_uuid: UUID, child_id: UUID, repo: ParentalLeaveRepositoryDep, _current_user: CurrentUserDep, ) -> list[ParentalLeaveFetchResponse]: service = ManualFetchService(repo) history = await service.list_history(personal_data_uuid, child_id) return [ParentalLeaveFetchResponse.from_entity(f) for f in history] # Scraper stubs — 501 until legal gate clears (settings.scraper_enabled). @scraper_router.post("/start") @limiter.limit("20/minute") async def start_fetch(request: Request, _current_user: CurrentUserDep) -> None: if not settings.scraper_enabled: raise _SCRAPER_DISABLED raise _SCRAPER_NOT_IMPLEMENTED @scraper_router.get("/poll") @limiter.limit("20/minute") async def poll_fetch(request: Request, _current_user: CurrentUserDep) -> None: if not settings.scraper_enabled: raise _SCRAPER_DISABLED raise _SCRAPER_NOT_IMPLEMENTED @scraper_router.post("/cancel") @limiter.limit("20/minute") async def cancel_fetch(request: Request, _current_user: CurrentUserDep) -> None: if not settings.scraper_enabled: raise _SCRAPER_DISABLED raise _SCRAPER_NOT_IMPLEMENTED