127 lines
3.5 KiB
Python
127 lines
3.5 KiB
Python
|
|
"""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}",
|
||
|
|
)
|