Spaces:
Sleeping
Sleeping
| import datetime | |
| import logging | |
| from typing import List | |
| from fastapi import FastAPI, Depends, HTTPException, status | |
| from pydantic import BaseModel | |
| from sqlalchemy.orm import Session | |
| from database import get_db, DimEstudiante, FactRendimientoAcademico | |
| from ner_engine import ner_engine | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| app = FastAPI( | |
| title="GiraGroup BI Backend Cloud", | |
| description="API para Tecnologías Emergentes II con BETO y Supabase", | |
| version="1.0.0" | |
| ) | |
| # El resto del código de ProcessSheetPayload y endpoints se mantiene exactamente igual... | |
| class ProcessSheetPayload(BaseModel): | |
| texto_celda: str | |
| nota_detectada: float | |
| asistencia: float | |
| incumplimiento_tareas: float | |
| id_docente: int | |
| id_modulo: int | |
| id_tiempo: int | |
| id_documento: int | |
| id_usuario: int | |
| def read_root(): | |
| return { | |
| "status": "healthy", | |
| "service": "GiraGroup BI Backend API Cloud", | |
| "ner_initialized": ner_engine._initialized or ner_engine.pipeline is not None | |
| } | |
| def procesar_registro_tabular(payload: ProcessSheetPayload, db: Session = Depends(get_db)): | |
| entidades = ner_engine.extract_entities(payload.texto_celda) | |
| confianza_ia = sum([e["score"] for e in entidades]) / len(entidades) if entidades else 1.0 | |
| forzar_revision = False | |
| if confianza_ia < 0.60: | |
| forzar_revision = True | |
| nombre_resuelto = payload.texto_celda.strip() | |
| estudiante = db.query(DimEstudiante).filter(DimEstudiante.nombre_completo == nombre_resuelto).first() | |
| if not estudiante: | |
| estudiante = DimEstudiante(nombre_completo=nombre_resuelto) | |
| db.add(estudiante) | |
| db.commit() | |
| db.refresh(estudiante) | |
| alertas_disparadas = [] | |
| if payload.nota_detectada <= 70.0: | |
| alertas_disparadas.append("RIESGO_ACADEMICO_CRITICO") | |
| if payload.asistencia < 70.0 or payload.incumplimiento_tareas > 30.0: | |
| alertas_disparadas.append("RIESGO_DESERCION_ALTA") | |
| try: | |
| nuevo_hecho = FactRendimientoAcademico( | |
| id_estudiante=estudiante.id_estudiante, | |
| id_docente=payload.id_docente, | |
| id_modulo=payload.id_modulo, | |
| id_tiempo=payload.id_tiempo, | |
| id_documento=payload.id_documento, | |
| id_usuario_carga=payload.id_usuario, | |
| nota_final=payload.nota_detectada, | |
| asistencia_pct=payload.asistencia, | |
| incumplimiento_actividades_pct=payload.incumplimiento_tareas, | |
| nivel_confianza_ia=confianza_ia, | |
| requiere_revision=forzar_revision | |
| ) | |
| db.add(nuevo_hecho) | |
| db.commit() | |
| return { | |
| "status": "processed", | |
| "id_estudiante_asignado": estudiante.id_estudiante, | |
| "confianza_modelo_beto": round(confianza_ia, 4), | |
| "requiere_auditoria_humana": forzar_revision, | |
| "alertas_estrategicas": alertas_disparadas | |
| } | |
| except Exception as err: | |
| db.rollback() | |
| logger.error(f"Fallo en persistencia: {err}") | |
| raise HTTPException(status_code=500, detail="Error al escribir en Supabase.") | |
| def obtener_riesgos_cruzados(limite_nota: float = 70.0, min_cuotas: int = 2, db: Session = Depends(get_db)): | |
| try: | |
| # Consulta transaccional al esquema estrella | |
| resultados = db.query(DimEstudiante, FactRendimientoAcademico).\ | |
| join(FactRendimientoAcademico, DimEstudiante.id_estudiante == FactRendimientoAcademico.id_estudiante).\ | |
| filter(FactRendimientoAcademico.nota_final <= limite_nota).\ | |
| all() | |
| data = [] | |
| for est, fact in resultados: | |
| data.append({ | |
| "estudiante": est.nombre_completo, | |
| "codigo": f"EST-{est.id_estudiante:06d}", | |
| "rendimiento": { | |
| "nota_actual": fact.nota_final, | |
| "estado_academico": "CRÍTICO" if fact.nota_final <= 70 else "REGULAR" | |
| }, | |
| "finanzas": { | |
| "cuotas_mora": min_cuotas, # Dato dinámico a cruzar con fact_situacion_financiera posteriormente | |
| "deuda_total": 350.0 * min_cuotas, | |
| "estado_cartera": "MORA" | |
| }, | |
| "nivel_riesgo_global": "ALTO - CRÍTICO" | |
| }) | |
| return {"status": "success", "data": data} | |
| except Exception as e: | |
| logger.error(f"Error consultando riesgos: {e}") | |
| raise HTTPException(status_code=500, detail="Error de base de datos") |