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

103 lines
3.7 KiB
Python

"""FetchDataService — stable contract for parental-leave data acquisition.
All features (analytics, tips, planning) depend on this interface, never on
FK's DOM or a specific scraper. Two concrete implementations:
- ManualFetchService — always ships, always works (manual user entry)
- FKScraperService — behind settings.scraper_enabled guard (pending legal review)
See ARCHITECTURE.md §2.3 for the legal/ToS gate before shipping scraping.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from datetime import datetime
from uuid import UUID, uuid4
from app.entities.parental_leave import ParentalLeaveFetch, TierData
from app.repositories.parental_leave_repository import ParentalLeaveRepository
from app.utils.errors import ValidationError
class FetchDataService(ABC):
@abstractmethod
async def get_latest(
self, personal_data_uuid: UUID, child_id: UUID
) -> ParentalLeaveFetch | None:
"""Return the most recent snapshot for this parent+child, or None."""
@abstractmethod
async def list_history(
self, personal_data_uuid: UUID, child_id: UUID
) -> list[ParentalLeaveFetch]:
"""Return all historical snapshots, newest first."""
class ManualFetchService(FetchDataService):
"""Persist user-entered FK data as a snapshot with source='manual'."""
def __init__(self, repo: ParentalLeaveRepository) -> None:
self._repo = repo
async def save_manual_entry(
self,
personal_data_uuid: UUID,
workspace_id: UUID,
child_id: UUID,
total_days: int,
days_used: int,
reserved_days_total: int,
reserved_days_left: int,
transferable_days_left: int,
dubbeldagar_left: int,
sjukpenning_total: int,
sjukpenning_used: int,
lagsta_niva_total: int,
lagsta_niva_used: int,
) -> ParentalLeaveFetch:
"""Validate and persist a manual data-entry snapshot."""
days_left = total_days - days_used
if days_left < 0:
raise ValidationError("days_used cannot exceed total_days")
if reserved_days_left > reserved_days_total:
raise ValidationError("reserved_days_left cannot exceed reserved_days_total")
tier_data = TierData(
sjukpenning_total=sjukpenning_total,
sjukpenning_used=sjukpenning_used,
sjukpenning_left=sjukpenning_total - sjukpenning_used,
lagsta_niva_total=lagsta_niva_total,
lagsta_niva_used=lagsta_niva_used,
lagsta_niva_left=lagsta_niva_total - lagsta_niva_used,
)
now = datetime.now()
fetch = ParentalLeaveFetch(
id=uuid4(),
personal_data_uuid=personal_data_uuid,
workspace_id=workspace_id,
child_id=child_id,
source="manual",
fetched_at=now,
total_days=total_days,
days_used=days_used,
days_left=days_left,
reserved_days_total=reserved_days_total,
reserved_days_left=reserved_days_left,
transferable_days_left=transferable_days_left,
dubbeldagar_left=dubbeldagar_left,
tier_data=tier_data,
created_at=now,
updated_at=now,
)
return await self._repo.create(fetch)
async def get_latest(
self, personal_data_uuid: UUID, child_id: UUID
) -> ParentalLeaveFetch | None:
return await self._repo.get_latest_by_parent(personal_data_uuid, child_id)
async def list_history(
self, personal_data_uuid: UUID, child_id: UUID
) -> list[ParentalLeaveFetch]:
return await self._repo.list_history(personal_data_uuid, child_id)