Files
MeeJayandClaude Opus 5 93f0689c1e Initial import: LifeTrack v1 (santé, vape, finances)
Tracker de vie auto-hébergé : suivi poids/calories/sport avec planning de
pesées, sevrage tabac (vape) avec modèle de coût DIY et économies, et
finances personnelles avec import de relevés bancaires.

Architecture : FastAPI + SQLAlchemy 2.0 + PostgreSQL 16, React 18 + TS +
Vite + Tailwind + ECharts, déploiement Docker Compose. Modules
auto-découverts des deux côtés (pkgutil / import.meta.glob) et framework
de connecteurs à deux voies (importeurs de fichiers + ingestion JSON)
pour brancher de nouvelles sources sans toucher au noyau.

Validé : 292 tests pytest, tsc + vite build, contrat API/web vérifié
contre le schéma OpenAPI, et déploiement Docker réel sur PostgreSQL 16
(28 tables, SPA servie par nginx, wizard de premier démarrage).

Documentation : README.md, docs/GUIDE.md, CONVENTIONS.md, et les
documents de conception et de recherche dans docs/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:48:57 +02:00

911 lines
44 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Guide d'utilisation de LifeTrack
Ce guide décrit **comment se servir de LifeTrack au quotidien** : brancher ses vraies
données, suivre son poids, remplir son journal alimentaire, piloter le sevrage tabagique et
importer ses relevés bancaires.
Il part du principe que l'application tourne déjà et que votre compte est créé. Sinon,
commencez par le [README](../README.md#démarrage-rapide-docker-compose).
---
## Sommaire
- [Avant de commencer](#avant-de-commencer)
- [Connecter Health Connect](#connecter-health-connect)
- [Suivi poids et calories au quotidien](#suivi-poids-et-calories-au-quotidien)
- [Nutrition](#nutrition)
- [Vape et sevrage tabagique](#vape-et-sevrage-tabagique)
- [Finances](#finances)
- [Dépannage](#dépannage)
---
## Avant de commencer
Trois réglages conditionnent presque tous les calculs. Faites-les une bonne fois pour
toutes dans **Réglages** :
| Onglet | À renseigner | Ce que ça débloque |
|---|---|---|
| **Profil** | Taille, sexe, date de naissance, niveau d'activité | Métabolisme de base, dépense estimée, IMC |
| **Objectif** | Poids de départ, poids cible, mode de calcul | Budget calorique quotidien, projection, planning |
| **Vape** | Date d'arrêt, cigarettes/jour avant, prix du paquet | Économies, jalons santé |
Le profil comporte aussi un **fuseau horaire** (`Europe/Paris` par défaut). C'est lui qui
détermine à quel jour appartient une donnée horodatée : changez-le si vous vivez ailleurs,
sinon laissez-le tranquille.
---
## Connecter Health Connect
Health Connect est le magasin de données santé d'Android. Point crucial à comprendre avant
tout : **c'est une base de données locale au téléphone. Aucun serveur ne peut aller y
chercher quoi que ce soit.** Il n'existe pas d'API cloud, et l'ancienne API Google Fit est
morte (inscriptions fermées depuis mai 2024, arrêt en 2026).
Les données ne peuvent donc que **partir du téléphone vers LifeTrack**. Trois chemins, du
plus automatique au plus manuel.
### Chemin 1 — Envoi automatique vers l'API d'ingestion
#### Étape 1 : créer une clé d'appareil
1. Ouvrez **Réglages → Appareils & API**.
2. Cliquez sur **Créer une clé**.
3. Donnez-lui un nom parlant (par exemple « Pixel de Julien »).
4. Cochez la portée **« Santé (pesées, activité, séances) »** — techniquement
`ingest:health`.
5. Validez.
> ⚠️ **La clé en clair n'est affichée qu'une seule fois.** Copiez-la immédiatement.
> LifeTrack n'en conserve qu'une empreinte : personne, pas même vous, ne pourra la
> réafficher. Si vous la perdez, révoquez-la et créez-en une autre.
>
> Une clé ressemble à `ltk_` suivi d'une longue chaîne aléatoire.
La liste vous montre ensuite, pour chaque clé, son préfixe, ses portées, sa date de création
et sa dernière utilisation — pratique pour vérifier qu'une passerelle envoie bien quelque
chose.
#### Étape 2 : l'adresse à viser
L'endpoint d'ingestion est :
```
POST http://<adresse-de-votre-serveur>/api/ingest/health
```
L'onglet **Imports → Connecteurs & API** affiche cette URL déjà construite avec l'adresse
depuis laquelle vous consultez l'application, avec un bouton **Copier**.
Deux choses obligatoires dans la requête :
- l'en-tête **`X-API-Key: ltk_…`** avec votre clé d'appareil ;
- un corps JSON de la forme suivante :
```json
{
"source": "health_connect",
"records": [
{
"type": "steps",
"external_id": "uuid-fourni-par-health-connect",
"data": { "day": "2026-08-13", "steps": 9421 }
},
{
"type": "weight",
"data": { "measured_at": "2026-08-13T06:42:00Z", "weight_kg": 83.4 }
}
]
}
```
Vous pouvez tester tout de suite depuis n'importe quelle machine :
```bash
curl -X POST http://localhost/api/ingest/health \
-H "X-API-Key: ltk_votre_cle_ici" \
-H "Content-Type: application/json" \
-d '{"source":"health_connect","records":[{"type":"weight","data":{"measured_at":"2026-08-13T06:42:00Z","weight_kg":83.4}}]}'
```
La réponse indique combien d'enregistrements ont été insérés, mis à jour, ignorés comme
doublons, et le détail des lignes rejetées. **Un enregistrement invalide ne fait jamais
échouer le lot entier.**
#### Étape 3 : quelles données passent
Huit types d'enregistrements sont acceptés :
| `type` | Ce que ça alimente | Champs principaux reconnus |
|---|---|---|
| `weight` | Pesées | `measured_at` / `time` / `start_time`, `weight_kg`, `body_fat_pct` |
| `steps` | Activité quotidienne | `day` ou `start_time`, `steps` / `count` |
| `distance` | Activité quotidienne | `distance_m` ou `distance_km` |
| `active_calories` | Activité quotidienne | `active_kcal` / `active_calories` |
| `total_calories` | Activité quotidienne (TDEE mesuré) | `total_kcal` / `total_calories` |
| `exercise_session` | Séances de sport | `start_time`, `end_time` ou `duration_s`, `exercise_type`, `energy_kcal`, `distance_m`, `avg_hr`, `max_hr` |
| `nutrition` | Journal alimentaire | `eaten_at`, `energy_kcal`, `name`, `meal_type`, macros |
| `hydration` | Hydratation | `drunk_at`, `volume_ml` ou `volume_liters` |
Le format des champs est volontairement tolérant : LifeTrack accepte les noms
« Health Connect » en `snake_case`, les objets imbriqués `value` et `metadata`, les
horodatages ISO 8601 comme les époques Unix en secondes ou millisecondes. Un horodatage sans
fuseau est interprété en **Europe/Paris** puis converti en UTC. La charge utile d'origine est
toujours conservée dans la ligne créée.
Les types de sport connus sont reconnus et traduits (`running`, `walking`, `cycling`,
`swimming`, `strength_training`, `hiit`, `yoga`, `hiking`, tapis de course en marche ou en
course…) ; le reste atterrit en « Autre » avec le libellé d'origine conservé.
#### Étape 4 : la passerelle Android — et ses limites réelles
L'application open source de référence est **health-connect-webhook**
(<https://github.com/mcnaveen/health-connect-webhook>, AGPL-3.0, disponible sur le Play
Store). Elle lit Health Connect en tâche de fond et POSTe du JSON vers l'URL de votre choix.
**Soyons parfaitement clairs : en l'état, elle ne peut pas être branchée directement sur
LifeTrack.** Deux obstacles, tous deux réels :
1. **Elle ne sait pas poser d'en-tête HTTP personnalisé.** Sa sécurité repose entièrement
sur le secret de l'URL. Or `POST /api/ingest/health` exige `X-API-Key`. Les documents de
conception prévoyaient une route acceptant un jeton dans le chemin de l'URL pour ce cas
précis — **elle n'a pas été implémentée**.
2. **Son enveloppe JSON n'est pas celle attendue.** Elle envoie un objet contenant
`timestamp`, `app_version` et un tableau par type de données. LifeTrack attend
`{"source": …, "records": [{"type": …, "data": …}]}`. Le *contenu* de chaque
enregistrement est bien compris par LifeTrack ; c'est l'emballage qui diffère.
Concrètement, vous avez trois options :
- **Intercaler un petit relais** entre le téléphone et LifeTrack (un script, une
automatisation Node-RED ou n8n, une fonction sur votre serveur) qui reçoit le webhook,
découpe les tableaux en `records` et rejoue la requête avec l'en-tête `X-API-Key`. C'est
une trentaine de lignes.
- **Utiliser n'importe quelle application capable de poser un en-tête HTTP** (Tasker,
HTTP Shortcuts, Macrodroid, ou une future application compagnon) : le format canonique
ci-dessus est simple à produire.
- **Rester sur l'import CSV** (chemin 2), qui fonctionne aujourd'hui sans aucun bricolage.
#### Les autres limites, à connaître avant de s'engager
- **Fenêtre de 48 heures.** La passerelle ne relit que les 48 dernières heures. Si votre
téléphone reste éteint ou hors ligne plus longtemps, ces journées sont définitivement
perdues pour ce canal — d'où l'intérêt de l'export CSV en filet de sécurité.
- **Historique limité à 30 jours.** Health Connect n'autorise par défaut la lecture que des
30 jours précédant la première autorisation. Tout ce qui est plus ancien ne viendra
**jamais** par ce chemin : il faudra passer par un export de fichiers.
- **Installation hors Play Store.** Une application compagnon maison devra être installée
manuellement (sideload) : APK signé localement, permissions accordées dans l'écran
Health Connect du téléphone. C'est faisable, mais ce n'est pas un simple clic.
- **Économiseurs de batterie.** Xiaomi, Huawei, Samsung et consorts tuent volontiers les
tâches de fond. Si les envois s'espacent sans raison, excluez l'application de
l'optimisation de batterie dans les réglages Android.
- **Sécurité réseau.** Une clé d'appareil circule à chaque envoi. N'exposez pas LifeTrack
en clair sur Internet : restez sur votre réseau local ou passez par un VPN, sinon mettez
du HTTPS devant nginx.
### Chemin 2 — Import CSV avec Health Sync (le plus fiable aujourd'hui)
**Health Sync** (<https://healthsync.app>, environ 4 € en achat unique, essai d'une semaine)
exporte automatiquement vos données Health Connect en CSV vers Google Drive, en tâche de
fond, sans intervention.
1. Installez et configurez Health Sync sur le téléphone, avec Health Connect comme source.
2. Activez l'export automatique vers Google Drive (fichiers jour, 7 jours, mois ou 30 jours
glissants).
3. Récupérez le CSV sur votre ordinateur.
4. Dans LifeTrack, ouvrez **Imports**, déposez le fichier et laissez la **détection
automatique** faire son travail — le profil attendu est
**« Health Sync / Health Connect (export CSV) »**.
5. Vérifiez l'aperçu, puis lancez l'import.
Ce chemin est aussi **le seul moyen de rattraper un historique profond** (au-delà des
30 jours de Health Connect) et de boucher les trous laissés par la fenêtre de 48 heures.
Ré-importer un fichier déjà traité ne crée aucun doublon : allez-y sans crainte.
Un profil **« Historique de poids (CSV date/poids) »** existe également pour un simple
fichier à deux colonnes date et poids, quelle qu'en soit l'origine.
### Chemin 3 — Saisie manuelle
Toujours disponible, et parfois plus rapide qu'on ne croit :
- une pesée depuis la carte **Aujourd'hui** du tableau de bord ou depuis
**Santé → Poids & Objectif** ;
- une séance de sport depuis **Santé → Activité & Sport** ;
- un repas depuis **Santé → Nutrition**.
C'est le mode de repli recommandé pour les séances de tapis de course : l'application
FitShow ne partage ses données ni avec Health Connect, ni avec Google Fit, ni sous forme de
fichiers. Ses séances resteront enfermées dans son application tant qu'un enregistrement
Bluetooth direct n'aura pas été développé (envisagé en v3).
---
## Suivi poids et calories au quotidien
### Le planning de pesées
LifeTrack ne vous demande pas de cocher des cases : **une habitude est considérée comme faite
dès qu'une donnée existe pour ce jour-là.** Vous vous pesez, la pesée compte. Vous
enregistrez un repas, le journal alimentaire du jour compte.
Pour définir votre plan, allez dans **Réglages → Objectif → Planning de suivi** (ou depuis
la page **Santé → Poids & Objectif**). Trois habitudes indépendantes :
| Habitude | Validée par |
|---|---|
| **Pesée** | Une pesée enregistrée ce jour-là |
| **Séance** | Une séance de sport ce jour-là |
| **Journal alimentaire** | Au moins un aliment enregistré ce jour-là |
Pour chacune, cochez les jours de la semaine concernés et activez-la. Une pesée quotidienne
donne la tendance la plus fiable ; trois fois par semaine suffisent largement.
LifeTrack en déduit ensuite :
- l'**assiduité** : jours faits ÷ jours planifiés, en pourcentage ;
- la **série en cours** et la **meilleure série**, comptées uniquement sur les jours
planifiés ;
- un **calendrier** à quatre états : fait, manqué, fait hors planning, jour de repos.
Détail appréciable : **le jour même ne casse jamais une série tant qu'il n'est pas fini.**
Si vous vous pesez habituellement le matin et que vous consultez l'application à midi sans
l'avoir fait, votre série reste intacte.
### La carte « Aujourd'hui »
En haut du tableau de bord, elle résume l'état du jour : ce qui était prévu, ce qui est fait,
et un bouton d'action directe pour chaque manque (**Noter ma pesée**, **Noter ma séance**).
C'est le point d'entrée quotidien de l'application.
### La saisie rapide d'une pesée
Le bouton **Noter ma pesée** ouvre une modale minimale : le poids, éventuellement la date et
l'heure si ce n'est pas maintenant. Deux secondes, et la tendance se recalcule.
Une seule règle à connaître : **c'est la première pesée du jour qui alimente la tendance.**
Si vous vous pesez trois fois dans la journée, seule celle du matin compte pour la courbe —
les autres restent visibles dans l'historique.
### Comprendre les chiffres
C'est ici que LifeTrack cesse d'être un carnet et devient un outil. Quatre notions, dans
l'ordre où elles s'enchaînent.
#### 1. Le métabolisme de base (BMR)
C'est ce que votre corps brûle **au repos complet**, juste pour rester en vie. LifeTrack
utilise la formule de **Mifflin-St Jeor**, la référence actuelle :
```
BMR = 10 × poids(kg) + 6,25 × taille(cm) 5 × âge(ans) + correctif
```
Le correctif vaut `+5` pour un homme, `161` pour une femme, `78` pour « autre ».
*Exemple : un homme de 40 ans, 178 cm, 83 kg → 10 × 83 + 6,25 × 178 5 × 40 + 5 = **1 747
kcal/jour**.* C'est ce que vous brûleriez en restant couché toute la journée.
#### 2. La dépense énergétique totale (TDEE)
C'est le BMR **plus tout le reste** : marcher, travailler, faire du sport, digérer.
LifeTrack le calcule chaque jour selon trois méthodes, par ordre de préférence :
1. **Mesuré** — si votre montre ou votre téléphone a envoyé une valeur de calories totales
crédible (supérieure à 80 % du BMR), c'est elle qui gagne. C'est la plus juste.
2. **BMR + calories actives** — si vous n'avez que les calories *actives* de la journée,
elles s'ajoutent au BMR.
3. **Estimé** — sinon, le BMR est multiplié par le facteur de votre niveau d'activité :
| Niveau | Facteur |
|---|---|
| Sédentaire | × 1,2 |
| Légèrement actif | × 1,375 |
| Modérément actif | × 1,55 |
| Très actif | × 1,725 |
| Extrêmement actif | × 1,9 |
Le TDEE utilisé pour votre budget est **lissé sur 7 jours** : une journée exceptionnelle ne
fait pas bondir votre autorisation calorique du lendemain.
#### 3. Les 7 700 kcal par kilo
C'est la constante qui relie calories et poids : **perdre 1 kg de masse corporelle
correspond à un déficit d'environ 7 700 kcal.**
D'où le calcul du budget quotidien :
```
Budget = TDEE lissé (rythme visé en kg/semaine × 7 700 7)
```
Autrement dit, chaque tranche de **0,1 kg/semaine** que vous visez coûte **110 kcal par
jour**.
*Exemple : TDEE lissé à 2 500 kcal, objectif 0,5 kg/semaine → déficit de 0,5 × 1 100 = 550
kcal/jour → **budget de 1 950 kcal**.*
Un **plancher de sécurité** s'applique : 1 500 kcal pour un homme, 1 200 kcal pour une
femme, sauf valeur personnalisée. Si votre objectif passe sous ce plancher, le budget est
relevé et l'interface vous avertit que le rythme visé est trop agressif.
#### 4. La tendance, pas la balance
Votre poids varie de un à deux kilos d'un jour à l'autre pour des raisons qui n'ont rien à
voir avec la graisse : sel, hydratation, digestion, cycle hormonal. Se fier au chiffre brut,
c'est se condamner à l'angoisse.
LifeTrack calcule donc une **moyenne mobile exponentielle** (méthode dite « Hacker's Diet »,
facteur de lissage 0,1) : chaque nouvelle pesée ne déplace la courbe que de 10 % de l'écart
constaté. Les jours sans pesée sont correctement compensés, sans fausser le lissage.
**C'est cette courbe de tendance qu'il faut regarder, pas les points.** À partir de trois
pesées, LifeTrack calcule aussi une pente par régression linéaire et en déduit la **date
d'atteinte estimée** de votre objectif. Trois réponses possibles :
- une date, si la pente va dans le bon sens ;
- **« objectif atteint »**, à moins de 100 g de la cible ;
- **« ne converge pas »**, si la pente est nulle, part dans le mauvais sens, ou donne une
échéance à plus de dix ans. Ce n'est pas un bug : c'est une information.
#### 5. La calibration adaptative
Après **21 jours au moins** de journal alimentaire renseigné, la page **Balance énergétique**
compare la perte de poids *prévue* par vos déficits cumulés à la perte *réellement observée*
sur la tendance, et en déduit votre **TDEE réel**.
C'est la fonction la plus utile de LifeTrack sur la durée : elle corrige d'un coup les
erreurs de la formule théorique, la sous-estimation systématique des portions et l'adaptation
métabolique. Si l'écart est important, un bouton vous propose d'adopter cette valeur mesurée.
---
## Nutrition
### Recherche d'aliments avec Open Food Facts
Dans **Santé → Nutrition**, le champ de recherche interroge, dans cet ordre :
1. **le cache local** de LifeTrack — tous les aliments déjà consultés ;
2. **Open Food Facts**, la base collaborative mondiale (plus de 3 millions de produits, très
bonne couverture des rayons français).
Tapez au moins **2 caractères**. La recherche est en français et passe par le serveur, jamais
par votre navigateur.
**Chaque produit consulté est mis en cache définitivement.** Un produit que vous mangez
souvent n'est téléchargé qu'une seule fois, puis répond instantanément, même hors ligne.
Vous pouvez aussi chercher par **code-barres**.
Les valeurs sont mémorisées pour 100 g : calories, protéines, glucides, sucres, lipides,
acides gras saturés, fibres, sel. Quand les calories manquent mais que les kilojoules sont
présents, la conversion est faite automatiquement (÷ 4,184).
> **Open Food Facts est collaboratif : les données ne sont pas garanties.** Un produit peut
> avoir des valeurs fantaisistes ou une portion mal renseignée. Un coup d'œil au chiffre
> avant de valider évite bien des surprises. Si la base est injoignable, LifeTrack vous le
> dit clairement et vous propose la saisie manuelle.
> Pas de base d'aliments génériques française à ce jour. La table CIQUAL de l'ANSES
> (« Pomme, crue », « Baguette courante »…) était prévue mais **n'a pas été intégrée**. Pour
> vos plats maison, saisissez les valeurs à la main — ou créez-vous un favori une bonne fois
> pour toutes.
### Favoris et aliments récents
Deux raccourcis qui font toute la différence sur la durée :
- **Favoris** — vos aliments et portions habituels, épinglés en tête de liste. Votre café du
matin, votre yaourt, votre portion de riz.
- **Récents** — les derniers aliments enregistrés, proposés automatiquement.
Le journal est organisé par repas : petit-déjeuner, déjeuner, dîner, collation. Une entrée
sans type de repas explicite est classée selon l'heure (avant 11 h, petit-déjeuner ; avant
15 h, déjeuner ; avant 18 h, collation ; ensuite, dîner).
L'**hydratation** se suit à part, avec un objectif quotidien réglable dans le profil
(2 000 ml par défaut).
### Importer son historique Foodvisor
Foodvisor **n'expose aucune API** : ni OAuth, ni webhook, ni endpoint « mes repas ». Aucune
synchronisation continue n'est possible. Le seul chemin est un **export ponctuel**, à faire
une fois pour rapatrier votre historique.
#### Étape 1 : demander l'export dans l'application
Dans Foodvisor : **Réglages → Compte → « Demander mes données »** (l'intitulé et
l'emplacement varient selon les versions ; sur le tableau de bord web, cherchez sous
`Account → Privacy → Data`).
L'export arrive **par e-mail**, généralement sous 24 à 72 heures.
#### Étape 2 : si l'option est absente ou l'export incomplet, invoquez le RGPD
Foodvisor est une société française : vous disposez du **droit d'accès (article 15)** et du
**droit à la portabilité (article 20)**. Écrivez à **`data@foodvisor.io`**. Modèle :
> Objet : Demande d'accès et de portabilité de mes données personnelles (RGPD)
>
> Bonjour,
>
> Titulaire d'un compte Foodvisor associé à l'adresse `<votre e-mail>`, je souhaite exercer
> mes droits d'accès (article 15 du RGPD) et de portabilité (article 20).
>
> Je vous demande de me transmettre **l'intégralité de mon journal alimentaire dans un format
> structuré, couramment utilisé et lisible par machine (CSV ou JSON)**, incluant pour chaque
> entrée : l'horodatage, le type de repas, le nom de l'aliment, la portion et les valeurs
> nutritionnelles (calories et macronutriments). Merci d'y joindre également mon historique
> de poids et mes objectifs.
>
> Le délai légal de réponse est d'un mois à compter de la réception de la présente.
>
> Cordialement,
> `<votre nom>`
Le délai légal est d'**un mois**, prolongeable de deux mois pour une demande complexe.
#### Étape 3 : importer dans LifeTrack
1. Ouvrez **Imports**.
2. Déposez le CSV du journal alimentaire (si vous avez reçu une archive ZIP, extrayez-la
d'abord : c'est le fichier contenant une ligne par aliment consommé qui nous intéresse).
3. Laissez la détection automatique, ou choisissez le profil **« Foodvisor (export CSV) »**.
4. Vérifiez l'aperçu, puis lancez l'import.
L'importeur est volontairement tolérant : il accepte les en-têtes en français comme en
anglais, les séparateurs `,` et `;`, les encodages UTF-8 et Windows-1252, la virgule
décimale. Chaque ligne doit au minimum comporter **une date, un nom d'aliment et des
calories** — les autres colonnes sont facultatives.
Ce que l'export Foodvisor **ne contient pas**, et que vous ne récupérerez donc jamais : les
photos, les scores de confiance de l'IA, les codes-barres scannés, les micronutriments
au-delà des macros, et la décomposition des recettes en ingrédients.
---
## Vape et sevrage tabagique
### Étape 1 : la référence tabac
Rien ne fonctionne sans elle. **Réglages → Vape** :
| Champ | Exemple | Rôle |
|---|---|---|
| **Date d'arrêt du tabac** | 12/03/2026 | Point de départ des économies et des jalons santé |
| **Cigarettes par jour** | 20 | Votre consommation **avant** l'arrêt |
| **Cigarettes par paquet** | 20 | Généralement 20 |
| **Prix du paquet** | 12,50 € | Prix **actuel** du paquet |
| **Taux de nicotine par défaut** | 6 mg/ml | Utilisé quand une recharge n'en précise pas |
Cette référence est **gelée** : elle représente ce que vous dépenseriez si vous n'aviez pas
arrêté. Elle n'est jamais recalculée à partir de vos données de vape.
> **Une nuance d'honnêteté que LifeTrack affiche lui-même :** le prix retenu est le prix
> d'aujourd'hui, pas celui du jour de votre arrêt. Comme le tabac augmente régulièrement, les
> économies affichées sur les périodes anciennes sont **légèrement optimistes**. C'est
> assumé, et c'est indiqué dans l'interface.
Tant que ce formulaire n'est pas rempli, le module vape affiche « Module vape non
configuré » et refuse de calculer quoi que ce soit — c'est normal.
### Étape 2 : saisir les recharges
Onglet **Consommation**, bouton d'ajout. Deux façons de déclarer :
- **Recharge** (`refill`) — le remplissage habituel : la date et le volume en ml. Plusieurs
recharges dans la journée s'additionnent.
- **Total du jour** (`daily_total`) — vous n'avez pas compté vos remplissages mais vous savez
que vous avez consommé 6 ml aujourd'hui. **Cette valeur remplace la somme des recharges du
jour**, elle ne s'y ajoute pas.
Vous pouvez préciser le taux de nicotine et la recette utilisée ; sinon les valeurs par
défaut s'appliquent.
Une journée sans aucune saisie n'est **pas** comptée comme zéro : elle est marquée « non
suivie ». Les moyennes ne portent que sur les jours réellement suivis, et l'interface
affiche la proportion de jours suivis pour que vous sachiez quelle confiance accorder aux
chiffres.
### Étape 3 : changer une résistance en un clic
Onglet **Résistances**. Un seul bouton : **la date et l'heure sont celles de maintenant**,
sans aucune saisie. C'est la fonction la plus utilisée du module, elle devait être
instantanée.
LifeTrack en déduit :
- la **durée de vie** de la résistance précédente, affichée immédiatement après le clic ;
- la **durée de vie moyenne**, calculée sur les **5 derniers cycles** (14 jours tant qu'il
n'y a pas au moins deux changements) ;
- le **volume d'e-liquide passé** dans chaque résistance ;
- l'**âge de la résistance en cours**.
Vous pouvez rattacher un produit du catalogue au changement pour que son prix entre dans le
calcul d'amortissement.
### Étape 4 : le modèle de coût DIY
Onglet **Coûts & modèle**. Deux niveaux, selon votre patience.
**Le catalogue de produits.** Chaque produit porte un prix et une contenance : base
(1 000 ml), booster nicotine (10 ml), arôme (30 ml), résistances (paquet de 5). C'est tout ce
dont LifeTrack a besoin.
**Les recettes.** Une recette déclare un volume total, un taux de nicotine visé et ses
composants. Le coût se calcule simplement :
```
coût d'un composant = quantité × (prix du flacon contenance du flacon)
coût au ml = somme des composants volume total
```
*Exemple pour 100 ml : 20 ml de booster à 1 € les 10 ml (2,00 €) + 10 ml d'arôme à 6 € les
30 ml (2,00 €) + 70 ml de base à 12 € le litre (0,84 €) = **4,84 € pour 100 ml, soit 4,8
centimes par ml**.*
Un **assistant de recette** fait le calcul inverse : donnez-lui un volume total, un taux de
nicotine visé et le taux de votre booster, il vous rend les millilitres de booster, d'arôme
et de base. LifeTrack recalcule ensuite le taux de nicotine réel de la recette et vous
**avertit si l'écart avec la cible dépasse 10 %**.
Si vous n'avez pas envie de saisir des recettes, LifeTrack se rabat sur la **moyenne pondérée
de vos achats de liquide** sur les 90 derniers jours. Moins précis, mais sans effort.
**Le coût réel par jour** additionne :
```
coût/jour = ml consommés × coût au ml + (prix d'une résistance durée de vie moyenne)
```
L'amortissement de la résistance est souvent la surprise du calcul : une résistance à 2,50 €
qui tient 10 jours, c'est 25 centimes par jour, parfois plus que le liquide lui-même.
L'onglet affiche en parallèle vos **dépenses réelles** (issues de vos achats saisis) : l'écart
avec le coût théorique révèle vos stocks et vos achats d'impulsion.
### Étape 5 : lire ses économies
Onglet **Économies**. Le principe :
```
économies cumulées = (coût tabac de référence × jours écoulés) (coût de vape cumulé)
```
Les journées sans saisie de recharge ne sont pas comptées à zéro — ce serait tricher et
gonfler artificiellement les économies. Elles sont **imputées à votre moyenne des 30 derniers
jours**.
Deux courbes sont proposées : les **économies théoriques** (à partir du coût au ml calculé)
et les **économies réelles** (à partir de vos achats effectifs). La seconde est la vérité
comptable ; la première est plus stable.
À côté : cigarettes évitées, paquets évités, et **temps de vie récupéré** (compté sur la base
de 11 minutes par cigarette). L'équivalence nicotine ↔ cigarettes est donnée à titre indicatif
sur la base de 12 mg par cigarette — c'est un ordre de grandeur, pas une mesure.
Enfin, les **jalons santé** : douze étapes datées depuis votre arrêt, de la fréquence
cardiaque qui redescend après 20 minutes jusqu'au risque coronarien redevenu normal après
15 ans, avec la progression vers le prochain palier.
---
## Finances
### Étape 1 : exporter un relevé depuis sa banque
Connectez-vous à l'espace client de votre banque et cherchez « Télécharger », « Exporter » ou
« Historique des opérations ». **Préférez toujours l'OFX au CSV quand il est proposé** : ce
format embarque un identifiant unique par opération, ce qui rend la déduplication parfaite.
Presets intégrés à LifeTrack :
| Banque | Format testé | À savoir |
|---|---|---|
| **BoursoBank / Boursorama** | CSV `;`, UTF-8 | Plusieurs années disponibles, période libre |
| **Crédit Agricole** | CSV `;`, ISO-8859-15 | Préambule de longueur variable ; 30 à 90 jours selon la caisse |
| **BNP Paribas** | CSV `;`, ISO-8859-1 | Première ligne = solde, pas d'en-tête de colonnes ; environ 90 jours |
| **Société Générale** | CSV `;`, ISO-8859-1 | Environ 6 mois d'historique |
| **La Banque Postale** | CSV `;`, ISO-8859-15 | Préambule d'environ 8 lignes ; environ 90 jours |
| **Caisse d'Épargne** | CSV `;`, ISO-8859-1 | Colonnes Débit/Crédit séparées, crédit préfixé `+` |
| **Fortuneo** | CSV `;`, encodage détecté | Jusqu'à environ 10 ans d'historique |
| **Revolut** | CSV `,`, UTF-8 | Historique complet ; les frais sont déduits du montant |
| **N26** | CSV `,`, UTF-8 | Historique complet |
| **PayPal** | CSV `,` | Rapport d'activité, 7 ans par tranches de 12 mois |
| **OFX** | `.ofx` / `.qfx` | Toutes banques — **à privilégier** |
| **CSV générique** | tout CSV | Avec mappage de colonnes ajustable |
Une banque absente de la liste ? Le **CSV générique** avec l'éditeur de mappage traite
n'importe quel fichier tabulaire.
> Les banques modifient la mise en page de leurs exports sans prévenir. C'est précisément
> pour cela que l'aperçu existe : **vérifiez-le systématiquement**.
### Étape 2 : créer un compte de destination
Avant le premier import, créez le compte correspondant dans **Finances → Comptes** : un nom,
un type (courant, épargne, PayPal, espèces). Chaque transaction est rattachée à un compte —
c'est ce qui permet ensuite de détecter les virements entre vos propres comptes.
### Étape 3 : l'aperçu avant import
Déposez le fichier depuis **Imports** ou depuis le module **Finances**, choisissez le compte
et le profil bancaire. **Rien n'est écrit en base à ce stade.**
L'aperçu vous montre :
- les **20 premières lignes** telles que LifeTrack les a comprises : date, libellé, montant ;
- le nombre de lignes lues, de **doublons qui seront ignorés**, et de lignes en erreur ;
- la **période couverte** par le fichier ;
- un avertissement si ce fichier a **déjà été importé** à l'identique.
Contrôlez trois choses : les dates ne sont pas inversées (13/08 ne doit pas devenir le
8 mars), les montants ont le bon signe (les dépenses en négatif), les libellés ne sont pas
tronqués ni truffés de caractères bizarres.
Si quelque chose cloche, ouvrez **Ajuster le mappage** : séparateur, encodage, séparateur
décimal, format de date, lignes d'en-tête à ignorer, correspondance des colonnes, mode des
montants (une colonne signée, ou colonnes Débit/Crédit séparées). Vos réglages sont
enregistrés dans un **profil personnel** — le preset d'origine n'est jamais modifié.
Ensuite seulement, **Lancer l'import**.
### Étape 4 : la déduplication lors des ré-imports
C'est le point qui permet de ne pas réfléchir. **Vous pouvez ré-importer le même fichier
autant de fois que vous voulez, et importer des fichiers qui se chevauchent : aucune
transaction ne sera dupliquée.**
Deux mécanismes :
1. **Identifiant externe** — quand la source en fournit un (FITID de l'OFX, identifiant de
transaction PayPal), il fait foi. C'est le cas idéal.
2. **Empreinte de contenu** — sinon, LifeTrack calcule une empreinte à partir du compte, de
la date comptable, du montant et du libellé. Un **numéro d'occurrence** distingue les
opérations légitimement identiques (deux cafés à 2,50 € le même jour au même endroit sont
bien deux transactions, pas un doublon).
Conséquence pratique : téléchargez le relevé du mois entier chaque mois sans vous soucier des
chevauchements. Seules les nouvelles opérations seront ajoutées.
### Étape 5 : les règles de catégorisation
Une arborescence de catégories françaises complète est créée automatiquement au premier usage
(Alimentation, Logement, Transports, Santé, Loisirs, Shopping, Vape & tabac, Banque & frais,
Impôts & taxes… et côté revenus : Salaire, Aides & prestations, Remboursements, Ventes…).
Dans **Finances → Transactions**, une règle se compose de **conditions** et d'**actions** :
| Condition | Effet |
|---|---|
| **Libellé contient** | Correspondance textuelle simple — commencez toujours par là |
| **Expression régulière** | Pour les cas tordus ; une expression invalide est refusée avec un message clair |
| **Sens** | Débit, crédit, ou indifférent |
| **Montant minimum / maximum** | Fourchette |
| **Compte** | Restreindre à un compte |
Chaque règle porte une **priorité** ; on peut les réordonner. Par défaut une règle qui
correspond **arrête** l'évaluation des suivantes.
Trois garde-fous utiles :
- **Aperçu d'une règle** avant de l'enregistrer : vous voyez exactement quelles transactions
existantes elle attraperait.
- **Application en masse**, avec un choix de portée : uniquement les non catégorisées, toutes
celles qui n'ont pas été classées à la main, ou vraiment toutes.
- **Mode simulation** (`dry_run`) : LifeTrack compte ce qu'il changerait sans rien changer.
> **Vos décisions manuelles sont sacrées.** Une transaction que vous avez catégorisée
> vous-même n'est jamais réécrite par une règle, sauf demande explicite de forçage. Vous
> pouvez lancer vos règles sans crainte de perdre votre travail de tri.
Il existe aussi une **catégorisation en masse** directe, pour sélectionner plusieurs
transactions et les classer d'un coup sans écrire de règle.
### Étape 6 : les budgets
Dans **Finances → Budgets**, définissez un montant mensuel par catégorie, avec un mois de
début et éventuellement un mois de fin. LifeTrack affiche pour le mois en cours le budget, le
réalisé, le restant, le pourcentage d'avancement et une **projection de fin de mois** basée
sur votre rythme de dépense actuel — l'indicateur qui permet de corriger le tir avant le 30.
Un budget peut être révisé sans perdre son historique : la nouvelle valeur s'applique à
partir du mois que vous indiquez.
### Étape 7 : les virements internes
Quand vous déplacez 500 € de votre compte courant vers votre livret, ce n'est **pas** une
dépense. Sans traitement, ces mouvements polluent tous vos graphiques.
**Finances → Aperçu → Détecter les virements** cherche les paires de transactions qui
s'annulent : montants opposés, sur deux comptes différents, à **moins de 3 jours
d'intervalle**, avec un libellé évocateur (`VIR`, `VIREMENT`, `TRANSFERT`, `PAYPAL`). Les
paires trouvées sont classées dans la catégorie système **« Virements internes »** et exclues
des totaux de dépenses.
La détection n'est pas infaillible : vous pouvez **lier deux transactions à la main**, ou
**délier** une paire mal appariée.
### Étape 8 : les récurrents
**Finances → Récurrents** détecte tout seul vos abonnements et prélèvements réguliers, sans
que vous ayez rien à déclarer. Une série est reconnue à partir de **3 occurrences** au moins,
avec un intervalle régulier :
| Périodicité | Intervalle accepté |
|---|---|
| Hebdomadaire | 6 à 8 jours |
| Mensuelle | 25 à 35 jours |
| Trimestrielle | 85 à 97 jours |
| Annuelle | 350 à 380 jours |
Pour chaque série : le montant moyen, le montant attendu, la dernière date, la **prochaine
échéance prédite**, et si elle est toujours active. En haut de page, le **total mensuel
estimé** ramène tout à une base mensuelle (une dépense annuelle compte pour un douzième) :
c'est le montant qui part de votre compte chaque mois avant même que vous ayez décidé quoi
que ce soit.
---
## Dépannage
### « Mon fichier CSV est refusé » / la détection automatique se trompe
Dans l'ordre :
1. **Vérifiez l'extension.** LifeTrack ne se fie pas qu'au contenu : `.csv`, `.txt` pour les
relevés bancaires, `.ofx` ou `.qfx` pour l'OFX. Un fichier renommé `.xls` sera rejeté même
s'il contient du CSV.
2. **Vérifiez la taille : 20 Mio maximum.** Au-delà, découpez l'export par périodes.
3. **Choisissez le profil à la main** au lieu de « Détection automatique ». La détection
s'appuie sur les en-têtes des 4 096 premiers octets ; un export exotique ou un préambule
inhabituel peut la mettre en défaut.
4. **Ouvrez « Ajuster le mappage »** si l'aperçu est vide ou incohérent. Les suspects
habituels : le séparateur (`;` en France, `,` pour Revolut, N26 et PayPal), l'encodage
(`cp1252` pour la plupart des banques françaises, `utf-8` pour les néobanques) et le
format de date (`%d/%m/%Y` contre `%Y-%m-%d`).
5. **Message « Aucune ligne exploitable — vérifiez le profil de source »** : le fichier a bien
été lu, mais aucune ligne n'a produit de transaction. C'est presque toujours un profil
inadapté, pas un fichier corrompu.
Après l'import, le **rapport d'erreurs** est téléchargeable en CSV depuis l'historique : il
indique le numéro de ligne et le motif de chaque rejet. Les 100 premières erreurs sont
conservées.
### « J'ai des doublons »
Rappel : le ré-import du même fichier n'en crée jamais. Si vous en voyez malgré tout :
- **La même opération saisie à la main *et* importée** — la déduplication ne joue qu'entre
lignes importées. Supprimez celle de trop.
- **Deux sources décrivant le même événement** — par exemple une séance de sport arrivée à la
fois par Health Connect et par un CSV. Les séances qui se chevauchent à plus de 80 % sont
signalées comme doublons potentiels, mais elles restent deux lignes distinctes : c'est
volontaire, chaque source garde ses données.
- **Deux comptes bancaires pour le même relevé** — la déduplication est calculée **par
compte**. Importer le même fichier sur deux comptes crée bien deux jeux de transactions.
Vérifiez le compte de destination.
- **Le même relevé importé avec deux profils différents** — les libellés interprétés
différemment produisent des empreintes différentes. Annulez l'un des deux lots.
Pour les activités quotidiennes (pas, calories), il n'y a pas de doublon possible : une seule
ligne existe par jour et par source, et les sources sont fusionnées **champ par champ** selon
un ordre de priorité (saisie manuelle, puis Health Connect, puis les autres). LifeTrack
n'additionne jamais deux sources — ce serait le meilleur moyen de compter ses pas deux fois.
### « Les dates ou les heures sont décalées »
- **Une donnée apparaît la veille ou le lendemain.** Tout est stocké en UTC et réaffiché dans
votre fuseau. Vérifiez le **fuseau horaire du profil** (`Réglages → Profil`,
`Europe/Paris` par défaut). Une pesée à 00 h 30 heure française est enregistrée à 22 h 30
UTC la veille : si le fuseau est mal réglé, elle tombe le mauvais jour.
- **Un horodatage sans fuseau** (fréquent dans les exports CSV et chez Foodvisor) est
interprété en **Europe/Paris**, jamais en UTC. C'est le bon choix dans 99 % des cas, mais
cela décale les données d'un export produit dans un autre pays.
- **Un jour de plus ou de moins autour du changement d'heure** : les agrégations journalières
utilisent le fuseau, pas un décalage fixe. Un décalage résiduel sur les journées de
transition n'est pas anormal.
- **Les dates du relevé bancaire sont fausses** (le 04/03 devient le 3 avril) : mauvais format
de date dans le profil. Corrigez `date_format` dans le mappage et **annulez puis
ré-importez** le lot.
### « Module vape non configuré »
Le module refuse de calculer tant que la **référence tabac** n'est pas saisie. Allez dans
**Réglages → Vape** et renseignez au minimum la date d'arrêt, les cigarettes par jour avant
l'arrêt et le prix du paquet.
Autres cas voisins :
- **Aucun coût affiché** : il faut soit au moins une recette avec ses composants et leurs
prix, soit au moins un achat de liquide enregistré. Sans l'un ou l'autre, LifeTrack ne peut
pas connaître votre coût au ml — et préfère n'afficher aucun chiffre plutôt qu'un chiffre
faux.
- **Durée de vie des résistances à 14 jours** : c'est la valeur par défaut tant qu'il n'y a
pas eu **deux changements** enregistrés. Elle deviendra réelle dès le deuxième clic.
- **Économies qui semblent trop belles** : vérifiez la proportion de jours suivis. Si vous ne
saisissez vos recharges qu'une fois sur trois, l'imputation par la moyenne fait le reste et
la précision s'en ressent.
### Annuler un import (rollback)
Tout lot d'import est réversible.
1. Ouvrez **Imports** (ou **Finances** pour un relevé bancaire).
2. Dans **Historique des imports**, repérez la ligne concernée.
3. Cliquez sur **Annuler cet import**.
4. Confirmez : le nombre exact de lignes qui seront supprimées vous est indiqué.
Les données créées par ce lot disparaissent des tableaux de bord. **Le fichier peut être
ré-importé ensuite** — c'est la manœuvre normale quand on s'aperçoit après coup qu'on a
choisi le mauvais profil ou le mauvais compte : on annule, on corrige le mappage, on
recommence.
**Cas particulier des transactions modifiées à la main.** Si vous avez catégorisé
vous-même ou annoté des transactions du lot, LifeTrack **refuse l'annulation** et vous
avertit. Vous pouvez alors cocher **« Supprimer quand même les lignes modifiées
manuellement »** pour forcer. C'est irréversible : votre travail de catégorisation sur ces
lignes sera perdu.
### « Open Food Facts est injoignable »
Message attendu si votre serveur n'a pas d'accès Internet ou si le service est indisponible.
Ce n'est pas bloquant :
- les aliments **déjà consultés** restent disponibles dans le cache local ;
- la **saisie manuelle** fonctionne toujours ;
- réessayez plus tard, la recherche repartira sans rien perdre.
Si la recherche est simplement vide, vérifiez que vous avez tapé au moins **2 caractères**.
### « La passerelle Health Connect n'envoie rien »
Dans l'ordre :
1. **Regardez la colonne « Dernière utilisation »** dans **Réglages → Appareils & API**.
Toujours « Jamais » ? Aucune requête n'est jamais arrivée : le problème est côté réseau ou
côté application.
2. **Testez l'endpoint avec `curl`** (exemple plus haut). Si `curl` fonctionne, LifeTrack
n'est pas en cause.
3. **Vérifiez l'en-tête `X-API-Key`.** C'est la cause numéro un — voir plus haut : la
passerelle `health-connect-webhook` **ne sait pas poser d'en-tête**, elle ne peut donc pas
fonctionner en direct.
4. **Vérifiez la portée de la clé.** Une erreur 403 « Cette clé d'appareil ne dispose pas du
droit requis » signifie qu'il manque `ingest:health`.
5. **Erreur 404 « Domaine d'ingestion inconnu »** : seul le domaine `health` est traité
aujourd'hui. `POST /api/ingest/vape` et `POST /api/ingest/finance` n'existent pas encore,
même si les portées correspondantes sont proposées à la création d'une clé.
6. **Le téléphone atteint-il le serveur ?** Depuis le navigateur du téléphone, ouvrez
`http://<adresse-du-serveur>/api/healthz`. Vous devez voir `{"status":"ok"}`. Sinon, c'est
un problème de réseau local, de pare-feu ou de VPN.
7. **Économiseur de batterie** : excluez l'application des optimisations Android.
### L'application ne démarre pas du tout
```bash
docker compose ps # quels conteneurs tournent
docker compose logs api # journaux du backend
docker compose logs web # journaux de nginx
```
- **`api` redémarre en boucle** : presque toujours la base. Vérifiez que `POSTGRES_PASSWORD`
dans `.env` correspond bien à celui avec lequel le volume a été créé. Si vous avez changé
le mot de passe **après** le premier démarrage, l'ancien volume conserve l'ancien : soit
vous remettez l'ancien mot de passe, soit vous repartez de zéro avec
`docker compose down -v` (**qui efface toutes vos données**).
- **Page blanche sur `http://localhost`** : `web` tourne mais `api` non. Regardez les
journaux de `api`.
- **Port déjà utilisé** : changez `WEB_PORT` dans `.env` (par exemple `8080`) puis relancez.
- **Déconnexion permanente / boucle vers l'écran de connexion** : `LIFETRACK_JWT_SECRET` a
changé entre deux démarrages, ce qui invalide tous les jetons existants. Reconnectez-vous
une fois, et ne modifiez plus ce secret.
> Rappel : la pile Docker Compose **n'a jamais été démarrée pendant le développement** (le
> démon Docker était indisponible). Si le tout premier `docker compose up --build` bute sur
> un détail d'infrastructure, ce n'est pas anormal — commencez toujours par lire les journaux
> du conteneur fautif.