Files
tableau-de-bord/docs/onboarding.md
Antoine Van Elstraete 85e6e502e5 feat(config): ajouter la configuration Home Assistant (étape 1)
Ajoute la section [home_assistant] au TOML et sa validation au chargement
via get_home_assistant_config(). La section est facultative pour préserver
la compatibilité avec les configurations existantes, mais si elle est
présente elle doit être complète et référencer un type de journée, un
trajet et un véhicule à moteur existants, sous peine de refuser le
démarrage de l'application. La configuration validée est exposée dans
app.config['HOME_ASSISTANT'].

Co-authored-by: OpenAI/GPT-5.6-Luna-Pro <vibecoder@antoineve.me>
2026-08-13 16:13:18 +02:00

15 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 fichier config.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> de app/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.html pour 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 :

  1. Affichage du formulaire (GET /entries/new ou /entries/<id>/edit) :

    • La route entry_form dans app/routes/entries.py récupère les véhicules à moteur et les profils de trajets depuis config.toml via app/config_loader.py.
    • Elle transmet ces données au template entry_form.html.
  2. 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.
    • 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 TimeSlot liés à la WorkEntry.
    • 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).
  • Home Assistant ([home_assistant]) : Les valeurs par défaut et le fuseau utilisés par la future API de présence :
    [home_assistant]
    timezone = "Europe/Paris"
    default_day_type = "WORK"
    default_journey_profile_id = "moteur_seul"
    default_motor_vehicle_id = "citadine"
    
    Cette section est facultative pour préserver le fonctionnement de l'interface Web sur les configurations existantes. Si elle est présente, elle doit être complète et référencer un type de journée, un trajet et un véhicule à moteur existants ; l'application refuse alors de démarrer en cas d'erreur. La configuration est accessible via get_home_assistant_config() dans app/config_loader.py.

Note : Ces données sont chargées en mémoire au démarrage de l'application dans app.config["TOML"].

Le secret de la future API ne doit pas être ajouté au fichier TOML. Il proviendra de la variable d'environnement WORKLOG_API_TOKEN lorsqu'elle sera implémentée ; cette étape ne le stocke ni ne le gère.

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, RTT et FERIE n'impliquent aucun trajet physique (définis dans day_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() de WorkEntry somme les durées des TimeSlot. 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 avec km_max = 0 repré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é dans app/business/travel_calc.py).

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

  1. Ouvre app/routes/entries.py et ajoute le type à la liste DAY_TYPES :
    DAY_TYPES = [
        ...
        ("REPOS", "Repos"),
    ]
    
  2. Ouvre app/__init__.py et ajoute sa traduction française dans _DAY_TYPE_LABELS :
    _DAY_TYPE_LABELS = {
        ...
        "REPOS": "Repos",
    }
    
  3. Ouvre app/business/time_calc.py et définis sa durée de référence dans _REFERENCE_MINUTES (ex: 0 minute pour un repos) :
    _REFERENCE_MINUTES = {
        ...
        "REPOS": 0,
    }
    
  4. Si ce type de journée n'implique aucun trajet, ouvre app/config_loader.py et ajoute-le dans day_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.db pour 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_db située dans app/__init__.py. Cette fonction s'exécute au démarrage de l'application, vérifie l'existence des colonnes via SQLite, et applique les instructions ALTER TABLE nécessaires de manière sécurisée.

Horodatages de métadonnées et calculs de durée métier

  • Métadonnées (created_at, updated_at) : L'application utilise datetime.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 objets datetime naïfs (sans tzinfo). 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 types DateTime(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 (TimeSlot via total_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 toujours db.engine à la place.

10. Liens utiles

Pour aller plus loin, n'hésite pas à consulter les documents suivants à la racine du projet :

  • README.md : Présentation générale, instructions d'installation détaillées et script d'import CSV en masse.
  • AGENTS.md : Guide de développement et consignes pour les agents d'intelligence artificielle travaillant sur ce dépôt.