fix: database initialization on sqlite and i18n logic

This commit is contained in:
2026-09-18 14:40:34 -03:00
parent 865538b727
commit 47b50ede7e
5 changed files with 747 additions and 1562 deletions
+571 -564
View File
File diff suppressed because it is too large Load Diff
+15 -30
View File
@@ -24,8 +24,9 @@ def create_app(config_class=Config):
cors.init_app(app) cors.init_app(app)
# Login manager configuration # Login manager configuration
from flask_babel import lazy_gettext
login_manager.login_view = 'auth.login' login_manager.login_view = 'auth.login'
login_manager.login_message = 'Por favor inicia sesión para acceder a esta página.' login_manager.login_message = lazy_gettext('Por favor inicia sesión para acceder a esta página.')
login_manager.login_message_category = 'info' login_manager.login_message_category = 'info'
# Import models to ensure they are registered # Import models to ensure they are registered
@@ -56,46 +57,30 @@ def create_app(config_class=Config):
# Language selector function # Language selector function
def get_locale(): def get_locale():
# 1. Parámetro explícito en la URL (?lang=es o ?lang=en) # 1. Si el usuario está autenticado y tiene idioma configurado explícitamente en su perfil, prevalece.
from flask_login import current_user
if current_user.is_authenticated:
if getattr(current_user, 'preferred_language', None) and current_user.preferred_language in app.config['LANGUAGES']:
return current_user.preferred_language
# El idioma por defecto siempre será español a menos que se configure a mano
return app.config['BABEL_DEFAULT_LOCALE']
# 2. Parámetro explícito en la URL (?lang=es o ?lang=en) (para usuarios no autenticados o login)
lang = request.args.get('lang') lang = request.args.get('lang')
if lang and lang in app.config['LANGUAGES']: if lang and lang in app.config['LANGUAGES']:
session['language'] = lang session['language'] = lang
return lang return lang
# 2. Si el usuario está autenticado y tiene idioma configurado en su perfil, prevalece en toda la sesión
from flask_login import current_user
if current_user.is_authenticated and getattr(current_user, 'preferred_language', None):
if current_user.preferred_language in app.config['LANGUAGES']:
return current_user.preferred_language
# 3. En la pantalla de Login (usuario no autenticado):
# El idioma predeterminado del login debe ser español o respetar el idioma del navegador.
# No debe verse afectado por cookies o sesiones residuales de otros usuarios.
is_login = (request.endpoint == 'auth.login' or request.path.rstrip('/') == '/login')
if is_login:
# Solo si el usuario cambió el idioma explícitamente en el selector del login
if session.get('login_lang_explicit') in app.config['LANGUAGES']:
return session['login_lang_explicit']
# Detectar según el navegador del usuario (soporta es-419, es-AR, es-ES, etc.) # 3. Si ya hay un idioma guardado en la sesión
if request.accept_languages:
for accepted_lang, _quality in request.accept_languages:
primary = accepted_lang.split('-')[0].lower()
if primary in app.config['LANGUAGES']:
return primary
# Idioma predeterminado por defecto del login: Español
return 'es'
# 4. Para otras rutas: verificar si hay idioma en sesión (ej. selector manual)
if 'language' in session and session['language'] in app.config['LANGUAGES']: if 'language' in session and session['language'] in app.config['LANGUAGES']:
return session['language'] return session['language']
# 5. Detección automática del navegador por defecto (soporta subtags regionales) # 4. Primera vez que se accede a la web (sin sesión): respetar el idioma del navegador
if request.accept_languages: if request.accept_languages:
for accepted_lang, _quality in request.accept_languages: for accepted_lang, _quality in request.accept_languages:
primary = accepted_lang.split('-')[0].lower() primary = accepted_lang.split('-')[0].lower()
if primary in app.config['LANGUAGES']: if primary in app.config['LANGUAGES']:
session['language'] = primary # Se guarda para la sesión actual
return primary return primary
# Fallback del sistema: Español # Fallback del sistema: Español
+69 -68
View File
@@ -21,74 +21,75 @@ def init_database():
db.create_all() db.create_all()
# 2. Migraciones incrementales seguras para bases de datos existentes # 2. Migraciones incrementales seguras para bases de datos existentes
db.session.execute(text(""" if db.engine.url.drivername != 'sqlite':
ALTER TABLE users ADD COLUMN IF NOT EXISTS preferred_language VARCHAR(10) DEFAULT NULL; db.session.execute(text("""
""")) ALTER TABLE users ADD COLUMN IF NOT EXISTS preferred_language VARCHAR(10) DEFAULT NULL;
db.session.execute(text(""" """))
ALTER TABLE users ADD COLUMN IF NOT EXISTS theme_preference VARCHAR(10) DEFAULT 'auto' NOT NULL; db.session.execute(text("""
""")) ALTER TABLE users ADD COLUMN IF NOT EXISTS theme_preference VARCHAR(10) DEFAULT 'auto' NOT NULL;
db.session.execute(text(""" """))
ALTER TABLE users ADD COLUMN IF NOT EXISTS role_id INTEGER REFERENCES roles(id) ON DELETE SET NULL; db.session.execute(text("""
""")) ALTER TABLE users ADD COLUMN IF NOT EXISTS role_id INTEGER REFERENCES roles(id) ON DELETE SET NULL;
db.session.execute(text(""" """))
ALTER TABLE subjects ADD COLUMN IF NOT EXISTS career_id INTEGER REFERENCES careers(id) ON DELETE SET NULL; db.session.execute(text("""
""")) ALTER TABLE subjects ADD COLUMN IF NOT EXISTS career_id INTEGER REFERENCES careers(id) ON DELETE SET NULL;
db.session.execute(text(""" """))
ALTER TABLE subjects ALTER COLUMN code TYPE VARCHAR(50); db.session.execute(text("""
""")) ALTER TABLE subjects ALTER COLUMN code TYPE VARCHAR(50);
db.session.execute(text(""" """))
CREATE TABLE IF NOT EXISTS buildings ( db.session.execute(text("""
id SERIAL PRIMARY KEY, CREATE TABLE IF NOT EXISTS buildings (
name VARCHAR(100) UNIQUE NOT NULL, id SERIAL PRIMARY KEY,
code VARCHAR(50) UNIQUE, name VARCHAR(100) UNIQUE NOT NULL,
address VARCHAR(255), code VARCHAR(50) UNIQUE,
floors INTEGER DEFAULT 1 NOT NULL, address VARCHAR(255),
description TEXT, floors INTEGER DEFAULT 1 NOT NULL,
is_active BOOLEAN DEFAULT TRUE NOT NULL, description TEXT,
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(), is_active BOOLEAN DEFAULT TRUE NOT NULL,
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(),
); updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW()
""")) );
db.session.execute(text(""" """))
CREATE TABLE IF NOT EXISTS audit_logs ( db.session.execute(text("""
id SERIAL PRIMARY KEY, CREATE TABLE IF NOT EXISTS audit_logs (
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL, id SERIAL PRIMARY KEY,
user_email VARCHAR(255), user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
action VARCHAR(50) NOT NULL, user_email VARCHAR(255),
module VARCHAR(50) NOT NULL, action VARCHAR(50) NOT NULL,
entity_id INTEGER, module VARCHAR(50) NOT NULL,
details TEXT, entity_id INTEGER,
ip_address VARCHAR(45), details TEXT,
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL ip_address VARCHAR(45),
); created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
CREATE INDEX IF NOT EXISTS ix_audit_logs_action ON audit_logs (action); );
CREATE INDEX IF NOT EXISTS ix_audit_logs_module ON audit_logs (module); CREATE INDEX IF NOT EXISTS ix_audit_logs_action ON audit_logs (action);
CREATE INDEX IF NOT EXISTS ix_audit_logs_created_at ON audit_logs (created_at); CREATE INDEX IF NOT EXISTS ix_audit_logs_module ON audit_logs (module);
""")) CREATE INDEX IF NOT EXISTS ix_audit_logs_created_at ON audit_logs (created_at);
db.session.execute(text(""" """))
ALTER TABLE classrooms ADD COLUMN IF NOT EXISTS building_id INTEGER REFERENCES buildings(id) ON DELETE SET NULL; db.session.execute(text("""
""")) ALTER TABLE classrooms ADD COLUMN IF NOT EXISTS building_id INTEGER REFERENCES buildings(id) ON DELETE SET NULL;
db.session.execute(text(""" """))
ALTER TABLE commissions ADD COLUMN IF NOT EXISTS shift VARCHAR(30); db.session.execute(text("""
""")) ALTER TABLE commissions ADD COLUMN IF NOT EXISTS shift VARCHAR(30);
db.session.execute(text(""" """))
ALTER TABLE reservations ADD COLUMN IF NOT EXISTS shift VARCHAR(30); db.session.execute(text("""
""")) ALTER TABLE reservations ADD COLUMN IF NOT EXISTS shift VARCHAR(30);
db.session.execute(text(""" """))
CREATE TABLE IF NOT EXISTS student_enrollments ( db.session.execute(text("""
id SERIAL PRIMARY KEY, CREATE TABLE IF NOT EXISTS student_enrollments (
student_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, id SERIAL PRIMARY KEY,
commission_id INTEGER NOT NULL REFERENCES commissions(id) ON DELETE CASCADE, student_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
status VARCHAR(20) NOT NULL DEFAULT 'activo', commission_id INTEGER NOT NULL REFERENCES commissions(id) ON DELETE CASCADE,
enrolled_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'activo',
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(), enrolled_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
notes TEXT, updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(),
CONSTRAINT uq_student_commission UNIQUE (student_id, commission_id) notes TEXT,
); CONSTRAINT uq_student_commission UNIQUE (student_id, commission_id)
CREATE INDEX IF NOT EXISTS ix_student_enrollments_student_id ON student_enrollments (student_id); );
CREATE INDEX IF NOT EXISTS ix_student_enrollments_commission_id ON student_enrollments (commission_id); CREATE INDEX IF NOT EXISTS ix_student_enrollments_student_id ON student_enrollments (student_id);
""")) CREATE INDEX IF NOT EXISTS ix_student_enrollments_commission_id ON student_enrollments (commission_id);
db.session.commit() """))
db.session.commit()
print("[OK] Esquema y tablas actualizadas con éxito.") print("[OK] Esquema y tablas actualizadas con éxito.")
# 3. Crear o sincronizar roles predeterminados y matriz de permisos # 3. Crear o sincronizar roles predeterminados y matriz de permisos
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff