94 lines
3.1 KiB
Python
94 lines
3.1 KiB
Python
"""
|
|
🌍 Location Autocomplete API Endpoint.
|
|
|
|
Provides autocomplete search for postal codes and cities from the
|
|
system.geo_postal_codes table. Used by the frontend SmartLocationInput
|
|
component to let users quickly find and select a zip/city pair.
|
|
|
|
Schema: system.geo_postal_codes
|
|
"""
|
|
import logging
|
|
from typing import List, Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy import select, or_
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.api.deps import get_db
|
|
from app.models.identity.address import GeoPostalCode
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class LocationAutocompleteItem(BaseModel):
|
|
"""Egy találat a település autocomplete keresőben."""
|
|
id: int
|
|
zip_code: str
|
|
city: str
|
|
country_code: str = "HU"
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
@router.get("/locations/autocomplete", response_model=List[LocationAutocompleteItem])
|
|
async def autocomplete_locations(
|
|
q: str = Query(..., min_length=1, max_length=100, description="Keresőszó (irányítószám vagy városrészlet)"),
|
|
country_code: str = Query("HU", max_length=5, description="Országkód szűrő (alapértelmezett: HU)"),
|
|
limit: int = Query(15, ge=1, le=50, description="Max találatok száma"),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""
|
|
GET /api/v1/locations/autocomplete?q=budapest
|
|
|
|
Autocomplete keresés a system.geo_postal_codes táblában.
|
|
A keresés az irányítószámra (zip_code) és a város névre (city) is
|
|
történik ILIKE operátorral.
|
|
|
|
Példák:
|
|
/api/v1/locations/autocomplete?q=1011 -> 1011 Budapest
|
|
/api/v1/locations/autocomplete?q=budapest -> 1011 Budapest, 1012 Budapest, ...
|
|
/api/v1/locations/autocomplete?q=101 -> 1011 Budapest, 1012 Budapest, ...
|
|
/api/v1/locations/autocomplete?q=bud&country_code=AT -> 5020 Salzburg, ...
|
|
"""
|
|
if not q.strip():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="A keresőszó (q) nem lehet üres.",
|
|
)
|
|
|
|
try:
|
|
stmt = (
|
|
select(GeoPostalCode)
|
|
.where(
|
|
GeoPostalCode.country_code == country_code,
|
|
or_(
|
|
GeoPostalCode.zip_code.ilike(f"%{q.strip()}%"),
|
|
GeoPostalCode.city.ilike(f"%{q.strip()}%"),
|
|
),
|
|
)
|
|
.order_by(GeoPostalCode.zip_code, GeoPostalCode.city)
|
|
.limit(limit)
|
|
)
|
|
result = await db.execute(stmt)
|
|
items = result.scalars().all()
|
|
|
|
return [
|
|
LocationAutocompleteItem(
|
|
id=item.id,
|
|
zip_code=item.zip_code,
|
|
city=item.city,
|
|
country_code=item.country_code,
|
|
)
|
|
for item in items
|
|
]
|
|
|
|
except Exception as e:
|
|
logger.error(f"Hiba a location autocomplete során: {e}", exc_info=True)
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="Hiba történt a település keresés során.",
|
|
)
|