Files
admin-edu-space/backend/app/models/user.py
T

169 lines
7.1 KiB
Python

from app import db
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True, nullable=False, index=True)
password_hash = db.Column(db.String(255), nullable=False)
name = db.Column(db.String(100), nullable=False)
role = db.Column(db.String(50), nullable=False)
is_active = db.Column(db.Boolean, default=True, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
last_login = db.Column(db.DateTime)
preferred_language = db.Column(db.String(10), default=None, nullable=True)
theme_preference = db.Column(db.String(10), default='auto', nullable=False)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id', ondelete='SET NULL'), nullable=True)
# Extended personal, contact and identity document fields
first_name = db.Column(db.String(100), nullable=True)
last_name = db.Column(db.String(100), nullable=True)
phone = db.Column(db.String(50), nullable=True)
personal_email = db.Column(db.String(255), nullable=True)
address = db.Column(db.String(255), nullable=True)
document_type = db.Column(db.String(20), default='DNI', nullable=True)
document_number = db.Column(db.String(30), nullable=True, index=True)
def get_first_name(self):
if self.first_name:
return self.first_name
return self.name.split()[0] if self.name else ''
def get_last_name(self):
if self.last_name:
return self.last_name
parts = self.name.split() if self.name else []
return ' '.join(parts[1:]) if len(parts) > 1 else ''
def get_formatted_document(self):
from app.constants.document_types import format_document
return format_document(self.document_type, self.document_number)
# Relationships
reservations = db.relationship('Reservation', backref='user', lazy=True, cascade='all, delete-orphan')
def set_password(self, password):
"""Set password hash"""
self.password_hash = generate_password_hash(password, method='pbkdf2:sha256')
def check_password(self, password):
"""Check password"""
return check_password_hash(self.password_hash, password)
def is_admin(self):
"""Check if user is admin"""
if self.role_obj and self.role_obj.name.lower() in ['admin', 'administrador']:
return True
return str(self.role).upper() in ['ADMIN', 'ADMINISTRADOR']
def get_institutional_role(self):
"""Returns normalized institutional role: 'admin', 'bedelia', 'docente', 'alumno'"""
raw = ''
if self.role_obj and self.role_obj.name:
raw = self.role_obj.name.lower().strip()
elif self.role:
raw = str(self.role).lower().strip()
if 'admin' in raw:
return 'admin'
if 'bedel' in raw:
return 'bedelia'
if 'docent' in raw or 'prof' in raw or 'teacher' in raw:
return 'docente'
if 'alumn' in raw or 'estud' in raw or 'student' in raw:
return 'alumno'
return 'docente'
def is_bedelia(self):
return self.get_institutional_role() == 'bedelia'
def is_docente(self):
return self.get_institutional_role() == 'docente'
def is_alumno(self):
return self.get_institutional_role() == 'alumno'
@property
def role_badge_display(self):
role_map = {
'admin': {'label': 'Administrador', 'class': 'badge-admin', 'icon': 'bi-shield-check', 'color': 'primary'},
'bedelia': {'label': 'Bedelía', 'class': 'badge-bedelia', 'icon': 'bi-building-gear', 'color': 'warning'},
'docente': {'label': 'Profesor / Docente', 'class': 'badge-docente', 'icon': 'bi-person-video3', 'color': 'info'},
'alumno': {'label': 'Alumno / Estudiante', 'class': 'badge-alumno', 'icon': 'bi-mortarboard', 'color': 'success'}
}
return role_map.get(self.get_institutional_role(), {'label': self.role or 'Docente', 'class': 'bg-secondary', 'icon': 'bi-person', 'color': 'secondary'})
def has_permission(self, module, min_level='read'):
"""Check if user has granular permission on a given module"""
if self.is_admin():
return True
if self.role_obj:
return self.role_obj.has_permission(module, min_level)
return False
def can_manage(self):
"""Check if user can access the Management / Admin section"""
return (
self.is_admin() or
self.is_bedelia() or
self.has_permission('users', 'read') or
self.has_permission('milestone_types', 'read') or
self.has_permission('import', 'read') or
self.has_permission('reservations', 'read_write') or
self.has_permission('classrooms', 'read_write') or
self.has_permission('buildings', 'read_write') or
self.has_permission('metrics', 'read')
)
def can_edit_reservation(self, reservation):
"""Check if user has permission to edit a given reservation"""
if not reservation:
return False
if self.is_admin():
return True
role_name = (self.role_obj.name if self.role_obj else self.role or '').lower()
if role_name in ['admin', 'administrador', 'bedelia', 'bedelía', 'operador']:
return True
if self.has_permission('reservations', 'read_write'):
# El docente creador puede modificar su propia reserva
if reservation.user_id == self.id:
return True
# Bedelía o roles de gestión con permiso de escritura total
if role_name in ['bedelia', 'bedelía', 'operador']:
return True
return False
def update_last_login(self):
"""Update last login timestamp"""
self.last_login = datetime.utcnow()
db.session.commit()
def to_dict(self):
"""Convert user to dictionary"""
return {
'id': self.id,
'email': self.email,
'institutional_email': self.email,
'personal_email': self.personal_email or '',
'name': self.name,
'first_name': self.get_first_name(),
'last_name': self.get_last_name(),
'phone': self.phone or '',
'address': self.address or '',
'document_type': self.document_type or 'DNI',
'document_number': self.document_number or '',
'document_formatted': self.get_formatted_document(),
'role': self.role,
'role_id': self.role_id,
'is_active': self.is_active,
'created_at': self.created_at.isoformat() if self.created_at else None,
'last_login': self.last_login.isoformat() if self.last_login else None,
'preferred_language': self.preferred_language,
'theme_preference': self.theme_preference
}
def __repr__(self):
return f'<User {self.email}>'