""" Aplicación FastAPI - Endpoints """ import logging from fastapi import FastAPI, HTTPException, Query from fastapi.responses import JSONResponse from models import RegistroRequest, RegistroResponse, CalificacionRequest, ActionRequest, ActionResponse, FunelCompraRequest, FunelCompraResponse from funciones import crear_registro, obtener_registros, actualizar_calificacion, crear_o_actualizar_action, obtener_action, obtener_todas_acciones, crear_evento_funel, obtener_eventos_funel, obtener_todos_eventos_funel from datetime import datetime # Configurar logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) app = FastAPI( title="API Imagen Log", description="API para gestionar registro de usuarios", version="1.0.0" ) @app.get("/", tags=["General"]) async def root(): """Endpoint raíz de bienvenida""" logger.info("📍 Llamado a endpoint raíz /") return { "mensaje": "Bienvenido a la API de Imagen Log", "version": "1.0.0", "documentacion": "/docs" } @app.get("/health", tags=["General"]) async def health(): """Verifica el estado de la API""" logger.info("✅ Health check") return { "status": "healthy", "timestamp": datetime.now().isoformat() } @app.post("/registrar", response_model=dict, tags=["Registros"]) async def registrar(datos: RegistroRequest): """ Crea un nuevo registro de usuario. **Parámetros requeridos:** - uid: UID de Firestore - display_name: Nombre a mostrar del usuario - pais: País del usuario - correo: Email del usuario **Parámetros opcionales:** - fecha_registro: Fecha de registro (default: ahora) - usos: Cantidad de usos (default: 0) - prompt: Prompt utilizado - prompt_type: Tipo de prompt - prompt_eval: Evaluación del prompt procesado - estilo: Estilo para la generación - estilo_agregado: Estilo adicional - calificacion: Calificación numérica - proveedor: Proveedor del servicio - seed: Seed para generación **Respuesta:** Retorna los datos del registro creado con su ID único """ logger.info(f"📝 Nuevo registro - UID: {datos.uid}, Email: {datos.correo}, Prompt: {datos.prompt}, Tipo: {datos.prompt_type}, Eval: {datos.prompt_eval}, Estilo: {datos.estilo}, Estilo Agregado: {datos.estilo_agregado}, Calificación: {datos.calificacion}") resultado = crear_registro(datos) if resultado["success"]: logger.info(f"✅ Registro creado exitosamente - ID: {resultado['data']['id']}, UID: {datos.uid}") return { "success": True, "message": "Registro creado exitosamente", "data": resultado["data"] } else: logger.error(f"❌ Error al crear registro: {resultado['error']}") raise HTTPException( status_code=400, detail=resultado["error"] ) @app.get("/registros", tags=["Registros"]) async def obtener_todos_registros( limit: int = Query(10, ge=1, le=100, description="Máximo registros a retornar"), offset: int = Query(0, ge=0, description="Desplazamiento para paginación") ): """ Obtiene todos los registros con paginación. **Parámetros:** - limit: Cantidad máxima de registros (1-100, default 10) - offset: Desplazamiento para paginación (default 0) **Respuesta:** Lista de registros con información de paginación """ logger.info(f"📚 Obteniendo registros - Limit: {limit}, Offset: {offset}") resultado = obtener_registros(limit, offset) if resultado["success"]: logger.info(f"✅ Se obtuvieron {len(resultado['data'])} registros (Total: {resultado['total']})") return { "success": True, "data": resultado["data"], "pagination": { "total": resultado["total"], "limit": resultado["limit"], "offset": resultado["offset"], "page": (resultado["offset"] // resultado["limit"]) + 1 } } else: logger.error(f"❌ Error al obtener registros: {resultado['error']}") raise HTTPException( status_code=500, detail=resultado["error"] ) @app.post("/guardar-calificacion", tags=["Registros"]) async def guardar_calificacion(datos: CalificacionRequest): """ Actualiza la calificación de un registro existente. **Parámetros requeridos:** - id: ID del registro a calificar - calificacion: Calificación numérica a asignar **Respuesta:** Retorna los datos del registro actualizado """ logger.info(f"⭐ Guardando calificación - ID: {datos.id}, Calificación: {datos.calificacion}") resultado = actualizar_calificacion(datos.id, datos.calificacion) if resultado["success"]: logger.info(f"✅ Calificación guardada exitosamente - ID: {datos.id}") return { "success": True, "message": "Calificación guardada exitosamente", "data": resultado["data"] } else: logger.error(f"❌ Error al guardar calificación: {resultado['error']}") raise HTTPException( status_code=400, detail=resultado["error"] ) @app.post("/action", response_model=dict, tags=["Actions"]) async def crear_actualizar_action(datos: ActionRequest): """ Crea o actualiza un registro de usuario en la tabla action_call. **Parámetros requeridos:** - usuario: Nombre de usuario único **Parámetros opcionales:** - uid: UID de Firestore - displayName: Nombre a mostrar - email: Email del usuario - action_call: Llamada a acción (boolean) - country_header: País del header - country_ip: País de la IP - creditos: Créditos disponibles - esta_hora: Usos esta hora - explicit_counter: Contador explícito - fecha_registro: Fecha de registro - gaClient: Cliente de Google Analytics - open_use: Uso abierto (boolean) - ritmo: Ritmo de uso - streak: Racha de usos - ultima_generacion_hora: Última generación esta hora - ultimo_uso: Último uso - usos: Total de usos **Respuesta:** Retorna los datos del action creado/actualizado """ logger.info(f"👤 Guardando action - Usuario: {datos.usuario}, UID: {datos.uid}, Email: {datos.email}") logger.info(f"📊 Datos: Action_Call: {datos.action_call}, Trigger: {datos.trigger_action}, Country: {datos.country_header}, Créditos: {datos.creditos}, Usos: {datos.usos}, Ritmo: {datos.ritmo}, Streak: {datos.streak}") resultado = crear_o_actualizar_action(datos) if resultado["success"]: logger.info(f"✅ Action guardado exitosamente - Usuario: {datos.usuario}, ID generado/actualizado") return { "success": True, "message": "Action guardado exitosamente", "data": resultado["data"] } else: logger.error(f"❌ Error al guardar action: {resultado['error']}") raise HTTPException( status_code=400, detail=resultado["error"] ) @app.get("/action/{usuario}", response_model=dict, tags=["Actions"]) async def obtener_action_endpoint(usuario: str): """ Obtiene un registro de usuario específico de la tabla action_call. **Parámetros:** - usuario: Nombre de usuario a buscar **Respuesta:** Retorna los datos del action """ logger.info(f"🔍 Buscando action - Usuario: {usuario}") resultado = obtener_action(usuario) if resultado["success"]: logger.info(f"✅ Action encontrado - Usuario: {usuario}, Email: {resultado['data'].get('email')}, Usos: {resultado['data'].get('usos')}, Créditos: {resultado['data'].get('creditos')}") return { "success": True, "data": resultado["data"] } else: logger.error(f"❌ Action no encontrado: {resultado['error']}") raise HTTPException( status_code=404, detail=resultado["error"] ) @app.get("/actions", tags=["Actions"]) async def obtener_acciones( limit: int = Query(10, ge=1, le=100, description="Máximo registros a retornar"), offset: int = Query(0, ge=0, description="Desplazamiento para paginación") ): """ Obtiene todos los registros de la tabla action_call con paginación. **Parámetros:** - limit: Cantidad máxima de registros (1-100, default 10) - offset: Desplazamiento para paginación (default 0) **Respuesta:** Lista de acciones con información de paginación """ logger.info(f"📚 Obteniendo actions - Limit: {limit}, Offset: {offset}, Página: {(offset // limit) + 1}") resultado = obtener_todas_acciones(limit, offset) if resultado["success"]: logger.info(f"✅ Se obtuvieron {len(resultado['data'])} actions (Total en BD: {resultado['total']})") return { "success": True, "data": resultado["data"], "pagination": { "total": resultado["total"], "limit": resultado["limit"], "offset": resultado["offset"], "page": (resultado["offset"] // resultado["limit"]) + 1 } } else: logger.error(f"❌ Error al obtener actions: {resultado['error']}") raise HTTPException( status_code=500, detail=resultado["error"] ) @app.post("/funel-compra", response_model=dict, tags=["Funel Compra"]) async def registrar_funel_compra(datos: FunelCompraRequest): """ Registra un evento en el funel de compra. Acciones típicas: inicio, visualizacion, intento_compra, compra_exitosa, abandono """ logger.info(f"📊 POST /funel-compra - Usuario: {datos.usuario}, Mail: {datos.mail}, Acción: {datos.accion}") resultado = crear_evento_funel(datos) if resultado["success"]: logger.info(f"✅ Evento registrado: {resultado['data']}") return { "success": True, "data": resultado["data"] } else: logger.error(f"❌ Error al registrar evento: {resultado['error']}") raise HTTPException( status_code=400, detail=resultado["error"] ) @app.get("/funel-compra/{usuario}", response_model=dict, tags=["Funel Compra"]) async def obtener_funel_usuario(usuario: str): """ Obtiene todos los eventos del funel de compra para un usuario específico. """ logger.info(f"🔍 GET /funel-compra/{usuario}") resultado = obtener_eventos_funel(usuario) if resultado["success"]: logger.info(f"✅ Se obtuvieron {resultado['total']} eventos para {usuario}") return { "success": True, "data": resultado["data"], "total": resultado["total"] } else: logger.error(f"❌ Error al obtener eventos: {resultado['error']}") raise HTTPException( status_code=500, detail=resultado["error"] ) @app.get("/funel-compra", response_model=dict, tags=["Funel Compra"]) async def listar_funel_compra(limit: int = Query(10, ge=1, le=100), offset: int = Query(0, ge=0)): """ Obtiene todos los eventos del funel de compra con paginación. """ logger.info(f"📚 GET /funel-compra - Limit: {limit}, Offset: {offset}") resultado = obtener_todos_eventos_funel(limit, offset) if resultado["success"]: logger.info(f"✅ Se obtuvieron {len(resultado['data'])} eventos (Total: {resultado['total']})") return { "success": True, "data": resultado["data"], "pagination": { "total": resultado["total"], "limit": limit, "offset": offset, "pages": (resultado["total"] + limit - 1) // limit } } else: logger.error(f"❌ Error al obtener eventos: {resultado['error']}") raise HTTPException( status_code=500, detail=resultado["error"] ) if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)