GestionTablets/docs/HISTORIA_PROYECTO_IVAN.md
ijuanes d184b0423e docs: add project history narrative in Spanish for Ivan
- Complete project narrative from CLI to web application
- Covers all milestones: i18n, responsive design, corporate identity
- Includes providers, technologies, and lessons learned
- Written in Spanish for Ivan
- Omits sensitive information (API keys, credentials)
2026-06-20 15:00:24 +01:00

19 KiB

Historia del Proyecto GestionTablets

Un relato técnico para Iván sobre el desarrollo del Sistema de Gestión de Tablets


📖 Índice

  1. Los Inicios
  2. La Arquitectura Base
  3. Proveedores y Tecnologías
  4. Hitos del Desarrollo
  5. Internacionalización (i18n)
  6. Identidad Corporativa
  7. Lecciones Aprendidas
  8. Estado Actual

🚀 Los Inicios

El proyecto GestionTablets nació como una herramienta interna para gestionar el préstamo de tablets y otros dispositivos en un entorno de equipo pequeño (5 usuarios técnicos). La idea inicial era simple: llevar un control claro de qué dispositivo estaba con qué persona, cuándo se prestó y cuándo se devolvió.

Contexto Inicial

  • Tipo de proyecto: Herramienta interna (no aplicación pública)
  • Usuarios objetivo: 5 usuarios técnicos
  • Requisitos principales:
    • Registrar tablets y otros dispositivos
    • Gestionar préstamos a usuarios
    • Historial de transacciones
    • Interfaz sencilla y funcional

Primera Versión: CLI

El proyecto comenzó con una versión CLI (Command Line Interface) en Python puro con SQLite. Esta versión, aunque funcional, tenía limitaciones:

# Ejemplo de la versión CLI inicial
import sqlite3
from datetime import datetime

def add_tablet(brand, model, serial_number):
    """Añadir una nueva tablet al inventario"""
    conn = sqlite3.connect('tablets.db')
    cursor = conn.cursor()
    cursor.execute('''
        INSERT INTO tablets (brand, model, serial_number, status)
        VALUES (?, ?, ?, 'available')
    ''', (brand, model, serial_number))
    conn.commit()
    print(f"✓ Tablet añadida: {brand} {model} ({serial_number})")

Limitaciones de la CLI:

  • Interfaz no intuitiva para usuarios no técnicos
  • Sin visualización de datos en formato amigable
  • Dificultad para ver el estado actual de préstamos

🏗️ La Arquitectura Base

Transición a Web: Flask

Se decidió migrar a una aplicación web usando Flask, un micro-framework de Python, para proporcionar una interfaz más amigable.

Estructura del proyecto:

GestionTablets/
├── app.py                  # Aplicación principal Flask
├── minimal_app.py          # Versión CLI (deprecada)
├── templates/              # Plantillas HTML (Jinja2)
│   ├── base.html           # Plantilla base
│   ├── index.html          # Página principal
│   ├── add_tablet.html     # Formulario añadir tablet
│   ├── add_user.html       # Formulario añadir usuario
│   ├── loan_tablet.html    # Formulario préstamo
│   └── ...
├── static/                 # Archivos estáticos
│   └── Schamann.png        # Logo corporativo
├── translations/           # Archivos de traducción
│   ├── es/LC_MESSAGES.mo
│   └── es/LC_MESSAGES.po
├── docs/                   # Documentación
│   ├── CORPORATE_DESIGN.md  # Diseño corporativo
│   ├── FRONTEND_OPTIONS.md # Opciones de frontend
│   ├── MIGRATION_TO_POSTGRES.md
│   └── RESPONSIVE_CSS.md
├── tests/                  # Suite de pruebas
│   ├── conftest.py
│   ├── test_core.py
│   ├── test_edge_cases.py
│   └── test_minimal_app.py
├── tablets.db              # Base de datos SQLite
├── pyproject.toml          # Configuración del proyecto
├── requirements.txt        # Dependencias
└── README.md               # Documentación principal

