MDD Odoo pour 

 x_health_kpi_daily (agrégé/jour)

pour un agrégat journalier x_health_kpi_daily, je vous propose de le créer via Odoo Studio comme nouveau modèle et de le rattacher au module “Contacts” (résultat : chaque ligne = un patient (res.partner) × une date). 

L’ETL que nous avons déjà pourra écrire/mettre à jour ces KPI par upsert.

Résumé (où et quoi créer)

  • Où ? Dans Contacts avec Odoo Studio
  • Quoi ? Un modèle dédié x_health_kpi_daily (1 ligne/jour/patient) + One2many sur res.partner
  • Comment ? L’ETL calcule les agrégats journaliers et upsert par x_external_uid.

Où l’ajouter dans Odoo Online

  • Module de base à étendre : Contacts (res.partner)


  • Dans Odoo Studio :
    1. Créer un modèle x_health_kpi_daily (menu dans l’app “Contacts” ou “Santé”).
    2. Ajouter un One2many sur res.partner : x_kpi_daily_ids (inverse x_patient_id).
    3. Créer les vues (liste, pivot, graphe) et filtres (patient, date, dernières 30 j).

Modèle de Données (MDD) — x_health_kpi_daily

Colonnes : Nom technique | Étiquette | Type | Oblig. | Rôle / Agrégation

x_patient_id | Patient | m2o res.partner | Oui | Rattachement (clé 1) x_date | Date | Date | Oui | Jour civil du patient (local ou UTC, voir x_tz) x_tz | Timezone (IANA) | Char | Non | Ex: "Europe/Paris" pour déterminer le jour local x_samples | # échantillons (jour) | Integer | Non | Total points agrégés ce jour (toutes métriques) x_missing_data | Données incomplètes ? | Boolean | Non | True si des métriques attendues manquent x_last_measure_at | Dernière mesure (UTC) | Datetime | Non | Fraîcheur du jour -- Poids / composition x_weight_kg_avg | Poids moyen (kg) | Float | Non | Moyenne poids x_weight_kg_min | Poids min (kg) | Float | Non | Minimum jour x_weight_kg_max | Poids max (kg) | Float | Non | Maximum jour x_bmi_avg | IMC moyen | Float | Non | Moyenne IMC x_fat_pct_avg | Masse grasse (%) moy. | Float | Non | Moyenne % -- Tension artérielle (TA) x_sbp_avg | Systolique moy. (mmHg) | Float | Non | Moyenne x_dbp_avg | Diastolique moy. (mmHg) | Float | Non | Moyenne x_bp_samples | # mesures TA | Integer | Non | Échantillons TA -- Cardio / SpO₂ x_rest_hr_avg | FC repos moy. (bpm) | Float | Non | Moyenne x_hr_min | FC min (bpm) | Float | Non | Min x_hr_max | FC max (bpm) | Float | Non | Max x_spo2_avg | SpO₂ moy. (%) | Float | Non | Moyenne -- Sommeil (agrégats) x_sleep_duration_min | Sommeil total (min) | Float | Non | Somme du jour (nuit rattachée au jour d’endormissement) x_sleep_score_avg | Score sommeil moy. | Float | Non | Moyenne x_sleep_awake_min | Éveillé (min) | Float | Non | Somme x_sleep_deep_min | Profond (min) | Float | Non | Somme x_sleep_rem_min | REM (min) | Float | Non | Somme x_sleep_light_min | Léger (min) | Float | Non | Somme -- Activité x_steps_sum | Pas (jour) | Integer | Non | Somme x_distance_m_sum | Distance (m) | Float | Non | Somme x_calories_sum | Calories (kcal) | Float | Non | Somme -- Qualité / traçabilité x_quality_flag | Qualité agrégat | Selection | Non | `ok` / `suspect` / `artifact` x_sources | Sources (JSON) | Json | Non | Compteurs par type (ex: {"weight":2,"bp":1}) x_external_uid | UID externe | Char | Oui | Clé d’upsert ETL (voir ci-dessous) x_ingested_at | Ingesté le (UTC) | Datetime | Non | Timestamp écriture x_etl_version | Version ETL | Char | Non | Ex: `etl-fastapi@1.1.0`

Clé d’upsert recommandée

  • x_external_uid = sha256(f"{patient_ref}|{date}|kpi_daily")
  • L’ETL doit faire create-or-update sur ce champ pour garantir l’idempotence (Studio n’ajoute pas de contrainte unique SQL ; c’est l’ETL qui prévient les doublons).

Vues Studio à prévoir

Liste (tree)

Colonnes : x_date, x_weight_kg_avg, x_sbp_avg, x_dbp_avg, x_rest_hr_avg, x_spo2_avg, x_steps_sum, x_sleep_duration_min, x_missing_data

Pivot

  • Lignes : x_patient_id ou x_date
  • Colonnes : mois(x_date)
  • Mesures : avg(x_weight_kg_avg), avg(x_rest_hr_avg), sum(x_steps_sum), avg(x_spo2_avg), avg(x_sbp_avg), avg(x_dbp_avg), sum(x_sleep_duration_min)

Graph (line/bar)

  • Dimension : x_date
  • Mesures : choisir 1–2 KPI (ex. x_weight_kg_avg, x_steps_sum)
  • Grouper par : x_patient_id (facultatif)

Politique d’agrégation (ETL)

  • Fenêtre jour : utilisez la timezone du patient (x_tz) pour découper de 00:00 à 23:59:59 locales.
  • Poids/IMC/SpO₂/HR : avg, min, max selon les champs.
  • TA (SBP/DBP) : moyenne des mesures validées ; renseigner x_bp_samples.
  • Sommeil : agréger par nuit, puis affecter à x_date du début de nuit (évite de couper une nuit sur deux jours).
  • Activité : steps, distance_m, calories → somme de la journée.
  • Qualité : mettez x_quality_flag='suspect' si trop peu d’échantillons ou données contradictoires.
  • Fraîcheur : x_last_measure_at = timestamp de la dernière mesure prise en compte.

Exemple d’upsert (ETL → Odoo JSON-RPC)

{ "model": "x_health_kpi_daily", "record": { "x_patient_id": 42, "x_date": "2025-11-12", "x_tz": "Europe/Paris", "x_weight_kg_avg": 72.4, "x_weight_kg_min": 72.1, "x_weight_kg_max": 72.8, "x_bmi_avg": 23.1, "x_fat_pct_avg": 18.7, "x_sbp_avg": 122.0, "x_dbp_avg": 78.0, "x_bp_samples": 1, "x_rest_hr_avg": 58.0, "x_hr_min": 52.0, "x_hr_max": 94.0, "x_spo2_avg": 96.0, "x_sleep_duration_min": 415, "x_sleep_score_avg": 79, "x_steps_sum": 8421, "x_distance_m_sum": 6120, "x_calories_sum": 2280, "x_samples": 37, "x_missing_data": false, "x_sources": {"weight":2,"bp":1,"sleep":1,"activity":1}, "x_external_uid": "b4b0…sha256…6e", "x_ingested_at": "2025-11-12 07:30:05", "x_etl_version": "etl-fastapi@1.1.0" } }

génère les écrans Studio (liste/pivot/graph) en image HD comme guide