Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# ¿Cómo trabajamos en SkillStat?

Este documento describe cómo organizamos el trabajo, cómo nos comunicamos
a través del historial de cambios y cómo escribimos código. Lo leemos todos
antes de tocar cualquier archivo del repositorio.

## Ramas del repositorio

Tenemos dos ramas permanentes que nunca se modifican directamente:

- `main` - contiene el código que está en producción. Solo recibe cambios
desde `develop` a través de un Pull Request revisado y aprobado.
- `develop` - es la rama de trabajo activo del equipo. Aquí integramos
todo antes de que llegue a producción.

Para cualquier tarea nueva creamos una rama temporal que nace desde
`develop` y muere cuando hacemos merge:

| Tipo | Cuándo usarla | Ejemplo |
|------|---------------|---------|
| `feature/` | Funcionalidad nueva | `feature/jwt-authentication` |
| `fix/` | Corrección de error en desarrollo | `fix/city-normalization` |
| `hotfix/` | Corrección urgente en producción | `hotfix/token-exposure` |
| `refactor/` | Reorganización sin cambiar comportamiento | `refactor/ingestion-cleanup` |
| `chore/` | Mantenimiento, dependencias, configuración | `chore/update-requirements` |
| `docs/` | Documentación únicamente | `docs/guia-entorno` |
| `test/` | Pruebas nuevas o actualizadas | `test/trends-service-unit` |
| `release/` | Preparación de versión para producción | `release/v1.0.0` |

Los nombres van en minúsculas, con guiones y sin acentos.

## Flujo de trabajo paso a paso

Cada vez que vamos a trabajar en algo seguimos estos pasos:

```bash
# Nos aseguramos de que develop esté al día antes de empezar
git checkout develop
git pull origin develop

# Creamos nuestra rama desde develop
git checkout -b feature/nombre-descriptivo

# Trabajamos y guardamos nuestros avances en commits
git add .
git commit -m "feat(scope): descripcion del cambio"

# Mantenemos nuestra rama sincronizada con develop mientras trabajamos
git fetch origin
git rebase origin/develop

# Subimos nuestra rama y abrimos el Pull Request hacia develop
git push origin feature/nombre-descriptivo
```

Nadie toca `main` ni `develop` directamente. Todo pasa por un Pull Request.
Nadie hace merge de su propio trabajo sin que alguien más lo haya revisado.
Después del merge borramos la rama.

## Formato de commits

Cada commit sigue esta estructura:

```
tipo(alcance): descripcion breve en imperativo
```

Los tipos disponibles:

| Tipo | Cuándo usarlo |
|------|---------------|
| `feat` | Funcionalidad nueva |
| `fix` | Corrección de error |
| `docs` | Cambio de documentación |
| `style` | Formato sin cambio de lógica |
| `refactor` | Reorganización sin cambio de comportamiento |
| `test` | Agregar o modificar pruebas |
| `chore` | Mantenimiento, dependencias, configuración |
| `perf` | Mejora de rendimiento |

Ejemplos reales del proyecto:

```
feat(auth): implement JWT login and token validation
fix(nlp): resolve phrase matcher failure on multi-word skills
chore(deps): add Flask-JWT-Extended and spaCy to requirements
docs(contributing): add commit format and branching guide
test(trends): add unit tests for weekly growth rate calculation
refactor(ingestion): isolate Adzuna client from ingestion logic
```

## Cómo escribimos comentarios en el código

Comentamos solo cuando el código por sí solo no deja clara la intención
detrás de lo que hace. Si el código se entiende solo, no ponemos comentario.

El comentario explica el por qué, no el qué. El qué ya lo dice el código.

Escribimos en primera persona del plural, en español, sin tecnicismos,
sin emojis y sin numerar los pasos. Aplica a todos los lenguajes del
proyecto: Python, JavaScript, CSS e HTML.

Así escribimos:

```python
# Descartamos las vacantes que ya procesamos para no duplicar los resultados.
pending = jobs.filter(processed=False)

# Guardamos el hash para detectar si la descripcion cambio en la fuente original.
job.description_hash = compute_hash(raw_description)

# Si el umbral ya se supero avisamos al usuario antes de continuar.
if demand_count >= alert.threshold:
notify_user(alert)
```

```javascript
// Esperamos el token antes de hacer la peticion para no enviar una solicitud sin autenticar.
const token = await getAuthToken();

// Mostramos el mensaje directamente para que el usuario sepa que paso sin tener que buscar.
showErrorMessage(error.message);
```

Así no escribimos:

```python
# 1. Filtramos los jobs
# Funcion para filtrar vacantes usando ORM
# Filter unprocessed jobs from database
# 🔍 Buscamos vacantes sin procesar
```

## Reglas que no se negocian

- Nadie hace push directo a `main` ni a `develop`.
- Nadie hace merge de su propio Pull Request sin revision previa.
- Un commit representa un solo cambio logico. No mezclamos cosas distintas.
- No dejamos deuda tecnica sin documentar. Si algo quedo incompleto
abrimos un issue o lo anotamos en el PR.
- Si una rama lleva mas de una semana sin actividad, revisamos si sigue
siendo necesaria o la cerramos.
29 changes: 29 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# backend/

Aqui vive la API REST, los servicios de negocio, los modelos de datos,
los repositorios y los clientes de APIs externas.

## Estructura principal

```
backend/
├── app/ # El nucleo de la aplicacion Flask
├── clients/ # Clientes para APIs externas
├── scheduler/ # Procesos automatizados periodicos
├── migrations/ # Migraciones de base de datos
├── tests/ # Pruebas unitarias e integracion
└── run.py # Punto de entrada del servidor
```

## Como arranca la aplicacion

El archivo `run.py` inicia el servidor. La aplicacion se construye
en `app/__init__.py` usando el patron application factory, que permite
crear instancias independientes para desarrollo, produccion y pruebas.
Comment on lines +20 to +22

## Variables de entorno

Todas las configuraciones sensibles (claves de API, URL de base de datos,
secreto JWT) viven en el archivo `.env`. Nunca se sube al repositorio.
El archivo `.env.example` muestra que variables se necesitan sin revelar
sus valores reales.
26 changes: 26 additions & 0 deletions backend/app/controllers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# controllers/

Aqui viven los Blueprints de Flask. Cada archivo corresponde a un
dominio de la aplicacion y agrupa las rutas relacionadas con ese dominio.

## Lo que va aqui

- Las rutas HTTP (endpoints) organizadas por Blueprint
- La validacion del formato de la peticion entrante
- La llamada al servicio correspondiente
- La construccion de la respuesta que se devuelve al cliente

## Lo que no va aqui

La logica de negocio no vive en los controladores. Si nos encontramos
escribiendo condiciones complejas o consultas a la base de datos dentro
de un Blueprint, eso pertenece a un servicio o un repositorio.

## Los Blueprints del proyecto

| Archivo | Dominio |
|---------|---------|
| `auth_bp.py` | Registro, login y gestion de sesion |
| `panorama_bp.py` | Tendencias, rankings y datos del dashboard |
| `alerts_bp.py` | Configuracion y gestion de alertas del usuario |
| `admin_bp.py` | Administracion de usuarios y respaldos |
22 changes: 22 additions & 0 deletions backend/app/models/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# models/

Aqui definimos la estructura de los datos del sistema usando SQLAlchemy.
Cada archivo representa una tabla de la base de datos.

## Lo que va aqui

- La definicion de columnas, tipos de dato y restricciones
- Las relaciones entre tablas (claves foraneas, backrefs)
- Las restricciones de integridad (UNIQUE, CHECK, NOT NULL)

## Lo que no va aqui

La logica de negocio no vive en los modelos. Los modelos describen
la forma de los datos, no lo que hacemos con ellos.

## Las entidades del proyecto

`category`, `city`, `skill`, `job`, `job_skill`, `user`, `alert`,
`trend_snapshot`, `backup`.

El diagrama entidad-relacion completo esta en `docs/diagramas/er.png`.
23 changes: 23 additions & 0 deletions backend/app/repositories/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# repositories/

Aqui vive toda la logica de acceso a la base de datos. Los repositorios
son el unico lugar del sistema donde hablamos directamente con PostgreSQL
a traves del ORM.

## Lo que va aqui

- Las consultas a la base de datos (lecturas, escrituras, filtros)
- La logica de paginacion y ordenamiento
- Las operaciones CRUD de cada entidad

## Lo que no va aqui

La logica de negocio no vive aqui. Si necesitamos hacer algo con los
datos despues de leerlos, ese trabajo pertenece al servicio que llamo
al repositorio.

## Como funciona

`base_repository.py` contiene las operaciones comunes (guardar, buscar
por ID, listar, eliminar). Cada repositorio especifico extiende esa base
y agrega las consultas particulares que su entidad necesita.
28 changes: 28 additions & 0 deletions backend/app/services/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# services/

Aqui vive la logica de negocio del sistema. Los servicios son el
corazon de SkillStat: procesan datos, toman decisiones y coordinan
el trabajo entre los distintos componentes.

## Lo que va aqui

- La logica que extrae habilidades de descripciones de vacantes
- La logica que calcula tendencias y metricas del mercado
- La logica que evalua alertas y decide cuando notificar
- La coordinacion entre repositorios y clientes externos

## Lo que no va aqui

Los servicios no conocen Flask. No manejan peticiones HTTP ni
construyen respuestas JSON. Tampoco acceden directamente a la base
de datos: para eso usan los repositorios.

