Files
pagasis/backend/load_initial_data.py
T
Valeria López 9d0d0b6f56 Agrgar SSL
2025-10-21 04:14:58 -06:00

471 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
"""
Script para cargar datos iniciales desde archivos Excel a la base de datos.
Este script importa:
- Eventos desde Conferencias MAC Agenda Completa.xlsx
- Estudiantes desde Student.xlsx
- Perfiles de Asistentes desde AssistantProfile.xlsx
- Estadísticas de Asistencia desde AttendanceStats.xlsx
"""
import os
import sys
import django
from pathlib import Path
# Configurar Django
BASE_DIR = Path(__file__).resolve().parent
sys.path.append(str(BASE_DIR))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mac_attendance.settings')
django.setup()
import openpyxl
from datetime import datetime, time
from django.contrib.auth.models import User
from authentication.models import Student, AssistantProfile, Asistente, SystemConfiguration
from events.models import Event
from attendance.models import Attendance, AttendanceStats
from django.utils import timezone
def import_events(file_path):
"""Importar eventos desde Conferencias MAC Agenda Completa.xlsx"""
print(f"\n📅 Importando eventos desde {file_path}...")
wb = openpyxl.load_workbook(file_path)
ws = wb.active
events_created = 0
events_updated = 0
# Saltar el encabezado (primera fila)
for row in ws.iter_rows(min_row=2, values_only=True):
if not row[0]: # Si no hay título, saltar
continue
title = row[0]
speaker = row[1] if len(row) > 1 and row[1] else ""
date_str = row[2] if len(row) > 2 and row[2] else None
start_time_str = row[3] if len(row) > 3 and row[3] else None
end_time_str = row[4] if len(row) > 4 and row[4] else None
event_type = row[5] if len(row) > 5 and row[5] else "Conferencia"
modality = row[6] if len(row) > 6 and row[6] else "Presencial"
location = row[7] if len(row) > 7 and row[7] else ""
description = row[8] if len(row) > 8 and row[8] else ""
is_active = bool(row[9]) if len(row) > 9 and row[9] else True
if not date_str or not start_time_str:
print(f" ⚠️ Saltando evento sin fecha u hora: {title}")
continue
# Convertir fecha
if isinstance(date_str, datetime):
event_date = date_str.date()
elif isinstance(date_str, str):
try:
event_date = datetime.strptime(date_str, '%Y-%m-%d').date()
except:
try:
event_date = datetime.strptime(date_str, '%d/%m/%Y').date()
except:
print(f" ⚠️ Formato de fecha inválido para {title}: {date_str}")
continue
else:
event_date = date_str
# Convertir hora de inicio
if isinstance(start_time_str, time):
start_time = start_time_str
elif isinstance(start_time_str, str):
try:
start_time = datetime.strptime(start_time_str, '%H:%M:%S').time()
except:
try:
start_time = datetime.strptime(start_time_str, '%H:%M').time()
except:
print(f" ⚠️ Formato de hora inválido para {title}: {start_time_str}")
continue
else:
start_time = start_time_str
# Convertir hora de fin
end_time = None
if end_time_str:
if isinstance(end_time_str, time):
end_time = end_time_str
elif isinstance(end_time_str, str):
try:
end_time = datetime.strptime(end_time_str, '%H:%M:%S').time()
except:
try:
end_time = datetime.strptime(end_time_str, '%H:%M').time()
except:
pass
# Crear o actualizar evento (sin validaciones para permitir fechas pasadas)
try:
event = Event.objects.get(title=title, date=event_date)
# Actualizar usando update() para evitar validaciones
Event.objects.filter(id=event.id).update(
description=description,
location=location,
start_time=start_time,
end_time=end_time,
is_active=is_active
)
created = False
except Event.DoesNotExist:
# Crear sin validaciones usando el método directo de la base
event = Event(
title=title,
date=event_date,
description=description,
location=location,
start_time=start_time,
end_time=end_time,
is_active=is_active
)
# Guardar sin llamar a clean() para evitar validación de fechas pasadas
super(Event, event).save(force_insert=True)
created = True
if created:
events_created += 1
print(f" ✅ Evento creado: {title} - {event_date} {start_time}")
else:
events_updated += 1
print(f"\n✅ Eventos importados: {events_created} creados, {events_updated} actualizados")
return events_created + events_updated
def import_students(file_path):
"""Importar estudiantes desde Student.xlsx"""
print(f"\n👨‍🎓 Importando estudiantes desde {file_path}...")
wb = openpyxl.load_workbook(file_path)
ws = wb.active
students_created = 0
students_updated = 0
for row in ws.iter_rows(min_row=2, values_only=True):
if not row[0]: # Si no hay número de cuenta, saltar
continue
account_number = str(row[0]).strip()
full_name = row[1] if len(row) > 1 and row[1] else "Sin nombre"
student, created = Student.objects.update_or_create(
account_number=account_number,
defaults={
'full_name': full_name,
'user_type': 'student'
}
)
if created:
students_created += 1
print(f" ✅ Estudiante creado: {account_number} - {full_name}")
else:
students_updated += 1
print(f"\n✅ Estudiantes importados: {students_created} creados, {students_updated} actualizados")
return students_created + students_updated
def import_assistant_profiles(file_path):
"""Importar perfiles de asistentes desde AssistantProfile.xlsx"""
print(f"\n👔 Importando perfiles de asistentes desde {file_path}...")
from authentication.models import UserProfile
wb = openpyxl.load_workbook(file_path)
ws = wb.active
profiles_created = 0
profiles_updated = 0
profiles_skipped = 0
for row in ws.iter_rows(min_row=2, values_only=True):
if not row[0]: # Si no hay número de cuenta, saltar
continue
account_number = str(row[0]).strip()
full_name = row[1] if len(row) > 1 and row[1] else "Sin nombre"
# Verificar si ya existe un UserProfile con este account_number
existing_profile = UserProfile.objects.filter(account_number=account_number).first()
if existing_profile:
# Si existe y es estudiante, actualizar a asistente
if existing_profile.user_type == 'student':
print(f" ⚠️ Saltando {account_number} - ya existe como estudiante")
profiles_skipped += 1
continue
else:
# Si ya es asistente, actualizar
existing_profile.full_name = full_name
existing_profile.save()
profiles_updated += 1
else:
# No existe, crear nuevo
profile = AssistantProfile.objects.create(
account_number=account_number,
full_name=full_name,
user_type='assistant'
)
profiles_created += 1
print(f" ✅ Perfil de asistente creado: {account_number} - {full_name}")
print(f"\n✅ Perfiles de asistentes importados: {profiles_created} creados, {profiles_updated} actualizados, {profiles_skipped} saltados")
return profiles_created + profiles_updated
def assign_permissions_to_assistant():
"""Asignar permisos al asistente para registrar asistencias"""
print(f"\n🔑 Asignando permisos a asistentes...")
from authentication.models import UserProfile
# Obtener solo los UserProfile que son asistentes
assistants = UserProfile.objects.filter(user_type='assistant')
permissions_created = 0
for assistant_profile in assistants:
# Crear permiso de asistente (Asistente model)
asistente, created = Asistente.objects.get_or_create(
user_profile=assistant_profile,
defaults={
'can_manage_events': True
}
)
if created:
permissions_created += 1
print(f" ✅ Permisos asignados a: {assistant_profile.full_name}")
print(f"\n✅ Permisos asignados: {permissions_created} asistentes")
return permissions_created
def import_attendance_stats(file_path):
"""Importar estadísticas de asistencia desde AttendanceStats.xlsx"""
print(f"\n📊 Importando estadísticas de asistencia desde {file_path}...")
from authentication.models import UserProfile
wb = openpyxl.load_workbook(file_path)
ws = wb.active
stats_created = 0
attendances_created = 0
# Obtener eventos principales
event1 = Event.objects.filter(title__icontains="Transformación Digital").first()
event2 = Event.objects.filter(title__icontains="Finanzas").first()
if not event1:
print(" ⚠️ Evento 'Transformación Digital' no encontrado")
if not event2:
print(" ⚠️ Evento 'Finanzas' no encontrado")
# Obtener el asistente para registered_by
assistant = UserProfile.objects.filter(user_type='assistant').first()
if not assistant:
print(" ⚠️ No hay asistentes en el sistema. Creando asistente por defecto...")
assistant = UserProfile.objects.create(
account_number='99999999',
full_name='Sistema - Importación Automática',
user_type='assistant'
)
print(f" ✅ Asistente creado: {assistant.full_name}")
for row in ws.iter_rows(min_row=2, values_only=True):
if not row[0]: # Si no hay número de cuenta, saltar
continue
account_number = str(row[0]).strip()
full_name = row[1] if len(row) > 1 else ""
attended_events = int(row[2]) if len(row) > 2 and row[2] else 0
total_events = int(row[3]) if len(row) > 3 and row[3] else 18
attendance_percentage = float(row[4]) if len(row) > 4 and row[4] else 0.0
try:
student = Student.objects.get(account_number=account_number)
except Student.DoesNotExist:
# Si el estudiante no existe, crearlo
student = Student.objects.create(
account_number=account_number,
full_name=full_name,
user_type='student'
)
print(f" ️ Estudiante creado desde stats: {account_number}")
# Crear estadísticas
stats, created = AttendanceStats.objects.update_or_create(
student=student,
defaults={
'total_events': total_events,
'attended_events': attended_events,
'attendance_percentage': attendance_percentage
}
)
if created:
stats_created += 1
# Crear asistencias según el número total
if attended_events == 2:
# Asistió a ambas conferencias
if event1:
try:
attendance = Attendance.objects.get(student=student, event=event1)
att_created = False
except Attendance.DoesNotExist:
attendance = Attendance(
student=student,
event=event1,
timestamp=timezone.now(),
is_valid=True,
registered_by=assistant
)
# Guardar sin validaciones
super(Attendance, attendance).save(force_insert=True)
att_created = True
attendances_created += 1
if event2:
try:
attendance = Attendance.objects.get(student=student, event=event2)
att_created = False
except Attendance.DoesNotExist:
attendance = Attendance(
student=student,
event=event2,
timestamp=timezone.now(),
is_valid=True,
registered_by=assistant
)
# Guardar sin validaciones
super(Attendance, attendance).save(force_insert=True)
att_created = True
attendances_created += 1
elif attended_events == 1:
# Asistió solo a "Transformación Digital"
if event1:
try:
attendance = Attendance.objects.get(student=student, event=event1)
att_created = False
except Attendance.DoesNotExist:
attendance = Attendance(
student=student,
event=event1,
timestamp=timezone.now(),
is_valid=True,
registered_by=assistant
)
# Guardar sin validaciones
super(Attendance, attendance).save(force_insert=True)
att_created = True
attendances_created += 1
print(f"\n✅ Estadísticas importadas: {stats_created} creadas")
print(f"✅ Asistencias creadas: {attendances_created}")
return stats_created
def configure_system_settings():
"""Configurar settings globales del sistema"""
print(f"\n⚙️ Configurando sistema global...")
config, created = SystemConfiguration.objects.get_or_create(
id=1,
defaults={
'minimum_attendance_percentage': 80.0,
'minutes_before_event': 10, # 10 minutos antes
'minutes_after_start': 25, # 25 minutos después
}
)
if created:
print(f" ✅ Configuración creada: 80% asistencia, 10min antes, 25min después")
else:
config.minimum_attendance_percentage = 80.0
config.minutes_before_event = 10
config.minutes_after_start = 25
config.save()
print(f" 🔄 Configuración actualizada")
return config
def main():
"""Función principal"""
print("="*60)
print("🚀 CARGA DE DATOS INICIALES - SISTEMA DE ASISTENCIAS MAC")
print("="*60)
# Rutas a los archivos Excel
base_path = Path(__file__).resolve().parent
events_file = base_path / "Conferencias MAC Agenda Completa (1) (1).xlsx"
students_file = base_path / "Student-2025-10-21.xlsx"
assistants_file = base_path / "AssistantProfile-2025-10-21.xlsx"
stats_file = base_path / "AttendanceStats-2025-10-21.xlsx"
# Verificar que existan los archivos
files_ok = True
for file_path, name in [
(events_file, "Eventos"),
(students_file, "Estudiantes"),
(assistants_file, "Asistentes"),
(stats_file, "Estadísticas")
]:
if not file_path.exists():
print(f"❌ Archivo no encontrado: {name} - {file_path}")
files_ok = False
if not files_ok:
print("\n⚠️ Algunos archivos no se encontraron. Abortando.")
return
# Importar datos
try:
# 1. Configurar sistema
configure_system_settings()
# 2. Importar eventos
import_events(events_file)
# 3. Importar estudiantes
import_students(students_file)
# 4. Importar asistentes
import_assistant_profiles(assistants_file)
# 5. Asignar permisos a asistentes
assign_permissions_to_assistant()
# 6. Importar estadísticas y asistencias
import_attendance_stats(stats_file)
print("\n" + "="*60)
print("✅ IMPORTACIÓN COMPLETADA EXITOSAMENTE")
print("="*60)
# Mostrar resumen
print(f"\n📊 Resumen de datos en la base de datos:")
print(f" - Eventos: {Event.objects.count()}")
print(f" - Estudiantes: {Student.objects.count()}")
print(f" - Asistentes: {AssistantProfile.objects.count()}")
print(f" - Permisos de asistentes: {Asistente.objects.count()}")
print(f" - Asistencias: {Attendance.objects.count()}")
print(f" - Estadísticas: {AttendanceStats.objects.count()}")
except Exception as e:
print(f"\n❌ Error durante la importación: {e}")
import traceback
traceback.print_exc()
if __name__ == '__main__':
main()