14 KiB
Guide d'intégration (Onboarding) pour les développeurs
Bienvenue sur le projet Tableau de bord pro ! Ce guide est conçu pour t'aider à prendre en main rapidement l'application, comprendre son architecture, ses règles métier et savoir comment y apporter des modifications courantes.
1. Objectif fonctionnel
Le Tableau de bord pro est une application web personnelle permettant de suivre :
- Le temps de travail quotidien : saisie des heures travaillées, des types de journées (travail, télétravail, garde, astreinte, formation, RTT, congé, maladie, férié) et des commentaires associés.
- Les déplacements professionnels : calcul automatique des distances parcourues par véhicule, estimation des émissions de CO₂ et calcul des frais réels déductibles selon le barème kilométrique fiscal officiel.
- Le solde des congés et RTT : suivi annuel des jours posés et du solde restant par rapport aux quotas alloués.
- Les rapports d'activité : bilans annuels et mensuels compilant les kilomètres, le CO₂, les frais réels et la répartition des types de journées.
2. Prérequis et démarrage local
Prérequis
- Python 3.11+ installé sur ta machine.
Démarrage local
Pour lancer l'application en mode développement, exécute les commandes suivantes dans ton terminal :
# 1. Créer l'environnement virtuel Python
python -m venv .venv
# 2. Activer l'environnement virtuel et installer les dépendances
.venv/bin/pip install -r requirements.txt
# 3. Lancer le serveur de développement Flask
.venv/bin/python run.py
L'application est alors accessible localement à l'adresse suivante : http://localhost:5000.
3. Architecture de l'application
L'application est construite avec le framework Flask en utilisant le Factory Pattern (via la fonction create_app dans app/__init__.py).
Structure des dossiers et fichiers clés
run.py: Point d'entrée pour lancer le serveur de développement.config.toml: Fichier de configuration statique (véhicules, trajets, barèmes fiscaux).app/: Code source de l'application.__init__.py: Initialisation de Flask, configuration de la base de données, enregistrement des blueprints et des filtres Jinja2.config_loader.py: Interface de lecture du fichierconfig.toml.models.py: Définition des modèles de données SQLAlchemy (SQLite).business/: Logique métier pure, sans dépendance à Flask (facile à tester) :time_calc.py: Calculs de durées, de soldes d'heures et statistiques de temps.travel_calc.py: Calculs de distances, d'émissions de CO₂ et de frais réels.leave_calc.py: Calculs de congés et RTT consommés.
routes/: Contrôleurs Flask gérant les requêtes HTTP et le rendu des templates :dashboard.py: Page d'accueil et vue d'ensemble.entries.py: Gestion (CRUD) des entrées de temps.reports.py: Génération des rapports annuels et mensuels.
templates/: Fichiers HTML utilisant Jinja2.
tests/: Tests unitaires et d'intégration (pytest).
Technologies Frontend
- Tailwind CSS (via CDN) : Utilisé pour le style. Les variables de design et les classes personnalisées (comme
.card,.btn-primary, etc.) sont définies directement dans la balise<style>deapp/templates/base.html. - HTMX : Utilisé pour rendre l'interface dynamique sans rechargement complet de page.
- JavaScript : Uniquement du JS inline dans
app/templates/entry_form.htmlpour la gestion dynamique du formulaire de saisie.
Authentification
L'application n'intègre aucun système d'authentification interne. La sécurité et l'authentification sont entièrement déléguées à un serveur HAProxy situé en amont (upstream) en production.
4. Flux d'une saisie de journée
Voici ce qui se passe lorsqu'un utilisateur saisit ou modifie une journée de travail :
-
Affichage du formulaire (GET
/entries/newou/entries/<id>/edit) :- La route
entry_formdansapp/routes/entries.pyrécupère les véhicules à moteur et les profils de trajets depuisconfig.tomlviaapp/config_loader.py. - Elle transmet ces données au template
entry_form.html.
- La route
-
Soumission du formulaire (POST) :
- L'utilisateur envoie la date, le type de journée, le trajet, le véhicule à moteur utilisé, le commentaire et les plages horaires.
- Application des règles de cohérence :
- Si le type de journée est un jour sans trajet (ex: Télétravail, Congé), le trajet est forcé à
None. - Si le trajet sélectionné n'implique pas de véhicule à moteur, le véhicule à moteur est forcé à
None.
- Si le type de journée est un jour sans trajet (ex: Télétravail, Congé), le trajet est forcé à
- Enregistrement de l'entrée (
WorkEntry) :- En création, l'application vérifie qu'aucune entrée n'existe déjà à cette date (unicité de la date).
- L'entrée est créée ou mise à jour dans la table
work_entries.
- Gestion des plages horaires (
TimeSlot) :- Pour simplifier la mise à jour, toutes les plages horaires existantes de cette journée sont supprimées de la base de données.
- Les nouvelles plages horaires soumises sont lues, validées et recréées sous forme d'objets
TimeSlotliés à laWorkEntry.
- Validation : La transaction est validée via
db.session.commit(), et l'utilisateur est redirigé vers le tableau de bord.
5. Rôle de config.toml vs SQLite
L'application sépare strictement la configuration métier statique des données utilisateur dynamiques.
config.toml (Configuration statique)
Ce fichier contient les paramètres qui ne changent pas fréquemment et qui définissent les règles de calcul :
- Véhicules (
[vehicles.*]) : Nom, type (moteur ou velo), carburant (electric, diesel, essence, none), émissions de CO₂ par km, et puissance fiscale (CV). - Trajets (
[journeys.*]) : Profils de trajets prédéfinis (ex: "moteur_seul", "moteur_velo") avec les distances associées par type de moyen de transport. - Barème kilométrique (
[bareme_kilometrique.YYYY.*]) : Les tranches fiscales officielles de remboursement par année et par puissance fiscale (CV).
Note : Ces données sont chargées en mémoire au démarrage de l'application dans app.config["TOML"].
SQLite / instance/worklog.db (Données dynamiques)
La base de données stocke l'activité saisie par l'utilisateur :
work_entries: Une ligne par jour saisi (date, type de jour, ID du trajet, ID du véhicule, commentaire, timestamps).time_slots: Les plages horaires travaillées associées à une journée (heure de début, heure de fin, ID de l'entrée).leave_balance: Les quotas annuels de congés et de RTT (année, total congés, total RTT).
6. Règles métier essentielles
Pour éviter les erreurs de calcul, garde bien en tête ces règles fondamentales :
- Types de journées : Les types autorisés sont
WORK,TT,GARDE,ASTREINTE,FORMATION,RTT,CONGE,MALADE,FERIE. - Absence de trajet : Les types
TT,MALADE,CONGE,RTTetFERIEn'impliquent aucun trajet physique (définis dansday_types_without_journey()). - Durée de référence du travail :
WORK,TT,FORMATION: 7h45 (soit 465 minutes).GARDE: 10h00 (soit 600 minutes).ASTREINTE,RTT,CONGE,MALADE,FERIE: 0 minute.
- Calcul du temps de travail : La méthode
total_minutes()deWorkEntrysomme les durées desTimeSlot. Elle gère le passage de minuit : si l'heure de fin est inférieure ou égale à l'heure de début (ex: 22:00 à 02:00), elle ajoute automatiquement 24 heures à la plage. - Frais réels et véhicules électriques :
- Le calcul des frais réels applique les tranches du barème kilométrique de
config.toml. Une tranche aveckm_max = 0représente la tranche supérieure sans limite. - Les véhicules électriques (
fuel = "electric") bénéficient d'une majoration automatique de 20 % sur le montant calculé des frais réels (géré dansapp/business/travel_calc.py).
- Le calcul des frais réels applique les tranches du barème kilométrique de
7. Tests et Qualité du code
Exécuter les tests
Les tests sont écrits avec pytest. Les tests de logique métier (test_time_calc.py, test_travel_calc.py) n'ont aucune dépendance à Flask et s'exécutent très rapidement. Les tests de routes utilisent des fixtures définies dans tests/conftest.py (base de données SQLite en mémoire et configuration TOML temporaire).
# Exécuter tous les tests
.venv/bin/python -m pytest
# Exécuter un fichier de test spécifique
.venv/bin/python -m pytest tests/test_time_calc.py -v
# Exécuter un test unitaire précis
.venv/bin/python -m pytest tests/test_routes.py::test_create_entry -v
Qualité et Formatage du code
Le projet utilise Ruff comme outil unique pour le linting, le tri des imports et le formatage du code. La configuration est définie dans pyproject.toml.
# Vérifier le code (linter)
.venv/bin/ruff check .
# Vérifier le formatage
.venv/bin/ruff format --check .
# Appliquer automatiquement le formatage et corriger les imports
.venv/bin/ruff format .
.venv/bin/ruff check --fix .
8. Guide de modifications courantes
A. Ajouter ou modifier un véhicule
Pour ajouter un nouveau véhicule (par exemple, une nouvelle voiture de 4 CV), ouvre config.toml et ajoute une section sous [vehicles] :
[vehicles.ma_nouvelle_voiture]
name = "Ma Nouvelle Voiture"
fuel = "essence"
co2_per_km = 120
cv = 4
type = "moteur"
B. Ajouter ou modifier un trajet prédéfini
Pour ajouter un trajet combinant voiture et vélo, ouvre config.toml et ajoute une section sous [journeys] :
[journeys.mon_nouveau_trajet]
name = "Mon Nouveau Trajet"
distances = { moteur = 10, velo = 5 }
C. Mettre à jour le barème kilométrique annuel
Chaque année, l'administration fiscale publie un nouveau barème. Pour l'ajouter (par exemple pour l'année 2027), ajoute les tranches correspondantes dans config.toml :
[[bareme_kilometrique.2027.cv_4.tranches]]
km_max = 5000
taux = 0.606
forfait = 0
[[bareme_kilometrique.2027.cv_4.tranches]]
km_max = 20000
taux = 0.340
forfait = 1330
[[bareme_kilometrique.2027.cv_4.tranches]]
km_max = 0
taux = 0.407
forfait = 0
D. Ajouter un nouveau type de journée
Si tu dois ajouter un nouveau type de journée (par exemple REPOS) :
- Ouvre
app/routes/entries.pyet ajoute le type à la listeDAY_TYPES:DAY_TYPES = [ ... ("REPOS", "Repos"), ] - Ouvre
app/__init__.pyet ajoute sa traduction française dans_DAY_TYPE_LABELS:_DAY_TYPE_LABELS = { ... "REPOS": "Repos", } - Ouvre
app/business/time_calc.pyet définis sa durée de référence dans_REFERENCE_MINUTES(ex: 0 minute pour un repos) :_REFERENCE_MINUTES = { ... "REPOS": 0, } - Si ce type de journée n'implique aucun trajet, ouvre
app/config_loader.pyet ajoute-le dansday_types_without_journey():def day_types_without_journey(): return {"TT", "MALADE", "CONGE", "RTT", "FERIE", "REPOS"}
9. Déploiement, limites et migrations
Déploiement en production
L'application est déployée dans /var/www/tableau-de-bord-pro/ et est gérée par un service systemd qui lance Gunicorn.
Pour mettre à jour la production :
# 1. Copier les fichiers (en excluant l'environnement virtuel et la base de données locale)
sudo rsync -a --exclude='.venv' --exclude='instance' . /var/www/tableau-de-bord-pro/
# 2. Se positionner dans le dossier de production
cd /var/www/tableau-de-bord-pro
# 3. Mettre à jour les dépendances si nécessaire
sudo .venv/bin/pip install -r requirements.txt
# 4. S'assurer que les permissions sont correctes
sudo chown -R www-data:www-data /var/www/tableau-de-bord-pro
# 5. Redémarrer le service systemd
sudo systemctl restart tableau-de-bord-pro
Limites et Migrations de base de données
- Pas d'Alembic : Le projet n'utilise pas d'outil de migration automatique comme Alembic. La base de données est initialisée avec
db.create_all(). - Changement de schéma : Si tu modifies un modèle dans
app/models.py(par exemple en ajoutant un champ) :- En développement : Tu peux simplement supprimer le fichier
instance/worklog.dbpour qu'il soit recréé au prochain démarrage (attention, cela supprime tes données de test). - En production : Tu dois écrire une migration manuelle dans la fonction
_migrate_dbsituée dansapp/__init__.py. Cette fonction s'exécute au démarrage de l'application, vérifie l'existence des colonnes via SQLite, et applique les instructionsALTER TABLEnécessaires de manière sécurisée.
- En développement : Tu peux simplement supprimer le fichier
Horodatages de métadonnées et calculs de durée métier
- Métadonnées (
created_at,updated_at) : L'application utilisedatetime.now(UTC)pour enregistrer ces métadonnées. Bien que les valeurs soient émises en UTC, SQLite et SQLAlchemy stockent et restituent par défaut des objetsdatetimenaïfs (sanstzinfo). Ces champs sont actuellement informatifs et non exploités par la logique métier. Si un besoin de lecture ou de manipulation de ces métadonnées avec fuseau émergeait, les pistes incluent l'utilisation de typesDateTime(timezone=True)ou la stricte convention documentée « naïf = UTC ». - Calculs de durée métier et transitions DST : Le calcul de la durée des plages horaires de travail (
TimeSlotviatotal_minutes()) est totalement indépendant des métadonnées et repose sur des heures murales (locales). Une plage horaire traversant un changement d'heure saisonnier (passage heure d'été/hiver / DST) soulève un enjeu métier spécifique (gestion des durées d'heures locales) qui nécessiterait, le cas échéant, une représentation dédiée ou une politique métier spécifique. db.get_engine(): Déprécié dans Flask-SQLAlchemy 3.x. Utilise toujoursdb.engineà la place.
10. Liens utiles
Pour aller plus loin, n'hésite pas à consulter les documents suivants à la racine du projet :