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>
299 lines
32 KiB
Markdown
299 lines
32 KiB
Markdown
# Recherche — Sources de données NUTRITION / CALORIES pour LifeTrack
|
||
|
||
> Document de recherche destiné aux agents d'implémentation. Rédigé le 2026-08-13.
|
||
> Langue : prose en français, identifiants de code en anglais (convention projet).
|
||
> Périmètre : ingestion des données alimentaires (repas, calories, macros) dans le module Santé/Fitness.
|
||
|
||
---
|
||
|
||
## 1. Résumé exécutif
|
||
|
||
| Source | Accès | Verdict v1 |
|
||
|---|---|---|
|
||
| Foodvisor — export in-app / RGPD | CSV limité, sur demande, pas d'automatisation | **Importeur CSV "best effort" + procédure RGPD documentée** (import ponctuel d'historique) |
|
||
| Foodvisor — API consommateur | **N'existe pas** | Hors périmètre |
|
||
| Foodvisor — Vision API (analyse photo) | Produit entreprise, tarif non public, contrat commercial | **Non viable v1** ; garder un port d'extension `photo_analyzer` |
|
||
| Foodvisor — via Health Connect | Non confirmé (Foodvisor Android se synchronise à Google Fit, pas à Health Connect de façon documentée) | Chemin indirect **à tester** via Health Sync ; ne pas en dépendre |
|
||
| Health Connect `NutritionRecord` (bridge Android) | Complet, structuré, sur l'appareil uniquement (pas d'API cloud) | **OUI — endpoint REST d'ingestion + app/bridge Android** (stratégie cible) |
|
||
| Open Food Facts API | Gratuit, ouvert (ODbL), code-barres + recherche | **OUI — pilier de la saisie manuelle assistée** |
|
||
| CIQUAL (ANSES) | Téléchargement Excel/XML, licence ouverte | **OUI — table locale d'aliments génériques français** |
|
||
| MyFitnessPal / Cronometer / Yazio — exports CSV | Formats variés, semi-documentés | Importeurs CSV **optionnels** (framework générique, mapping par profil) |
|
||
| Saisie manuelle rapide | — | **OUI — socle indispensable** |
|
||
|
||
**Stratégie v1 recommandée** : saisie manuelle rapide + recherche produit Open Food Facts (proxy backend + cache) + table CIQUAL locale pour les aliments génériques + framework d'importeurs CSV (Foodvisor, MyFitnessPal, Cronometer) + endpoint REST `POST /api/v1/ingest/nutrition` consommé par un bridge Health Connect. Un connecteur Foodvisor "direct" reste impossible aujourd'hui faute d'API ; on préserve la possibilité future via l'architecture connecteurs (section 8).
|
||
|
||
---
|
||
|
||
## 2. Foodvisor (l'app utilisée par l'utilisateur)
|
||
|
||
### 2.1 Ce que Foodvisor est (et n'est pas)
|
||
|
||
Foodvisor (société française, éditeur de l'app `io.foodvisor.foodvisor` sur Android et `id1064020872` sur iOS) est une app grand public de comptage de calories par photo (IA de reconnaissance alimentaire), scan de code-barres et saisie manuelle, avec coaching payant. **Il n'existe aucune API consommateur** (pas d'OAuth, pas d'endpoint "mes repas", pas de webhook). Toute intégration "live" est donc exclue.
|
||
|
||
### 2.2 Export de données utilisateur (in-app + RGPD)
|
||
|
||
Constats issus de plusieurs sources (guides Nutrola 2025-2026, politique de confidentialité Foodvisor) :
|
||
|
||
- **Export intégré** : dans l'app, `Réglages` → section `Compte` → entrée type « Demander mes données » / « Request my data » (l'intitulé et l'emplacement exacts varient selon les versions ; sur le tableau de bord web l'option est sous `Account` → `Privacy` → `Data`). L'export est **envoyé par e-mail**, généralement sous 24-72 h (le SLA de la politique de confidentialité autorise jusqu'à 30 jours).
|
||
- **Contenu de l'export** (archive ZIP contenant plusieurs CSV, ou CSV/PDF selon les versions) :
|
||
- objectifs et réglages en **CSV clé-valeur "à plat"** (objectif calorique, répartition des macros, métriques corporelles, niveau d'activité) ;
|
||
- **journal alimentaire** : une ligne par entrée avec horodatage, créneau de repas (petit-déjeuner/déjeuner/dîner/collation), nom de l'aliment, portion, macros calculées (protéines, glucides, lipides) et calories ;
|
||
- **totaux quotidiens** calories + macronutriments (parfois limités à une période récente et non à l'historique complet) ;
|
||
- **historique de poids** saisi manuellement (date + valeur, une ligne par pesée) ;
|
||
- aliments/recettes personnalisés avec leurs macros mais **rarement** la décomposition en ingrédients.
|
||
- **Ce qui manque** (confirmé par les guides de migration) : photos et scores de confiance IA, détails des scans code-barres (codes EAN), micronutriments au-delà des macros, horodatages fins au-delà du jour pour les totaux, notes/noms personnalisés, résumés de coaching.
|
||
- **Pièges de format observés** : horodatages « généralement ISO 8601 mais parfois en heure locale sans offset » ; encodage UTF-8 (Windows-1252 sur d'anciens exports) ; en-têtes potentiellement en français ou en anglais selon la langue du compte. **L'importeur devra être tolérant** (détection d'encodage, de délimiteur `,`/`;`, et mapping d'en-têtes bilingue).
|
||
- **Demande RGPD (article 15, droit d'accès + article 20, portabilité)** : Foodvisor est une société française ; contact **`data@foodvisor.io`** (contact officiel indiqué dans la politique de confidentialité pour l'exercice des droits d'accès, rectification, effacement, portabilité). Délai légal : **1 mois**, prolongeable de 2 mois pour les demandes complexes. Demander explicitement « l'intégralité de mon journal alimentaire au format structuré lisible par machine (CSV ou JSON), incluant les horodatages, portions et valeurs nutritionnelles » — cette voie contourne les limitations du bouton d'export intégré.
|
||
|
||
**Conséquence pour LifeTrack** : l'import Foodvisor est un **import ponctuel de rattrapage d'historique** (one-shot ou trimestriel), pas un flux continu. L'importeur CSV Foodvisor doit être conçu comme un *profil de mapping* du framework d'import générique (section 7.3), avec un écran de prévisualisation/mapping de colonnes car le format exact n'est **pas documenté officiellement et peut changer**. Ne pas coder en dur des noms de colonnes : livrer un mapping par défaut + éditeur de mapping.
|
||
|
||
### 2.3 Foodvisor Vision API (produit développeur)
|
||
|
||
- Produit B2B de Foodvisor : détection d'aliments et calcul nutritionnel à partir d'une photo (items reconnus, calories, macros, estimation des portions). Page produit : `https://www.foodvisor.io/en/vision/` ; documentation : `https://vision.foodvisor.io/docs` (⚠️ lors de nos tests d'août 2026, ce sous-domaine **ne résout plus en DNS** — signe que le produit est peu maintenu ou réservé aux clients sous contrat).
|
||
- **Modèle d'accès : contrat commercial entreprise uniquement.** Pas d'inscription self-service, pas de tarif public, pas de free tier, pas de spécification OpenAPI publique ; « les détails d'endpoints et d'authentification sont partagés avec les clients » sous accord. Accès via le formulaire de contact commercial.
|
||
- **Verdict** : inutilisable pour un projet personnel auto-hébergé en v1. Si l'analyse de photos de repas devient une exigence, alternatives à évaluer plus tard (non recherchées en détail ici, à vérifier avant usage) : LogMeal API, Passio Nutrition-AI, ou un LLM multimodal généraliste. **Décision d'architecture** : définir côté backend une interface `PhotoAnalyzer` (entrée : image ; sortie : liste de `FoodItemCandidate` avec macros et confiance) pour brancher n'importe quel fournisseur plus tard, y compris Foodvisor Vision si un contrat devenait accessible.
|
||
|
||
### 2.4 Foodvisor → Health Connect ?
|
||
|
||
- Sur **iOS**, Foodvisor se synchronise avec **Apple Health** (non pertinent ici, utilisateur Android).
|
||
- Sur **Android**, la seule intégration documentée est **Google Fit** (import/export nutrition, exercice, poids vers l'app Google Fit ; des fils de support Google Fit confirment cette synchro et ses ratés). **Aucune mention publique de support Health Connect** par Foodvisor à date (recherches août 2026).
|
||
- ⚠️ Les **API Google Fit sont dépréciées** (annonce Google mai 2024, arrêt programmé, migration officielle vers Health Connect). Le chemin `Foodvisor → Google Fit` est donc fragile et sans avenir.
|
||
- **Chemin indirect à tester** (effort ~30 min, aucune dépendance de code) : l'app tierce **Health Sync** (`nl.appyhapps.healthsync`, payante ~3-4 €/an) sait recopier des données entre Google Fit et Health Connect pour certains types de données. À tester sur le téléphone de l'utilisateur : si les repas Foodvisor poussés dans Google Fit peuvent être recopiés en `NutritionRecord` Health Connect, alors le bridge Health Connect de LifeTrack (section 3) récupérera les données Foodvisor **sans aucun code spécifique Foodvisor**. Ne pas bloquer la v1 sur ce test ; c'est un bonus.
|
||
|
||
---
|
||
|
||
## 3. Health Connect — type de données Nutrition (chemin cible)
|
||
|
||
### 3.1 Principe et contrainte fondamentale
|
||
|
||
Health Connect (Android 14+ intégré ; app système) est un magasin de données santé **sur l'appareil uniquement : il n'existe AUCUNE API cloud/REST côté serveur**. Pour amener les données dans LifeTrack, il faut une app Android (le « bridge » déjà prévu au projet) qui lit Health Connect via `androidx.health.connect:connect-client` et pousse vers l'endpoint REST authentifié de LifeTrack.
|
||
|
||
### 3.2 `NutritionRecord` — champs exacts
|
||
|
||
Classe : `androidx.health.connect.client.records.NutritionRecord` (miroir plateforme : `android.health.connect.datatypes.NutritionRecord`). Un enregistrement = un repas/une prise alimentaire sur un intervalle de temps.
|
||
|
||
- **Temporalité** : `startTime`, `endTime` (Instant, UTC), `startZoneOffset`, `endZoneOffset` (nullable).
|
||
- **Identité** : `name` (String?, nom du repas/aliment), `mealType` (Int) avec valeurs : `MEAL_TYPE_UNKNOWN = 0`, `MEAL_TYPE_BREAKFAST = 1`, `MEAL_TYPE_LUNCH = 2`, `MEAL_TYPE_DINNER = 3`, `MEAL_TYPE_SNACK = 4`.
|
||
- **Énergie** : `energy`, `energyFromFat` — type `Energy` (exposer en kcal via `energy.inKilocalories`).
|
||
- **Nutriments** (tous de type `Mass`, à lire en grammes via `inGrams` ; tous nullables) : `protein`, `totalCarbohydrate`, `sugar`, `dietaryFiber`, `totalFat`, `saturatedFat`, `unsaturatedFat`, `monounsaturatedFat`, `polyunsaturatedFat`, `transFat`, `cholesterol`, `sodium`, `potassium`, `calcium`, `iron`, `magnesium`, `phosphorus`, `zinc`, `copper`, `manganese`, `selenium`, `iodine`, `chromium`, `molybdenum`, `chloride`, `caffeine`, `biotin`, `folate`, `folicAcid`, `niacin`, `pantothenicAcid`, `riboflavin`, `thiamin`, `vitaminA`, `vitaminB6`, `vitaminB12`, `vitaminC`, `vitaminD`, `vitaminE`, `vitaminK`.
|
||
- ⚠️ Les documentations Google confirment : **tous les nutriments sont en grammes** (y compris sodium — pas en mg — et les vitamines), l'énergie en kcal. Convertir côté bridge ou côté API, mais **stocker en unités SI cohérentes** (voir 7.2).
|
||
- **Métadonnées** (`metadata`) : `id` (UUID Health Connect, **clé de déduplication idéale**), `dataOrigin.packageName` (app source, ex. `com.myfitnesspal.android`, permet le champ `source_app`), `lastModifiedTime`, `clientRecordId`/`clientRecordVersion`, `recordingMethod`, `device`.
|
||
- **Agrégats disponibles** (API `aggregate`) : `NutritionRecord.ENERGY_TOTAL`, `PROTEIN_TOTAL`, `TOTAL_CARBOHYDRATE_TOTAL`, `TOTAL_FAT_TOTAL`, etc. — utile si le bridge veut pousser des totaux journaliers en plus des repas unitaires. Recommandation : **pousser les enregistrements unitaires** et laisser LifeTrack agréger.
|
||
|
||
### 3.3 Permissions et limites côté bridge
|
||
|
||
- Permission de lecture : `android.permission.health.READ_NUTRITION` (+ autres types déjà prévus : steps, calories, distance, poids, exercices).
|
||
- **Limite des 30 jours** : par défaut une app ne peut lire que les données antérieures de 30 jours max à la première autorisation. La permission `android.permission.health.READ_HEALTH_DATA_HISTORY` (introduite en 2024) lève cette limite — **à demander dès la v1 du bridge** pour récupérer l'historique complet.
|
||
- Lecture incrémentale : utiliser l'API **Changes / changement de jetons** (`getChangesToken` + `getChanges`) pour ne pousser que les deltas ; repli : requête par `TimeRangeFilter` depuis le dernier push réussi.
|
||
- Lecture en arrière-plan : permission `READ_HEALTH_DATA_IN_BACKGROUND` + WorkManager périodique (les bridges existants utilisent des périodes de 1-2 h).
|
||
|
||
### 3.4 Bridges open source réutilisables (au lieu de tout écrire)
|
||
|
||
- **HCGateway** (`github.com/ShuchirJ/HCGateway`) : bridge REST universel Health Connect ↔ serveur, app Android + serveur auto-hébergeable, sync bidirectionnelle, push toutes les ~2 h. Projet jeune (« API may change without notice ») mais **la partie app Android est une excellente base de code de référence** (lecture de tous les types de records dont nutrition, sérialisation JSON).
|
||
- **health-connect-webhook** (`github.com/mcnaveen/health-connect-webhook`) : app Android qui pousse les données Health Connect vers un webhook arbitraire — modèle exactement aligné avec notre endpoint d'ingestion générique.
|
||
- **Health Sync** (payant, closed source) : pour la recopie inter-apps sur le téléphone (cf. 2.4), pas pour pousser vers LifeTrack.
|
||
|
||
**Recommandation** : concevoir l'endpoint d'ingestion LifeTrack (section 7.4) de façon à pouvoir accepter soit notre futur bridge maison, soit un HCGateway/webhook adapté, en gardant un schéma JSON proche du modèle `NutritionRecord`.
|
||
|
||
---
|
||
|
||
## 4. Open Food Facts (OFF) — recherche produit + code-barres
|
||
|
||
Base collaborative mondiale (>3 M produits, très bonne couverture des produits vendus en France). **Gratuit, sans clé d'API.**
|
||
|
||
### 4.1 Endpoints
|
||
|
||
- **Produit par code-barres** :
|
||
`GET https://world.openfoodfacts.org/api/v2/product/{barcode}` (JSON). Utiliser `https://fr.openfoodfacts.org/...` pour prioriser les libellés français.
|
||
Paramètre `fields` pour limiter la réponse, ex. :
|
||
`?fields=code,product_name,product_name_fr,brands,quantity,serving_size,serving_quantity,nutriments,nutriscore_grade,nova_group,ecoscore_grade,image_front_small_url,categories_tags,lang`
|
||
- **API v3** (courante, recommandée pour les nouvelles intégrations) : `GET /api/v3/product/{barcode}` — v2 reste supportée ; v0/v1 à éviter.
|
||
- **Recherche plein texte** : ⚠️ pas disponible dans l'API v2 serveur. Deux options :
|
||
- **Search-a-licious** (service de recherche officiel) : `https://search.openfoodfacts.org` (API documentée sur ce domaine, `GET /search?q=...&langs=fr&page_size=20`) — recommandé ;
|
||
- legacy : `GET https://fr.openfoodfacts.org/cgi/search.pl?search_terms=...&json=1` (lent, déprécié mais fonctionnel).
|
||
- **Recherche filtrée v2** (par catégorie/marque/nutriment, sans plein texte) : `GET /api/v2/search?categories_tags=...&fields=...`.
|
||
- **Environnement de staging** : `https://world.openfoodfacts.net` (HTTP Basic `off`/`off`) — utiliser pour les tests d'intégration automatisés.
|
||
- **SDK Python officiel** : paquet PyPI `openfoodfacts` (`openfoodfacts-python`) — utilisable directement dans l'API FastAPI, mais un simple client `httpx` suffit.
|
||
|
||
### 4.2 Champs `nutriments` utiles (clés JSON exactes)
|
||
|
||
Par 100 g : `energy-kcal_100g`, `energy_100g` (kJ), `proteins_100g`, `carbohydrates_100g`, `sugars_100g`, `fat_100g`, `saturated-fat_100g`, `fiber_100g`, `salt_100g`, `sodium_100g` (en g). Variantes par portion : suffixe `_serving` (ex. `energy-kcal_serving`) + `serving_size` (texte, ex. "30 g") et `serving_quantity` (nombre). Les champs peuvent être **absents ou incohérents** (données collaboratives) : toujours valider (`energy-kcal_100g` manquant → recalculer depuis kJ ÷ 4,184 ; contrôler que macros × facteurs Atwater ≈ énergie à ±20 %).
|
||
|
||
### 4.3 Règles d'usage impératives
|
||
|
||
- **User-Agent personnalisé obligatoire** : format `AppName/Version (ContactEmail)`, ex. `LifeTrack/1.0 (meejayproduction@gmail.com)` — sous peine d'être traité comme bot.
|
||
- **Rate limits (docs officielles, août 2026)** : **15 req/min/IP** pour les lectures produit, **10 req/min/IP** pour les recherches. Conséquences d'implémentation : (a) toutes les requêtes OFF passent par le **backend LifeTrack (proxy)**, jamais depuis le navigateur ; (b) **cache local** de chaque produit consulté (table `food_items`, cf. 7.2) — un produit scanné une fois ne re-sollicite plus OFF ; (c) throttle côté serveur (token bucket 10/min) + retry avec backoff sur 429.
|
||
- **Pas de scraping massif via l'API** : pour un besoin hors-ligne complet, utiliser les **exports intégraux** (CSV ~ plusieurs Go, JSONL, dump MongoDB, Parquet sur Hugging Face) — non nécessaire en v1.
|
||
- **Licence** : base sous **ODbL** (contenus sous DbCL, images CC-BY-SA). Pour un usage personnel auto-hébergé : afficher une attribution « Données produits : Open Food Facts (ODbL) » dans l'UI suffit largement ; l'obligation de partage à l'identique ne s'applique que si l'on redistribue une base dérivée.
|
||
|
||
---
|
||
|
||
## 5. CIQUAL (ANSES) — table de composition française (aliments génériques)
|
||
|
||
Complément indispensable d'OFF : OFF couvre les **produits industriels code-barrés**, CIQUAL couvre les **aliments génériques** (« Pomme, crue », « Baguette courante », « Poulet rôti, cuisse ») — exactement ce qu'il faut pour la saisie manuelle de plats maison.
|
||
|
||
- **Éditeur** : ANSES (Agence nationale de sécurité sanitaire de l'alimentation). Site de consultation : `https://ciqual.anses.fr`.
|
||
- **Versions** : table **Ciqual 2020** (~3 185 aliments, ~60 constituants) ; une **table Ciqual 2025** a été publiée fin 2025 (~3 484 aliments, sucres individuels détaillés, fruits/légumes mis à jour incl. outre-mer). Prendre la 2025 si disponible au moment de l'implémentation, sinon 2020.
|
||
- **Téléchargement** : formats **Excel (.xls/.xlsx) et XML**, depuis `ciqual.anses.fr` (rubrique téléchargement) et sur **data.gouv.fr** : jeu de données « Table de composition nutritionnelle des aliments Ciqual » (`https://www.data.gouv.fr/datasets/table-de-composition-nutritionnelle-des-aliments-ciqual-2020`). La table 2025 est aussi déposée sur `entrepot.recherche.data.gouv.fr` (DOI `10.57745/RDMHWY`). Documentation PDF officielle du format Excel disponible sur le site Ciqual.
|
||
- **Licence** : open data (Licence Ouverte / Etalab 2.0 via data.gouv.fr) — usage libre avec mention de la source « ANSES — Table Ciqual ».
|
||
- **Structure du fichier Excel (à connaître pour l'importeur)** :
|
||
- une ligne par aliment : `alim_code` (code numérique stable), `alim_nom_fr` (libellé français), `alim_nom_eng`, groupes/sous-groupes (`alim_grp_code`, `alim_grp_nom_fr`, `alim_ssgrp_...`) ;
|
||
- une colonne par constituant, valeurs **pour 100 g**, libellés du type « Energie, Règlement UE N° 1169/2011 (kcal/100 g) », « Protéines, N x facteur de Jones (g/100 g) », « Glucides (g/100 g) », « Lipides (g/100 g) », « Sucres (g/100 g) », « Fibres alimentaires (g/100 g) », « Sel chlorure de sodium (g/100 g) », vitamines/minéraux ;
|
||
- **pièges de parsing** : séparateur décimal **virgule** ; valeurs non numériques `"-"` (non déterminé), `"traces"`, et bornes `"< 0,5"` — l'importeur doit normaliser (`traces` → 0, `< x` → x/2 ou 0 selon règle choisie, `-` → NULL) ; encodage à vérifier (exports historiques en Windows-1252/latin-1).
|
||
- **Intégration recommandée** : script one-shot `scripts/import_ciqual.py` qui charge le fichier Excel/XML dans la table `food_items` avec `source='ciqual'`, `source_id=alim_code`. Le fichier CIQUAL est mis dans un volume/dossier `data/` (préchargé dans l'image ou téléchargé au premier lancement par l'admin) — pas d'appel réseau au runtime.
|
||
|
||
---
|
||
|
||
## 6. Exports CSV des autres apps de suivi alimentaire
|
||
|
||
Objectif : le framework d'import CSV doit accepter les exports des apps majeures, pour migration d'historique ou si l'utilisateur change d'app un jour.
|
||
|
||
### 6.1 MyFitnessPal
|
||
|
||
- **Export "File Export" (Premium uniquement)** : Web → `Reports`/`Settings` → export par plage de dates ; production asynchrone (minutes à heures), lien envoyé par e-mail ; **3 CSV : Nutrition, Exercise, Progress**. Le CSV Nutrition est à la granularité **jour × repas** (Breakfast/Lunch/Dinner/Snacks), avec colonnes type : `Date`, `Meal`, `Calories`, `Fat (g)`, `Saturated Fat`, `Polyunsaturated Fat`, `Monounsaturated Fat`, `Trans Fat`, `Cholesterol`, `Sodium (mg)`, `Potassium`, `Carbohydrates (g)`, `Fiber`, `Sugar`, `Protein (g)`, `Vitamin A`, `Vitamin C`, `Calcium`, `Iron`, `Note` (⚠️ liste constatée sur des exports réels et outils communautaires, non documentée officiellement — **valider sur un échantillon réel** avant de figer le mapping ; d'où l'éditeur de mapping, 7.3).
|
||
- **Compte gratuit** : demande de téléchargement des données personnelles (RGPD/CCPA) via les réglages du compte — archive moins structurée.
|
||
- Il existe une lib Python non officielle `python-myfitnesspal` (scraping avec login) — fragile, **hors périmètre v1**.
|
||
|
||
### 6.2 Cronometer
|
||
|
||
- Export self-service (Web → `More`/`Account` → `Export Data`, plage de dates), **immédiat et gratuit**, en 5 fichiers CSV : `servings.csv` (une ligne par aliment consommé : `Day`, `Group` (repas), `Food Name`, `Amount`, puis ~70 colonnes de nutriments avec unités dans l'en-tête, ex. `Energy (kcal)`, `Protein (g)`, `Sodium (mg)`), `dailysummary.csv` (totaux/jour), `exercises.csv`, `biometrics.csv` (poids !), `notes.csv`. Le plus propre et le plus riche des trois — profil de mapping facile.
|
||
|
||
### 6.3 Yazio
|
||
|
||
- **Pas d'export CSV natif** : l'app ne propose qu'un PDF limité ; l'archive complète s'obtient par **demande RGPD** (siège à Erfurt, Allemagne ; réponse sous 1 mois ; en pratique ZIP de JSON/CSV).
|
||
- Outils open source non officiels : `github.com/aleksandr-bogdanov/yazio-exporter` (Python ; login e-mail/mot de passe sur l'API non officielle de Yazio, export JSON/CSV/SQLite : journal, aliments consommés, poids, exercices, eau, 40+ micronutriments) et `github.com/tobintax/yazio-csv-exporter`. Utilisables ponctuellement par l'utilisateur **en dehors** de LifeTrack ; LifeTrack se contente d'importer les CSV produits.
|
||
|
||
### 6.4 Foodvisor (rappel)
|
||
|
||
Voir 2.2 : ZIP de CSV (journal par entrée, totaux quotidiens, poids, réglages clé-valeur). Profil d'import dédié, tolérant (encodage, délimiteur, en-têtes FR/EN, horodatages sans offset → interpréter en Europe/Paris puis convertir en UTC).
|
||
|
||
---
|
||
|
||
## 7. Stratégie d'ingestion nutrition v1 — recommandations concrètes
|
||
|
||
### 7.1 Les quatre canaux v1 (ordre de priorité d'implémentation)
|
||
|
||
1. **Saisie manuelle rapide** (UI français) : formulaire "repas" minimal — date/heure (défaut : maintenant, TZ Europe/Paris), type de repas (petit-déjeuner/déjeuner/dîner/collation), soit saisie libre kcal+macros, soit sélection d'un aliment (recherche locale `food_items` + OFF + CIQUAL) × quantité. Duplication d'un repas précédent ("répéter hier") = fonctionnalité à fort ROI.
|
||
2. **Recherche produit** : endpoint backend `GET /api/v1/foods/search?q=...` qui interroge (a) le cache local `food_items`, (b) CIQUAL local, (c) Open Food Facts (Search-a-licious + fallback code-barres direct), fusionne et met en cache. Scan de code-barres possible plus tard côté PWA (caméra) → `GET /api/v1/foods/barcode/{ean}`.
|
||
3. **Importeurs CSV** via le framework d'import générique du projet : profils `foodvisor`, `myfitnesspal`, `cronometer` (+ `generic_nutrition_csv` avec mapping manuel). Pipeline : upload → détection encodage/délimiteur → proposition de mapping (profil) → prévisualisation → validation → insertion idempotente.
|
||
4. **Endpoint d'ingestion REST** pour le bridge Health Connect (voir 7.4) — même endpoint générique que pour les pas/poids, avec le type `nutrition`.
|
||
|
||
### 7.2 Modèle de données proposé (SQLAlchemy, PostgreSQL)
|
||
|
||
Deux tables, séparant **référentiel d'aliments** et **journal** :
|
||
|
||
```text
|
||
food_items # referential/cache of foods
|
||
id UUID PK
|
||
source TEXT NOT NULL -- 'off' | 'ciqual' | 'custom'
|
||
source_id TEXT -- OFF barcode | ciqual alim_code | NULL
|
||
name TEXT NOT NULL -- French label preferred
|
||
brand TEXT
|
||
energy_kcal_100g NUMERIC
|
||
protein_g_100g NUMERIC
|
||
carbs_g_100g NUMERIC
|
||
sugar_g_100g NUMERIC
|
||
fat_g_100g NUMERIC
|
||
sat_fat_g_100g NUMERIC
|
||
fiber_g_100g NUMERIC
|
||
salt_g_100g NUMERIC -- salt = sodium * 2.5
|
||
serving_size_g NUMERIC
|
||
raw_payload JSONB -- full OFF/CIQUAL record
|
||
UNIQUE (source, source_id)
|
||
|
||
nutrition_entries # the journal (one row = one food eaten OR one whole meal)
|
||
id UUID PK
|
||
user_id UUID FK
|
||
eaten_at TIMESTAMPTZ NOT NULL -- stored UTC
|
||
meal_type TEXT NOT NULL -- 'breakfast'|'lunch'|'dinner'|'snack'|'unknown'
|
||
label TEXT -- free-text name
|
||
food_item_id UUID FK NULL -- when picked from referential
|
||
quantity_g NUMERIC NULL
|
||
energy_kcal NUMERIC NOT NULL -- always denormalized at entry level
|
||
protein_g NUMERIC
|
||
carbs_g NUMERIC
|
||
sugar_g NUMERIC
|
||
fat_g NUMERIC
|
||
sat_fat_g NUMERIC
|
||
fiber_g NUMERIC
|
||
sodium_mg NUMERIC
|
||
source TEXT NOT NULL -- 'manual'|'import:foodvisor'|'import:mfp'|'import:cronometer'|'healthconnect'
|
||
source_record_id TEXT -- HC metadata.id, or import row hash
|
||
source_app TEXT -- HC dataOrigin.packageName
|
||
import_batch_id UUID FK NULL
|
||
UNIQUE (user_id, source, source_record_id)
|
||
```
|
||
|
||
- Unités stockées : **kcal, grammes, sodium en mg** (convention affichage FR) — conversions faites à l'ingestion (Health Connect fournit tout en g/kcal, cf. 3.2).
|
||
- Le bilan énergétique quotidien (kcal in) = `SUM(energy_kcal)` groupé par jour **en Europe/Paris** (conversion au moment de la requête, pas au stockage).
|
||
- **Déduplication inter-sources** : contrainte unique `(user_id, source, source_record_id)` pour l'idempotence intra-source ; pour l'inter-sources (ex. un repas présent à la fois dans le CSV Foodvisor et via Health Connect), règle v1 simple : l'UI signale les jours où deux sources se chevauchent (>1 source sur la même journée) et propose de masquer une source par plage de dates (`source_priority` par jour). Ne pas tenter de matching flou par entrée en v1.
|
||
|
||
### 7.3 Framework d'import CSV — exigences pour la nutrition
|
||
|
||
- Détection : BOM/UTF-8/Windows-1252, délimiteur `,`/`;`/tab, format de date (`YYYY-MM-DD`, `DD/MM/YYYY`, ISO 8601 avec/sans offset — sans offset ⇒ supposer Europe/Paris).
|
||
- Profils = fichiers de mapping déclaratifs (JSON/YAML versionnés dans le repo) : `column → field`, transformations (`comma_decimal`, `meal_type_map` FR/EN : `Petit-déjeuner→breakfast`, etc.), granularité (`per_food_row` pour Cronometer `servings.csv`, `per_meal_row` pour MFP, `per_entry_row` pour Foodvisor).
|
||
- Idempotence : `source_record_id = SHA-256(user_id + source + date + meal + label + kcal)` en absence d'ID natif ; ré-import du même fichier = 0 doublon.
|
||
- Chaque import crée un `import_batch` (annulable en bloc — indispensable vu l'instabilité des formats).
|
||
|
||
### 7.4 Endpoint d'ingestion Health Connect (bridge)
|
||
|
||
`POST /api/v1/ingest/nutrition` (JWT device token, scope `ingest`) — schéma aligné sur `NutritionRecord` :
|
||
|
||
```json
|
||
{
|
||
"records": [
|
||
{
|
||
"source_record_id": "hc-uuid-from-metadata-id",
|
||
"source_app": "io.foodvisor.foodvisor",
|
||
"start_time": "2026-08-13T11:45:00Z",
|
||
"end_time": "2026-08-13T12:15:00Z",
|
||
"meal_type": "lunch",
|
||
"name": "Salade poulet",
|
||
"energy_kcal": 620.0,
|
||
"protein_g": 42.1,
|
||
"total_carbohydrate_g": 51.0,
|
||
"sugar_g": 8.2,
|
||
"total_fat_g": 24.3,
|
||
"saturated_fat_g": 6.1,
|
||
"dietary_fiber_g": 7.0,
|
||
"sodium_g": 1.1
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
Réponse : `{"accepted": n, "duplicates": m, "rejected": [...]}` — upsert sur `(user_id, 'healthconnect', source_record_id)`. Le même pattern (`/ingest/steps`, `/ingest/weight`, ...) sert tout le bridge Health Connect. Côté bridge : demander `READ_NUTRITION` + `READ_HEALTH_DATA_HISTORY`, sync incrémentale par Changes API, batch de 100-500 records.
|
||
|
||
### 7.5 Garder un futur connecteur Foodvisor possible
|
||
|
||
1. **Architecture connecteurs** : chaque source = classe `Connector` (métadonnées, capacités `file_import`/`api_pull`/`push`, mapping). `FoodvisorCsvConnector` (v1, `file_import`) et un éventuel `FoodvisorApiConnector` (si une API apparaît) partagent le même normaliseur `FoodvisorRecordNormalizer`.
|
||
2. **Interface `PhotoAnalyzer`** (cf. 2.3) : port d'extension pour Foodvisor Vision ou tout autre service d'analyse de photo.
|
||
3. **Chemin sans code** : si le test Health Sync (2.4) réussit, les données Foodvisor arrivent déjà par le canal Health Connect avec `source_app` identifiable.
|
||
4. **Veille** : re-vérifier périodiquement (a) l'apparition d'un support Health Connect dans l'app Foodvisor Android (fiche Play Store, permissions `android.permission.health.WRITE_NUTRITION`), (b) la résurrection de `vision.foodvisor.io`.
|
||
|
||
---
|
||
|
||
## 8. Checklist pour les agents d'implémentation
|
||
|
||
- [ ] Table `food_items` + import CIQUAL (script one-shot, gestion `traces`/`< x`/virgule décimale).
|
||
- [ ] Proxy OFF : client httpx avec `User-Agent: LifeTrack/1.0 (meejayproduction@gmail.com)`, cache DB, throttle 10 req/min, staging `.net` pour les tests.
|
||
- [ ] `GET /foods/search`, `GET /foods/barcode/{ean}` ; UI de saisie manuelle FR (types de repas : Petit-déjeuner, Déjeuner, Dîner, Collation).
|
||
- [ ] Framework import CSV + profils `cronometer` (le plus simple, commencer par lui), `myfitnesspal`, `foodvisor`, `generic_nutrition_csv` ; éditeur de mapping ; batches annulables.
|
||
- [ ] `POST /api/v1/ingest/nutrition` idempotent (+ doc French pour configurer HCGateway/webhook en attendant le bridge maison).
|
||
- [ ] Attributions UI : « Open Food Facts (ODbL) », « ANSES — Table Ciqual ».
|
||
- [ ] Doc utilisateur FR : procédure d'export Foodvisor in-app + modèle d'e-mail RGPD à `data@foodvisor.io`.
|
||
|
||
## 9. Sources
|
||
|
||
- Guide export Foodvisor (FR) : https://nutrola.app/fr/blog/how-to-export-data-from-foodvisor ; migration : https://nutrola.app/en/blog/migrating-from-foodvisor-how-to-import-data
|
||
- Politique de confidentialité Foodvisor : https://www.foodvisor.io/en/privacy-policy/ ; support privacy : https://foodvisor.zendesk.com/hc/en-us/categories/360002566060-Privacy
|
||
- Foodvisor Vision API : https://www.foodvisor.io/en/vision/ ; https://vision.foodvisor.io/docs (DNS KO août 2026) ; https://github.com/api-evangelist/foodvisor ; https://apis.io/providers/foodvisor/
|
||
- Foodvisor ↔ Google Fit (fil support) : https://support.google.com/fit/thread/327566968
|
||
- Health Connect `NutritionRecord` : https://developer.android.com/reference/androidx/health/connect/client/records/NutritionRecord ; https://developer.android.com/reference/android/health/connect/datatypes/NutritionRecord ; nutrition data types : https://developers.google.com/health/data-types/nutrition ; écriture : https://developer.android.com/health-and-fitness/health-connect/write-data
|
||
- Bridges : https://github.com/ShuchirJ/HCGateway ; https://github.com/mcnaveen/health-connect-webhook ; https://healthsync.app/
|
||
- Open Food Facts API : https://openfoodfacts.github.io/openfoodfacts-server/api/ ; recherche : https://search.openfoodfacts.org
|
||
- CIQUAL : https://ciqual.anses.fr ; https://www.data.gouv.fr/datasets/table-de-composition-nutritionnelle-des-aliments-ciqual-2020 ; doc table 2025 : https://ciqual.anses.fr/cms/sites/default/files/inline-files/Table%20Ciqual%202025%20doc%20FR_2025_11_19.pdf ; dépôt 2025 : https://entrepot.recherche.data.gouv.fr/dataset.xhtml?persistentId=doi:10.57745/RDMHWY
|
||
- MyFitnessPal export : https://support.myfitnesspal.com/hc/en-us/articles/360032273352-Data-Export-FAQs ; https://quantifiedself.com/blog/access-export-myfitnesspal-data/
|
||
- Cronometer export : https://support.cronometer.com/hc/en-us/articles/360018760151-Account-Settings ; forums : https://forums.cronometer.com/discussion/460/exporting-data
|
||
- Yazio : https://nutrola.app/en/blog/how-to-export-data-from-yazio ; https://github.com/aleksandr-bogdanov/yazio-exporter ; https://github.com/tobintax/yazio-csv-exporter
|