feat: gestion de materias por rol - Bedelia, Docente y Alumno

- Nuevo modelo StudentEnrollment (tabla student_enrollments)
  con inscripcion unica por alumno/comision, estado y notas

- Migracion automatica en init_db.py (CREATE TABLE IF NOT EXISTS)

- Rutas /admin/commissions/ para Bedelia y Admin:
  listado con filtros, detalle, asignar docente,
  inscribir/dar de baja alumnos, cambiar estado

- Blueprint my_subjects_bp en /mis-materias:
  Docente ve solo sus comisiones asignadas
  Alumno ve solo sus inscripciones activas
  Admin/Bedelia ven todo

- Templates: admin/commissions/list.html, detail.html
            my_subjects/list.html, detail.html

- Navbar actualizado por rol:
  Docente y Alumno: 'Mis Materias'
  Bedelia y Admin: 'Gestion de Comisiones'
This commit is contained in:
Carlos Tello
2026-09-05 12:24:04 -03:00
parent 69d545b718
commit 1fda344248
12 changed files with 1410 additions and 3 deletions
+3 -1
View File
@@ -9,6 +9,7 @@ from .genetic_algorithm import GeneticAlgorithm, ReservationOptimizer
from .role import Role, Permission, SYSTEM_MODULES
from .milestone import MilestoneType, AcademicMilestone, BASE_MILESTONE_TYPES
from .audit_log import AuditLog
from .enrollment import StudentEnrollment
__all__ = [
'User',
@@ -29,5 +30,6 @@ __all__ = [
'MilestoneType',
'AcademicMilestone',
'BASE_MILESTONE_TYPES',
'AuditLog'
'AuditLog',
'StudentEnrollment'
]
+49
View File
@@ -0,0 +1,49 @@
from app import db
from datetime import datetime
class StudentEnrollment(db.Model):
"""Association table: student (User) ↔ Commission enrollment managed by Bedelía."""
__tablename__ = 'student_enrollments'
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(
db.Integer,
db.ForeignKey('users.id', ondelete='CASCADE'),
nullable=False,
index=True
)
commission_id = db.Column(
db.Integer,
db.ForeignKey('commissions.id', ondelete='CASCADE'),
nullable=False,
index=True
)
status = db.Column(db.String(20), nullable=False, default='activo') # activo | retirado | condicional
enrolled_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
notes = db.Column(db.Text, nullable=True)
# Unique constraint: a student can only be enrolled once per commission
__table_args__ = (
db.UniqueConstraint('student_id', 'commission_id', name='uq_student_commission'),
)
# Relationships
student = db.relationship('User', backref=db.backref('enrollments', lazy='dynamic', cascade='all, delete-orphan'))
commission = db.relationship('Commission', backref=db.backref('enrollments', lazy='dynamic', cascade='all, delete-orphan'))
def to_dict(self):
return {
'id': self.id,
'student_id': self.student_id,
'student_name': self.student.name if self.student else None,
'student_email': self.student.email if self.student else None,
'commission_id': self.commission_id,
'status': self.status,
'enrolled_at': self.enrolled_at.isoformat() if self.enrolled_at else None,
'notes': self.notes,
}
def __repr__(self):
return f'<StudentEnrollment student={self.student_id} commission={self.commission_id} status={self.status}>'