feat(sync): add academic data reset tool, configurable sheets URL and fix sync timeout
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user