GestionTablets/docs/HISTORIA_PROYECTO_IVAN.md

638 lines
19 KiB
Markdown
Raw Normal View History

# 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](#los-inicios)
2. [La Arquitectura Base](#la-arquitectura-base)
3. [Proveedores y Tecnologías](#proveedores-y-tecnologías)
4. [Hitos del Desarrollo](#hitos-del-desarrollo)
5. [Internacionalización (i18n)](#internacionalización-i18n)
6. [Identidad Corporativa](#identidad-corporativa)
7. [Lecciones Aprendidas](#lecciones-aprendidas)
8. [Estado Actual](#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:
```python
# 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:**
```sql
-- 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**:
```python
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
```python
# 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:**
```javascript
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**:
```html
<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
### Logo
- **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
```bash
# 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
```bash
# Desarrollo
python3 app.py
# Acceder a:
# http://localhost:5000 - Español (por defecto)
# http://localhost:5000?lang=en - Inglés
```
### Pruebas
```bash
# 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*