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