ezmedicationinput 0.1.42 → 0.1.44
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/README.md +31 -1
- package/dist/advice-rules.json +772 -0
- package/dist/advice-terminology.json +104 -0
- package/dist/advice.d.ts +16 -0
- package/dist/advice.js +1375 -0
- package/dist/event-trigger.d.ts +14 -0
- package/dist/event-trigger.js +501 -0
- package/dist/fhir-translations.d.ts +5 -0
- package/dist/fhir-translations.js +117 -0
- package/dist/fhir.d.ts +6 -4
- package/dist/fhir.js +566 -134
- package/dist/format.d.ts +5 -2
- package/dist/format.js +581 -219
- package/dist/i18n.d.ts +4 -2
- package/dist/i18n.js +725 -197
- package/dist/index.d.ts +0 -1
- package/dist/index.js +221 -169
- package/dist/internal-types.d.ts +5 -5
- package/dist/ir.d.ts +4 -0
- package/dist/ir.js +178 -0
- package/dist/lexer/lex.d.ts +2 -0
- package/dist/lexer/lex.js +401 -0
- package/dist/lexer/meaning.d.ts +71 -0
- package/dist/lexer/meaning.js +619 -0
- package/dist/lexer/surface.d.ts +2 -0
- package/dist/lexer/surface.js +62 -0
- package/dist/lexer/token-types.d.ts +36 -0
- package/dist/lexer/token-types.js +19 -0
- package/dist/maps.d.ts +6 -12
- package/dist/maps.js +793 -247
- package/dist/parser-state.d.ts +101 -0
- package/dist/parser-state.js +441 -0
- package/dist/parser.d.ts +7 -7
- package/dist/parser.js +3598 -1974
- package/dist/prn.d.ts +4 -0
- package/dist/prn.js +59 -0
- package/dist/schedule.js +230 -32
- package/dist/site-phrases.d.ts +35 -0
- package/dist/site-phrases.js +344 -0
- package/dist/timing-summary.d.ts +25 -0
- package/dist/timing-summary.js +138 -0
- package/dist/types.d.ts +248 -32
- package/dist/types.js +49 -1
- package/dist/utils/text.d.ts +3 -0
- package/dist/utils/text.js +48 -0
- package/package.json +1 -1
|
@@ -0,0 +1,344 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EventTiming, FhirDayOfWeek, FhirPeriodUnit } from "./types";
|
|
2
|
+
export interface TimingSummaryOptions {
|
|
3
|
+
groupMealTimingsByRelation?: boolean;
|
|
4
|
+
includeTimesPerDaySummary?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export type MealRelation = "before" | "after" | "with";
|
|
7
|
+
export type MealName = "breakfast" | "lunch" | "dinner";
|
|
8
|
+
export interface MealTimingGroup {
|
|
9
|
+
relation: MealRelation;
|
|
10
|
+
meals: MealName[];
|
|
11
|
+
codes: EventTiming[];
|
|
12
|
+
}
|
|
13
|
+
export interface TimingSummaryInput {
|
|
14
|
+
frequency?: number;
|
|
15
|
+
frequencyMax?: number;
|
|
16
|
+
period?: number;
|
|
17
|
+
periodMax?: number;
|
|
18
|
+
periodUnit?: FhirPeriodUnit;
|
|
19
|
+
timingCode?: string;
|
|
20
|
+
dayOfWeek?: FhirDayOfWeek[];
|
|
21
|
+
when?: EventTiming[];
|
|
22
|
+
timeOfDay?: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare function getMealTimingGroup(when: EventTiming[], options?: TimingSummaryOptions): MealTimingGroup | undefined;
|
|
25
|
+
export declare function inferDailyOccurrenceCount(input: TimingSummaryInput, options?: TimingSummaryOptions): number | undefined;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMealTimingGroup = getMealTimingGroup;
|
|
4
|
+
exports.inferDailyOccurrenceCount = inferDailyOccurrenceCount;
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
const MEAL_TIMING_DETAILS = {
|
|
7
|
+
[types_1.EventTiming["Before Breakfast"]]: { relation: "before", meal: "breakfast" },
|
|
8
|
+
[types_1.EventTiming["Before Lunch"]]: { relation: "before", meal: "lunch" },
|
|
9
|
+
[types_1.EventTiming["Before Dinner"]]: { relation: "before", meal: "dinner" },
|
|
10
|
+
[types_1.EventTiming["After Breakfast"]]: { relation: "after", meal: "breakfast" },
|
|
11
|
+
[types_1.EventTiming["After Lunch"]]: { relation: "after", meal: "lunch" },
|
|
12
|
+
[types_1.EventTiming["After Dinner"]]: { relation: "after", meal: "dinner" },
|
|
13
|
+
[types_1.EventTiming.Breakfast]: { relation: "with", meal: "breakfast" },
|
|
14
|
+
[types_1.EventTiming.Lunch]: { relation: "with", meal: "lunch" },
|
|
15
|
+
[types_1.EventTiming.Dinner]: { relation: "with", meal: "dinner" }
|
|
16
|
+
};
|
|
17
|
+
const MEAL_ORDER = {
|
|
18
|
+
breakfast: 0,
|
|
19
|
+
lunch: 1,
|
|
20
|
+
dinner: 2
|
|
21
|
+
};
|
|
22
|
+
const INFERABLE_DAILY_EVENT_TIMINGS = new Set([
|
|
23
|
+
types_1.EventTiming["Before Sleep"],
|
|
24
|
+
types_1.EventTiming["Before Breakfast"],
|
|
25
|
+
types_1.EventTiming["Before Lunch"],
|
|
26
|
+
types_1.EventTiming["Before Dinner"],
|
|
27
|
+
types_1.EventTiming["After Breakfast"],
|
|
28
|
+
types_1.EventTiming["After Lunch"],
|
|
29
|
+
types_1.EventTiming["After Dinner"],
|
|
30
|
+
types_1.EventTiming.Breakfast,
|
|
31
|
+
types_1.EventTiming.Lunch,
|
|
32
|
+
types_1.EventTiming.Dinner,
|
|
33
|
+
types_1.EventTiming.Morning,
|
|
34
|
+
types_1.EventTiming["Early Morning"],
|
|
35
|
+
types_1.EventTiming["Late Morning"],
|
|
36
|
+
types_1.EventTiming.Noon,
|
|
37
|
+
types_1.EventTiming.Afternoon,
|
|
38
|
+
types_1.EventTiming["Early Afternoon"],
|
|
39
|
+
types_1.EventTiming["Late Afternoon"],
|
|
40
|
+
types_1.EventTiming.Evening,
|
|
41
|
+
types_1.EventTiming["Early Evening"],
|
|
42
|
+
types_1.EventTiming["Late Evening"],
|
|
43
|
+
types_1.EventTiming.Night,
|
|
44
|
+
types_1.EventTiming.Wake,
|
|
45
|
+
types_1.EventTiming["After Sleep"]
|
|
46
|
+
]);
|
|
47
|
+
function uniqueValues(values) {
|
|
48
|
+
const seen = new Set();
|
|
49
|
+
const result = [];
|
|
50
|
+
for (const value of values) {
|
|
51
|
+
if (!seen.has(value)) {
|
|
52
|
+
seen.add(value);
|
|
53
|
+
result.push(value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
function getMealTimingGroup(when, options) {
|
|
59
|
+
if (!(options === null || options === void 0 ? void 0 : options.groupMealTimingsByRelation)) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const uniqueWhen = uniqueValues(when);
|
|
63
|
+
if (uniqueWhen.length < 2) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
let relation;
|
|
67
|
+
const meals = [];
|
|
68
|
+
const groupedCodes = [];
|
|
69
|
+
let sawFirstMeal = false;
|
|
70
|
+
for (let i = 0; i < uniqueWhen.length; i += 1) {
|
|
71
|
+
const code = uniqueWhen[i];
|
|
72
|
+
const detail = MEAL_TIMING_DETAILS[code];
|
|
73
|
+
if (!detail) {
|
|
74
|
+
if (sawFirstMeal) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (!sawFirstMeal) {
|
|
80
|
+
sawFirstMeal = true;
|
|
81
|
+
}
|
|
82
|
+
if (!relation) {
|
|
83
|
+
relation = detail.relation;
|
|
84
|
+
}
|
|
85
|
+
else if (relation !== detail.relation) {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
meals.push(detail.meal);
|
|
89
|
+
groupedCodes.push(code);
|
|
90
|
+
}
|
|
91
|
+
if (groupedCodes.length < 2) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
for (let i = 1; i < meals.length; i += 1) {
|
|
95
|
+
const current = meals[i];
|
|
96
|
+
let j = i - 1;
|
|
97
|
+
while (j >= 0 && MEAL_ORDER[meals[j]] > MEAL_ORDER[current]) {
|
|
98
|
+
meals[j + 1] = meals[j];
|
|
99
|
+
j -= 1;
|
|
100
|
+
}
|
|
101
|
+
meals[j + 1] = current;
|
|
102
|
+
}
|
|
103
|
+
if (!relation) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
relation,
|
|
108
|
+
meals,
|
|
109
|
+
codes: groupedCodes
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function inferDailyOccurrenceCount(input, options) {
|
|
113
|
+
var _a, _b, _c, _d;
|
|
114
|
+
if (!(options === null || options === void 0 ? void 0 : options.includeTimesPerDaySummary)) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
if (input.frequency !== undefined || input.frequencyMax !== undefined || input.timingCode) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
if (input.period !== undefined || input.periodMax !== undefined || input.periodUnit !== undefined) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
if (((_b = (_a = input.dayOfWeek) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
const uniqueWhen = uniqueValues((_c = input.when) !== null && _c !== void 0 ? _c : []);
|
|
127
|
+
for (let i = 0; i < uniqueWhen.length; i += 1) {
|
|
128
|
+
if (!INFERABLE_DAILY_EVENT_TIMINGS.has(uniqueWhen[i])) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const uniqueTimes = uniqueValues((_d = input.timeOfDay) !== null && _d !== void 0 ? _d : []);
|
|
133
|
+
const occurrences = uniqueWhen.length + uniqueTimes.length;
|
|
134
|
+
if (occurrences === 0) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
return occurrences;
|
|
138
|
+
}
|