chore: resolve final review findings

Co-authored-by: OpenAI/GPT-5.6-Terra <vibecoder@antoineve.me>
Co-authored-by: DeepSeek/DeepSeek-v4-Pro <vibecoder@antoineve.me>
Co-authored-by: DeepSeek/DeepSeek-v4-Flash <vibecoder@antoineve.me>
This commit is contained in:
2026-08-13 11:48:38 +02:00
parent 21fd877d7a
commit fd128dd09b
7 changed files with 22 additions and 23 deletions

2
.gitignore vendored
View File

@@ -5,3 +5,5 @@ __pycache__/
instance/ instance/
.env .env
*.db *.db
.ruff_cache/
.pytest_cache/

View File

@@ -14,9 +14,9 @@ Architecture et composants clés :
""" """
import os import os
import tomllib
import sqlalchemy as sa import sqlalchemy as sa
import tomllib
from flask import Flask from flask import Flask
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy

View File

@@ -1,3 +1,6 @@
import statistics as _stats
def minutes_to_str(minutes: int) -> str: def minutes_to_str(minutes: int) -> str:
""" """
Convertit une durée en minutes en une chaîne formatée lisible (ex: "7h45" ou "-1h15"). Convertit une durée en minutes en une chaîne formatée lisible (ex: "7h45" ou "-1h15").
@@ -58,9 +61,6 @@ def week_balance_minutes(actual_minutes: int, reference_minutes: int) -> int:
return actual_minutes - reference_minutes return actual_minutes - reference_minutes
import statistics as _stats
def monthly_stats(entries: list) -> dict: def monthly_stats(entries: list) -> dict:
""" """
Calcule la médiane journalière et la médiane hebdomadaire (semaines ISO) pour un groupe d'entrées. Calcule la médiane journalière et la médiane hebdomadaire (semaines ISO) pour un groupe d'entrées.

View File

@@ -8,15 +8,15 @@ Contrat TOML :
1. Véhicules (`[vehicles]`) : 1. Véhicules (`[vehicles]`) :
- Chaque véhicule possède un identifiant unique (clé). - Chaque véhicule possède un identifiant unique (clé).
- Attributs : `name` (nom d'affichage), `type` ("moteur" ou "velo"), `fuel` ("electric", "essence", etc.), - Attributs : `name` (nom d'affichage), `type` ("moteur" ou "velo"), `fuel` ("electric", "essence", etc.),
et optionnellement `cv` (puissance fiscale pour le barème kilométrique). `co2_per_km` (émissions de CO2 en grammes par km), et optionnellement `cv` (puissance fiscale pour le barème kilométrique).
2. Trajets (`[journeys]`) : 2. Trajets (`[journeys]`) :
- Profils de trajets prédéfinis (ex: "domicile-travail"). - Profils de trajets prédéfinis (ex: "moteur_seul").
- Attributs : `name` (nom d'affichage), `distances` (dictionnaire associant un type de véhicule à une distance en km). - Attributs : `name` (nom d'affichage), `distances` (dictionnaire associant un type de véhicule à une distance en km).
3. Barème kilométrique (`[bareme_kilometrique.YYYY]`) : 3. Barème kilométrique (`[bareme_kilometrique.YYYY]`) :
- Organisé par année (ex: "2026") puis par puissance fiscale (`cv_3`, `cv_4`, `cv_5`, `cv_6`, `cv_7plus`). - Organisé par année (ex: "2026") puis par puissance fiscale (`cv_3`, `cv_4`, `cv_5`, `cv_6`, `cv_7plus`).
- Chaque catégorie contient une liste de `tranches` définissant les formules de calcul des frais réels. - Chaque catégorie contient une liste de `tranches` définissant les formules de calcul des frais réels.
- Une tranche possède : `km_max` (limite supérieure de la tranche, `km_max = 0` signifie "pas de limite supérieure"), - Une tranche possède : `km_max` (limite supérieure de la tranche, `km_max = 0` signifie "pas de limite supérieure"),
`coeff` (coefficient multiplicateur par km) et `fixe` (montant forfaitaire à ajouter). `taux` (coefficient multiplicateur par km) et `forfait` (montant forfaitaire à ajouter).
4. Types de jours sans trajet : 4. Types de jours sans trajet :
- Certains types de journées (Télétravail, Maladie, Congé, RTT, Férié) n'impliquent aucun déplacement physique. - Certains types de journées (Télétravail, Maladie, Congé, RTT, Férié) n'impliquent aucun déplacement physique.
""" """
@@ -29,7 +29,7 @@ def get_vehicles():
Retourne: Retourne:
dict: Un dictionnaire des véhicules où la clé est l'identifiant du véhicule dict: Un dictionnaire des véhicules où la clé est l'identifiant du véhicule
et la valeur est un dictionnaire contenant ses propriétés (name, type, fuel, cv). et la valeur est un dictionnaire contenant ses propriétés (name, type, fuel, co2_per_km, cv).
Retourne un dictionnaire vide si aucune configuration n'est chargée. Retourne un dictionnaire vide si aucune configuration n'est chargée.
""" """
return current_app.config.get("TOML", {}).get("vehicles", {}) return current_app.config.get("TOML", {}).get("vehicles", {})
@@ -95,7 +95,7 @@ def get_bareme(year: int, cv: int) -> list[dict]:
Retourne: Retourne:
list[dict]: Une liste de dictionnaires représentant les tranches applicables. list[dict]: Une liste de dictionnaires représentant les tranches applicables.
Chaque tranche contient 'km_max' (0 si pas de limite), 'coeff' et 'fixe'. Chaque tranche contient 'km_max' (0 si pas de limite), 'taux' et 'forfait'.
""" """
bareme = current_app.config.get("TOML", {}).get("bareme_kilometrique", {}) bareme = current_app.config.get("TOML", {}).get("bareme_kilometrique", {})
year_data = bareme.get(str(year), {}) year_data = bareme.get(str(year), {})

