/** * ───────────────────────────────────────────────────────────────────────────── * Modèles de données (portage de `data/model/*.kt`). * * ⚠️ COMPATIBILITÉ BACKUP : les noms de champs sont CEUX DE GSON (noms de * propriétés Kotlin) — identiques aux entités Room. NE JAMAIS renommer un * champ ici sans vérifier BackupManager.kt (sinon les exports Android ne * s'importent plus) : * - Treatment : id, name, type, route, doseAmount, doseUnit, isActive, * notes, esterType, pkModel, absorptionHours, eliminationHalfLifeHours, * bioavailabilityFraction, scaleFactor, forecastIntervalDays, * reminderHour, reminderMinute, reminderEnabled, calendarEventId, createdAt * - DoseLog : id, treatmentId, timestamp, doseAmount, notes, esterType * - LabResult : id, marker, value, unit, timestamp, notes * - TConfig : base, floor, k * - UserSettings : language, autoCalibrate, alertE2High, alertE2Low, * alertTHigh, alertTLow (champs PLATS — cf BackupManager.kt) * * `calendarEventId` est conservé (fidelité du schéma) mais TOUJOURS null * côté web : les événements d'agenda récurrents sont impossibles dans un * navigateur (pas de CalendarProvider) — fonctionnalité Android non portée. * ───────────────────────────────────────────────────────────────────────────── */ export const TreatmentType = { ESTRADIOL: 'ESTRADIOL', ANTI_ANDROGEN: 'ANTI_ANDROGEN', PROGESTOGEN: 'PROGESTOGEN', OTHER: 'OTHER', }; export const AdministrationRoute = { ORAL: 'ORAL', TRANSDERMAL_GEL: 'TRANSDERMAL_GEL', TRANSDERMAL_PATCH: 'TRANSDERMAL_PATCH', INJECTION_IM: 'INJECTION_IM', INJECTION_SUBCUT: 'INJECTION_SUBCUT', OTHER: 'OTHER', }; /** * Esters proposables dans l'override par dose pour un MODÈLE PK donné * (miroir de `Esters.choicesForModel` — la liste suit la couverture RÉELLE * du modèle : TFS = 7 esters ; WHSAH = 6 (PEP non couvert) ; Estrannaise * ANALYTIQUE (v1.9.0) = 6 esters injectables, dont EUCS exclusif à ce fit — * un ester hors liste produirait 0 pg/mL : aucun fallback silencieux). * * @param {string} pkModel * @returns {string[]} */ export function choicesForModel(pkModel) { if (pkModel === 'TFS') return ['EV', 'EU', 'EEN', 'EB', 'EC', 'ECS', 'PEP']; if (pkModel === 'WHS') return ['EV', 'EU', 'EEN', 'EB', 'EC', 'ECS']; if (pkModel === 'ESE') return ['EV', 'EU', 'EEN', 'EC', 'EB', 'EUCS']; // v1.9.4 : analytique (parité Android v1.9.0) return ['EV', 'EU', 'EEN']; } /** * Libellé humain d'un ester (miroir de `esterLabel` du DoseDialog Android). * @param {string} ester * @returns {string} */ export function esterLabel(ester) { switch (ester) { case 'EV': return 'EV — valerate'; case 'EU': return 'EU — undecylate'; case 'EEN': return 'EEn — enanthate'; case 'EB': return 'EB — benzoate'; case 'EC': return 'EC — cypionate (oil)'; case 'ECS': return 'EC — cypionate (suspension)'; case 'PEP': return 'PEP — polyestradiol phosphate'; default: return ester; } } /** * Clé i18n du label d'un modèle PK (miroir de `modelLabelRes`, fix #59 — * UNIQUE source du label : TOUTE nouvelle utilisation doit passer par ce * helper, pas par un ternaire à 2 branches qui oubliait WHSAH). * @param {string} pkModel * @returns {string} clé i18n */ export function modelLabelKey(pkModel) { if (pkModel === 'TFS') return 'model_tfs'; if (pkModel === 'WHS') return 'model_whs'; return 'model_ese'; } /** Clé i18n d'une voie d'administration. */ export function routeLabelKey(route) { switch (route) { case 'ORAL': return 'route_oral'; case 'TRANSDERMAL_GEL': return 'route_gel'; case 'TRANSDERMAL_PATCH': return 'route_patch'; case 'INJECTION_IM': return 'route_injection_im'; case 'INJECTION_SUBCUT': return 'route_injection_sc'; default: return 'route_other'; } } /** Clé i18n d'un type de traitement. */ export function typeLabelKey(type) { switch (type) { case 'ESTRADIOL': return 'type_estradiol'; case 'ANTI_ANDROGEN': return 'type_antiandrogen'; case 'PROGESTOGEN': return 'type_progestogen'; default: return 'type_other'; } } /** * Est-ce une voie injectable (IM/SC) ? * @param {string} route * @returns {boolean} */ export function isInjectionRoute(route) { return route === 'INJECTION_IM' || route === 'INJECTION_SUBCUT'; } /** * Suggère une clé i18n de route depuis un preset (utilisé par l'éditeur). * (Aucune logique : lisibilité des écrans.) */ /** * Crée un Treatment avec les valeurs par défaut de Room (`Treatment.kt`). * Les traitements sont des objets PLATS (JSON-serialisables directement). * * @param {Partial} [init={}] Champs à surcharger * @returns {object} */ export function newTreatment(init = {}) { return { id: 0, // 0 = pas encore en base (auto-increment côté store) name: '', type: TreatmentType.ESTRADIOL, route: AdministrationRoute.INJECTION_IM, doseAmount: 0, doseUnit: 'mg', isActive: true, notes: null, esterType: 'NONE', pkModel: 'ESE', // PK Bateman (gel/patch/oral) absorptionHours: 4, eliminationHalfLifeHours: 24, bioavailabilityFraction: 1.0, // Calibration : ratio lab / prédiction (le « Scale factor » du .ods) scaleFactor: 1.0, // Simulation prévisionnelle : intervalle en JOURS (null = pas de prévision) forecastIntervalDays: null, // Rappel reminderHour: null, reminderMinute: null, reminderEnabled: false, // Agenda : TOUJOURS null côté web (feature Android non portable) calendarEventId: null, createdAt: Date.now(), ...init, }; } /** * Propriétés DÉRIVÉES d'un treatment — recalculées à la demande (pas * stockées) : miroir des getters Kotlin `isInjection` / `usesProfileModel`. * * @param {object} tr * @returns {{isInjection:boolean, usesProfileModel:boolean}} */ export function treatmentFlags(tr) { const isInjection = isInjectionRoute(tr.route); return { isInjection, // Un traitement a un PROFIL PK s'il est injectable ET avec un ester // (les traitements Bateman — gel/patch/oral — n'en ont pas) usesProfileModel: isInjection && tr.esterType !== 'NONE', }; } /** * Crée un DoseLog (valeurs par défaut = `DoseLog.kt`). * @param {Partial} [init={}] * @returns {object} */ export function newDoseLog(init = {}) { return { id: 0, treatmentId: 0, timestamp: Date.now(), doseAmount: 0, notes: null, // Override par injection (null = ester du traitement) esterType: null, ...init, }; } /** * Crée un LabResult (valeurs par défaut = `LabResult.kt`). * @param {Partial} [init={}] * @returns {object} */ export function newLabResult(init = {}) { return { id: 0, marker: 'E2', value: 0, unit: 'pg/mL', timestamp: Date.now(), notes: null, ...init, }; }