From 4c0093c2b0569b6be360b888eebe9d937d0d83b3 Mon Sep 17 00:00:00 2001 From: Antoine Van Elstraete Date: Fri, 13 Mar 2026 13:45:19 +0100 Subject: [PATCH] feat: increase RTT from 18 to 19 - Update LeaveBalance model default rtt_total to 19 - Update all tests to verify 19 RTT instead of 18 - Update documentation (design and technical plan) - Update run.py to bind to 0.0.0.0 for external access - Update CLAUDE.md deployment instructions Co-Authored-By: Claude Haiku 4.5 --- CLAUDE.md | 5 +- app/models.py | 2 +- .../2026-03-11-repartition-types-jours.md | 186 ++++++++++++++++++ ...26-03-11-tableau-de-bord-travail-design.md | 4 +- .../2026-03-11-tableau-de-bord-travail.md | 4 +- run.py | 2 +- tests/test_leave_calc.py | 2 +- 7 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 docs/plans/2026-03-11-repartition-types-jours.md diff --git a/CLAUDE.md b/CLAUDE.md index 1657ffc..eafe029 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,8 +21,9 @@ python -m venv .venv # Run a single test .venv/bin/python -m pytest tests/test_routes.py::test_create_entry -v -# Production (Gunicorn) -SECRET_KEY= ./start.sh +# Production (systemd) — déployé dans /var/www/tableau-de-bord-pro/ +sudo systemctl edit --full tableau-de-bord-pro # configurer SECRET_KEY +sudo systemctl restart tableau-de-bord-pro # Git commit (GPG signing désactivé — pinentry inaccessible dans cet env) git -c commit.gpgsign=false commit -m "..." diff --git a/app/models.py b/app/models.py index 7d69a64..4054384 100644 --- a/app/models.py +++ b/app/models.py @@ -54,4 +54,4 @@ class LeaveBalance(db.Model): id: so.Mapped[int] = so.mapped_column(primary_key=True) year: so.Mapped[int] = so.mapped_column(sa.Integer, unique=True, nullable=False) conges_total: so.Mapped[int] = so.mapped_column(sa.Integer, default=28) - rtt_total: so.Mapped[int] = so.mapped_column(sa.Integer, default=18) + rtt_total: so.Mapped[int] = so.mapped_column(sa.Integer, default=19) diff --git a/docs/plans/2026-03-11-repartition-types-jours.md b/docs/plans/2026-03-11-repartition-types-jours.md new file mode 100644 index 0000000..34454eb --- /dev/null +++ b/docs/plans/2026-03-11-repartition-types-jours.md @@ -0,0 +1,186 @@ +# Répartition par type de jour — Carte rapport + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Ajouter une carte dans `/reports/` listant le nombre de jours travaillés par type (Travail, Garde, Formation…) pour l'année sélectionnée, en n'affichant que les types avec au moins 1 occurrence. + +**Architecture:** Une fonction pure `count_day_types(entries)` est ajoutée dans `app/business/time_calc.py` (cohérent avec les autres fonctions de comptage). La route `reports.py` l'appelle et passe le résultat au template. Le template affiche une nouvelle carte avec la liste, en utilisant le filtre `day_type_fr` existant. + +**Tech Stack:** Python 3, Flask, Jinja2, pytest + +--- + +## Tâche 1 : Fonction `count_day_types` dans `time_calc.py` + +**Files:** +- Modify: `app/business/time_calc.py` +- Test: `tests/test_time_calc.py` + +### Étape 1 : Écrire le test qui échoue + +Ajouter à la fin de `tests/test_time_calc.py` : + +```python +from app.business.time_calc import count_day_types +from app.models import WorkEntry +from datetime import date + + +def test_count_day_types_basic(): + entries = [ + WorkEntry(date=date(2025, 1, 2), day_type="WORK"), + WorkEntry(date=date(2025, 1, 3), day_type="WORK"), + WorkEntry(date=date(2025, 1, 6), day_type="TT"), + WorkEntry(date=date(2025, 1, 7), day_type="GARDE"), + ] + result = count_day_types(entries) + assert result == {"WORK": 2, "TT": 1, "GARDE": 1} + + +def test_count_day_types_empty(): + assert count_day_types([]) == {} + + +def test_count_day_types_single_type(): + entries = [ + WorkEntry(date=date(2025, 2, 1), day_type="RTT"), + WorkEntry(date=date(2025, 2, 2), day_type="RTT"), + ] + result = count_day_types(entries) + assert result == {"RTT": 2} +``` + +### Étape 2 : Vérifier que le test échoue + +```bash +.venv/bin/python -m pytest tests/test_time_calc.py::test_count_day_types_basic -v +``` + +Attendu : `FAILED` — `ImportError: cannot import name 'count_day_types'` + +### Étape 3 : Implémenter la fonction + +Ajouter à la fin de `app/business/time_calc.py` : + +```python +def count_day_types(entries: list) -> dict[str, int]: + """Retourne un dict {day_type: count} pour une liste d'entrées, sans les zéros.""" + counts: dict[str, int] = {} + for entry in entries: + counts[entry.day_type] = counts.get(entry.day_type, 0) + 1 + return counts +``` + +### Étape 4 : Vérifier que les tests passent + +```bash +.venv/bin/python -m pytest tests/test_time_calc.py -v +``` + +Attendu : tous `PASSED`. + +### Étape 5 : Commit + +```bash +git -c commit.gpgsign=false add app/business/time_calc.py tests/test_time_calc.py +git -c commit.gpgsign=false commit -m "feat: count_day_types — compte les jours par type" +``` + +--- + +## Tâche 2 : Brancher la fonction dans la route `reports.py` + +**Files:** +- Modify: `app/routes/reports.py` + +### Étape 1 : Importer et appeler `count_day_types` + +Dans `app/routes/reports.py`, modifier l'import : + +```python +from app.business.travel_calc import compute_km_for_entry, compute_co2_grams, compute_frais_reels +from app.business.time_calc import count_day_types +``` + +Puis, juste avant le `return render_template(...)`, ajouter : + +```python + day_type_counts = count_day_types(entries) +``` + +Et passer la variable au template : + +```python + return render_template( + "reports.html", + year=year, + total_km=total_km, + total_co2_kg=round(total_co2 / 1000, 2), + frais_reels=frais_reels, + vehicles=vehicles, + day_type_counts=day_type_counts, + ) +``` + +### Étape 2 : Vérifier que les tests passent + +```bash +.venv/bin/python -m pytest -v +``` + +Attendu : tous `PASSED`. + +--- + +## Tâche 3 : Carte dans le template `reports.html` + +**Files:** +- Modify: `app/templates/reports.html` + +### Étape 1 : Ajouter la carte après la carte "Frais réels" + +Insérer avant `{% endblock %}` : + +```html + +
+

