Add POI API routes and Celery task
FastAPI router with CRUD endpoints for POIs, distance calculation trigger, and distance queries. Streaming GeoJSON endpoint now accepts include_poi_distances=true to inject travel times into features. Celery task wraps the distance calculator with progress reporting.
This commit is contained in:
parent
da0a56895d
commit
bd788df9aa
4 changed files with 332 additions and 3 deletions
92
tasks/poi_tasks.py
Normal file
92
tasks/poi_tasks.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
"""Celery tasks for POI distance calculation."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from celery import Task
|
||||
from celery_app import app
|
||||
from database import engine
|
||||
from models.listing import ListingType
|
||||
from repositories.listing_repository import ListingRepository
|
||||
from repositories.poi_repository import POIRepository
|
||||
from services.poi_distance_calculator import calculate_poi_distances
|
||||
|
||||
logger = logging.getLogger("uvicorn.error")
|
||||
|
||||
celery_logger = logging.getLogger("celery.task")
|
||||
if not celery_logger.handlers:
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(logging.Formatter(
|
||||
"%(asctime)s [%(levelname)s] %(name)s: %(message)s"
|
||||
))
|
||||
celery_logger.addHandler(handler)
|
||||
celery_logger.setLevel(logging.INFO)
|
||||
|
||||
|
||||
@app.task(bind=True)
|
||||
def calculate_poi_distances_task(
|
||||
self: Task,
|
||||
poi_id: int,
|
||||
travel_modes: list[str],
|
||||
listing_type: str,
|
||||
listing_ids: list[int] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Background task to calculate distances from listings to a POI.
|
||||
|
||||
Args:
|
||||
poi_id: ID of the PointOfInterest.
|
||||
travel_modes: List of travel modes (WALK, BICYCLE, TRANSIT).
|
||||
listing_type: "BUY" or "RENT".
|
||||
listing_ids: Optional subset of listing IDs.
|
||||
"""
|
||||
celery_logger.info(
|
||||
f"Starting POI distance calculation: poi_id={poi_id}, "
|
||||
f"modes={travel_modes}, type={listing_type}"
|
||||
)
|
||||
|
||||
self.update_state(state="PROGRESS", meta={
|
||||
"phase": "starting",
|
||||
"progress": 0,
|
||||
"message": "Starting distance calculation...",
|
||||
})
|
||||
|
||||
listing_repo = ListingRepository(engine)
|
||||
poi_repo = POIRepository(engine)
|
||||
|
||||
poi = poi_repo.get_poi_by_id(poi_id)
|
||||
if poi is None:
|
||||
celery_logger.error(f"POI {poi_id} not found")
|
||||
return {"error": f"POI {poi_id} not found", "distances_computed": 0}
|
||||
|
||||
lt = ListingType(listing_type)
|
||||
|
||||
def on_progress(completed: int, total: int, message: str) -> None:
|
||||
progress = round(completed / total, 2) if total > 0 else 0
|
||||
self.update_state(state="PROGRESS", meta={
|
||||
"phase": "computing",
|
||||
"progress": progress,
|
||||
"processed": completed,
|
||||
"total": total,
|
||||
"message": message,
|
||||
})
|
||||
|
||||
total = asyncio.run(
|
||||
calculate_poi_distances(
|
||||
listing_repo=listing_repo,
|
||||
poi_repo=poi_repo,
|
||||
poi=poi,
|
||||
travel_modes=travel_modes,
|
||||
listing_type=lt,
|
||||
listing_ids=listing_ids,
|
||||
on_progress=on_progress,
|
||||
)
|
||||
)
|
||||
|
||||
celery_logger.info(f"POI distance calculation complete: {total} distances computed")
|
||||
|
||||
return {
|
||||
"phase": "completed",
|
||||
"progress": 1,
|
||||
"distances_computed": total,
|
||||
"message": f"Computed {total} distances for POI '{poi.name}'",
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue