249 lines
9.2 KiB
Python
249 lines
9.2 KiB
Python
"""Debug CLI — POI commands."""
|
|
import click
|
|
import httpx
|
|
|
|
from cli._context import CliContext, get_http_headers, output, error_output, resolve_user_id
|
|
from database import engine
|
|
from models.listing import ListingType
|
|
from repositories.poi_repository import POIRepository
|
|
from services import poi_service, task_service
|
|
|
|
|
|
@click.group("pois")
|
|
def pois_group() -> None:
|
|
"""Point of Interest management commands."""
|
|
pass
|
|
|
|
|
|
@pois_group.command("list")
|
|
@click.pass_context
|
|
def list_pois(ctx: click.Context) -> None:
|
|
"""List all POIs for the user."""
|
|
cli_ctx: CliContext = ctx.obj["cli_ctx"]
|
|
|
|
try:
|
|
if cli_ctx.use_http:
|
|
resp = httpx.get(
|
|
f"{cli_ctx.api_base_url}/api/poi",
|
|
headers=get_http_headers(cli_ctx.user_email),
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
else:
|
|
user_id = resolve_user_id(cli_ctx.user_email)
|
|
repo = POIRepository(engine)
|
|
pois = poi_service.get_user_pois(repo, user_id)
|
|
data = [
|
|
{
|
|
"id": p.id,
|
|
"name": p.name,
|
|
"address": p.address,
|
|
"latitude": p.latitude,
|
|
"longitude": p.longitude,
|
|
"created_at": str(p.created_at),
|
|
}
|
|
for p in pois
|
|
]
|
|
|
|
output(data, cli_ctx.json_output)
|
|
except httpx.HTTPStatusError as e:
|
|
error_output(f"HTTP {e.response.status_code}: {e.response.text}", cli_ctx.json_output)
|
|
except Exception as e:
|
|
error_output(str(e), cli_ctx.json_output)
|
|
|
|
|
|
@pois_group.command("create")
|
|
@click.option("--name", required=True, help="POI name")
|
|
@click.option("--address", required=True, help="Address")
|
|
@click.option("--lat", required=True, type=float, help="Latitude")
|
|
@click.option("--lon", required=True, type=float, help="Longitude")
|
|
@click.pass_context
|
|
def create_poi(ctx: click.Context, name: str, address: str, lat: float, lon: float) -> None:
|
|
"""Create a new POI."""
|
|
cli_ctx: CliContext = ctx.obj["cli_ctx"]
|
|
|
|
try:
|
|
if cli_ctx.use_http:
|
|
resp = httpx.post(
|
|
f"{cli_ctx.api_base_url}/api/poi",
|
|
headers=get_http_headers(cli_ctx.user_email),
|
|
json={"name": name, "address": address, "latitude": lat, "longitude": lon},
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
else:
|
|
user_id = resolve_user_id(cli_ctx.user_email)
|
|
repo = POIRepository(engine)
|
|
result = poi_service.create_poi(repo, user_id, name, address, lat, lon)
|
|
data = {
|
|
"id": result.poi.id,
|
|
"name": result.poi.name,
|
|
"address": result.poi.address,
|
|
"latitude": result.poi.latitude,
|
|
"longitude": result.poi.longitude,
|
|
}
|
|
|
|
output(data, cli_ctx.json_output)
|
|
except httpx.HTTPStatusError as e:
|
|
error_output(f"HTTP {e.response.status_code}: {e.response.text}", cli_ctx.json_output)
|
|
except Exception as e:
|
|
error_output(str(e), cli_ctx.json_output)
|
|
|
|
|
|
@pois_group.command("update")
|
|
@click.argument("poi_id", type=int)
|
|
@click.option("--name", default=None, help="New name")
|
|
@click.option("--address", default=None, help="New address")
|
|
@click.option("--lat", default=None, type=float, help="New latitude")
|
|
@click.option("--lon", default=None, type=float, help="New longitude")
|
|
@click.pass_context
|
|
def update_poi(ctx: click.Context, poi_id: int, name: str | None, address: str | None, lat: float | None, lon: float | None) -> None:
|
|
"""Update an existing POI."""
|
|
cli_ctx: CliContext = ctx.obj["cli_ctx"]
|
|
|
|
try:
|
|
if cli_ctx.use_http:
|
|
body: dict[str, str | float] = {}
|
|
if name is not None:
|
|
body["name"] = name
|
|
if address is not None:
|
|
body["address"] = address
|
|
if lat is not None:
|
|
body["latitude"] = lat
|
|
if lon is not None:
|
|
body["longitude"] = lon
|
|
resp = httpx.put(
|
|
f"{cli_ctx.api_base_url}/api/poi/{poi_id}",
|
|
headers=get_http_headers(cli_ctx.user_email),
|
|
json=body,
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
else:
|
|
user_id = resolve_user_id(cli_ctx.user_email)
|
|
repo = POIRepository(engine)
|
|
result = poi_service.update_poi(repo, poi_id, user_id, name, address, lat, lon)
|
|
if result is None:
|
|
error_output("POI not found", cli_ctx.json_output)
|
|
return
|
|
data = {
|
|
"id": result.poi.id,
|
|
"name": result.poi.name,
|
|
"address": result.poi.address,
|
|
"latitude": result.poi.latitude,
|
|
"longitude": result.poi.longitude,
|
|
}
|
|
|
|
output(data, cli_ctx.json_output)
|
|
except httpx.HTTPStatusError as e:
|
|
error_output(f"HTTP {e.response.status_code}: {e.response.text}", cli_ctx.json_output)
|
|
except Exception as e:
|
|
error_output(str(e), cli_ctx.json_output)
|
|
|
|
|
|
@pois_group.command("delete")
|
|
@click.argument("poi_id", type=int)
|
|
@click.pass_context
|
|
def delete_poi(ctx: click.Context, poi_id: int) -> None:
|
|
"""Delete a POI."""
|
|
cli_ctx: CliContext = ctx.obj["cli_ctx"]
|
|
|
|
try:
|
|
if cli_ctx.use_http:
|
|
resp = httpx.delete(
|
|
f"{cli_ctx.api_base_url}/api/poi/{poi_id}",
|
|
headers=get_http_headers(cli_ctx.user_email),
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
else:
|
|
user_id = resolve_user_id(cli_ctx.user_email)
|
|
repo = POIRepository(engine)
|
|
deleted = poi_service.delete_poi(repo, poi_id, user_id)
|
|
if not deleted:
|
|
error_output("POI not found", cli_ctx.json_output)
|
|
return
|
|
data = {"success": True}
|
|
|
|
output(data, cli_ctx.json_output)
|
|
except httpx.HTTPStatusError as e:
|
|
error_output(f"HTTP {e.response.status_code}: {e.response.text}", cli_ctx.json_output)
|
|
except Exception as e:
|
|
error_output(str(e), cli_ctx.json_output)
|
|
|
|
|
|
@pois_group.command("calculate")
|
|
@click.argument("poi_id", type=int)
|
|
@click.option("--travel-modes", required=True, help="Comma-separated: WALK,BICYCLE,TRANSIT")
|
|
@click.option("--type", "-t", "listing_type", required=True, type=click.Choice(["RENT", "BUY"]))
|
|
@click.pass_context
|
|
def calculate_distances(ctx: click.Context, poi_id: int, travel_modes: str, listing_type: str) -> None:
|
|
"""Trigger distance calculation for a POI."""
|
|
cli_ctx: CliContext = ctx.obj["cli_ctx"]
|
|
modes = [m.strip().upper() for m in travel_modes.split(",")]
|
|
lt = ListingType[listing_type]
|
|
|
|
try:
|
|
if cli_ctx.use_http:
|
|
resp = httpx.post(
|
|
f"{cli_ctx.api_base_url}/api/poi/{poi_id}/calculate",
|
|
headers=get_http_headers(cli_ctx.user_email),
|
|
json={"travel_modes": modes, "listing_type": listing_type},
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
else:
|
|
result = poi_service.trigger_calculation(
|
|
poi_id=poi_id,
|
|
travel_modes=modes,
|
|
listing_type=lt,
|
|
user_email=cli_ctx.user_email,
|
|
)
|
|
if result.task_id:
|
|
task_service.add_task_for_user(cli_ctx.user_email, result.task_id)
|
|
data = {"task_id": result.task_id or "", "message": result.message}
|
|
|
|
output(data, cli_ctx.json_output)
|
|
except httpx.HTTPStatusError as e:
|
|
error_output(f"HTTP {e.response.status_code}: {e.response.text}", cli_ctx.json_output)
|
|
except Exception as e:
|
|
error_output(str(e), cli_ctx.json_output)
|
|
|
|
|
|
@pois_group.command("distances")
|
|
@click.argument("listing_id", type=int)
|
|
@click.option("--type", "-t", "listing_type", default="RENT", type=click.Choice(["RENT", "BUY"]))
|
|
@click.pass_context
|
|
def get_distances(ctx: click.Context, listing_id: int, listing_type: str) -> None:
|
|
"""Get POI distances for a specific listing."""
|
|
cli_ctx: CliContext = ctx.obj["cli_ctx"]
|
|
lt = ListingType[listing_type]
|
|
|
|
try:
|
|
if cli_ctx.use_http:
|
|
resp = httpx.get(
|
|
f"{cli_ctx.api_base_url}/api/poi/distances",
|
|
headers=get_http_headers(cli_ctx.user_email),
|
|
params={"listing_id": listing_id, "listing_type": listing_type},
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
else:
|
|
user_id = resolve_user_id(cli_ctx.user_email)
|
|
repo = POIRepository(engine)
|
|
distances = poi_service.get_distances_for_listing(repo, listing_id, lt, user_id)
|
|
data = [
|
|
{
|
|
"poi_id": d.poi_id,
|
|
"travel_mode": d.travel_mode,
|
|
"duration_seconds": d.duration_seconds,
|
|
"distance_meters": d.distance_meters,
|
|
}
|
|
for d in distances
|
|
]
|
|
|
|
output(data, cli_ctx.json_output)
|
|
except httpx.HTTPStatusError as e:
|
|
error_output(f"HTTP {e.response.status_code}: {e.response.text}", cli_ctx.json_output)
|
|
except Exception as e:
|
|
error_output(str(e), cli_ctx.json_output)
|