Répartition {{ year }}

+ {% if day_type_counts %} +
+ {% for day_type, count in day_type_counts.items() %} +
+ {{ day_type | day_type_fr }} + + {{ count }}j + +
+ {% endfor %} +
+ {% else %} +

Aucune entrée pour {{ year }}.

+ {% endif %} +
+``` + +### Étape 2 : Vérifier visuellement + +```bash +.venv/bin/python run.py +``` + +Naviguer sur `http://localhost:5000/reports/` — la carte doit apparaître avec la liste des types. + +### Étape 3 : Vérifier les tests + +```bash +.venv/bin/python -m pytest -v +``` + +Attendu : tous `PASSED`. + +### Étape 4 : Commit + +```bash +git -c commit.gpgsign=false add app/routes/reports.py app/templates/reports.html +git -c commit.gpgsign=false commit -m "feat: carte répartition par type de jour dans le rapport" +``` diff --git a/docs/plans/2026-03-11-tableau-de-bord-travail-design.md b/docs/plans/2026-03-11-tableau-de-bord-travail-design.md index fc8fafb..acc1f49 100644 --- a/docs/plans/2026-03-11-tableau-de-bord-travail-design.md +++ b/docs/plans/2026-03-11-tableau-de-bord-travail-design.md @@ -88,7 +88,7 @@ Une journée peut avoir N plages horaires non-consécutives (ex: 9h-18h + 22h-00 | id | INTEGER PK | | | year | INTEGER UNIQUE | Année | | conges_total | INTEGER | Total congés (défaut 28) | -| rtt_total | INTEGER | Total RTT (défaut 18) | +| rtt_total | INTEGER | Total RTT (défaut 19) | Les jours utilisés sont calculés dynamiquement depuis `work_entries`. @@ -156,7 +156,7 @@ Toutes les vues sont conçues pour mobile en premier (Tailwind responsive). - **Carte "Aujourd'hui"** : boutons rapides "Arrivée" / "Départ" (horodatage automatique), sélecteur de profil de trajet - **Synthèse semaine courante** (lun-dim) : total heures, écart vs objectif (5 × 7h45 = 38h45) - **Synthèse mensuelle** : jours travaillés, km par véhicule, CO2 -- **Solde congés/RTT** : jauge visuelle (ex: "12/18 RTT utilisés") +- **Solde congés/RTT** : jauge visuelle (ex: "12/19 RTT utilisés") ### Page Saisie / Édition d'un jour diff --git a/docs/plans/2026-03-11-tableau-de-bord-travail.md b/docs/plans/2026-03-11-tableau-de-bord-travail.md index 884c4be..3c0ca8a 100644 --- a/docs/plans/2026-03-11-tableau-de-bord-travail.md +++ b/docs/plans/2026-03-11-tableau-de-bord-travail.md @@ -454,7 +454,7 @@ class LeaveBalance(db.Model): id: so.Mapped[int] = so.mapped_column(primary_key=True) year: so.Mapped[int] = so.mapped_column(sa.Integer, unique=True, nullable=False) conges_total: so.Mapped[int] = so.mapped_column(sa.Integer, default=28) - rtt_total: so.Mapped[int] = so.mapped_column(sa.Integer, default=18) + rtt_total: so.Mapped[int] = so.mapped_column(sa.Integer, default=19) ``` **Step 2: Vérifier la création des tables** @@ -769,7 +769,7 @@ def test_get_or_create_balance_creates_default(app): balance = get_or_create_balance(2025) assert balance.year == 2025 assert balance.conges_total == 28 - assert balance.rtt_total == 18 + assert balance.rtt_total == 19 def test_get_or_create_balance_returns_existing(app): diff --git a/run.py b/run.py index 488dae9..69a94d8 100644 --- a/run.py +++ b/run.py @@ -3,4 +3,4 @@ from app import create_app app = create_app() if __name__ == "__main__": - app.run(debug=True) + app.run(debug=True, host="0.0.0.0") diff --git a/tests/test_leave_calc.py b/tests/test_leave_calc.py index f69e4e0..1b13862 100644 --- a/tests/test_leave_calc.py +++ b/tests/test_leave_calc.py @@ -27,7 +27,7 @@ def test_get_or_create_balance_creates_default(app): balance = get_or_create_balance(2025) assert balance.year == 2025 assert balance.conges_total == 28 - assert balance.rtt_total == 18 + assert balance.rtt_total == 19 def test_get_or_create_balance_returns_existing(app): -- 2.47.3