mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
fix(glucose): state band edges in the reader's glucose unit
The time-in-range labels in the doctor report, the target caption on the glucose panel, the glucose page description and the fasting explainer printed fixed mg/dL figures, so a reader on mmol/L saw "Low (< 70 mg/dL)" beside values in mmol/L. The cut-points now live in one place (`GLUCOSE_TIR_CUT_POINTS`, `GLUCOSE_FASTING_CUT_POINTS`) with the SI figure each guideline prints, the analytics engine counts its bands from the same constants, and every sentence takes the band edges and unit as parameters through `glucoseBandParams`, formatted with the reader's number format. The unit-preference guard now also reads the locale bundles: a string that names mg/dL or mmol/L fails unless it is allowlisted with a reason. Refs #1067
This commit is contained in:
1 parent
fc47006c88
commit
75361d0666
19 files changed
+357
-70
No files matched your search
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* Mutation check: add `unit="kg"` back to any swept page and this goes red.
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
import { readdirSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { stripComments, walkSourceFiles } from "./helpers/source-files";
|
||||
@@ -515,3 +515,74 @@ describe("unit-preference display guard — AI and Coach producers", () => {
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Locale strings that may name a glucose unit literally, each with the reason
|
||||
* it is not a reading shown in the reader's unit. Keys are dotted paths and
|
||||
* apply to every locale bundle.
|
||||
*/
|
||||
const GLUCOSE_UNIT_LITERAL_ALLOWLIST: Record<string, string> = {
|
||||
"dashboard.metric.unit.glucose":
|
||||
"the unit token the dashboard summary wire hands the native client; the web resolves the glucose unit from the account instead",
|
||||
"labs.biomarker.form.unitPlaceholder":
|
||||
"an example of a free-text lab unit in an input placeholder, not a reading",
|
||||
};
|
||||
|
||||
const GLUCOSE_UNIT_LITERAL = /mg\/dl|mmol\/l\b/i;
|
||||
|
||||
function flattenStrings(
|
||||
node: unknown,
|
||||
path: string,
|
||||
out: Array<[string, string]>,
|
||||
): void {
|
||||
if (typeof node === "string") {
|
||||
out.push([path, node]);
|
||||
} else if (node && typeof node === "object") {
|
||||
for (const [key, value] of Object.entries(node)) {
|
||||
flattenStrings(value, path ? `${path}.${key}` : key, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("unit-preference display guard — locale strings", () => {
|
||||
const MESSAGES = join(process.cwd(), "messages");
|
||||
const bundles = readdirSync(MESSAGES).filter((f) => f.endsWith(".json"));
|
||||
|
||||
it("reads every locale bundle", () => {
|
||||
expect(bundles.length).toBeGreaterThanOrEqual(7);
|
||||
});
|
||||
|
||||
it("no locale string fixes a glucose unit outside the allowlist", () => {
|
||||
// A sentence that prints "70–180 mg/dL" shows mg/dL to a reader who
|
||||
// chose mmol/L. Band and unit go in as parameters (`glucoseBandParams`).
|
||||
const offenders: string[] = [];
|
||||
for (const file of bundles) {
|
||||
const entries: Array<[string, string]> = [];
|
||||
flattenStrings(
|
||||
JSON.parse(readFileSync(join(MESSAGES, file), "utf8")),
|
||||
"",
|
||||
entries,
|
||||
);
|
||||
for (const [key, value] of entries) {
|
||||
if (key in GLUCOSE_UNIT_LITERAL_ALLOWLIST) continue;
|
||||
if (GLUCOSE_UNIT_LITERAL.test(value)) offenders.push(`${file}: ${key}`);
|
||||
}
|
||||
}
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
|
||||
it("every allowlisted key still exists and still names a unit", () => {
|
||||
// An entry whose string was reworded or removed is a stale exemption.
|
||||
const en: Array<[string, string]> = [];
|
||||
flattenStrings(
|
||||
JSON.parse(readFileSync(join(MESSAGES, "en.json"), "utf8")),
|
||||
"",
|
||||
en,
|
||||
);
|
||||
const byKey = new Map(en);
|
||||
const stale = Object.keys(GLUCOSE_UNIT_LITERAL_ALLOWLIST).filter(
|
||||
(key) => !GLUCOSE_UNIT_LITERAL.test(byKey.get(key) ?? ""),
|
||||
);
|
||||
expect(stale).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user