Download backend/api/endpoints/user_layers.py from GerardCB/perch: direct link, hf CLI and curl.
- Browser
- Download file 3.11 kB
-
https://huggingface.co/spaces/GerardCB/perch/resolve/main/backend/api/endpoints/user_layers.py
- Command line
-
hf download hf://spaces/GerardCB/perch/backend/api/endpoints/user_layers.py
-
curl -L -o user_layers.py https://huggingface.co/spaces/GerardCB/perch/resolve/main/backend/api/endpoints/user_layers.py
3.11 kB
| from fastapi import APIRouter, HTTPException | |
| from pydantic import BaseModel | |
| from typing import Dict, Any, Optional | |
| import uuid | |
| import datetime | |
| from backend.core.geo_engine import get_geo_engine | |
| from backend.core.session_store import get_session_store | |
| router = APIRouter() | |
| # Must match executor.py's DEFAULT_SESSION_ID | |
| DEFAULT_SESSION_ID = "default-session" | |
| class UserLayerRequest(BaseModel): | |
| geojson: Dict[str, Any] | |
| name: str = "User Draw" | |
| description: Optional[str] = None | |
| class UserLayerResponse(BaseModel): | |
| id: str | |
| name: str # The table name in DuckDB | |
| display_name: str | |
| # Registered with and without the trailing slash. Starlette only redirects | |
| # "/layers/user" -> "/layers/user/" when nothing else matches, and the | |
| # catch-all that serves the frontend matches everything — so in the deployed | |
| # build the redirect never happened and a POST came back 405. | |
| async def register_user_layer(request: UserLayerRequest): | |
| """ | |
| Registers a new user-drawn layer. | |
| Saves GeoJSON to backend and makes it available for SQL querying. | |
| """ | |
| try: | |
| layer_id = str(uuid.uuid4()) | |
| geo_engine = get_geo_engine() | |
| session_store = get_session_store() # Use singleton | |
| # Register in DuckDB | |
| table_name = geo_engine.register_layer(layer_id, request.geojson) | |
| # Register in Session Store | |
| layer_metadata = { | |
| "id": layer_id, | |
| "name": request.name, | |
| "table_name": table_name, | |
| "type": "user_draw", | |
| "timestamp": datetime.datetime.now().isoformat(), | |
| "description": request.description | |
| } | |
| # Use the same session ID as executor.py | |
| session_store.add_layer(DEFAULT_SESSION_ID, layer_metadata) | |
| return UserLayerResponse( | |
| id=layer_id, | |
| name=table_name, | |
| display_name=request.name | |
| ) | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def delete_user_layer(layer_id: str): | |
| """ | |
| Deletes a user-drawn layer from DuckDB and session store. | |
| Frees backend resources and prevents agent from querying the layer. | |
| """ | |
| try: | |
| geo_engine = get_geo_engine() | |
| session_store = get_session_store() | |
| # Remove from DuckDB | |
| dropped = geo_engine.drop_layer(layer_id) | |
| # Remove from session store | |
| removed = session_store.remove_layer(DEFAULT_SESSION_ID, layer_id) | |
| if not dropped and not removed: | |
| raise HTTPException(status_code=404, detail=f"Layer {layer_id} not found") | |
| return {"status": "deleted", "layer_id": layer_id} | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| raise HTTPException(status_code=500, detail=str(e)) | |