// Reading the engine's optional clinician fields (feat/engine-clinician-context). Each is // optional: with the options off, Context falls back to the API's own wording. import type { CheckRun, EngineCalculatedEgfr, EngineFindingExtras, EngineKidneyLimit, EngineResultExtras, EngineTestExtras, RuleReview, } from "../api/types"; import { formatDate } from "../flow"; import { notCheckedLines } from "./checks"; import { listNames, tierOf } from "./format"; type Result = CheckRun["assessment"]["contextual"]; type Finding = Result["findings"][number]; type Test = Result["tests_to_ask_about"][number]; type Dose = Result["dose_check"][number]; export const findingExtras = (f: Finding) => f as Finding & EngineFindingExtras; export const resultExtras = (r: Result) => r as Result & EngineResultExtras; export const testExtras = (t: Test) => t as Test & EngineTestExtras; export const doseExtras = (d: Dose) => d as Dose & { kidney_limit?: EngineKidneyLimit }; /** Clinician wording first (CHK-5), then the API's, then the engine's patient title. Wording not signed off is a draft. */ export function findingWording(f: Finding, detail: CheckRun["details"][string] | undefined) { const x = findingExtras(f); return { title: x.clinician_title ?? detail?.clinicianTitle ?? f.title, why: x.clinician_why ?? detail?.clinicianExplanation ?? f.clinical, draft: x.clinician_wording_review === "draft" || detail?.wording === "derived" || detail?.wording === "engine", }; } const reviewLabels: Record = { signed_off: "Rule signed off", draft: "Rule is a draft", changed_since_sign_off: "Rule changed since sign-off", not_in_accepted_sign_off: "Rule not yet signed off for real patients", not_yet_reviewed: "Rule not yet reviewed", unknown_rule: "Rule not recognised", }; /** The rule's review state, from the engine when it says, else from the API. Null when signed off. */ export function ruleReviewLabel(f: Finding, apiReviewed: boolean | undefined): string | null { const review = findingExtras(f).rule_review; if (review) return review === "signed_off" ? null : reviewLabels[review]; return apiReviewed === false ? reviewLabels.not_yet_reviewed : null; } const reactionWords: Record = { swelling: "read as swelling", breathing: "read as breathing trouble", blistering: "read as blistering skin", severity_severe: "recorded as severe", not_severe: "read as not severe", unknown: "not known, so treated as severe", }; /** CHK-6a: "Recorded reaction: hives (read as not severe). Without it: Review today." */ export function allergyReactionLine(f: Finding): string | null { const x = findingExtras(f); if (!x.allergy_reaction) return null; const r = x.allergy_reaction; const what = r.reaction ? r.reaction.toLowerCase() : "not recorded"; const extra = [r.onset, r.severity, r.year ? `in ${r.year}` : null].filter(Boolean).join(", "); const before = x.tier_without_reaction && x.tier_without_reaction !== f.tier ? ` Without the reaction: ${tierOf(x.tier_without_reaction).label}.` : ""; return `Recorded reaction to ${r.allergy}: ${what}${extra ? ` (${extra})` : ""}, ${reactionWords[r.reaction_class]}.${before}`; } /** FND-7: "Needed with Metformin", from the engine when it names the medicines. */ export function testMedicines(t: Test, fallback: string[]): string[] { const named = testExtras(t).medicines?.map((m) => m.name); return named?.length ? [...new Set(named)] : fallback; } /** CHK-10: an eGFR worked out from creatinine is always labelled calculated, with its inputs. */ export function calculatedEgfrLine(c: EngineCalculatedEgfr | undefined): string | null { if (!c) return null; if (c.status === "not_calculated") return c.plain; const k = c.inputs.creatinine; return `eGFR ${c.value} ${c.unit}, calculated from creatinine ${k.value} ${k.unit} (${formatDate(k.observed_at.slice(0, 10))}), age ${c.inputs.age}, ${c.inputs.sex}, with ${c.formula}.`; } /** CHK-11: the kidney-adjusted maximum behind a dose line. */ export function kidneyLimitLine(k: EngineKidneyLimit | undefined): string | null { if (!k) return null; const egfr = `eGFR ${k.egfr_comparator ?? ""}${k.egfr}${k.egfr_calculated ? " (calculated)" : ""}, ${formatDate(k.egfr_observed_at.slice(0, 10))}`; const limit = k.max_daily_mg === null ? "the label lists it as not for use in this band" : `the label's maximum in this band is ${k.max_daily_mg.toLocaleString("en")} mg a day`; const amount = k.daily_mg === null ? "" : `; this list adds up to ${k.daily_mg.toLocaleString("en")} mg a day`; return `${egfr}: band ${k.band} mL/min, ${limit}${amount}. Section ${k.section}.`; } /** CHK-3: the engine's own "not checked" lines when it gives them, else the API's; plus rules skipped as unsigned. */ export function notCheckedFor(check: CheckRun): string[] { const r = resultExtras(check.assessment.contextual); const lines = r.coverage?.lines?.length ? [...r.coverage.lines] : notCheckedLines(check.notChecked, check.versions.rulesReviewed); const skipped = r.signed_off_only?.skipped ?? []; if (skipped.length) lines.push( `Rules waiting for review, not run: ${listNames(skipped.map((s) => s.rule_id))}${skipped.some((s) => s.medicines.length) ? ` (${listNames([...new Set(skipped.flatMap((s) => s.medicines.map((m) => m.name)))])})` : ""}.`, ); if (!lines.some((l) => /check-in/i.test(l))) lines.push("Answers from check-ins: no rule reads them."); return lines; }