Files
F-r-ldraledigplannerare/backend/app/routers/fetch_data.py
T
2026-06-01 16:48:21 +02:00

120 lines
4.3 KiB
Python

"""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