@imranq2/fhirpatientsummary 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +427 -343
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +427 -343
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -103,35 +103,28 @@ var SOCIAL_HISTORY_LOINC_CODES = {
|
|
|
103
103
|
"72166-2": "Tobacco Use",
|
|
104
104
|
"74013-4": "Alcohol Use"
|
|
105
105
|
};
|
|
106
|
+
var BLOOD_PRESSURE_LOINC_CODES = {
|
|
107
|
+
OBSERVATION: "85354-9",
|
|
108
|
+
SYSTOLIC: "8480-6",
|
|
109
|
+
DIASTOLIC: "8462-4"
|
|
110
|
+
};
|
|
106
111
|
|
|
107
112
|
// src/structures/ips_section_resource_map.ts
|
|
108
|
-
var IPSSectionResourceMap = {
|
|
109
|
-
["Patient" /* PATIENT */]: ["Patient"],
|
|
110
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
111
|
-
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
112
|
-
// Medication resource is needed for identifying name of medication
|
|
113
|
-
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
114
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
115
|
-
// Immunization can include Organization as a related resource
|
|
116
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
117
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
118
|
-
// Device resource is used for medical devices name
|
|
119
|
-
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
120
|
-
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
121
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
|
|
122
|
-
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
123
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
124
|
-
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
125
|
-
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
126
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
127
|
-
};
|
|
128
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),
|
|
129
120
|
// Only include active conditions
|
|
130
121
|
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
131
122
|
// Only include completed immunizations
|
|
132
123
|
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
133
124
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
134
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),
|
|
135
128
|
// Only include finalized diagnostic reports
|
|
136
129
|
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
137
130
|
// Only include completed procedures
|
|
@@ -147,14 +140,9 @@ var IPSSectionResourceFilters = {
|
|
|
147
140
|
// Only include active care plans
|
|
148
141
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
149
142
|
// Only include active advance directives (Consent resources)
|
|
150
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
151
|
-
// Patient section: only Patient resource
|
|
152
|
-
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
|
|
143
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
153
144
|
};
|
|
154
145
|
var IPSSectionResourceHelper = class {
|
|
155
|
-
static getResourceTypesForSection(section) {
|
|
156
|
-
return IPSSectionResourceMap[section] || [];
|
|
157
|
-
}
|
|
158
146
|
static getResourceFilterForSection(section) {
|
|
159
147
|
return IPSSectionResourceFilters[section];
|
|
160
148
|
}
|
|
@@ -164,11 +152,11 @@ var IPSSectionResourceHelper = class {
|
|
|
164
152
|
var import_luxon = require("luxon");
|
|
165
153
|
var TemplateUtilities = class {
|
|
166
154
|
/**
|
|
167
|
-
* Constructor to initialize the TemplateUtilities with a FHIR
|
|
168
|
-
* @param
|
|
155
|
+
* Constructor to initialize the TemplateUtilities with a FHIR resources
|
|
156
|
+
* @param resources - FHIR resources
|
|
169
157
|
*/
|
|
170
|
-
constructor(
|
|
171
|
-
this.
|
|
158
|
+
constructor(resources) {
|
|
159
|
+
this.resources = resources;
|
|
172
160
|
}
|
|
173
161
|
/**
|
|
174
162
|
* Formats a CodeableConcept object
|
|
@@ -199,7 +187,7 @@ var TemplateUtilities = class {
|
|
|
199
187
|
return "";
|
|
200
188
|
}
|
|
201
189
|
resolveReference(ref) {
|
|
202
|
-
if (!ref || !this.
|
|
190
|
+
if (!ref || !this.resources) {
|
|
203
191
|
return null;
|
|
204
192
|
}
|
|
205
193
|
const referenceParts = ref.reference?.split("/");
|
|
@@ -208,10 +196,10 @@ var TemplateUtilities = class {
|
|
|
208
196
|
}
|
|
209
197
|
const referenceResourceType = referenceParts[0];
|
|
210
198
|
const referenceResourceId = referenceParts[1];
|
|
211
|
-
const resource = this.
|
|
212
|
-
return entry.
|
|
199
|
+
const resource = this.resources.find((entry) => {
|
|
200
|
+
return entry.resourceType === referenceResourceType && entry.id === referenceResourceId;
|
|
213
201
|
});
|
|
214
|
-
return resource ? resource
|
|
202
|
+
return resource ? resource : null;
|
|
215
203
|
}
|
|
216
204
|
/**
|
|
217
205
|
* Renders a Device reference
|
|
@@ -530,6 +518,28 @@ var TemplateUtilities = class {
|
|
|
530
518
|
return "";
|
|
531
519
|
}
|
|
532
520
|
extractObservationValue(observation) {
|
|
521
|
+
if (observation.code && observation.code.coding && "component" in observation && Array.isArray(observation.component)) {
|
|
522
|
+
const bpCode = observation.code.coding.find(
|
|
523
|
+
(c) => c.code === BLOOD_PRESSURE_LOINC_CODES.OBSERVATION
|
|
524
|
+
);
|
|
525
|
+
if (bpCode) {
|
|
526
|
+
const systolicComponent = observation.component?.find(
|
|
527
|
+
(c) => c.code?.coding?.some(
|
|
528
|
+
(cc) => cc.code === BLOOD_PRESSURE_LOINC_CODES.SYSTOLIC
|
|
529
|
+
)
|
|
530
|
+
);
|
|
531
|
+
const diastolicComponent = observation.component?.find(
|
|
532
|
+
(c) => c.code?.coding?.some(
|
|
533
|
+
(cc) => cc.code === BLOOD_PRESSURE_LOINC_CODES.DIASTOLIC
|
|
534
|
+
)
|
|
535
|
+
);
|
|
536
|
+
if (systolicComponent && diastolicComponent) {
|
|
537
|
+
const systolic = this.extractObservationValue(systolicComponent);
|
|
538
|
+
const diastolic = this.extractObservationValue(diastolicComponent);
|
|
539
|
+
return `${systolic}/${diastolic}`;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
533
543
|
const valueFields = [
|
|
534
544
|
"valueString",
|
|
535
545
|
"valueInteger",
|
|
@@ -760,7 +770,7 @@ var TemplateUtilities = class {
|
|
|
760
770
|
if (parts.length === 2) {
|
|
761
771
|
const resourceType = parts[0];
|
|
762
772
|
const resourceId = parts[1];
|
|
763
|
-
const resource = this.
|
|
773
|
+
const resource = this.resources?.find((resource2) => resource2.resourceType === resourceType && resource2.id === resourceId);
|
|
764
774
|
if (resource) {
|
|
765
775
|
return `${resourceType}/${resourceId}`;
|
|
766
776
|
}
|
|
@@ -800,48 +810,96 @@ var TemplateUtilities = class {
|
|
|
800
810
|
// src/narratives/templates/typescript/PatientTemplate.ts
|
|
801
811
|
var PatientTemplate = class _PatientTemplate {
|
|
802
812
|
/**
|
|
803
|
-
* Generate HTML narrative for Patient
|
|
804
|
-
* @param
|
|
813
|
+
* Generate HTML narrative for Patient resources
|
|
814
|
+
* @param resources - FHIR Patient resources
|
|
805
815
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
806
816
|
* @returns HTML string for rendering
|
|
807
817
|
*/
|
|
808
|
-
generateNarrative(
|
|
809
|
-
return _PatientTemplate.generateStaticNarrative(
|
|
818
|
+
generateNarrative(resources, timezone) {
|
|
819
|
+
return _PatientTemplate.generateStaticNarrative(resources, timezone);
|
|
810
820
|
}
|
|
811
821
|
/**
|
|
812
822
|
* Internal static implementation that actually generates the narrative
|
|
813
|
-
* @param
|
|
823
|
+
* @param resources - FHIR Patient resources
|
|
814
824
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
815
825
|
* @returns HTML string for rendering
|
|
816
826
|
*/
|
|
817
827
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
818
|
-
static generateStaticNarrative(
|
|
819
|
-
const templateUtilities = new TemplateUtilities(
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
<
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
828
|
+
static generateStaticNarrative(resources, timezone) {
|
|
829
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
830
|
+
const combinedPatient = this.combinePatients(resources);
|
|
831
|
+
return `<div>
|
|
832
|
+
<ul>
|
|
833
|
+
<li><strong>Name(s):</strong>${this.renderNames(combinedPatient)}</li>
|
|
834
|
+
<li><strong>Gender:</strong>${combinedPatient.gender ? this.capitalize(combinedPatient.gender) : ""}</li>
|
|
835
|
+
<li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
|
|
836
|
+
<li><strong>Identifier(s):</strong>${this.renderIdentifiers(combinedPatient)}</li>
|
|
837
|
+
<li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
|
|
838
|
+
<li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
|
|
839
|
+
<li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
|
|
840
|
+
<li><strong>Deceased:</strong>${this.renderDeceased(combinedPatient)}</li>
|
|
841
|
+
<li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
|
|
842
|
+
</ul>
|
|
843
|
+
</div>`;
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Combines multiple patient resources into a single patient object
|
|
847
|
+
* Merges fields, preferring non-empty values
|
|
848
|
+
* @param patients - Array of patient resources
|
|
849
|
+
* @returns Combined patient resource
|
|
850
|
+
*/
|
|
851
|
+
static combinePatients(patients) {
|
|
852
|
+
if (patients.length === 1) {
|
|
853
|
+
return patients[0];
|
|
854
|
+
}
|
|
855
|
+
const combined = patients[0];
|
|
856
|
+
const allNames = [];
|
|
857
|
+
const allIdentifiers = [];
|
|
858
|
+
const allTelecom = [];
|
|
859
|
+
const allAddresses = [];
|
|
860
|
+
const allCommunication = [];
|
|
861
|
+
patients.forEach((patient) => {
|
|
862
|
+
if (patient.name) {
|
|
863
|
+
allNames.push(...patient.name);
|
|
838
864
|
}
|
|
839
|
-
|
|
840
|
-
|
|
865
|
+
if (patient.identifier) {
|
|
866
|
+
allIdentifiers.push(...patient.identifier);
|
|
867
|
+
}
|
|
868
|
+
if (patient.telecom) {
|
|
869
|
+
allTelecom.push(...patient.telecom);
|
|
870
|
+
}
|
|
871
|
+
if (patient.address) {
|
|
872
|
+
allAddresses.push(...patient.address);
|
|
873
|
+
}
|
|
874
|
+
if (patient.communication) {
|
|
875
|
+
allCommunication.push(...patient.communication);
|
|
876
|
+
}
|
|
877
|
+
if (!combined.gender && patient.gender) {
|
|
878
|
+
combined.gender = patient.gender;
|
|
879
|
+
}
|
|
880
|
+
if (!combined.birthDate && patient.birthDate) {
|
|
881
|
+
combined.birthDate = patient.birthDate;
|
|
882
|
+
}
|
|
883
|
+
if (!combined.maritalStatus && patient.maritalStatus) {
|
|
884
|
+
combined.maritalStatus = patient.maritalStatus;
|
|
885
|
+
}
|
|
886
|
+
if (!combined.deceasedBoolean && patient.deceasedBoolean !== void 0) {
|
|
887
|
+
combined.deceasedBoolean = patient.deceasedBoolean;
|
|
888
|
+
}
|
|
889
|
+
if (!combined.deceasedDateTime && patient.deceasedDateTime) {
|
|
890
|
+
combined.deceasedDateTime = patient.deceasedDateTime;
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
combined.name = allNames;
|
|
894
|
+
combined.identifier = allIdentifiers;
|
|
895
|
+
combined.telecom = allTelecom;
|
|
896
|
+
combined.address = allAddresses;
|
|
897
|
+
combined.communication = allCommunication;
|
|
898
|
+
return combined;
|
|
841
899
|
}
|
|
842
900
|
/**
|
|
843
901
|
* Renders patient names as HTML list items
|
|
844
|
-
* @param patient - Patient
|
|
902
|
+
* @param patient - Patient resources
|
|
845
903
|
* @returns HTML string of list items
|
|
846
904
|
*/
|
|
847
905
|
static renderNames(patient) {
|
|
@@ -852,14 +910,16 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
852
910
|
patient.name.forEach((name) => {
|
|
853
911
|
if (name.use !== "old") {
|
|
854
912
|
const nameText = name.text || ((name.given || []).join(" ") + " " + (name.family || "")).trim();
|
|
855
|
-
|
|
913
|
+
if (nameText) {
|
|
914
|
+
uniqueNames.add(nameText);
|
|
915
|
+
}
|
|
856
916
|
}
|
|
857
917
|
});
|
|
858
918
|
return Array.from(uniqueNames).map((nameText) => `<ul><li>${nameText}</li></ul>`).join("");
|
|
859
919
|
}
|
|
860
920
|
/**
|
|
861
921
|
* Renders patient identifiers as HTML list items
|
|
862
|
-
* @param patient - Patient
|
|
922
|
+
* @param patient - Patient resources
|
|
863
923
|
* @returns HTML string of list items
|
|
864
924
|
*/
|
|
865
925
|
static renderIdentifiers(patient) {
|
|
@@ -874,7 +934,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
874
934
|
}
|
|
875
935
|
/**
|
|
876
936
|
* Renders patient telecom information grouped by system
|
|
877
|
-
* @param patient - Patient
|
|
937
|
+
* @param patient - Patient resources
|
|
878
938
|
* @returns HTML string grouped by system
|
|
879
939
|
*/
|
|
880
940
|
static renderTelecom(patient) {
|
|
@@ -882,9 +942,10 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
882
942
|
return "";
|
|
883
943
|
}
|
|
884
944
|
const systemPriority = ["email", "phone", "pager", "sms", "fax", "url", "other"];
|
|
945
|
+
const numberSystems = ["phone", "pager", "sms", "fax"];
|
|
885
946
|
const telecomBySystem = /* @__PURE__ */ new Map();
|
|
886
947
|
patient.telecom.forEach((telecom) => {
|
|
887
|
-
if (telecom.system && telecom.value) {
|
|
948
|
+
if (telecom.system && telecom.value && telecom.use !== "old") {
|
|
888
949
|
const system = telecom.system.toLowerCase();
|
|
889
950
|
if (!telecomBySystem.has(system)) {
|
|
890
951
|
telecomBySystem.set(system, /* @__PURE__ */ new Set());
|
|
@@ -892,6 +953,29 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
892
953
|
telecomBySystem.get(system).add(telecom.value);
|
|
893
954
|
}
|
|
894
955
|
});
|
|
956
|
+
for (const system of numberSystems) {
|
|
957
|
+
const currentNumbers = Array.from(telecomBySystem.get(system) || []);
|
|
958
|
+
if (currentNumbers.length <= 1) continue;
|
|
959
|
+
const numbersWithCleaned = currentNumbers.map((num) => ({
|
|
960
|
+
original: num,
|
|
961
|
+
cleaned: num.replace(/\D/g, "")
|
|
962
|
+
}));
|
|
963
|
+
const toRemove = /* @__PURE__ */ new Set();
|
|
964
|
+
for (let i = 0; i < numbersWithCleaned.length; i++) {
|
|
965
|
+
for (let j = i + 1; j < numbersWithCleaned.length; j++) {
|
|
966
|
+
const num1 = numbersWithCleaned[i];
|
|
967
|
+
const num2 = numbersWithCleaned[j];
|
|
968
|
+
if (num1.cleaned.endsWith(num2.cleaned)) {
|
|
969
|
+
toRemove.add(num2.original);
|
|
970
|
+
} else if (num2.cleaned.endsWith(num1.cleaned)) {
|
|
971
|
+
toRemove.add(num1.original);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
toRemove.forEach((numberToRemove) => {
|
|
976
|
+
telecomBySystem.get(system)?.delete(numberToRemove);
|
|
977
|
+
});
|
|
978
|
+
}
|
|
895
979
|
return Array.from(telecomBySystem.entries()).sort(([systemA], [systemB]) => {
|
|
896
980
|
const priorityA = systemPriority.indexOf(systemA);
|
|
897
981
|
const priorityB = systemPriority.indexOf(systemB);
|
|
@@ -909,7 +993,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
909
993
|
}
|
|
910
994
|
/**
|
|
911
995
|
* Renders patient addresses as HTML list items
|
|
912
|
-
* @param patient - Patient
|
|
996
|
+
* @param patient - Patient resources
|
|
913
997
|
* @returns HTML string of list items
|
|
914
998
|
*/
|
|
915
999
|
static renderAddresses(patient) {
|
|
@@ -918,7 +1002,33 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
918
1002
|
}
|
|
919
1003
|
const uniqueAddresses = /* @__PURE__ */ new Set();
|
|
920
1004
|
patient.address.forEach((address) => {
|
|
921
|
-
|
|
1005
|
+
if (address.use === "old") {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
const addressArray = [];
|
|
1009
|
+
if (address.text) {
|
|
1010
|
+
addressArray.push(address.text);
|
|
1011
|
+
} else {
|
|
1012
|
+
if (address.line) {
|
|
1013
|
+
addressArray.push(...address.line);
|
|
1014
|
+
}
|
|
1015
|
+
if (address.city) {
|
|
1016
|
+
addressArray.push(address.city);
|
|
1017
|
+
}
|
|
1018
|
+
if (address.district) {
|
|
1019
|
+
addressArray.push(address.district);
|
|
1020
|
+
}
|
|
1021
|
+
if (address.state) {
|
|
1022
|
+
addressArray.push(address.state);
|
|
1023
|
+
}
|
|
1024
|
+
if (address.country) {
|
|
1025
|
+
addressArray.push(address.country);
|
|
1026
|
+
}
|
|
1027
|
+
if (address.postalCode) {
|
|
1028
|
+
addressArray.push(address.postalCode);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
const addressText = addressArray.join(", ").trim();
|
|
922
1032
|
if (addressText) {
|
|
923
1033
|
uniqueAddresses.add(addressText);
|
|
924
1034
|
}
|
|
@@ -927,7 +1037,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
927
1037
|
}
|
|
928
1038
|
/**
|
|
929
1039
|
* Renders patient deceased status
|
|
930
|
-
* @param patient - Patient
|
|
1040
|
+
* @param patient - Patient resources
|
|
931
1041
|
* @returns HTML string for deceased status
|
|
932
1042
|
*/
|
|
933
1043
|
static renderDeceased(patient) {
|
|
@@ -942,19 +1052,25 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
942
1052
|
/**
|
|
943
1053
|
* Renders patient communication preferences as HTML list items
|
|
944
1054
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
945
|
-
* @param patient - Patient
|
|
1055
|
+
* @param patient - Patient resources
|
|
946
1056
|
* @returns HTML string of list items
|
|
947
1057
|
*/
|
|
948
1058
|
static renderCommunication(templateUtilities, patient) {
|
|
949
1059
|
if (!patient.communication || patient.communication.length === 0) {
|
|
950
1060
|
return "";
|
|
951
1061
|
}
|
|
952
|
-
|
|
953
|
-
|
|
1062
|
+
const uniqueLanguages = /* @__PURE__ */ new Set();
|
|
1063
|
+
const preferredLanguages = /* @__PURE__ */ new Set();
|
|
1064
|
+
patient.communication.forEach((comm) => {
|
|
954
1065
|
const language = templateUtilities.codeableConcept(comm.language);
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1066
|
+
if (language) {
|
|
1067
|
+
if (comm.preferred) {
|
|
1068
|
+
preferredLanguages.add(language);
|
|
1069
|
+
}
|
|
1070
|
+
uniqueLanguages.add(language);
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
return Array.from(uniqueLanguages).map((language) => `<ul><li>${language}${preferredLanguages.has(language) ? " (preferred)" : ""}</li></ul>`).join("");
|
|
958
1074
|
}
|
|
959
1075
|
/**
|
|
960
1076
|
* Capitalizes first letter of a string
|
|
@@ -971,32 +1087,30 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
971
1087
|
var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
972
1088
|
/**
|
|
973
1089
|
* Generate HTML narrative for AllergyIntolerance resources
|
|
974
|
-
* @param
|
|
1090
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
975
1091
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
976
1092
|
* @returns HTML string for rendering
|
|
977
1093
|
*/
|
|
978
|
-
generateNarrative(
|
|
979
|
-
return _AllergyIntoleranceTemplate.generateStaticNarrative(
|
|
1094
|
+
generateNarrative(resources, timezone) {
|
|
1095
|
+
return _AllergyIntoleranceTemplate.generateStaticNarrative(resources, timezone);
|
|
980
1096
|
}
|
|
981
1097
|
/**
|
|
982
1098
|
* Internal static implementation that actually generates the narrative
|
|
983
|
-
* @param
|
|
1099
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
984
1100
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
985
1101
|
* @returns HTML string for rendering
|
|
986
1102
|
*/
|
|
987
|
-
static generateStaticNarrative(
|
|
988
|
-
const templateUtilities = new TemplateUtilities(
|
|
1103
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1104
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
989
1105
|
const activeAllergies = [];
|
|
990
1106
|
const resolvedAllergies = [];
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
activeAllergies.push(allergy);
|
|
999
|
-
}
|
|
1107
|
+
for (const resourceItem of resources) {
|
|
1108
|
+
const allergy = resourceItem;
|
|
1109
|
+
const isResolved = allergy.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code));
|
|
1110
|
+
if (isResolved) {
|
|
1111
|
+
resolvedAllergies.push(allergy);
|
|
1112
|
+
} else {
|
|
1113
|
+
activeAllergies.push(allergy);
|
|
1000
1114
|
}
|
|
1001
1115
|
}
|
|
1002
1116
|
activeAllergies.sort((a, b) => {
|
|
@@ -1020,7 +1134,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1020
1134
|
<th>Status</th>
|
|
1021
1135
|
<th>Category</th>
|
|
1022
1136
|
<th>Reaction</th>
|
|
1023
|
-
<th>Severity</th>
|
|
1024
1137
|
<th>Onset Date</th>
|
|
1025
1138
|
<th>Comments</th>
|
|
1026
1139
|
</tr>
|
|
@@ -1048,7 +1161,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1048
1161
|
<th>Status</th>
|
|
1049
1162
|
<th>Category</th>
|
|
1050
1163
|
<th>Reaction</th>
|
|
1051
|
-
<th>Severity</th>
|
|
1052
1164
|
<th>Onset Date</th>
|
|
1053
1165
|
<th>Comments</th>
|
|
1054
1166
|
<th>Resolved Date</th>
|
|
@@ -1086,7 +1198,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1086
1198
|
<td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
|
|
1087
1199
|
<td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
|
|
1088
1200
|
<td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
|
|
1089
|
-
<td class="Severity">${templateUtilities.safeConcat(allergy.reaction, "severity") || "-"}</td>
|
|
1090
1201
|
<td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
|
|
1091
1202
|
<td class="Comments">${templateUtilities.renderNotes(allergy.note, timezone, { styled: true, warning: true })}</td>`;
|
|
1092
1203
|
if (includeResolved) {
|
|
@@ -1112,12 +1223,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1112
1223
|
var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
1113
1224
|
/**
|
|
1114
1225
|
* Generate HTML narrative for Medication resources
|
|
1115
|
-
* @param
|
|
1226
|
+
* @param resources - FHIR Medication resources
|
|
1116
1227
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1117
1228
|
* @returns HTML string for rendering
|
|
1118
1229
|
*/
|
|
1119
|
-
generateNarrative(
|
|
1120
|
-
return _MedicationSummaryTemplate.generateStaticNarrative(
|
|
1230
|
+
generateNarrative(resources, timezone) {
|
|
1231
|
+
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
|
|
1121
1232
|
}
|
|
1122
1233
|
/**
|
|
1123
1234
|
* Safely parse a date string and return a valid Date object or null
|
|
@@ -1182,16 +1293,16 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1182
1293
|
}
|
|
1183
1294
|
/**
|
|
1184
1295
|
* Internal static implementation that actually generates the narrative
|
|
1185
|
-
* @param
|
|
1296
|
+
* @param resources - FHIR Medication resources
|
|
1186
1297
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1187
1298
|
* @returns HTML string for rendering
|
|
1188
1299
|
*/
|
|
1189
1300
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1190
|
-
static generateStaticNarrative(
|
|
1191
|
-
const templateUtilities = new TemplateUtilities(
|
|
1301
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1302
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1192
1303
|
let html = "";
|
|
1193
|
-
const medicationRequests = this.getMedicationRequests(templateUtilities,
|
|
1194
|
-
const medicationStatements = this.getMedicationStatements(templateUtilities,
|
|
1304
|
+
const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
|
|
1305
|
+
const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
|
|
1195
1306
|
const allActiveMedications = [];
|
|
1196
1307
|
const allInactiveMedications = [];
|
|
1197
1308
|
medicationRequests.forEach((mr) => {
|
|
@@ -1236,42 +1347,42 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1236
1347
|
};
|
|
1237
1348
|
if (allActiveMedications.length > 0) {
|
|
1238
1349
|
sortMedications(allActiveMedications);
|
|
1239
|
-
html += this.renderCombinedMedications(templateUtilities, allActiveMedications,
|
|
1350
|
+
html += this.renderCombinedMedications(templateUtilities, allActiveMedications, true);
|
|
1240
1351
|
}
|
|
1241
1352
|
if (allInactiveMedications.length > 0) {
|
|
1242
1353
|
sortMedications(allInactiveMedications);
|
|
1243
|
-
html += this.renderCombinedMedications(templateUtilities, allInactiveMedications,
|
|
1354
|
+
html += this.renderCombinedMedications(templateUtilities, allInactiveMedications, false);
|
|
1244
1355
|
}
|
|
1245
1356
|
return html;
|
|
1246
1357
|
}
|
|
1247
1358
|
/**
|
|
1248
|
-
* Extract MedicationRequest resources
|
|
1359
|
+
* Extract MedicationRequest resources
|
|
1249
1360
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1250
|
-
* @param
|
|
1361
|
+
* @param resources - FHIR Medication resources
|
|
1251
1362
|
* @returns Array of MedicationRequest resources
|
|
1252
1363
|
*/
|
|
1253
|
-
static getMedicationRequests(templateUtilities,
|
|
1254
|
-
if (
|
|
1364
|
+
static getMedicationRequests(templateUtilities, resources) {
|
|
1365
|
+
if (resources.length === 0) {
|
|
1255
1366
|
return [];
|
|
1256
1367
|
}
|
|
1257
|
-
return
|
|
1258
|
-
resource: entry
|
|
1259
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1368
|
+
return resources.filter((entry) => entry.resourceType === "MedicationRequest").map((entry) => ({
|
|
1369
|
+
resource: entry,
|
|
1370
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1260
1371
|
}));
|
|
1261
1372
|
}
|
|
1262
1373
|
/**
|
|
1263
|
-
* Extract MedicationStatement resources
|
|
1374
|
+
* Extract MedicationStatement resources
|
|
1264
1375
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1265
|
-
* @param
|
|
1376
|
+
* @param resources - FHIR Medication resources
|
|
1266
1377
|
* @returns Array of MedicationStatement resources
|
|
1267
1378
|
*/
|
|
1268
|
-
static getMedicationStatements(templateUtilities,
|
|
1269
|
-
if (
|
|
1379
|
+
static getMedicationStatements(templateUtilities, resources) {
|
|
1380
|
+
if (resources.length === 0) {
|
|
1270
1381
|
return [];
|
|
1271
1382
|
}
|
|
1272
|
-
return
|
|
1273
|
-
resource: entry
|
|
1274
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1383
|
+
return resources.filter((entry) => entry.resourceType === "MedicationStatement").map((entry) => ({
|
|
1384
|
+
resource: entry,
|
|
1385
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1275
1386
|
}));
|
|
1276
1387
|
}
|
|
1277
1388
|
/**
|
|
@@ -1281,9 +1392,9 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1281
1392
|
* @param sectionTitle - Title for the section
|
|
1282
1393
|
* @returns HTML string for rendering
|
|
1283
1394
|
*/
|
|
1284
|
-
static renderCombinedMedications(templateUtilities, medications,
|
|
1395
|
+
static renderCombinedMedications(templateUtilities, medications, isActiveSection) {
|
|
1285
1396
|
let html = `
|
|
1286
|
-
<h3>${
|
|
1397
|
+
<h3>${isActiveSection ? "Active Medications" : "Inactive Medications"}</h3>
|
|
1287
1398
|
<table>
|
|
1288
1399
|
<thead>
|
|
1289
1400
|
<tr>
|
|
@@ -1292,8 +1403,8 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1292
1403
|
<th>Sig</th>
|
|
1293
1404
|
<th>Dispense Quantity</th>
|
|
1294
1405
|
<th>Refills</th>
|
|
1295
|
-
<th>Start Date</th
|
|
1296
|
-
<th>End Date</th
|
|
1406
|
+
<th>Start Date</th>${isActiveSection ? "" : `
|
|
1407
|
+
<th>End Date</th>`}
|
|
1297
1408
|
<th>Status</th>
|
|
1298
1409
|
</tr>
|
|
1299
1410
|
</thead>
|
|
@@ -1351,8 +1462,8 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1351
1462
|
<td>${sig}</td>
|
|
1352
1463
|
<td>${dispenseQuantity}</td>
|
|
1353
1464
|
<td>${refills}</td>
|
|
1354
|
-
<td>${startDate}</td
|
|
1355
|
-
<td>${endDate}</td
|
|
1465
|
+
<td>${startDate}</td>${isActiveSection ? "" : `
|
|
1466
|
+
<td>${endDate}</td>`}
|
|
1356
1467
|
<td>${status}</td>
|
|
1357
1468
|
</tr>`;
|
|
1358
1469
|
}
|
|
@@ -1367,28 +1478,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1367
1478
|
var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
1368
1479
|
/**
|
|
1369
1480
|
* Generate HTML narrative for Immunization resources
|
|
1370
|
-
* @param
|
|
1481
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1371
1482
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1372
1483
|
* @returns HTML string for rendering
|
|
1373
1484
|
*/
|
|
1374
|
-
generateNarrative(
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
}
|
|
1382
|
-
return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
|
|
1485
|
+
generateNarrative(resources, timezone) {
|
|
1486
|
+
resources.sort((a, b) => {
|
|
1487
|
+
const dateA = a.occurrenceDateTime;
|
|
1488
|
+
const dateB = b.occurrenceDateTime;
|
|
1489
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1490
|
+
});
|
|
1491
|
+
return _ImmunizationsTemplate.generateStaticNarrative(resources, timezone);
|
|
1383
1492
|
}
|
|
1384
1493
|
/**
|
|
1385
1494
|
* Internal static implementation that actually generates the narrative
|
|
1386
|
-
* @param
|
|
1495
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1387
1496
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1388
1497
|
* @returns HTML string for rendering
|
|
1389
1498
|
*/
|
|
1390
|
-
static generateStaticNarrative(
|
|
1391
|
-
const templateUtilities = new TemplateUtilities(
|
|
1499
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1500
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1392
1501
|
let html = `
|
|
1393
1502
|
<table>
|
|
1394
1503
|
<thead>
|
|
@@ -1403,21 +1512,20 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1403
1512
|
</tr>
|
|
1404
1513
|
</thead>
|
|
1405
1514
|
<tbody>`;
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
}
|
|
1515
|
+
const immunizations = resources.filter((resourceItem) => resourceItem.resourceType === "Immunization");
|
|
1516
|
+
if (immunizations.length > 0) {
|
|
1517
|
+
for (const resourceItem of immunizations) {
|
|
1518
|
+
const imm = resourceItem;
|
|
1519
|
+
html += `
|
|
1520
|
+
<tr id="${templateUtilities.narrativeLinkId(imm)}">
|
|
1521
|
+
<td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
|
|
1522
|
+
<td>${imm.status || ""}</td>
|
|
1523
|
+
<td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
|
|
1524
|
+
<td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
|
|
1525
|
+
<td>${imm.lotNumber || ""}</td>
|
|
1526
|
+
<td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
|
|
1527
|
+
<td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
|
|
1528
|
+
</tr>`;
|
|
1421
1529
|
}
|
|
1422
1530
|
}
|
|
1423
1531
|
html += `
|
|
@@ -1431,23 +1539,23 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1431
1539
|
var ProblemListTemplate = class _ProblemListTemplate {
|
|
1432
1540
|
/**
|
|
1433
1541
|
* Generate HTML narrative for Problem List
|
|
1434
|
-
* @param
|
|
1542
|
+
* @param resources - FHIR Condition resources
|
|
1435
1543
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1436
1544
|
* @returns HTML string for rendering
|
|
1437
1545
|
*/
|
|
1438
|
-
generateNarrative(
|
|
1439
|
-
return _ProblemListTemplate.generateStaticNarrative(
|
|
1546
|
+
generateNarrative(resources, timezone) {
|
|
1547
|
+
return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
|
|
1440
1548
|
}
|
|
1441
1549
|
/**
|
|
1442
1550
|
* Internal static implementation that actually generates the narrative
|
|
1443
|
-
* @param
|
|
1551
|
+
* @param resources - FHIR Condition resources
|
|
1444
1552
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1445
1553
|
* @returns HTML string for rendering
|
|
1446
1554
|
*/
|
|
1447
|
-
static generateStaticNarrative(
|
|
1448
|
-
const templateUtilities = new TemplateUtilities(
|
|
1555
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1556
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1449
1557
|
let html = ``;
|
|
1450
|
-
const activeConditions =
|
|
1558
|
+
const activeConditions = resources.map((entry) => entry) || [];
|
|
1451
1559
|
activeConditions.sort((a, b) => {
|
|
1452
1560
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1453
1561
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1458,7 +1566,6 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1458
1566
|
<thead>
|
|
1459
1567
|
<tr>
|
|
1460
1568
|
<th>Problem</th>
|
|
1461
|
-
<th>Severity</th>
|
|
1462
1569
|
<th>Onset Date</th>
|
|
1463
1570
|
<th>Recorded Date</th>
|
|
1464
1571
|
<th>Notes</th>
|
|
@@ -1468,7 +1575,6 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1468
1575
|
for (const cond of activeConditions) {
|
|
1469
1576
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1470
1577
|
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1471
|
-
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1472
1578
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1473
1579
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1474
1580
|
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
@@ -1484,22 +1590,22 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1484
1590
|
var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
1485
1591
|
/**
|
|
1486
1592
|
* Generate HTML narrative for Vital Signs
|
|
1487
|
-
* @param
|
|
1593
|
+
* @param resources - FHIR Observation resources
|
|
1488
1594
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1489
1595
|
* @returns HTML string for rendering
|
|
1490
1596
|
*/
|
|
1491
|
-
generateNarrative(
|
|
1492
|
-
return _VitalSignsTemplate.generateStaticNarrative(
|
|
1597
|
+
generateNarrative(resources, timezone) {
|
|
1598
|
+
return _VitalSignsTemplate.generateStaticNarrative(resources, timezone);
|
|
1493
1599
|
}
|
|
1494
1600
|
/**
|
|
1495
1601
|
* Internal static implementation that actually generates the narrative
|
|
1496
|
-
* @param
|
|
1602
|
+
* @param resources - FHIR Observation resources
|
|
1497
1603
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1498
1604
|
* @returns HTML string for rendering
|
|
1499
1605
|
*/
|
|
1500
|
-
static generateStaticNarrative(
|
|
1501
|
-
const templateUtilities = new TemplateUtilities(
|
|
1502
|
-
const observations =
|
|
1606
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1607
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1608
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1503
1609
|
observations.sort((a, b) => {
|
|
1504
1610
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1505
1611
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1542,28 +1648,21 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1542
1648
|
var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
1543
1649
|
/**
|
|
1544
1650
|
* Generate HTML narrative for Medical Device resources
|
|
1545
|
-
* @param
|
|
1651
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1546
1652
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1547
1653
|
* @returns HTML string for rendering
|
|
1548
1654
|
*/
|
|
1549
|
-
generateNarrative(
|
|
1550
|
-
|
|
1551
|
-
resource.entry.sort((a, b) => {
|
|
1552
|
-
const dateA = a.resource?.recordedOn;
|
|
1553
|
-
const dateB = b.resource?.recordedOn;
|
|
1554
|
-
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1555
|
-
});
|
|
1556
|
-
}
|
|
1557
|
-
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1655
|
+
generateNarrative(resources, timezone) {
|
|
1656
|
+
return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
|
|
1558
1657
|
}
|
|
1559
1658
|
/**
|
|
1560
1659
|
* Internal static implementation that actually generates the narrative
|
|
1561
|
-
* @param
|
|
1660
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1562
1661
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1563
1662
|
* @returns HTML string for rendering
|
|
1564
1663
|
*/
|
|
1565
|
-
static generateStaticNarrative(
|
|
1566
|
-
const templateUtilities = new TemplateUtilities(
|
|
1664
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1665
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1567
1666
|
let html = `
|
|
1568
1667
|
<table>
|
|
1569
1668
|
<thead>
|
|
@@ -1575,19 +1674,19 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1575
1674
|
</tr>
|
|
1576
1675
|
</thead>
|
|
1577
1676
|
<tbody>`;
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1677
|
+
const deviceStatements = resources.filter((resourceItem) => resourceItem.resourceType === "DeviceUseStatement").map((resourceItem) => resourceItem).sort((a, b) => {
|
|
1678
|
+
const dateA = a.recordedOn;
|
|
1679
|
+
const dateB = b.recordedOn;
|
|
1680
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1681
|
+
});
|
|
1682
|
+
for (const dus of deviceStatements) {
|
|
1683
|
+
html += `
|
|
1684
|
+
<tr id="${templateUtilities.narrativeLinkId(dus)}">
|
|
1685
|
+
<td>${templateUtilities.renderDevice(dus.device)}</td>
|
|
1686
|
+
<td>${dus.status || ""}</td>
|
|
1687
|
+
<td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
|
|
1688
|
+
<td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
|
|
1689
|
+
</tr>`;
|
|
1591
1690
|
}
|
|
1592
1691
|
html += `
|
|
1593
1692
|
</tbody>
|
|
@@ -1600,23 +1699,23 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1600
1699
|
var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
1601
1700
|
/**
|
|
1602
1701
|
* Generate HTML narrative for Diagnostic Results
|
|
1603
|
-
* @param
|
|
1702
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1604
1703
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1605
1704
|
* @returns HTML string for rendering
|
|
1606
1705
|
*/
|
|
1607
|
-
generateNarrative(
|
|
1608
|
-
return _DiagnosticResultsTemplate.generateStaticNarrative(
|
|
1706
|
+
generateNarrative(resources, timezone) {
|
|
1707
|
+
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
|
|
1609
1708
|
}
|
|
1610
1709
|
/**
|
|
1611
1710
|
* Internal static implementation that actually generates the narrative
|
|
1612
|
-
* @param
|
|
1711
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1613
1712
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1614
1713
|
* @returns HTML string for rendering
|
|
1615
1714
|
*/
|
|
1616
|
-
static generateStaticNarrative(
|
|
1617
|
-
const templateUtilities = new TemplateUtilities(
|
|
1715
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1716
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1618
1717
|
let html = "";
|
|
1619
|
-
const observations = this.getObservations(
|
|
1718
|
+
const observations = this.getObservations(resources);
|
|
1620
1719
|
if (observations.length > 0) {
|
|
1621
1720
|
observations.sort((a, b) => {
|
|
1622
1721
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
@@ -1625,7 +1724,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1625
1724
|
});
|
|
1626
1725
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1627
1726
|
}
|
|
1628
|
-
const diagnosticReports = this.getDiagnosticReports(
|
|
1727
|
+
const diagnosticReports = this.getDiagnosticReports(resources);
|
|
1629
1728
|
if (diagnosticReports.length > 0) {
|
|
1630
1729
|
diagnosticReports.sort((a, b) => {
|
|
1631
1730
|
const dateA = a.issued;
|
|
@@ -1637,26 +1736,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1637
1736
|
return html;
|
|
1638
1737
|
}
|
|
1639
1738
|
/**
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
static getObservations(
|
|
1645
|
-
|
|
1646
|
-
return [];
|
|
1647
|
-
}
|
|
1648
|
-
return resource.entry.filter((entry) => entry.resource?.resourceType === "Observation").map((entry) => entry.resource);
|
|
1739
|
+
* Get all Observation resources from the resource array
|
|
1740
|
+
* @param resources - FHIR resources array
|
|
1741
|
+
* @returns Array of Observation resources
|
|
1742
|
+
*/
|
|
1743
|
+
static getObservations(resources) {
|
|
1744
|
+
return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
|
|
1649
1745
|
}
|
|
1650
1746
|
/**
|
|
1651
|
-
*
|
|
1652
|
-
* @param
|
|
1747
|
+
* Get all DiagnosticReport resources from the resource array
|
|
1748
|
+
* @param resources - FHIR resources array
|
|
1653
1749
|
* @returns Array of DiagnosticReport resources
|
|
1654
1750
|
*/
|
|
1655
|
-
static getDiagnosticReports(
|
|
1656
|
-
|
|
1657
|
-
return [];
|
|
1658
|
-
}
|
|
1659
|
-
return resource.entry.filter((entry) => entry.resource?.resourceType === "DiagnosticReport").map((entry) => entry.resource);
|
|
1751
|
+
static getDiagnosticReports(resources) {
|
|
1752
|
+
return resources.filter((resourceItem) => resourceItem.resourceType === "DiagnosticReport").map((resourceItem) => resourceItem);
|
|
1660
1753
|
}
|
|
1661
1754
|
/**
|
|
1662
1755
|
* Render HTML table for Observation resources
|
|
@@ -1712,7 +1805,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1712
1805
|
<thead>
|
|
1713
1806
|
<tr>
|
|
1714
1807
|
<th>Report</th>
|
|
1715
|
-
<th>Status</th>
|
|
1716
1808
|
<th>Category</th>
|
|
1717
1809
|
<th>Result</th>
|
|
1718
1810
|
<th>Issued</th>
|
|
@@ -1727,7 +1819,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1727
1819
|
html += `
|
|
1728
1820
|
<tr id="${templateUtilities.narrativeLinkId(report)}">
|
|
1729
1821
|
<td>${templateUtilities.codeableConcept(report.code)}</td>
|
|
1730
|
-
<td>${report.status || ""}</td>
|
|
1731
1822
|
<td>${templateUtilities.firstFromCodeableConceptList(report.category)}</td>
|
|
1732
1823
|
<td>${resultCount}</td>
|
|
1733
1824
|
<td>${report.issued ? templateUtilities.renderTime(report.issued, timezone) : ""}</td>
|
|
@@ -1744,26 +1835,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1744
1835
|
var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
1745
1836
|
/**
|
|
1746
1837
|
* Generate HTML narrative for Procedure resources
|
|
1747
|
-
* @param
|
|
1838
|
+
* @param resources - FHIR Procedure resources
|
|
1748
1839
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1749
1840
|
* @returns HTML string for rendering
|
|
1750
1841
|
*/
|
|
1751
|
-
generateNarrative(
|
|
1752
|
-
|
|
1753
|
-
const dateA = a.
|
|
1754
|
-
const dateB = b.
|
|
1842
|
+
generateNarrative(resources, timezone) {
|
|
1843
|
+
resources.sort((a, b) => {
|
|
1844
|
+
const dateA = a.performedDateTime || a.performedPeriod?.start;
|
|
1845
|
+
const dateB = b.performedDateTime || b.performedPeriod?.start;
|
|
1755
1846
|
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1756
1847
|
});
|
|
1757
|
-
return _HistoryOfProceduresTemplate.generateStaticNarrative(
|
|
1848
|
+
return _HistoryOfProceduresTemplate.generateStaticNarrative(resources, timezone);
|
|
1758
1849
|
}
|
|
1759
1850
|
/**
|
|
1760
1851
|
* Internal static implementation that actually generates the narrative
|
|
1761
|
-
* @param
|
|
1852
|
+
* @param resources - FHIR Procedure resources
|
|
1762
1853
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1763
1854
|
* @returns HTML string for rendering
|
|
1764
1855
|
*/
|
|
1765
|
-
static generateStaticNarrative(
|
|
1766
|
-
const templateUtilities = new TemplateUtilities(
|
|
1856
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1857
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1767
1858
|
let html = `
|
|
1768
1859
|
<table>
|
|
1769
1860
|
<thead>
|
|
@@ -1774,16 +1865,14 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1774
1865
|
</tr>
|
|
1775
1866
|
</thead>
|
|
1776
1867
|
<tbody>`;
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
<
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
</tr>`;
|
|
1786
|
-
}
|
|
1868
|
+
for (const resourceItem of resources) {
|
|
1869
|
+
const proc = resourceItem;
|
|
1870
|
+
html += `
|
|
1871
|
+
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
1872
|
+
<td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
|
|
1873
|
+
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
1874
|
+
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
1875
|
+
</tr>`;
|
|
1787
1876
|
}
|
|
1788
1877
|
html += `
|
|
1789
1878
|
</tbody>
|
|
@@ -1796,22 +1885,22 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1796
1885
|
var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
1797
1886
|
/**
|
|
1798
1887
|
* Generate HTML narrative for Social History
|
|
1799
|
-
* @param
|
|
1888
|
+
* @param resources - FHIR Observation resources
|
|
1800
1889
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1801
1890
|
* @returns HTML string for rendering
|
|
1802
1891
|
*/
|
|
1803
|
-
generateNarrative(
|
|
1804
|
-
return _SocialHistoryTemplate.generateStaticNarrative(
|
|
1892
|
+
generateNarrative(resources, timezone) {
|
|
1893
|
+
return _SocialHistoryTemplate.generateStaticNarrative(resources, timezone);
|
|
1805
1894
|
}
|
|
1806
1895
|
/**
|
|
1807
1896
|
* Internal static implementation that actually generates the narrative
|
|
1808
|
-
* @param
|
|
1897
|
+
* @param resources - FHIR Observation resources
|
|
1809
1898
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1810
1899
|
* @returns HTML string for rendering
|
|
1811
1900
|
*/
|
|
1812
|
-
static generateStaticNarrative(
|
|
1813
|
-
const templateUtilities = new TemplateUtilities(
|
|
1814
|
-
const observations =
|
|
1901
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1902
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1903
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1815
1904
|
observations.sort((a, b) => {
|
|
1816
1905
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1817
1906
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1850,14 +1939,14 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1850
1939
|
var PastHistoryOfIllnessTemplate = class {
|
|
1851
1940
|
/**
|
|
1852
1941
|
* Generate HTML narrative for Past History of Illnesses
|
|
1853
|
-
* @param
|
|
1942
|
+
* @param resources - FHIR Condition resources
|
|
1854
1943
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1855
1944
|
* @returns HTML string for rendering
|
|
1856
1945
|
*/
|
|
1857
|
-
generateNarrative(
|
|
1858
|
-
const templateUtilities = new TemplateUtilities(
|
|
1946
|
+
generateNarrative(resources, timezone) {
|
|
1947
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1859
1948
|
let html = ``;
|
|
1860
|
-
const resolvedConditions =
|
|
1949
|
+
const resolvedConditions = resources.map((entry) => entry) || [];
|
|
1861
1950
|
resolvedConditions.sort((a, b) => {
|
|
1862
1951
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1863
1952
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1868,7 +1957,6 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1868
1957
|
<thead>
|
|
1869
1958
|
<tr>
|
|
1870
1959
|
<th>Problem</th>
|
|
1871
|
-
<th>Severity</th>
|
|
1872
1960
|
<th>Onset Date</th>
|
|
1873
1961
|
<th>Recorded Date</th>
|
|
1874
1962
|
<th>Resolved Date</th>
|
|
@@ -1879,7 +1967,6 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1879
1967
|
for (const cond of resolvedConditions) {
|
|
1880
1968
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1881
1969
|
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1882
|
-
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1883
1970
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1884
1971
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1885
1972
|
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
@@ -1896,13 +1983,13 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1896
1983
|
var PlanOfCareTemplate = class {
|
|
1897
1984
|
/**
|
|
1898
1985
|
* Generate HTML narrative for Plan of Care
|
|
1899
|
-
* @param
|
|
1986
|
+
* @param resources - FHIR CarePlan resources
|
|
1900
1987
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1901
1988
|
* @returns HTML string for rendering
|
|
1902
1989
|
*/
|
|
1903
|
-
generateNarrative(
|
|
1904
|
-
const templateUtilities = new TemplateUtilities(
|
|
1905
|
-
const carePlans =
|
|
1990
|
+
generateNarrative(resources, timezone) {
|
|
1991
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1992
|
+
const carePlans = resources.map((entry) => entry) || [];
|
|
1906
1993
|
carePlans.sort((a, b) => {
|
|
1907
1994
|
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1908
1995
|
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
@@ -1941,37 +2028,35 @@ var PlanOfCareTemplate = class {
|
|
|
1941
2028
|
var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
1942
2029
|
/**
|
|
1943
2030
|
* Generate HTML narrative for Functional Status
|
|
1944
|
-
* @param
|
|
2031
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
1945
2032
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1946
2033
|
* @returns HTML string for rendering
|
|
1947
2034
|
*/
|
|
1948
|
-
generateNarrative(
|
|
1949
|
-
return _FunctionalStatusTemplate.generateStaticNarrative(
|
|
2035
|
+
generateNarrative(resources, timezone) {
|
|
2036
|
+
return _FunctionalStatusTemplate.generateStaticNarrative(resources, timezone);
|
|
1950
2037
|
}
|
|
1951
2038
|
/**
|
|
1952
2039
|
* Internal static implementation that actually generates the narrative
|
|
1953
|
-
* @param
|
|
2040
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
1954
2041
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1955
2042
|
* @returns HTML string for rendering
|
|
1956
2043
|
*/
|
|
1957
|
-
static generateStaticNarrative(
|
|
1958
|
-
const templateUtilities = new TemplateUtilities(
|
|
2044
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2045
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1959
2046
|
let html = ``;
|
|
1960
2047
|
const activeConditions = [];
|
|
1961
2048
|
const clinicalImpressions = [];
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
activeConditions.push(cond);
|
|
1971
|
-
}
|
|
1972
|
-
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
1973
|
-
clinicalImpressions.push(entry.resource);
|
|
2049
|
+
for (const resourceItem of resources) {
|
|
2050
|
+
if (resourceItem.resourceType === "Condition") {
|
|
2051
|
+
const cond = resourceItem;
|
|
2052
|
+
const isResolved = cond.clinicalStatus?.coding?.some(
|
|
2053
|
+
(c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
|
|
2054
|
+
);
|
|
2055
|
+
if (!isResolved) {
|
|
2056
|
+
activeConditions.push(cond);
|
|
1974
2057
|
}
|
|
2058
|
+
} else if (resourceItem.resourceType === "ClinicalImpression") {
|
|
2059
|
+
clinicalImpressions.push(resourceItem);
|
|
1975
2060
|
}
|
|
1976
2061
|
}
|
|
1977
2062
|
activeConditions.sort((a, b) => {
|
|
@@ -1990,7 +2075,6 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
1990
2075
|
<thead>
|
|
1991
2076
|
<tr>
|
|
1992
2077
|
<th>Problem</th>
|
|
1993
|
-
<th>Severity</th>
|
|
1994
2078
|
<th>Onset Date</th>
|
|
1995
2079
|
<th>Recorded Date</th>
|
|
1996
2080
|
<th>Notes</th>
|
|
@@ -2000,7 +2084,6 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
2000
2084
|
for (const cond of activeConditions) {
|
|
2001
2085
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
2002
2086
|
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
2003
|
-
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
2004
2087
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
2005
2088
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
2006
2089
|
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
@@ -2070,22 +2153,22 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
2070
2153
|
var PregnancyTemplate = class _PregnancyTemplate {
|
|
2071
2154
|
/**
|
|
2072
2155
|
* Generate HTML narrative for Pregnancy
|
|
2073
|
-
* @param
|
|
2156
|
+
* @param resources - FHIR Observation resources
|
|
2074
2157
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2075
2158
|
* @returns HTML string for rendering
|
|
2076
2159
|
*/
|
|
2077
|
-
generateNarrative(
|
|
2078
|
-
return _PregnancyTemplate.generateStaticNarrative(
|
|
2160
|
+
generateNarrative(resources, timezone) {
|
|
2161
|
+
return _PregnancyTemplate.generateStaticNarrative(resources, timezone);
|
|
2079
2162
|
}
|
|
2080
2163
|
/**
|
|
2081
2164
|
* Internal static implementation that actually generates the narrative
|
|
2082
|
-
* @param
|
|
2165
|
+
* @param resources - FHIR Observation resources
|
|
2083
2166
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2084
2167
|
* @returns HTML string for rendering
|
|
2085
2168
|
*/
|
|
2086
|
-
static generateStaticNarrative(
|
|
2087
|
-
const templateUtilities = new TemplateUtilities(
|
|
2088
|
-
const observations =
|
|
2169
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2170
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2171
|
+
const observations = resources.map((entry) => entry) || [];
|
|
2089
2172
|
observations.sort((a, b) => {
|
|
2090
2173
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2091
2174
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -2101,8 +2184,8 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2101
2184
|
</tr>
|
|
2102
2185
|
</thead>
|
|
2103
2186
|
<tbody>`;
|
|
2104
|
-
for (const
|
|
2105
|
-
const obs =
|
|
2187
|
+
for (const resource of observations) {
|
|
2188
|
+
const obs = resource;
|
|
2106
2189
|
html += `
|
|
2107
2190
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2108
2191
|
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
@@ -2121,29 +2204,27 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2121
2204
|
var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
2122
2205
|
/**
|
|
2123
2206
|
* Generate HTML narrative for Advance Directives
|
|
2124
|
-
* @param
|
|
2207
|
+
* @param resources - FHIR Consent resources
|
|
2125
2208
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2126
2209
|
* @returns HTML string for rendering
|
|
2127
2210
|
*/
|
|
2128
|
-
generateNarrative(
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
}
|
|
2136
|
-
return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
|
|
2211
|
+
generateNarrative(resources, timezone) {
|
|
2212
|
+
resources.sort((a, b) => {
|
|
2213
|
+
const dateA = new Date(a.dateTime || 0);
|
|
2214
|
+
const dateB = new Date(b.dateTime || 0);
|
|
2215
|
+
return dateB.getTime() - dateA.getTime();
|
|
2216
|
+
});
|
|
2217
|
+
return _AdvanceDirectivesTemplate.generateStaticNarrative(resources, timezone);
|
|
2137
2218
|
}
|
|
2138
2219
|
/**
|
|
2139
2220
|
* Internal static implementation that actually generates the narrative
|
|
2140
|
-
* @param
|
|
2221
|
+
* @param resources - FHIR Consent resources
|
|
2141
2222
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2142
2223
|
* @returns HTML string for rendering
|
|
2143
2224
|
*/
|
|
2144
2225
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2145
|
-
static generateStaticNarrative(
|
|
2146
|
-
const templateUtilities = new TemplateUtilities(
|
|
2226
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2227
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2147
2228
|
let html = `
|
|
2148
2229
|
<table>
|
|
2149
2230
|
<thead>
|
|
@@ -2155,17 +2236,15 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
2155
2236
|
</tr>
|
|
2156
2237
|
</thead>
|
|
2157
2238
|
<tbody>`;
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
<
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
</tr>`;
|
|
2168
|
-
}
|
|
2239
|
+
for (const resourceItem of resources) {
|
|
2240
|
+
const consent = resourceItem;
|
|
2241
|
+
html += `
|
|
2242
|
+
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
2243
|
+
<td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
|
|
2244
|
+
<td>${consent.status || ""}</td>
|
|
2245
|
+
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
2246
|
+
<td>${consent.dateTime || ""}</td>
|
|
2247
|
+
</tr>`;
|
|
2169
2248
|
}
|
|
2170
2249
|
html += `
|
|
2171
2250
|
</tbody>
|
|
@@ -2179,16 +2258,16 @@ var TypeScriptTemplateMapper = class {
|
|
|
2179
2258
|
/**
|
|
2180
2259
|
* Generates HTML narrative for a specific IPS section
|
|
2181
2260
|
* @param section - The IPS section
|
|
2182
|
-
* @param
|
|
2261
|
+
* @param resources - FHIR resources
|
|
2183
2262
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2184
2263
|
* @returns HTML string for rendering
|
|
2185
2264
|
*/
|
|
2186
|
-
static generateNarrative(section,
|
|
2265
|
+
static generateNarrative(section, resources, timezone) {
|
|
2187
2266
|
const templateClass = this.sectionToTemplate[section];
|
|
2188
2267
|
if (!templateClass) {
|
|
2189
2268
|
throw new Error(`No template found for section: ${section}`);
|
|
2190
2269
|
}
|
|
2191
|
-
return templateClass.generateNarrative(
|
|
2270
|
+
return templateClass.generateNarrative(resources, timezone);
|
|
2192
2271
|
}
|
|
2193
2272
|
};
|
|
2194
2273
|
// Map of section types to their template classes
|
|
@@ -2251,14 +2330,7 @@ var NarrativeGenerator = class {
|
|
|
2251
2330
|
return void 0;
|
|
2252
2331
|
}
|
|
2253
2332
|
try {
|
|
2254
|
-
const
|
|
2255
|
-
resourceType: "Bundle",
|
|
2256
|
-
type: "collection",
|
|
2257
|
-
entry: resources.map((resource) => ({
|
|
2258
|
-
resource
|
|
2259
|
-
}))
|
|
2260
|
-
};
|
|
2261
|
-
const content = TypeScriptTemplateMapper.generateNarrative(section, bundle, timezone);
|
|
2333
|
+
const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone);
|
|
2262
2334
|
if (!content) {
|
|
2263
2335
|
return void 0;
|
|
2264
2336
|
}
|
|
@@ -2343,13 +2415,16 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2343
2415
|
/**
|
|
2344
2416
|
* sets the patient resource for the IPS Composition.
|
|
2345
2417
|
* This is not needed if you are calling read_bundle, but can be used to set the patient resource directly.
|
|
2346
|
-
* @param
|
|
2418
|
+
* @param patients - FHIR Patient resource to set
|
|
2347
2419
|
*/
|
|
2348
|
-
setPatient(
|
|
2349
|
-
if (!
|
|
2420
|
+
setPatient(patients) {
|
|
2421
|
+
if (!Array.isArray(patients)) {
|
|
2422
|
+
patients = [patients];
|
|
2423
|
+
}
|
|
2424
|
+
if (patients.length === 0 || !patients.every((patient) => patient.resourceType === "Patient")) {
|
|
2350
2425
|
throw new Error("Invalid Patient resource");
|
|
2351
2426
|
}
|
|
2352
|
-
this.
|
|
2427
|
+
this.patients = patients;
|
|
2353
2428
|
return this;
|
|
2354
2429
|
}
|
|
2355
2430
|
/**
|
|
@@ -2407,21 +2482,26 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2407
2482
|
if (!bundle.entry) {
|
|
2408
2483
|
return this;
|
|
2409
2484
|
}
|
|
2410
|
-
const
|
|
2411
|
-
|
|
2485
|
+
const patientEntries = [];
|
|
2486
|
+
const resources = [];
|
|
2487
|
+
bundle.entry.forEach((e) => {
|
|
2488
|
+
if (e.resource?.resourceType === "Patient") {
|
|
2489
|
+
patientEntries.push(e.resource);
|
|
2490
|
+
this.resources.add(e.resource);
|
|
2491
|
+
} else if (e.resource) {
|
|
2492
|
+
resources.push(e.resource);
|
|
2493
|
+
}
|
|
2494
|
+
});
|
|
2495
|
+
if (patientEntries.length === 0) {
|
|
2412
2496
|
throw new Error("Patient resource not found in the bundle");
|
|
2413
2497
|
}
|
|
2414
|
-
this.
|
|
2415
|
-
const resources = bundle.entry.map((e) => e.resource);
|
|
2498
|
+
this.patients = patientEntries;
|
|
2416
2499
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
let sectionResources = resources.filter(
|
|
2420
|
-
(r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
|
|
2421
|
-
);
|
|
2422
|
-
if (customFilter) {
|
|
2423
|
-
sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
|
|
2500
|
+
if (sectionType === "Patient" /* PATIENT */) {
|
|
2501
|
+
continue;
|
|
2424
2502
|
}
|
|
2503
|
+
const sectionFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2504
|
+
const sectionResources = resources.filter((resource) => sectionFilter(resource));
|
|
2425
2505
|
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2426
2506
|
}
|
|
2427
2507
|
return this;
|
|
@@ -2432,16 +2512,18 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2432
2512
|
* @param authorOrganizationName - Name of the authoring organization
|
|
2433
2513
|
* @param baseUrl - Base URL for the FHIR server (e.g., 'https://example.com/fhir')
|
|
2434
2514
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2515
|
+
* @param patientId - Optional patient ID to use as primary patient for composition reference
|
|
2435
2516
|
*/
|
|
2436
|
-
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone) {
|
|
2517
|
+
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId) {
|
|
2437
2518
|
if (baseUrl.endsWith("/")) {
|
|
2438
2519
|
baseUrl = baseUrl.slice(0, -1);
|
|
2439
2520
|
}
|
|
2440
|
-
if (!this.
|
|
2521
|
+
if (!this.patients) {
|
|
2441
2522
|
throw new Error("Patient resource must be set before building the bundle");
|
|
2442
2523
|
}
|
|
2524
|
+
const primaryPatientId = patientId ?? this.patients[0].id;
|
|
2443
2525
|
const composition = {
|
|
2444
|
-
id: `Composition-${
|
|
2526
|
+
id: `Composition-${primaryPatientId}`,
|
|
2445
2527
|
resourceType: "Composition",
|
|
2446
2528
|
status: "final",
|
|
2447
2529
|
type: {
|
|
@@ -2452,7 +2534,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2452
2534
|
}]
|
|
2453
2535
|
},
|
|
2454
2536
|
subject: {
|
|
2455
|
-
reference: `Patient/${
|
|
2537
|
+
reference: `Patient/${primaryPatientId}`
|
|
2456
2538
|
},
|
|
2457
2539
|
author: [{
|
|
2458
2540
|
reference: `Organization/${authorOrganizationId}`,
|
|
@@ -2464,7 +2546,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2464
2546
|
section: this.sections,
|
|
2465
2547
|
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2466
2548
|
"Patient" /* PATIENT */,
|
|
2467
|
-
|
|
2549
|
+
this.patients,
|
|
2468
2550
|
timezone,
|
|
2469
2551
|
true
|
|
2470
2552
|
)
|
|
@@ -2483,9 +2565,11 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2483
2565
|
fullUrl: `${baseUrl}/Composition/${composition.id}`,
|
|
2484
2566
|
resource: composition
|
|
2485
2567
|
});
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2568
|
+
this.patients.forEach((patient) => {
|
|
2569
|
+
bundle.entry?.push({
|
|
2570
|
+
fullUrl: `${baseUrl}/Patient/${patient.id}`,
|
|
2571
|
+
resource: patient
|
|
2572
|
+
});
|
|
2489
2573
|
});
|
|
2490
2574
|
this.resources.forEach((resource) => {
|
|
2491
2575
|
if (resource.resourceType !== "Patient") {
|