fhir-dosage-utils 1.1.0 → 1.2.0

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/module.js CHANGED
@@ -56,43 +56,77 @@ function $7cf24331c38efaf1$export$7de3722258b87db9({ config: config, dos: dos, i
56
56
  }
57
57
 
58
58
 
59
- // types
60
- function $465e6e43e1df9717$export$4cd16590995531a9({ dos: dos, config: config, i18next: i18next }) {
61
- // If empty, return undefined
62
- if (dos.doseAndRate === undefined) return undefined;
63
- // Find the first entry that match criteria
64
- let doseRange = dos.doseAndRate.find((s)=>s.doseRange !== undefined);
65
- // If not found, skip
66
- if (doseRange === undefined) return undefined;
67
- let low = doseRange.doseRange.low?.value;
68
- let high = doseRange.doseRange.high?.value;
69
- let quantityUnit = doseRange.doseRange.high !== undefined ? doseRange.doseRange.high : doseRange.doseRange.low;
70
- // quantity unit
71
- let unit = config.fromFHIRQuantityUnitToString({
59
+ // Utility function
60
+ // Quantity unit to string
61
+ function $73073500f7f08f16$var$transformQuantityUnitToString(i18next, quantity, config) {
62
+ let quantityValue = quantity.value;
63
+ // If common units from HL7, do the job
64
+ if (quantity.system === "http://hl7.org/fhir/ValueSet/duration-units") {
65
+ let code = quantity.code;
66
+ return i18next.t(`unitsOfTime:withoutCount.${code}`, {
67
+ count: quantityValue
68
+ });
69
+ } else // otherwise, it is UCUM, ... so let the user do the job
70
+ return config.fromFHIRQuantityUnitToString({
72
71
  language: config.language,
73
- quantity: quantityUnit
72
+ quantity: quantity
74
73
  });
75
- // Three cases
76
- // 1. Both low & high are present
77
- if (high !== undefined && low !== undefined) return i18next.t("fields.doseRange.lowAndHigh", {
78
- low: low,
79
- high: high,
74
+ }
75
+ function $73073500f7f08f16$export$be17d167ed50d870({ range: range, config: config, i18next: i18next }) {
76
+ // Extract params
77
+ const { low: low, high: high } = range;
78
+ const lowValue = low?.value;
79
+ const highValue = high?.value;
80
+ // prepare unit display
81
+ let quantityUnit = high || low;
82
+ let hasUnit = quantityUnit?.unit !== undefined || quantityUnit?.code !== undefined;
83
+ // Four cases
84
+ // 1. If we have a empty object, return undefined
85
+ if (lowValue === undefined && highValue === undefined) return undefined;
86
+ // quantity unit
87
+ let unit = hasUnit ? $73073500f7f08f16$var$transformQuantityUnitToString(i18next, quantityUnit, config) : "";
88
+ let technicalKey = hasUnit ? "withUnit" : "withoutUnit";
89
+ // 2. Both low & high are present
90
+ if (lowValue !== undefined && highValue !== undefined) return i18next.t(`amount.range.${technicalKey}.lowAndHigh`, {
91
+ low: lowValue,
92
+ high: highValue,
80
93
  unit: unit
81
94
  });
82
- // 2. Only high is present
83
- if (high !== undefined) return i18next.t("fields.doseRange.onlyHigh", {
84
- high: high,
95
+ // 3. Only high is present
96
+ if (highValue !== undefined) return i18next.t(`amount.range.${technicalKey}.onlyHigh`, {
97
+ high: highValue,
85
98
  unit: unit
86
99
  });
87
- // 3. Only low is present
100
+ // 4. Only low is present
88
101
  // Warning, this case is kind dangerous and clinically unsafe so minimal effort on this ...
89
- return i18next.t("fields.doseRange.onlyLow", {
90
- low: low,
102
+ return i18next.t(`amount.range.${technicalKey}.onlyLow`, {
103
+ low: lowValue,
91
104
  unit: unit
92
105
  });
93
106
  }
94
107
 
95
108
 
109
+ function $465e6e43e1df9717$export$4cd16590995531a9({ dos: dos, config: config, i18next: i18next }) {
110
+ // If empty, return undefined
111
+ if (dos.doseAndRate === undefined) return undefined;
112
+ // Find the first entry that match criteria
113
+ let doseRange = dos.doseAndRate.find((s)=>s.doseRange !== undefined);
114
+ // If not found, skip
115
+ if (doseRange === undefined) return undefined;
116
+ // Turn range into a text
117
+ const text = (0, $73073500f7f08f16$export$be17d167ed50d870)({
118
+ range: doseRange.doseRange,
119
+ config: config,
120
+ i18next: i18next
121
+ });
122
+ // Reject if empty
123
+ if (text === undefined) return undefined;
124
+ return i18next.t("fields.doseRange", {
125
+ rangeText: text
126
+ });
127
+ }
128
+
129
+
96
130
  // types
97
131
  function $bbbd1877a73b7a2b$export$4e24ef8c7997cc56({ dos: dos, config: config, i18next: i18next }) {
98
132
  // If empty, return undefined
@@ -115,72 +149,91 @@ function $bbbd1877a73b7a2b$export$4e24ef8c7997cc56({ dos: dos, config: config, i
115
149
  }
116
150
 
117
151
 
118
- // types
152
+ // Utility function
153
+
119
154
  function $09db039855b6d015$export$d3dd7d3522271dba({ dos: dos, config: config, i18next: i18next }) {
120
155
  // If empty, return undefined
121
156
  if (dos.doseAndRate === undefined) return undefined;
122
157
  // Find the first entry that match criteria
123
- let rateRange = dos.doseAndRate.find((s)=>s.rateRange !== undefined);
158
+ let doseAndRate = dos.doseAndRate.find((s)=>s.rateRange !== undefined);
124
159
  // If not found, skip
125
- if (rateRange === undefined) return undefined;
126
- // low / high
127
- let low = rateRange.rateRange.low;
128
- let high = rateRange.rateRange.high;
129
- let quantityLow = low?.value;
130
- let quantityHigh = high?.value;
131
- // quantity unit
132
- let unit = config.fromFHIRQuantityUnitToString({
133
- language: config.language,
134
- quantity: high || low
160
+ if (doseAndRate === undefined) return undefined;
161
+ // Turn range into a text
162
+ const rangeText = (0, $73073500f7f08f16$export$be17d167ed50d870)({
163
+ range: doseAndRate.rateRange,
164
+ config: config,
165
+ i18next: i18next
135
166
  });
136
- // Three cases
137
- // 1. Both low & high are present
138
- if (quantityHigh !== undefined && quantityLow !== undefined) return i18next.t("fields.rateRange.lowAndHigh", {
139
- low: quantityLow,
140
- high: quantityHigh,
141
- unit: unit
142
- });
143
- // 2. Only high is present
144
- if (quantityHigh !== undefined) return i18next.t("fields.rateRange.onlyHigh", {
145
- high: quantityHigh,
146
- unit: unit
147
- });
148
- // 3. Only low is present
149
- // Warning, this case is kind dangerous and clinically unsafe so minimal effort on this ...
150
- return i18next.t("fields.rateRange.onlyLow", {
151
- low: quantityLow,
152
- unit: unit
167
+ // Reject if empty
168
+ if (rangeText === undefined) return undefined;
169
+ // return the final string
170
+ return i18next.t("fields.rateRange", {
171
+ rangeText: rangeText
153
172
  });
154
173
  }
155
174
 
156
175
 
157
- // types
158
- function $520e93a43a731e9b$export$b71cfd2510242de2({ dos: dos, config: config, i18next: i18next }) {
159
- // If empty, return undefined
160
- if (dos.doseAndRate === undefined) return undefined;
161
- // Find the first entry that match criteria
162
- let rateRatio = dos.doseAndRate.find((s)=>s.rateRatio !== undefined);
163
- // If not found, skip
164
- if (rateRatio === undefined) return undefined;
165
- // num / dem
166
- let numerator = rateRatio.rateRatio.numerator;
167
- let denominator = rateRatio.rateRatio.denominator;
168
- let quantityNum = numerator?.value || 1;
169
- let quantityDenom = denominator?.value || 1;
176
+ // Quantity has an unit ?
177
+ function $eb371feb739abc89$var$hasUnit(quantity) {
178
+ return (quantity?.unit || quantity?.code) !== undefined;
179
+ }
180
+ function $eb371feb739abc89$export$fdc6e9cbd31555fb({ ratio: ratio, config: config, i18next: i18next }) {
181
+ // Extract params
182
+ const { denominator: denominator, numerator: numerator } = ratio;
170
183
  // units as text
171
- let numeratorUnit = numerator !== undefined ? config.fromFHIRQuantityUnitToString({
184
+ let numeratorUnit = $eb371feb739abc89$var$hasUnit(numerator) ? config.fromFHIRQuantityUnitToString({
172
185
  language: config.language,
173
186
  quantity: numerator
174
- }) : "";
175
- let denominatorUnit = denominator !== undefined ? config.fromFHIRQuantityUnitToString({
187
+ }) : undefined;
188
+ let denominatorUnit = $eb371feb739abc89$var$hasUnit(denominator) ? config.fromFHIRQuantityUnitToString({
176
189
  language: config.language,
177
190
  quantity: denominator
178
- }) : "";
179
- return i18next.t("fields.rateRatio.rateRatio", {
180
- count: quantityDenom,
181
- quantityNumerator: quantityNum,
182
- numeratorUnit: numeratorUnit,
183
- denominatorUnit: denominatorUnit
191
+ }) : undefined;
192
+ // quantity
193
+ let quantityNumerator = numerator?.value;
194
+ let quantityDenominator = denominator?.value;
195
+ // Collect
196
+ const parts = [];
197
+ // Deal with numerator first
198
+ if (quantityNumerator !== undefined) {
199
+ let technicalKey = numeratorUnit !== undefined ? "withUnit" : "withoutUnit";
200
+ const numeratorString = i18next.t(`amount.ratio.${technicalKey}.numerator`, {
201
+ count: quantityNumerator,
202
+ numeratorUnit: numeratorUnit
203
+ });
204
+ parts.push(numeratorString);
205
+ }
206
+ // Deal with denominator
207
+ if (quantityDenominator !== undefined) {
208
+ let technicalKey = denominatorUnit !== undefined ? "withUnit" : "withoutUnit";
209
+ const denominatorString = i18next.t(`amount.ratio.${technicalKey}.denominator`, {
210
+ count: quantityDenominator,
211
+ denominatorUnit: denominatorUnit
212
+ });
213
+ parts.push(denominatorString);
214
+ }
215
+ // Concatenate the result
216
+ if (parts.length === 0) return undefined;
217
+ else return parts.join(" ");
218
+ }
219
+
220
+
221
+ function $520e93a43a731e9b$export$b71cfd2510242de2({ dos: dos, config: config, i18next: i18next }) {
222
+ // If empty, return undefined
223
+ if (dos.doseAndRate === undefined) return undefined;
224
+ // Find the first entry that match criteria
225
+ let doseAndRate = dos.doseAndRate.find((s)=>s.rateRatio !== undefined);
226
+ // If not found, skip
227
+ if (doseAndRate === undefined) return undefined;
228
+ // Turn ratio to text
229
+ const ratioText = (0, $eb371feb739abc89$export$fdc6e9cbd31555fb)({
230
+ config: config,
231
+ i18next: i18next,
232
+ ratio: doseAndRate.rateRatio
233
+ });
234
+ if (ratioText === undefined) return undefined;
235
+ return i18next.t("fields.rateRatio", {
236
+ ratioText: ratioText
184
237
  });
185
238
  }
186
239
 
@@ -480,21 +533,7 @@ function $ac69a26e361175ba$export$b927a06bc51aea32({ dos: dos, config: config, i
480
533
 
481
534
 
482
535
  // types
483
- // Quantity unit to string
484
- function $6b27b297c2af1001$var$transformQuantityUnitToString(i18next, quantity, config) {
485
- let quantityValue = quantity.value;
486
- // If common units from HL7, do the job
487
- if (quantity.system === "http://hl7.org/fhir/ValueSet/duration-units") {
488
- let code = quantity.code;
489
- return i18next.t(`unitsOfTime:withoutCount.${code}`, {
490
- count: quantityValue
491
- });
492
- } else // otherwise, it is UCUM, ... so let the user do the job
493
- return config.fromFHIRQuantityUnitToString({
494
- language: config.language,
495
- quantity: quantity
496
- });
497
- }
536
+
498
537
  function $6b27b297c2af1001$export$8c667cbf7bebaa93({ dos: dos, config: config, i18next: i18next }) {
499
538
  // If empty, return undefined
500
539
  if (dos.timing === undefined || dos.timing.repeat === undefined) return undefined;
@@ -504,26 +543,17 @@ function $6b27b297c2af1001$export$8c667cbf7bebaa93({ dos: dos, config: config, i
504
543
  // Do nothing if no boundsRange, I am not a wizard
505
544
  if (boundsRange === undefined) return undefined;
506
545
  else {
507
- let low = boundsRange.low;
508
- let high = boundsRange.high;
509
- // quantity unit
510
- let unit = $6b27b297c2af1001$var$transformQuantityUnitToString(i18next, high || low, config);
511
- // Three cases
512
- // 1. Both low & high are present
513
- if (high !== undefined && low !== undefined) return i18next.t("fields.boundsRange.lowAndHigh", {
514
- low: low.value,
515
- high: high.value,
516
- unit: unit
517
- });
518
- // 2. Only high is present
519
- if (high !== undefined) return i18next.t("fields.boundsRange.onlyHigh", {
520
- high: high.value,
521
- unit: unit
546
+ // Turn range into a text
547
+ const rangeText = (0, $73073500f7f08f16$export$be17d167ed50d870)({
548
+ range: boundsRange,
549
+ config: config,
550
+ i18next: i18next
522
551
  });
523
- // 3. Only low is present
524
- return i18next.t("fields.boundsRange.onlyLow", {
525
- low: low.value,
526
- unit: unit
552
+ // Reject if empty
553
+ if (rangeText === undefined) return undefined;
554
+ // return the final string
555
+ return i18next.t("fields.boundsRange", {
556
+ rangeText: rangeText
527
557
  });
528
558
  }
529
559
  }
@@ -748,6 +778,7 @@ function $f28938e27e1e9773$export$75a89431d80a701a({ dos: dos, config: config, i
748
778
 
749
779
 
750
780
 
781
+
751
782
  function $252769c9f296cd6d$export$346b11a8cb220f02({ dos: dos, config: config, i18next: i18next }) {
752
783
  // If empty, return undefined
753
784
  if (dos.maxDosePerPeriod === undefined) return undefined;
@@ -762,29 +793,14 @@ function $252769c9f296cd6d$export$346b11a8cb220f02({ dos: dos, config: config, i
762
793
  if (values.length === 0) return undefined;
763
794
  // Periods are expressed as ratio (like rateRatio)
764
795
  const valuesAsString = values.map((period)=>{
765
- // num / dem
766
- let numerator = period.numerator;
767
- let denominator = period.denominator;
768
- let quantityNum = numerator?.value || 1;
769
- let quantityDenom = denominator?.value || 1;
770
- // units as text
771
- let numeratorUnit = numerator !== undefined ? config.fromFHIRQuantityUnitToString({
772
- language: config.language,
773
- quantity: numerator
774
- }) : "";
775
- let denominatorUnit = denominator !== undefined ? config.fromFHIRQuantityUnitToString({
776
- language: config.language,
777
- quantity: denominator
778
- }) : "";
779
- return i18next.t("fields.maxDosePerPeriod.maxDosePerPeriod", {
780
- count: quantityDenom,
781
- quantityNumerator: quantityNum,
782
- numeratorUnit: numeratorUnit,
783
- denominatorUnit: denominatorUnit
796
+ return (0, $eb371feb739abc89$export$fdc6e9cbd31555fb)({
797
+ config: config,
798
+ i18next: i18next,
799
+ ratio: period
784
800
  });
785
- });
801
+ }).filter((s)=>s !== undefined);
786
802
  const maxDosePerPeriodText = (0, $f475af73bad0ba43$export$826742c1df3eca39)(i18next, valuesAsString);
787
- return i18next.t("fields.maxDosePerPeriod.general", {
803
+ return i18next.t("fields.maxDosePerPeriod", {
788
804
  count: values.length,
789
805
  maxDosePerPeriodText: maxDosePerPeriodText
790
806
  });
@@ -1004,9 +1020,8 @@ class $8435b8d847fb3eb7$export$1c191bca55f84a03 {
1004
1020
  return this.getFields(dos, ...order);
1005
1021
  }
1006
1022
  /**
1007
- * Turn multiple FHIR Dosage object into text
1008
- */ fromMultipleDosageToText(dosages) {
1009
- // As we can have concurrent / sequential instructions, we need a generic algorithm to do the job
1023
+ * Does this array of Dosage objects contains only "sequential" instructions ?
1024
+ */ containsOnlySequentialInstructions(dosages) {
1010
1025
  // 1. Collect all sequences number
1011
1026
  let sequencesNumbers = dosages.map((d)=>d.sequence).filter((s)=>s !== undefined);
1012
1027
  // 2. Convert it to a Set
@@ -1014,11 +1029,13 @@ class $8435b8d847fb3eb7$export$1c191bca55f84a03 {
1014
1029
  // 3. We have a "sequential" situation in two cases
1015
1030
  // A) No sequence number were provided
1016
1031
  // B) All sequence numbers are different
1017
- if (encounteredSequenceNumbers.size === 0 || encounteredSequenceNumbers.size === dosages.length) {
1018
- const dosagesAsText = dosages.map((d)=>this.fromDosageToText(d));
1019
- return (0, $f475af73bad0ba43$export$826742c1df3eca39)(this.i18nInstance, dosagesAsText, "then");
1020
- }
1021
- // 4. We have both "sequential" and "concurrent" instructions - time to see what is the configuration
1032
+ return encounteredSequenceNumbers.size === 0 || encounteredSequenceNumbers.size === dosages.length;
1033
+ }
1034
+ /**
1035
+ * Turn this array of Dosage objects into a data structure useful to handle "sequential" and "concurrent" instructions (cf. "sequence" property).
1036
+ * @returns {Dosage[][]} - A two-dimensional array where each inner array contains Dosage objects belonging to the same sequence numberr.
1037
+ */ groupBySequence(dosages) {
1038
+ // Prepare variables
1022
1039
  let groups = {};
1023
1040
  let sequences = new Set();
1024
1041
  for(let idx = 0; idx < dosages.length; idx++){
@@ -1027,23 +1044,39 @@ class $8435b8d847fb3eb7$export$1c191bca55f84a03 {
1027
1044
  // Get the sequence number (normally, in real world, it should be present in this case)
1028
1045
  // If no sequence number, assume it is idx + 1
1029
1046
  let sequenceNr = dosage.sequence || idx + 1;
1030
- // Generate the text version
1031
- let dosageAsText = this.fromDosageToText(dosage);
1032
1047
  // Retrieve of create previous entries for this sequence number
1033
1048
  let localGroup = groups[sequenceNr] || [];
1034
1049
  // Add entry
1035
- localGroup.push(dosageAsText);
1050
+ localGroup.push(dosage);
1036
1051
  // Pushback result
1037
1052
  groups[sequenceNr] = localGroup;
1038
1053
  // For reminder of the parsed sequence
1039
1054
  sequences.add(sequenceNr);
1040
1055
  }
1041
- // 5. Now that data structures are filled, it is a piece of cake to generate the result
1042
- let sequentialInstructions = [
1056
+ // By using the Set values, we are sure it is returned in the way Dosages were written
1057
+ return [
1043
1058
  ...sequences.values()
1044
1059
  ].map((sequence)=>{
1045
1060
  let concurrentInstructions = groups[sequence];
1046
- return (0, $f475af73bad0ba43$export$826742c1df3eca39)(this.i18nInstance, concurrentInstructions, "and");
1061
+ return concurrentInstructions;
1062
+ });
1063
+ }
1064
+ /**
1065
+ * Turn multiple FHIR Dosage objects into text
1066
+ */ fromMultipleDosageToText(dosages) {
1067
+ // As we can have concurrent / sequential instructions, we need a generic algorithm to do the job
1068
+ const hasOnlySequentialInstructions = this.containsOnlySequentialInstructions(dosages);
1069
+ // Sequential instructions
1070
+ if (hasOnlySequentialInstructions) {
1071
+ const dosagesAsText = dosages.map((d)=>this.fromDosageToText(d));
1072
+ return (0, $f475af73bad0ba43$export$826742c1df3eca39)(this.i18nInstance, dosagesAsText, "then");
1073
+ }
1074
+ // We have both "sequential" and "concurrent" instructions - time to see what is the configuration
1075
+ let sortedDosages = this.groupBySequence(dosages);
1076
+ // Now that data structures are filled, it is a piece of cake to generate the result
1077
+ let sequentialInstructions = sortedDosages.map((concurrentInstructions)=>{
1078
+ let concurrentInstructionsAsString = concurrentInstructions.map((dosage)=>this.fromDosageToText(dosage));
1079
+ return (0, $f475af73bad0ba43$export$826742c1df3eca39)(this.i18nInstance, concurrentInstructionsAsString, "and");
1047
1080
  });
1048
1081
  return (0, $f475af73bad0ba43$export$826742c1df3eca39)(this.i18nInstance, sequentialInstructions, "then");
1049
1082
  }