## Los servicios del proyecto

| Archivo | Que hace |
|---------|----------|
| `ingestion_service.py` | Recopila vacantes desde la API de Adzuna |
| `skills_extraction_service.py` | Extrae habilidades tecnicas de descripciones |
| `market_trends_service.py` | Calcula metricas de demanda y tendencias |
| `alerts_service.py` | Evalua alertas y coordina las notificaciones |
| `backup_service.py` | Genera y restaura respaldos de la base de datos |
23 changes: 23 additions & 0 deletions backend/clients/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# clients/

Aqui viven los clientes que se comunican con APIs externas. Cada archivo
envuelve una sola API y expone metodos claros para que los servicios
puedan usarla sin conocer los detalles de la comunicacion HTTP.

## Lo que va aqui

- La logica de autenticacion con cada API externa
- El manejo de errores y reintentos de conexion
- La transformacion de la respuesta al formato que el sistema necesita

## Los clientes del proyecto

| Archivo | API que envuelve |
|---------|-----------------|
| `adzuna_client.py` | Adzuna Jobs API - fuente de vacantes |
| `nominatim_client.py` | Nominatim / OpenStreetMap - geocodificacion |
| `sendgrid_client.py` | SendGrid - envio de correos de alerta |
| `base_client.py` | Logica compartida: reintentos, timeouts, headers |

Los servicios nunca llaman directamente a `requests` o `httpx`.
Siempre usan estos clientes.
23 changes: 23 additions & 0 deletions data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# data/

Aqui viven los activos de datos que usa el modulo de procesamiento
de lenguaje natural. No es codigo de la aplicacion: son los archivos
que alimentan al extractor de habilidades.

## Lo que hay aqui

- `dictionaries/skills_esco.jsonl` - habilidades tecnicas de la
taxonomia ESCO (base de datos oficial de la Union Europea). Se
descarga y procesa con el script `scripts/build_dictionary.py`.
- `dictionaries/skills_custom.jsonl` - habilidades que el equipo
agrega manualmente cuando la taxonomia base no las incluye.
- `dictionaries/skill_aliases.json` - mapeo de abreviaciones y
variantes al nombre canonico. Por ejemplo: `"JS": "JavaScript"`.
- `samples/vacantes_sample.json` - vacantes de prueba para
desarrollar y probar el extractor sin consumir la API real.

## Como agregar una habilidad nueva

Si encontramos una habilidad que el sistema no detecta, la agregamos
en `skills_custom.jsonl` siguiendo el mismo formato que el resto
del archivo. Si es una abreviacion, la agregamos en `skill_aliases.json`.
46 changes: 46 additions & 0 deletions docs/ARQUITECTURA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Arquitectura de SkillStat

SkillStat es una plataforma de inteligencia de mercado laboral que
recopila vacantes tecnologicas, extrae habilidades con procesamiento
de lenguaje natural y presenta los resultados en un dashboard analitico
llamado Panorama.

## Como esta organizado el repositorio

```
SkillStat/
├── frontend/ # Interfaz web: vistas, estilos y logica del cliente
├── backend/ # API REST, servicios, modelos y acceso a datos
├── data/ # Activos de datos para el procesamiento NLP
├── scripts/ # Herramientas de uso manual para el equipo
└── docs/ # Documentacion interna del proyecto
```

## Las capas del backend

El backend sigue una arquitectura en capas donde cada capa tiene una
responsabilidad clara y no invade la del resto:

| Capa | Donde vive | Que hace |
|------|-----------|----------|
| Controladores | `backend/app/controllers/` | Recibe peticiones HTTP y devuelve respuestas |
| Servicios | `backend/app/services/` | Contiene la logica de negocio del sistema |
| Repositorios | `backend/app/repositories/` | Habla con la base de datos |
| Modelos | `backend/app/models/` | Define la estructura de los datos |
| Clientes | `backend/clients/` | Se comunica con APIs externas |

Los controladores llaman a los servicios. Los servicios llaman a los
repositorios. Los repositorios hablan con la base de datos. Los clientes
son usados por los servicios para comunicarse con el exterior.

## Donde encontrar cada cosa

- Las rutas de la API viven en `backend/app/controllers/`
- La logica que procesa vacantes y extrae habilidades vive en `backend/app/services/`
- Las tablas de la base de datos estan definidas en `backend/app/models/`
- Las consultas a la base de datos viven en `backend/app/repositories/`
- Las conexiones con Adzuna, Nominatim y SendGrid viven en `backend/clients/`
- Las vistas HTML del Panorama y el resto de pantallas viven en `frontend/views/`
- Los archivos de estilos estan en `frontend/assets/css/`
- La logica del cliente esta en `frontend/assets/js/`
- El diccionario de habilidades tecnologicas vive en `data/dictionaries/`
Loading