Initial commit
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user