feat(api): fix lint

This commit is contained in:
Alejandro Vazquez
2026-03-27 14:52:16 -03:00
parent 192d1dd41c
commit 402f8e07a8
11 changed files with 361 additions and 55 deletions
+43 -43
View File
@@ -1,65 +1,65 @@
from flask import Flask from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate from flask_migrate import Migrate
from flask_login import LoginManager
from flask_cors import CORS from flask_cors import CORS
from dotenv import load_dotenv from flask_babel import Babel, gettext as _
from config.config import Config from config.config import Config
import os # Initialize extensions
load_dotenv()
db = SQLAlchemy() db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate() migrate = Migrate()
login_manager = LoginManager()
cors = CORS()
babel = Babel()
def create_app(): def create_app(config_class=Config):
app = Flask(__name__) app = Flask(__name__)
app.config.from_object(config_class)
# Configuration # Initialize extensions with app
app.config.from_object(Config)
# Initialize extensions
db.init_app(app) db.init_app(app)
login_manager.init_app(app)
migrate.init_app(app, db) migrate.init_app(app, db)
CORS(app) login_manager.init_app(app)
cors.init_app(app)
# Login manager configuration # Login manager configuration
login_manager.login_view = 'auth.login' login_manager.login_view = 'auth.login'
login_manager.login_message = 'Please log in to access this page.' login_manager.login_message = 'Por favor inicia sesión para acceder a esta página.'
login_manager.login_message_category = 'info'
@login_manager.user_loader
def load_user(user_id):
from app.models.user import User
return User.query.get(int(user_id))
# Register blueprints # Register blueprints
from app.routes.auth import auth_bp from app.routes import auth_bp, classrooms_bp, main_bp, schedule_bp
from app.routes.main import main_bp
from app.routes.classrooms import classrooms_bp
from app.routes.schedule import schedule_bp
app.register_blueprint(auth_bp) app.register_blueprint(auth_bp, url_prefix="/")
app.register_blueprint(main_bp) app.register_blueprint(classrooms_bp, url_prefix="/classrooms")
app.register_blueprint(classrooms_bp, url_prefix='/classrooms') app.register_blueprint(main_bp, url_prefix="/")
app.register_blueprint(schedule_bp, url_prefix='/schedule') app.register_blueprint(schedule_bp, url_prefix="/schedule")
# Create database tables # Babel language selector
with app.app_context(): @app.context_processor
db.create_all() def inject_conf_vars():
return dict(
languages=app.config['LANGUAGES'],
current_lang=request.session.get('language', request.accept_languages.best_match(app.config['LANGUAGES']) or app.config['BABEL_DEFAULT_LOCALE'])
)
# Language selector function
def get_locale():
# 1. Check if language is explicitly set in URL parameters
if request.args.get('lang'):
lang = request.args.get('lang')
if lang in app.config['LANGUAGES']:
return lang
# Create default admin user if not exists # 2. Check if language is stored in session
from app.models.user import User if 'language' in request.session:
if not User.query.filter_by(email='admin@edu-space.com').first(): return request.session['language']
admin = User(
email='admin@edu-space.com', # 3. Check browser language preference
name='System Administrator', return request.accept_languages.best_match(app.config['LANGUAGES']) or app.config['BABEL_DEFAULT_LOCALE']
role='admin'
) # Configure Babel
admin.set_password('admin123') # Change this in production babel.init_app(app, locale_selector=get_locale)
db.session.add(admin)
db.session.commit()
return app return app
+6
View File
@@ -0,0 +1,6 @@
from .auth import auth_bp
from .classrooms import classrooms_bp
from .main import main_bp
from .schedule import schedule_bp
__all__ = ['auth_bp', 'classrooms_bp', 'main_bp', 'schedule_bp']
+11 -2
View File
@@ -1,4 +1,4 @@
from flask import Blueprint, render_template, jsonify from flask import Blueprint, render_template, jsonify, redirect, url_for, session, request
from flask_login import login_required, current_user from flask_login import login_required, current_user
from app.models.reservation import Reservation from app.models.reservation import Reservation
from app.models.classroom import Classroom from app.models.classroom import Classroom
@@ -6,6 +6,7 @@ from app.models.subject import Subject
from app.models.subject import Commission from app.models.subject import Commission
from datetime import datetime, timedelta from datetime import datetime, timedelta
from sqlalchemy import func from sqlalchemy import func
from flask_babel import gettext as _
main_bp = Blueprint('main', __name__) main_bp = Blueprint('main', __name__)
@@ -105,4 +106,12 @@ def dashboard_stats_api():
'today_reservations': today_reservations, 'today_reservations': today_reservations,
'week_reservations': week_reservations, 'week_reservations': week_reservations,
'monthly_data': list(reversed(monthly_data)) 'monthly_data': list(reversed(monthly_data))
}) })
@main_bp.route('/set_language/<language>')
def set_language(language=None):
if language not in ['en', 'es']:
language = 'en'
session['language'] = language
return redirect(request.referrer or url_for('main.dashboard'))
+27 -2
View File
@@ -1,9 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" data-bs-theme="dark"> <html lang="{{ session.language or request.accept_languages.best_match(['es', 'en']) or 'en' }}" data-bs-theme="dark">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Edu-Space Admin{% endblock %}</title> <title>{% block title %}{% trans %}Edu-Space Admin{% endtrans %}{% endblock %}</title>
<!-- Bootstrap 5 CSS --> <!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
@@ -69,6 +69,31 @@
</ul> </ul>
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
<i class="bi bi-person-circle"></i> {{ current_user.first_name }} {{ current_user.last_name }}
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{{ url_for('auth.profile') }}">
<i class="bi bi-person"></i> Profile
</a></li>
<li><a class="dropdown-item" href="{{ url_for('auth.change_password') }}">
<i class="bi bi-key"></i> Change Password
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="{{ url_for('auth.logout') }}">
<i class="bi bi-box-arrow-right"></i> Logout
</a></li>
</ul>
</li>
</ul>
<ul class="navbar-nav">
<!-- Language Switcher -->
<li class="nav-item">
{% include 'partials/language_switcher.html' %}
</li>
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"> <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
<i class="bi bi-person-circle"></i> {{ current_user.first_name }} {{ current_user.last_name }} <i class="bi bi-person-circle"></i> {{ current_user.first_name }} {{ current_user.last_name }}
+7 -7
View File
@@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Dashboard - Edu-Space Admin{% endblock %} {% block title %}{% trans %}Dashboard - Edu-Space Admin{% endtrans %}{% endblock %}
{% block content %} {% block content %}
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
@@ -9,8 +9,8 @@
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">
<h1 class="h3 mb-0"> <h1 class="h3 mb-0">
<i class="bi bi-speedometer2 text-primary"></i> Dashboard <i class="bi bi-speedometer2 text-primary"></i> {% trans %}Dashboard{% endtrans %}
<small class="text-muted ms-2">Overview</small> <small class="text-muted ms-2">{% trans %}Overview{% endtrans %}</small>
</h1> </h1>
</div> </div>
</div> </div>
@@ -22,7 +22,7 @@
<div class="card-body"> <div class="card-body">
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<div> <div>
<h6 class="text-muted mb-2">Total Classrooms</h6> <h6 class="text-muted mb-2">{% trans %}Total Classrooms{% endtrans %}</h6>
<h2 class="mb-0">{{ stats.total_classrooms }}</h2> <h2 class="mb-0">{{ stats.total_classrooms }}</h2>
</div> </div>
<div class="bg-primary bg-opacity-10 rounded-circle p-3"> <div class="bg-primary bg-opacity-10 rounded-circle p-3">
@@ -38,7 +38,7 @@
<div class="card-body"> <div class="card-body">
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<div> <div>
<h6 class="text-muted mb-2">Total Subjects</h6> <h6 class="text-muted mb-2">{% trans %}Total Subjects{% endtrans %}</h6>
<h2 class="mb-0">{{ stats.total_subjects }}</h2> <h2 class="mb-0">{{ stats.total_subjects }}</h2>
</div> </div>
<div class="bg-success bg-opacity-10 rounded-circle p-3"> <div class="bg-success bg-opacity-10 rounded-circle p-3">
@@ -54,7 +54,7 @@
<div class="card-body"> <div class="card-body">
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<div> <div>
<h6 class="text-muted mb-2">Today's Reservations</h6> <h6 class="text-muted mb-2">{% trans %}Today's Reservations{% endtrans %}</h6>
<h2 class="mb-0">{{ stats.today_reservations }}</h2> <h2 class="mb-0">{{ stats.today_reservations }}</h2>
</div> </div>
<div class="bg-info bg-opacity-10 rounded-circle p-3"> <div class="bg-info bg-opacity-10 rounded-circle p-3">
@@ -70,7 +70,7 @@
<div class="card-body"> <div class="card-body">
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<div> <div>
<h6 class="text-muted mb-2">This Week</h6> <h6 class="text-muted mb-2">{% trans %}This Week{% endtrans %}</h6>
<h2 class="mb-0">{{ stats.week_reservations }}</h2> <h2 class="mb-0">{{ stats.week_reservations }}</h2>
</div> </div>
<div class="bg-warning bg-opacity-10 rounded-circle p-3"> <div class="bg-warning bg-opacity-10 rounded-circle p-3">
@@ -0,0 +1,27 @@
{% if config.LANGUAGES|length > 1 %}
<div class="dropdown language-switcher">
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" type="button" id="languageDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-globe"></i>
{% if session.language == 'es' or (session.language is none and request.accept_languages.best_match(['es', 'en']) == 'es') %}
Spanish
{% else %}
English
{% endif %}
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="languageDropdown">
{% set current_lang = session.language or request.accept_languages.best_match(['es', 'en']) or 'en' %}
<li>
<a class="dropdown-item {% if current_lang == 'en' %}active{% endif %}"
href="{{ url_for('main.set_language', language='en') }}">
<i class="bi bi-check-circle-fill text-primary me-2"></i>English
</a>
</li>
<li>
<a class="dropdown-item {% if current_lang == 'es' %}active{% endif %}"
href="{{ url_for('main.set_language', language='es') }}">
<i class="bi bi-check-circle-fill text-primary me-2"></i>Español
</a>
</li>
</ul>
</div>
{% endif %}
+3
View File
@@ -0,0 +1,3 @@
[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.i18n
+7 -1
View File
@@ -12,4 +12,10 @@ class Config:
# Upload settings # Upload settings
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max file size MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max file size
UPLOAD_FOLDER = 'app/static/uploads' UPLOAD_FOLDER = 'app/static/uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
# Internationalization
LANGUAGES = ['en', 'es']
BABEL_DEFAULT_LOCALE = 'en'
BABEL_DEFAULT_TIMEZONE = 'UTC'
BABEL_TRANSLATION_DIRECTORIES = 'translations'
+2
View File
@@ -4,6 +4,7 @@ Flask-Login==0.6.3
Flask-WTF==1.2.1 Flask-WTF==1.2.1
Flask-Migrate==4.0.5 Flask-Migrate==4.0.5
Flask-CORS==6.0.2 Flask-CORS==6.0.2
Flask-Babel==4.0.0
WTForms==3.1.0 WTForms==3.1.0
Werkzeug==3.0.1 Werkzeug==3.0.1
SQLAlchemy==2.0.23 SQLAlchemy==2.0.23
@@ -14,3 +15,4 @@ python-dateutil==2.8.2
Pillow==10.1.0 Pillow==10.1.0
email_validator>=2.0.0 email_validator>=2.0.0
psycopg2-binary==2.9.9 psycopg2-binary==2.9.9
Babel==2.14.0
+114
View File
@@ -0,0 +1,114 @@
# English (en) translations for Edu-Space Admin
#
msgid ""
msgstr ""
"Project-Id-Version: Edu-Space Admin 1.0\n"
"POT-Creation-Date: 2024-01-01 00:00+0000\n"
"PO-Revision-Date: 2024-01-01 00:00+0000\n"
"Last-Translator: Admin <admin@edu-space.com>\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pybabel\n"
#: app/templates/base.html:6
msgid "Edu-Space Admin"
msgstr "Edu-Space Admin"
#: app/templates/dashboard.html:3
msgid "Dashboard - Edu-Space Admin"
msgstr "Dashboard - Edu-Space Admin"
#: app/templates/dashboard.html:12
msgid "Dashboard"
msgstr "Dashboard"
#: app/templates/dashboard.html:13
msgid "Overview"
msgstr "Overview"
#: app/templates/dashboard.html:25
msgid "Total Classrooms"
msgstr "Total Classrooms"
#: app/templates/dashboard.html:41
msgid "Total Subjects"
msgstr "Total Subjects"
#: app/templates/dashboard.html:57
msgid "Today's Reservations"
msgstr "Today's Reservations"
#: app/templates/dashboard.html:73
msgid "This Week"
msgstr "This Week"
#: app/templates/dashboard.html:91
msgid "Monthly Reservations"
msgstr "Monthly Reservations"
#: app/templates/dashboard.html:105
msgid "Quick Actions"
msgstr "Quick Actions"
#: app/templates/dashboard.html:111
msgid "Add Classroom"
msgstr "Add Classroom"
#: app/templates/dashboard.html:114
msgid "New Reservation"
msgstr "New Reservation"
#: app/templates/dashboard.html:117
msgid "Today's Schedule"
msgstr "Today's Schedule"
#: app/templates/dashboard.html:120
msgid "View Calendar"
msgstr "View Calendar"
#: app/templates/dashboard.html:135
msgid "Upcoming Reservations (Next 7 Days)"
msgstr "Upcoming Reservations (Next 7 Days)"
#: app/templates/dashboard.html:138
msgid "View All"
msgstr "View All"
#: app/templates/dashboard.html:148
#: app/templates/dashboard.html:149
#: app/templates/dashboard.html:150
#: app/templates/dashboard.html:151
#: app/templates/dashboard.html:152
#: app/templates/dashboard.html:153
msgid "Date & Time"
msgstr "Date & Time"
#: app/templates/dashboard.html:149
msgid "Classroom"
msgstr "Classroom"
#: app/templates/dashboard.html:150
msgid "Subject"
msgstr "Subject"
#: app/templates/dashboard.html:151
msgid "Teacher"
msgstr "Teacher"
#: app/templates/dashboard.html:152
msgid "Status"
msgstr "Status"
#: app/templates/dashboard.html:153
msgid "Actions"
msgstr "Actions"
#: app/templates/dashboard.html:171
msgid "View"
msgstr "View"
#: app/templates/dashboard.html:182
msgid "No upcoming reservations in the next 7 days"
msgstr "No upcoming reservations in the next 7 days"
+114
View File
@@ -0,0 +1,114 @@
# Spanish (es) translations for Edu-Space Admin
#
msgid ""
msgstr ""
"Project-Id-Version: Edu-Space Admin 1.0\n"
"POT-Creation-Date: 2024-01-01 00:00+0000\n"
"PO-Revision-Date: 2024-01-01 00:00+0000\n"
"Last-Translator: Admin <admin@edu-space.com>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pybabel\n"
#: app/templates/base.html:6
msgid "Edu-Space Admin"
msgstr "Admin Edu-Space"
#: app/templates/dashboard.html:3
msgid "Dashboard - Edu-Space Admin"
msgstr "Panel - Admin Edu-Space"
#: app/templates/dashboard.html:12
msgid "Dashboard"
msgstr "Panel"
#: app/templates/dashboard.html:13
msgid "Overview"
msgstr "Resumen"
#: app/templates/dashboard.html:25
msgid "Total Classrooms"
msgstr "Total de Aulas"
#: app/templates/dashboard.html:41
msgid "Total Subjects"
msgstr "Total de Materias"
#: app/templates/dashboard.html:57
msgid "Today's Reservations"
msgstr "Reservas de Hoy"
#: app/templates/dashboard.html:73
msgid "This Week"
msgstr "Esta Semana"
#: app/templates/dashboard.html:91
msgid "Monthly Reservations"
msgstr "Reservas Mensuales"
#: app/templates/dashboard.html:105
msgid "Quick Actions"
msgstr "Acciones Rápidas"
#: app/templates/dashboard.html:111
msgid "Add Classroom"
msgstr "Agregar Aula"
#: app/templates/dashboard.html:114
msgid "New Reservation"
msgstr "Nueva Reserva"
#: app/templates/dashboard.html:117
msgid "Today's Schedule"
msgstr "Horario de Hoy"
#: app/templates/dashboard.html:120
msgid "View Calendar"
msgstr "Ver Calendario"
#: app/templates/dashboard.html:135
msgid "Upcoming Reservations (Next 7 Days)"
msgstr "Próximas Reservas (Próximos 7 Días)"
#: app/templates/dashboard.html:138
msgid "View All"
msgstr "Ver Todas"
#: app/templates/dashboard.html:148
#: app/templates/dashboard.html:149
#: app/templates/dashboard.html:150
#: app/templates/dashboard.html:151
#: app/templates/dashboard.html:152
#: app/templates/dashboard.html:153
msgid "Date & Time"
msgstr "Fecha y Hora"
#: app/templates/dashboard.html:149
msgid "Classroom"
msgstr "Aula"
#: app/templates/dashboard.html:150
msgid "Subject"
msgstr "Materia"
#: app/templates/dashboard.html:151
msgid "Teacher"
msgstr "Profesor"
#: app/templates/dashboard.html:152
msgid "Status"
msgstr "Estado"
#: app/templates/dashboard.html:153
msgid "Actions"
msgstr "Acciones"
#: app/templates/dashboard.html:171
msgid "View"
msgstr "Ver"
#: app/templates/dashboard.html:182
msgid "No upcoming reservations in the next 7 days"
msgstr "No hay próximas reservas en los próximos 7 días"