feat: increase RTT from 18 to 19 #1

Merged
AntoineVe merged 1 commits from increase-rtt-to-19 into master 2026-03-13 13:50:13 +01:00
7 changed files with 196 additions and 9 deletions

View File

@@ -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=<secret> ./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 "..."

View File

@@ -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)

View File

@@ -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 par type de jour -->
<div class="card card-amber">
<p class="card-label">Répartition {{ year }}</p>
{% if day_type_counts %}
<div class="space-y-2 mt-1">
{% for day_type, count in day_type_counts.items() %}
<div class="flex items-baseline justify-between">
<span class="text-xs" style="color:#8A8278;">{{ day_type | day_type_fr }}</span>
<span class="font-data font-semibold text-sm" style="color:var(--ink);">
{{ count }}<span class="font-normal text-xs ml-1" style="color:#9A9288;">j</span>
</span>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-sm" style="color:#9A9288;">Aucune entrée pour {{ year }}.</p>
{% endif %}
</div>
```
### É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"
```

View File

@@ -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

View File

@@ -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):

2
run.py
View File

@@ -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")

View File

@@ -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):