Base de Datos: SQLite

Se optó por SQLite como base de datos por su simplicidad y porque es perfecta para:

  • Aplicaciones internas con pocos usuarios
  • No requiere servidor de base de datos
  • Fácil de mantener y hacer backup
  • Incluida en Python (sin dependencias adicionales)

Esquema de la base de datos:

-- Tablets
CREATE TABLE tablets (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    brand TEXT NOT NULL,
    model TEXT NOT NULL,
    serial_number TEXT UNIQUE NOT NULL,
    status TEXT DEFAULT 'available',
    notes TEXT
);

-- Usuarios
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT,
    phone TEXT,
    identification TEXT UNIQUE
);

-- Préstamos
CREATE TABLE loans (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    tablet_id INTEGER NOT NULL,
    user_id INTEGER NOT NULL,
    loan_date TEXT NOT NULL,
    return_date TEXT,
    status TEXT DEFAULT 'active',
    FOREIGN KEY (tablet_id) REFERENCES tablets(id),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

-- Dispositivos no prestables
CREATE TABLE non_loanable_devices (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    brand TEXT NOT NULL,
    model TEXT NOT NULL,
    serial_number TEXT UNIQUE NOT NULL,
    device_type TEXT NOT NULL,
    location TEXT,
    status TEXT DEFAULT 'available',
    notes TEXT,
    purchase_date TEXT,
    purchase_cost REAL
);

🛠️ Proveedores y Tecnologías

Lenguaje y Framework

  • Python 3.14+: Lenguaje principal del proyecto
  • Flask 3.x: Framework web principal
  • Flask-Babel 4.0.0: Para internacionalización (i18n)
  • Jinja2: Motor de plantillas

Frontend

  • Tailwind CSS: Framework CSS para estilos (vía CDN)
  • HTMX: Para interactividad sin JavaScript (en algunas partes)
  • Alpine.js: Considerado pero no implementado

Base de Datos

  • SQLite: Base de datos principal (embebida)
  • PostgreSQL: Opción futura documentada (ver docs/MIGRATION_TO_POSTGRES.md)

Herramientas de Desarrollo

  • pytest: Framework de pruebas
  • pytest-cov: Cobertura de código
  • Git: Control de versiones
  • GitHub/GitLab: Hosting del repositorio (servidor propio en hq.ijuanes.ovh:3333)

Infraestructura

  • Servidor Git: http://hq.ijuanes.ovh:3333
  • Usuario: ijuanes
  • Repositorio: GestionTablets

🎯 Hitos del Desarrollo

Fase 1: Versión CLI Inicial

Fecha: Junio 2026 (inicio)

  • Creación de minimal_app.py con funciones básicas
  • Implementación de CRUD para tablets y usuarios
  • Gestión básica de préstamos
  • Base de datos SQLite

Commit representativo: Versión inicial del proyecto


Fase 2: Migración a Web

Fecha: Mediados de Junio 2026

  • Creación de app.py con Flask
  • Diseño de plantillas HTML con Jinja2
  • Implementación de rutas web:
    • / - Página principal con tablets disponibles
    • /add_tablet - Añadir nueva tablet
    • /add_user - Añadir nuevo usuario
    • /loan_tablet - Prestar tablet
    • /return_tablet/<id> - Devolver tablet
    • /history - Historial de préstamos

Mejoras:

  • Interfaz visual más intuitiva
  • Navegación entre páginas
  • Visualización de datos en tablas

Fase 3: Internacionalización (i18n)

Fecha: Junio 2026

Problema Inicial

La aplicación solo estaba en inglés y no tenía soporte para múltiples idiomas.

Solución Implementada

  1. Flask-Babel 4.0.0: Integración con Flask
  2. Estructura de traducción:
    translations/
    └── es/
        ├── LC_MESSAGES.mo  # Archivo binario
        └── LC_MESSAGES.po  # Archivo fuente
    
  3. Configuración:
    app.config['BABEL_DEFAULT_LOCALE'] = 'es'
    app.config['LANGUAGES'] = {'en': 'English', 'es': 'Español'}
    app.config['BABEL_TRANSLATION_DIRECTORIES'] = 'translations'
    

Problemas y Soluciones

Problema 1: Flask-Babel 4.0.0 cambió su API

  • @babel.localeselector - Eliminado
  • @babel.locale_selector - No existe
  • Babel(app, locale_selector=func) - Enfoque correcto

Solución: Reescribir la configuración para usar el nuevo API.

Commit: 49e4562 - fix(i18n): define get_locale function before passing to Babel constructor

Problema 2: Las plantillas no usaban el sistema de traducción

  • Muchas plantillas tenían texto en inglés hardcodeado
  • Ejemplo: add_tablet.html, add_user.html, loan_tablet.html

Solución: Actualizar todas las plantillas para usar {{ _('texto') }}

Commit: 2248237 - fix(i18n): enable translations in all templates

Problema 3: No se guardaba la preferencia de idioma

  • El idioma se perdía al navegar entre páginas
  • No había cookies ni sesión para persistir la selección

Solución: Implementar persistencia con cookies + sesión

# En app.py
from flask import session, make_response

def get_locale():
    # 1. Intentar obtener de cookie
    lang = request.cookies.get('language')
    if lang and lang in app.config['LANGUAGES']:
        return lang
    # 2. Intentar obtener de sesión
    if hasattr(request, 'session') and 'language' in request.session:
        return request.session['language']
    # 3. Default a Español
    return 'es'

@app.route('/set_language')
def set_language():
    lang = request.args.get('lang')
    if lang and lang in app.config['LANGUAGES']:
        session['language'] = lang
        resp = make_response(redirect(request.referrer or '/'))
        resp.set_cookie('language', lang, max_age=31536000)  # 1 año
        return resp
    return redirect(request.referrer or '/')

Commit: 2248237 - Cookie-based language persistence


Fase 4: Pruebas Unitarias

Fecha: Junio 2026

Se implementó una suite completa de pruebas con pytest:

Estructura:

tests/
├── __init__.py
├── conftest.py         # Fixtures de pytest
├── test_core.py        # 21 pruebas de funcionalidad principal
├── test_edge_cases.py  # 14 pruebas de casos límite
└── test_minimal_app.py # 11 pruebas de funciones de aplicación

Cobertura:

  • Pruebas de funciones CRUD
  • Casos límite (prestar tablet ya prestada, devolver préstamo inexistente)
  • Validación de datos (números de serie duplicados)
  • Manejo de errores

Resultado: 46 pruebas, todas pasando

Commits:

  • 83ac67f - test: add comprehensive unit test suite
  • dea2f23 - docs(ui): update RESPONSIVE_CSS.md with bug fix documentation

Fase 5: Diseño Responsive

Fecha: Junio 2026

Problema: La navegación desbordaba en pantallas pequeñas o al hacer zoom.

Solución: Implementar CSS responsive con Tailwind CSS

Cambios:

  • Añadir overflow-x: hidden a body y contenedores
  • Envolver tablas en contenedores con scroll horizontal
  • Añadir breakpoints para móvil, tablet y desktop
  • Crear docs/RESPONSIVE_CSS.md con documentación

Commits:

  • 28232db - fix(ui): add missing </style> tag in base.html
  • 927c323 - feat(ui): add responsive CSS for mobile accessibility
  • 26b77cf - fix(ui): prevent navigation overflow on zoom and small screens

Fase 6: Documentación de Migración a PostgreSQL

Fecha: Junio 2026

Aunque el proyecto usa SQLite, se documentó el camino para migrar a PostgreSQL:

Archivo: docs/MIGRATION_TO_POSTGRES.md

Contenido:

  • Comparación SQLite vs PostgreSQL
  • Pasos de migración
  • Cambios en el código
  • Configuración de conexión
  • Scripts SQL para crear tablas

Commit: dea2f23 - docs: add PostgreSQL migration guide


Fase 7: Identidad Corporativa

Fecha: 20 Junio 2026

Logo Corporativo

  • Archivo: assets/Schamann.png (175x161 píxeles)
  • Ubicación web: /static/Schamann.png
  • Integración: Añadido al header de la aplicación
  • Estilo: Forzado a 40px de altura con object-contain

Commit: c233040 - Add corporative logo

Paleta de Colores Corporativos

Se reemplazó la paleta de colores genérica por los colores corporativos:

Color Código Hex RGB Uso
Azul Cobalto #1338BE rgb(19, 56, 190) Headers, navegación
Naranja Tigre #FC6A03 rgb(252, 106, 3) Acentos, indicadores de carga
Verde Esmeralda #028A0F rgb(2, 138, 15) Estados de éxito

Configuración en Tailwind:

tailwind.config = {
    theme: {
        extend: {
            colors: {
                cobalt: {
                    50: '#f0f5ff',
                    500: '#1338BE',
                    // ... todas las sombras
                },
                tiger: {
                    50: '#fff8f0',
                    500: '#FC6A03',
                    // ...
                },
                emerald: {
                    50: '#f0fdf4',
                    500: '#028A0F',
                    // ...
                }
            }
        }
    }
}

Documentación: docs/CORPORATE_DESIGN.md

Commit: 8c1d12a - feat(ui): add corporate logo and update colors


🌍 Internacionalización (i18n)

Estado Actual

Completo:

  • Flask-Babel 4.0.0 configurado correctamente
  • Archivos de traducción creados (es/LC_MESSAGES.po, es/LC_MESSAGES.mo)
  • Todas las plantillas usan {{ _('texto') }}
  • Persistencia de idioma con cookies (1 año) + sesión
  • Español como idioma por defecto
  • Cambiador de idioma funcional en el header

Cómo Funciona

  1. Selección de idioma:

    • Por defecto: Español
    • URL: ?lang=en o ?lang=es
    • Cookie: language (1 año de expiración)
    • Sesión: session['language']
  2. Uso en plantillas:

    <h1>{{ _('Tablet Management System') }}</h1>
    <p>{{ _('Available Tablets') }}: {{ count }}</p>
    
  3. Traducciones:

    • Los archivos .po contienen las traducciones
    • Se compilan a .mo para uso en producción
    • Comando para actualizar: pybabel extract -o translations/messages.pot .

Idiomas Soportados

  • Español (es): Idioma por defecto
  • Inglés (en): Alternativa

🎨 Identidad Corporativa

  • Archivo: Schamann.png
  • Dimensiones: 175x161 píxeles
  • Formato: PNG con transparencia
  • Ubicación: /static/Schamann.png
  • Visualización: 40px de altura, manteniendo proporciones

Colores

Azul Cobalto (#1338BE)

  • Uso principal: Headers, navegación, acciones principales
  • Significado: Profesionalismo, confianza
  • Contraste: Texto blanco sobre fondos oscuros

Naranja Tigre (#FC6A03)

  • Uso principal: Acentos, indicadores de carga, acciones secundarias
  • Significado: Energía, acción
  • Contraste: Texto blanco sobre fondos

Verde Esmeralda (#028A0F)

  • Uso principal: Estados de éxito, confirmaciones
  • Significado: Éxito, positividad
  • Contraste: Texto blanco sobre fondos

Documentación

Todo el diseño corporativo está documentado en docs/CORPORATE_DESIGN.md:

  • Paleta de colores completa con sombras
  • Guías de uso
  • Ejemplos de implementación
  • Accesibilidad

📚 Lecciones Aprendidas

1. Versiones de Librerías

Lección: Siempre verificar la compatibilidad de versiones.

Ejemplo: Flask-Babel 4.0.0 cambió su API completamente. Lo que funcionaba en la versión 3.x no funcionaba en 4.x.

Solución: Leer la documentación de la versión específica que se está usando.


2. Internacionalización desde el Inicio

Lección: Implementar i18n desde el principio, no como un afterthought.

Problema: Muchas plantillas tenían texto hardcodeado en inglés.

Solución: Revisar todas las plantillas y envolver todo el texto visible con {{ _('...') }}.


3. Persistencia de Preferencias

Lección: Para aplicaciones internas, las cookies son suficientes.

Decisión: No implementar consentimiento de cookies (es una app interna).

Implementación: Cookie de 1 año + sesión para persistencia de idioma.


4. Diseño Responsive

Lección: Probar en diferentes tamaños de pantalla y niveles de zoom.

Problema: La navegación desbordaba al hacer zoom o en pantallas pequeñas.

Solución: Usar overflow-x: hidden y contenedores con scroll.


5. Pruebas Unitarias

Lección: Las pruebas dan confianza para hacer refactorización.

Beneficio: Al tener 46 pruebas pasando, pudimos hacer cambios en la configuración de i18n sin miedo a romper algo.


🎯 Estado Actual

Versión

  • Branch principal: develop
  • Commits recientes:
    • 2248237 - fix(i18n): enable translations in all templates and implement cookie-based language persistence
    • 8c1d12a - feat(ui): add corporate logo and update colors to Cobalt Blue, Tiger Orange, Emerald Green
    • c233040 - Add corporative logo
    • 49e4562 - fix(i18n): define get_locale function before passing to Babel constructor

Funcionalidades

Funcionalidad Estado Notas
Gestión de tablets CRUD completo
Gestión de usuarios CRUD completo
Préstamos Prestar y devolver
Historial Visualización completa
Internacionalización Español/Inglés
Persistencia de idioma Cookies + sesión
Diseño responsive Móvil, tablet, desktop
Logo corporativo Integración completa
Colores corporativos Paleta aplicada
Pruebas unitarias 46 pruebas pasando

Próximos Pasos (Opcionales)

  1. Migración a PostgreSQL: Documentación lista, solo falta implementar
  2. Más idiomas: Añadir francés, alemán, etc.
  3. Autenticación: Implementar login para usuarios
  4. Notificaciones: Recordatorios de devoluciones
  5. Exportación: Exportar datos a CSV/Excel

📞 Cómo Ejecutar el Proyecto

Requisitos

  • Python 3.14+
  • pip o uv (gestor de paquetes)

Instalación

# Clonar el repositorio
git clone http://hq.ijuanes.ovh:3333/ijuanes/GestionTablets.git
cd GestionTablets

# Crear entorno virtual
python3 -m venv .venv
source .venv/bin/activate  # Linux/Mac
# .venv\Scripts\activate  # Windows

# Instalar dependencias
pip install -r requirements.txt

# Inicializar base de datos
python3 app.py  # La base de datos se crea automáticamente

Ejecutar

# Desarrollo
python3 app.py

# Acceder a:
# http://localhost:5000 - Español (por defecto)
# http://localhost:5000?lang=en - Inglés

Pruebas

# Ejecutar todas las pruebas
python3 -m pytest tests/ -v

# Con cobertura
python3 -m pytest tests/ --cov=. --cov-report=html

🏁 Conclusión

El proyecto GestionTablets ha evolucionado de una simple CLI a una aplicación web completa con:

  • Internacionalización funcional
  • Identidad corporativa integrada
  • Diseño responsive
  • Suite de pruebas completa
  • Documentación exhaustiva

Es un ejemplo de cómo una herramienta interna puede ser profesional, mantenible y escalable, incluso con recursos limitados.

Para Iván: Este proyecto demuestra que con Python, Flask y SQLite se pueden crear aplicaciones robustas para equipos pequeños, y que la internacionalización y el diseño corporativo son importantes incluso en herramientas internas.


Documento generado el 20 de Junio de 2026 Para: Iván Proyecto: GestionTablets