vistas de ciudadano, escalar animaciones de mascota, implementacion de chatbot para concientizacion, modificacion de datos de ciudadano, modificacion de vista principal

This commit is contained in:
shinra32
2026-05-23 05:03:05 -06:00
parent 89dcc6250b
commit ca076607c7
39 changed files with 2909 additions and 560 deletions

View File

@@ -0,0 +1,132 @@
"""Reportes ciudadanos sobre unidades (incidents)."""
import uuid
from typing import Optional
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from app.core.deps import get_current_user
from app.core.supabase_client import supabase_admin
from app.schemas.incidents import (
IncidentOut,
UnitPublic,
)
router = APIRouter(prefix="/incidents", tags=["incidents"])
BUCKET = "incident-photos"
VALID_CATEGORIES = {"derrame", "dano_propiedad", "conducta", "no_recoleccion", "otro"}
@router.get("/units", response_model=list[UnitPublic])
def list_units(_user: dict = Depends(get_current_user)):
"""Lista unidades activas para que el ciudadano elija al reportar."""
try:
res = (
supabase_admin.table("units")
.select("id, plate, status")
.eq("status", "active")
.order("id")
.execute()
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error al listar unidades: {e}")
return [UnitPublic(**r) for r in (res.data or [])]
def _upload_photo(user_id: str, photo: UploadFile) -> str:
ext = (photo.filename or "").rsplit(".", 1)[-1].lower() or "jpg"
if ext not in {"jpg", "jpeg", "png", "webp"}:
ext = "jpg"
path = f"{user_id}/{uuid.uuid4().hex}.{ext}"
content = photo.file.read()
content_type = photo.content_type or f"image/{ext}"
try:
supabase_admin.storage.from_(BUCKET).upload(
path=path,
file=content,
file_options={"content-type": content_type, "upsert": "false"},
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error al subir foto: {e}")
try:
public = supabase_admin.storage.from_(BUCKET).get_public_url(path)
return public if isinstance(public, str) else str(public)
except Exception:
return path
@router.post("", response_model=IncidentOut, status_code=201)
async def create_incident(
category: str = Form(...),
description: str = Form(...),
unit_id: Optional[int] = Form(None),
photo: Optional[UploadFile] = File(None),
current_user: dict = Depends(get_current_user),
):
if category not in VALID_CATEGORIES:
raise HTTPException(status_code=400, detail="Categoría inválida")
if len(description.strip()) < 3:
raise HTTPException(status_code=400, detail="Descripción demasiado corta")
user_id = current_user["user_id"]
photo_url: Optional[str] = None
if photo is not None and photo.filename:
photo_url = _upload_photo(user_id, photo)
payload = {
"user_id": user_id,
"unit_id": unit_id,
"category": category,
"description": description.strip(),
"photo_url": photo_url,
}
try:
res = supabase_admin.table("incidents").insert(payload).execute()
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error al guardar reporte: {e}")
row = (res.data or [{}])[0]
return IncidentOut(
id=row.get("id"),
user_id=row.get("user_id"),
unit_id=row.get("unit_id"),
category=row.get("category"),
description=row.get("description"),
photo_url=row.get("photo_url"),
status=row.get("status") or "open",
created_at=row.get("created_at") and str(row["created_at"]),
)
@router.get("/me", response_model=list[IncidentOut])
def my_incidents(current_user: dict = Depends(get_current_user)):
try:
res = (
supabase_admin.table("incidents")
.select("*")
.eq("user_id", current_user["user_id"])
.order("created_at", desc=True)
.execute()
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error al listar reportes: {e}")
out: list[IncidentOut] = []
for row in res.data or []:
out.append(
IncidentOut(
id=row.get("id"),
user_id=row.get("user_id"),
unit_id=row.get("unit_id"),
category=row.get("category"),
description=row.get("description"),
photo_url=row.get("photo_url"),
status=row.get("status") or "open",
created_at=row.get("created_at") and str(row["created_at"]),
)
)
return out