import { normalizeText } from './normalize.js'; /** * CHK-5: clinician wording states a fact and its basis, never an instruction (PRD principle 1). Every draft is * checked here in tests, and each rendered text again at run time with the medicine names masked; a text with a * problem is left out, never shown. MARMAR never decides, prescribes, diagnoses or recommends treatment. */ const word = (source: string) => new RegExp(`(? = [ // Telling anyone what to do: a clause that starts with a verb of action. ['instruction', /^(?:please )?(?:stop|start|restart|resume|change|switch|swap|replace|substitute|reduce|increase|decrease|lower|raise|adjust|titrate|halve|double|give|take|use|avoid|withhold|hold|omit|skip|discontinue|continue|keep|prescribe|consider|check|recheck|monitor|measure|test|refer|order|repeat|call|phone|contact|ask|tell|inform|advise|counsel|review|reassess|confirm|arrange|admit|send|seek|go|add|remove|do|don ?t|never|ensure|make sure|be)(?![a-z0-9])/], ['second_person', word('you|your|yours|yourself')], ['advice', word('should|shouldnt|must|mustnt|ought to|need to|needs to|has to|have to|had better|it is best|it would be (?:best|better|wise)|is advised|are advised|is recommended|are recommended|please')], ['advice', word('recommend|recommends|recommended|recommending|advise|advises|advised|advising|suggest|suggests|suggested|suggesting|consider|considers|considered|considering')], ['decision', word('decide|decides|decided|deciding|decision|diagnose|diagnoses|diagnosed|diagnosing|diagnosis|prescribe|prescribes|prescribing')], ['stop_or_change', word('stop|stops|stopped|stopping|discontinu[a-z]*|withh(?:o|e)ld[a-z]*|switch(?:es|ed|ing)?|swap(?:s|ped|ping)?|substitut[a-z]*|taper[a-z]*|avoid[a-z]*|halv(?:e|es|ed|ing)|do not|dont|never')], ['dose_change', word('(?:reduc|increas|decreas|lower|rais|adjust|chang|alter|titrat|halv|doubl|cut)[a-z]*(?: [a-z]+){0,2} (?:dose|doses|dosage|dosing)')], ['dose_change', word('(?:dose|doses|dosage)(?: [a-z]+){0,2} (?:reduc|increas|decreas|lower|rais|adjust|chang|alter|titrat|halv|doubl)[a-z]*')], ['reassurance', word('safe|safely|safer|safest|safety|harmless|no problem|not a problem|nothing to worry|fine|ok|okay|alright|all right|reassur[a-z]*')], ['patient_voice', word('call 112|call 999|call 911|get help now|before the next dose|pharmacist today|doctor today')], ]; const escape = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); /** Problems with a clinician title or explanation; empty means it may be shown. Names are masked first: they are data. */ export function clinicianWordingProblems(text: string, names: readonly string[] = []): string[] { let said = ` ${normalizeText(text)} `; for (const name of [...new Set(names.map(normalizeText).filter(Boolean))].sort((a, b) => b.length - a.length)) { said = said.replace(new RegExp(`(? { let clause = ` ${normalizeText(part)} `; for (const name of names.map(normalizeText).filter(Boolean)) clause = clause.replace(new RegExp(`(?(); for (const [problem, pattern] of RULES) { if (problem === 'instruction') { if (clauses.some((clause) => pattern.test(clause))) problems.add(problem); } else if (pattern.test(said)) problems.add(problem); } return [...problems]; }