#!/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.subject import Commission, Subject 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, '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() # 2. Desvincular hitos de materias o comisiones si se van a eliminar if reset_subjects or reset_commissions: AcademicMilestone.query.update({ AcademicMilestone.subject_id: None, AcademicMilestone.commission_id: None }) db.session.flush() # 3. Eliminar Inscripciones y Comisiones if reset_commissions: deleted_counts['enrollments'] = StudentEnrollment.query.delete() deleted_counts['commissions'] = Commission.query.delete() # 4. Eliminar Materias / Asignaturas if reset_subjects: deleted_counts['subjects'] = Subject.query.delete() # 5. Eliminar Carreras if reset_careers: deleted_counts['careers'] = Career.query.delete() # 6. Eliminar Recursos y Aulas if reset_classrooms: deleted_counts['classroom_resources'] = ClassroomResource.query.delete() deleted_counts['classrooms'] = Classroom.query.delete() 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()