ezmedicationinput 0.1.44 → 0.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +18842 -0
- package/dist/index.js +18689 -675
- package/package.json +17 -6
- package/dist/advice-rules.json +0 -772
- package/dist/advice-terminology.json +0 -104
- package/dist/advice.js +0 -1375
- package/dist/context.js +0 -50
- package/dist/event-trigger.d.ts +0 -14
- package/dist/event-trigger.js +0 -501
- package/dist/fhir-translations.js +0 -117
- package/dist/fhir.js +0 -693
- package/dist/format.js +0 -1034
- package/dist/i18n.js +0 -1341
- package/dist/internal-types.d.ts +0 -60
- package/dist/internal-types.js +0 -2
- package/dist/ir.js +0 -178
- package/dist/lexer/lex.js +0 -401
- package/dist/lexer/meaning.js +0 -619
- package/dist/lexer/surface.js +0 -62
- package/dist/lexer/token-types.js +0 -19
- package/dist/maps.js +0 -2226
- package/dist/package.json +0 -3
- package/dist/parser-state.js +0 -441
- package/dist/parser.js +0 -5631
- package/dist/prn.js +0 -59
- package/dist/safety.js +0 -15
- package/dist/schedule.js +0 -1636
- package/dist/segment.js +0 -203
- package/dist/site-phrases.js +0 -344
- package/dist/suggest.js +0 -907
- package/dist/timing-summary.js +0 -138
- package/dist/types.js +0 -276
- package/dist/utils/array.js +0 -11
- package/dist/utils/enum.d.ts +0 -2
- package/dist/utils/enum.js +0 -7
- package/dist/utils/object.js +0 -34
- package/dist/utils/strength.js +0 -149
- package/dist/utils/text.js +0 -48
- package/dist/utils/units.js +0 -82
package/dist/segment.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.splitSigSegments = splitSigSegments;
|
|
4
|
-
const COMMA_SEGMENT_STARTERS = new Set([
|
|
5
|
-
"apply",
|
|
6
|
-
"take",
|
|
7
|
-
"instill",
|
|
8
|
-
"inject",
|
|
9
|
-
"spray",
|
|
10
|
-
"use",
|
|
11
|
-
"od",
|
|
12
|
-
"os",
|
|
13
|
-
"ou",
|
|
14
|
-
"re",
|
|
15
|
-
"le",
|
|
16
|
-
"be",
|
|
17
|
-
"right",
|
|
18
|
-
"left",
|
|
19
|
-
"both",
|
|
20
|
-
"each"
|
|
21
|
-
]);
|
|
22
|
-
const DIRECTIONAL_SEGMENT_STARTERS = new Set(["right", "left", "both", "each"]);
|
|
23
|
-
const SEPARATOR_RULES = [
|
|
24
|
-
{
|
|
25
|
-
name: "double-slash",
|
|
26
|
-
match: (input, index) => (input.startsWith("//", index) ? 2 : 0)
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: "line-break",
|
|
30
|
-
match: (input, index) => {
|
|
31
|
-
const ch = input[index];
|
|
32
|
-
if (ch === "\r" && input[index + 1] === "\n") {
|
|
33
|
-
return 2;
|
|
34
|
-
}
|
|
35
|
-
return ch === "\n" || ch === "\r" ? 1 : 0;
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: "pipe",
|
|
40
|
-
match: (input, index) => input[index] === "|" && hasNonWhitespaceAround(input, index) ? consumePipeRun(input, index) : 0
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: "plus",
|
|
44
|
-
match: (input, index) => input[index] === "+" && hasNonWhitespaceAround(input, index) ? 1 : 0
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: "slash-divider",
|
|
48
|
-
match: (input, index) => input[index] === "/" && isDividerSlash(input, index) ? 1 : 0
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
name: "comma-clause",
|
|
52
|
-
match: (input, index, currentStart) => input[index] === "," && shouldSplitComma(input, index, currentStart) ? 1 : 0
|
|
53
|
-
}
|
|
54
|
-
];
|
|
55
|
-
function splitSigSegments(input) {
|
|
56
|
-
const segments = [];
|
|
57
|
-
let currentStart = 0;
|
|
58
|
-
let depth = 0;
|
|
59
|
-
const pushSegment = (rawStart, rawEnd) => {
|
|
60
|
-
let start = rawStart;
|
|
61
|
-
let end = rawEnd;
|
|
62
|
-
while (start < end && /\s/.test(input[start])) {
|
|
63
|
-
start += 1;
|
|
64
|
-
}
|
|
65
|
-
while (end > start && /\s/.test(input[end - 1])) {
|
|
66
|
-
end -= 1;
|
|
67
|
-
}
|
|
68
|
-
if (end <= start) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
segments.push({
|
|
72
|
-
text: input.slice(start, end),
|
|
73
|
-
start,
|
|
74
|
-
end
|
|
75
|
-
});
|
|
76
|
-
};
|
|
77
|
-
for (let index = 0; index < input.length; index += 1) {
|
|
78
|
-
const ch = input[index];
|
|
79
|
-
if (ch === "(" || ch === "[" || ch === "{") {
|
|
80
|
-
depth += 1;
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if ((ch === ")" || ch === "]" || ch === "}") && depth > 0) {
|
|
84
|
-
depth -= 1;
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (depth > 0) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
for (const rule of SEPARATOR_RULES) {
|
|
91
|
-
const length = rule.match(input, index, currentStart);
|
|
92
|
-
if (!length) {
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
pushSegment(currentStart, index);
|
|
96
|
-
currentStart = index + length;
|
|
97
|
-
index = currentStart - 1;
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
pushSegment(currentStart, input.length);
|
|
102
|
-
if (segments.length > 0) {
|
|
103
|
-
return segments;
|
|
104
|
-
}
|
|
105
|
-
const fallback = input.trim();
|
|
106
|
-
if (!fallback) {
|
|
107
|
-
return [];
|
|
108
|
-
}
|
|
109
|
-
const start = input.indexOf(fallback);
|
|
110
|
-
return [{ text: fallback, start, end: start + fallback.length }];
|
|
111
|
-
}
|
|
112
|
-
function hasNonWhitespaceAround(input, index) {
|
|
113
|
-
const left = previousNonWhitespace(input, index - 1);
|
|
114
|
-
const right = nextNonWhitespace(input, index + 1);
|
|
115
|
-
return left !== undefined && right !== undefined;
|
|
116
|
-
}
|
|
117
|
-
function isDividerSlash(input, index) {
|
|
118
|
-
var _a, _b;
|
|
119
|
-
if (input[index - 1] === "/" || input[index + 1] === "/") {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
const previous = previousNonWhitespace(input, index - 1);
|
|
123
|
-
const next = nextNonWhitespace(input, index + 1);
|
|
124
|
-
if (previous === undefined || next === undefined) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
if (/\d/.test(previous) && /\d/.test(next)) {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
return /\s/.test((_a = input[index - 1]) !== null && _a !== void 0 ? _a : "") || /\s/.test((_b = input[index + 1]) !== null && _b !== void 0 ? _b : "");
|
|
131
|
-
}
|
|
132
|
-
function shouldSplitComma(input, index, currentStart) {
|
|
133
|
-
var _a, _b;
|
|
134
|
-
if (!hasNonWhitespaceAround(input, index)) {
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
const left = input.slice(currentStart, index).trim();
|
|
138
|
-
const right = input.slice(index + 1).trim();
|
|
139
|
-
if (!left || !right) {
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
if (startsWithTimeExpression(right)) {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
const rightToken = (_b = (_a = right.match(/^([a-z]+|\d+(?:\.\d+)?)/i)) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.toLowerCase();
|
|
146
|
-
if (!rightToken) {
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
if (/^\d/.test(rightToken)) {
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
if (COMMA_SEGMENT_STARTERS.has(rightToken)) {
|
|
153
|
-
if (DIRECTIONAL_SEGMENT_STARTERS.has(rightToken)) {
|
|
154
|
-
return looksLikeDirectionalClause(right);
|
|
155
|
-
}
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
function looksLikeDirectionalClause(text) {
|
|
161
|
-
const normalized = text.trim().toLowerCase();
|
|
162
|
-
if (!normalized) {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
if (/\b\d+(?:\.\d+)?\b/.test(normalized)) {
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
return /\b(once|twice|thrice|daily|bid|tid|qid|q\d+[a-z0-9/-]*|every|prn|hs|morning|lunch|dinner|noon|night|weekly|monthly)\b/.test(normalized);
|
|
169
|
-
}
|
|
170
|
-
function startsWithTimeExpression(text) {
|
|
171
|
-
const trimmed = text.replace(/^\s+/, "");
|
|
172
|
-
if (!trimmed) {
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
return (/^@\s*\d{1,2}([:.]\d{2})?\s*(am|pm)?\b/i.test(trimmed) ||
|
|
176
|
-
/^\d{1,2}[:.]\d{2}\s*(am|pm)?\b/i.test(trimmed) ||
|
|
177
|
-
/^\d{1,2}\s*(am|pm)\b/i.test(trimmed));
|
|
178
|
-
}
|
|
179
|
-
function previousNonWhitespace(input, index) {
|
|
180
|
-
for (let cursor = index; cursor >= 0; cursor -= 1) {
|
|
181
|
-
const ch = input[cursor];
|
|
182
|
-
if (!/\s/.test(ch)) {
|
|
183
|
-
return ch;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return undefined;
|
|
187
|
-
}
|
|
188
|
-
function nextNonWhitespace(input, index) {
|
|
189
|
-
for (let cursor = index; cursor < input.length; cursor += 1) {
|
|
190
|
-
const ch = input[cursor];
|
|
191
|
-
if (!/\s/.test(ch)) {
|
|
192
|
-
return ch;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
function consumePipeRun(input, index) {
|
|
198
|
-
let cursor = index;
|
|
199
|
-
while (cursor < input.length && input[cursor] === "|") {
|
|
200
|
-
cursor += 1;
|
|
201
|
-
}
|
|
202
|
-
return cursor - index;
|
|
203
|
-
}
|
package/dist/site-phrases.js
DELETED
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isTimingOnlySitePhrase = isTimingOnlySitePhrase;
|
|
4
|
-
exports.hasExternalSurfaceModifier = hasExternalSurfaceModifier;
|
|
5
|
-
exports.extractExplicitSiteCandidate = extractExplicitSiteCandidate;
|
|
6
|
-
exports.selectBestResidualSiteCandidate = selectBestResidualSiteCandidate;
|
|
7
|
-
exports.inferRouteHintFromSitePhrase = inferRouteHintFromSitePhrase;
|
|
8
|
-
const maps_1 = require("./maps");
|
|
9
|
-
const meaning_1 = require("./lexer/meaning");
|
|
10
|
-
const types_1 = require("./types");
|
|
11
|
-
function getInferableDefaultRouteHint(canonical, definition) {
|
|
12
|
-
const routeHint = definition === null || definition === void 0 ? void 0 : definition.routeHint;
|
|
13
|
-
if (!routeHint) {
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
16
|
-
switch (routeHint) {
|
|
17
|
-
case types_1.RouteCode["Ophthalmic route"]:
|
|
18
|
-
return canonical.includes("eye") || canonical.includes("eyelid")
|
|
19
|
-
? routeHint
|
|
20
|
-
: undefined;
|
|
21
|
-
case types_1.RouteCode["Otic route"]:
|
|
22
|
-
return canonical.includes("ear") ? routeHint : undefined;
|
|
23
|
-
case types_1.RouteCode["Nasal route"]:
|
|
24
|
-
return canonical.includes("nostril") ||
|
|
25
|
-
canonical.includes("naris") ||
|
|
26
|
-
canonical.includes("nares") ||
|
|
27
|
-
canonical === "nose"
|
|
28
|
-
? routeHint
|
|
29
|
-
: undefined;
|
|
30
|
-
case types_1.RouteCode["Oral route"]:
|
|
31
|
-
return canonical === "mouth" ? routeHint : undefined;
|
|
32
|
-
case types_1.RouteCode["Sublingual route"]:
|
|
33
|
-
return canonical.includes("tongue") ? routeHint : undefined;
|
|
34
|
-
case types_1.RouteCode["Buccal route"]:
|
|
35
|
-
return canonical.includes("cheek") ? routeHint : undefined;
|
|
36
|
-
case types_1.RouteCode["Intravenous route"]:
|
|
37
|
-
return canonical.includes("vein") ? routeHint : undefined;
|
|
38
|
-
case types_1.RouteCode["Per vagina"]:
|
|
39
|
-
return canonical.includes("vagina") ? routeHint : undefined;
|
|
40
|
-
case types_1.RouteCode["Per rectum"]:
|
|
41
|
-
return canonical.includes("rectum") || canonical === "anus"
|
|
42
|
-
? routeHint
|
|
43
|
-
: undefined;
|
|
44
|
-
case types_1.RouteCode["Topical route"]:
|
|
45
|
-
return canonical.startsWith("affected ") ? routeHint : undefined;
|
|
46
|
-
default:
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function mergeBodySiteRouteHint(canonical, customDefinition, defaultDefinition) {
|
|
51
|
-
var _a;
|
|
52
|
-
const definition = customDefinition !== null && customDefinition !== void 0 ? customDefinition : defaultDefinition;
|
|
53
|
-
if (!definition) {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
const routeHint = (_a = customDefinition === null || customDefinition === void 0 ? void 0 : customDefinition.routeHint) !== null && _a !== void 0 ? _a : getInferableDefaultRouteHint(canonical, defaultDefinition);
|
|
57
|
-
if (routeHint === definition.routeHint) {
|
|
58
|
-
return definition;
|
|
59
|
-
}
|
|
60
|
-
return Object.assign(Object.assign({}, definition), { routeHint });
|
|
61
|
-
}
|
|
62
|
-
function isExplicitSiteBoundaryToken(lower, options, services) {
|
|
63
|
-
if (!lower) {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
if ((0, meaning_1.isWorkflowInstructionWord)(lower)) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
if (maps_1.EVENT_TIMING_TOKENS[lower] ||
|
|
70
|
-
maps_1.TIMING_ABBREVIATIONS[lower] ||
|
|
71
|
-
maps_1.WORD_FREQUENCIES[lower] ||
|
|
72
|
-
services.hasFrequencyLikeWord(lower) ||
|
|
73
|
-
(0, meaning_1.isCountKeywordWord)(lower) ||
|
|
74
|
-
services.mapFrequencyAdverb(lower) ||
|
|
75
|
-
services.mapIntervalUnit(lower) ||
|
|
76
|
-
maps_1.MEAL_KEYWORDS[lower]) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
if (services.hasRouteLikeWord(lower, options) || services.normalizeUnit(lower, options)) {
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
if ((0, meaning_1.isApplicationVerbWord)(lower)) {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
if (lower === "prn" || lower === "as" || lower === "needed" || lower === "for") {
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
if (services.isNumericToken(lower) && !services.isOrdinalToken(lower)) {
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
function isTimingOnlySitePhrase(words) {
|
|
94
|
-
if (!words.length) {
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
let phrase = "";
|
|
98
|
-
let allTimingWords = true;
|
|
99
|
-
for (let index = 0; index < words.length; index += 1) {
|
|
100
|
-
const word = words[index];
|
|
101
|
-
if (index > 0) {
|
|
102
|
-
phrase += " ";
|
|
103
|
-
}
|
|
104
|
-
phrase += word;
|
|
105
|
-
if (!maps_1.EVENT_TIMING_TOKENS[word] &&
|
|
106
|
-
!maps_1.TIMING_ABBREVIATIONS[word] &&
|
|
107
|
-
!maps_1.MEAL_KEYWORDS[word]) {
|
|
108
|
-
allTimingWords = false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (maps_1.EVENT_TIMING_TOKENS[phrase] ||
|
|
112
|
-
maps_1.TIMING_ABBREVIATIONS[phrase] ||
|
|
113
|
-
maps_1.MEAL_KEYWORDS[phrase]) {
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
return allTimingWords;
|
|
117
|
-
}
|
|
118
|
-
function hasExternalSurfaceModifier(siteText) {
|
|
119
|
-
const normalized = (0, maps_1.normalizeBodySiteKey)(siteText);
|
|
120
|
-
if (!normalized) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
const words = normalized.split(/\s+/);
|
|
124
|
-
for (const word of words) {
|
|
125
|
-
if (word.length > 0 && (0, meaning_1.isSiteSurfaceModifierWord)(word)) {
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
function extractExplicitSiteCandidate(tokens, consumed, startIndex, options, services) {
|
|
132
|
-
var _a;
|
|
133
|
-
const anchor = tokens[startIndex];
|
|
134
|
-
if (!anchor || consumed.has(anchor.index)) {
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
const anchorLower = services.normalizeTokenLower(anchor);
|
|
138
|
-
if (!(0, meaning_1.isSiteAnchorWord)(anchorLower)) {
|
|
139
|
-
return undefined;
|
|
140
|
-
}
|
|
141
|
-
if (!services.hasExplicitSiteIntroduction(startIndex)) {
|
|
142
|
-
return undefined;
|
|
143
|
-
}
|
|
144
|
-
const collected = [anchor.index];
|
|
145
|
-
const contentWords = [];
|
|
146
|
-
const candidateTextParts = [anchor.original];
|
|
147
|
-
let hasSiteHint = false;
|
|
148
|
-
for (let cursor = startIndex + 1; cursor < tokens.length; cursor += 1) {
|
|
149
|
-
const candidate = tokens[cursor];
|
|
150
|
-
if (!candidate || consumed.has(candidate.index)) {
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
const lower = services.normalizeTokenLower(candidate);
|
|
154
|
-
if (/^[;:(),]+$/.test(lower)) {
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
if (services.siteFillerWords.has(lower) && contentWords.length === 0) {
|
|
158
|
-
collected.push(candidate.index);
|
|
159
|
-
candidateTextParts.push(candidate.original);
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
if ((0, meaning_1.isSiteListConnectorWord)(lower) || lower === ",") {
|
|
163
|
-
let hasFollowingContent = false;
|
|
164
|
-
for (let lookahead = cursor + 1; lookahead < tokens.length; lookahead += 1) {
|
|
165
|
-
const lookaheadToken = tokens[lookahead];
|
|
166
|
-
if (!lookaheadToken || consumed.has(lookaheadToken.index)) {
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
const lookaheadLower = services.normalizeTokenLower(lookaheadToken);
|
|
170
|
-
if (services.siteFillerWords.has(lookaheadLower)) {
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
hasFollowingContent = !isExplicitSiteBoundaryToken(lookaheadLower, options, services);
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
if (!hasFollowingContent) {
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
collected.push(candidate.index);
|
|
180
|
-
candidateTextParts.push(candidate.original);
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
if (isExplicitSiteBoundaryToken(lower, options, services)) {
|
|
184
|
-
if (lower === "top") {
|
|
185
|
-
const nextToken = services.getNextActiveToken(cursor);
|
|
186
|
-
if (nextToken && services.normalizeTokenLower(nextToken) === "of") {
|
|
187
|
-
collected.push(candidate.index);
|
|
188
|
-
candidateTextParts.push(candidate.original);
|
|
189
|
-
contentWords.push(lower);
|
|
190
|
-
hasSiteHint = true;
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
collected.push(candidate.index);
|
|
197
|
-
candidateTextParts.push(candidate.original);
|
|
198
|
-
if (!services.siteConnectors.has(lower) && !services.siteFillerWords.has(lower)) {
|
|
199
|
-
contentWords.push(lower);
|
|
200
|
-
if (services.isBodySiteHint(lower, services.customSiteHints) ||
|
|
201
|
-
(0, meaning_1.isSiteSurfaceModifierWord)(lower) ||
|
|
202
|
-
services.isOrdinalToken(lower)) {
|
|
203
|
-
hasSiteHint = true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
if (!contentWords.length || isTimingOnlySitePhrase(contentWords)) {
|
|
208
|
-
return undefined;
|
|
209
|
-
}
|
|
210
|
-
const candidateText = candidateTextParts.join(" ").replace(/\s+/g, " ").trim();
|
|
211
|
-
if (candidateText && ((_a = services.isInstructionLikeText) === null || _a === void 0 ? void 0 : _a.call(services, candidateText))) {
|
|
212
|
-
return undefined;
|
|
213
|
-
}
|
|
214
|
-
if (!hasSiteHint &&
|
|
215
|
-
contentWords.length === 1 &&
|
|
216
|
-
contentWords[0] !== "area" &&
|
|
217
|
-
anchorLower === "in") {
|
|
218
|
-
return undefined;
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
tokenIndices: collected,
|
|
222
|
-
source: "explicit"
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
function selectBestResidualSiteCandidate(groups, prnSiteSuffixIndices, services) {
|
|
226
|
-
var _a;
|
|
227
|
-
let bestTokenIndices;
|
|
228
|
-
let bestScore = Number.NEGATIVE_INFINITY;
|
|
229
|
-
for (const group of groups) {
|
|
230
|
-
const filteredTokens = [];
|
|
231
|
-
const contentWords = [];
|
|
232
|
-
const candidateTextParts = [];
|
|
233
|
-
let hasWorkflowWord = false;
|
|
234
|
-
let knownWordCount = 0;
|
|
235
|
-
let modifierCount = 0;
|
|
236
|
-
for (const token of group.tokens) {
|
|
237
|
-
if (prnSiteSuffixIndices.has(token.index)) {
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
filteredTokens.push(token);
|
|
241
|
-
const lower = services.normalizeTokenLower(token);
|
|
242
|
-
if (lower.length === 0 ||
|
|
243
|
-
services.siteConnectors.has(lower) ||
|
|
244
|
-
services.siteFillerWords.has(lower) ||
|
|
245
|
-
(0, meaning_1.isSiteListConnectorWord)(lower) ||
|
|
246
|
-
lower === ",") {
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
candidateTextParts.push(token.original);
|
|
250
|
-
contentWords.push(lower);
|
|
251
|
-
if ((0, meaning_1.isWorkflowInstructionWord)(lower)) {
|
|
252
|
-
hasWorkflowWord = true;
|
|
253
|
-
}
|
|
254
|
-
if (services.isBodySiteHint(lower, services.customSiteHints)) {
|
|
255
|
-
knownWordCount += 1;
|
|
256
|
-
}
|
|
257
|
-
if ((0, meaning_1.isSiteSurfaceModifierWord)(lower) || services.isOrdinalToken(lower)) {
|
|
258
|
-
modifierCount += 1;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (!filteredTokens.length) {
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
if (!contentWords.length || isTimingOnlySitePhrase(contentWords)) {
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
if (candidateTextParts.length) {
|
|
268
|
-
const candidateText = candidateTextParts.join(" ").replace(/\s+/g, " ").trim();
|
|
269
|
-
if (candidateText && ((_a = services.isInstructionLikeText) === null || _a === void 0 ? void 0 : _a.call(services, candidateText))) {
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (hasWorkflowWord) {
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
const startsWithApplicationContext = services.hasApplicationVerbBefore(filteredTokens[0].index);
|
|
277
|
-
const previous = services.getPreviousActiveToken(filteredTokens[0].index);
|
|
278
|
-
const anchoredBySiteConnector = previous
|
|
279
|
-
? (0, meaning_1.isSiteAnchorWord)(services.normalizeTokenLower(previous))
|
|
280
|
-
: false;
|
|
281
|
-
if (knownWordCount === 0 &&
|
|
282
|
-
modifierCount === 0 &&
|
|
283
|
-
!anchoredBySiteConnector) {
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
const score = knownWordCount * 5 +
|
|
287
|
-
modifierCount * 2 +
|
|
288
|
-
(startsWithApplicationContext ? 2 : 0) +
|
|
289
|
-
(anchoredBySiteConnector ? 2 : 0) -
|
|
290
|
-
filteredTokens.length * 0.2 +
|
|
291
|
-
filteredTokens[0].index * 0.001;
|
|
292
|
-
if (score > bestScore) {
|
|
293
|
-
bestScore = score;
|
|
294
|
-
bestTokenIndices = [];
|
|
295
|
-
for (const token of filteredTokens) {
|
|
296
|
-
bestTokenIndices.push(token.index);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
if (!bestTokenIndices || !bestTokenIndices.length) {
|
|
301
|
-
return undefined;
|
|
302
|
-
}
|
|
303
|
-
return {
|
|
304
|
-
tokenIndices: bestTokenIndices,
|
|
305
|
-
source: "residual"
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
function inferRouteHintFromSitePhrase(siteText, options, services) {
|
|
309
|
-
const canonical = (0, maps_1.normalizeBodySiteKey)(siteText);
|
|
310
|
-
if (!canonical) {
|
|
311
|
-
return undefined;
|
|
312
|
-
}
|
|
313
|
-
const customExact = services.lookupBodySiteDefinition(options === null || options === void 0 ? void 0 : options.siteCodeMap, canonical);
|
|
314
|
-
const exact = mergeBodySiteRouteHint(canonical, customExact, maps_1.DEFAULT_BODY_SITE_SNOMED[canonical]);
|
|
315
|
-
if (exact === null || exact === void 0 ? void 0 : exact.routeHint) {
|
|
316
|
-
return exact.routeHint;
|
|
317
|
-
}
|
|
318
|
-
if (hasExternalSurfaceModifier(siteText)) {
|
|
319
|
-
return undefined;
|
|
320
|
-
}
|
|
321
|
-
const words = [];
|
|
322
|
-
for (const word of canonical.split(/\s+/)) {
|
|
323
|
-
if (word.length > 0) {
|
|
324
|
-
words.push(word);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
for (let length = words.length; length >= 1; length -= 1) {
|
|
328
|
-
for (let start = 0; start + length <= words.length; start += 1) {
|
|
329
|
-
let candidate = "";
|
|
330
|
-
for (let offset = 0; offset < length; offset += 1) {
|
|
331
|
-
if (offset > 0) {
|
|
332
|
-
candidate += " ";
|
|
333
|
-
}
|
|
334
|
-
candidate += words[start + offset];
|
|
335
|
-
}
|
|
336
|
-
const customDefinition = services.lookupBodySiteDefinition(options === null || options === void 0 ? void 0 : options.siteCodeMap, candidate);
|
|
337
|
-
const definition = mergeBodySiteRouteHint(candidate, customDefinition, maps_1.DEFAULT_BODY_SITE_SNOMED[candidate]);
|
|
338
|
-
if (definition === null || definition === void 0 ? void 0 : definition.routeHint) {
|
|
339
|
-
return definition.routeHint;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return undefined;
|
|
344
|
-
}
|