From c91026e4dae7fae0479bf80039190000a8da2d76 Mon Sep 17 00:00:00 2001 From: Administrator Date: Fri, 3 Jul 2026 16:48:26 -0500 Subject: [PATCH] Actualizar backend/modules/ocupabilidad/processor.py --- backend/modules/ocupabilidad/processor.py | 117 ++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/backend/modules/ocupabilidad/processor.py b/backend/modules/ocupabilidad/processor.py index eb8ebdd..3cd34c3 100644 --- a/backend/modules/ocupabilidad/processor.py +++ b/backend/modules/ocupabilidad/processor.py @@ -154,6 +154,123 @@ class CursoProcessor: print(f"❌ Error obteniendo datos procesados: {e}") return [] + # ========================================================================= + # DETALLE DE ALUMNOS DE UN CURSO (para el popup "Ver") + # ========================================================================= + def obtener_alumnos_curso(self, num_indice): + """Alumnos matriculados de un curso (num_indice), TODOS menos ANU. + Devuelve nombre + cod_estado (ALU/PRE/RET) + curso anterior TEAC/TERC (si existe).""" + try: + idx = str(num_indice).strip() + conn = self.data_manager.get_connection() + cursor = conn.cursor() + + # 1) Alumnos del curso actual (nombre, estado, dni, fecha de inicio del curso) + sql = """ + SELECT + sgeca_matricula.num_matricula, + sgeca_matricula.cod_estado AS cod_estado, + sgema_alumno.dsc_documento AS dni, + sgema_alumno.dsc_apellido_paterno + ' ' + sgema_alumno.dsc_apellido_materno + + ', ' + sgema_alumno.dsc_nombres AS dsc_alumno, + rp.fch_inicio AS fch_inicio_actual + FROM sgeca_matricula + INNER JOIN sgema_alumno ON sgeca_matricula.cod_alumno = sgema_alumno.cod_alumno + LEFT JOIN sgede_RP_programa rp + ON sgeca_matricula.cod_periodo = rp.cod_detalle + AND sgeca_matricula.cod_programa = rp.cod_programa + AND sgeca_matricula.num_indice = rp.num_indice + WHERE sgeca_matricula.num_indice = ? + AND sgeca_matricula.cod_localidad LIKE 'SCENT' + AND sgeca_matricula.cod_estado NOT IN ('ANU') + ORDER BY dsc_alumno + """ + cursor.execute(sql, idx) + filas = [] + dnis = [] + for row in cursor.fetchall(): + mat = str(row[0]).strip() + if mat.endswith('.0'): mat = mat[:-2] + dni = str(row[2]).strip() if row[2] else "" + filas.append({ + "matricula": mat, + "estado": str(row[1]).strip().upper(), + "dni": dni, + "alumno": str(row[3]).strip(), + "fch_inicio_actual": self.parse_fecha(row[4]), + "curso_anterior": "-", + "estado_anterior": "-", + }) + if dni: + dnis.append(dni) + + # 2) Historial de esos alumnos (cursos TEAC/TERC, no ANU, no SUS) + historial = {} # dni -> lista de {programa, fecha, estado} + if dnis: + dnis_unicos = list(set(dnis)) + for i in range(0, len(dnis_unicos), 1000): + chunk = dnis_unicos[i:i+1000] + ph = ",".join(["?"] * len(chunk)) + sql_hist = f""" + SELECT a.dsc_documento, + rp.dsc_det_programa AS programa_detallado, + p.dsc_programa AS programa_general, + rp.fch_inicio, m.cod_estado + FROM sgeca_matricula m + INNER JOIN sgema_alumno a ON m.cod_alumno = a.cod_alumno + INNER JOIN sgeca_programa p ON m.cod_programa = p.cod_programa + INNER JOIN sgede_RP_programa rp + ON m.cod_periodo = rp.cod_detalle + AND m.cod_programa = rp.cod_programa + AND m.num_indice = rp.num_indice + WHERE a.dsc_documento IN ({ph}) + AND m.cod_localidad LIKE 'SCENT' + AND m.cod_estado IN ('ALU','PRE','RET') + AND rp.cod_estado <> 'SUS' + """ + cursor.execute(sql_hist, *chunk) + for r in cursor.fetchall(): + d = str(r[0]).strip() + det = str(r[1]).strip() if r[1] else "" + gen = str(r[2]).strip() if r[2] else "" + historial.setdefault(d, []).append({ + "programa": det if det and det != "None" else gen, # detallado (como Cobranza) + "programa_clasif": gen, # general, para clasificar TEAC/TERC + "fecha": self.parse_fecha(r[3]), + "estado": str(r[4]).strip().upper(), + }) + conn.close() + + # 3) Para cada alumno, buscar el ÚLTIMO curso anterior TEAC/TERC + for f in filas: + dni = f["dni"] + fch_act = f["fch_inicio_actual"] + if not dni or not fch_act or dni not in historial: + continue + candidatos = [] + for h in historial[dni]: + if not h["fecha"]: + continue + if not (h["fecha"] < fch_act): # debe ser anterior + continue + cat = self.obtener_categoria_programa(h.get("programa_clasif") or h["programa"]) + if cat not in ("TEAC", "TERC"): # solo TEAC/TERC + continue + candidatos.append(h) + if candidatos: + ultimo = max(candidatos, key=lambda x: x["fecha"]) + f["curso_anterior"] = ultimo["programa"] + f["estado_anterior"] = ultimo["estado"] + + # limpiar campos internos + for f in filas: + f.pop("dni", None) + f.pop("fch_inicio_actual", None) + return filas + except Exception as e: + print(f"❌ Error obtener_alumnos_curso: {e}") + return [] + def normalizar_texto(self, texto): if not texto: return "" texto = str(texto).upper().strip()