ezmedicationinput 0.1.43 → 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/format.js DELETED
@@ -1,737 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatInternal = formatInternal;
4
- const types_1 = require("./types");
5
- const timing_summary_1 = require("./timing-summary");
6
- const ROUTE_SHORT = {
7
- [types_1.RouteCode["Oral route"]]: "PO",
8
- [types_1.RouteCode["Sublingual route"]]: "SL",
9
- [types_1.RouteCode["Buccal route"]]: "BUC",
10
- [types_1.RouteCode["Respiratory tract route (qualifier value)"]]: "INH",
11
- [types_1.RouteCode["Nasal route"]]: "IN",
12
- [types_1.RouteCode["Topical route"]]: "TOP",
13
- [types_1.RouteCode["Transdermal route"]]: "TD",
14
- [types_1.RouteCode["Subcutaneous route"]]: "SC",
15
- [types_1.RouteCode["Intramuscular route"]]: "IM",
16
- [types_1.RouteCode["Intravenous route"]]: "IV",
17
- [types_1.RouteCode["Per rectum"]]: "PR",
18
- [types_1.RouteCode["Per vagina"]]: "PV",
19
- [types_1.RouteCode["Ophthalmic route"]]: "OPH",
20
- [types_1.RouteCode["Intravitreal route (qualifier value)"]]: "IVT"
21
- };
22
- const WHEN_TEXT = {
23
- [types_1.EventTiming["Before Sleep"]]: "at bedtime",
24
- [types_1.EventTiming["Before Meal"]]: "before meals",
25
- [types_1.EventTiming["Before Breakfast"]]: "before breakfast",
26
- [types_1.EventTiming["Before Lunch"]]: "before lunch",
27
- [types_1.EventTiming["Before Dinner"]]: "before dinner",
28
- [types_1.EventTiming["After Meal"]]: "after meals",
29
- [types_1.EventTiming["After Breakfast"]]: "after breakfast",
30
- [types_1.EventTiming["After Lunch"]]: "after lunch",
31
- [types_1.EventTiming["After Dinner"]]: "after dinner",
32
- [types_1.EventTiming.Meal]: "with meals",
33
- [types_1.EventTiming.Breakfast]: "with breakfast",
34
- [types_1.EventTiming.Lunch]: "with lunch",
35
- [types_1.EventTiming.Dinner]: "with dinner",
36
- [types_1.EventTiming.Morning]: "in the morning",
37
- [types_1.EventTiming["Early Morning"]]: "in the early morning",
38
- [types_1.EventTiming["Late Morning"]]: "in the late morning",
39
- [types_1.EventTiming.Noon]: "at noon",
40
- [types_1.EventTiming.Afternoon]: "in the afternoon",
41
- [types_1.EventTiming["Early Afternoon"]]: "in the early afternoon",
42
- [types_1.EventTiming["Late Afternoon"]]: "in the late afternoon",
43
- [types_1.EventTiming.Evening]: "in the evening",
44
- [types_1.EventTiming["Early Evening"]]: "in the early evening",
45
- [types_1.EventTiming["Late Evening"]]: "in the late evening",
46
- [types_1.EventTiming.Night]: "at night",
47
- [types_1.EventTiming.Wake]: "after waking",
48
- [types_1.EventTiming["After Sleep"]]: "after sleep",
49
- [types_1.EventTiming.Immediate]: "immediately"
50
- };
51
- const DAY_NAMES = {
52
- mon: "Monday",
53
- tue: "Tuesday",
54
- wed: "Wednesday",
55
- thu: "Thursday",
56
- fri: "Friday",
57
- sat: "Saturday",
58
- sun: "Sunday"
59
- };
60
- const EN_TIMES_PER_DAY = {
61
- 1: "once daily",
62
- 2: "twice daily",
63
- 3: "three times daily",
64
- 4: "four times daily"
65
- };
66
- const DEFAULT_ROUTE_GRAMMAR = { verb: "Use" };
67
- const ROUTE_GRAMMAR = {
68
- [types_1.RouteCode["Oral route"]]: { verb: "Take", routePhrase: "by mouth" },
69
- [types_1.RouteCode["Ophthalmic route"]]: {
70
- verb: "Instill",
71
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "in the eye"),
72
- sitePreposition: "in"
73
- },
74
- [types_1.RouteCode["Intravitreal route (qualifier value)"]]: {
75
- verb: "Inject",
76
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "into the eye"),
77
- sitePreposition: "into"
78
- },
79
- [types_1.RouteCode["Per rectum"]]: {
80
- verb: "Use",
81
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "rectally"),
82
- sitePreposition: "into"
83
- },
84
- [types_1.RouteCode["Topical route"]]: {
85
- verb: "Apply",
86
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "topically"),
87
- sitePreposition: "to"
88
- },
89
- [types_1.RouteCode["Transdermal route"]]: {
90
- verb: "Apply",
91
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "transdermally"),
92
- sitePreposition: "to"
93
- },
94
- [types_1.RouteCode["Subcutaneous route"]]: {
95
- verb: "Inject",
96
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "subcutaneously"),
97
- sitePreposition: "into"
98
- },
99
- [types_1.RouteCode["Intramuscular route"]]: {
100
- verb: "Inject",
101
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "intramuscularly"),
102
- sitePreposition: "into"
103
- },
104
- [types_1.RouteCode["Intravenous route"]]: {
105
- verb: "Inject",
106
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "intravenously"),
107
- sitePreposition: "into"
108
- },
109
- [types_1.RouteCode["Nasal route"]]: {
110
- verb: "Use",
111
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "via nasal route"),
112
- sitePreposition: "into"
113
- },
114
- [types_1.RouteCode["Respiratory tract route (qualifier value)"]]: {
115
- verb: "Use",
116
- routePhrase: ({ hasSite }) => (hasSite ? undefined : "via inhalation"),
117
- sitePreposition: "into"
118
- }
119
- };
120
- function grammarFromRouteText(text) {
121
- if (!text) {
122
- return undefined;
123
- }
124
- const normalized = text.trim().toLowerCase();
125
- if (!normalized) {
126
- return undefined;
127
- }
128
- if (normalized.includes("mouth") || normalized.includes("oral")) {
129
- return ROUTE_GRAMMAR[types_1.RouteCode["Oral route"]];
130
- }
131
- if (normalized.includes("ophthalm")) {
132
- return ROUTE_GRAMMAR[types_1.RouteCode["Ophthalmic route"]];
133
- }
134
- if (normalized.includes("intravitreal")) {
135
- return ROUTE_GRAMMAR[types_1.RouteCode["Intravitreal route (qualifier value)"]];
136
- }
137
- if (normalized.includes("topical")) {
138
- return ROUTE_GRAMMAR[types_1.RouteCode["Topical route"]];
139
- }
140
- if (normalized.includes("transdermal")) {
141
- return ROUTE_GRAMMAR[types_1.RouteCode["Transdermal route"]];
142
- }
143
- if (normalized.includes("subcutaneous") || normalized === "sc" || normalized === "sq") {
144
- return ROUTE_GRAMMAR[types_1.RouteCode["Subcutaneous route"]];
145
- }
146
- if (normalized.includes("intramuscular") || normalized === "im") {
147
- return ROUTE_GRAMMAR[types_1.RouteCode["Intramuscular route"]];
148
- }
149
- if (normalized.includes("intravenous") || normalized === "iv") {
150
- return ROUTE_GRAMMAR[types_1.RouteCode["Intravenous route"]];
151
- }
152
- if (normalized.includes("rectal") || normalized.includes("rectum")) {
153
- return ROUTE_GRAMMAR[types_1.RouteCode["Per rectum"]];
154
- }
155
- if (normalized.includes("nasal")) {
156
- return ROUTE_GRAMMAR[types_1.RouteCode["Nasal route"]];
157
- }
158
- if (normalized.includes("inhal")) {
159
- return ROUTE_GRAMMAR[types_1.RouteCode["Respiratory tract route (qualifier value)"]];
160
- }
161
- return undefined;
162
- }
163
- function resolveRouteGrammar(internal) {
164
- var _a, _b;
165
- if (internal.routeCode && ROUTE_GRAMMAR[internal.routeCode]) {
166
- return (_a = ROUTE_GRAMMAR[internal.routeCode]) !== null && _a !== void 0 ? _a : DEFAULT_ROUTE_GRAMMAR;
167
- }
168
- return (_b = grammarFromRouteText(internal.routeText)) !== null && _b !== void 0 ? _b : DEFAULT_ROUTE_GRAMMAR;
169
- }
170
- function pluralize(unit, value) {
171
- if (Math.abs(value) === 1) {
172
- if (unit === "tab")
173
- return "tablet";
174
- if (unit === "cap")
175
- return "capsule";
176
- return unit;
177
- }
178
- if (unit === "tab" || unit === "tablet")
179
- return "tablets";
180
- if (unit === "cap" || unit === "capsule")
181
- return "capsules";
182
- if (unit === "mL")
183
- return "mL";
184
- if (unit === "mg")
185
- return "mg";
186
- if (unit === "puff")
187
- return value === 1 ? "puff" : "puffs";
188
- if (unit === "patch")
189
- return value === 1 ? "patch" : "patches";
190
- if (unit === "drop")
191
- return value === 1 ? "drop" : "drops";
192
- if (unit === "suppository")
193
- return value === 1 ? "suppository" : "suppositories";
194
- return unit;
195
- }
196
- function describeFrequency(internal) {
197
- const { frequency, frequencyMax, period, periodMax, periodUnit, timingCode } = internal;
198
- if (frequency !== undefined &&
199
- frequencyMax !== undefined &&
200
- periodUnit === types_1.FhirPeriodUnit.Day &&
201
- (!period || period === 1)) {
202
- if (frequency === 1 && frequencyMax === 1) {
203
- return "once daily";
204
- }
205
- if (frequency === 1 && frequencyMax === 2) {
206
- return "one to two times daily";
207
- }
208
- return `${stripTrailingZero(frequency)} to ${stripTrailingZero(frequencyMax)} times daily`;
209
- }
210
- if (frequency && periodUnit === types_1.FhirPeriodUnit.Day && (!period || period === 1)) {
211
- const dailyText = EN_TIMES_PER_DAY[frequency];
212
- if (dailyText) {
213
- return dailyText;
214
- }
215
- return `${stripTrailingZero(frequency)} times daily`;
216
- }
217
- if (periodUnit === types_1.FhirPeriodUnit.Hour && period) {
218
- if (periodMax && periodMax !== period) {
219
- return `every ${stripTrailingZero(period)} to ${stripTrailingZero(periodMax)} hours`;
220
- }
221
- return `every ${stripTrailingZero(period)} hour${period === 1 ? "" : "s"}`;
222
- }
223
- if (periodUnit === types_1.FhirPeriodUnit.Day && period && period !== 1) {
224
- if (period === 2 && (!periodMax || periodMax === 2)) {
225
- return "every other day";
226
- }
227
- if (periodMax && periodMax !== period) {
228
- return `every ${stripTrailingZero(period)} to ${stripTrailingZero(periodMax)} days`;
229
- }
230
- return `every ${stripTrailingZero(period)} days`;
231
- }
232
- if (periodUnit === types_1.FhirPeriodUnit.Week && period) {
233
- if (period === 1 && (!periodMax || periodMax === 1)) {
234
- return "once weekly";
235
- }
236
- if (periodMax && periodMax !== period) {
237
- return `every ${stripTrailingZero(period)} to ${stripTrailingZero(periodMax)} weeks`;
238
- }
239
- return `every ${stripTrailingZero(period)} weeks`;
240
- }
241
- if (periodUnit === types_1.FhirPeriodUnit.Month && period) {
242
- if (period === 1 && (!periodMax || periodMax === 1)) {
243
- return "once monthly";
244
- }
245
- if (periodMax && periodMax !== period) {
246
- return `every ${stripTrailingZero(period)} to ${stripTrailingZero(periodMax)} months`;
247
- }
248
- return `every ${stripTrailingZero(period)} months`;
249
- }
250
- if (timingCode) {
251
- if (timingCode === "WK") {
252
- return "once weekly";
253
- }
254
- if (timingCode === "MO") {
255
- return "once monthly";
256
- }
257
- const map = {
258
- BID: "twice daily",
259
- TID: "three times daily",
260
- QID: "four times daily",
261
- QD: "once daily",
262
- QOD: "every other day",
263
- Q6H: "every 6 hours",
264
- Q8H: "every 8 hours"
265
- };
266
- if (map[timingCode]) {
267
- return map[timingCode];
268
- }
269
- }
270
- if (frequency && periodUnit === undefined && period === undefined) {
271
- if (frequency === 1)
272
- return "once";
273
- return `${stripTrailingZero(frequency)} times`;
274
- }
275
- return undefined;
276
- }
277
- function describeFrequencyCount(count) {
278
- if (!count || count <= 0) {
279
- return undefined;
280
- }
281
- const dailyText = EN_TIMES_PER_DAY[count];
282
- if (dailyText) {
283
- return dailyText;
284
- }
285
- return `${stripTrailingZero(count)} times daily`;
286
- }
287
- function formatDoseShort(internal) {
288
- if (internal.doseRange) {
289
- const { low, high } = internal.doseRange;
290
- const base = `${stripTrailingZero(low)}-${stripTrailingZero(high)}`;
291
- if (internal.unit) {
292
- return `${base} ${internal.unit}`;
293
- }
294
- return base;
295
- }
296
- if (internal.dose !== undefined) {
297
- const dosePart = internal.unit
298
- ? `${stripTrailingZero(internal.dose)} ${internal.unit}`
299
- : `${stripTrailingZero(internal.dose)}`;
300
- return dosePart.trim();
301
- }
302
- return undefined;
303
- }
304
- function formatDoseLong(internal) {
305
- if (internal.doseRange) {
306
- const { low, high } = internal.doseRange;
307
- if (internal.unit) {
308
- return `${stripTrailingZero(low)} to ${stripTrailingZero(high)} ${pluralize(internal.unit, high)}`;
309
- }
310
- return `${stripTrailingZero(low)} to ${stripTrailingZero(high)}`;
311
- }
312
- if (internal.dose !== undefined) {
313
- if (internal.unit) {
314
- return `${stripTrailingZero(internal.dose)} ${pluralize(internal.unit, internal.dose)}`;
315
- }
316
- return `${stripTrailingZero(internal.dose)}`;
317
- }
318
- return undefined;
319
- }
320
- function summarizeMealTimingGroup(group) {
321
- let relationText = "with";
322
- if (group.relation === "before") {
323
- relationText = "before";
324
- }
325
- else if (group.relation === "after") {
326
- relationText = "after";
327
- }
328
- return `${relationText} ${joinWithAnd(group.meals)}`;
329
- }
330
- function collectWhenPhrases(internal, options) {
331
- var _a, _b;
332
- if (!internal.when.length) {
333
- return [];
334
- }
335
- const unique = [];
336
- const seen = new Set();
337
- let hasSpecificAfter = false;
338
- let hasSpecificBefore = false;
339
- let hasSpecificWith = false;
340
- for (const code of internal.when) {
341
- if (!seen.has(code)) {
342
- seen.add(code);
343
- unique.push(code);
344
- if (code === types_1.EventTiming["After Breakfast"] ||
345
- code === types_1.EventTiming["After Lunch"] ||
346
- code === types_1.EventTiming["After Dinner"]) {
347
- hasSpecificAfter = true;
348
- }
349
- if (code === types_1.EventTiming["Before Breakfast"] ||
350
- code === types_1.EventTiming["Before Lunch"] ||
351
- code === types_1.EventTiming["Before Dinner"]) {
352
- hasSpecificBefore = true;
353
- }
354
- if (code === types_1.EventTiming.Breakfast ||
355
- code === types_1.EventTiming.Lunch ||
356
- code === types_1.EventTiming.Dinner) {
357
- hasSpecificWith = true;
358
- }
359
- }
360
- }
361
- const filtered = [];
362
- for (let i = 0; i < unique.length; i += 1) {
363
- const code = unique[i];
364
- if (code === types_1.EventTiming["After Meal"] && hasSpecificAfter) {
365
- continue;
366
- }
367
- if (code === types_1.EventTiming["Before Meal"] && hasSpecificBefore) {
368
- continue;
369
- }
370
- if (code === types_1.EventTiming.Meal && hasSpecificWith) {
371
- continue;
372
- }
373
- filtered.push(code);
374
- }
375
- const mealGroup = (0, timing_summary_1.getMealTimingGroup)(filtered, options);
376
- if (mealGroup) {
377
- const groupedCodes = new Set(mealGroup.codes);
378
- const phrases = [];
379
- let insertedGroup = false;
380
- for (let i = 0; i < filtered.length; i += 1) {
381
- const code = filtered[i];
382
- if (groupedCodes.has(code)) {
383
- if (!insertedGroup) {
384
- phrases.push(summarizeMealTimingGroup(mealGroup));
385
- insertedGroup = true;
386
- }
387
- continue;
388
- }
389
- const text = (_a = WHEN_TEXT[code]) !== null && _a !== void 0 ? _a : code;
390
- if (text) {
391
- phrases.push(text);
392
- }
393
- }
394
- return phrases;
395
- }
396
- const phrases = [];
397
- for (let i = 0; i < filtered.length; i += 1) {
398
- const text = (_b = WHEN_TEXT[filtered[i]]) !== null && _b !== void 0 ? _b : filtered[i];
399
- if (text) {
400
- phrases.push(text);
401
- }
402
- }
403
- return phrases;
404
- }
405
- function joinWithAnd(parts) {
406
- if (!parts.length) {
407
- return "";
408
- }
409
- if (parts.length === 1) {
410
- return parts[0];
411
- }
412
- if (parts.length === 2) {
413
- return `${parts[0]} and ${parts[1]}`;
414
- }
415
- return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
416
- }
417
- function combineFrequencyAndEvents(frequency, events) {
418
- if (!frequency) {
419
- if (!events.length) {
420
- return {};
421
- }
422
- return { event: joinWithAnd(events) };
423
- }
424
- if (!events.length) {
425
- return { frequency };
426
- }
427
- if (events.length === 1 && events[0] === "at bedtime") {
428
- const lowerFrequency = frequency.toLowerCase();
429
- if (lowerFrequency === "twice daily" || lowerFrequency === "three times daily" || lowerFrequency === "four times daily") {
430
- return { frequency: `${frequency} and ${events[0]}` };
431
- }
432
- }
433
- return { frequency, event: joinWithAnd(events) };
434
- }
435
- function buildRoutePhrase(internal, grammar, hasSite) {
436
- var _a;
437
- if (typeof grammar.routePhrase === "function") {
438
- return grammar.routePhrase({ hasSite, internal });
439
- }
440
- if (typeof grammar.routePhrase === "string") {
441
- return grammar.routePhrase;
442
- }
443
- const text = (_a = internal.routeText) === null || _a === void 0 ? void 0 : _a.trim();
444
- if (!text) {
445
- return undefined;
446
- }
447
- const normalized = text.toLowerCase();
448
- if (normalized.startsWith("by ") || normalized.startsWith("per ") || normalized.startsWith("via ")) {
449
- return text;
450
- }
451
- if (normalized === "oral") {
452
- return "by mouth";
453
- }
454
- if (normalized === "intravenous") {
455
- return "intravenously";
456
- }
457
- if (normalized === "intramuscular") {
458
- return "intramuscularly";
459
- }
460
- if (normalized === "subcutaneous") {
461
- return "subcutaneously";
462
- }
463
- if (normalized === "topical") {
464
- return "topically";
465
- }
466
- if (normalized === "transdermal") {
467
- return "transdermally";
468
- }
469
- if (normalized === "intranasal" || normalized === "nasal") {
470
- return "via nasal route";
471
- }
472
- if (normalized.includes("inhal")) {
473
- return "via inhalation";
474
- }
475
- return `via ${text}`;
476
- }
477
- function formatSite(internal, grammar) {
478
- var _a;
479
- const text = (_a = internal.siteText) === null || _a === void 0 ? void 0 : _a.trim();
480
- if (!text) {
481
- return undefined;
482
- }
483
- const lower = text.toLowerCase();
484
- if (internal.routeCode === types_1.RouteCode["Per rectum"]) {
485
- if (lower === "rectum" || lower === "rectal") {
486
- return undefined;
487
- }
488
- }
489
- let preposition = grammar.sitePreposition;
490
- if (!preposition) {
491
- if (lower.includes("eye")) {
492
- preposition = "in";
493
- }
494
- else if (lower.includes("nostril") || lower.includes("nose")) {
495
- preposition = "into";
496
- }
497
- else if (lower.includes("lung") || lower.includes("airway") || lower.includes("bronch")) {
498
- preposition = "into";
499
- }
500
- else if (lower.includes("ear")) {
501
- preposition = "in";
502
- }
503
- else if (/(skin|arm|leg|thigh|abdomen|shoulder|hand|foot|cheek|forearm|back|buttock|hip|face|hair|scalp|forehead|chin|neck)/.test(lower)) {
504
- preposition = "to";
505
- }
506
- else {
507
- preposition = "at";
508
- }
509
- }
510
- const noun = formatSiteNoun(text, preposition);
511
- return `${preposition} ${noun}`.trim();
512
- }
513
- function formatSiteNoun(site, preposition) {
514
- const trimmed = site.trim();
515
- const lower = trimmed.toLowerCase();
516
- const skipArticlePrefixes = [
517
- "the ",
518
- "both ",
519
- "each ",
520
- "either ",
521
- "every ",
522
- "all ",
523
- "bilateral ",
524
- ];
525
- for (const prefix of skipArticlePrefixes) {
526
- if (lower.startsWith(prefix)) {
527
- return trimmed;
528
- }
529
- }
530
- const needsArticle = /^(left|right|upper|lower|inner|outer|mid|middle|posterior|anterior|proximal|distal|medial|lateral|dorsal|ventral)\b/.test(lower);
531
- if (needsArticle || preposition === "at") {
532
- return `the ${trimmed}`;
533
- }
534
- if (/(eye|nostril|ear|arm|leg|thigh|abdomen|hand|foot|cheek|skin|back)/.test(lower)) {
535
- return `the ${trimmed}`;
536
- }
537
- return `the ${trimmed}`;
538
- }
539
- function describeDayOfWeek(internal) {
540
- if (!internal.dayOfWeek.length) {
541
- return undefined;
542
- }
543
- const days = internal.dayOfWeek.map((d) => { var _a; return (_a = DAY_NAMES[d]) !== null && _a !== void 0 ? _a : d; });
544
- if (!days.length) {
545
- return undefined;
546
- }
547
- return `on ${joinWithAnd(days)}`;
548
- }
549
- function formatInternal(internal, style, localization, options) {
550
- const defaults = {
551
- short: formatShort(internal),
552
- long: formatLong(internal, options)
553
- };
554
- if (!localization) {
555
- return defaults[style];
556
- }
557
- const formatDefault = (target) => defaults[target];
558
- if (style === "short" && localization.formatShort) {
559
- const context = {
560
- style: "short",
561
- internal,
562
- defaultText: defaults.short,
563
- groupMealTimingsByRelation: Boolean(options === null || options === void 0 ? void 0 : options.groupMealTimingsByRelation),
564
- includeTimesPerDaySummary: Boolean(options === null || options === void 0 ? void 0 : options.includeTimesPerDaySummary),
565
- formatDefault
566
- };
567
- return localization.formatShort(context);
568
- }
569
- if (style === "long" && localization.formatLong) {
570
- const context = {
571
- style: "long",
572
- internal,
573
- defaultText: defaults.long,
574
- groupMealTimingsByRelation: Boolean(options === null || options === void 0 ? void 0 : options.groupMealTimingsByRelation),
575
- includeTimesPerDaySummary: Boolean(options === null || options === void 0 ? void 0 : options.includeTimesPerDaySummary),
576
- formatDefault
577
- };
578
- return localization.formatLong(context);
579
- }
580
- return defaults[style];
581
- }
582
- function formatShort(internal) {
583
- var _a;
584
- const parts = [];
585
- const dosePart = formatDoseShort(internal);
586
- if (dosePart) {
587
- parts.push(dosePart);
588
- }
589
- if (internal.routeCode) {
590
- const short = ROUTE_SHORT[internal.routeCode];
591
- if (short) {
592
- parts.push(short);
593
- }
594
- else if (internal.routeText) {
595
- parts.push(internal.routeText);
596
- }
597
- }
598
- else if (internal.routeText) {
599
- parts.push(internal.routeText);
600
- }
601
- if (internal.timingCode) {
602
- parts.push(internal.timingCode);
603
- }
604
- else if (internal.frequency !== undefined &&
605
- internal.frequencyMax !== undefined &&
606
- internal.periodUnit === types_1.FhirPeriodUnit.Day &&
607
- (!internal.period || internal.period === 1)) {
608
- parts.push(`${stripTrailingZero(internal.frequency)}-${stripTrailingZero(internal.frequencyMax)}x/d`);
609
- }
610
- else if (internal.frequency &&
611
- internal.periodUnit === types_1.FhirPeriodUnit.Day &&
612
- (!internal.period || internal.period === 1)) {
613
- parts.push(`${stripTrailingZero(internal.frequency)}x/d`);
614
- }
615
- else if (internal.period && internal.periodUnit) {
616
- const base = stripTrailingZero(internal.period);
617
- const qualifier = internal.periodMax && internal.periodMax !== internal.period
618
- ? `${base}-${stripTrailingZero(internal.periodMax)}`
619
- : base;
620
- parts.push(`Q${qualifier}${internal.periodUnit.toUpperCase()}`);
621
- }
622
- if (internal.when.length) {
623
- parts.push(internal.when.join(" "));
624
- }
625
- if (internal.dayOfWeek.length) {
626
- parts.push(internal.dayOfWeek
627
- .map((d) => d.charAt(0).toUpperCase() + d.slice(1, 3))
628
- .join(","));
629
- }
630
- if ((_a = internal.timeOfDay) === null || _a === void 0 ? void 0 : _a.length) {
631
- parts.push(internal.timeOfDay.map(t => t.slice(0, 5)).join(","));
632
- }
633
- if (internal.count !== undefined) {
634
- parts.push(`x${stripTrailingZero(internal.count)}`);
635
- }
636
- if (internal.asNeeded) {
637
- if (internal.asNeededReason) {
638
- parts.push(`PRN ${internal.asNeededReason}`);
639
- }
640
- else {
641
- parts.push("PRN");
642
- }
643
- }
644
- return parts.filter(Boolean).join(" ");
645
- }
646
- function formatLong(internal, options) {
647
- var _a, _b, _c;
648
- const grammar = resolveRouteGrammar(internal);
649
- const dosePart = (_a = formatDoseLong(internal)) !== null && _a !== void 0 ? _a : "the medication";
650
- const sitePart = formatSite(internal, grammar);
651
- const routePart = buildRoutePhrase(internal, grammar, Boolean(sitePart));
652
- const frequencyPart = (_b = describeFrequency(internal)) !== null && _b !== void 0 ? _b : describeFrequencyCount((0, timing_summary_1.inferDailyOccurrenceCount)(internal, options));
653
- const eventParts = collectWhenPhrases(internal, options);
654
- if ((_c = internal.timeOfDay) === null || _c === void 0 ? void 0 : _c.length) {
655
- const timeStrings = [];
656
- for (const time of internal.timeOfDay) {
657
- const parts = time.split(":");
658
- const h = parseInt(parts[0], 10);
659
- const m = parseInt(parts[1], 10);
660
- const isAm = h < 12;
661
- const displayH = h % 12 || 12;
662
- const displayM = m < 10 ? `0${m}` : `${m}`;
663
- timeStrings.push(`${displayH}:${displayM}${isAm ? " am" : " pm"}`);
664
- }
665
- if (timeStrings.length > 0) {
666
- eventParts.push(`at ${timeStrings.join(", ")}`);
667
- }
668
- }
669
- const timing = combineFrequencyAndEvents(frequencyPart, eventParts);
670
- const dayPart = describeDayOfWeek(internal);
671
- const countPart = internal.count
672
- ? `for ${stripTrailingZero(internal.count)} ${internal.count === 1 ? "dose" : "doses"}`
673
- : undefined;
674
- const asNeededPart = internal.asNeeded
675
- ? internal.asNeededReason
676
- ? `as needed for ${internal.asNeededReason}`
677
- : "as needed"
678
- : undefined;
679
- const segments = [dosePart];
680
- if (routePart) {
681
- segments.push(routePart);
682
- }
683
- if (timing.frequency) {
684
- segments.push(timing.frequency);
685
- }
686
- if (timing.event) {
687
- segments.push(timing.event);
688
- }
689
- if (dayPart) {
690
- segments.push(dayPart);
691
- }
692
- if (countPart) {
693
- segments.push(countPart);
694
- }
695
- if (asNeededPart) {
696
- segments.push(asNeededPart);
697
- }
698
- if (sitePart) {
699
- segments.push(sitePart);
700
- }
701
- const body = segments.filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
702
- if (!body) {
703
- const instructionText = formatAdditionalInstructions(internal);
704
- if (!instructionText) {
705
- return `${grammar.verb}.`;
706
- }
707
- return `${grammar.verb}. ${instructionText}`.trim();
708
- }
709
- const instructionText = formatAdditionalInstructions(internal);
710
- const baseSentence = `${grammar.verb} ${body}.`;
711
- return instructionText ? `${baseSentence} ${instructionText}` : baseSentence;
712
- }
713
- function formatAdditionalInstructions(internal) {
714
- var _a;
715
- if (!((_a = internal.additionalInstructions) === null || _a === void 0 ? void 0 : _a.length)) {
716
- return undefined;
717
- }
718
- const phrases = internal.additionalInstructions
719
- .map((instruction) => { var _a; return instruction.text || ((_a = instruction.coding) === null || _a === void 0 ? void 0 : _a.display); })
720
- .filter((text) => Boolean(text))
721
- .map((text) => text.trim())
722
- .filter((text) => text.length > 0);
723
- if (!phrases.length) {
724
- return undefined;
725
- }
726
- return phrases
727
- .map((phrase) => (/[.!?]$/.test(phrase) ? phrase : `${phrase}.`))
728
- .join(" ")
729
- .trim();
730
- }
731
- function stripTrailingZero(value) {
732
- const text = value.toString();
733
- if (text.includes(".")) {
734
- return text.replace(/\.0+$/, "").replace(/0+$/, "");
735
- }
736
- return text;
737
- }