#!/usr/bin/env python # -*- coding: utf-8 -*- """ Herramienta de Limpieza y Reinicio de Datos Académicos para UniCABA / Edu-Space. Permite eliminar de forma segura y en el orden estricto de claves foráneas: - Asignaciones de aulas / Reservas (reservations) - Inscripciones de alumnos (student_enrollments) - Comisiones de cursada (commissions) - Asignaturas / Materias (subjects) - Carreras académicas (careers) - Recursos de aula y Aulas físicas / virtuales (classroom_resources, classrooms) PRESERVA INTOLERABLEMENTE: - Usuarios del sistema (Users: admin, bedelia, docentes, alumnos) - Roles y Permisos RBAC - Edificios institucionales (Buildings) - Registros de auditoría (AuditLogs) """ import sys import os import argparse # Asegurar que el directorio backend está en el PYTHONPATH sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from app import create_app, db from app.models.reservation import Reservation from app.models.enrollment import StudentEnrollment from app.models.milestone import AcademicMilestone from app.models.grade import MilestoneGrade from app.models.subject import Commission, Subject, CommissionTeacher from app.models.career import Career from app.models.classroom import Classroom, ClassroomResource def execute_academic_reset( reset_reservations=True, reset_commissions=True, reset_subjects=True, reset_careers=True, reset_classrooms=True ): """ Ejecuta el reinicio de las entidades académicas seleccionadas. Retorna un diccionario con el conteo de registros eliminados. """ # Si se reinicia una entidad superior, automáticamente deben reiniciarse sus dependientes if reset_careers: reset_subjects = True if reset_subjects: reset_commissions = True if reset_commissions or reset_classrooms: reset_reservations = True deleted_counts = { 'reservations': 0, 'enrollments': 0, 'commissions': 0, 'commission_teachers': 0, 'milestones': 0, 'subjects': 0, 'careers': 0, 'classrooms': 0, 'classroom_resources': 0 } try: # 1. Eliminar Reservas (Asignaciones de aulas) if reset_reservations: deleted_counts['reservations'] = Reservation.query.delete() db.session.flush() # 2. Eliminar calificaciones e hitos evaluativos vinculados a comisiones o materias if reset_subjects or reset_commissions or reset_careers: try: MilestoneGrade.query.delete() deleted_counts['milestones'] = AcademicMilestone.query.delete() except Exception as me: print("Notice during milestones cleanup:", me) db.session.flush() # 3. Eliminar Docentes de comisión, Inscripciones de alumnos y Comisiones if reset_commissions or reset_subjects or reset_careers: try: deleted_counts['commission_teachers'] = CommissionTeacher.query.delete() except Exception as cte: print("Notice during commission teachers cleanup:", cte) db.session.flush() deleted_counts['enrollments'] = StudentEnrollment.query.delete() db.session.flush() deleted_counts['commissions'] = Commission.query.delete() db.session.flush() # 4. Eliminar Materias / Asignaturas if reset_subjects or reset_careers: deleted_counts['subjects'] = Subject.query.delete() db.session.flush() # 5. Eliminar Carreras if reset_careers: deleted_counts['careers'] = Career.query.delete() db.session.flush() # 6. Eliminar Recursos y Aulas if reset_classrooms: deleted_counts['classroom_resources'] = ClassroomResource.query.delete() deleted_counts['classrooms'] = Classroom.query.delete() db.session.flush() db.session.commit() return { 'success': True, 'deleted': deleted_counts, 'message': 'Datos académicos reiniciados exitosamente.' } except Exception as e: db.session.rollback() return { 'success': False, 'error': str(e), 'deleted': deleted_counts } def main(): parser = argparse.ArgumentParser(description="Limpieza y purga de datos académicos para pruebas del importador.") parser.add_argument('--all', action='store_true', help="Eliminar todo: reservas, comisiones, asignaturas, carreras y aulas.") parser.add_argument('--reservations', action='store_true', help="Eliminar únicamente las asignaciones de aulas.") parser.add_argument('--classrooms', action='store_true', help="Eliminar aulas y sus asignaciones asociadas.") parser.add_argument('--careers', action='store_true', help="Eliminar carreras, asignaturas, comisiones y asignaciones.") parser.add_argument('--yes', '-y', action='store_true', help="Confirmar automáticamente sin preguntar.") args = parser.parse_args() # Si no se pasó ninguna opción específica, asumir --all si el usuario lo confirma if not (args.all or args.reservations or args.classrooms or args.careers): args.all = True app = create_app() with app.app_context(): # Mostrar estado actual de la BD print("=" * 60) print(" ESTADO ACTUAL DE ENTIDADES ACADÉMICAS EN BASE DE DATOS") print("=" * 60) print(f" - Asignaciones de Aulas (Reservas): {Reservation.query.count()}") print(f" - Comisiones: {Commission.query.count()}") print(f" - Asignaturas / Materias: {Subject.query.count()}") print(f" - Carreras: {Career.query.count()}") print(f" - Aulas (Físicas y Virtuales): {Classroom.query.count()}") print("=" * 60) if not args.yes: confirm = input("\n¿Está SEGURO de eliminar los datos seleccionados? Escriba 'CONFIRMAR': ") if confirm.strip() != 'CONFIRMAR': print("Operación cancelada.") sys.exit(0) r_res = args.all or args.reservations or args.classrooms or args.careers r_comm = args.all or args.careers r_subj = args.all or args.careers r_car = args.all or args.careers r_cls = args.all or args.classrooms print("\nEjecutando limpieza de datos...") res = execute_academic_reset( reset_reservations=r_res, reset_commissions=r_comm, reset_subjects=r_subj, reset_careers=r_car, reset_classrooms=r_cls ) if res['success']: print("\n[OK] Limpieza completada con éxito:") for k, v in res['deleted'].items(): print(f" - {k}: {v} registros eliminados") else: print(f"\n[ERROR] Falló la limpieza: {res['error']}") sys.exit(1) if __name__ == '__main__': main()