"""Unified POI service - shared between CLI and HTTP API. This module provides the core business logic for POI operations. Both the CLI (main.py) and HTTP API (api/poi_routes.py) should use these functions. """ from dataclasses import dataclass from models.listing import ListingType from models.poi import PointOfInterest from models.poi_distance import POIDistance from repositories.poi_repository import POIRepository @dataclass class POIResult: """Result of a POI operation.""" poi: PointOfInterest message: str | None = None @dataclass class CalculateResult: """Result of a distance calculation.""" task_id: str | None # None if run synchronously distances_computed: int message: str def get_user_pois(repository: POIRepository, user_id: int) -> list[PointOfInterest]: """Get all POIs for a user.""" return repository.get_pois_for_user(user_id) def get_poi(repository: POIRepository, poi_id: int) -> PointOfInterest | None: """Get a single POI by ID.""" return repository.get_poi_by_id(poi_id) def create_poi( repository: POIRepository, user_id: int, name: str, address: str, latitude: float, longitude: float, ) -> POIResult: """Create a new POI for a user.""" poi = PointOfInterest( user_id=user_id, name=name, address=address, latitude=latitude, longitude=longitude, ) # type: ignore[call-arg] created = repository.create_poi(poi) return POIResult(poi=created, message=f"Created POI '{name}'") def update_poi( repository: POIRepository, poi_id: int, user_id: int, name: str | None = None, address: str | None = None, latitude: float | None = None, longitude: float | None = None, ) -> POIResult | None: """Update an existing POI. Returns None if not found or not owned by user.""" poi = repository.get_poi_by_id(poi_id) if poi is None or poi.user_id != user_id: return None if name is not None: poi.name = name if address is not None: poi.address = address if latitude is not None: poi.latitude = latitude if longitude is not None: poi.longitude = longitude updated = repository.update_poi(poi) return POIResult(poi=updated, message=f"Updated POI '{updated.name}'") def delete_poi(repository: POIRepository, poi_id: int, user_id: int) -> bool: """Delete a POI. Returns False if not found or not owned by user.""" poi = repository.get_poi_by_id(poi_id) if poi is None or poi.user_id != user_id: return False return repository.delete_poi(poi_id) def get_distances_for_listing( repository: POIRepository, listing_id: int, listing_type: ListingType, user_id: int, ) -> list[POIDistance]: """Get all POI distances for a specific listing.""" return repository.get_distances_for_listings( [listing_id], listing_type, user_id ) def trigger_calculation( poi_id: int, travel_modes: list[str], listing_type: ListingType, user_email: str, listing_ids: list[int] | None = None, ) -> CalculateResult: """Trigger a background distance calculation task.""" from tasks.poi_tasks import calculate_poi_distances_task task = calculate_poi_distances_task.delay( poi_id=poi_id, travel_modes=travel_modes, listing_type=listing_type.value, listing_ids=listing_ids, ) return CalculateResult( task_id=task.id, distances_computed=0, message=f"Task {task.id} started for POI {poi_id}", )