View File

@@ -1,17 +1,15 @@
[tool.ruff] [tool.ruff]
target-version = "py310" target-version = "py311"
line-length = 100 line-length = 100
include = ["*.py"] include = ["*.py"]
[tool.ruff.lint] [tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I"] select = ["E4", "E7", "E9", "F", "I"]
# Ces imports différés sont intentionnels : ils évitent des dépendances # Cet import différé est intentionnel : il prépare sys.path pour le script
# circulaires ou préparent sys.path pour les scripts lancés directement. # lancé directement (imports des modules de l'app après insertion du parent).
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"app/business/time_calc.py" = ["E402"]
"scripts/import_csv.py" = ["E402"] "scripts/import_csv.py" = ["E402"]
"tests/test_time_calc.py" = ["E402"]
[tool.ruff.format] [tool.ruff.format]
quote-style = "double" quote-style = "double"

View File

@@ -33,7 +33,7 @@ from app.models import TimeSlot, WorkEntry
DAY_TYPES = {"WORK", "TT", "GARDE", "ASTREINTE", "FORMATION", "RTT", "CONGE", "MALADE", "FERIE"} DAY_TYPES = {"WORK", "TT", "GARDE", "ASTREINTE", "FORMATION", "RTT", "CONGE", "MALADE", "FERIE"}
def main(csv_path: str, config_path: str = None): def main(csv_path: str, config_path: str | None = None):
"""Importe les données depuis un fichier CSV vers la base de données.""" """Importe les données depuis un fichier CSV vers la base de données."""
# Créer l'application Flask avec la config # Créer l'application Flask avec la config

View File

@@ -1,8 +1,14 @@
from datetime import date
from datetime import time as dtime
from app.business.time_calc import ( from app.business.time_calc import (
count_day_types,
minutes_to_str, minutes_to_str,
monthly_stats,
week_balance_minutes, week_balance_minutes,
work_minutes_reference, work_minutes_reference,
) )
from app.models import TimeSlot, WorkEntry
def test_minutes_to_str_basic(): def test_minutes_to_str_basic():
@@ -42,13 +48,6 @@ def test_week_balance_negative():
assert week_balance_minutes(2200, 2325) == -125 assert week_balance_minutes(2200, 2325) == -125
from datetime import date
from datetime import time as dtime
from app.business.time_calc import count_day_types, monthly_stats
from app.models import TimeSlot, WorkEntry
def test_count_day_types_basic(): def test_count_day_types_basic():
entries = [ entries = [
WorkEntry(date=date(2025, 1, 2), day_type="WORK"), WorkEntry(date=date(2025, 1, 2), day_type="WORK"),