añadido los estados de reporte
This commit is contained in:
@@ -35,6 +35,11 @@ class ReportRepository(ABC):
|
||||
"""Actualiza la visibilidad de un reporte"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def update_estado(self, report_id: str, new_estado: str) -> None:
|
||||
"""Actualiza el estado de un reporte"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete(self, report_id: str) -> bool:
|
||||
"""Elimina un reporte"""
|
||||
|
||||
@@ -19,7 +19,8 @@ class CreateReport:
|
||||
|
||||
def execute(self, id_usuario: int, tipo_reporte: int, descripcion: str,
|
||||
ubicacion: Optional[str] = None, lat: Optional[float] = None,
|
||||
lng: Optional[float] = None, image_filename: Optional[str] = None) -> Dict[str, Any]:
|
||||
lng: Optional[float] = None, image_filename: Optional[str] = None,
|
||||
estado: str = "en proceso") -> Dict[str, Any]:
|
||||
"""
|
||||
Sends a create report message to RabbitMQ.
|
||||
Valida previamente:
|
||||
@@ -44,6 +45,14 @@ class CreateReport:
|
||||
"message": "El tipo de reporte debe estar entre 1 y 5"
|
||||
}
|
||||
|
||||
# Validación: estado válido
|
||||
estados_validos = ["en proceso", "no resuelto", "resuelto"]
|
||||
if estado not in estados_validos:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"El estado del reporte debe ser uno de: {', '.join(estados_validos)}"
|
||||
}
|
||||
|
||||
# Validación: usuario existe (CRÍTICO)
|
||||
try:
|
||||
user = self.user_repo.find_by_id(id_usuario)
|
||||
@@ -73,6 +82,7 @@ class CreateReport:
|
||||
lng=lng,
|
||||
image_filename=image_filename,
|
||||
visibilidad=50.0, # Visibilidad inicial neutral
|
||||
estado=estado,
|
||||
fecha_creacion=fecha_creacion.isoformat()
|
||||
)
|
||||
|
||||
@@ -244,3 +254,57 @@ class DeleteReport:
|
||||
"status": "error",
|
||||
"message": "Error al enviar eliminación del reporte a la cola de procesamiento"
|
||||
}
|
||||
|
||||
class UpdateReportStatus:
|
||||
"""Use case para actualizar el estado de un reporte"""
|
||||
def __init__(self, repo: ReportRepository):
|
||||
if not isinstance(repo, ReportRepository):
|
||||
raise TypeError("repo must implement ReportRepository")
|
||||
self.repo = repo
|
||||
|
||||
def execute(self, report_id: str, new_estado: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Actualiza el estado de un reporte.
|
||||
Valida previamente:
|
||||
- Reporte existe
|
||||
- Estado es válido
|
||||
|
||||
Returns:
|
||||
Dictionary with status and message
|
||||
"""
|
||||
# Validación: estado válido
|
||||
estados_validos = ["en proceso", "no resuelto", "resuelto"]
|
||||
if new_estado not in estados_validos:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"El estado del reporte debe ser uno de: {', '.join(estados_validos)}"
|
||||
}
|
||||
|
||||
# Validación: reporte existe
|
||||
try:
|
||||
report = self.repo.find_by_id(report_id)
|
||||
if not report:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Reporte con ID {report_id} no existe"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Error al buscar reporte: {str(e)}"
|
||||
}
|
||||
|
||||
# Actualizar estado
|
||||
try:
|
||||
self.repo.update_estado(report_id, new_estado)
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Estado del reporte actualizado a '{new_estado}'",
|
||||
"id_reporte": report_id,
|
||||
"nuevo_estado": new_estado
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Error al actualizar estado del reporte: {str(e)}"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from typing import Optional, Literal
|
||||
|
||||
@dataclass
|
||||
class Report:
|
||||
@@ -14,4 +14,5 @@ class Report:
|
||||
lng: Optional[float] = None
|
||||
image_filename: Optional[str] = None # Nombre del archivo de imagen WebP
|
||||
visibilidad: float = 0.0 # 0-100 (puntuación comunitaria)
|
||||
estado: Literal["en proceso", "no resuelto", "resuelto"] = "en proceso" # Estado del reporte
|
||||
fecha_creacion: Optional[datetime] = None
|
||||
|
||||
@@ -23,6 +23,7 @@ class ReportRepositoryMongo(ReportRepository):
|
||||
"lng": report.lng,
|
||||
"image_filename": report.image_filename,
|
||||
"visibilidad": report.visibilidad,
|
||||
"estado": report.estado,
|
||||
"fecha_creacion": report.fecha_creacion or datetime.utcnow()
|
||||
}
|
||||
result = self.collection.insert_one(report_dict)
|
||||
@@ -59,6 +60,13 @@ class ReportRepositoryMongo(ReportRepository):
|
||||
{"$set": {"visibilidad": new_visibility}}
|
||||
)
|
||||
|
||||
def update_estado(self, report_id: str, new_estado: str) -> None:
|
||||
"""Actualiza el estado de un reporte"""
|
||||
self.collection.update_one(
|
||||
{"id_reporte": report_id},
|
||||
{"$set": {"estado": new_estado}}
|
||||
)
|
||||
|
||||
def delete(self, report_id: str) -> bool:
|
||||
"""Elimina un reporte"""
|
||||
result = self.collection.delete_one({"id_reporte": report_id})
|
||||
@@ -83,5 +91,6 @@ class ReportRepositoryMongo(ReportRepository):
|
||||
lng=doc.get("lng"),
|
||||
image_filename=doc.get("image_filename"),
|
||||
visibilidad=doc.get("visibilidad"),
|
||||
estado=doc.get("estado", "en proceso"),
|
||||
fecha_creacion=doc.get("fecha_creacion")
|
||||
)
|
||||
|
||||
@@ -65,6 +65,7 @@ class ReportMessage:
|
||||
lng: Optional[float] = None
|
||||
image_filename: Optional[str] = None # Nombre del archivo de imagen guardado
|
||||
visibilidad: Optional[float] = None
|
||||
estado: Optional[str] = None # Estado del reporte: "en proceso", "no resuelto", "resuelto"
|
||||
fecha_creacion: Optional[str] = None # ISO format datetime string
|
||||
penalize_author: Optional[bool] = None # For update_visibility event
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from fastapi import APIRouter, HTTPException, status, UploadFile, File, Form
|
||||
from infrastructure.api.reports.schemas import ReportCreateRequest, ReportUpdateVisibilityRequest, ReportResponse
|
||||
from infrastructure.api.reports.schemas import ReportCreateRequest, ReportUpdateVisibilityRequest, ReportUpdateStatusRequest, ReportResponse
|
||||
from application.services.report_services import (
|
||||
CreateReport, GetReportById, GetReportsByUser, ListAllReports,
|
||||
UpdateReportVisibility, GetShadowbannedReports, DeleteReport
|
||||
UpdateReportVisibility, GetShadowbannedReports, DeleteReport, UpdateReportStatus
|
||||
)
|
||||
from infrastructure.adapters.persistence.report_repository_mongo import ReportRepositoryMongo
|
||||
from infrastructure.adapters.persistence.user_repository_sql import UserRepositorySQL
|
||||
@@ -28,6 +28,7 @@ def _report_to_response(report) -> dict:
|
||||
"lng": report.lng,
|
||||
"image_url": image_storage.get_image_url(report.image_filename) if report.image_filename else None,
|
||||
"visibilidad": report.visibilidad,
|
||||
"estado": report.estado,
|
||||
"fecha_creacion": report.fecha_creacion
|
||||
}
|
||||
|
||||
@@ -39,6 +40,7 @@ async def create_report(
|
||||
ubicacion: Optional[str] = Form(None),
|
||||
lat: Optional[float] = Form(None),
|
||||
lng: Optional[float] = Form(None),
|
||||
estado: str = Form("en proceso"),
|
||||
file: Optional[UploadFile] = File(None)
|
||||
):
|
||||
"""Crea un nuevo reporte - envía a cola de procesamiento con validaciones previas"""
|
||||
@@ -57,7 +59,8 @@ async def create_report(
|
||||
ubicacion=ubicacion,
|
||||
lat=lat,
|
||||
lng=lng,
|
||||
image_filename=image_filename
|
||||
image_filename=image_filename,
|
||||
estado=estado
|
||||
)
|
||||
|
||||
if result["status"] == "error":
|
||||
@@ -225,3 +228,40 @@ async def delete_report(report_id: str):
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error interno del servidor"
|
||||
)
|
||||
|
||||
@router.put("/{report_id}/status", status_code=status.HTTP_200_OK)
|
||||
async def update_report_status(report_id: str, status_data: ReportUpdateStatusRequest):
|
||||
"""Actualiza el estado de un reporte"""
|
||||
try:
|
||||
update_use_case = UpdateReportStatus(report_repo)
|
||||
result = update_use_case.execute(
|
||||
report_id=report_id,
|
||||
new_estado=status_data.estado
|
||||
)
|
||||
|
||||
if result["status"] == "error":
|
||||
message = result["message"]
|
||||
if "no existe" in message:
|
||||
# 404 Not Found: reporte no existe
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=message
|
||||
)
|
||||
else:
|
||||
# 400 Bad Request: error de validación
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=message
|
||||
)
|
||||
|
||||
# 200 OK: estado actualizado correctamente
|
||||
return result
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error al actualizar estado del reporte {report_id}: {e}", exc_info=True)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Error interno del servidor"
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from typing import Optional, Literal
|
||||
from fastapi import Form, UploadFile, File
|
||||
|
||||
class ReportCreateRequest(BaseModel):
|
||||
@@ -11,6 +11,7 @@ class ReportCreateRequest(BaseModel):
|
||||
ubicacion: Optional[str] = None
|
||||
lat: Optional[float] = None
|
||||
lng: Optional[float] = None
|
||||
estado: Literal["en proceso", "no resuelto", "resuelto"] = "en proceso" # Estado del reporte
|
||||
# file se recibe como UploadFile en el endpoint, no en el modelo
|
||||
|
||||
class ReportUpdateVisibilityRequest(BaseModel):
|
||||
@@ -18,6 +19,10 @@ class ReportUpdateVisibilityRequest(BaseModel):
|
||||
new_visibility: float
|
||||
penalize_author: bool = False
|
||||
|
||||
class ReportUpdateStatusRequest(BaseModel):
|
||||
"""Solicitud para actualizar el estado de un reporte"""
|
||||
estado: Literal["en proceso", "no resuelto", "resuelto"]
|
||||
|
||||
class ReportResponse(BaseModel):
|
||||
"""Respuesta con datos de reporte"""
|
||||
id_reporte: str
|
||||
@@ -29,6 +34,7 @@ class ReportResponse(BaseModel):
|
||||
lng: Optional[float]
|
||||
image_url: Optional[str] = None # URL pública para acceder a la imagen
|
||||
visibilidad: float
|
||||
estado: Literal["en proceso", "no resuelto", "resuelto"] = "en proceso" # Estado del reporte
|
||||
fecha_creacion: Optional[datetime]
|
||||
|
||||
class Config:
|
||||
|
||||
Reference in New Issue
Block a user