mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(report): release the medication effect only with the medication list
The effect block names every medication it describes and its confounders, so a report or share link that withheld the medication list still carried the names. The aggregator now loads the block only when both leaves are admitted, the clinician view checks the list leaf as well, and the leaf label says it needs the list.
This commit is contained in:
1 parent
733b00ee1a
commit
cfe5e2d3c6
13 files changed
+95
-12
No files matched your search
+1
-1
@@ -12427,7 +12427,7 @@
|
||||
"leafSurgicalHistory": "Operationen und Eingriffe",
|
||||
"leafImmunizations": "Impfungen",
|
||||
"leafEmergency": "Notfalldaten (Blutgruppe, Kontakte, Verfügungen)",
|
||||
"leafMedicationEffect": "Veränderung rund um den Start jedes Medikaments",
|
||||
"leafMedicationEffect": "Veränderung rund um den Start jedes Medikaments (braucht die Medikamentenliste)",
|
||||
"leafWorkouts": "Trainings (Übersicht je Sportart)",
|
||||
"leafHrvSdnn": "HRV (SDNN)"
|
||||
},
|
||||
|
||||
+1
-1
@@ -12427,7 +12427,7 @@
|
||||
"leafSurgicalHistory": "Surgical history",
|
||||
"leafImmunizations": "Immunizations",
|
||||
"leafEmergency": "Emergency information (blood type, contacts, directives)",
|
||||
"leafMedicationEffect": "Change around each medication start",
|
||||
"leafMedicationEffect": "Change around each medication start (needs the medication list)",
|
||||
"leafWorkouts": "Workouts (summary per sport)",
|
||||
"leafHrvSdnn": "HRV (SDNN)"
|
||||
},
|
||||
|
||||
+1
-1
@@ -12427,7 +12427,7 @@
|
||||
"leafSurgicalHistory": "Antecedentes quirúrgicos",
|
||||
"leafImmunizations": "Vacunas",
|
||||
"leafEmergency": "Información de emergencia (grupo sanguíneo, contactos, voluntades)",
|
||||
"leafMedicationEffect": "Cambio en torno al inicio de cada medicamento",
|
||||
"leafMedicationEffect": "Cambio en torno al inicio de cada medicamento (requiere la lista de medicamentos)",
|
||||
"leafWorkouts": "Entrenamientos (resumen por deporte)",
|
||||
"leafHrvSdnn": "VFC (SDNN)"
|
||||
},
|
||||
|
||||
+1
-1
@@ -12427,7 +12427,7 @@
|
||||
"leafSurgicalHistory": "Antécédents chirurgicaux",
|
||||
"leafImmunizations": "Vaccinations",
|
||||
"leafEmergency": "Informations d'urgence (groupe sanguin, contacts, directives)",
|
||||
"leafMedicationEffect": "Changement autour du démarrage de chaque médicament",
|
||||
"leafMedicationEffect": "Changement autour du démarrage de chaque médicament (nécessite la liste des médicaments)",
|
||||
"leafWorkouts": "Entraînements (résumé par sport)",
|
||||
"leafHrvSdnn": "VFC (SDNN)"
|
||||
},
|
||||
|
||||
+1
-1
@@ -12427,7 +12427,7 @@
|
||||
"leafSurgicalHistory": "Anamnesi chirurgica",
|
||||
"leafImmunizations": "Vaccinazioni",
|
||||
"leafEmergency": "Informazioni di emergenza (gruppo sanguigno, contatti, disposizioni)",
|
||||
"leafMedicationEffect": "Variazione intorno all'inizio di ogni farmaco",
|
||||
"leafMedicationEffect": "Variazione intorno all'inizio di ogni farmaco (richiede l'elenco dei farmaci)",
|
||||
"leafWorkouts": "Allenamenti (riepilogo per sport)",
|
||||
"leafHrvSdnn": "HRV (SDNN)"
|
||||
},
|
||||
|
||||
+1
-1
@@ -12427,7 +12427,7 @@
|
||||
"leafSurgicalHistory": "수술 이력",
|
||||
"leafImmunizations": "예방접종",
|
||||
"leafEmergency": "응급 정보 (혈액형, 연락처, 사전지시)",
|
||||
"leafMedicationEffect": "각 약 시작 전후의 변화",
|
||||
"leafMedicationEffect": "각 약 시작 전후의 변화 (약 목록 필요)",
|
||||
"leafWorkouts": "운동(종목별 요약)",
|
||||
"leafHrvSdnn": "심박변이도 (SDNN)"
|
||||
},
|
||||
|
||||
+1
-1
@@ -12427,7 +12427,7 @@
|
||||
"leafSurgicalHistory": "Operatiegeschiedenis",
|
||||
"leafImmunizations": "Vaccinaties",
|
||||
"leafEmergency": "Noodinformatie (bloedgroep, contactpersonen, wilsverklaringen)",
|
||||
"leafMedicationEffect": "Verandering rond de start van elk medicijn",
|
||||
"leafMedicationEffect": "Verandering rond de start van elk medicijn (vereist de medicatielijst)",
|
||||
"leafWorkouts": "Trainingen (overzicht per sport)",
|
||||
"leafHrvSdnn": "HRV (SDNN)"
|
||||
},
|
||||
|
||||
+1
-1
@@ -12427,7 +12427,7 @@
|
||||
"leafSurgicalHistory": "Historia operacji",
|
||||
"leafImmunizations": "Szczepienia",
|
||||
"leafEmergency": "Informacje na wypadek nagły (grupa krwi, kontakty, oświadczenia)",
|
||||
"leafMedicationEffect": "Zmiana wokół rozpoczęcia każdego leku",
|
||||
"leafMedicationEffect": "Zmiana wokół rozpoczęcia każdego leku (wymaga listy leków)",
|
||||
"leafWorkouts": "Treningi (podsumowanie według dyscypliny)",
|
||||
"leafHrvSdnn": "HRV (SDNN)"
|
||||
},
|
||||
|
||||
@@ -163,6 +163,53 @@ describe("<ClinicianView>", () => {
|
||||
expect(html).not.toContain("Current smoker");
|
||||
});
|
||||
|
||||
it("shows the medication effect only beside the medication list", () => {
|
||||
const report = makeReport({
|
||||
medicationEffects: [
|
||||
{
|
||||
medicationName: "Named effect drug",
|
||||
startsOn: "2026-01-05",
|
||||
targets: [
|
||||
{
|
||||
type: "BLOOD_PRESSURE_SYS",
|
||||
effect: {
|
||||
present: false,
|
||||
reason: "insufficient_after",
|
||||
level: 0.95,
|
||||
window: null,
|
||||
days: {
|
||||
before: 40,
|
||||
after: 3,
|
||||
excludedLowAdherence: 0,
|
||||
excludedNotTaking: 0,
|
||||
},
|
||||
adherence: {
|
||||
status: "tracked",
|
||||
floorPct: 80,
|
||||
lookbackDays: 14,
|
||||
},
|
||||
levelChange: null,
|
||||
slopeChangePerWeek: null,
|
||||
preSlopePerWeek: null,
|
||||
fit: null,
|
||||
confounders: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
const leaves = ["MEDICATION_EFFECT", "BLOOD_PRESSURE_SYS"] as const;
|
||||
expect(
|
||||
render(report, {
|
||||
selection: selectionFromLeaves([...leaves, "MEDICATION_LIST"]),
|
||||
}),
|
||||
).toContain("Named effect drug");
|
||||
expect(
|
||||
render(report, { selection: selectionFromLeaves([...leaves]) }),
|
||||
).not.toContain("Named effect drug");
|
||||
});
|
||||
|
||||
it("names both windows when a lab's report and the saved band disagree", () => {
|
||||
// Two windows that disagree about the same number, and a card showing one
|
||||
// of them is a partial answer. The PDF settles this with a footnote under
|
||||
|
||||
@@ -113,7 +113,11 @@ export function MedicationEffectSection({
|
||||
/** A stated calendar day (YYYY-MM-DD) in the owner's date order. */
|
||||
fmtDay: (day: string) => string;
|
||||
}) {
|
||||
const effects = report.medicationEffects ?? [];
|
||||
// The block names medications: without the list leaf it shows nothing,
|
||||
// whatever the payload carries.
|
||||
const effects = scope.admits("MEDICATION_LIST")
|
||||
? (report.medicationEffects ?? [])
|
||||
: [];
|
||||
const glucoseUnit = resolveGlucoseUnit(report.glucoseUnit ?? null);
|
||||
return (
|
||||
<LeafSection
|
||||
|
||||
@@ -509,8 +509,9 @@ export interface DoctorReportData {
|
||||
* metric(s), around the medication's start (v1.44). Computed without AI from
|
||||
* the same day values and dose ledger the efficacy tab reads. A target rides
|
||||
* only when its own measurement leaf is admitted too, so the block never
|
||||
* carries a figure derived from a series the owner withheld. Null when the
|
||||
* MEDICATION_EFFECT leaf was not selected or nothing qualified.
|
||||
* carries a figure derived from a series the owner withheld. It names the
|
||||
* medications, so it rides only when MEDICATION_LIST is admitted as well.
|
||||
* Null when either leaf was not selected or nothing qualified.
|
||||
*/
|
||||
medicationEffects?: DoctorReportMedicationEffect[] | null;
|
||||
}
|
||||
|
||||
@@ -409,6 +409,33 @@ describe("the derived figures ride their own leaf", () => {
|
||||
expect(off.medications.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("withholds the medication effect without the medication list", async () => {
|
||||
// The effect block names every medication it describes, so withholding
|
||||
// the list must withhold the names here too.
|
||||
const leaves: ReportLeafId[] = [
|
||||
"MEDICATION_EFFECT",
|
||||
...MEASUREMENT_LEAF_IDS,
|
||||
];
|
||||
const withList = await collectDoctorReportData(
|
||||
"u1",
|
||||
RANGE,
|
||||
selectionFromLeaves([...leaves, "MEDICATION_LIST"]),
|
||||
{ moduleMap: moduleMap() },
|
||||
);
|
||||
expect(withList.medicationEffects?.length ?? 0).toBeGreaterThan(0);
|
||||
|
||||
const withoutList = await collectDoctorReportData(
|
||||
"u1",
|
||||
RANGE,
|
||||
selectionFromLeaves(leaves),
|
||||
{ moduleMap: moduleMap() },
|
||||
);
|
||||
expect(withoutList.medicationEffects ?? null).toBeNull();
|
||||
expect(JSON.stringify(withoutList)).not.toContain(
|
||||
withList.medicationEffects![0].medicationName,
|
||||
);
|
||||
});
|
||||
|
||||
it("withholds the glucose panel while the raw series stays", async () => {
|
||||
const withPanel = await collectDoctorReportData(
|
||||
"u1",
|
||||
|
||||
@@ -674,7 +674,11 @@ export async function collectDoctorReportData(
|
||||
: Promise.resolve(null),
|
||||
// The effect around each listed medication's start. Its own leaf AND the
|
||||
// target's measurement leaf must both admit it; the loader asks the gate.
|
||||
gate.admits("MEDICATION_EFFECT")
|
||||
// The block names every medication it describes (and the confounders),
|
||||
// so it also needs the medication list: an owner who withheld the list
|
||||
// never releases the names through the effect block. Same rule as the
|
||||
// clinician view's compliance rows, which are named from the list.
|
||||
gate.admits("MEDICATION_EFFECT") && gate.admits("MEDICATION_LIST")
|
||||
? loadReportMedicationEffects({
|
||||
userId,
|
||||
medicationIds: medications.map((m) => m.id),
|
||||
|
||||
Reference in new issue
Block a user