import type { CheckMedicine } from './engine.js'; import { normalizeText, prnUnitKind, type Strength } from './normalize.js'; /** How much of an ingredient a medicine gives (M1), moved out of engine.ts unchanged to keep it under its size. */ /** Why a dose couldn't be worked out, for the dose_check wording. */ export type DoseProblem = 'topical' | 'no_strength' | 'strength_unreadable' | 'strength_not_per_ml' | 'no_frequency' | 'liquid' | 'unit_unknown' | 'prn_unit_unknown' | 'combination_mg'; export interface Dose { /** mg in each dose, when it can be worked out. */ perDoseMg: number | null; /** mg in 24 hours, from the doses a day or the most a day. */ perDayMg: number | null; problem: DoseProblem | null; } const LIQUID_FORMS = new Set(['liquid', 'syrup', 'suspension', 'solution', 'elixir', 'drops', 'injection']); /** A name that says the medicine is a liquid ("Codeine linctus", "Paracetamol oral solution"): a saved form may be missing or "other" (R1). */ const LIQUID_NAME = /(? ({ perDoseMg: null, perDayMg: null, problem }); // A strength entered but unreadable is said as such: "Add the strength" is only for one that is missing (gap 2). const noStrength: DoseProblem = how.unreadable ? 'strength_unreadable' : 'no_strength'; let perDoseMg: number; // R1: spoons or drops of a liquid whose strength isn't per ml: the strength is what can't be used, so it is said. if (unit === 'unknown') return none(liquid && strength?.per === 'unit' ? 'strength_not_per_ml' : 'unit_unknown'); if (unit === 'mg') { if (how.combination) return none('combination_mg'); perDoseMg = units; } else if (unit === 'ml') { // M1: a liquid's strength that isn't per ml ("120 mg") can't give the mg in a dose in ml. if (strength?.per !== 'ml') return none(strength ? 'strength_not_per_ml' : noStrength); perDoseMg = strength.mg * units; } else { if (liquid) return none(strength?.per === 'unit' ? 'strength_not_per_ml' : 'liquid'); if (strength?.per !== 'unit') return none(noStrength); perDoseMg = strength.mg * units; } if (medicine.timesPerDay) return { perDoseMg, perDayMg: perDoseMg * medicine.timesPerDay, problem: null }; const most = medicine.prnMaxPerDay; if (!most) return { perDoseMg, perDayMg: null, problem: 'no_frequency' }; // "The most a day" can count doses, tablets, ml or mg (prnMaxUnit). const perUnit = strength?.per === 'unit' && !liquid ? strength.mg : null; switch (prnUnitKind(medicine.prnMaxUnit)) { case 'dose': return { perDoseMg, perDayMg: perDoseMg * most, problem: null }; case 'unit': return perUnit !== null ? { perDoseMg, perDayMg: perUnit * most, problem: null } : { perDoseMg, perDayMg: null, problem: 'prn_unit_unknown' }; case 'ml': return strength?.per === 'ml' ? { perDoseMg, perDayMg: strength.mg * most, problem: null } : { perDoseMg, perDayMg: null, problem: 'prn_unit_unknown' }; // A weight is converted: "at most 6 g a day" is 6000 mg (M-1). A combination's could be any of its medicines'. case 'mg': return how.combination ? { perDoseMg, perDayMg: null, problem: 'prn_unit_unknown' } : { perDoseMg, perDayMg: most, problem: null }; case 'g': return how.combination ? { perDoseMg, perDayMg: null, problem: 'prn_unit_unknown' } : { perDoseMg, perDayMg: most * 1000, problem: null }; case 'mcg': return how.combination ? { perDoseMg, perDayMg: null, problem: 'prn_unit_unknown' } : { perDoseMg, perDayMg: most / 1000, problem: null }; // No unit: unambiguous only when a dose is one tablet (or one of whatever it comes in). case null: return unit === 'unit' && units === 1 ? { perDoseMg, perDayMg: perDoseMg * most, problem: null } : { perDoseMg, perDayMg: null, problem: 'prn_unit_unknown' }; default: return { perDoseMg, perDayMg: null, problem: 'prn_unit_unknown' }; } } export const DOSE_PROBLEM_PLAIN: Record = { topical: 'Doses of creams, gels, drops, sprays and patches aren\'t checked.', no_strength: 'Add the strength and how often to check the dose.', strength_unreadable: 'We couldn\'t read the strength entered, so the dose wasn\'t checked.', strength_not_per_ml: 'For a liquid we need the strength per 5 ml, so the dose wasn\'t checked.', no_frequency: 'Add how often it is taken, or the most a day, to check the dose.', liquid: 'For a liquid, add the strength per ml (for example 120 mg/5 ml) and the dose in ml to check it.', unit_unknown: 'We can only check doses in tablets, ml or mg, so this one wasn\'t checked.', prn_unit_unknown: 'Say whether the most a day is in tablets or doses to check it.', combination_mg: 'It has more than one medicine in it, so add the dose in tablets or ml to check it.', }; /** Ruling A: the couldn't-check line for an adult's dose that can't be turned into mg (dose_unit_unknown), by why. A spoon is never taken as 5 ml. */ export const DOSE_NOT_IN_MG: Partial string>> = { unit_unknown: (name) => `We couldn't work out how much ${name} is in each dose, so we couldn't check it against the label. Enter the dose in ml (a medicine spoon is 5 ml), tablets or mg.`, liquid: (name) => `We couldn't work out how much ${name} is in each dose: for a liquid we need the dose in ml. Enter the dose in ml to check it against the label.`, combination_mg: (name) => `We couldn't work out the dose of ${name}: it has more than one medicine in it, so we need the dose in tablets or ml to check it against the label.`, prn_unit_unknown: (name) => `We couldn't work out the most ${name} a day: say whether it is in tablets or doses to check it against the label.`, };