#!/usr/bin/env python3 """ Seed de la base de datos de la plataforma de usuarios. Crea: roles, usuarios, credenciales, profesores, alumnos, cursos, matrículas, actividades, entregas, calificaciones y progreso. Uso (desde user_platform/): python seed.py La contraseña por defecto de todos los usuarios es: Password123! """ import os import random from datetime import datetime, timedelta, timezone from decimal import Decimal # ── DB URL (lee el .env raíz del proyecto) ────────────────────────────────────── def _read_env(path: str) -> dict[str, str]: result: dict[str, str] = {} if os.path.exists(path): with open(path) as f: for line in f: line = line.strip() if line and not line.startswith("#") and "=" in line: key, _, val = line.partition("=") result[key.strip()] = val.strip() return result # Busca el .env raíz subiendo desde user_platform/ _here = os.path.dirname(os.path.abspath(__file__)) env = _read_env(os.path.join(_here, "..", ".env")) pg_user = env.get("PLATFORM_DB_USER", "user-database-user") pg_pass = env.get("PLATFORM_DB_PASSWORD", "") pg_db = env.get("PLATFORM_DB_NAME", "user-database") pg_port = env.get("PLATFORM_DB_PORT", "55557") DB_URL = os.environ.get("PLATFORM_DB_URL", f"postgresql://{pg_user}:{pg_pass}@localhost:{pg_port}/{pg_db}") # ── ORM ───────────────────────────────────────────────────────────────────────── from sqlalchemy import create_engine, Column, Integer, String, Boolean, Text, DateTime, Numeric, ForeignKey, UniqueConstraint from sqlalchemy.orm import sessionmaker, DeclarativeBase, relationship from passlib.context import CryptContext pwd_ctx = CryptContext(schemes=["bcrypt"], deprecated="auto") engine = create_engine(DB_URL, pool_pre_ping=True) Session = sessionmaker(bind=engine) class Base(DeclarativeBase): pass class Rol(Base): __tablename__ = "roles" id_rol = Column(Integer, primary_key=True) nombre = Column(String(50), unique=True, nullable=False) usuarios = relationship("Usuario", back_populates="rol") class Usuario(Base): __tablename__ = "usuarios" id_usuario = Column(Integer, primary_key=True) nombre = Column(String(100), nullable=False) apellidos = Column(String(150)) email = Column(String(150), unique=True, nullable=False) fecha_creacion = Column(DateTime, default=lambda: datetime.now(timezone.utc)) activo = Column(Boolean, default=True) id_rol = Column(Integer, ForeignKey("roles.id_rol"), nullable=False) rol = relationship("Rol", back_populates="usuarios") credencial = relationship("Credencial", back_populates="usuario", uselist=False) profesor = relationship("Profesor", back_populates="usuario", uselist=False) alumno = relationship("Alumno", back_populates="usuario", uselist=False) class Credencial(Base): __tablename__ = "credenciales" id_credencial = Column(Integer, primary_key=True) id_usuario = Column(Integer, ForeignKey("usuarios.id_usuario", ondelete="CASCADE"), unique=True) password_hash = Column(Text, nullable=False) ultimo_login = Column(DateTime) usuario = relationship("Usuario", back_populates="credencial") class Profesor(Base): __tablename__ = "profesores" id_profesor = Column(Integer, primary_key=True) id_usuario = Column(Integer, ForeignKey("usuarios.id_usuario", ondelete="CASCADE"), unique=True) especialidad = Column(String(100)) usuario = relationship("Usuario", back_populates="profesor") cursos = relationship("Curso", back_populates="profesor") class Alumno(Base): __tablename__ = "alumnos" id_alumno = Column(Integer, primary_key=True) id_usuario = Column(Integer, ForeignKey("usuarios.id_usuario", ondelete="CASCADE"), unique=True) nivel = Column(String(50)) usuario = relationship("Usuario", back_populates="alumno") matriculas = relationship("Matricula", back_populates="alumno") entregas = relationship("Entrega", back_populates="alumno") progreso = relationship("Progreso", back_populates="alumno") class Curso(Base): __tablename__ = "cursos" id_curso = Column(Integer, primary_key=True) nombre = Column(String(150), nullable=False) descripcion = Column(Text) fecha_creacion = Column(DateTime, default=lambda: datetime.now(timezone.utc)) id_profesor = Column(Integer, ForeignKey("profesores.id_profesor"), nullable=False) profesor = relationship("Profesor", back_populates="cursos") matriculas = relationship("Matricula", back_populates="curso") actividades = relationship("Actividad", back_populates="curso") progreso = relationship("Progreso", back_populates="curso") class Matricula(Base): __tablename__ = "matriculas" __table_args__ = (UniqueConstraint("id_alumno", "id_curso"),) id_matricula = Column(Integer, primary_key=True) id_alumno = Column(Integer, ForeignKey("alumnos.id_alumno", ondelete="CASCADE")) id_curso = Column(Integer, ForeignKey("cursos.id_curso", ondelete="CASCADE")) fecha_matricula = Column(DateTime, default=lambda: datetime.now(timezone.utc)) alumno = relationship("Alumno", back_populates="matriculas") curso = relationship("Curso", back_populates="matriculas") class Actividad(Base): __tablename__ = "actividades" id_actividad = Column(Integer, primary_key=True) titulo = Column(String(200), nullable=False) descripcion = Column(Text) fecha_publicacion = Column(DateTime, default=lambda: datetime.now(timezone.utc)) fecha_entrega = Column(DateTime) puntuacion_maxima = Column(Numeric(5, 2)) id_curso = Column(Integer, ForeignKey("cursos.id_curso", ondelete="CASCADE")) curso = relationship("Curso", back_populates="actividades") entregas = relationship("Entrega", back_populates="actividad") class Entrega(Base): __tablename__ = "entregas" id_entrega = Column(Integer, primary_key=True) id_actividad = Column(Integer, ForeignKey("actividades.id_actividad", ondelete="CASCADE")) id_alumno = Column(Integer, ForeignKey("alumnos.id_alumno", ondelete="CASCADE")) fecha_entrega = Column(DateTime, default=lambda: datetime.now(timezone.utc)) contenido = Column(Text) estado = Column(String(50), default="calificado") actividad = relationship("Actividad", back_populates="entregas") alumno = relationship("Alumno", back_populates="entregas") calificacion = relationship("Calificacion", back_populates="entrega", uselist=False) class Calificacion(Base): __tablename__ = "calificaciones" id_calificacion = Column(Integer, primary_key=True) id_entrega = Column(Integer, ForeignKey("entregas.id_entrega", ondelete="CASCADE"), unique=True) nota = Column(Numeric(5, 2)) observaciones = Column(Text) fecha_calificacion = Column(DateTime, default=lambda: datetime.now(timezone.utc)) entrega = relationship("Entrega", back_populates="calificacion") class Progreso(Base): __tablename__ = "progreso" __table_args__ = (UniqueConstraint("id_alumno", "id_curso"),) id_progreso = Column(Integer, primary_key=True) id_alumno = Column(Integer, ForeignKey("alumnos.id_alumno", ondelete="CASCADE")) id_curso = Column(Integer, ForeignKey("cursos.id_curso", ondelete="CASCADE")) porcentaje = Column(Numeric(5, 2), default=0) alumno = relationship("Alumno", back_populates="progreso") curso = relationship("Curso", back_populates="progreso") class Horario(Base): __tablename__ = "horarios" id_horario = Column(Integer, primary_key=True) id_curso = Column(Integer, ForeignKey("cursos.id_curso", ondelete="CASCADE")) dia_semana = Column(Integer, nullable=False) hora_inicio = Column(String(5), nullable=False) hora_fin = Column(String(5), nullable=False) aula = Column(String(50)) class Proyecto(Base): __tablename__ = "proyectos" id_proyecto = Column(Integer, primary_key=True) id_curso = Column(Integer, ForeignKey("cursos.id_curso", ondelete="CASCADE")) titulo = Column(String(200), nullable=False) descripcion = Column(Text) fecha_entrega = Column(DateTime) estado = Column(String(50), default="en curso") porcentaje_completado = Column(Numeric(5, 2), default=0) class ProyectoEstudiante(Base): __tablename__ = "proyectos_estudiantes" id_proyecto = Column(Integer, ForeignKey("proyectos.id_proyecto", ondelete="CASCADE"), primary_key=True) id_alumno = Column(Integer, ForeignKey("alumnos.id_alumno", ondelete="CASCADE"), primary_key=True) class Examen(Base): __tablename__ = "examenes" id_examen = Column(Integer, primary_key=True) id_curso = Column(Integer, ForeignKey("cursos.id_curso", ondelete="CASCADE")) titulo = Column(String(200), nullable=False) temario = Column(Text) fecha = Column(DateTime, nullable=False) duracion_minutos = Column(Integer, nullable=False) modalidad = Column(String(50)) # ── Data ───────────────────────────────────────────────────────────────────────── DEFAULT_PASSWORD = pwd_ctx.hash("Password123!") ROLES = ["admin", "profesor", "estudiante"] ADMINS = [ {"nombre": "Emmanuel", "apellidos": "Bizimana", "email": "admin@opendata.edu"}, ] TEACHERS = [ {"nombre": "Sarah", "apellidos": "Kamau", "email": "s.kamau@opendata.edu", "especialidad": "Mathematics"}, {"nombre": "Patrick", "apellidos": "Habimana", "email": "p.habimana@opendata.edu", "especialidad": "Science"}, {"nombre": "Aline", "apellidos": "Nkurunziza", "email": "a.nkurunziza@opendata.edu","especialidad": "English"}, {"nombre": "Moses", "apellidos": "Kato", "email": "m.kato@opendata.edu", "especialidad": "ICT"}, {"nombre": "Faith", "apellidos": "Okello", "email": "f.okello@opendata.edu", "especialidad": "History & Civics"}, {"nombre": "Neema", "apellidos": "Mushi", "email": "n.mushi@opendata.edu", "especialidad": "Geography"}, {"nombre": "Samuel", "apellidos": "Otieno", "email": "s.otieno@opendata.edu", "especialidad": "Biology & Chemistry"}, ] STUDENTS = [ {"nombre": "Grace", "apellidos": "Uwimana", "email": "g.uwimana@students.edu", "nivel": "Secondary 5"}, {"nombre": "John", "apellidos": "Mwangi", "email": "j.mwangi@students.edu", "nivel": "Secondary 3"}, {"nombre": "Claudine", "apellidos": "Bizimana", "email": "c.bizimana@students.edu", "nivel": "Secondary 2"}, {"nombre": "Peter", "apellidos": "Habimana", "email": "p.habimana2@students.edu", "nivel": "Secondary 4"}, {"nombre": "Amina", "apellidos": "Kamau", "email": "a.kamau@students.edu", "nivel": "Secondary 1"}, {"nombre": "David", "apellidos": "Okello", "email": "d.okello@students.edu", "nivel": "Secondary 6"}, {"nombre": "Fatuma", "apellidos": "Otieno", "email": "f.otieno@students.edu", "nivel": "Secondary 3"}, {"nombre": "Joseph", "apellidos": "Kato", "email": "j.kato@students.edu", "nivel": "Secondary 2"}, {"nombre": "Esperance","apellidos": "Nkurunziza", "email": "e.nkurunziza@students.edu","nivel": "Secondary 5"}, {"nombre": "Ali", "apellidos": "Mushi", "email": "a.mushi@students.edu", "nivel": "Secondary 4"}, {"nombre": "Zawadi", "apellidos": "Habimana", "email": "z.habimana@students.edu", "nivel": "Secondary 1"}, {"nombre": "Rehema", "apellidos": "Uwimana", "email": "r.uwimana@students.edu", "nivel": "Secondary 3"}, {"nombre": "Baraka", "apellidos": "Mwangi", "email": "b.mwangi@students.edu", "nivel": "Secondary 6"}, {"nombre": "Solange", "apellidos": "Kato", "email": "s.kato@students.edu", "nivel": "Secondary 2"}, {"nombre": "Omar", "apellidos": "Otieno", "email": "o.otieno@students.edu", "nivel": "Secondary 4"}, {"nombre": "Immaculée","apellidos": "Bizimana", "email": "i.bizimana@students.edu", "nivel": "Secondary 5"}, {"nombre": "Mussa", "apellidos": "Kamau", "email": "m.kamau@students.edu", "nivel": "Secondary 1"}, {"nombre": "Vestine", "apellidos": "Okello", "email": "v.okello@students.edu", "nivel": "Secondary 3"}, {"nombre": "Patrick", "apellidos": "Mushi", "email": "p.mushi@students.edu", "nivel": "Secondary 6"}, {"nombre": "Dativa", "apellidos": "Nkurunziza", "email": "d.nkurunziza@students.edu","nivel": "Secondary 2"}, ] COURSES = [ { "nombre": "Advanced Mathematics — Secondary 5 & 6", "descripcion": "Álgebra lineal, cálculo diferencial, estadística aplicada y resolución de problemas complejos.", "teacher_email": "s.kamau@opendata.edu", }, { "nombre": "English Language & Literature", "descripcion": "Comprensión lectora, escritura académica, análisis literario y comunicación oral.", "teacher_email": "a.nkurunziza@opendata.edu", }, { "nombre": "Integrated Science — Secondary 1 & 2", "descripcion": "Fundamentos de biología, química y física con enfoque experimental.", "teacher_email": "p.habimana@opendata.edu", }, { "nombre": "ICT & Digital Literacy", "descripcion": "Ofimática, programación básica, seguridad digital y gestión de datos.", "teacher_email": "m.kato@opendata.edu", }, { "nombre": "History, Civics & Social Studies", "descripcion": "Historia de África del Este, gobierno, derechos humanos y ciudadanía activa.", "teacher_email": "f.okello@opendata.edu", }, { "nombre": "Geography & Environment", "descripcion": "Geografía física y humana, cambio climático y gestión de recursos naturales.", "teacher_email": "n.mushi@opendata.edu", }, { "nombre": "Biology & Chemistry — Secondary 4 & 5", "descripcion": "Biología celular, genética, reacciones químicas orgánicas e inorgánicas.", "teacher_email": "s.otieno@opendata.edu", }, ] ACTIVITY_TEMPLATES = [ { "titulo": "Tarea 1 — Evaluación inicial", "descripcion": "Evaluación de conocimientos previos y diagnóstico del nivel del estudiante.", "puntuacion_maxima": Decimal("20.00"), "dias_publicacion": -60, "dias_entrega": -55, }, { "titulo": "Cuestionario — Trimestre 1", "descripcion": "Cuestionario de seguimiento sobre los contenidos del primer trimestre.", "puntuacion_maxima": Decimal("30.00"), "dias_publicacion": -40, "dias_entrega": -35, }, { "titulo": "Proyecto colaborativo", "descripcion": "Trabajo en grupo sobre un caso de estudio real relacionado con la asignatura.", "puntuacion_maxima": Decimal("50.00"), "dias_publicacion": -25, "dias_entrega": -15, }, { "titulo": "Examen Parcial — Trimestre 2", "descripcion": "Evaluación escrita de los contenidos del segundo trimestre.", "puntuacion_maxima": Decimal("40.00"), "dias_publicacion": -10, "dias_entrega": -5, }, { "titulo": "Entrega final — Portfolio", "descripcion": "Portfolio de trabajos realizados durante el curso con reflexión personal.", "puntuacion_maxima": Decimal("60.00"), "dias_publicacion": -3, "dias_entrega": 10, }, ] SUBMISSION_TEXTS = [ "He revisado el material y completado todos los ejercicios propuestos.", "Adjunto mi trabajo con las correcciones indicadas en clase.", "He realizado la tarea individualmente siguiendo las instrucciones del profesor.", "Incluyo referencias adicionales que encontré durante la investigación.", "Trabajo completado. Tuve dificultades con la parte práctica pero lo resolví.", ] # ── Seed logic ─────────────────────────────────────────────────────────────────── def seed(): print("\n=== Seed: Plataforma de Usuarios ===\n") Base.metadata.create_all(bind=engine) print("✓ Esquema verificado") db = Session() random.seed(42) try: # ── Roles rol_map: dict[str, Rol] = {} for nombre in ROLES: r = db.query(Rol).filter(Rol.nombre == nombre).first() if not r: r = Rol(nombre=nombre) db.add(r) db.flush() rol_map[nombre] = r db.commit() print(f"✓ Roles: {list(rol_map.keys())}") # ── Admins for data in ADMINS: if db.query(Usuario).filter(Usuario.email == data["email"]).first(): continue u = Usuario(**data, id_rol=rol_map["admin"].id_rol) db.add(u) db.flush() db.add(Credencial(id_usuario=u.id_usuario, password_hash=DEFAULT_PASSWORD)) db.commit() print(f"✓ Admins: {len(ADMINS)}") # ── Teachers profesor_map: dict[str, Profesor] = {} for data in TEACHERS: u = db.query(Usuario).filter(Usuario.email == data["email"]).first() if not u: u = Usuario( nombre=data["nombre"], apellidos=data["apellidos"], email=data["email"], id_rol=rol_map["profesor"].id_rol, ) db.add(u) db.flush() db.add(Credencial(id_usuario=u.id_usuario, password_hash=DEFAULT_PASSWORD)) p = db.query(Profesor).filter(Profesor.id_usuario == u.id_usuario).first() if not p: p = Profesor(id_usuario=u.id_usuario, especialidad=data["especialidad"]) db.add(p) db.flush() profesor_map[data["email"]] = p db.commit() print(f"✓ Profesores: {len(TEACHERS)}") # ── Students alumno_list: list[Alumno] = [] for data in STUDENTS: u = db.query(Usuario).filter(Usuario.email == data["email"]).first() if not u: u = Usuario( nombre=data["nombre"], apellidos=data["apellidos"], email=data["email"], id_rol=rol_map["estudiante"].id_rol, ) db.add(u) db.flush() db.add(Credencial(id_usuario=u.id_usuario, password_hash=DEFAULT_PASSWORD)) a = db.query(Alumno).filter(Alumno.id_usuario == u.id_usuario).first() if not a: a = Alumno(id_usuario=u.id_usuario, nivel=data["nivel"]) db.add(a) db.flush() alumno_list.append(a) db.commit() print(f"✓ Alumnos: {len(STUDENTS)}") # ── Courses curso_list: list[Curso] = [] for data in COURSES: c = db.query(Curso).filter(Curso.nombre == data["nombre"]).first() if not c: profesor = profesor_map[data["teacher_email"]] c = Curso( nombre=data["nombre"], descripcion=data["descripcion"], id_profesor=profesor.id_profesor, fecha_creacion=datetime.now(timezone.utc) - timedelta(days=90), ) db.add(c) db.flush() curso_list.append(c) db.commit() print(f"✓ Cursos: {len(COURSES)}") # ── Horarios, Proyectos, Examenes horario_count = 0 proyecto_count = 0 examen_count = 0 proyecto_list: list[Proyecto] = [] for curso in curso_list: # Horarios dias = random.sample([1, 2, 3, 4, 5], 2) for dia in dias: h_inicio = random.choice(["08:00", "10:00", "12:00", "14:00"]) h_fin = f"{int(h_inicio[:2])+2:02d}:00" aula = random.choice(["Sala Digital A", "Sala Digital B", "Lab 1", "Lab 2", "Aula 101"]) h = Horario(id_curso=curso.id_curso, dia_semana=dia, hora_inicio=h_inicio, hora_fin=h_fin, aula=aula) db.add(h) horario_count += 1 # Proyectos now = datetime.now(timezone.utc) p1 = Proyecto(id_curso=curso.id_curso, titulo=f"Proyecto Final: {curso.nombre}", descripcion="Aplicar los conceptos del curso en un caso práctico.", fecha_entrega=now + timedelta(days=30), estado="en curso", porcentaje_completado=Decimal(str(random.randint(10, 80)))) p2 = Proyecto(id_curso=curso.id_curso, titulo=f"Investigación: {curso.nombre}", descripcion="Investigación bibliográfica y presentación.", fecha_entrega=now - timedelta(days=10), estado="entregado", porcentaje_completado=Decimal("100.00")) db.add_all([p1, p2]) db.flush() proyecto_list.extend([p1, p2]) proyecto_count += 2 # Examenes e1 = Examen(id_curso=curso.id_curso, titulo=f"Examen Parcial", temario="Unidades 1 a 3", fecha=now - timedelta(days=5), duracion_minutos=90, modalidad="Presencial") e2 = Examen(id_curso=curso.id_curso, titulo=f"Examen Final", temario="Todo el temario", fecha=now + timedelta(days=25), duracion_minutos=120, modalidad="Online") db.add_all([e1, e2]) examen_count += 2 db.commit() print(f"✓ Horarios: {horario_count}, Proyectos: {proyecto_count}, Exámenes: {examen_count}") # ── Activities (5 per course) actividad_map: dict[int, list[Actividad]] = {} for curso in curso_list: actividad_map[curso.id_curso] = [] for tmpl in ACTIVITY_TEMPLATES: titulo_full = f"{curso.nombre.split('—')[0].strip()} — {tmpl['titulo']}" a = db.query(Actividad).filter(Actividad.titulo == titulo_full).first() if not a: now = datetime.now(timezone.utc) a = Actividad( titulo=titulo_full, descripcion=tmpl["descripcion"], puntuacion_maxima=tmpl["puntuacion_maxima"], id_curso=curso.id_curso, fecha_publicacion=now + timedelta(days=tmpl["dias_publicacion"]), fecha_entrega=now + timedelta(days=tmpl["dias_entrega"]), ) db.add(a) db.flush() actividad_map[curso.id_curso].append(a) db.commit() total_acts = sum(len(v) for v in actividad_map.values()) print(f"✓ Actividades: {total_acts}") # ── Enrollments: cada alumno en 3-4 cursos aleatorios matricula_count = 0 entrega_count = 0 for alumno in alumno_list: n_cursos = random.randint(3, 5) cursos_alumno = random.sample(curso_list, min(n_cursos, len(curso_list))) for curso in cursos_alumno: # Matrícula m = db.query(Matricula).filter( Matricula.id_alumno == alumno.id_alumno, Matricula.id_curso == curso.id_curso, ).first() if not m: m = Matricula( id_alumno=alumno.id_alumno, id_curso=curso.id_curso, fecha_matricula=datetime.now(timezone.utc) - timedelta(days=85), ) db.add(m) db.flush() matricula_count += 1 # Entregas y calificaciones (solo actividades ya cerradas) actividades_cerradas = [ a for a in actividad_map[curso.id_curso] if a.fecha_entrega and a.fecha_entrega.replace(tzinfo=timezone.utc) < datetime.now(timezone.utc) ] notas: list[float] = [] for actividad in actividades_cerradas: e = db.query(Entrega).filter( Entrega.id_alumno == alumno.id_alumno, Entrega.id_actividad == actividad.id_actividad, ).first() if not e: entrega_fecha = actividad.fecha_entrega - timedelta(hours=random.randint(1, 48)) e = Entrega( id_actividad=actividad.id_actividad, id_alumno=alumno.id_alumno, fecha_entrega=entrega_fecha, contenido=random.choice(SUBMISSION_TEXTS), estado="calificado", ) db.add(e) db.flush() entrega_count += 1 # Nota: distribución realista centrada en 60-85 max_nota = float(actividad.puntuacion_maxima) base_pct = random.gauss(0.72, 0.15) base_pct = max(0.3, min(1.0, base_pct)) nota = round(base_pct * max_nota, 2) notas.append(nota / max_nota * 100) obs_list = [ "Buen trabajo, sigue así.", "Necesita mejorar la argumentación.", "Excelente presentación y contenido.", "Trabajo correcto pero le falta profundidad.", "Muy buen desempeño.", ] db.add(Calificacion( id_entrega=e.id_entrega, nota=Decimal(str(nota)), observaciones=random.choice(obs_list), fecha_calificacion=entrega_fecha + timedelta(days=random.randint(1, 5)), )) # Progreso del alumno en este curso if actividades_cerradas: entregadas = len(actividades_cerradas) total_act = len(actividad_map[curso.id_curso]) pct_entrega = (entregadas / total_act) * 100 pct_nota = (sum(notas) / len(notas)) if notas else 0 porcentaje = round((pct_entrega * 0.4 + pct_nota * 0.6), 2) prog = db.query(Progreso).filter( Progreso.id_alumno == alumno.id_alumno, Progreso.id_curso == curso.id_curso, ).first() if not prog: db.add(Progreso( id_alumno=alumno.id_alumno, id_curso=curso.id_curso, porcentaje=Decimal(str(porcentaje)), )) # Asignar proyectos del curso al alumno proyectos_curso = [p for p in proyecto_list if p.id_curso == curso.id_curso] for p in proyectos_curso: pe = db.query(ProyectoEstudiante).filter(ProyectoEstudiante.id_proyecto == p.id_proyecto, ProyectoEstudiante.id_alumno == alumno.id_alumno).first() if not pe: db.add(ProyectoEstudiante(id_proyecto=p.id_proyecto, id_alumno=alumno.id_alumno)) db.commit() print(f"✓ Matrículas: {matricula_count}") print(f"✓ Entregas: {entrega_count}") print(f"✓ Progreso calculado") # ── Resumen final print("\n─── Resumen ────────────────────────────────") print(f" Usuarios totales: {db.query(Usuario).count()}") print(f" Profesores: {db.query(Profesor).count()}") print(f" Alumnos: {db.query(Alumno).count()}") print(f" Cursos: {db.query(Curso).count()}") print(f" Actividades: {db.query(Actividad).count()}") print(f" Entregas: {db.query(Entrega).count()}") print(f" Calificaciones: {db.query(Calificacion).count()}") print(f" Progreso registros:{db.query(Progreso).count()}") print(f" Horarios: {db.query(Horario).count()}") print(f" Proyectos: {db.query(Proyecto).count()}") print(f" Exámenes: {db.query(Examen).count()}") print("────────────────────────────────────────────") print("\n Contraseña de todos los usuarios: Password123!") print(" Ya puedes hacer login desde la API.\n") except Exception as e: db.rollback() print(f"\n✗ Error durante el seed: {e}") raise finally: db.close() if __name__ == "__main__": seed()