@imranq2/fhirpatientsummary 1.0.16 → 1.0.18
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 +6 -6
- package/dist/index.cjs +661 -340
- package/dist/index.d.cts +74 -8
- package/dist/index.d.ts +74 -8
- package/dist/index.js +661 -340
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -110,33 +110,21 @@ var BLOOD_PRESSURE_LOINC_CODES = {
|
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
// src/structures/ips_section_resource_map.ts
|
|
113
|
-
var IPSSectionResourceMap = {
|
|
114
|
-
["Patient" /* PATIENT */]: ["Patient"],
|
|
115
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
116
|
-
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
117
|
-
// Medication resource is needed for identifying name of medication
|
|
118
|
-
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
119
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
120
|
-
// Immunization can include Organization as a related resource
|
|
121
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
122
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
123
|
-
// Device resource is used for medical devices name
|
|
124
|
-
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
125
|
-
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
126
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
|
|
127
|
-
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
128
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
129
|
-
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
130
|
-
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
131
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
132
|
-
};
|
|
133
113
|
var IPSSectionResourceFilters = {
|
|
114
|
+
// Patient section: only Patient resource
|
|
115
|
+
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient",
|
|
116
|
+
// Only include allergies
|
|
117
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance",
|
|
118
|
+
// includes MedicationRequest, MedicationStatement. Medication is needed for medication names
|
|
119
|
+
["MedicationSummarySection" /* MEDICATIONS */]: (resource) => ["MedicationRequest", "MedicationStatement", "Medication"].includes(resource.resourceType),
|
|
134
120
|
// Only include active conditions
|
|
135
121
|
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
136
122
|
// Only include completed immunizations
|
|
137
123
|
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
138
124
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
139
125
|
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "vital-signs")),
|
|
126
|
+
// Includes DeviceUseStatement. Device is needed for linked device details
|
|
127
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
|
|
140
128
|
// Only include finalized diagnostic reports
|
|
141
129
|
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
142
130
|
// Only include completed procedures
|
|
@@ -152,28 +140,30 @@ var IPSSectionResourceFilters = {
|
|
|
152
140
|
// Only include active care plans
|
|
153
141
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
154
142
|
// Only include active advance directives (Consent resources)
|
|
155
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
156
|
-
|
|
157
|
-
|
|
143
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
144
|
+
};
|
|
145
|
+
var IPSSectionSummaryCompositionFilter = {
|
|
146
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === "https://fhir.icanbwell.com/4_0_0/CodeSystem/composition/" && c.code === "allergy_summary_document"),
|
|
147
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === "https://fhir.icanbwell.com/4_0_0/CodeSystem/composition/" && c.code === "vital_summary_document")
|
|
158
148
|
};
|
|
159
149
|
var IPSSectionResourceHelper = class {
|
|
160
|
-
static getResourceTypesForSection(section) {
|
|
161
|
-
return IPSSectionResourceMap[section] || [];
|
|
162
|
-
}
|
|
163
150
|
static getResourceFilterForSection(section) {
|
|
164
151
|
return IPSSectionResourceFilters[section];
|
|
165
152
|
}
|
|
153
|
+
static getSummaryCompositionFilterForSection(section) {
|
|
154
|
+
return IPSSectionSummaryCompositionFilter[section];
|
|
155
|
+
}
|
|
166
156
|
};
|
|
167
157
|
|
|
168
158
|
// src/narratives/templates/typescript/TemplateUtilities.ts
|
|
169
159
|
var import_luxon = require("luxon");
|
|
170
160
|
var TemplateUtilities = class {
|
|
171
161
|
/**
|
|
172
|
-
* Constructor to initialize the TemplateUtilities with a FHIR
|
|
173
|
-
* @param
|
|
162
|
+
* Constructor to initialize the TemplateUtilities with a FHIR resources
|
|
163
|
+
* @param resources - FHIR resources
|
|
174
164
|
*/
|
|
175
|
-
constructor(
|
|
176
|
-
this.
|
|
165
|
+
constructor(resources) {
|
|
166
|
+
this.resources = resources;
|
|
177
167
|
}
|
|
178
168
|
/**
|
|
179
169
|
* Formats a CodeableConcept object
|
|
@@ -204,7 +194,7 @@ var TemplateUtilities = class {
|
|
|
204
194
|
return "";
|
|
205
195
|
}
|
|
206
196
|
resolveReference(ref) {
|
|
207
|
-
if (!ref || !this.
|
|
197
|
+
if (!ref || !this.resources) {
|
|
208
198
|
return null;
|
|
209
199
|
}
|
|
210
200
|
const referenceParts = ref.reference?.split("/");
|
|
@@ -213,10 +203,10 @@ var TemplateUtilities = class {
|
|
|
213
203
|
}
|
|
214
204
|
const referenceResourceType = referenceParts[0];
|
|
215
205
|
const referenceResourceId = referenceParts[1];
|
|
216
|
-
const resource = this.
|
|
217
|
-
return entry.
|
|
206
|
+
const resource = this.resources.find((entry) => {
|
|
207
|
+
return entry.resourceType === referenceResourceType && entry.id === referenceResourceId;
|
|
218
208
|
});
|
|
219
|
-
return resource ? resource
|
|
209
|
+
return resource ? resource : null;
|
|
220
210
|
}
|
|
221
211
|
/**
|
|
222
212
|
* Renders a Device reference
|
|
@@ -612,6 +602,137 @@ var TemplateUtilities = class {
|
|
|
612
602
|
}
|
|
613
603
|
return status;
|
|
614
604
|
}
|
|
605
|
+
extractObservationSummaryValue(data, timezone) {
|
|
606
|
+
if (data["valueQuantity.value"] !== void 0) {
|
|
607
|
+
const value = data["valueQuantity.value"];
|
|
608
|
+
const unit = data["valueQuantity.unit"];
|
|
609
|
+
return unit ? `${value} ${unit}` : `${value}`;
|
|
610
|
+
}
|
|
611
|
+
if (data["valueCodeableConcept.text"] !== void 0) {
|
|
612
|
+
return data["valueCodeableConcept.text"];
|
|
613
|
+
}
|
|
614
|
+
if (data["valueCodeableConcept.coding.display"] !== void 0) {
|
|
615
|
+
return data["valueCodeableConcept.coding.display"];
|
|
616
|
+
}
|
|
617
|
+
if (data["valueString"] !== void 0) {
|
|
618
|
+
return data["valueString"];
|
|
619
|
+
}
|
|
620
|
+
if (data["valueBoolean"] !== void 0) {
|
|
621
|
+
return String(data["valueBoolean"]);
|
|
622
|
+
}
|
|
623
|
+
if (data["valueInteger"] !== void 0) {
|
|
624
|
+
return String(data["valueInteger"]);
|
|
625
|
+
}
|
|
626
|
+
if (data["valueDateTime"] !== void 0) {
|
|
627
|
+
return this.renderTime(data["valueDateTime"], timezone);
|
|
628
|
+
}
|
|
629
|
+
if (data["valuePeriod.start"] !== void 0 || data["valuePeriod.end"] !== void 0) {
|
|
630
|
+
const start = this.renderTime(data["valuePeriod.start"], timezone);
|
|
631
|
+
const end = this.renderTime(data["valuePeriod.end"], timezone);
|
|
632
|
+
if (start && end) {
|
|
633
|
+
return `${start} - ${end}`;
|
|
634
|
+
} else if (start) {
|
|
635
|
+
return `${start}`;
|
|
636
|
+
} else if (end) {
|
|
637
|
+
return `${end}`;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (data["valueTime"] !== void 0) {
|
|
641
|
+
return this.renderTime(data["valueTime"], timezone);
|
|
642
|
+
}
|
|
643
|
+
if (data["valueSampledData.origin.value"] !== void 0 || data["valueSampledData.origin.unit"] !== void 0) {
|
|
644
|
+
const originValue = data["valueSampledData.origin.value"];
|
|
645
|
+
const originUnit = data["valueSampledData.origin.unit"];
|
|
646
|
+
let result = "";
|
|
647
|
+
if (originValue !== void 0 && originUnit !== void 0) {
|
|
648
|
+
result = `${originValue} ${originUnit}`;
|
|
649
|
+
} else if (originValue !== void 0) {
|
|
650
|
+
result = `${originValue}`;
|
|
651
|
+
} else if (originUnit !== void 0) {
|
|
652
|
+
result = `${originUnit}`;
|
|
653
|
+
}
|
|
654
|
+
const period = data["valueSampledData.period"];
|
|
655
|
+
const factor = data["valueSampledData.factor"];
|
|
656
|
+
const lowerLimit = data["valueSampledData.lowerLimit"];
|
|
657
|
+
const upperLimit = data["valueSampledData.upperLimit"];
|
|
658
|
+
const sampledData = data["valueSampledData.data"];
|
|
659
|
+
const extras = [];
|
|
660
|
+
if (period !== void 0) extras.push(`period: ${period}`);
|
|
661
|
+
if (factor !== void 0) extras.push(`factor: ${factor}`);
|
|
662
|
+
if (lowerLimit !== void 0) extras.push(`lowerLimit: ${lowerLimit}`);
|
|
663
|
+
if (upperLimit !== void 0) extras.push(`upperLimit: ${upperLimit}`);
|
|
664
|
+
if (sampledData !== void 0) extras.push(`data: ${sampledData}`);
|
|
665
|
+
if (extras.length > 0) {
|
|
666
|
+
result += ` (${extras.join(", ")})`;
|
|
667
|
+
}
|
|
668
|
+
return result;
|
|
669
|
+
}
|
|
670
|
+
if (data["valueRange.low.value"] !== void 0 || data["valueRange.high.value"] !== void 0) {
|
|
671
|
+
let referenceRange = "";
|
|
672
|
+
if (data["valueRange.low.value"] !== void 0) {
|
|
673
|
+
referenceRange += `${data["valueRange.low.value"]}`;
|
|
674
|
+
if (data["valueRange.low.unit"] !== void 0) {
|
|
675
|
+
referenceRange += ` ${data["valueRange.low.unit"]}`;
|
|
676
|
+
}
|
|
677
|
+
referenceRange = referenceRange.trim();
|
|
678
|
+
if (data["valueRange.high.value"] !== void 0) {
|
|
679
|
+
referenceRange += " - ";
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (data["valueRange.high.value"] !== void 0) {
|
|
683
|
+
referenceRange += `${data["valueRange.high.value"]}`;
|
|
684
|
+
if (data["valueRange.high.unit"] !== void 0) {
|
|
685
|
+
referenceRange += ` ${data["valueRange.high.unit"]}`;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return referenceRange.trim();
|
|
689
|
+
}
|
|
690
|
+
if (data["valueRatio.numerator.value"] !== void 0 || data["valueRatio.denominator.value"] !== void 0) {
|
|
691
|
+
let ratio = "";
|
|
692
|
+
if (data["valueRatio.numerator.value"] !== void 0) {
|
|
693
|
+
ratio += `${data["valueRatio.numerator.value"]}`;
|
|
694
|
+
if (data["valueRatio.numerator.unit"] !== void 0) {
|
|
695
|
+
ratio += ` ${data["valueRatio.numerator.unit"]}`;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (data["valueRatio.denominator.value"] !== void 0) {
|
|
699
|
+
ratio += " / ";
|
|
700
|
+
ratio += `${data["valueRatio.denominator.value"]}`;
|
|
701
|
+
if (data["valueRatio.denominator.unit"] !== void 0) {
|
|
702
|
+
ratio += ` ${data["valueRatio.denominator.unit"]}`;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return ratio.trim();
|
|
706
|
+
}
|
|
707
|
+
return "";
|
|
708
|
+
}
|
|
709
|
+
extractObservationSummaryReferenceRange(data) {
|
|
710
|
+
let referenceRange = "";
|
|
711
|
+
if (data["referenceRange.low.value"]) {
|
|
712
|
+
referenceRange += `${data["referenceRange.low.value"]} ${data["referenceRange.low.unit"]}`;
|
|
713
|
+
referenceRange.trim();
|
|
714
|
+
if (data["referenceRange.high.value"]) {
|
|
715
|
+
referenceRange += " - ";
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (data["referenceRange.high.value"]) {
|
|
719
|
+
referenceRange += `${data["referenceRange.high.value"]} ${data["referenceRange.high.unit"]}`;
|
|
720
|
+
}
|
|
721
|
+
return referenceRange.trim();
|
|
722
|
+
}
|
|
723
|
+
extractObservationSummaryEffectiveTime(data, timezone) {
|
|
724
|
+
if (data["effectiveDateTime"]) {
|
|
725
|
+
return this.renderTime(data["effectiveDateTime"], timezone);
|
|
726
|
+
}
|
|
727
|
+
let effectiveTimePeriod = "";
|
|
728
|
+
if (data["effectivePeriod.start"]) {
|
|
729
|
+
effectiveTimePeriod += this.renderTime(data["effectivePeriod.start"], timezone);
|
|
730
|
+
}
|
|
731
|
+
if (data["effectivePeriod.end"]) {
|
|
732
|
+
effectiveTimePeriod += ` - ${this.renderTime(data["effectivePeriod.end"], timezone)}`;
|
|
733
|
+
}
|
|
734
|
+
return effectiveTimePeriod.trim();
|
|
735
|
+
}
|
|
615
736
|
formatQuantityValue(quantity) {
|
|
616
737
|
if (!quantity) return "";
|
|
617
738
|
const parts = [];
|
|
@@ -787,7 +908,7 @@ var TemplateUtilities = class {
|
|
|
787
908
|
if (parts.length === 2) {
|
|
788
909
|
const resourceType = parts[0];
|
|
789
910
|
const resourceId = parts[1];
|
|
790
|
-
const resource = this.
|
|
911
|
+
const resource = this.resources?.find((resource2) => resource2.resourceType === resourceType && resource2.id === resourceId);
|
|
791
912
|
if (resource) {
|
|
792
913
|
return `${resourceType}/${resourceId}`;
|
|
793
914
|
}
|
|
@@ -827,48 +948,96 @@ var TemplateUtilities = class {
|
|
|
827
948
|
// src/narratives/templates/typescript/PatientTemplate.ts
|
|
828
949
|
var PatientTemplate = class _PatientTemplate {
|
|
829
950
|
/**
|
|
830
|
-
* Generate HTML narrative for Patient
|
|
831
|
-
* @param
|
|
951
|
+
* Generate HTML narrative for Patient resources
|
|
952
|
+
* @param resources - FHIR Patient resources
|
|
832
953
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
833
954
|
* @returns HTML string for rendering
|
|
834
955
|
*/
|
|
835
|
-
generateNarrative(
|
|
836
|
-
return _PatientTemplate.generateStaticNarrative(
|
|
956
|
+
generateNarrative(resources, timezone) {
|
|
957
|
+
return _PatientTemplate.generateStaticNarrative(resources, timezone);
|
|
837
958
|
}
|
|
838
959
|
/**
|
|
839
960
|
* Internal static implementation that actually generates the narrative
|
|
840
|
-
* @param
|
|
961
|
+
* @param resources - FHIR Patient resources
|
|
841
962
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
842
963
|
* @returns HTML string for rendering
|
|
843
964
|
*/
|
|
844
965
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
845
|
-
static generateStaticNarrative(
|
|
846
|
-
const templateUtilities = new TemplateUtilities(
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
<
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
966
|
+
static generateStaticNarrative(resources, timezone) {
|
|
967
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
968
|
+
const combinedPatient = this.combinePatients(resources);
|
|
969
|
+
return `<div>
|
|
970
|
+
<ul>
|
|
971
|
+
<li><strong>Name(s):</strong>${this.renderNames(combinedPatient)}</li>
|
|
972
|
+
<li><strong>Gender:</strong>${combinedPatient.gender ? this.capitalize(combinedPatient.gender) : ""}</li>
|
|
973
|
+
<li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
|
|
974
|
+
<li><strong>Identifier(s):</strong>${this.renderIdentifiers(combinedPatient)}</li>
|
|
975
|
+
<li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
|
|
976
|
+
<li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
|
|
977
|
+
<li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
|
|
978
|
+
<li><strong>Deceased:</strong>${this.renderDeceased(combinedPatient)}</li>
|
|
979
|
+
<li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
|
|
980
|
+
</ul>
|
|
981
|
+
</div>`;
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Combines multiple patient resources into a single patient object
|
|
985
|
+
* Merges fields, preferring non-empty values
|
|
986
|
+
* @param patients - Array of patient resources
|
|
987
|
+
* @returns Combined patient resource
|
|
988
|
+
*/
|
|
989
|
+
static combinePatients(patients) {
|
|
990
|
+
if (patients.length === 1) {
|
|
991
|
+
return patients[0];
|
|
992
|
+
}
|
|
993
|
+
const combined = patients[0];
|
|
994
|
+
const allNames = [];
|
|
995
|
+
const allIdentifiers = [];
|
|
996
|
+
const allTelecom = [];
|
|
997
|
+
const allAddresses = [];
|
|
998
|
+
const allCommunication = [];
|
|
999
|
+
patients.forEach((patient) => {
|
|
1000
|
+
if (patient.name) {
|
|
1001
|
+
allNames.push(...patient.name);
|
|
865
1002
|
}
|
|
866
|
-
|
|
867
|
-
|
|
1003
|
+
if (patient.identifier) {
|
|
1004
|
+
allIdentifiers.push(...patient.identifier);
|
|
1005
|
+
}
|
|
1006
|
+
if (patient.telecom) {
|
|
1007
|
+
allTelecom.push(...patient.telecom);
|
|
1008
|
+
}
|
|
1009
|
+
if (patient.address) {
|
|
1010
|
+
allAddresses.push(...patient.address);
|
|
1011
|
+
}
|
|
1012
|
+
if (patient.communication) {
|
|
1013
|
+
allCommunication.push(...patient.communication);
|
|
1014
|
+
}
|
|
1015
|
+
if (!combined.gender && patient.gender) {
|
|
1016
|
+
combined.gender = patient.gender;
|
|
1017
|
+
}
|
|
1018
|
+
if (!combined.birthDate && patient.birthDate) {
|
|
1019
|
+
combined.birthDate = patient.birthDate;
|
|
1020
|
+
}
|
|
1021
|
+
if (!combined.maritalStatus && patient.maritalStatus) {
|
|
1022
|
+
combined.maritalStatus = patient.maritalStatus;
|
|
1023
|
+
}
|
|
1024
|
+
if (!combined.deceasedBoolean && patient.deceasedBoolean !== void 0) {
|
|
1025
|
+
combined.deceasedBoolean = patient.deceasedBoolean;
|
|
1026
|
+
}
|
|
1027
|
+
if (!combined.deceasedDateTime && patient.deceasedDateTime) {
|
|
1028
|
+
combined.deceasedDateTime = patient.deceasedDateTime;
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
combined.name = allNames;
|
|
1032
|
+
combined.identifier = allIdentifiers;
|
|
1033
|
+
combined.telecom = allTelecom;
|
|
1034
|
+
combined.address = allAddresses;
|
|
1035
|
+
combined.communication = allCommunication;
|
|
1036
|
+
return combined;
|
|
868
1037
|
}
|
|
869
1038
|
/**
|
|
870
1039
|
* Renders patient names as HTML list items
|
|
871
|
-
* @param patient - Patient
|
|
1040
|
+
* @param patient - Patient resources
|
|
872
1041
|
* @returns HTML string of list items
|
|
873
1042
|
*/
|
|
874
1043
|
static renderNames(patient) {
|
|
@@ -888,7 +1057,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
888
1057
|
}
|
|
889
1058
|
/**
|
|
890
1059
|
* Renders patient identifiers as HTML list items
|
|
891
|
-
* @param patient - Patient
|
|
1060
|
+
* @param patient - Patient resources
|
|
892
1061
|
* @returns HTML string of list items
|
|
893
1062
|
*/
|
|
894
1063
|
static renderIdentifiers(patient) {
|
|
@@ -903,7 +1072,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
903
1072
|
}
|
|
904
1073
|
/**
|
|
905
1074
|
* Renders patient telecom information grouped by system
|
|
906
|
-
* @param patient - Patient
|
|
1075
|
+
* @param patient - Patient resources
|
|
907
1076
|
* @returns HTML string grouped by system
|
|
908
1077
|
*/
|
|
909
1078
|
static renderTelecom(patient) {
|
|
@@ -962,7 +1131,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
962
1131
|
}
|
|
963
1132
|
/**
|
|
964
1133
|
* Renders patient addresses as HTML list items
|
|
965
|
-
* @param patient - Patient
|
|
1134
|
+
* @param patient - Patient resources
|
|
966
1135
|
* @returns HTML string of list items
|
|
967
1136
|
*/
|
|
968
1137
|
static renderAddresses(patient) {
|
|
@@ -984,9 +1153,18 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
984
1153
|
if (address.city) {
|
|
985
1154
|
addressArray.push(address.city);
|
|
986
1155
|
}
|
|
1156
|
+
if (address.district) {
|
|
1157
|
+
addressArray.push(address.district);
|
|
1158
|
+
}
|
|
1159
|
+
if (address.state) {
|
|
1160
|
+
addressArray.push(address.state);
|
|
1161
|
+
}
|
|
987
1162
|
if (address.country) {
|
|
988
1163
|
addressArray.push(address.country);
|
|
989
1164
|
}
|
|
1165
|
+
if (address.postalCode) {
|
|
1166
|
+
addressArray.push(address.postalCode);
|
|
1167
|
+
}
|
|
990
1168
|
}
|
|
991
1169
|
const addressText = addressArray.join(", ").trim();
|
|
992
1170
|
if (addressText) {
|
|
@@ -997,7 +1175,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
997
1175
|
}
|
|
998
1176
|
/**
|
|
999
1177
|
* Renders patient deceased status
|
|
1000
|
-
* @param patient - Patient
|
|
1178
|
+
* @param patient - Patient resources
|
|
1001
1179
|
* @returns HTML string for deceased status
|
|
1002
1180
|
*/
|
|
1003
1181
|
static renderDeceased(patient) {
|
|
@@ -1012,7 +1190,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
1012
1190
|
/**
|
|
1013
1191
|
* Renders patient communication preferences as HTML list items
|
|
1014
1192
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1015
|
-
* @param patient - Patient
|
|
1193
|
+
* @param patient - Patient resources
|
|
1016
1194
|
* @returns HTML string of list items
|
|
1017
1195
|
*/
|
|
1018
1196
|
static renderCommunication(templateUtilities, patient) {
|
|
@@ -1047,32 +1225,81 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
1047
1225
|
var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
1048
1226
|
/**
|
|
1049
1227
|
* Generate HTML narrative for AllergyIntolerance resources
|
|
1050
|
-
* @param
|
|
1228
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
1051
1229
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1052
1230
|
* @returns HTML string for rendering
|
|
1053
1231
|
*/
|
|
1054
|
-
generateNarrative(
|
|
1055
|
-
return _AllergyIntoleranceTemplate.generateStaticNarrative(
|
|
1232
|
+
generateNarrative(resources, timezone) {
|
|
1233
|
+
return _AllergyIntoleranceTemplate.generateStaticNarrative(resources, timezone);
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Generate HTML narrative for AllergyIntolerance resources using summary
|
|
1237
|
+
* @param resources - FHIR Composition resources
|
|
1238
|
+
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1239
|
+
* @returns HTML string for rendering
|
|
1240
|
+
*/
|
|
1241
|
+
generateSummaryNarrative(resources, timezone) {
|
|
1242
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1243
|
+
let html = `
|
|
1244
|
+
<div>
|
|
1245
|
+
<table>
|
|
1246
|
+
<thead>
|
|
1247
|
+
<tr>
|
|
1248
|
+
<th>Allergen</th>
|
|
1249
|
+
<th>Criticality</th>
|
|
1250
|
+
<th>Recorded Date</th>
|
|
1251
|
+
</tr>
|
|
1252
|
+
</thead>
|
|
1253
|
+
<tbody>`;
|
|
1254
|
+
for (const resourceItem of resources) {
|
|
1255
|
+
for (const rowData of resourceItem.section ?? []) {
|
|
1256
|
+
const data = {};
|
|
1257
|
+
for (const columnData of rowData.section ?? []) {
|
|
1258
|
+
switch (columnData.title) {
|
|
1259
|
+
case "Allergen Name":
|
|
1260
|
+
data["allergen"] = columnData.text?.div ?? "";
|
|
1261
|
+
break;
|
|
1262
|
+
case "Criticality":
|
|
1263
|
+
data["criticality"] = columnData.text?.div ?? "";
|
|
1264
|
+
break;
|
|
1265
|
+
case "Recorded Date":
|
|
1266
|
+
data["recordedDate"] = columnData.text?.div ?? "";
|
|
1267
|
+
break;
|
|
1268
|
+
default:
|
|
1269
|
+
break;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
html += `
|
|
1273
|
+
<tr>
|
|
1274
|
+
<td>${data["allergen"] ?? "-"}</td>
|
|
1275
|
+
<td>${data["criticality"] ?? "-"}</td>
|
|
1276
|
+
<td>${templateUtilities.renderTime(data["recordedDate"], timezone) ?? "-"}</td>
|
|
1277
|
+
</tr>`;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
html += `
|
|
1281
|
+
</tbody>
|
|
1282
|
+
</table>
|
|
1283
|
+
</div>`;
|
|
1284
|
+
return html;
|
|
1056
1285
|
}
|
|
1057
1286
|
/**
|
|
1058
1287
|
* Internal static implementation that actually generates the narrative
|
|
1059
|
-
* @param
|
|
1288
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
1060
1289
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1061
1290
|
* @returns HTML string for rendering
|
|
1062
1291
|
*/
|
|
1063
|
-
static generateStaticNarrative(
|
|
1064
|
-
const templateUtilities = new TemplateUtilities(
|
|
1292
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1293
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1065
1294
|
const activeAllergies = [];
|
|
1066
1295
|
const resolvedAllergies = [];
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
activeAllergies.push(allergy);
|
|
1075
|
-
}
|
|
1296
|
+
for (const resourceItem of resources) {
|
|
1297
|
+
const allergy = resourceItem;
|
|
1298
|
+
const isResolved = allergy.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code));
|
|
1299
|
+
if (isResolved) {
|
|
1300
|
+
resolvedAllergies.push(allergy);
|
|
1301
|
+
} else {
|
|
1302
|
+
activeAllergies.push(allergy);
|
|
1076
1303
|
}
|
|
1077
1304
|
}
|
|
1078
1305
|
activeAllergies.sort((a, b) => {
|
|
@@ -1185,12 +1412,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1185
1412
|
var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
1186
1413
|
/**
|
|
1187
1414
|
* Generate HTML narrative for Medication resources
|
|
1188
|
-
* @param
|
|
1415
|
+
* @param resources - FHIR Medication resources
|
|
1189
1416
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1190
1417
|
* @returns HTML string for rendering
|
|
1191
1418
|
*/
|
|
1192
|
-
generateNarrative(
|
|
1193
|
-
return _MedicationSummaryTemplate.generateStaticNarrative(
|
|
1419
|
+
generateNarrative(resources, timezone) {
|
|
1420
|
+
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
|
|
1194
1421
|
}
|
|
1195
1422
|
/**
|
|
1196
1423
|
* Safely parse a date string and return a valid Date object or null
|
|
@@ -1255,16 +1482,16 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1255
1482
|
}
|
|
1256
1483
|
/**
|
|
1257
1484
|
* Internal static implementation that actually generates the narrative
|
|
1258
|
-
* @param
|
|
1485
|
+
* @param resources - FHIR Medication resources
|
|
1259
1486
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1260
1487
|
* @returns HTML string for rendering
|
|
1261
1488
|
*/
|
|
1262
1489
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1263
|
-
static generateStaticNarrative(
|
|
1264
|
-
const templateUtilities = new TemplateUtilities(
|
|
1490
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1491
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1265
1492
|
let html = "";
|
|
1266
|
-
const medicationRequests = this.getMedicationRequests(templateUtilities,
|
|
1267
|
-
const medicationStatements = this.getMedicationStatements(templateUtilities,
|
|
1493
|
+
const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
|
|
1494
|
+
const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
|
|
1268
1495
|
const allActiveMedications = [];
|
|
1269
1496
|
const allInactiveMedications = [];
|
|
1270
1497
|
medicationRequests.forEach((mr) => {
|
|
@@ -1318,33 +1545,33 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1318
1545
|
return html;
|
|
1319
1546
|
}
|
|
1320
1547
|
/**
|
|
1321
|
-
* Extract MedicationRequest resources
|
|
1548
|
+
* Extract MedicationRequest resources
|
|
1322
1549
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1323
|
-
* @param
|
|
1550
|
+
* @param resources - FHIR Medication resources
|
|
1324
1551
|
* @returns Array of MedicationRequest resources
|
|
1325
1552
|
*/
|
|
1326
|
-
static getMedicationRequests(templateUtilities,
|
|
1327
|
-
if (
|
|
1553
|
+
static getMedicationRequests(templateUtilities, resources) {
|
|
1554
|
+
if (resources.length === 0) {
|
|
1328
1555
|
return [];
|
|
1329
1556
|
}
|
|
1330
|
-
return
|
|
1331
|
-
resource: entry
|
|
1332
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1557
|
+
return resources.filter((entry) => entry.resourceType === "MedicationRequest").map((entry) => ({
|
|
1558
|
+
resource: entry,
|
|
1559
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1333
1560
|
}));
|
|
1334
1561
|
}
|
|
1335
1562
|
/**
|
|
1336
|
-
* Extract MedicationStatement resources
|
|
1563
|
+
* Extract MedicationStatement resources
|
|
1337
1564
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1338
|
-
* @param
|
|
1565
|
+
* @param resources - FHIR Medication resources
|
|
1339
1566
|
* @returns Array of MedicationStatement resources
|
|
1340
1567
|
*/
|
|
1341
|
-
static getMedicationStatements(templateUtilities,
|
|
1342
|
-
if (
|
|
1568
|
+
static getMedicationStatements(templateUtilities, resources) {
|
|
1569
|
+
if (resources.length === 0) {
|
|
1343
1570
|
return [];
|
|
1344
1571
|
}
|
|
1345
|
-
return
|
|
1346
|
-
resource: entry
|
|
1347
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1572
|
+
return resources.filter((entry) => entry.resourceType === "MedicationStatement").map((entry) => ({
|
|
1573
|
+
resource: entry,
|
|
1574
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1348
1575
|
}));
|
|
1349
1576
|
}
|
|
1350
1577
|
/**
|
|
@@ -1440,28 +1667,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1440
1667
|
var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
1441
1668
|
/**
|
|
1442
1669
|
* Generate HTML narrative for Immunization resources
|
|
1443
|
-
* @param
|
|
1670
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1444
1671
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1445
1672
|
* @returns HTML string for rendering
|
|
1446
1673
|
*/
|
|
1447
|
-
generateNarrative(
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
}
|
|
1455
|
-
return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
|
|
1674
|
+
generateNarrative(resources, timezone) {
|
|
1675
|
+
resources.sort((a, b) => {
|
|
1676
|
+
const dateA = a.occurrenceDateTime;
|
|
1677
|
+
const dateB = b.occurrenceDateTime;
|
|
1678
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1679
|
+
});
|
|
1680
|
+
return _ImmunizationsTemplate.generateStaticNarrative(resources, timezone);
|
|
1456
1681
|
}
|
|
1457
1682
|
/**
|
|
1458
1683
|
* Internal static implementation that actually generates the narrative
|
|
1459
|
-
* @param
|
|
1684
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1460
1685
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1461
1686
|
* @returns HTML string for rendering
|
|
1462
1687
|
*/
|
|
1463
|
-
static generateStaticNarrative(
|
|
1464
|
-
const templateUtilities = new TemplateUtilities(
|
|
1688
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1689
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1465
1690
|
let html = `
|
|
1466
1691
|
<table>
|
|
1467
1692
|
<thead>
|
|
@@ -1476,21 +1701,20 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1476
1701
|
</tr>
|
|
1477
1702
|
</thead>
|
|
1478
1703
|
<tbody>`;
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
}
|
|
1704
|
+
const immunizations = resources.filter((resourceItem) => resourceItem.resourceType === "Immunization");
|
|
1705
|
+
if (immunizations.length > 0) {
|
|
1706
|
+
for (const resourceItem of immunizations) {
|
|
1707
|
+
const imm = resourceItem;
|
|
1708
|
+
html += `
|
|
1709
|
+
<tr id="${templateUtilities.narrativeLinkId(imm)}">
|
|
1710
|
+
<td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
|
|
1711
|
+
<td>${imm.status || ""}</td>
|
|
1712
|
+
<td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
|
|
1713
|
+
<td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
|
|
1714
|
+
<td>${imm.lotNumber || ""}</td>
|
|
1715
|
+
<td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
|
|
1716
|
+
<td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
|
|
1717
|
+
</tr>`;
|
|
1494
1718
|
}
|
|
1495
1719
|
}
|
|
1496
1720
|
html += `
|
|
@@ -1504,23 +1728,23 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1504
1728
|
var ProblemListTemplate = class _ProblemListTemplate {
|
|
1505
1729
|
/**
|
|
1506
1730
|
* Generate HTML narrative for Problem List
|
|
1507
|
-
* @param
|
|
1731
|
+
* @param resources - FHIR Condition resources
|
|
1508
1732
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1509
1733
|
* @returns HTML string for rendering
|
|
1510
1734
|
*/
|
|
1511
|
-
generateNarrative(
|
|
1512
|
-
return _ProblemListTemplate.generateStaticNarrative(
|
|
1735
|
+
generateNarrative(resources, timezone) {
|
|
1736
|
+
return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
|
|
1513
1737
|
}
|
|
1514
1738
|
/**
|
|
1515
1739
|
* Internal static implementation that actually generates the narrative
|
|
1516
|
-
* @param
|
|
1740
|
+
* @param resources - FHIR Condition resources
|
|
1517
1741
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1518
1742
|
* @returns HTML string for rendering
|
|
1519
1743
|
*/
|
|
1520
|
-
static generateStaticNarrative(
|
|
1521
|
-
const templateUtilities = new TemplateUtilities(
|
|
1744
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1745
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1522
1746
|
let html = ``;
|
|
1523
|
-
const activeConditions =
|
|
1747
|
+
const activeConditions = resources.map((entry) => entry) || [];
|
|
1524
1748
|
activeConditions.sort((a, b) => {
|
|
1525
1749
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1526
1750
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1551,26 +1775,95 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1551
1775
|
}
|
|
1552
1776
|
};
|
|
1553
1777
|
|
|
1778
|
+
// src/structures/ips_section_constants.ts
|
|
1779
|
+
var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
|
|
1780
|
+
"Systolic Blood Pressure": "valueRatio.numerator.value",
|
|
1781
|
+
"Diastolic Blood Pressure": "valueRatio.denominator.value",
|
|
1782
|
+
"Default": "valueString"
|
|
1783
|
+
};
|
|
1784
|
+
|
|
1554
1785
|
// src/narratives/templates/typescript/VitalSignsTemplate.ts
|
|
1555
1786
|
var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
1556
1787
|
/**
|
|
1557
1788
|
* Generate HTML narrative for Vital Signs
|
|
1558
|
-
* @param
|
|
1789
|
+
* @param resources - FHIR Observation resources
|
|
1559
1790
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1560
1791
|
* @returns HTML string for rendering
|
|
1561
1792
|
*/
|
|
1562
|
-
generateNarrative(
|
|
1563
|
-
return _VitalSignsTemplate.generateStaticNarrative(
|
|
1793
|
+
generateNarrative(resources, timezone) {
|
|
1794
|
+
return _VitalSignsTemplate.generateStaticNarrative(resources, timezone);
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* Generate HTML narrative for vital signs using summary
|
|
1798
|
+
* @param resources - FHIR Composition resources
|
|
1799
|
+
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1800
|
+
* @returns HTML string for rendering
|
|
1801
|
+
*/
|
|
1802
|
+
generateSummaryNarrative(resources, timezone) {
|
|
1803
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1804
|
+
let html = `
|
|
1805
|
+
<div>
|
|
1806
|
+
<table>
|
|
1807
|
+
<thead>
|
|
1808
|
+
<tr>
|
|
1809
|
+
<th>Vital Name</th>
|
|
1810
|
+
<th>Result</th>
|
|
1811
|
+
<th>Reference Range</th>
|
|
1812
|
+
<th>Date</th>
|
|
1813
|
+
</tr>
|
|
1814
|
+
</thead>
|
|
1815
|
+
<tbody>`;
|
|
1816
|
+
for (const resourceItem of resources) {
|
|
1817
|
+
for (const rowData of resourceItem.section ?? []) {
|
|
1818
|
+
const data = {};
|
|
1819
|
+
for (const columnData of rowData.section ?? []) {
|
|
1820
|
+
const columnTitle = columnData.title;
|
|
1821
|
+
if (columnTitle) {
|
|
1822
|
+
if (Object.keys(VITAL_SIGNS_SUMMARY_COMPONENT_MAP).includes(
|
|
1823
|
+
columnTitle
|
|
1824
|
+
)) {
|
|
1825
|
+
const vitalData = {};
|
|
1826
|
+
for (const component of columnData.section?.[0]?.section ?? []) {
|
|
1827
|
+
if (component.title) {
|
|
1828
|
+
vitalData[component.title] = component.text?.div ?? "";
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
const vitalValue = templateUtilities.extractObservationSummaryValue(
|
|
1832
|
+
vitalData,
|
|
1833
|
+
timezone
|
|
1834
|
+
);
|
|
1835
|
+
if (vitalValue) {
|
|
1836
|
+
const dataKey = VITAL_SIGNS_SUMMARY_COMPONENT_MAP[columnTitle] ?? VITAL_SIGNS_SUMMARY_COMPONENT_MAP["Default"];
|
|
1837
|
+
data[dataKey] = vitalValue;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
data[columnTitle] = columnData.text?.div ?? "";
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
html += `
|
|
1844
|
+
<tr>
|
|
1845
|
+
<td>${data["Vital Name"] ?? "-"}</td>
|
|
1846
|
+
<td>${templateUtilities.extractObservationSummaryValue(data, timezone) ?? "-"}</td>
|
|
1847
|
+
<td>${templateUtilities.extractObservationSummaryReferenceRange(data) ?? "-"}</td>
|
|
1848
|
+
<td>${templateUtilities.extractObservationSummaryEffectiveTime(data, timezone) ?? "-"}</td>
|
|
1849
|
+
</tr>`;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
html += `
|
|
1853
|
+
</tbody>
|
|
1854
|
+
</table>
|
|
1855
|
+
</div>`;
|
|
1856
|
+
return html;
|
|
1564
1857
|
}
|
|
1565
1858
|
/**
|
|
1566
1859
|
* Internal static implementation that actually generates the narrative
|
|
1567
|
-
* @param
|
|
1860
|
+
* @param resources - FHIR Observation resources
|
|
1568
1861
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1569
1862
|
* @returns HTML string for rendering
|
|
1570
1863
|
*/
|
|
1571
|
-
static generateStaticNarrative(
|
|
1572
|
-
const templateUtilities = new TemplateUtilities(
|
|
1573
|
-
const observations =
|
|
1864
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1865
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1866
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1574
1867
|
observations.sort((a, b) => {
|
|
1575
1868
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1576
1869
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1580,7 +1873,7 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1580
1873
|
<table>
|
|
1581
1874
|
<thead>
|
|
1582
1875
|
<tr>
|
|
1583
|
-
<th>
|
|
1876
|
+
<th>Vital Name</th>
|
|
1584
1877
|
<th>Result</th>
|
|
1585
1878
|
<th>Unit</th>
|
|
1586
1879
|
<th>Interpretation</th>
|
|
@@ -1613,28 +1906,21 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1613
1906
|
var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
1614
1907
|
/**
|
|
1615
1908
|
* Generate HTML narrative for Medical Device resources
|
|
1616
|
-
* @param
|
|
1909
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1617
1910
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1618
1911
|
* @returns HTML string for rendering
|
|
1619
1912
|
*/
|
|
1620
|
-
generateNarrative(
|
|
1621
|
-
|
|
1622
|
-
resource.entry.sort((a, b) => {
|
|
1623
|
-
const dateA = a.resource?.recordedOn;
|
|
1624
|
-
const dateB = b.resource?.recordedOn;
|
|
1625
|
-
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1626
|
-
});
|
|
1627
|
-
}
|
|
1628
|
-
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1913
|
+
generateNarrative(resources, timezone) {
|
|
1914
|
+
return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
|
|
1629
1915
|
}
|
|
1630
1916
|
/**
|
|
1631
1917
|
* Internal static implementation that actually generates the narrative
|
|
1632
|
-
* @param
|
|
1918
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1633
1919
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1634
1920
|
* @returns HTML string for rendering
|
|
1635
1921
|
*/
|
|
1636
|
-
static generateStaticNarrative(
|
|
1637
|
-
const templateUtilities = new TemplateUtilities(
|
|
1922
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1923
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1638
1924
|
let html = `
|
|
1639
1925
|
<table>
|
|
1640
1926
|
<thead>
|
|
@@ -1646,19 +1932,19 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1646
1932
|
</tr>
|
|
1647
1933
|
</thead>
|
|
1648
1934
|
<tbody>`;
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1935
|
+
const deviceStatements = resources.filter((resourceItem) => resourceItem.resourceType === "DeviceUseStatement").map((resourceItem) => resourceItem).sort((a, b) => {
|
|
1936
|
+
const dateA = a.recordedOn;
|
|
1937
|
+
const dateB = b.recordedOn;
|
|
1938
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1939
|
+
});
|
|
1940
|
+
for (const dus of deviceStatements) {
|
|
1941
|
+
html += `
|
|
1942
|
+
<tr id="${templateUtilities.narrativeLinkId(dus)}">
|
|
1943
|
+
<td>${templateUtilities.renderDevice(dus.device)}</td>
|
|
1944
|
+
<td>${dus.status || ""}</td>
|
|
1945
|
+
<td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
|
|
1946
|
+
<td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
|
|
1947
|
+
</tr>`;
|
|
1662
1948
|
}
|
|
1663
1949
|
html += `
|
|
1664
1950
|
</tbody>
|
|
@@ -1671,23 +1957,23 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1671
1957
|
var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
1672
1958
|
/**
|
|
1673
1959
|
* Generate HTML narrative for Diagnostic Results
|
|
1674
|
-
* @param
|
|
1960
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1675
1961
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1676
1962
|
* @returns HTML string for rendering
|
|
1677
1963
|
*/
|
|
1678
|
-
generateNarrative(
|
|
1679
|
-
return _DiagnosticResultsTemplate.generateStaticNarrative(
|
|
1964
|
+
generateNarrative(resources, timezone) {
|
|
1965
|
+
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
|
|
1680
1966
|
}
|
|
1681
1967
|
/**
|
|
1682
1968
|
* Internal static implementation that actually generates the narrative
|
|
1683
|
-
* @param
|
|
1969
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1684
1970
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1685
1971
|
* @returns HTML string for rendering
|
|
1686
1972
|
*/
|
|
1687
|
-
static generateStaticNarrative(
|
|
1688
|
-
const templateUtilities = new TemplateUtilities(
|
|
1973
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1974
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1689
1975
|
let html = "";
|
|
1690
|
-
const observations = this.getObservations(
|
|
1976
|
+
const observations = this.getObservations(resources);
|
|
1691
1977
|
if (observations.length > 0) {
|
|
1692
1978
|
observations.sort((a, b) => {
|
|
1693
1979
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
@@ -1696,7 +1982,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1696
1982
|
});
|
|
1697
1983
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1698
1984
|
}
|
|
1699
|
-
const diagnosticReports = this.getDiagnosticReports(
|
|
1985
|
+
const diagnosticReports = this.getDiagnosticReports(resources);
|
|
1700
1986
|
if (diagnosticReports.length > 0) {
|
|
1701
1987
|
diagnosticReports.sort((a, b) => {
|
|
1702
1988
|
const dateA = a.issued;
|
|
@@ -1708,26 +1994,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1708
1994
|
return html;
|
|
1709
1995
|
}
|
|
1710
1996
|
/**
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
static getObservations(
|
|
1716
|
-
|
|
1717
|
-
return [];
|
|
1718
|
-
}
|
|
1719
|
-
return resource.entry.filter((entry) => entry.resource?.resourceType === "Observation").map((entry) => entry.resource);
|
|
1997
|
+
* Get all Observation resources from the resource array
|
|
1998
|
+
* @param resources - FHIR resources array
|
|
1999
|
+
* @returns Array of Observation resources
|
|
2000
|
+
*/
|
|
2001
|
+
static getObservations(resources) {
|
|
2002
|
+
return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
|
|
1720
2003
|
}
|
|
1721
2004
|
/**
|
|
1722
|
-
*
|
|
1723
|
-
* @param
|
|
2005
|
+
* Get all DiagnosticReport resources from the resource array
|
|
2006
|
+
* @param resources - FHIR resources array
|
|
1724
2007
|
* @returns Array of DiagnosticReport resources
|
|
1725
2008
|
*/
|
|
1726
|
-
static getDiagnosticReports(
|
|
1727
|
-
|
|
1728
|
-
return [];
|
|
1729
|
-
}
|
|
1730
|
-
return resource.entry.filter((entry) => entry.resource?.resourceType === "DiagnosticReport").map((entry) => entry.resource);
|
|
2009
|
+
static getDiagnosticReports(resources) {
|
|
2010
|
+
return resources.filter((resourceItem) => resourceItem.resourceType === "DiagnosticReport").map((resourceItem) => resourceItem);
|
|
1731
2011
|
}
|
|
1732
2012
|
/**
|
|
1733
2013
|
* Render HTML table for Observation resources
|
|
@@ -1813,26 +2093,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1813
2093
|
var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
1814
2094
|
/**
|
|
1815
2095
|
* Generate HTML narrative for Procedure resources
|
|
1816
|
-
* @param
|
|
2096
|
+
* @param resources - FHIR Procedure resources
|
|
1817
2097
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1818
2098
|
* @returns HTML string for rendering
|
|
1819
2099
|
*/
|
|
1820
|
-
generateNarrative(
|
|
1821
|
-
|
|
1822
|
-
const dateA = a.
|
|
1823
|
-
const dateB = b.
|
|
2100
|
+
generateNarrative(resources, timezone) {
|
|
2101
|
+
resources.sort((a, b) => {
|
|
2102
|
+
const dateA = a.performedDateTime || a.performedPeriod?.start;
|
|
2103
|
+
const dateB = b.performedDateTime || b.performedPeriod?.start;
|
|
1824
2104
|
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1825
2105
|
});
|
|
1826
|
-
return _HistoryOfProceduresTemplate.generateStaticNarrative(
|
|
2106
|
+
return _HistoryOfProceduresTemplate.generateStaticNarrative(resources, timezone);
|
|
1827
2107
|
}
|
|
1828
2108
|
/**
|
|
1829
2109
|
* Internal static implementation that actually generates the narrative
|
|
1830
|
-
* @param
|
|
2110
|
+
* @param resources - FHIR Procedure resources
|
|
1831
2111
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1832
2112
|
* @returns HTML string for rendering
|
|
1833
2113
|
*/
|
|
1834
|
-
static generateStaticNarrative(
|
|
1835
|
-
const templateUtilities = new TemplateUtilities(
|
|
2114
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2115
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1836
2116
|
let html = `
|
|
1837
2117
|
<table>
|
|
1838
2118
|
<thead>
|
|
@@ -1843,16 +2123,14 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1843
2123
|
</tr>
|
|
1844
2124
|
</thead>
|
|
1845
2125
|
<tbody>`;
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
<
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
</tr>`;
|
|
1855
|
-
}
|
|
2126
|
+
for (const resourceItem of resources) {
|
|
2127
|
+
const proc = resourceItem;
|
|
2128
|
+
html += `
|
|
2129
|
+
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
2130
|
+
<td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
|
|
2131
|
+
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
2132
|
+
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
2133
|
+
</tr>`;
|
|
1856
2134
|
}
|
|
1857
2135
|
html += `
|
|
1858
2136
|
</tbody>
|
|
@@ -1865,22 +2143,22 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1865
2143
|
var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
1866
2144
|
/**
|
|
1867
2145
|
* Generate HTML narrative for Social History
|
|
1868
|
-
* @param
|
|
2146
|
+
* @param resources - FHIR Observation resources
|
|
1869
2147
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1870
2148
|
* @returns HTML string for rendering
|
|
1871
2149
|
*/
|
|
1872
|
-
generateNarrative(
|
|
1873
|
-
return _SocialHistoryTemplate.generateStaticNarrative(
|
|
2150
|
+
generateNarrative(resources, timezone) {
|
|
2151
|
+
return _SocialHistoryTemplate.generateStaticNarrative(resources, timezone);
|
|
1874
2152
|
}
|
|
1875
2153
|
/**
|
|
1876
2154
|
* Internal static implementation that actually generates the narrative
|
|
1877
|
-
* @param
|
|
2155
|
+
* @param resources - FHIR Observation resources
|
|
1878
2156
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1879
2157
|
* @returns HTML string for rendering
|
|
1880
2158
|
*/
|
|
1881
|
-
static generateStaticNarrative(
|
|
1882
|
-
const templateUtilities = new TemplateUtilities(
|
|
1883
|
-
const observations =
|
|
2159
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2160
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2161
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1884
2162
|
observations.sort((a, b) => {
|
|
1885
2163
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1886
2164
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1919,14 +2197,14 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1919
2197
|
var PastHistoryOfIllnessTemplate = class {
|
|
1920
2198
|
/**
|
|
1921
2199
|
* Generate HTML narrative for Past History of Illnesses
|
|
1922
|
-
* @param
|
|
2200
|
+
* @param resources - FHIR Condition resources
|
|
1923
2201
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1924
2202
|
* @returns HTML string for rendering
|
|
1925
2203
|
*/
|
|
1926
|
-
generateNarrative(
|
|
1927
|
-
const templateUtilities = new TemplateUtilities(
|
|
2204
|
+
generateNarrative(resources, timezone) {
|
|
2205
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1928
2206
|
let html = ``;
|
|
1929
|
-
const resolvedConditions =
|
|
2207
|
+
const resolvedConditions = resources.map((entry) => entry) || [];
|
|
1930
2208
|
resolvedConditions.sort((a, b) => {
|
|
1931
2209
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1932
2210
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1963,13 +2241,13 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1963
2241
|
var PlanOfCareTemplate = class {
|
|
1964
2242
|
/**
|
|
1965
2243
|
* Generate HTML narrative for Plan of Care
|
|
1966
|
-
* @param
|
|
2244
|
+
* @param resources - FHIR CarePlan resources
|
|
1967
2245
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1968
2246
|
* @returns HTML string for rendering
|
|
1969
2247
|
*/
|
|
1970
|
-
generateNarrative(
|
|
1971
|
-
const templateUtilities = new TemplateUtilities(
|
|
1972
|
-
const carePlans =
|
|
2248
|
+
generateNarrative(resources, timezone) {
|
|
2249
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2250
|
+
const carePlans = resources.map((entry) => entry) || [];
|
|
1973
2251
|
carePlans.sort((a, b) => {
|
|
1974
2252
|
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1975
2253
|
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
@@ -2008,37 +2286,35 @@ var PlanOfCareTemplate = class {
|
|
|
2008
2286
|
var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
2009
2287
|
/**
|
|
2010
2288
|
* Generate HTML narrative for Functional Status
|
|
2011
|
-
* @param
|
|
2289
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
2012
2290
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2013
2291
|
* @returns HTML string for rendering
|
|
2014
2292
|
*/
|
|
2015
|
-
generateNarrative(
|
|
2016
|
-
return _FunctionalStatusTemplate.generateStaticNarrative(
|
|
2293
|
+
generateNarrative(resources, timezone) {
|
|
2294
|
+
return _FunctionalStatusTemplate.generateStaticNarrative(resources, timezone);
|
|
2017
2295
|
}
|
|
2018
2296
|
/**
|
|
2019
2297
|
* Internal static implementation that actually generates the narrative
|
|
2020
|
-
* @param
|
|
2298
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
2021
2299
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2022
2300
|
* @returns HTML string for rendering
|
|
2023
2301
|
*/
|
|
2024
|
-
static generateStaticNarrative(
|
|
2025
|
-
const templateUtilities = new TemplateUtilities(
|
|
2302
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2303
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2026
2304
|
let html = ``;
|
|
2027
2305
|
const activeConditions = [];
|
|
2028
2306
|
const clinicalImpressions = [];
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
activeConditions.push(cond);
|
|
2038
|
-
}
|
|
2039
|
-
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
2040
|
-
clinicalImpressions.push(entry.resource);
|
|
2307
|
+
for (const resourceItem of resources) {
|
|
2308
|
+
if (resourceItem.resourceType === "Condition") {
|
|
2309
|
+
const cond = resourceItem;
|
|
2310
|
+
const isResolved = cond.clinicalStatus?.coding?.some(
|
|
2311
|
+
(c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
|
|
2312
|
+
);
|
|
2313
|
+
if (!isResolved) {
|
|
2314
|
+
activeConditions.push(cond);
|
|
2041
2315
|
}
|
|
2316
|
+
} else if (resourceItem.resourceType === "ClinicalImpression") {
|
|
2317
|
+
clinicalImpressions.push(resourceItem);
|
|
2042
2318
|
}
|
|
2043
2319
|
}
|
|
2044
2320
|
activeConditions.sort((a, b) => {
|
|
@@ -2135,22 +2411,22 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
2135
2411
|
var PregnancyTemplate = class _PregnancyTemplate {
|
|
2136
2412
|
/**
|
|
2137
2413
|
* Generate HTML narrative for Pregnancy
|
|
2138
|
-
* @param
|
|
2414
|
+
* @param resources - FHIR Observation resources
|
|
2139
2415
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2140
2416
|
* @returns HTML string for rendering
|
|
2141
2417
|
*/
|
|
2142
|
-
generateNarrative(
|
|
2143
|
-
return _PregnancyTemplate.generateStaticNarrative(
|
|
2418
|
+
generateNarrative(resources, timezone) {
|
|
2419
|
+
return _PregnancyTemplate.generateStaticNarrative(resources, timezone);
|
|
2144
2420
|
}
|
|
2145
2421
|
/**
|
|
2146
2422
|
* Internal static implementation that actually generates the narrative
|
|
2147
|
-
* @param
|
|
2423
|
+
* @param resources - FHIR Observation resources
|
|
2148
2424
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2149
2425
|
* @returns HTML string for rendering
|
|
2150
2426
|
*/
|
|
2151
|
-
static generateStaticNarrative(
|
|
2152
|
-
const templateUtilities = new TemplateUtilities(
|
|
2153
|
-
const observations =
|
|
2427
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2428
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2429
|
+
const observations = resources.map((entry) => entry) || [];
|
|
2154
2430
|
observations.sort((a, b) => {
|
|
2155
2431
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2156
2432
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -2166,8 +2442,8 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2166
2442
|
</tr>
|
|
2167
2443
|
</thead>
|
|
2168
2444
|
<tbody>`;
|
|
2169
|
-
for (const
|
|
2170
|
-
const obs =
|
|
2445
|
+
for (const resource of observations) {
|
|
2446
|
+
const obs = resource;
|
|
2171
2447
|
html += `
|
|
2172
2448
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2173
2449
|
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
@@ -2186,29 +2462,27 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2186
2462
|
var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
2187
2463
|
/**
|
|
2188
2464
|
* Generate HTML narrative for Advance Directives
|
|
2189
|
-
* @param
|
|
2465
|
+
* @param resources - FHIR Consent resources
|
|
2190
2466
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2191
2467
|
* @returns HTML string for rendering
|
|
2192
2468
|
*/
|
|
2193
|
-
generateNarrative(
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
}
|
|
2201
|
-
return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
|
|
2469
|
+
generateNarrative(resources, timezone) {
|
|
2470
|
+
resources.sort((a, b) => {
|
|
2471
|
+
const dateA = new Date(a.dateTime || 0);
|
|
2472
|
+
const dateB = new Date(b.dateTime || 0);
|
|
2473
|
+
return dateB.getTime() - dateA.getTime();
|
|
2474
|
+
});
|
|
2475
|
+
return _AdvanceDirectivesTemplate.generateStaticNarrative(resources, timezone);
|
|
2202
2476
|
}
|
|
2203
2477
|
/**
|
|
2204
2478
|
* Internal static implementation that actually generates the narrative
|
|
2205
|
-
* @param
|
|
2479
|
+
* @param resources - FHIR Consent resources
|
|
2206
2480
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2207
2481
|
* @returns HTML string for rendering
|
|
2208
2482
|
*/
|
|
2209
2483
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2210
|
-
static generateStaticNarrative(
|
|
2211
|
-
const templateUtilities = new TemplateUtilities(
|
|
2484
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2485
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2212
2486
|
let html = `
|
|
2213
2487
|
<table>
|
|
2214
2488
|
<thead>
|
|
@@ -2220,17 +2494,15 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
2220
2494
|
</tr>
|
|
2221
2495
|
</thead>
|
|
2222
2496
|
<tbody>`;
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
<
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
</tr>`;
|
|
2233
|
-
}
|
|
2497
|
+
for (const resourceItem of resources) {
|
|
2498
|
+
const consent = resourceItem;
|
|
2499
|
+
html += `
|
|
2500
|
+
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
2501
|
+
<td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
|
|
2502
|
+
<td>${consent.status || ""}</td>
|
|
2503
|
+
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
2504
|
+
<td>${consent.dateTime || ""}</td>
|
|
2505
|
+
</tr>`;
|
|
2234
2506
|
}
|
|
2235
2507
|
html += `
|
|
2236
2508
|
</tbody>
|
|
@@ -2244,16 +2516,20 @@ var TypeScriptTemplateMapper = class {
|
|
|
2244
2516
|
/**
|
|
2245
2517
|
* Generates HTML narrative for a specific IPS section
|
|
2246
2518
|
* @param section - The IPS section
|
|
2247
|
-
* @param
|
|
2519
|
+
* @param resources - FHIR resources
|
|
2248
2520
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2521
|
+
* @param useSectionSummary - Whether to use the section summary for narrative generation
|
|
2249
2522
|
* @returns HTML string for rendering
|
|
2250
2523
|
*/
|
|
2251
|
-
static generateNarrative(section,
|
|
2524
|
+
static generateNarrative(section, resources, timezone, useSectionSummary = false) {
|
|
2252
2525
|
const templateClass = this.sectionToTemplate[section];
|
|
2253
2526
|
if (!templateClass) {
|
|
2254
2527
|
throw new Error(`No template found for section: ${section}`);
|
|
2255
2528
|
}
|
|
2256
|
-
return templateClass.
|
|
2529
|
+
return useSectionSummary ? templateClass.generateSummaryNarrative(
|
|
2530
|
+
resources,
|
|
2531
|
+
timezone
|
|
2532
|
+
) : templateClass.generateNarrative(resources, timezone);
|
|
2257
2533
|
}
|
|
2258
2534
|
};
|
|
2259
2535
|
// Map of section types to their template classes
|
|
@@ -2309,21 +2585,15 @@ var NarrativeGenerator = class {
|
|
|
2309
2585
|
* @param section - IPS section type
|
|
2310
2586
|
* @param resources - Array of domain resources
|
|
2311
2587
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2588
|
+
* @param useSectionSummary - Whether to use section summary for narrative generation (default: false)
|
|
2312
2589
|
* @returns Generated HTML content or undefined if no resources
|
|
2313
2590
|
*/
|
|
2314
|
-
static async generateNarrativeContentAsync(section, resources, timezone) {
|
|
2591
|
+
static async generateNarrativeContentAsync(section, resources, timezone, useSectionSummary = false) {
|
|
2315
2592
|
if (!resources || resources.length === 0) {
|
|
2316
2593
|
return void 0;
|
|
2317
2594
|
}
|
|
2318
2595
|
try {
|
|
2319
|
-
const
|
|
2320
|
-
resourceType: "Bundle",
|
|
2321
|
-
type: "collection",
|
|
2322
|
-
entry: resources.map((resource) => ({
|
|
2323
|
-
resource
|
|
2324
|
-
}))
|
|
2325
|
-
};
|
|
2326
|
-
const content = TypeScriptTemplateMapper.generateNarrative(section, bundle, timezone);
|
|
2596
|
+
const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone, useSectionSummary);
|
|
2327
2597
|
if (!content) {
|
|
2328
2598
|
return void 0;
|
|
2329
2599
|
}
|
|
@@ -2374,10 +2644,11 @@ var NarrativeGenerator = class {
|
|
|
2374
2644
|
* @param resources - Array of domain resources
|
|
2375
2645
|
* @param timezone - Optional timezone to use for date formatting
|
|
2376
2646
|
* @param minify - Whether to minify the HTML content (default: true)
|
|
2647
|
+
* @param useSectionSummary - Whether to use section summary for narrative generation (default: false)
|
|
2377
2648
|
* @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
|
|
2378
2649
|
*/
|
|
2379
|
-
static async generateNarrativeAsync(section, resources, timezone, minify = true) {
|
|
2380
|
-
const content = await this.generateNarrativeContentAsync(section, resources, timezone);
|
|
2650
|
+
static async generateNarrativeAsync(section, resources, timezone, minify = true, useSectionSummary = false) {
|
|
2651
|
+
const content = await this.generateNarrativeContentAsync(section, resources, timezone, useSectionSummary);
|
|
2381
2652
|
if (!content) {
|
|
2382
2653
|
return void 0;
|
|
2383
2654
|
}
|
|
@@ -2408,22 +2679,51 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2408
2679
|
/**
|
|
2409
2680
|
* sets the patient resource for the IPS Composition.
|
|
2410
2681
|
* This is not needed if you are calling read_bundle, but can be used to set the patient resource directly.
|
|
2411
|
-
* @param
|
|
2682
|
+
* @param patients - FHIR Patient resource to set
|
|
2412
2683
|
*/
|
|
2413
|
-
setPatient(
|
|
2414
|
-
if (!
|
|
2684
|
+
setPatient(patients) {
|
|
2685
|
+
if (!Array.isArray(patients)) {
|
|
2686
|
+
patients = [patients];
|
|
2687
|
+
}
|
|
2688
|
+
if (patients.length === 0 || !patients.every((patient) => patient.resourceType === "Patient")) {
|
|
2415
2689
|
throw new Error("Invalid Patient resource");
|
|
2416
2690
|
}
|
|
2417
|
-
this.
|
|
2691
|
+
this.patients = patients;
|
|
2418
2692
|
return this;
|
|
2419
2693
|
}
|
|
2420
2694
|
/**
|
|
2421
2695
|
* Adds a section to the composition with async HTML minification
|
|
2696
|
+
* @param narrative - Narrative content for the section
|
|
2697
|
+
* @param sectionType - IPS section type
|
|
2698
|
+
* @param validResources - Array of domain resources
|
|
2699
|
+
*/
|
|
2700
|
+
addSectionAsync(narrative, sectionType, validResources) {
|
|
2701
|
+
const sectionEntry = {
|
|
2702
|
+
title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
|
|
2703
|
+
code: {
|
|
2704
|
+
coding: [{
|
|
2705
|
+
system: "http://loinc.org",
|
|
2706
|
+
code: IPS_SECTION_LOINC_CODES[sectionType],
|
|
2707
|
+
display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
2708
|
+
}],
|
|
2709
|
+
text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
2710
|
+
},
|
|
2711
|
+
text: narrative,
|
|
2712
|
+
entry: validResources.map((resource) => ({
|
|
2713
|
+
reference: `${resource.resourceType}/${resource.id}`,
|
|
2714
|
+
display: resource.resourceType
|
|
2715
|
+
}))
|
|
2716
|
+
};
|
|
2717
|
+
this.sections.push(sectionEntry);
|
|
2718
|
+
return this;
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Make and adds a section to the composition with async HTML minification
|
|
2422
2722
|
* @param sectionType - IPS section type
|
|
2423
2723
|
* @param validResources - Array of domain resources
|
|
2424
2724
|
* @param timezone - Optional timezone to use for date formatting
|
|
2425
2725
|
*/
|
|
2426
|
-
async
|
|
2726
|
+
async makeSectionAsync(sectionType, validResources, timezone) {
|
|
2427
2727
|
for (const resource of validResources) {
|
|
2428
2728
|
this.resources.add(resource);
|
|
2429
2729
|
}
|
|
@@ -2443,51 +2743,68 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2443
2743
|
} else {
|
|
2444
2744
|
return this;
|
|
2445
2745
|
}
|
|
2446
|
-
|
|
2447
|
-
title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
|
|
2448
|
-
code: {
|
|
2449
|
-
coding: [{
|
|
2450
|
-
system: "http://loinc.org",
|
|
2451
|
-
code: IPS_SECTION_LOINC_CODES[sectionType],
|
|
2452
|
-
display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
2453
|
-
}],
|
|
2454
|
-
text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
2455
|
-
},
|
|
2456
|
-
text: narrative,
|
|
2457
|
-
entry: validResources.map((resource) => ({
|
|
2458
|
-
reference: `${resource.resourceType}/${resource.id}`,
|
|
2459
|
-
display: resource.resourceType
|
|
2460
|
-
}))
|
|
2461
|
-
};
|
|
2462
|
-
this.sections.push(sectionEntry);
|
|
2746
|
+
this.addSectionAsync(narrative, sectionType, validResources);
|
|
2463
2747
|
}
|
|
2464
2748
|
return this;
|
|
2465
2749
|
}
|
|
2750
|
+
async makeSectionFromSummaryAsync(sectionType, summaryCompositions, resources, timezone) {
|
|
2751
|
+
const sectionResources = [];
|
|
2752
|
+
for (const summaryComposition of summaryCompositions) {
|
|
2753
|
+
const resourceEntries = summaryComposition?.section?.flatMap((sec) => sec.entry || []) ?? [];
|
|
2754
|
+
resources.forEach((resource) => {
|
|
2755
|
+
if (resourceEntries?.some((entry) => entry.reference === `${resource.resourceType}/${resource.id}`)) {
|
|
2756
|
+
this.resources.add(resource);
|
|
2757
|
+
sectionResources.push(resource);
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
}
|
|
2761
|
+
const narrative = await NarrativeGenerator.generateNarrativeAsync(
|
|
2762
|
+
sectionType,
|
|
2763
|
+
summaryCompositions,
|
|
2764
|
+
timezone,
|
|
2765
|
+
true,
|
|
2766
|
+
true
|
|
2767
|
+
);
|
|
2768
|
+
this.addSectionAsync(narrative, sectionType, sectionResources);
|
|
2769
|
+
return this;
|
|
2770
|
+
}
|
|
2466
2771
|
/**
|
|
2467
2772
|
* Reads a FHIR Bundle and extracts resources for each section defined in IPSSections.
|
|
2468
2773
|
* @param bundle - FHIR Bundle containing resources
|
|
2469
2774
|
* @param timezone - Optional timezone to use for date formatting
|
|
2775
|
+
* @param useSummaryCompositions - Whether to use summary compositions (default: false)
|
|
2470
2776
|
*/
|
|
2471
|
-
async readBundleAsync(bundle, timezone) {
|
|
2777
|
+
async readBundleAsync(bundle, timezone, useSummaryCompositions = false) {
|
|
2472
2778
|
if (!bundle.entry) {
|
|
2473
2779
|
return this;
|
|
2474
2780
|
}
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2781
|
+
const patientEntries = [];
|
|
2782
|
+
const resources = [];
|
|
2783
|
+
bundle.entry.forEach((e) => {
|
|
2784
|
+
if (e.resource?.resourceType === "Patient") {
|
|
2785
|
+
patientEntries.push(e.resource);
|
|
2786
|
+
this.resources.add(e.resource);
|
|
2787
|
+
} else if (e.resource) {
|
|
2788
|
+
resources.push(e.resource);
|
|
2789
|
+
}
|
|
2790
|
+
});
|
|
2791
|
+
if (patientEntries.length === 0) {
|
|
2477
2792
|
throw new Error("Patient resource not found in the bundle");
|
|
2478
2793
|
}
|
|
2479
|
-
this.
|
|
2480
|
-
const resources = bundle.entry.map((e) => e.resource);
|
|
2794
|
+
this.patients = patientEntries;
|
|
2481
2795
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
);
|
|
2487
|
-
if (
|
|
2488
|
-
|
|
2796
|
+
if (sectionType === "Patient" /* PATIENT */) {
|
|
2797
|
+
continue;
|
|
2798
|
+
}
|
|
2799
|
+
const summaryCompositionFilter = useSummaryCompositions ? IPSSectionResourceHelper.getSummaryCompositionFilterForSection(sectionType) : void 0;
|
|
2800
|
+
const sectionSummary = summaryCompositionFilter ? resources.filter((resource) => summaryCompositionFilter(resource)) : void 0;
|
|
2801
|
+
if (sectionSummary) {
|
|
2802
|
+
await this.makeSectionFromSummaryAsync(sectionType, sectionSummary, resources, timezone);
|
|
2803
|
+
} else {
|
|
2804
|
+
const sectionFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2805
|
+
const sectionResources = resources.filter((resource) => sectionFilter(resource));
|
|
2806
|
+
await this.makeSectionAsync(sectionType, sectionResources, timezone);
|
|
2489
2807
|
}
|
|
2490
|
-
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2491
2808
|
}
|
|
2492
2809
|
return this;
|
|
2493
2810
|
}
|
|
@@ -2497,16 +2814,18 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2497
2814
|
* @param authorOrganizationName - Name of the authoring organization
|
|
2498
2815
|
* @param baseUrl - Base URL for the FHIR server (e.g., 'https://example.com/fhir')
|
|
2499
2816
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2817
|
+
* @param patientId - Optional patient ID to use as primary patient for composition reference
|
|
2500
2818
|
*/
|
|
2501
|
-
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone) {
|
|
2819
|
+
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId) {
|
|
2502
2820
|
if (baseUrl.endsWith("/")) {
|
|
2503
2821
|
baseUrl = baseUrl.slice(0, -1);
|
|
2504
2822
|
}
|
|
2505
|
-
if (!this.
|
|
2823
|
+
if (!this.patients) {
|
|
2506
2824
|
throw new Error("Patient resource must be set before building the bundle");
|
|
2507
2825
|
}
|
|
2826
|
+
const primaryPatientId = patientId ?? this.patients[0].id;
|
|
2508
2827
|
const composition = {
|
|
2509
|
-
id: `Composition-${
|
|
2828
|
+
id: `Composition-${primaryPatientId}`,
|
|
2510
2829
|
resourceType: "Composition",
|
|
2511
2830
|
status: "final",
|
|
2512
2831
|
type: {
|
|
@@ -2517,7 +2836,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2517
2836
|
}]
|
|
2518
2837
|
},
|
|
2519
2838
|
subject: {
|
|
2520
|
-
reference: `Patient/${
|
|
2839
|
+
reference: `Patient/${primaryPatientId}`
|
|
2521
2840
|
},
|
|
2522
2841
|
author: [{
|
|
2523
2842
|
reference: `Organization/${authorOrganizationId}`,
|
|
@@ -2529,7 +2848,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2529
2848
|
section: this.sections,
|
|
2530
2849
|
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2531
2850
|
"Patient" /* PATIENT */,
|
|
2532
|
-
|
|
2851
|
+
this.patients,
|
|
2533
2852
|
timezone,
|
|
2534
2853
|
true
|
|
2535
2854
|
)
|
|
@@ -2548,9 +2867,11 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2548
2867
|
fullUrl: `${baseUrl}/Composition/${composition.id}`,
|
|
2549
2868
|
resource: composition
|
|
2550
2869
|
});
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2870
|
+
this.patients.forEach((patient) => {
|
|
2871
|
+
bundle.entry?.push({
|
|
2872
|
+
fullUrl: `${baseUrl}/Patient/${patient.id}`,
|
|
2873
|
+
resource: patient
|
|
2874
|
+
});
|
|
2554
2875
|
});
|
|
2555
2876
|
this.resources.forEach((resource) => {
|
|
2556
2877
|
if (resource.resourceType !== "Patient") {
|