from app import db from datetime import datetime # Definition of system modules available for RBAC permissions SYSTEM_MODULES = [ { 'id': 'classrooms', 'name': 'Aulas y Espacios', 'name_en': 'Classrooms & Spaces', 'icon': 'bi-door-open', 'description': 'Gestión de aulas físicas, aulas virtuales (∞) y capacidades.' }, { 'id': 'buildings', 'name': 'Edificios y Sedes', 'name_en': 'Buildings & Campuses', 'icon': 'bi-buildings', 'description': 'Administración de sede central, campus virtual, pisos y ubicaciones.' }, { 'id': 'reservations', 'name': 'Reservas de Aulas', 'name_en': 'Classroom Reservations', 'icon': 'bi-calendar-check', 'description': 'Creación, modificación, aprobación, confirmación y cancelación de reservas.' }, { 'id': 'schedule', 'name': 'Cartelera y Cronograma', 'name_en': 'Schedule & Billboard', 'icon': 'bi-calendar3', 'description': 'Visualización de calendario interactivo, cartelera de hoy y comisiones.' }, { 'id': 'academic', 'name': 'Gestión Académica', 'name_en': 'Academic Management', 'icon': 'bi-book', 'description': 'Administración de carreras, asignaturas, códigos y comisiones.' }, { 'id': 'import', 'name': 'Sincronización Sheets', 'name_en': 'Sheets Sync & Import', 'icon': 'bi-file-earmark-spreadsheet', 'description': 'Importación y sincronización de datos académicos desde Google Sheets.' }, { 'id': 'optimizer', 'name': 'Optimizador IA', 'name_en': 'AI Room Optimizer', 'icon': 'bi-cpu', 'description': 'Ejecución y configuración del algoritmo genético de asignación.' }, { 'id': 'users', 'name': 'Usuarios y Accesos RBAC', 'name_en': 'Users & Access RBAC', 'icon': 'bi-shield-lock', 'description': 'Administración de cuentas, roles y matriz granular de permisos.' }, { 'id': 'milestone_types', 'name': 'Tipificaciones de Hitos', 'name_en': 'Milestone Types', 'icon': 'bi-tags', 'description': 'Catálogo dinámico de tipos de evaluaciones, parciales, TPs y eventos.' }, { 'id': 'metrics', 'name': 'Métricas de Ocupación', 'name_en': 'Occupancy Metrics', 'icon': 'bi-graph-up', 'description': 'Visualización y análisis de porcentajes de ocupación por aula (día, semana y mes).' }, { 'id': 'enrollment', 'name': 'Inscripciones y Cursadas', 'name_en': 'Enrollments & Courses', 'icon': 'bi-diagram-3', 'description': 'Gestión de inscripciones de alumnos a comisiones y asignación de docentes.' } ] ACCESS_LEVELS = ['none', 'read', 'read_write'] class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), unique=True, nullable=False, index=True) description = db.Column(db.String(255)) is_system = db.Column(db.Boolean, default=False, nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) # Relationships permissions = db.relationship('Permission', backref='role', lazy='joined', cascade='all, delete-orphan') users = db.relationship('User', backref='role_obj', lazy='select') def get_permission(self, module): """Get access level string for a given module ('none', 'read', 'read_write')""" for p in self.permissions: if p.module == module: return p.access_level return 'none' def has_permission(self, module, min_level='read'): """ Check if role has sufficient permission level. Hierarchy: 'read_write' > 'read' > 'none' """ current_level = self.get_permission(module) if current_level == 'none': return False if min_level == 'read': return current_level in ['read', 'read_write'] if min_level == 'read_write': return current_level == 'read_write' return False def set_permission(self, module, access_level): """Set or update permission level for a module""" if access_level not in ACCESS_LEVELS: access_level = 'none' for p in self.permissions: if p.module == module: p.access_level = access_level return p new_perm = Permission(role=self, module=module, access_level=access_level) db.session.add(new_perm) return new_perm def to_dict(self): return { 'id': self.id, 'name': self.name, 'description': self.description, 'is_system': self.is_system, 'created_at': self.created_at.isoformat() if self.created_at else None, 'user_count': len(self.users) if self.users else 0, 'permissions': {p.module: p.access_level for p in self.permissions} } def __repr__(self): return f'' class Permission(db.Model): __tablename__ = 'permissions' id = db.Column(db.Integer, primary_key=True) role_id = db.Column(db.Integer, db.ForeignKey('roles.id', ondelete='CASCADE'), nullable=False, index=True) module = db.Column(db.String(50), nullable=False) access_level = db.Column(db.String(20), default='none', nullable=False) __table_args__ = ( db.UniqueConstraint('role_id', 'module', name='uq_role_module'), ) def __repr__(self): return f''