mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
refactor(ai): keep each language's outbound-screen banks in its own module
outbound-screen.ts held every locale's dose, risk, causal, continuation and change-stem patterns plus the shared units and slot builder, and grew with each language. The banks now live in outbound-screen/locales/<locale>.ts, one per language, each exporting the same LocaleBank shape; the units, target-dose shape, object slot and generic-name stems are in outbound-screen/shared.ts. The screen composes them per locale and keeps the matching logic, the continuation rule and the general rationale. No pattern changed: compiling the old and the new module and comparing every bank of every locale, with and without a medication-name list, gives the same regular expressions (108 of 108 identical), and the screen tests still block and pass the same sentences in every locale. The screen is 457 lines instead of 1,597, and buildDoseChangeClass is a four-line composition.
This commit is contained in:
1 parent
8c11bb8385
commit
dcb2598bb2
11 files changed
+1486
-1179
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,178 @@
|
||||
/** Outbound-screen banks for German, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
objectSlot,
|
||||
TARGET,
|
||||
LETTER,
|
||||
ENGINE,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const DE_STOP =
|
||||
"nicht|nie|niemals|kein\\w*|und|oder|aber|wenn|ob|natrium|salz|koffein|zucker|wasser|eiweiß|protein|ballaststoff\\w*|kalorien";
|
||||
|
||||
const DE_SLOT = objectSlot(DE_STOP);
|
||||
|
||||
const DE_SHORT = objectSlot(
|
||||
`${DE_STOP}|als|zu|mit|für|gegen|statt|ohne|über`,
|
||||
2,
|
||||
);
|
||||
|
||||
const DE_MED_NOUN =
|
||||
"\\S*(?:dosis|tablette\\w*|medikament\\w*|medikation|spritze\\w*|insulin|pille\\w*|kapsel\\w*)";
|
||||
|
||||
/**
|
||||
* The object a German change verb may carry before its target ("erhöhe
|
||||
* deine Dosis auf 2,4 mg"). English allows "your dose" in the same slot;
|
||||
* without it the German imperative with an object slipped through.
|
||||
*/
|
||||
const DE_DOSE_OBJECT =
|
||||
"(?:\\s+(?:(?:deine|die|ihre|eure|seine)\\s+)?(?:dosis|dosierung|medikation|wochendosis|tagesdosis))?";
|
||||
|
||||
/** A German count in front of a medication noun ("eine Tablette mehr"). */
|
||||
const DE_COUNT = "ein(?:e|en)?|zwei|drei|vier|halbe|\\d+(?:[.,]\\d+)?";
|
||||
|
||||
const SPELLED_DE =
|
||||
"(?:null|eins?|zwei|drei|vier|fünf|sechs|sieben|acht|neun|zehn|elf|zwölf|dreizehn|vierzehn|fünfzehn|sechzehn|siebzehn|achtzehn|neunzehn|zwanzig|dreißig|vierzig|fünfzig|sechzig|siebzig|achtzig|neunzig)";
|
||||
|
||||
const PCT_WORD_DE = `${SPELLED_DE}\\s+prozent`;
|
||||
|
||||
const QUAL_NUM_DE = `(?:\\d{1,3}\\s*%|${PCT_WORD_DE})`;
|
||||
|
||||
const RISK_NOUN_DE = "(?:risiko|wahrscheinlichkeit|chance)";
|
||||
|
||||
const HORIZON_DE = "(?:10[- ]jahres|zehn[- ]jahres|lebenszeit)[- ]?risiko";
|
||||
|
||||
const RESULT_VERB_DE =
|
||||
"(?:ordnet\\s+(?:dich|sie)\\s+ein|stuft\\s+(?:dich|sie)\\s+ein|f[äa]llst\\s+in|einordnen|liegst\\s+im)";
|
||||
|
||||
const RISK_BAND_DE =
|
||||
"(?:hoh|niedrig|mittler|erhöht|moderat|gering)\\w*[- ]?risiko(?:bereich|kategorie|gruppe|band)";
|
||||
|
||||
const RISK_LEVEL_DE =
|
||||
"(?:erhöht|hoch|höher|mittel|mäßig|grenzwertig|besorgniserregend|deutlich)";
|
||||
|
||||
const RISK_VERDICT_DE = `(?:ist|liegt|erscheint|wirkt|bleibt|zeigt|deutet\\s+auf|weist\\s+auf)\\s+(?:\\w+\\s+){0,2}${RISK_LEVEL_DE}`;
|
||||
|
||||
const DE_MED_CLASS =
|
||||
"\\S*(?:blutverdünner\\w*|gerinnungshemmer\\w*|antikoagul\\w*|statin\\w*|betablocker\\w*|diuretik\\w*|wassertablette\\w*|blutdrucksenker\\w*|antidepressiv\\w*|schlaftablette\\w*)";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`(?:${DE_MED_NOUN}|${medicationExtras(DE_MED_CLASS, names)})`,
|
||||
doseChangeClass: (med) => [
|
||||
new RegExp(
|
||||
`\\b(?:erhöh(?:e|en)?|steiger(?:e|n)?|reduzier(?:e|en)?|senk(?:e|en)?|verringer(?:e|n)?|halbier(?:e|en)?|verdopp(?:e?le|eln)|stell(?:e|en)?|geh(?:e)?|setz(?:e|en)?|bring(?:e|en)?)(?:\\s+sie)?\\s+${DE_SLOT}(?:(?:hoch|rauf|runter|herunter)\\s+)?(?:auf|um)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:nimm|nehmen\\s+sie|spritz|spritzen\\s+sie|injizier(?:e)?|injizieren\\s+sie|beginn(?:e)?\\s+mit|beginnen\\s+sie\\s+mit|starte\\s+mit)\\s+${DE_SLOT}${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<![${LETTER}])(?:halbier(?:e|en)?|verdopp(?:e?le|eln)|pausier(?:e|en)?|überspring(?:e|en)?|stopp(?:e|en)?)(?:\\s+sie)?\\s+${DE_SLOT}${med}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:setz(?:e|en)?)(?:\\s+sie)?(?:\\s+(?!(?:nicht|nie|niemals|kein\\w*)(?=\\s))[^\\s.?!,;:]+){1,4}?\\s+ab(?=\\s*(?:[.?!,;:]|$|und\\s|oder\\s))`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:lass|lassen\\s+sie)\\s+${DE_SLOT}${med}(?:(?!\\b(?:nicht|nie|niemals)\\b)[^.?!]){0,20}?\\b(?:aus|weg)\\b`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\bh(?:ör(?:e)?|ören\\s+sie)\\s+(?:mit\\s+\\S+\\s+)?auf\\b[^.?!]{0,40}?\\b(?:zu\\s+nehmen|einzunehmen|zu\\s+spritzen)`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:solltest|sollten\\s+sie|empfehle|rate\\s+(?:dir|ihnen)|würde\\s+ich)\\b(?:(?!\\b(?:nicht|nie|niemals|kein\\w*)\\b)[^.?!]){0,40}?\\b(?:absetzen|abzusetzen|halbieren|zu\\s+halbieren|verdoppeln|zu\\s+verdoppeln|auslassen|auszulassen|weglassen|wegzulassen|pausieren|zu\\s+pausieren)\\b`,
|
||||
"i",
|
||||
),
|
||||
// "eine Tablette mehr", "noch eine Tablette", "eine zusätzliche
|
||||
// Tablette", "mehr Insulin". A negation ("keine") cannot enter the slot.
|
||||
new RegExp(
|
||||
`\\b(?:nimm|nehmen\\s+sie|spritz|spritzen\\s+sie|injizier(?:e)?|injizieren\\s+sie)\\s+${DE_SHORT}(?:(?:noch\\s+)?(?:${DE_COUNT})\\s+${DE_SHORT}${med}\\s+mehr\\b|noch\\s+(?:${DE_COUNT})\\s+${DE_SHORT}${med}|(?:(?:${DE_COUNT})\\s+)?(?:zusätzlich\\w*|extra|weitere\\w*|mehr)[-\\s]+${DE_SHORT}${med})`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\bh(?:ör(?:e)?|ören\\s+sie)\\s+mit\\s+${DE_SHORT}${med}\\s+auf\\b`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\bverzichte(?:n\\s+sie)?\\s+${DE_SHORT}auf\\s+${DE_SHORT}${med}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
dose: [
|
||||
new RegExp(
|
||||
`\\b(?:erhöh\\w*|steiger\\w*|setz\\w*\\s+(?:hoch|rauf)|geh\\w*\\s+(?:hoch|rauf))${DE_DOSE_OBJECT}\\s+(?:auf|um)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:reduzier\\w*|senk\\w*|verringer\\w*|nimm\\s+(?:weniger|runter))${DE_DOSE_OBJECT}\\s+(?:auf|um)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\bn[äa]chste\\s+(?:stufe|dosis)\\b[^.?!]{0,30}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// German puts the verb last in a subordinate clause ("auf 7,5 mg zu
|
||||
// erhöhen"), so the verb-first patterns above miss the most natural
|
||||
// phrasing of the instruction. Match the inverted order too.
|
||||
new RegExp(
|
||||
`\\b(?:auf|um)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}[^.?!]{0,30}\\b(?:erhöh\\S*|steiger\\S*|reduzier\\S*|senk\\S*|verringer\\S*|hochsetz\\S*)`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:erwäg\\w*|probier\\w*|du\\s+solltest|ich\\s+empfehle|ich\\s+schlage\\s+vor)\\b[^.?!]{0,40}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
/\b(?:halbier\w*|verdoppel\w*|lass\w*\s+aus|setz\w*\s+ab|pausier\w*)\b[^.?!]{0,25}\b(?:dosis|tablette\w*|medikament\w*|spritze\w*|insulin)\b[^.?!]{0,40}\b(?:für\s+\w+\s+(?:tag|tage|woche|wochen|monat\w*)|um\s+zu\s+(?:sehen|testen)|zum\s+(?:test|ausprobieren))\b/i,
|
||||
],
|
||||
risk: [
|
||||
new RegExp(
|
||||
`\\brisiko\\s+(?:von|bei|liegt\\s+bei)\\s+(?:etwa\\s+|ungefähr\\s+|~)?\\d{1,3}\\s*%`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(`\\b\\d{1,3}\\s*%\\s+${RISK_NOUN_DE}\\b`, "i"),
|
||||
// spelled-out percent as a risk figure, either order
|
||||
new RegExp(`\\b${RISK_NOUN_DE}\\b[^.?!]{0,40}\\b${PCT_WORD_DE}\\b`, "i"),
|
||||
new RegExp(`\\b${PCT_WORD_DE}\\b[^.?!]{0,40}\\b${RISK_NOUN_DE}\\b`, "i"),
|
||||
// 10-year horizon + a qualifying number, either order
|
||||
new RegExp(`\\b${HORIZON_DE}\\b[^.?!]{0,40}${QUAL_NUM_DE}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_DE}[^.?!]{0,40}\\b${HORIZON_DE}\\b`, "i"),
|
||||
// named engine (same literals) + qualifying number or categorical result
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_DE}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_DE}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_DE})\\b[^.?!]{0,60}${RESULT_VERB_DE}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_DE})\\b[^.?!]{0,60}${RISK_BAND_DE}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_DE})\\b[^.?!]{0,40}${RISK_VERDICT_DE}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
causal: [
|
||||
/\bweil\b/i,
|
||||
/\bwegen\b/i,
|
||||
/\bverursach\w*/i,
|
||||
/\baufgrund\b/i,
|
||||
/\bführt[e]?\s+zu\b/i,
|
||||
/\bdurch\s+\w+\s+(?:verursacht|ausgelöst)\b/i,
|
||||
/\bschuld\b/i,
|
||||
/\bauslöser\b/i,
|
||||
/\bverantwortlich\s+für\b/i,
|
||||
],
|
||||
continuation:
|
||||
/\b(?:weiterhin|weiter\s+(?:einnehmen|nehmen)|nimm\s+weiter|beibehalten|behalte\s+bei|wie\s+(?:verordnet|verschrieben|besprochen|gewohnt|bisher)|bleib(?:e|st)?\s+bei)\b/i,
|
||||
changeStem:
|
||||
/\b(?:erhöh|steiger|reduzier|senk|verringer|halbier|verdoppel|absetz|auslass|hochsetz|runtersetz|titrier)\w*/i,
|
||||
};
|
||||
@@ -0,0 +1,189 @@
|
||||
/** Outbound-screen banks for English, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
objectSlot,
|
||||
TARGET,
|
||||
ENGINE,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const EN_STOP =
|
||||
"not|never|and|or|but|if|whether|when|sodium|salt|caffeine|sugars?|fib(?:er|re)|protein|carbs?|carbohydrates?|cholesterol|water|fluids?|coffee|alcohol|steps";
|
||||
|
||||
const EN_SLOT = objectSlot(EN_STOP);
|
||||
|
||||
const EN_SHORT = objectSlot(
|
||||
`${EN_STOP}|to|for|with|at|in|on|of|from|before|after|about|into|than|while|during|like`,
|
||||
2,
|
||||
);
|
||||
|
||||
/**
|
||||
* Where an English imperative can start: the sentence head (or a list bullet),
|
||||
* after a colon / dash, or after a recommending lead-in. A negated imperative
|
||||
* ("don't skip your dose") never reaches the verb from any of these.
|
||||
*/
|
||||
const EN_LEAD =
|
||||
"(?:^\\s*(?:[-*•]\\s*|\\d+[.)]\\s*)?|[:;—–]\\s*|(?<!\\b(?:whether|if)\\s)\\b(?:please|just|then|now|instead|so|you\\s+(?:should|need\\s+to|must|ought\\s+to)|try\\s+to|go\\s+ahead\\s+and|i'?d|i\\s+would|time\\s+to)\\s+)";
|
||||
|
||||
/** A recommending cue that may sit in front of a gerund / infinitive change. */
|
||||
const EN_CUE =
|
||||
"\\b(?:consider|try|recommend|suggest|worth|might\\s+want\\s+to|may\\s+want\\s+to|need\\s+to|time\\s+to|i'?d|i\\s+would|should|could)\\b";
|
||||
|
||||
const EN_MED_NOUN =
|
||||
"(?:dose|doses|dosage|pill|pills|tablet|tablets|capsule|capsules|medication|medications|medicine|meds|insulin|injection|injections|shot|jab|inhaler)\\b";
|
||||
|
||||
// Spelled-out cardinal numbers (0–99) so a digit-less "twelve percent" still
|
||||
// counts as a fabricated figure, not an educational aside.
|
||||
const SPELLED_EN =
|
||||
"(?:zero|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety)(?:[- ](?:one|two|three|four|five|six|seven|eight|nine))?";
|
||||
|
||||
const PCT_WORD_EN = `${SPELLED_EN}\\s+(?:percent|per\\s+cent)`;
|
||||
|
||||
const QUAL_NUM_EN = `(?:\\d{1,3}\\s*%|${PCT_WORD_EN}|score\\s+of\\s+\\d)`;
|
||||
|
||||
const RISK_NOUN_EN = "(?:risk|chance|probability|likelihood)";
|
||||
|
||||
const HORIZON_EN =
|
||||
"(?:10[- ]year|ten[- ]year|lifetime)\\s+(?:cardiovascular|cardiac|heart|stroke|mortality|cvd|ascvd)\\s+risk";
|
||||
|
||||
const RESULT_VERB_EN =
|
||||
"(?:puts?\\s+you|would\\s+put\\s+you|placing\\s+you|places?\\s+you|you\\s+fall|you'?d\\s+fall|classif\\w*\\s+you)";
|
||||
|
||||
const RISK_BAND_EN =
|
||||
"(?:high|higher|intermediate|elevated|moderate|low|borderline)[- ]?risk\\s+(?:band|category|group|range)";
|
||||
|
||||
// A categorical VERDICT on the engine / horizon itself ("your 10-year risk IS
|
||||
// elevated") — a fabricated result with no digits. The horizon phrase and a
|
||||
// named engine are inherently about-this-user, so an attached risk-level
|
||||
// adjective is an assertion, not education. A model-perfect refusal names the
|
||||
// horizon but attaches no such adjective, so it still passes.
|
||||
const RISK_LEVEL_EN =
|
||||
"(?:elevated|high|higher|intermediate|moderate|borderline|raised|concerning|significant)";
|
||||
|
||||
const RISK_VERDICT_EN = `(?:is|are|looks?|appears?|seems?|sits?|remains?|comes?\\s+back|runs?|suggests?|indicat\\w*|shows?|reflects?|points?\\s+to)\\s+(?:\\w+\\s+){0,2}${RISK_LEVEL_EN}`;
|
||||
|
||||
const EN_MED_CLASS =
|
||||
"(?:blood[- ]?thinners?|anti-?coagulants?|anti-?platelets?|statins?|beta[- ]?blockers?|diuretics?|water\\s+pills?|ace\\s+inhibitors?|steroids?|antidepressants?|sleeping\\s+pills?)\\b";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`(?:${EN_MED_NOUN}|${medicationExtras(EN_MED_CLASS, names)})`,
|
||||
doseChangeClass: (med) => [
|
||||
new RegExp(
|
||||
`\\b(?:increase|raise|up|bump|boost|titrate|step|move|go|ramp|push|switch|change|adjust|lower|reduce|cut|drop|decrease|taper|halve|double|trim|bring)\\s+${EN_SLOT}(?:from\\s+${TARGET}\\s+)?(?:up\\s+|down\\s+|back\\s+)?(?:to|by)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`${EN_CUE}[^.?!]{0,20}?\\b(?:increas(?:e|ing)|rais(?:e|ing)|bump(?:ing)?|boost(?:ing)?|titrat(?:e|ing)|lower(?:ing)?|reduc(?:e|ing)|cut(?:ting)?|drop(?:ping)?|decreas(?:e|ing)|taper(?:ing)?|halv(?:e|ing)|doubl(?:e|ing)|switch(?:ing)?|adjust(?:ing)?|go(?:ing)?\\s+up|step(?:ping)?\\s+up)\\s+${EN_SLOT}(?:from\\s+${TARGET}\\s+)?(?:up\\s+|down\\s+)?(?:to|by)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`${EN_LEAD}(?:take|start\\s+(?:taking|on|with|at)|begin\\s+(?:taking|with|at)|inject|use|give\\s+yourself)\\s+${EN_SLOT}${TARGET}`,
|
||||
"im",
|
||||
),
|
||||
new RegExp(
|
||||
`${EN_LEAD}(?:halve|double|skip|pause|hold|drop|miss|split|cut)\\s+${EN_SLOT}${med}`,
|
||||
"im",
|
||||
),
|
||||
new RegExp(
|
||||
`${EN_LEAD}(?:(?:(?:stop|quit)\\s+(?:taking|injecting)|discontinue)\\s+\\S|(?:come|wean\\s+(?:yourself\\s+)?)\\s*off\\s+${EN_SLOT}${med})`,
|
||||
"im",
|
||||
),
|
||||
new RegExp(
|
||||
`${EN_CUE}[^.?!]{0,20}?\\b(?:(?:halv|doubl|skipp|paus|hold|dropp|miss|splitt|cutt)ing\\s+${EN_SLOT}${med}|(?:stopping|quitting)\\s+(?:taking|injecting)\\s+\\S|discontinuing\\s+\\S|coming\\s+off\\s+${EN_SLOT}${med})`,
|
||||
"i",
|
||||
),
|
||||
// An extra, another or one more of a medication: "take an extra
|
||||
// ramipril", "take one more tablet of ramipril". The short slot ends at a
|
||||
// preposition, so "take an extra minute to log your medication" passes.
|
||||
new RegExp(
|
||||
`${EN_LEAD}(?:take|use|inject|add|give\\s+yourself|pop)\\s+(?:another|(?:(?:an?|one|two|three|\\d+)\\s+)?(?:extra|additional|more|further|second))\\s+${EN_SHORT}${med}`,
|
||||
"im",
|
||||
),
|
||||
// Stop, leave out or go without a named medication or a drug class:
|
||||
// "stop your blood thinner", "leave out the Eliquis".
|
||||
new RegExp(
|
||||
`${EN_LEAD}(?:stop|quit|discontinue|omit|leave\\s+out|hold\\s+off\\s+on|go\\s+without)\\s+${EN_SHORT}${med}`,
|
||||
"im",
|
||||
),
|
||||
new RegExp(`${EN_LEAD}leave\\s+${EN_SHORT}${med}\\s+out\\b`, "im"),
|
||||
// The same behind a recommending cue. A referral ("before", "ask",
|
||||
// "whether") or a negation between the cue and the verb ends it.
|
||||
new RegExp(
|
||||
`${EN_CUE}(?:(?!\\b(?:not|never|before|without|until|unless|ask|asking|whether|if)\\b)[^.?!]){0,20}?\\b(?:(?:stopping|omitting|leaving\\s+out)\\s+${EN_SHORT}${med}|taking\\s+(?:another|(?:(?:an?|one|two|\\d+)\\s+)?(?:extra|additional|more))\\s+${EN_SHORT}${med})`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
dose: [
|
||||
// step/move/increase/raise/bump/titrate/go up to|by N unit
|
||||
new RegExp(
|
||||
`\\b(?:step|move|increase|raise|bump|titrat\\w*|go|ramp|push|up)\\s+(?:it\\s+)?(?:up\\s+|your\\s+dose\\s+)?(?:to|by)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// lower/reduce/cut/drop/decrease/back off/taper(ing) to|by N unit
|
||||
new RegExp(
|
||||
`\\b(?:lower|reduce|cut|drop|decrease|back\\s+off|taper\\w*)\\s+(?:it\\s+|your\\s+dose\\s+)?(?:to|by)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// consider/try/should/recommend ... N unit
|
||||
new RegExp(
|
||||
`\\b(?:consider|try|you\\s+should|i'?d?\\s+recommend|i\\s+suggest)\\b[^.?!]{0,40}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// Experiment-shaped dose changes the "to/by N unit" patterns miss. BOTH a
|
||||
// medication object AND a trial cue are required, so a benign behavioural
|
||||
// experiment ("double your steps for two weeks") never trips.
|
||||
/\b(?:halv\w*|doubl\w*|skip\w*|stop\s+taking|quit\s+taking|come\s+off)\b[^.?!]{0,25}\b(?:dose|doses|pill|pills|tablet|tablets|medication|meds?|insulin|injection)\b[^.?!]{0,40}\b(?:for\s+\w+\s+(?:day|days|week|weeks|month|months)|to\s+(?:see|test|try|check)|next\s+(?:week|month))\b/i,
|
||||
],
|
||||
risk: [
|
||||
// (1) digit percent adjacent to a risk noun, either order
|
||||
new RegExp(`\\b\\d{1,3}\\s*%\\s+${RISK_NOUN_EN}\\b`, "i"),
|
||||
new RegExp(
|
||||
`\\b${RISK_NOUN_EN}\\s+(?:of|is|at|around|about|near|sits?\\s+at|would\\s+be)\\s+(?:about\\s+|roughly\\s+|approximately\\s+|around\\s+|~)?\\d{1,3}\\s*%`,
|
||||
"i",
|
||||
),
|
||||
// (2) spelled-out percent as a risk figure, either order (D4b)
|
||||
new RegExp(`\\b${RISK_NOUN_EN}\\b[^.?!]{0,40}\\b${PCT_WORD_EN}\\b`, "i"),
|
||||
new RegExp(`\\b${PCT_WORD_EN}\\b[^.?!]{0,40}\\b${RISK_NOUN_EN}\\b`, "i"),
|
||||
// (3) 10-year horizon phrase + a qualifying number, either order (M4)
|
||||
new RegExp(`\\b${HORIZON_EN}\\b[^.?!]{0,40}${QUAL_NUM_EN}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_EN}[^.?!]{0,40}\\b${HORIZON_EN}\\b`, "i"),
|
||||
// (4) named engine + a qualifying number, either order
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_EN}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_EN}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
// (5) engine / horizon + a categorical RESULT assertion, numberless (D4)
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_EN})\\b[^.?!]{0,60}${RESULT_VERB_EN}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_EN})\\b[^.?!]{0,60}${RISK_BAND_EN}`,
|
||||
"i",
|
||||
),
|
||||
// (6) engine / horizon + a categorical risk-level verdict, numberless
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_EN})\\b[^.?!]{0,40}${RISK_VERDICT_EN}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
causal: [
|
||||
/\bbecause\b/i,
|
||||
/\bcaused?\s+by\b/i,
|
||||
/\bcaus(?:e|es|ed|ing)\b/i,
|
||||
/\bdue\s+to\b/i,
|
||||
/\bled\s+to\b/i,
|
||||
/\bresulted\s+in\b/i,
|
||||
/\bresult\s+of\b/i,
|
||||
/\bculprit\b/i,
|
||||
/\bdriven\s+by\b/i,
|
||||
/\bthanks\s+to\b/i,
|
||||
/\bowing\s+to\b/i,
|
||||
/\bresponsible\s+for\b/i,
|
||||
],
|
||||
continuation:
|
||||
/\b(?:keep\s+taking|keep\s+on\s+taking|continue\s+(?:taking|with|on)|stay(?:ing)?\s+(?:on|at)|remain(?:ing)?\s+on|as\s+prescribed|as\s+directed|as\s+usual|as\s+before|as\s+you\s+have\s+been)\b/i,
|
||||
changeStem:
|
||||
/\b(?:increas|reduc|taper|titrat|step\s+(?:up|down)|lower|raise|halv|doubl|skip|bump|ramp)\w*/i,
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
/** Outbound-screen banks for Spanish, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
objectSlot,
|
||||
TARGET,
|
||||
LETTER,
|
||||
ENGINE,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const ES_STOP =
|
||||
"no|nunca|y|o|pero|si|sodio|sal|cafeína|azúcar|agua|proteínas?|fibra";
|
||||
|
||||
const ES_SLOT = objectSlot(ES_STOP);
|
||||
|
||||
const ES_SHORT = objectSlot(`${ES_STOP}|para|con|antes|después|sin|a|al`, 2);
|
||||
|
||||
const ES_MED_NOUN =
|
||||
"(?:dosis|pastillas?|comprimidos?|medicamentos?|medicaci[óo]n|insulina|inyecci[óo]n|inyecciones|c[áa]psulas?|tratamiento)";
|
||||
|
||||
const SPELLED_ES =
|
||||
"(?:cero|uno|dos|tres|cuatro|cinco|seis|siete|ocho|nueve|diez|once|doce|trece|catorce|quince|dieciséis|diecisiete|dieciocho|diecinueve|veinte|treinta|cuarenta|cincuenta|sesenta)";
|
||||
|
||||
const PCT_WORD_ES = `${SPELLED_ES}\\s+por\\s+ciento`;
|
||||
|
||||
const QUAL_NUM_ES = `(?:\\d{1,3}\\s*%|${PCT_WORD_ES})`;
|
||||
|
||||
const RISK_NOUN_ES = "(?:riesgo|probabilidad)";
|
||||
|
||||
const RESULT_VERB_ES =
|
||||
"(?:lo\\s+(?:coloca|sitúa|clasifica|pone)|le\\s+(?:coloca|sitúa|clasifica)|se\\s+encuentra\\s+en|está\\s+en\\s+(?:la\\s+)?categor|cae\\s+en)";
|
||||
|
||||
const ES_MED_CLASS =
|
||||
"(?:anticoagulantes?|antiagregantes?|estatinas?|betabloqueantes?|diuréticos?|antidepresivos?)";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`(?:${ES_MED_NOUN}|${medicationExtras(ES_MED_CLASS, names)})`,
|
||||
doseChangeClass: (med) => [
|
||||
new RegExp(
|
||||
`(?<!\\b(?:no|nunca)\\s)\\b(?:aumente|aumenta|aumentar|suba|sube|subir|pase|pasar|incremente|incrementar|reduzca|reduce|reducir|baje|bajar|disminuya|disminuir|duplique|duplicar|lleve|llevar|ajuste|ajustar)\\s+${ES_SLOT}(?:de\\s+${TARGET}\\s+)?(?:a|en|hasta)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:no|nunca)\\s)\\b(?:tome|tómese|inyecte|inyéctese|administre|empiece\\s+con|comience\\s+con)\\s+${ES_SLOT}${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:no|nunca)\\s)\\b(?:omita|sáltese|salte|suspenda|interrumpa|duplique|divida|parta)\\s+${ES_SLOT}${med}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:no|nunca)\\s)\\b(?:deje|deja|dejar)\\s+de\\s+(?:tomar|usar|inyectarse)\\b`,
|
||||
"i",
|
||||
),
|
||||
// "una pastilla de más", "otra pastilla", "una dosis extra".
|
||||
new RegExp(
|
||||
`(?<!\\b(?:no|nunca)\\s)\\b(?:tome|tómese|inyecte|inyéctese|añada|agregue)\\s+${ES_SHORT}(?:(?:un|una|dos|tres|\\d+)\\s+${ES_SHORT}${med}[^.?!]{0,20}?(?:\\bm[áa]s(?![${LETTER}])|\\bextra\\b|\\badicional(?:es)?\\b)|otr[oa]s?\\s+${ES_SHORT}${med})`,
|
||||
"i",
|
||||
),
|
||||
// Stop a named medication or a drug class: "deje el anticoagulante".
|
||||
new RegExp(
|
||||
`(?<!\\b(?:no|nunca)\\s)\\b(?:deje|pare|abandone)\\s+${ES_SHORT}${med}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
dose: [
|
||||
// aumente / suba / pase a|en N unit
|
||||
new RegExp(
|
||||
`\\b(?:aument\\w*|sub\\w*|pas\\w*|increment\\w*)\\s+(?:su\\s+dosis\\s+)?(?:a|en|hasta)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// reduzca / baje / disminuya a|en N unit
|
||||
new RegExp(
|
||||
`\\b(?:reduzc\\w*|reduc\\w*|baj\\w*|disminu\\w*)\\s+(?:su\\s+dosis\\s+)?(?:a|en|hasta)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:considere|pruebe|deber[íi]a|recomiendo|sugiero)\\b[^.?!]{0,40}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:pr[óo]xima|siguiente|nueva)\\s+dosis\\b[^.?!]{0,30}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
risk: [
|
||||
/\briesgo\s+(?:del?|es\s+del?|de\s+aproximadamente)\s+(?:aproximadamente\s+|~)?\d{1,3}\s*%/i,
|
||||
/\b\d{1,3}\s*%\s+(?:de\s+)?(?:riesgo|probabilidad)/i,
|
||||
/\briesgo\s+(?:cardiovascular|card[íi]aco|de\s+ictus|de\s+mortalidad)\s+a\s+(?:10|diez)\s+a[ñn]os\b/i,
|
||||
new RegExp(`\\b${RISK_NOUN_ES}\\b[^.?!]{0,40}\\d{1,3}\\s*%`, "i"),
|
||||
new RegExp(`\\d{1,3}\\s*%[^.?!]{0,40}\\b${RISK_NOUN_ES}\\b`, "i"),
|
||||
new RegExp(`\\b${RISK_NOUN_ES}\\b[^.?!]{0,40}\\b${PCT_WORD_ES}\\b`, "i"),
|
||||
new RegExp(`\\b${PCT_WORD_ES}\\b[^.?!]{0,40}\\b${RISK_NOUN_ES}\\b`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_ES}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_ES}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,60}${RESULT_VERB_ES}`, "i"),
|
||||
],
|
||||
causal: [
|
||||
/\bporque\b/i,
|
||||
/\bdebido\s+a\b/i,
|
||||
/\ba\s+causa\s+de\b/i,
|
||||
/\bcaus(?:a|an|ó|ado)/i,
|
||||
/\bprovoc\w*/i,
|
||||
/\bllev[óo]\s+a\b/i,
|
||||
/\bresponsable\s+de\b/i,
|
||||
/\bgracias\s+a\b/i,
|
||||
/\bimpuls\w*\s+(?:por|el|la)\b/i,
|
||||
],
|
||||
continuation:
|
||||
/\b(?:siga\s+(?:tomando|con)|continúe|mantenga|según\s+lo\s+prescrito)\b/i,
|
||||
changeStem: /\b(?:aument|reduzc|reduc|baj|disminu|dobl|omit|salt)\w*/i,
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
/** Outbound-screen banks for French, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
objectSlot,
|
||||
TARGET,
|
||||
ENGINE,
|
||||
ELIDED,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const FR_STOP =
|
||||
"pas|jamais|ne|et|ou|mais|si|sodium|sel|caféine|sucres?|eau|protéines?|fibres?";
|
||||
|
||||
const FR_SLOT = objectSlot(FR_STOP);
|
||||
|
||||
const FR_SHORT = objectSlot(
|
||||
`${FR_STOP}|pour|avec|avant|après|sans|à|au|aux`,
|
||||
2,
|
||||
);
|
||||
|
||||
const FR_MED_NOUN =
|
||||
"(?:doses?|comprimés?|médicaments?|traitement|insuline|injections?|piqûres?|pilules?|gélules?|cachets?)";
|
||||
|
||||
const SPELLED_FR =
|
||||
"(?:zéro|un|deux|trois|quatre|cinq|six|sept|huit|neuf|dix|onze|douze|treize|quatorze|quinze|seize|dix-sept|dix-huit|dix-neuf|vingt|trente|quarante|cinquante|soixante)";
|
||||
|
||||
const PCT_WORD_FR = `${SPELLED_FR}\\s+(?:pour\\s+cent|pour-cent)`;
|
||||
|
||||
const QUAL_NUM_FR = `(?:\\d{1,3}\\s*%|${PCT_WORD_FR})`;
|
||||
|
||||
const RISK_NOUN_FR = "(?:risque|probabilité|chance)";
|
||||
|
||||
const RESULT_VERB_FR =
|
||||
"(?:vous\\s+(?:met|place|situe|classe|met\\s+dans)|vous\\s+êtes\\s+(?:dans|classé)|vous\\s+tombez\\s+dans)";
|
||||
|
||||
const FR_MED_CLASS =
|
||||
"(?:anticoagulants?|antiagrégants?|statines?|bêta-?bloquants?|diurétiques?|antidépresseurs?|somnifères?)";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`${ELIDED}(?:${FR_MED_NOUN}|${medicationExtras(FR_MED_CLASS, names)})`,
|
||||
doseChangeClass: (med) => [
|
||||
new RegExp(
|
||||
`(?<!\\b(?:vous|nous|je|tu|il|elle|on)\\s)(?<!n['’])\\b(?:(?:augment|mont|pass|port|major|diminu|baiss|abaiss|doubl|ramen)(?:ez|e|er)|r[ée]dui(?:sez|s|re))\\s+${FR_SLOT}(?:de\\s+${TARGET}\\s+)?(?:[àa]|de|jusqu'[àa])\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:vous|nous|je|tu|il|elle|on)\\s)\\b(?:prenez|prends|injectez|injecte|commencez\\s+(?:par|à|avec))\\s+${FR_SLOT}${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:vous|nous|je|tu|il|elle|on)\\s)(?<!n['’])\\b(?:divisez|doublez|sautez|suspendez|interrompez|oubliez|arrêtez|cessez|stoppez|supprimez)\\s+${FR_SLOT}${med}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!n['’])\\b(?:arrêtez|cessez|stoppez)\\s+de\\s+(?:prendre|vous\\s+injecter)\\b`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:vous\\s+devriez|je\\s+recommande\\s+de|je\\s+sugg[èe]re\\s+de|envisagez\\s+de|essayez\\s+de)\\s+(?:sauter|suspendre|arrêter|interrompre|diviser|doubler)\\s+${FR_SLOT}(?:de\\s+prendre\\s+\\S|${med})`,
|
||||
"i",
|
||||
),
|
||||
// "un comprimé en plus", "un ramipril supplémentaire", "un autre
|
||||
// comprimé", "encore une dose".
|
||||
new RegExp(
|
||||
`(?<!\\bne\\s)(?<!\\b(?:vous|nous|je|tu|il|elle|on)\\s)\\b(?:prenez|prends|injectez|ajoutez)\\s+${FR_SHORT}(?:(?:encore\\s+)?(?:un|une|deux|trois|\\d+)\\s+${FR_SHORT}${med}[^.?!]{0,30}?(?:\\ben\\s+plus\\b|\\bde\\s+plus\\b|\\bsuppl[ée]mentaires?)|(?:un|une)\\s+autres?\\s+${FR_SHORT}${med}|encore\\s+(?:un|une)\\s+${FR_SHORT}${med})`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
dose: [
|
||||
// augmentez / montez / passez / portez à|de N unit
|
||||
new RegExp(
|
||||
`\\b(?:augment\\w*|mont\\w*|pass\\w*|port\\w*|majo\\w*)\\s+(?:votre\\s+dose\\s+)?(?:[àa]|de|jusqu'[àa])\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// réduisez / diminuez / baissez à|de N unit
|
||||
new RegExp(
|
||||
`\\b(?:r[ée]duis\\w*|r[ée]duire|diminu\\w*|baiss\\w*|abaiss\\w*)\\s+(?:votre\\s+dose\\s+)?(?:[àa]|de|jusqu'[àa])\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:envisagez|essayez|vous\\s+devriez|je\\s+recommande|je\\s+sugg[èe]re)\\b[^.?!]{0,40}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:prochaine|nouvelle)\\s+dose\\b[^.?!]{0,30}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
risk: [
|
||||
/\brisque\s+(?:est\s+)?(?:de\s+|d['’])?(?:environ\s+|~)?\d{1,3}\s*%/i,
|
||||
/\b\d{1,3}\s*%\s+(?:de\s+)?(?:risque|probabilit[ée]|chance)/i,
|
||||
/\brisque\s+(?:cardiovasculaire|cardiaque|d'avc|de\s+mortalit[ée])\s+[àa]\s+(?:10|dix)\s+ans\b/i,
|
||||
// digit percent as a risk figure, either order (the noun need not be adjacent)
|
||||
new RegExp(`\\b${RISK_NOUN_FR}\\b[^.?!]{0,40}\\d{1,3}\\s*%`, "i"),
|
||||
new RegExp(`\\d{1,3}\\s*%[^.?!]{0,40}\\b${RISK_NOUN_FR}\\b`, "i"),
|
||||
// spelled-out percent as a risk figure, either order
|
||||
new RegExp(`\\b${RISK_NOUN_FR}\\b[^.?!]{0,40}\\b${PCT_WORD_FR}\\b`, "i"),
|
||||
new RegExp(`\\b${PCT_WORD_FR}\\b[^.?!]{0,40}\\b${RISK_NOUN_FR}\\b`, "i"),
|
||||
// named engine + qualifying number, either order
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_FR}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_FR}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
// engine + categorical result assertion, numberless
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,60}${RESULT_VERB_FR}`, "i"),
|
||||
],
|
||||
causal: [
|
||||
/\bparce\s+qu\w*/i,
|
||||
/\b[àa]\s+cause\s+de\b/i,
|
||||
/\bcaus(?:e|es|ent|é|ée)\b/i,
|
||||
/\ben\s+raison\s+de\b/i,
|
||||
/\bentra[îi]n\w*/i,
|
||||
/\ba\s+conduit\s+[àa]/i,
|
||||
/\bd[ûu]\s+[àa]/i,
|
||||
/\bresponsable\s+de\b/i,
|
||||
/\bgr[âa]ce\s+[àa]/i,
|
||||
/\bprovoqu\w*/i,
|
||||
],
|
||||
continuation:
|
||||
/\b(?:continuez?\s+(?:à|de|le)|gardez|comme\s+prescrit|tel\s+que\s+prescrit)\b/i,
|
||||
changeStem:
|
||||
/\b(?:augment|réduis|réduir|diminu|baiss|abaiss|doubl|arrêt|saut)\w*/i,
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
/** Outbound-screen banks for Italian, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
objectSlot,
|
||||
TARGET,
|
||||
ENGINE,
|
||||
ELIDED,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const IT_STOP =
|
||||
"non|mai|e|o|ma|se|sodio|sale|caffeina|zucchero|acqua|proteine|fibre";
|
||||
|
||||
const IT_SLOT = objectSlot(IT_STOP);
|
||||
|
||||
const IT_SHORT = objectSlot(`${IT_STOP}|per|con|prima|dopo|senza|a|al`, 2);
|
||||
|
||||
const IT_MED_NOUN =
|
||||
"(?:dos[ei]|compress[ae]|pastigli[ae]|farmac[oi]|medicinal[ei]|insulina|iniezion[ei]|capsul[ae]|terapia)";
|
||||
|
||||
const SPELLED_IT =
|
||||
"(?:zero|uno|due|tre|quattro|cinque|sei|sette|otto|nove|dieci|undici|dodici|tredici|quattordici|quindici|sedici|diciassette|diciotto|diciannove|venti|trenta|quaranta|cinquanta|sessanta)";
|
||||
|
||||
const PCT_WORD_IT = `${SPELLED_IT}\\s+per\\s+cento`;
|
||||
|
||||
const QUAL_NUM_IT = `(?:\\d{1,3}\\s*%|${PCT_WORD_IT})`;
|
||||
|
||||
const RISK_NOUN_IT = "(?:rischio|probabilità)";
|
||||
|
||||
const RESULT_VERB_IT =
|
||||
"(?:la\\s+(?:colloca|mette|classifica|pone)|rientra\\s+(?:in|nella)|si\\s+trova\\s+(?:in|nella)|ricade\\s+in)";
|
||||
|
||||
const IT_MED_CLASS =
|
||||
"(?:anticoagulant[ei]|antiaggregant[ei]|statin[ae]|betabloccant[ei]|diuretic[oi]|antidepressiv[oi])";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`${ELIDED}(?:${IT_MED_NOUN}|${medicationExtras(IT_MED_CLASS, names)})`,
|
||||
doseChangeClass: (med) => [
|
||||
new RegExp(
|
||||
`(?<!\\b(?:non|mai)\\s)\\b(?:aumenti|aumentare|incrementi|incrementare|porti|portare|passi|passare|salga|salire|riduca|ridurre|abbassi|abbassare|diminuisca|diminuire|dimezzi|dimezzare|raddoppi|raddoppiare)\\s+${IT_SLOT}(?:da\\s+${TARGET}\\s+)?(?:a|di|fino\\s+a)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:non|mai)\\s)\\b(?:prenda|assuma|inietti|si\\s+inietti|inizi\\s+con|cominci\\s+con)\\s+${IT_SLOT}${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:non|mai)\\s)\\b(?:salti|sospenda|interrompa|dimezzi|raddoppi|divida|ometta|tralasci|smetta)\\s+${IT_SLOT}${med}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:non|mai)\\s)\\bsmett(?:a|i|ere)\\s+di\\s+(?:prendere|assumere|usare)\\b`,
|
||||
"i",
|
||||
),
|
||||
// "una compressa in più", "un'altra compressa", "una dose extra".
|
||||
new RegExp(
|
||||
`(?<!\\b(?:non|mai)\\s)\\b(?:prenda|assuma|inietti|aggiunga)\\s+${IT_SHORT}(?:(?:un|una|uno|due|tre|\\d+)\\s+${IT_SHORT}${med}[^.?!]{0,30}?(?:\\bin\\s+più|\\bextra\\b|\\baggiuntiv[aoei]\\b|\\bin\\s+aggiunta\\b)|(?:un['’]altra|un\\s+altro|altr[eio])\\s*${IT_SHORT}${med})`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
dose: [
|
||||
// aumenti / salga / passi a|di N unit
|
||||
new RegExp(
|
||||
`\\b(?:aument\\w*|sal\\w*|pass\\w*|increment\\w*)\\s+(?:la\\s+sua\\s+dose\\s+)?(?:a|di|fino\\s+a)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// riduca / abbassi / diminuisca a|di N unit
|
||||
new RegExp(
|
||||
`\\b(?:riduc\\w*|ridur\\w*|abbass\\w*|diminu\\w*|cal\\w*)\\s+(?:la\\s+sua\\s+dose\\s+)?(?:a|di|fino\\s+a)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:consideri|provi|dovrebbe|raccomando|suggerisco)\\b[^.?!]{0,40}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:prossima|nuova)\\s+dose\\b[^.?!]{0,30}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
risk: [
|
||||
/\brischio\s+(?:del?|dell'|[èe]\s+del?|di\s+circa)\s+(?:circa\s+|~)?\d{1,3}\s*%/i,
|
||||
/\b\d{1,3}\s*%\s+(?:di\s+)?(?:rischio|probabilit[àa])/i,
|
||||
/\brischio\s+(?:cardiovascolare|cardiaco|di\s+ictus|di\s+mortalit[àa])\s+a\s+(?:10|dieci)\s+anni\b/i,
|
||||
new RegExp(`\\b${RISK_NOUN_IT}\\b[^.?!]{0,40}\\d{1,3}\\s*%`, "i"),
|
||||
new RegExp(`\\d{1,3}\\s*%[^.?!]{0,40}\\b${RISK_NOUN_IT}\\b`, "i"),
|
||||
new RegExp(`\\b${RISK_NOUN_IT}\\b[^.?!]{0,40}\\b${PCT_WORD_IT}\\b`, "i"),
|
||||
new RegExp(`\\b${PCT_WORD_IT}\\b[^.?!]{0,40}\\b${RISK_NOUN_IT}\\b`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_IT}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_IT}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,60}${RESULT_VERB_IT}`, "i"),
|
||||
],
|
||||
causal: [
|
||||
/\bperch[ée]/i,
|
||||
/\ba\s+causa\s+di\b/i,
|
||||
/\bcaus(?:a|ano|ato|ata)\b/i,
|
||||
/\bprovoc\w*/i,
|
||||
/\bha\s+portato\s+a\b/i,
|
||||
/\bdovuto\s+a\b/i,
|
||||
/\bresponsabile\s+di\b/i,
|
||||
/\bgrazie\s+a\b/i,
|
||||
/\bguid\w*\s+da\b/i,
|
||||
],
|
||||
continuation: /\b(?:continui\s+(?:a|con|il)|mantenga|come\s+prescritto)\b/i,
|
||||
changeStem: /\b(?:aument|riduc|ridur|abbass|diminu|raddoppi|salt|dimezz)\w*/i,
|
||||
};
|
||||
@@ -0,0 +1,140 @@
|
||||
/** Outbound-screen banks for Korean, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DRUG_UNIT,
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
ENGINE,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const KO_MED_NOUN =
|
||||
"(?:약을|약은|약\\s|알약|정제|인슐린|주사|용량|복용량|투약)";
|
||||
|
||||
/**
|
||||
* Korean dosage-form counters. They attach to the digit with no space ("2정",
|
||||
* "10단위"); `정` is guarded against "정상" (normal) and `알` against "알레르기".
|
||||
*/
|
||||
const KO_COUNT = "(?:단위|유닛|캡슐|정(?!상|도|확|말)|알(?!레))";
|
||||
|
||||
const KO_TARGET = `(?:[\\d.,]+(?:\\s*${DRUG_UNIT}${UNIT_END}|${KO_COUNT})|(?:한|두|세|네|반)\\s*(?:알(?!레)|정(?!상|도|확|말)|캡슐))`;
|
||||
|
||||
/**
|
||||
* A Korean imperative or recommendation that closes a dose clause. The stem
|
||||
* must meet the polite-imperative ending directly, so the prohibitive
|
||||
* "건너뛰지 마세요" (don't skip) and "줄이지 마세요" (don't lower) never match.
|
||||
*/
|
||||
const KO_IMPERATIVE =
|
||||
"(?:(?:늘리|올리|높이|줄이|내리|낮추|증량하|감량하|중단하|끊으|건너뛰|멈추|복용하|드시|드|투여하|맞으|주사하|바꾸|변경하)(?:세요|십시오|시기\\s*바랍니다|셔야|는\\s*(?:것이|게)\\s*좋)|(?:늘려|올려|높여|줄여|내려|낮춰|끊어|멈춰|바꿔|드셔|복용해|투여해|증량해|감량해|중단해)\\s*(?:보세요|주세요|보십시오|야\\s*(?:해요|합니다)))";
|
||||
|
||||
// Korean carries no `\\b` next to a Hangul token: `\\b` is an ASCII \\w boundary
|
||||
// and Hangul is not \\w, so /\\b위험/ matches nothing. Spelled-out percentages are
|
||||
// omitted deliberately — Korean writes "12%", and the Sino-Korean numerals
|
||||
// ("이", "삼") collide with common particles.
|
||||
const QUAL_NUM_KO = "(?:\\d{1,3}\\s*(?:%|퍼센트|프로))";
|
||||
|
||||
const RISK_NOUN_KO = "(?:위험(?:도|률|성)?|확률|가능성)";
|
||||
|
||||
const HORIZON_KO = "(?:10년|십\\s?년|평생)\\s*(?:위험(?:도|률)?|리스크)";
|
||||
|
||||
const RESULT_VERB_KO =
|
||||
"(?:(?:에|으로|로)\\s*(?:분류|해당|속하|들어가)|군에\\s*(?:속하|해당))";
|
||||
|
||||
const RISK_BAND_KO =
|
||||
"(?:고|중간|중등도|저|높은|낮은)\\s*위험\\s*(?:군|범위|등급|구간)";
|
||||
|
||||
const RISK_LEVEL_KO = "(?:높|상승|증가|중등도|경계|우려)";
|
||||
|
||||
const RISK_VERDICT_KO = `(?:은|는|이|가)\\s*[^.?!]{0,10}${RISK_LEVEL_KO}`;
|
||||
|
||||
const KO_MED_CLASS =
|
||||
"(?:혈액\\s*희석제|항응고제|항혈소판제|스타틴|베타\\s*차단제|이뇨제|항우울제)";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`(?:${KO_MED_NOUN}|(?:${medicationExtras(KO_MED_CLASS, names, false)})\\S{0,3})`,
|
||||
doseChangeClass: (med) => [
|
||||
// A stated amount closed by an imperative or a recommendation, with or
|
||||
// without a target particle: "2000mg으로 증량하세요", "하루 2정 드세요".
|
||||
new RegExp(`${KO_TARGET}[^.?!]{0,25}${KO_IMPERATIVE}`, "i"),
|
||||
// A medication object halved, doubled, skipped or stopped by imperative.
|
||||
new RegExp(
|
||||
`${med}[^.?!]{0,20}(?:(?:절반|반)\\s*으로\\s*(?:줄이|나누)(?:세요|십시오)|(?:절반|반)\\s*으로\\s*(?:줄여|나눠)\\s*(?:보세요|주세요)|두\\s?배로\\s*(?:늘리|올리)(?:세요|십시오)|두\\s?배로\\s*(?:늘려|올려)\\s*(?:보세요|주세요)|(?:건너뛰|중단하|끊으|멈추|그만\\s*드시|그만\\s*복용하)(?:세요|십시오))`,
|
||||
),
|
||||
new RegExp(
|
||||
`(?:복용|투약|투여|주사)(?:을|를)?\\s*(?:중단하|멈추|그만하|끊으)(?:세요|십시오)`,
|
||||
),
|
||||
// One more or an additional dose of a medication, closed by an
|
||||
// imperative: "라미프릴을 한 알 더 드세요", "인슐린을 추가로 맞으세요".
|
||||
// The prohibitive "더 드시지 마세요" never meets an imperative ending.
|
||||
new RegExp(
|
||||
`${med}[^.?!]{0,12}(?:(?:(?:한|두|세|\\d+)\\s*(?:알|정|캡슐|번|단위)?\\s*)?더|추가로)\\s*(?:드세요|드십시오|복용하세요|복용하십시오|투여하세요|맞으세요|맞으십시오|주사하세요)`,
|
||||
),
|
||||
],
|
||||
// Korean is verb-final: the dose comes first and the change verb closes the
|
||||
// clause ("2.4mg으로 늘려 보세요"), the mirror image of the Latin banks. A
|
||||
// target-marking particle on the dose is required, the same demand the Latin
|
||||
// banks make of "to" / "by" / "auf" — without it the permitted restatement
|
||||
// "7.5mg을 복용 중이고, 체중은 내려가고 있어요" matched the lowering pattern.
|
||||
dose: [
|
||||
new RegExp(
|
||||
`[\\d.,]+\\s*${DOSE_UNIT}\\s*(?:으로|로|까지)[^.?!]{0,20}(?:올리|올려|늘리|늘려|증량|높이|높여|인상)`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`[\\d.,]+\\s*${DOSE_UNIT}\\s*(?:으로|로|까지)[^.?!]{0,20}(?:내리|내려|줄이|줄여|감량|낮추|낮춰|감축)`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:권해|권장|권고|추천|제안)[^.?!]{0,40}[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}[^.?!]{0,40}(?:권해|권장|권고|추천|제안|드심|드셔|복용하세요|시도해)`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:다음|새)\\s*(?:용량|단계|복용량)[^.?!]{0,30}[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// Both a medication object AND a trial cue are required, as in the EN bank,
|
||||
// so "걸음 수를 두 배로 늘려 2주간 해보세요" never trips.
|
||||
/(?:약|알|정|인슐린|주사|복용|용량)[^.?!]{0,25}(?:절반|반으로|두\s?배|건너뛰|중단|끊)[^.?!]{0,40}(?:주간|주 동안|일 동안|달 동안|보세요|보십시오|보자|해 ?보)/,
|
||||
],
|
||||
risk: [
|
||||
new RegExp(`${RISK_NOUN_KO}[^.?!]{0,15}${QUAL_NUM_KO}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_KO}[^.?!]{0,15}${RISK_NOUN_KO}`, "i"),
|
||||
new RegExp(`${HORIZON_KO}[^.?!]{0,40}${QUAL_NUM_KO}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_KO}[^.?!]{0,40}${HORIZON_KO}`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_KO}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_KO}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
new RegExp(
|
||||
`(?:\\b${ENGINE}\\b|${HORIZON_KO})[^.?!]{0,60}${RESULT_VERB_KO}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:\\b${ENGINE}\\b|${HORIZON_KO})[^.?!]{0,60}${RISK_BAND_KO}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:\\b${ENGINE}\\b|${HORIZON_KO})[^.?!]{0,40}${RISK_VERDICT_KO}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
causal: [
|
||||
/때문(?:에|이)/,
|
||||
/(?:으로|로)\s*인(?:해|하여|한)/,
|
||||
/탓(?:에|이|으로)/,
|
||||
/(?:이|가)\s*원인/,
|
||||
/원인(?:은|이에요|입니다|이라|으로)/,
|
||||
/유발/,
|
||||
/초래/,
|
||||
/야기/,
|
||||
/덕분(?:에|이)/,
|
||||
/(?:영향을|결과로)\s*(?:줘서|줬|나타났|이어졌)/,
|
||||
],
|
||||
continuation:
|
||||
/(?:그대로\s*(?:유지|복용)|계속\s*(?:복용|드시|유지)|처방(?:대로|받은\s*대로)|지시(?:대로|받은\s*대로)|유지하세요)/,
|
||||
changeStem:
|
||||
/(?:올리|올려|늘리|늘려|증량|높이|높여|인상|내리|내려|줄이|줄여|감량|낮추|낮춰|감축|절반|반으로|두\s?배|건너뛰|중단|끊)/,
|
||||
};
|
||||
@@ -0,0 +1,179 @@
|
||||
/** Outbound-screen banks for Dutch, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DRUG_UNIT,
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
objectSlot,
|
||||
TARGET,
|
||||
LETTER,
|
||||
ENGINE,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const NL_STOP =
|
||||
"niet|nooit|geen|en|of|maar|als|wanneer|natrium|zout|cafeïne|suiker\\S*|water|vocht|eiwit\\S*|vezels?|koffie|alcohol|stappen|calorieën";
|
||||
|
||||
const NL_SLOT = objectSlot(NL_STOP);
|
||||
|
||||
const NL_SHORT = objectSlot(
|
||||
`${NL_STOP}|om|voor|met|bij|in|op|van|naar|tot|na|zonder|tijdens|over|dan|te`,
|
||||
2,
|
||||
);
|
||||
|
||||
const NL_MED_NOUN = `(?:[${LETTER}]*(?:dosis|doses|dosering|tablet(?:ten|je|jes)?|medicijn(?:en)?|medicatie|medicament(?:en)?|geneesmiddel(?:en)?|insuline|injecties?|capsules?)|pil(?:len|letje|letjes)?|prik(?:ken)?|spuitjes?|inhalator)(?![${LETTER}])`;
|
||||
|
||||
const SPELLED_NL =
|
||||
"(?:nul|een|één|twee|drie|vier|vijf|zes|zeven|acht|negen|tien|elf|twaalf|dertien|veertien|vijftien|zestien|zeventien|achttien|negentien|twintig|dertig|veertig|vijftig|zestig)";
|
||||
|
||||
const PCT_WORD_NL = `${SPELLED_NL}\\s+procent`;
|
||||
|
||||
const QUAL_NUM_NL = `(?:\\d{1,3}\\s*%|${PCT_WORD_NL})`;
|
||||
|
||||
const RISK_NOUN_NL = "(?:risico\\w*|kans|waarschijnlijkheid)";
|
||||
|
||||
const HORIZON_NL =
|
||||
"(?:(?:10|tien)[- ]?jaars?[- ]?risico\\w*|levenslang\\s+risico\\w*|risico\\w*(?:\\s+\\S+){0,3}\\s+(?:binnen|in)\\s+(?:de\\s+komende\\s+)?(?:10|tien)\\s+jaar)";
|
||||
|
||||
const RESULT_VERB_NL =
|
||||
"(?:plaatst\\s+(?:je|u)|deelt\\s+(?:je|u)\\s+in|zet\\s+(?:je|u)\\s+in|(?:je|u)\\s+valt\\s+in|valt\\s+(?:je|u)\\s+in|brengt\\s+(?:je|u)\\s+in)";
|
||||
|
||||
const RISK_BAND_NL =
|
||||
"(?:hoog|hoger|laag|lager|middel|matig|verhoogd)\\w*[- ]?risico(?:categorie|groep|klasse|zone)";
|
||||
|
||||
const RISK_LEVEL_NL =
|
||||
"(?:verhoogd|hoog|hoger|matig|gemiddeld|zorgwekkend|aanzienlijk)";
|
||||
|
||||
const RISK_VERDICT_NL = `(?:is|ligt|lijkt|blijft|wijst\\s+op|komt\\s+uit\\s+op)\\s+(?:\\w+\\s+){0,2}${RISK_LEVEL_NL}`;
|
||||
|
||||
const NL_MED_CLASS =
|
||||
"\\S*(?:bloedverdunner\\w*|antistolling\\w*|anticoagul\\w*|plaatjesremmer\\w*|statine\\w*|b[eè]tablokker\\w*|plastablet\\w*|diuretic\\w*|diuretica|antidepressiv\\w*|antidepressiva|slaappil\\w*|slaapmiddel\\w*|bloeddrukverlager\\w*|ace-remmer\\w*)";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`(?:${NL_MED_NOUN}|${medicationExtras(NL_MED_CLASS, names)})`,
|
||||
doseChangeClass: (med) => [
|
||||
new RegExp(
|
||||
`(?<!\\b(?:niet|nooit)\\s)\\b(?:verhoog|verhogen|verlaag|verlagen|verminder|verminderen|halveer|halveren|verdubbel|verdubbelen)\\s+${NL_SLOT}(?:van\\s+${TARGET}\\s+)?(?:naar|tot|met)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:niet|nooit)\\s)\\b(?:ga|gaan|breng|brengen|bouw|bouwen|zet|zetten)\\s+${NL_SLOT}(?:van\\s+${TARGET}\\s+)?(?:omhoog\\s+|omlaag\\s+|terug\\s+|af\\s+|op\\s+)?(?:naar|tot)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:niet|nooit)\\s)\\bzet\\s+${NL_SLOT}op\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
// The infinitive closes a Dutch clause: "om je metformine naar 2000 mg te
|
||||
// verhogen". A past participle ("verhoogd") is a log restatement and
|
||||
// does not match.
|
||||
new RegExp(
|
||||
`\\b(?:naar|tot|met)\\s+${TARGET}[^.?!]{0,30}?\\b(?:verhogen|verlagen|verminderen|ophogen|opbouwen|afbouwen|terugbrengen|halveren|verdubbelen)\\b`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:niet|nooit)\\s)\\b(?:neem|slik|spuit|injecteer|begin\\s+met|start\\s+met)\\s+${NL_SLOT}${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:niet|nooit)\\s)\\b(?:halveer|verdubbel|pauzeer|deel|onderbreek)\\s+${NL_SLOT}${med}`,
|
||||
"i",
|
||||
),
|
||||
// "Overslaan" is separable: "sla je avonddosis over". A negation between
|
||||
// the object and the particle ("sla je dosis niet over") is a warning.
|
||||
new RegExp(
|
||||
`\\bsla\\s+${NL_SLOT}${med}(?:(?!\\b(?:niet|nooit)\\b)[^.?!]){0,25}?\\bover\\b`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\bstop\\s+met\\s+(?:het\\s+)?(?:innemen|slikken|gebruiken|spuiten|nemen)\\b`,
|
||||
"i",
|
||||
),
|
||||
// Stop, halt or leave out a named medication or a drug class: "stop je
|
||||
// bloedverdunner", "laat de Eliquis morgen weg".
|
||||
new RegExp(`\\b(?:stop|staak)\\s+(?:met\\s+)?${NL_SHORT}${med}`, "i"),
|
||||
new RegExp(
|
||||
`\\blaat\\s+${NL_SLOT}${med}(?:(?!\\b(?:niet|nooit)\\b)[^.?!]){0,25}?\\bweg\\b`,
|
||||
"i",
|
||||
),
|
||||
// The same behind a recommending cue, with the verb last. A referral
|
||||
// ("vraag", "overleg", "voordat", "of") or a negation ends it.
|
||||
new RegExp(
|
||||
`\\b(?:je\\s+zou|u\\s+zou|je\\s+kunt|u\\s+kunt|ik\\s+raad|ik\\s+adviseer|overweeg|probeer|het\\s+is\\s+(?:beter|verstandig))\\b(?:(?!\\b(?:niet|nooit|geen|vraag|overleg|voordat|of|eerst)\\b)[^.?!]){0,40}?${med}(?:(?!\\b(?:niet|nooit)\\b)[^.?!]){0,30}?\\b(?:overslaan|over\\s+te\\s+slaan|stoppen|te\\s+stoppen|staken|te\\s+staken|halveren|te\\s+halveren|verdubbelen|te\\s+verdubbelen|pauzeren|te\\s+pauzeren|weglaten|weg\\s+te\\s+laten)\\b`,
|
||||
"i",
|
||||
),
|
||||
// An extra, another or one more of a medication: "neem een extra
|
||||
// ramipril", "neem nog een tablet", "neem vanavond een tablet meer".
|
||||
// A negation ("geen") cannot enter the short slot.
|
||||
new RegExp(
|
||||
`(?<!\\b(?:niet|nooit)\\s)\\b(?:neem|slik|spuit|injecteer|voeg)\\s+${NL_SHORT}(?:(?:(?:een|één|twee|\\d+)\\s+)?(?:extra|aanvullende?|tweede)[-\\s]+${NL_SHORT}${med}|nog\\s+(?:een|één|twee|\\d+)\\s+${NL_SHORT}${med}|(?:een|één|twee|drie|\\d+)\\s+${NL_SHORT}${med}\\s+(?:meer|extra)\\b)`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
dose: [
|
||||
// verhoog / ga omhoog / bouw op naar|tot|met N unit
|
||||
new RegExp(
|
||||
`\\b(?:verhoog|verhogen|ophogen|ga\\s+omhoog|bouw\\s+op|opbouwen)(?:\\s+(?:je|jouw|de|uw)\\s+(?:dosis|dosering))?\\s+(?:naar|tot|met)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// verlaag / verminder / bouw af naar|tot|met N unit
|
||||
new RegExp(
|
||||
`\\b(?:verlaag|verlagen|verminder|verminderen|ga\\s+omlaag|bouw\\s+af|afbouwen)(?:\\s+(?:je|jouw|de|uw)\\s+(?:dosis|dosering))?\\s+(?:naar|tot|met)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// A recommending cue in front of a drug amount. `ml` is left out: "probeer
|
||||
// 2000 ml water per dag" is hydration advice.
|
||||
new RegExp(
|
||||
`\\b(?:overweeg|probeer|je\\s+zou|u\\s+zou|ik\\s+raad|ik\\s+adviseer|ik\\s+stel\\s+voor)\\b[^.?!]{0,40}\\b[\\d.,]+\\s*${DRUG_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:volgende|nieuwe)\\s+(?:dosis|stap)\\b[^.?!]{0,30}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
/\b(?:halveer|halveren|verdubbel|verdubbelen|sla\s+\S+\s+over|overslaan|stop\s+met)\b[^.?!]{0,25}(?:dosis|tablet\w*|medicijn\w*|medicatie|insuline|injectie\w*)[^.?!]{0,40}\b(?:(?:een|twee|drie|vier|\d+)\s+(?:dag|dagen|week|weken|maand|maanden)|om\s+te\s+(?:zien|testen|kijken|proberen))\b/i,
|
||||
],
|
||||
risk: [
|
||||
new RegExp(
|
||||
`\\brisico\\w*\\s+(?:van|is|ligt\\s+op|bedraagt|op)\\s+(?:ongeveer\\s+|zo['’]?n\\s+|circa\\s+|rond\\s+|~)?\\d{1,3}\\s*%`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(`\\b\\d{1,3}\\s*%\\s+${RISK_NOUN_NL}\\b`, "i"),
|
||||
new RegExp(`\\b${RISK_NOUN_NL}\\b[^.?!]{0,40}\\b${PCT_WORD_NL}\\b`, "i"),
|
||||
new RegExp(`\\b${PCT_WORD_NL}\\b[^.?!]{0,40}\\b${RISK_NOUN_NL}\\b`, "i"),
|
||||
new RegExp(`\\b${HORIZON_NL}\\b[^.?!]{0,40}${QUAL_NUM_NL}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_NL}[^.?!]{0,40}\\b${HORIZON_NL}\\b`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_NL}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_NL}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_NL})\\b[^.?!]{0,60}${RESULT_VERB_NL}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_NL})\\b[^.?!]{0,60}${RISK_BAND_NL}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_NL})\\b[^.?!]{0,40}${RISK_VERDICT_NL}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
causal: [
|
||||
/\bomdat\b/i,
|
||||
/\bdoordat\b/i,
|
||||
/\bvanwege\b/i,
|
||||
/\bveroorza\w*/i,
|
||||
/\b(?:ten|als)\s+gevolg\s+van\b/i,
|
||||
/\bleid(?:t|de|den)\s+tot\b/i,
|
||||
/\bkomt\s+door\b/i,
|
||||
/\bdankzij\b/i,
|
||||
/\bde\s+schuld\s+van\b/i,
|
||||
/\boorzaak\b/i,
|
||||
/\bverantwoordelijk\s+voor\b/i,
|
||||
],
|
||||
continuation:
|
||||
/\b(?:blijf|ga\s+door\s+met|neem\s+\S+\s+(?:gewoon\s+)?verder|zoals\s+(?:voorgeschreven|afgesproken|gebruikelijk|gewoonlijk|altijd|eerder|je\s+gewend\s+bent)|volgens\s+(?:voorschrift|afspraak))\b/i,
|
||||
changeStem:
|
||||
/\b(?:verho|verla|vermind|halve|verdubbel|ophog|afbouw|bouw\s+af|overslaan|sla\s+\S+\s+over|stop)\w*/i,
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
/** Outbound-screen banks for Polish, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
objectSlot,
|
||||
TARGET,
|
||||
ENGINE,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const PL_STOP =
|
||||
"nie|i|lub|albo|ale|jeśli|czy|sód|sodu|sól|soli|kofein\\S*|cukr\\S*|wod\\S*|białk\\S*|błonnik\\S*";
|
||||
|
||||
const PL_SLOT = objectSlot(PL_STOP);
|
||||
|
||||
const PL_SHORT = objectSlot(`${PL_STOP}|z|ze|do|na|po|przed|bez|dla`, 2);
|
||||
|
||||
const PL_MED_NOUN =
|
||||
"(?:dawk[aęiy]|dawek|tabletk[aęiy]|tabletek|lek|leki|leku|leków|insulin[aęy]|zastrzyk[ia]?|zastrzyków|kapsułk[aęiy]|pigułk[aęiy])(?=[\\s.,;:!?]|$)";
|
||||
|
||||
const SPELLED_PL =
|
||||
"(?:zero|jeden|dwa|trzy|cztery|pięć|sześć|siedem|osiem|dziewięć|dziesięć|jedenaście|dwanaście|trzynaście|czternaście|piętnaście|szesnaście|siedemnaście|osiemnaście|dziewiętnaście|dwadzieścia|trzydzieści|czterdzieści|pięćdziesiąt|sześćdziesiąt)";
|
||||
|
||||
const PCT_WORD_PL = `${SPELLED_PL}\\s+procent`;
|
||||
|
||||
const QUAL_NUM_PL = `(?:\\d{1,3}\\s*%|${PCT_WORD_PL})`;
|
||||
|
||||
const RISK_NOUN_PL = "(?:ryzyk\\w*|prawdopodobieństw\\w*)";
|
||||
|
||||
const RESULT_VERB_PL =
|
||||
"(?:umieszcza\\s+(?:cię|pana|panią)|klasyfikuje\\s+(?:cię|pana|panią)|znajdujesz\\s+się\\s+w|wpadasz\\s+w|kwalifikuje\\s+(?:cię|pana|panią))";
|
||||
|
||||
const PL_MED_CLASS =
|
||||
"(?:lek\\S*\\s+przeciwzakrzepow\\S*|antykoagulant\\S*|statyn\\S*|beta-?bloker\\S*|diuretyk\\S*|lek\\S*\\s+moczopędn\\S*|antydepresant\\S*)";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`(?:${PL_MED_NOUN}|${medicationExtras(PL_MED_CLASS, names)})`,
|
||||
doseChangeClass: (med) => [
|
||||
new RegExp(
|
||||
`(?<!\\bnie\\s)\\b(?:zwi[ęe]ksz(?:y[ćc]|cie)?|podnie[śs](?:[ćc]|cie)?|podwy[żz]sz(?:y[ćc])?|zmniejsz(?:y[ćc]|cie)?|obni[żz](?:y[ćc]|cie)?|zredukuj(?:cie)?|zredukowa[ćc]|przejd[źz](?:cie)?|przej[śs][ćc]|zmie[ńn]|zmieni[ćc])(?=\\s)\\s+${PL_SLOT}(?:z\\s+${TARGET}\\s+)?(?:do|o|na)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:(?<!\\bnie\\s)\\b(?:przyjmij|przyjmuj|we[źz]|bierz|wstrzyknij|wstrzykuj|zacznij\\s+od)|\\b(?:proszę|należy|warto)\\s+(?:przyj[ąa][ćc]|wzi[ąa][ćc]|bra[ćc]|przyjmowa[ćc]|wstrzykiwa[ćc]|wstrzykn[ąa][ćc]|zacz[ąa][ćc]\\s+od))(?=\\s)\\s+${PL_SLOT}${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:(?<!\\bnie\\s)\\b(?:pomi[ńn](?:cie)?|opu[śs][ćc]|odstaw(?:cie)?|wstrzymaj|podziel|podwój)|\\b(?:proszę|należy|warto)\\s+(?:pomin[ąa][ćc]|odstawi[ćc]|wstrzyma[ćc]|przerwa[ćc]|podzieli[ćc]|podwoi[ćc]))(?=\\s)\\s+${PL_SLOT}${med}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:(?<!\\bnie\\s)\\bprzesta[ńn](?:cie)?|\\b(?:proszę|należy)\\s+przesta[ćc])\\s+(?:brać|przyjmować|stosować)(?=[\\s.,;:!?]|$)`,
|
||||
"i",
|
||||
),
|
||||
// "dodatkową tabletkę", "jeszcze jedną dawkę", "jedną tabletkę więcej".
|
||||
new RegExp(
|
||||
`(?:(?<!\\bnie\\s)\\b(?:we[źz]|bierz|przyjmij|wstrzyknij|dodaj)|\\b(?:proszę|należy|warto)\\s+(?:wzi[ąa][ćc]|przyj[ąa][ćc]|wstrzykn[ąa][ćc]|doda[ćc]))(?=\\s)\\s+${PL_SHORT}(?:(?:dodatkow\\S*|kolejn\\S*|jeszcze\\s+jedn\\S*)\\s+${PL_SHORT}${med}|(?:jedn\\S*|dwie|dwa|trzy|\\d+)\\s+${PL_SHORT}${med}\\s+więcej(?=[\\s.,;:!?]|$))`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
dose: [
|
||||
// zwiększ / podnieś / przejdź do|o N unit
|
||||
new RegExp(
|
||||
`\\b(?:zwi[ęe]ksz\\S*|podnie[śs]\\S*|przejd[źz]\\S*|podwy[żz]sz\\S*)\\s+(?:dawk\\S*\\s+)?(?:do|o)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// zmniejsz / obniż / zredukuj do|o N unit
|
||||
new RegExp(
|
||||
`\\b(?:zmniejsz\\S*|obni[żz]\\S*|zreduk\\S*|reduk\\S*)\\s+(?:dawk\\S*\\s+)?(?:do|o)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:rozwa[żz]\\S*|spr[óo]buj\\S*|powinien|powinna|zalecam|sugeruj[ęe])[^.?!]{0,40}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:nast[ęe]pna|kolejna|nowa)\\s+dawka\\b[^.?!]{0,30}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
risk: [
|
||||
/\bryzyko\s+(?:wynosi\s+|około\s+|~)?\d{1,3}\s*%/i,
|
||||
/\b\d{1,3}\s*%\s+(?:ryzyka|prawdopodobie[ńn]stwa)/i,
|
||||
/\bryzyk\w*\s+(?:sercowo[- ]naczyniow\w*|zawału|udaru|zgonu)\s+w\s+(?:ci[ąa]gu\s+)?(?:10|dziesi[ęe]ciu)\s+lat\b/i,
|
||||
new RegExp(`\\b${RISK_NOUN_PL}\\b[^.?!]{0,40}\\d{1,3}\\s*%`, "i"),
|
||||
new RegExp(`\\d{1,3}\\s*%[^.?!]{0,40}\\b${RISK_NOUN_PL}\\b`, "i"),
|
||||
new RegExp(`\\b${RISK_NOUN_PL}\\b[^.?!]{0,40}\\b${PCT_WORD_PL}\\b`, "i"),
|
||||
new RegExp(`\\b${PCT_WORD_PL}\\b[^.?!]{0,40}\\b${RISK_NOUN_PL}\\b`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_PL}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_PL}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,60}${RESULT_VERB_PL}`, "i"),
|
||||
],
|
||||
causal: [
|
||||
/\bponiewa[żz]/i,
|
||||
/\bz\s+powodu\b/i,
|
||||
/\bpowoduj\w*/i,
|
||||
/\bspowodowa\w*/i,
|
||||
/\bprzyczyn\w*/i,
|
||||
/\bdoprowadzi\w*\s+do\b/i,
|
||||
/\bwskutek\b/i,
|
||||
/\bdzi[ęe]ki\b/i,
|
||||
/\bodpowiedzialn\w*\s+za\b/i,
|
||||
],
|
||||
continuation:
|
||||
/\b(?:kontynuuj|przyjmuj\s+dalej|zgodnie\s+z\s+zaleceniem|pozosta[ńn]\s+przy)\b/i,
|
||||
changeStem:
|
||||
/\b(?:zwiększ|zmniejsz|obniż|podnie[śs]|podwyższ|zreduk|pomi[ńn]|opu[śs][ćc])\w*/i,
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
/** Outbound-screen banks for Brazilian Portuguese, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DRUG_UNIT,
|
||||
DOSE_UNIT,
|
||||
UNIT_END,
|
||||
objectSlot,
|
||||
TARGET,
|
||||
LETTER,
|
||||
ENGINE,
|
||||
medicationExtras,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const PT_STOP =
|
||||
"não|nao|nunca|jamais|e|ou|mas|se|sódio|sal|cafeína|açúcar\\S*|água|proteínas?|fibras?|café|álcool|passos|calorias";
|
||||
|
||||
const PT_SLOT = objectSlot(PT_STOP);
|
||||
|
||||
const PT_SHORT = objectSlot(
|
||||
`${PT_STOP}|para|pra|com|antes|depois|sem|a|ao|à|de|do|da|em|no|na|por|durante|sobre`,
|
||||
2,
|
||||
);
|
||||
|
||||
const PT_MED_NOUN =
|
||||
"(?:doses?|comprimidos?|pastilhas?|dr[áa]geas?|c[áa]psulas?|medicamentos?|rem[ée]dios?|medica[çc](?:[ãa]o|[õo]es)|insulina|inje[çc](?:[ãa]o|[õo]es)|tratamento)";
|
||||
|
||||
const SPELLED_PT =
|
||||
"(?:zero|um|uma|dois|duas|tr[êe]s|quatro|cinco|seis|sete|oito|nove|dez|onze|doze|treze|quatorze|catorze|quinze|dezesseis|dezessete|dezoito|dezenove|vinte|trinta|quarenta|cinquenta|sessenta)";
|
||||
|
||||
const PCT_WORD_PT = `${SPELLED_PT}\\s+por\\s+cento`;
|
||||
|
||||
const QUAL_NUM_PT = `(?:\\d{1,3}\\s*%|${PCT_WORD_PT})`;
|
||||
|
||||
const RISK_NOUN_PT = "(?:risco|probabilidade|chance)";
|
||||
|
||||
const HORIZON_PT =
|
||||
"(?:risco\\s+(?:cardiovascular|card[íi]aco|de\\s+(?:avc|infarto|morte|mortalidade))\\s+(?:em|para|nos\\s+pr[óo]ximos)\\s+(?:10|dez)\\s+anos)";
|
||||
|
||||
const RESULT_VERB_PT =
|
||||
"(?:(?:te|lhe|o|a)\\s+(?:coloca|classifica|situa|enquadra|p[õo]e)|coloca\\s+voc[êe]|voc[êe]\\s+(?:est[áa]|fica|cai|se\\s+enquadra)\\s+(?:n[oa]s?|em))";
|
||||
|
||||
const RISK_BAND_PT =
|
||||
"(?:(?:faixa|categoria|grupo)\\s+de\\s+)?(?:alto|baixo|m[ée]dio|moderado|elevado|intermedi[áa]rio)\\s+risco";
|
||||
|
||||
const RISK_LEVEL_PT =
|
||||
"(?:elevad[oa]|alt[oa]|moderad[oa]|intermedi[áa]ri[oa]|lim[íi]trofe|preocupante|significativ[oa])";
|
||||
|
||||
const RISK_VERDICT_PT = `(?:é|est[áa]|parece|fica|continua|indica|mostra|aponta\\s+para)\\s+(?:\\S+\\s+){0,2}${RISK_LEVEL_PT}`;
|
||||
|
||||
const PT_MED_CLASS =
|
||||
"(?:anticoagulantes?|antiagregantes?|estatinas?|beta-?bloqueadores?|diur[ée]ticos?|antidepressivos?|anti-?hipertensivos?|sonífer[oa]s?|rem[ée]dios?\\s+(?:para\\s+(?:a\\s+)?|de\\s+)(?:press[ãa]o|dormir|afinar\\s+o\\s+sangue))";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`(?:${PT_MED_NOUN}|${medicationExtras(PT_MED_CLASS, names)})`,
|
||||
doseChangeClass: (med) => [
|
||||
// Brazilian Portuguese addresses the reader as "você", whose imperative
|
||||
// is the subjunctive form ("aumente", "tome", "pule"). The indicative a
|
||||
// log restatement uses ("você toma 1000 mg", "a dose passou para 2000
|
||||
// mg") is not in any verb list.
|
||||
new RegExp(
|
||||
`(?<!\\b(?:não|nao|nunca|jamais)\\s)\\b(?:aumente|aumentar|suba|subir|passe|passar|eleve|elevar|reduza|reduzir|diminua|diminuir|baixe|baixar|abaixe|abaixar|dobre|dobrar|duplique|duplicar|leve|levar|ajuste|ajustar|mude|mudar)\\s+${PT_SLOT}(?:de\\s+${TARGET}\\s+)?(?:para|pra|a|até|em)\\s+${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:não|nao|nunca|jamais)\\s)\\b(?:tome|use|aplique|injete|administre|comece\\s+(?:com|a\\s+tomar)|passe\\s+a\\s+(?:tomar|usar|aplicar))\\s+${PT_SLOT}${TARGET}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:não|nao|nunca|jamais)\\s)\\b(?:pule|omita|suspenda|interrompa|dobre|duplique|divida|parta)\\s+${PT_SLOT}${med}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!\\b(?:não|nao|nunca|jamais)\\s)\\b(?:pare|deixe)\\s+de\\s+(?:tomar|usar|aplicar|injetar)\\b`,
|
||||
"i",
|
||||
),
|
||||
// Stop or drop a named medication or a drug class: "pare o
|
||||
// anticoagulante", "tire a metformina".
|
||||
new RegExp(
|
||||
`(?<!\\b(?:não|nao|nunca|jamais)\\s)\\b(?:pare|largue|abandone|tire|retire)\\s+${PT_SHORT}${med}`,
|
||||
"i",
|
||||
),
|
||||
// The same behind a recommending cue, with the verb in the infinitive.
|
||||
// A referral ("antes", "converse", "pergunte", "se") or a negation ends it.
|
||||
new RegExp(
|
||||
`\\b(?:voc[êe]\\s+(?:deve|deveria|pode|poderia|precisa)|recomendo|sugiro|considere|experimente|tente|vale\\s+a\\s+pena|seria\\s+(?:bom|melhor))(?:(?!\\b(?:não|nao|nunca|jamais|antes|sem|pergunte|consulte|converse|fale|se)(?![${LETTER}]))[^.?!]){0,40}?\\b(?:(?:parar|deixar)\\s+de\\s+(?:tomar|usar|aplicar|injetar)\\b|(?:pular|omitir|suspender|interromper|dobrar|duplicar|dividir|parar)\\s+${PT_SLOT}${med})`,
|
||||
"i",
|
||||
),
|
||||
// An extra, another or one more of a medication: "tome um comprimido a
|
||||
// mais", "tome outra dose", "tome mais uma insulina".
|
||||
new RegExp(
|
||||
`(?<!\\b(?:não|nao|nunca|jamais)\\s)\\b(?:tome|use|aplique|injete|acrescente|adicione)\\s+${PT_SHORT}(?:(?:um|uma|dois|duas|três|\\d+)\\s+${PT_SHORT}${med}[^.?!]{0,20}?(?:\\ba\\s+mais\\b|\\bextras?\\b|\\badiciona(?:l|is)\\b)|(?:outr[oa]s?|mais\\s+(?:um|uma))\\s+${PT_SHORT}${med})`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
dose: [
|
||||
// aumente / suba / passe a dose para|a|até|em N unit
|
||||
new RegExp(
|
||||
`\\b(?:aumente|aumentar|suba|subir|passe|passar|eleve|elevar)\\s+(?:a\\s+|sua\\s+|a\\s+sua\\s+)?(?:dose\\s+)?(?:para|pra|a|até|em)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// reduza / diminua / baixe a dose para|a|até|em N unit
|
||||
new RegExp(
|
||||
`\\b(?:reduza|reduzir|diminua|diminuir|baixe|baixar|abaixe|abaixar)\\s+(?:a\\s+|sua\\s+|a\\s+sua\\s+)?(?:dose\\s+)?(?:para|pra|a|até|em)\\s+[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
// A recommending cue in front of a drug amount. `ml` is left out: "tente
|
||||
// beber 2000 ml de água por dia" is hydration advice.
|
||||
new RegExp(
|
||||
`\\b(?:considere|experimente|tente|voc[êe]\\s+deveria|voc[êe]\\s+deve|recomendo|sugiro)\\b[^.?!]{0,40}\\b[\\d.,]+\\s*${DRUG_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:pr[óo]xima|nova)\\s+(?:dose|etapa)\\b[^.?!]{0,30}\\b[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
/\b(?:dobr\w*|duplic\w*|pul(?:e|ar)|suspend\w*|interromp\w*|pare\s+de\s+tomar|parar\s+de\s+tomar)\b[^.?!]{0,25}\b(?:doses?|comprimidos?|rem[ée]dios?|medicamentos?|medica[çc]|insulina|inje[çc])[^.?!]{0,40}\b(?:por\s+(?:\d+|um|uma|dois|duas|tr[êe]s|quatro)\s+(?:dias?|semanas?|m[êe]s|meses)|para\s+(?:ver|testar|experimentar))\b/i,
|
||||
],
|
||||
risk: [
|
||||
/\brisco\s+(?:de|é\s+de|fica\s+em|está\s+em)\s+(?:aproximadamente\s+|cerca\s+de\s+|uns\s+|~)?\d{1,3}\s*%/i,
|
||||
/\b\d{1,3}\s*%\s+(?:de\s+)?(?:risco|probabilidade|chance)/i,
|
||||
new RegExp(`\\b${RISK_NOUN_PT}\\b[^.?!]{0,40}\\d{1,3}\\s*%`, "i"),
|
||||
new RegExp(`\\d{1,3}\\s*%[^.?!]{0,40}\\b${RISK_NOUN_PT}\\b`, "i"),
|
||||
new RegExp(`\\b${RISK_NOUN_PT}\\b[^.?!]{0,40}\\b${PCT_WORD_PT}\\b`, "i"),
|
||||
new RegExp(`\\b${PCT_WORD_PT}\\b[^.?!]{0,40}\\b${RISK_NOUN_PT}\\b`, "i"),
|
||||
new RegExp(`\\b${HORIZON_PT}\\b[^.?!]{0,40}${QUAL_NUM_PT}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_PT}[^.?!]{0,40}\\b${HORIZON_PT}\\b`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_PT}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_PT}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_PT})\\b[^.?!]{0,60}${RESULT_VERB_PT}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_PT})\\b[^.?!]{0,60}${RISK_BAND_PT}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`\\b(?:${ENGINE}|${HORIZON_PT})\\b[^.?!]{0,40}${RISK_VERDICT_PT}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
causal: [
|
||||
/\bporque\b/i,
|
||||
/\bpor\s+causa\s+d/i,
|
||||
/\bdevido\s+[aà]/i,
|
||||
/\bcaus(?:a|am|ou|aram|ado|ada|ando)\b/i,
|
||||
/\bprovoc\w*/i,
|
||||
/\blev(?:ou|aram)\s+[aà]/i,
|
||||
/\bresult(?:ou|aram)\s+(?:em|d[eoa])\b/i,
|
||||
/\bem\s+raz[ãa]o\s+d/i,
|
||||
/\bem\s+decorr[êe]ncia\s+d/i,
|
||||
/\bgra[çc]as\s+[aà]/i,
|
||||
/\brespons[áa]ve(?:l|is)\s+(?:por|pel[oa]s?)\b/i,
|
||||
/\bculpa\s+d/i,
|
||||
],
|
||||
continuation:
|
||||
/\b(?:continue\s+(?:tomando|usando|com|a\s+tomar)|siga\s+(?:tomando|com)|mantenha|conforme\s+(?:prescrito|receitado|orientado|combinado)|como\s+(?:prescrito|receitado|de\s+costume|sempre|antes)|de\s+acordo\s+com\s+a\s+(?:receita|prescri))/i,
|
||||
changeStem:
|
||||
/\b(?:aument|reduz|diminu|dobr|duplic|omit|suspend|interromp|baix(?:e|ar|ando)\b|abaix|elev(?:e|ar)\b|sub(?:a|ir)\b|pul(?:e|ar|ando)\b|parar?\s+de\s+tomar)\w*/i,
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* What every locale's outbound-screen bank is built from: the dose units, the
|
||||
* target-dose shape, the object slot, the generic-name stems, and the shape of
|
||||
* a bank. The screen itself is `../outbound-screen.ts`; each language's bank
|
||||
* is one module in `./locales/`.
|
||||
*/
|
||||
|
||||
export /**
|
||||
* Count units — a dose expressed as a number of dosage forms ("2 tablets",
|
||||
* "zwei Hübe" written as "2 Hübe", "1 comprimé"). The same forms the
|
||||
* medication wizard offers (`src/lib/medications/dose-units.ts`), in every
|
||||
* locale the banks cover. Without them "take 3 tablets a day" carried no unit
|
||||
* the screen recognised and passed every pattern.
|
||||
*/
|
||||
const COUNT_UNIT =
|
||||
"(?:tablets?|pills?|capsules?|drops?|puffs?|sprays?|tabletten?|kapseln?|tropfen|hübe|hub|sprühstöße|comprimés?|gélules?|gouttes?|bouffées?|comprimidos?|pastillas?|c[áa]psulas?|gotas?|compress[ae]|pastigli[ae]|gocce|goccia|tabletk[aęiy]|tabletek|kapsułk[aęiy]|kapsułek|krople|kropli|druppels?|pillen|pufjes?|pastilhas?|dr[áa]geas?|jatos?|borrifadas?)";
|
||||
|
||||
export /**
|
||||
* Dose units for a target dose a model must never name. Mass and insulin
|
||||
* units plus the count forms; `ml` is left out on purpose because
|
||||
* "increase your water intake to 2000 ml" is ordinary hydration advice.
|
||||
*/
|
||||
const DRUG_UNIT = `(?:mg|mcg|µg|iu|ui|i\\.u\\.|units?|unidades?|unità|unités?|jednostek|jednostki|eenheden|eenheid|ie|i\\.e\\.|einheiten|${COUNT_UNIT})`;
|
||||
|
||||
export /**
|
||||
* Dose units, shared across every locale. The GLP-1 + general oral-dose
|
||||
* vocabulary; `ie` / `i.e.` / `einheiten` / `unità` / `jednostk` cover the
|
||||
* insulin-unit spellings the locales use (`eenheden` is the Dutch one, `ui`
|
||||
* the Portuguese abbreviation of "unidades internacionais").
|
||||
*/
|
||||
const DOSE_UNIT = `(?:ml|${DRUG_UNIT})`;
|
||||
|
||||
export /**
|
||||
* What may follow a dose unit. A concentration is a lab value, not a dose:
|
||||
* "keep your glucose under 140 mg/dL" and "eGFR above 60 ml/min" name no
|
||||
* medication, and a bare `\b` let both trip the "try … N mg" pattern. A
|
||||
* per-day or per-kilogram rate ("2000 mg/day", "10 mg/kg") IS a dose and still
|
||||
* matches. The lookahead also ends a unit that closes on a non-word letter
|
||||
* ("unità", "i.e.") where `\b` cannot.
|
||||
*/
|
||||
const UNIT_END = "(?!\\w|\\s*\\/\\s*(?:dl|l|min|mmol)\\b)";
|
||||
|
||||
export /**
|
||||
* One to four words of object between a change verb and its target — "your
|
||||
* metformin", "the evening insulin dose", "votre metformine". The original
|
||||
* English pattern accepted only "it" or "your dose" in that slot, so naming the
|
||||
* drug ("increase your metformin to 2000 mg") walked past the screen.
|
||||
*
|
||||
* A word may not be a digit run, a sentence break, a negation (a negated
|
||||
* imperative is a warning, not an instruction), a clause joiner, or a
|
||||
* dietary noun: "lower your sodium to 1500 mg" and "cut caffeine to 200 mg"
|
||||
* are nutrition guidance, not a dose change.
|
||||
*/
|
||||
function objectSlot(stopWords: string, maxWords = 4): string {
|
||||
return `(?:(?!(?:${stopWords})(?=\\s))[^\\s\\d.?!,;:]+\\s+){0,${maxWords}}?`;
|
||||
}
|
||||
|
||||
export /**
|
||||
* A count written as a word: "take two tablets", "nimm zwei Tabletten",
|
||||
* "prenez deux comprimés", "weź dwie tabletki". The union of every covered
|
||||
* locale, because a provider mixes languages. Articles ("a", "un", "eine")
|
||||
* are left out on purpose: "take a tablet with food" is an article, not a
|
||||
* count, and the "eine Tablette mehr" shape is its own pattern below.
|
||||
*/
|
||||
const SPELLED_COUNT =
|
||||
"(?:one|two|three|four|five|six|half\\s+an?|zwei|drei|vier|fünf|sechs|eine?n?\\s+halbe|halbe|deux|trois|quatre|cinq|demi|dos|tres|cuatro|cinco|media|due|tre|quattro|cinque|mezza|dwie|dwa|trzy|cztery|pięć|jedn[ąa]|pół|één|twee|drie|vier|vijf|zes|halve|dois|duas|três|quatro|meia|meio)";
|
||||
|
||||
export /** A target dose: a number, a drug unit, and an honest unit end. */
|
||||
const TARGET = `(?:[\\d.,]+\\s*${DRUG_UNIT}${UNIT_END}|(?<![\\w])${SPELLED_COUNT}\\s+${COUNT_UNIT}${UNIT_END})`;
|
||||
|
||||
export /** The letters a drug name is spelled with in the Latin-script locales. */
|
||||
const LETTER = "a-zà-ÿąćęłńóśźż";
|
||||
|
||||
export /**
|
||||
* Common generic-name stems (INN): ramipril, losartan, bisoprolol,
|
||||
* atorvastatin, amlodipine, metformin, semaglutide, apixaban, omeprazole,
|
||||
* furosemide. A surface that passes no schedule (a nudge, the document chat)
|
||||
* still recognises "skip the amlodipine". Two letters must precede the stem,
|
||||
* so "April" is not a drug; the stems are chosen so no everyday word ends in
|
||||
* one ("comparing" is why heparin is spelled out rather than stemmed).
|
||||
*/
|
||||
const INN_STEM = `(?<![${LETTER}])[${LETTER}]{2,}(?:pril|sartan|olol|statin|dipin|dypin|formin|glutid|gliflozin|gliptin|xaban|ksaban|prazol|semid)[${LETTER}]{0,4}(?![${LETTER}])|(?<![${LETTER}])(?:enoxa|dalte)?heparin[${LETTER}]{0,4}`;
|
||||
|
||||
export const ENGINE = "(?:framingham|ascvd|score2|qrisk)";
|
||||
|
||||
/*
|
||||
* Each bank also builds a tighter slot for the shapes that carry no number: an
|
||||
* extra / one more / stop imperative. At most two words, and a preposition ends
|
||||
* it, so "take an extra minute to log your medication" and "stop worrying about
|
||||
* your medication" never reach the medication noun.
|
||||
*/
|
||||
|
||||
/**
|
||||
* An elided article ("l'amlodipine", "dell'insulina") may sit in front of a
|
||||
* French or Italian medication noun.
|
||||
*/
|
||||
export const ELIDED = "(?:[a-z]{1,4}['’])?";
|
||||
|
||||
/**
|
||||
* What a locale's medication noun accepts beside its dosage-form words: its
|
||||
* drug-class words, the generic-name stems (not for Korean, whose script the
|
||||
* stems are not written in), and the person's own medication names when the
|
||||
* caller passes them.
|
||||
*/
|
||||
export function medicationExtras(
|
||||
medClass: string,
|
||||
names: string | null,
|
||||
withStems = true,
|
||||
): string {
|
||||
return [medClass, withStems ? INN_STEM : null, names]
|
||||
.filter((part): part is string => part !== null)
|
||||
.join("|");
|
||||
}
|
||||
|
||||
/** One language's banks, composed by the screen. */
|
||||
export interface LocaleBank {
|
||||
/**
|
||||
* The medication-noun alternation the dose-change class uses: the
|
||||
* dosage-form and treatment words, the generic drug-class words ("Stop your
|
||||
* blood thinner" names no dosage form and no drug, and it is the most
|
||||
* dangerous stop of all), the generic-name stems, and the person's own
|
||||
* medication names when the caller passes them.
|
||||
*/
|
||||
vocabulary(names: string | null): string;
|
||||
/** The dose-change class over that medication noun. */
|
||||
doseChangeClass(med: string): readonly RegExp[];
|
||||
/** Target-dose prescriptions. */
|
||||
dose: readonly RegExp[];
|
||||
/** Risk-score fabrication. */
|
||||
risk: readonly RegExp[];
|
||||
/** Asserted causation (GROUND RULE 12). */
|
||||
causal: readonly RegExp[];
|
||||
/** A maintenance anchor that may exempt a dose sentence. */
|
||||
continuation: RegExp;
|
||||
/** A change stem that cancels that exemption. */
|
||||
changeStem: RegExp;
|
||||
}
|
||||
Reference in new issue
Block a user