@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.js
CHANGED
|
@@ -75,35 +75,28 @@ var SOCIAL_HISTORY_LOINC_CODES = {
|
|
|
75
75
|
"72166-2": "Tobacco Use",
|
|
76
76
|
"74013-4": "Alcohol Use"
|
|
77
77
|
};
|
|
78
|
+
var BLOOD_PRESSURE_LOINC_CODES = {
|
|
79
|
+
OBSERVATION: "85354-9",
|
|
80
|
+
SYSTOLIC: "8480-6",
|
|
81
|
+
DIASTOLIC: "8462-4"
|
|
82
|
+
};
|
|
78
83
|
|
|
79
84
|
// src/structures/ips_section_resource_map.ts
|
|
80
|
-
var IPSSectionResourceMap = {
|
|
81
|
-
["Patient" /* PATIENT */]: ["Patient"],
|
|
82
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
83
|
-
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
84
|
-
// Medication resource is needed for identifying name of medication
|
|
85
|
-
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
86
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
87
|
-
// Immunization can include Organization as a related resource
|
|
88
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
89
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
90
|
-
// Device resource is used for medical devices name
|
|
91
|
-
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
92
|
-
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
93
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
|
|
94
|
-
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
95
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
96
|
-
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
97
|
-
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
98
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
99
|
-
};
|
|
100
85
|
var IPSSectionResourceFilters = {
|
|
86
|
+
// Patient section: only Patient resource
|
|
87
|
+
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient",
|
|
88
|
+
// Only include allergies
|
|
89
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance",
|
|
90
|
+
// includes MedicationRequest, MedicationStatement. Medication is needed for medication names
|
|
91
|
+
["MedicationSummarySection" /* MEDICATIONS */]: (resource) => ["MedicationRequest", "MedicationStatement", "Medication"].includes(resource.resourceType),
|
|
101
92
|
// Only include active conditions
|
|
102
93
|
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
103
94
|
// Only include completed immunizations
|
|
104
95
|
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
105
96
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
106
97
|
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "vital-signs")),
|
|
98
|
+
// Includes DeviceUseStatement. Device is needed for linked device details
|
|
99
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
|
|
107
100
|
// Only include finalized diagnostic reports
|
|
108
101
|
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
109
102
|
// Only include completed procedures
|
|
@@ -119,14 +112,9 @@ var IPSSectionResourceFilters = {
|
|
|
119
112
|
// Only include active care plans
|
|
120
113
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
121
114
|
// Only include active advance directives (Consent resources)
|
|
122
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
123
|
-
// Patient section: only Patient resource
|
|
124
|
-
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
|
|
115
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
125
116
|
};
|
|
126
117
|
var IPSSectionResourceHelper = class {
|
|
127
|
-
static getResourceTypesForSection(section) {
|
|
128
|
-
return IPSSectionResourceMap[section] || [];
|
|
129
|
-
}
|
|
130
118
|
static getResourceFilterForSection(section) {
|
|
131
119
|
return IPSSectionResourceFilters[section];
|
|
132
120
|
}
|
|
@@ -136,11 +124,11 @@ var IPSSectionResourceHelper = class {
|
|
|
136
124
|
import { DateTime } from "luxon";
|
|
137
125
|
var TemplateUtilities = class {
|
|
138
126
|
/**
|
|
139
|
-
* Constructor to initialize the TemplateUtilities with a FHIR
|
|
140
|
-
* @param
|
|
127
|
+
* Constructor to initialize the TemplateUtilities with a FHIR resources
|
|
128
|
+
* @param resources - FHIR resources
|
|
141
129
|
*/
|
|
142
|
-
constructor(
|
|
143
|
-
this.
|
|
130
|
+
constructor(resources) {
|
|
131
|
+
this.resources = resources;
|
|
144
132
|
}
|
|
145
133
|
/**
|
|
146
134
|
* Formats a CodeableConcept object
|
|
@@ -171,7 +159,7 @@ var TemplateUtilities = class {
|
|
|
171
159
|
return "";
|
|
172
160
|
}
|
|
173
161
|
resolveReference(ref) {
|
|
174
|
-
if (!ref || !this.
|
|
162
|
+
if (!ref || !this.resources) {
|
|
175
163
|
return null;
|
|
176
164
|
}
|
|
177
165
|
const referenceParts = ref.reference?.split("/");
|
|
@@ -180,10 +168,10 @@ var TemplateUtilities = class {
|
|
|
180
168
|
}
|
|
181
169
|
const referenceResourceType = referenceParts[0];
|
|
182
170
|
const referenceResourceId = referenceParts[1];
|
|
183
|
-
const resource = this.
|
|
184
|
-
return entry.
|
|
171
|
+
const resource = this.resources.find((entry) => {
|
|
172
|
+
return entry.resourceType === referenceResourceType && entry.id === referenceResourceId;
|
|
185
173
|
});
|
|
186
|
-
return resource ? resource
|
|
174
|
+
return resource ? resource : null;
|
|
187
175
|
}
|
|
188
176
|
/**
|
|
189
177
|
* Renders a Device reference
|
|
@@ -502,6 +490,28 @@ var TemplateUtilities = class {
|
|
|
502
490
|
return "";
|
|
503
491
|
}
|
|
504
492
|
extractObservationValue(observation) {
|
|
493
|
+
if (observation.code && observation.code.coding && "component" in observation && Array.isArray(observation.component)) {
|
|
494
|
+
const bpCode = observation.code.coding.find(
|
|
495
|
+
(c) => c.code === BLOOD_PRESSURE_LOINC_CODES.OBSERVATION
|
|
496
|
+
);
|
|
497
|
+
if (bpCode) {
|
|
498
|
+
const systolicComponent = observation.component?.find(
|
|
499
|
+
(c) => c.code?.coding?.some(
|
|
500
|
+
(cc) => cc.code === BLOOD_PRESSURE_LOINC_CODES.SYSTOLIC
|
|
501
|
+
)
|
|
502
|
+
);
|
|
503
|
+
const diastolicComponent = observation.component?.find(
|
|
504
|
+
(c) => c.code?.coding?.some(
|
|
505
|
+
(cc) => cc.code === BLOOD_PRESSURE_LOINC_CODES.DIASTOLIC
|
|
506
|
+
)
|
|
507
|
+
);
|
|
508
|
+
if (systolicComponent && diastolicComponent) {
|
|
509
|
+
const systolic = this.extractObservationValue(systolicComponent);
|
|
510
|
+
const diastolic = this.extractObservationValue(diastolicComponent);
|
|
511
|
+
return `${systolic}/${diastolic}`;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
505
515
|
const valueFields = [
|
|
506
516
|
"valueString",
|
|
507
517
|
"valueInteger",
|
|
@@ -732,7 +742,7 @@ var TemplateUtilities = class {
|
|
|
732
742
|
if (parts.length === 2) {
|
|
733
743
|
const resourceType = parts[0];
|
|
734
744
|
const resourceId = parts[1];
|
|
735
|
-
const resource = this.
|
|
745
|
+
const resource = this.resources?.find((resource2) => resource2.resourceType === resourceType && resource2.id === resourceId);
|
|
736
746
|
if (resource) {
|
|
737
747
|
return `${resourceType}/${resourceId}`;
|
|
738
748
|
}
|
|
@@ -772,48 +782,96 @@ var TemplateUtilities = class {
|
|
|
772
782
|
// src/narratives/templates/typescript/PatientTemplate.ts
|
|
773
783
|
var PatientTemplate = class _PatientTemplate {
|
|
774
784
|
/**
|
|
775
|
-
* Generate HTML narrative for Patient
|
|
776
|
-
* @param
|
|
785
|
+
* Generate HTML narrative for Patient resources
|
|
786
|
+
* @param resources - FHIR Patient resources
|
|
777
787
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
778
788
|
* @returns HTML string for rendering
|
|
779
789
|
*/
|
|
780
|
-
generateNarrative(
|
|
781
|
-
return _PatientTemplate.generateStaticNarrative(
|
|
790
|
+
generateNarrative(resources, timezone) {
|
|
791
|
+
return _PatientTemplate.generateStaticNarrative(resources, timezone);
|
|
782
792
|
}
|
|
783
793
|
/**
|
|
784
794
|
* Internal static implementation that actually generates the narrative
|
|
785
|
-
* @param
|
|
795
|
+
* @param resources - FHIR Patient resources
|
|
786
796
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
787
797
|
* @returns HTML string for rendering
|
|
788
798
|
*/
|
|
789
799
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
790
|
-
static generateStaticNarrative(
|
|
791
|
-
const templateUtilities = new TemplateUtilities(
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
<
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
800
|
+
static generateStaticNarrative(resources, timezone) {
|
|
801
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
802
|
+
const combinedPatient = this.combinePatients(resources);
|
|
803
|
+
return `<div>
|
|
804
|
+
<ul>
|
|
805
|
+
<li><strong>Name(s):</strong>${this.renderNames(combinedPatient)}</li>
|
|
806
|
+
<li><strong>Gender:</strong>${combinedPatient.gender ? this.capitalize(combinedPatient.gender) : ""}</li>
|
|
807
|
+
<li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
|
|
808
|
+
<li><strong>Identifier(s):</strong>${this.renderIdentifiers(combinedPatient)}</li>
|
|
809
|
+
<li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
|
|
810
|
+
<li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
|
|
811
|
+
<li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
|
|
812
|
+
<li><strong>Deceased:</strong>${this.renderDeceased(combinedPatient)}</li>
|
|
813
|
+
<li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
|
|
814
|
+
</ul>
|
|
815
|
+
</div>`;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Combines multiple patient resources into a single patient object
|
|
819
|
+
* Merges fields, preferring non-empty values
|
|
820
|
+
* @param patients - Array of patient resources
|
|
821
|
+
* @returns Combined patient resource
|
|
822
|
+
*/
|
|
823
|
+
static combinePatients(patients) {
|
|
824
|
+
if (patients.length === 1) {
|
|
825
|
+
return patients[0];
|
|
826
|
+
}
|
|
827
|
+
const combined = patients[0];
|
|
828
|
+
const allNames = [];
|
|
829
|
+
const allIdentifiers = [];
|
|
830
|
+
const allTelecom = [];
|
|
831
|
+
const allAddresses = [];
|
|
832
|
+
const allCommunication = [];
|
|
833
|
+
patients.forEach((patient) => {
|
|
834
|
+
if (patient.name) {
|
|
835
|
+
allNames.push(...patient.name);
|
|
810
836
|
}
|
|
811
|
-
|
|
812
|
-
|
|
837
|
+
if (patient.identifier) {
|
|
838
|
+
allIdentifiers.push(...patient.identifier);
|
|
839
|
+
}
|
|
840
|
+
if (patient.telecom) {
|
|
841
|
+
allTelecom.push(...patient.telecom);
|
|
842
|
+
}
|
|
843
|
+
if (patient.address) {
|
|
844
|
+
allAddresses.push(...patient.address);
|
|
845
|
+
}
|
|
846
|
+
if (patient.communication) {
|
|
847
|
+
allCommunication.push(...patient.communication);
|
|
848
|
+
}
|
|
849
|
+
if (!combined.gender && patient.gender) {
|
|
850
|
+
combined.gender = patient.gender;
|
|
851
|
+
}
|
|
852
|
+
if (!combined.birthDate && patient.birthDate) {
|
|
853
|
+
combined.birthDate = patient.birthDate;
|
|
854
|
+
}
|
|
855
|
+
if (!combined.maritalStatus && patient.maritalStatus) {
|
|
856
|
+
combined.maritalStatus = patient.maritalStatus;
|
|
857
|
+
}
|
|
858
|
+
if (!combined.deceasedBoolean && patient.deceasedBoolean !== void 0) {
|
|
859
|
+
combined.deceasedBoolean = patient.deceasedBoolean;
|
|
860
|
+
}
|
|
861
|
+
if (!combined.deceasedDateTime && patient.deceasedDateTime) {
|
|
862
|
+
combined.deceasedDateTime = patient.deceasedDateTime;
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
combined.name = allNames;
|
|
866
|
+
combined.identifier = allIdentifiers;
|
|
867
|
+
combined.telecom = allTelecom;
|
|
868
|
+
combined.address = allAddresses;
|
|
869
|
+
combined.communication = allCommunication;
|
|
870
|
+
return combined;
|
|
813
871
|
}
|
|
814
872
|
/**
|
|
815
873
|
* Renders patient names as HTML list items
|
|
816
|
-
* @param patient - Patient
|
|
874
|
+
* @param patient - Patient resources
|
|
817
875
|
* @returns HTML string of list items
|
|
818
876
|
*/
|
|
819
877
|
static renderNames(patient) {
|
|
@@ -824,14 +882,16 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
824
882
|
patient.name.forEach((name) => {
|
|
825
883
|
if (name.use !== "old") {
|
|
826
884
|
const nameText = name.text || ((name.given || []).join(" ") + " " + (name.family || "")).trim();
|
|
827
|
-
|
|
885
|
+
if (nameText) {
|
|
886
|
+
uniqueNames.add(nameText);
|
|
887
|
+
}
|
|
828
888
|
}
|
|
829
889
|
});
|
|
830
890
|
return Array.from(uniqueNames).map((nameText) => `<ul><li>${nameText}</li></ul>`).join("");
|
|
831
891
|
}
|
|
832
892
|
/**
|
|
833
893
|
* Renders patient identifiers as HTML list items
|
|
834
|
-
* @param patient - Patient
|
|
894
|
+
* @param patient - Patient resources
|
|
835
895
|
* @returns HTML string of list items
|
|
836
896
|
*/
|
|
837
897
|
static renderIdentifiers(patient) {
|
|
@@ -846,7 +906,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
846
906
|
}
|
|
847
907
|
/**
|
|
848
908
|
* Renders patient telecom information grouped by system
|
|
849
|
-
* @param patient - Patient
|
|
909
|
+
* @param patient - Patient resources
|
|
850
910
|
* @returns HTML string grouped by system
|
|
851
911
|
*/
|
|
852
912
|
static renderTelecom(patient) {
|
|
@@ -854,9 +914,10 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
854
914
|
return "";
|
|
855
915
|
}
|
|
856
916
|
const systemPriority = ["email", "phone", "pager", "sms", "fax", "url", "other"];
|
|
917
|
+
const numberSystems = ["phone", "pager", "sms", "fax"];
|
|
857
918
|
const telecomBySystem = /* @__PURE__ */ new Map();
|
|
858
919
|
patient.telecom.forEach((telecom) => {
|
|
859
|
-
if (telecom.system && telecom.value) {
|
|
920
|
+
if (telecom.system && telecom.value && telecom.use !== "old") {
|
|
860
921
|
const system = telecom.system.toLowerCase();
|
|
861
922
|
if (!telecomBySystem.has(system)) {
|
|
862
923
|
telecomBySystem.set(system, /* @__PURE__ */ new Set());
|
|
@@ -864,6 +925,29 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
864
925
|
telecomBySystem.get(system).add(telecom.value);
|
|
865
926
|
}
|
|
866
927
|
});
|
|
928
|
+
for (const system of numberSystems) {
|
|
929
|
+
const currentNumbers = Array.from(telecomBySystem.get(system) || []);
|
|
930
|
+
if (currentNumbers.length <= 1) continue;
|
|
931
|
+
const numbersWithCleaned = currentNumbers.map((num) => ({
|
|
932
|
+
original: num,
|
|
933
|
+
cleaned: num.replace(/\D/g, "")
|
|
934
|
+
}));
|
|
935
|
+
const toRemove = /* @__PURE__ */ new Set();
|
|
936
|
+
for (let i = 0; i < numbersWithCleaned.length; i++) {
|
|
937
|
+
for (let j = i + 1; j < numbersWithCleaned.length; j++) {
|
|
938
|
+
const num1 = numbersWithCleaned[i];
|
|
939
|
+
const num2 = numbersWithCleaned[j];
|
|
940
|
+
if (num1.cleaned.endsWith(num2.cleaned)) {
|
|
941
|
+
toRemove.add(num2.original);
|
|
942
|
+
} else if (num2.cleaned.endsWith(num1.cleaned)) {
|
|
943
|
+
toRemove.add(num1.original);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
toRemove.forEach((numberToRemove) => {
|
|
948
|
+
telecomBySystem.get(system)?.delete(numberToRemove);
|
|
949
|
+
});
|
|
950
|
+
}
|
|
867
951
|
return Array.from(telecomBySystem.entries()).sort(([systemA], [systemB]) => {
|
|
868
952
|
const priorityA = systemPriority.indexOf(systemA);
|
|
869
953
|
const priorityB = systemPriority.indexOf(systemB);
|
|
@@ -881,7 +965,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
881
965
|
}
|
|
882
966
|
/**
|
|
883
967
|
* Renders patient addresses as HTML list items
|
|
884
|
-
* @param patient - Patient
|
|
968
|
+
* @param patient - Patient resources
|
|
885
969
|
* @returns HTML string of list items
|
|
886
970
|
*/
|
|
887
971
|
static renderAddresses(patient) {
|
|
@@ -890,7 +974,33 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
890
974
|
}
|
|
891
975
|
const uniqueAddresses = /* @__PURE__ */ new Set();
|
|
892
976
|
patient.address.forEach((address) => {
|
|
893
|
-
|
|
977
|
+
if (address.use === "old") {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
const addressArray = [];
|
|
981
|
+
if (address.text) {
|
|
982
|
+
addressArray.push(address.text);
|
|
983
|
+
} else {
|
|
984
|
+
if (address.line) {
|
|
985
|
+
addressArray.push(...address.line);
|
|
986
|
+
}
|
|
987
|
+
if (address.city) {
|
|
988
|
+
addressArray.push(address.city);
|
|
989
|
+
}
|
|
990
|
+
if (address.district) {
|
|
991
|
+
addressArray.push(address.district);
|
|
992
|
+
}
|
|
993
|
+
if (address.state) {
|
|
994
|
+
addressArray.push(address.state);
|
|
995
|
+
}
|
|
996
|
+
if (address.country) {
|
|
997
|
+
addressArray.push(address.country);
|
|
998
|
+
}
|
|
999
|
+
if (address.postalCode) {
|
|
1000
|
+
addressArray.push(address.postalCode);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
const addressText = addressArray.join(", ").trim();
|
|
894
1004
|
if (addressText) {
|
|
895
1005
|
uniqueAddresses.add(addressText);
|
|
896
1006
|
}
|
|
@@ -899,7 +1009,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
899
1009
|
}
|
|
900
1010
|
/**
|
|
901
1011
|
* Renders patient deceased status
|
|
902
|
-
* @param patient - Patient
|
|
1012
|
+
* @param patient - Patient resources
|
|
903
1013
|
* @returns HTML string for deceased status
|
|
904
1014
|
*/
|
|
905
1015
|
static renderDeceased(patient) {
|
|
@@ -914,19 +1024,25 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
914
1024
|
/**
|
|
915
1025
|
* Renders patient communication preferences as HTML list items
|
|
916
1026
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
917
|
-
* @param patient - Patient
|
|
1027
|
+
* @param patient - Patient resources
|
|
918
1028
|
* @returns HTML string of list items
|
|
919
1029
|
*/
|
|
920
1030
|
static renderCommunication(templateUtilities, patient) {
|
|
921
1031
|
if (!patient.communication || patient.communication.length === 0) {
|
|
922
1032
|
return "";
|
|
923
1033
|
}
|
|
924
|
-
|
|
925
|
-
|
|
1034
|
+
const uniqueLanguages = /* @__PURE__ */ new Set();
|
|
1035
|
+
const preferredLanguages = /* @__PURE__ */ new Set();
|
|
1036
|
+
patient.communication.forEach((comm) => {
|
|
926
1037
|
const language = templateUtilities.codeableConcept(comm.language);
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1038
|
+
if (language) {
|
|
1039
|
+
if (comm.preferred) {
|
|
1040
|
+
preferredLanguages.add(language);
|
|
1041
|
+
}
|
|
1042
|
+
uniqueLanguages.add(language);
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
return Array.from(uniqueLanguages).map((language) => `<ul><li>${language}${preferredLanguages.has(language) ? " (preferred)" : ""}</li></ul>`).join("");
|
|
930
1046
|
}
|
|
931
1047
|
/**
|
|
932
1048
|
* Capitalizes first letter of a string
|
|
@@ -943,32 +1059,30 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
943
1059
|
var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
944
1060
|
/**
|
|
945
1061
|
* Generate HTML narrative for AllergyIntolerance resources
|
|
946
|
-
* @param
|
|
1062
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
947
1063
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
948
1064
|
* @returns HTML string for rendering
|
|
949
1065
|
*/
|
|
950
|
-
generateNarrative(
|
|
951
|
-
return _AllergyIntoleranceTemplate.generateStaticNarrative(
|
|
1066
|
+
generateNarrative(resources, timezone) {
|
|
1067
|
+
return _AllergyIntoleranceTemplate.generateStaticNarrative(resources, timezone);
|
|
952
1068
|
}
|
|
953
1069
|
/**
|
|
954
1070
|
* Internal static implementation that actually generates the narrative
|
|
955
|
-
* @param
|
|
1071
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
956
1072
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
957
1073
|
* @returns HTML string for rendering
|
|
958
1074
|
*/
|
|
959
|
-
static generateStaticNarrative(
|
|
960
|
-
const templateUtilities = new TemplateUtilities(
|
|
1075
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1076
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
961
1077
|
const activeAllergies = [];
|
|
962
1078
|
const resolvedAllergies = [];
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
activeAllergies.push(allergy);
|
|
971
|
-
}
|
|
1079
|
+
for (const resourceItem of resources) {
|
|
1080
|
+
const allergy = resourceItem;
|
|
1081
|
+
const isResolved = allergy.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code));
|
|
1082
|
+
if (isResolved) {
|
|
1083
|
+
resolvedAllergies.push(allergy);
|
|
1084
|
+
} else {
|
|
1085
|
+
activeAllergies.push(allergy);
|
|
972
1086
|
}
|
|
973
1087
|
}
|
|
974
1088
|
activeAllergies.sort((a, b) => {
|
|
@@ -992,7 +1106,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
992
1106
|
<th>Status</th>
|
|
993
1107
|
<th>Category</th>
|
|
994
1108
|
<th>Reaction</th>
|
|
995
|
-
<th>Severity</th>
|
|
996
1109
|
<th>Onset Date</th>
|
|
997
1110
|
<th>Comments</th>
|
|
998
1111
|
</tr>
|
|
@@ -1020,7 +1133,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1020
1133
|
<th>Status</th>
|
|
1021
1134
|
<th>Category</th>
|
|
1022
1135
|
<th>Reaction</th>
|
|
1023
|
-
<th>Severity</th>
|
|
1024
1136
|
<th>Onset Date</th>
|
|
1025
1137
|
<th>Comments</th>
|
|
1026
1138
|
<th>Resolved Date</th>
|
|
@@ -1058,7 +1170,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1058
1170
|
<td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
|
|
1059
1171
|
<td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
|
|
1060
1172
|
<td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
|
|
1061
|
-
<td class="Severity">${templateUtilities.safeConcat(allergy.reaction, "severity") || "-"}</td>
|
|
1062
1173
|
<td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
|
|
1063
1174
|
<td class="Comments">${templateUtilities.renderNotes(allergy.note, timezone, { styled: true, warning: true })}</td>`;
|
|
1064
1175
|
if (includeResolved) {
|
|
@@ -1084,12 +1195,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1084
1195
|
var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
1085
1196
|
/**
|
|
1086
1197
|
* Generate HTML narrative for Medication resources
|
|
1087
|
-
* @param
|
|
1198
|
+
* @param resources - FHIR Medication resources
|
|
1088
1199
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1089
1200
|
* @returns HTML string for rendering
|
|
1090
1201
|
*/
|
|
1091
|
-
generateNarrative(
|
|
1092
|
-
return _MedicationSummaryTemplate.generateStaticNarrative(
|
|
1202
|
+
generateNarrative(resources, timezone) {
|
|
1203
|
+
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
|
|
1093
1204
|
}
|
|
1094
1205
|
/**
|
|
1095
1206
|
* Safely parse a date string and return a valid Date object or null
|
|
@@ -1154,16 +1265,16 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1154
1265
|
}
|
|
1155
1266
|
/**
|
|
1156
1267
|
* Internal static implementation that actually generates the narrative
|
|
1157
|
-
* @param
|
|
1268
|
+
* @param resources - FHIR Medication resources
|
|
1158
1269
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1159
1270
|
* @returns HTML string for rendering
|
|
1160
1271
|
*/
|
|
1161
1272
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1162
|
-
static generateStaticNarrative(
|
|
1163
|
-
const templateUtilities = new TemplateUtilities(
|
|
1273
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1274
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1164
1275
|
let html = "";
|
|
1165
|
-
const medicationRequests = this.getMedicationRequests(templateUtilities,
|
|
1166
|
-
const medicationStatements = this.getMedicationStatements(templateUtilities,
|
|
1276
|
+
const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
|
|
1277
|
+
const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
|
|
1167
1278
|
const allActiveMedications = [];
|
|
1168
1279
|
const allInactiveMedications = [];
|
|
1169
1280
|
medicationRequests.forEach((mr) => {
|
|
@@ -1208,42 +1319,42 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1208
1319
|
};
|
|
1209
1320
|
if (allActiveMedications.length > 0) {
|
|
1210
1321
|
sortMedications(allActiveMedications);
|
|
1211
|
-
html += this.renderCombinedMedications(templateUtilities, allActiveMedications,
|
|
1322
|
+
html += this.renderCombinedMedications(templateUtilities, allActiveMedications, true);
|
|
1212
1323
|
}
|
|
1213
1324
|
if (allInactiveMedications.length > 0) {
|
|
1214
1325
|
sortMedications(allInactiveMedications);
|
|
1215
|
-
html += this.renderCombinedMedications(templateUtilities, allInactiveMedications,
|
|
1326
|
+
html += this.renderCombinedMedications(templateUtilities, allInactiveMedications, false);
|
|
1216
1327
|
}
|
|
1217
1328
|
return html;
|
|
1218
1329
|
}
|
|
1219
1330
|
/**
|
|
1220
|
-
* Extract MedicationRequest resources
|
|
1331
|
+
* Extract MedicationRequest resources
|
|
1221
1332
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1222
|
-
* @param
|
|
1333
|
+
* @param resources - FHIR Medication resources
|
|
1223
1334
|
* @returns Array of MedicationRequest resources
|
|
1224
1335
|
*/
|
|
1225
|
-
static getMedicationRequests(templateUtilities,
|
|
1226
|
-
if (
|
|
1336
|
+
static getMedicationRequests(templateUtilities, resources) {
|
|
1337
|
+
if (resources.length === 0) {
|
|
1227
1338
|
return [];
|
|
1228
1339
|
}
|
|
1229
|
-
return
|
|
1230
|
-
resource: entry
|
|
1231
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1340
|
+
return resources.filter((entry) => entry.resourceType === "MedicationRequest").map((entry) => ({
|
|
1341
|
+
resource: entry,
|
|
1342
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1232
1343
|
}));
|
|
1233
1344
|
}
|
|
1234
1345
|
/**
|
|
1235
|
-
* Extract MedicationStatement resources
|
|
1346
|
+
* Extract MedicationStatement resources
|
|
1236
1347
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1237
|
-
* @param
|
|
1348
|
+
* @param resources - FHIR Medication resources
|
|
1238
1349
|
* @returns Array of MedicationStatement resources
|
|
1239
1350
|
*/
|
|
1240
|
-
static getMedicationStatements(templateUtilities,
|
|
1241
|
-
if (
|
|
1351
|
+
static getMedicationStatements(templateUtilities, resources) {
|
|
1352
|
+
if (resources.length === 0) {
|
|
1242
1353
|
return [];
|
|
1243
1354
|
}
|
|
1244
|
-
return
|
|
1245
|
-
resource: entry
|
|
1246
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1355
|
+
return resources.filter((entry) => entry.resourceType === "MedicationStatement").map((entry) => ({
|
|
1356
|
+
resource: entry,
|
|
1357
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1247
1358
|
}));
|
|
1248
1359
|
}
|
|
1249
1360
|
/**
|
|
@@ -1253,9 +1364,9 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1253
1364
|
* @param sectionTitle - Title for the section
|
|
1254
1365
|
* @returns HTML string for rendering
|
|
1255
1366
|
*/
|
|
1256
|
-
static renderCombinedMedications(templateUtilities, medications,
|
|
1367
|
+
static renderCombinedMedications(templateUtilities, medications, isActiveSection) {
|
|
1257
1368
|
let html = `
|
|
1258
|
-
<h3>${
|
|
1369
|
+
<h3>${isActiveSection ? "Active Medications" : "Inactive Medications"}</h3>
|
|
1259
1370
|
<table>
|
|
1260
1371
|
<thead>
|
|
1261
1372
|
<tr>
|
|
@@ -1264,8 +1375,8 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1264
1375
|
<th>Sig</th>
|
|
1265
1376
|
<th>Dispense Quantity</th>
|
|
1266
1377
|
<th>Refills</th>
|
|
1267
|
-
<th>Start Date</th
|
|
1268
|
-
<th>End Date</th
|
|
1378
|
+
<th>Start Date</th>${isActiveSection ? "" : `
|
|
1379
|
+
<th>End Date</th>`}
|
|
1269
1380
|
<th>Status</th>
|
|
1270
1381
|
</tr>
|
|
1271
1382
|
</thead>
|
|
@@ -1323,8 +1434,8 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1323
1434
|
<td>${sig}</td>
|
|
1324
1435
|
<td>${dispenseQuantity}</td>
|
|
1325
1436
|
<td>${refills}</td>
|
|
1326
|
-
<td>${startDate}</td
|
|
1327
|
-
<td>${endDate}</td
|
|
1437
|
+
<td>${startDate}</td>${isActiveSection ? "" : `
|
|
1438
|
+
<td>${endDate}</td>`}
|
|
1328
1439
|
<td>${status}</td>
|
|
1329
1440
|
</tr>`;
|
|
1330
1441
|
}
|
|
@@ -1339,28 +1450,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1339
1450
|
var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
1340
1451
|
/**
|
|
1341
1452
|
* Generate HTML narrative for Immunization resources
|
|
1342
|
-
* @param
|
|
1453
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1343
1454
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1344
1455
|
* @returns HTML string for rendering
|
|
1345
1456
|
*/
|
|
1346
|
-
generateNarrative(
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
}
|
|
1354
|
-
return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
|
|
1457
|
+
generateNarrative(resources, timezone) {
|
|
1458
|
+
resources.sort((a, b) => {
|
|
1459
|
+
const dateA = a.occurrenceDateTime;
|
|
1460
|
+
const dateB = b.occurrenceDateTime;
|
|
1461
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1462
|
+
});
|
|
1463
|
+
return _ImmunizationsTemplate.generateStaticNarrative(resources, timezone);
|
|
1355
1464
|
}
|
|
1356
1465
|
/**
|
|
1357
1466
|
* Internal static implementation that actually generates the narrative
|
|
1358
|
-
* @param
|
|
1467
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1359
1468
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1360
1469
|
* @returns HTML string for rendering
|
|
1361
1470
|
*/
|
|
1362
|
-
static generateStaticNarrative(
|
|
1363
|
-
const templateUtilities = new TemplateUtilities(
|
|
1471
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1472
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1364
1473
|
let html = `
|
|
1365
1474
|
<table>
|
|
1366
1475
|
<thead>
|
|
@@ -1375,21 +1484,20 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1375
1484
|
</tr>
|
|
1376
1485
|
</thead>
|
|
1377
1486
|
<tbody>`;
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
}
|
|
1487
|
+
const immunizations = resources.filter((resourceItem) => resourceItem.resourceType === "Immunization");
|
|
1488
|
+
if (immunizations.length > 0) {
|
|
1489
|
+
for (const resourceItem of immunizations) {
|
|
1490
|
+
const imm = resourceItem;
|
|
1491
|
+
html += `
|
|
1492
|
+
<tr id="${templateUtilities.narrativeLinkId(imm)}">
|
|
1493
|
+
<td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
|
|
1494
|
+
<td>${imm.status || ""}</td>
|
|
1495
|
+
<td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
|
|
1496
|
+
<td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
|
|
1497
|
+
<td>${imm.lotNumber || ""}</td>
|
|
1498
|
+
<td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
|
|
1499
|
+
<td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
|
|
1500
|
+
</tr>`;
|
|
1393
1501
|
}
|
|
1394
1502
|
}
|
|
1395
1503
|
html += `
|
|
@@ -1403,23 +1511,23 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1403
1511
|
var ProblemListTemplate = class _ProblemListTemplate {
|
|
1404
1512
|
/**
|
|
1405
1513
|
* Generate HTML narrative for Problem List
|
|
1406
|
-
* @param
|
|
1514
|
+
* @param resources - FHIR Condition resources
|
|
1407
1515
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1408
1516
|
* @returns HTML string for rendering
|
|
1409
1517
|
*/
|
|
1410
|
-
generateNarrative(
|
|
1411
|
-
return _ProblemListTemplate.generateStaticNarrative(
|
|
1518
|
+
generateNarrative(resources, timezone) {
|
|
1519
|
+
return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
|
|
1412
1520
|
}
|
|
1413
1521
|
/**
|
|
1414
1522
|
* Internal static implementation that actually generates the narrative
|
|
1415
|
-
* @param
|
|
1523
|
+
* @param resources - FHIR Condition resources
|
|
1416
1524
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1417
1525
|
* @returns HTML string for rendering
|
|
1418
1526
|
*/
|
|
1419
|
-
static generateStaticNarrative(
|
|
1420
|
-
const templateUtilities = new TemplateUtilities(
|
|
1527
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1528
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1421
1529
|
let html = ``;
|
|
1422
|
-
const activeConditions =
|
|
1530
|
+
const activeConditions = resources.map((entry) => entry) || [];
|
|
1423
1531
|
activeConditions.sort((a, b) => {
|
|
1424
1532
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1425
1533
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1430,7 +1538,6 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1430
1538
|
<thead>
|
|
1431
1539
|
<tr>
|
|
1432
1540
|
<th>Problem</th>
|
|
1433
|
-
<th>Severity</th>
|
|
1434
1541
|
<th>Onset Date</th>
|
|
1435
1542
|
<th>Recorded Date</th>
|
|
1436
1543
|
<th>Notes</th>
|
|
@@ -1440,7 +1547,6 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1440
1547
|
for (const cond of activeConditions) {
|
|
1441
1548
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1442
1549
|
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1443
|
-
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1444
1550
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1445
1551
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1446
1552
|
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
@@ -1456,22 +1562,22 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1456
1562
|
var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
1457
1563
|
/**
|
|
1458
1564
|
* Generate HTML narrative for Vital Signs
|
|
1459
|
-
* @param
|
|
1565
|
+
* @param resources - FHIR Observation resources
|
|
1460
1566
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1461
1567
|
* @returns HTML string for rendering
|
|
1462
1568
|
*/
|
|
1463
|
-
generateNarrative(
|
|
1464
|
-
return _VitalSignsTemplate.generateStaticNarrative(
|
|
1569
|
+
generateNarrative(resources, timezone) {
|
|
1570
|
+
return _VitalSignsTemplate.generateStaticNarrative(resources, timezone);
|
|
1465
1571
|
}
|
|
1466
1572
|
/**
|
|
1467
1573
|
* Internal static implementation that actually generates the narrative
|
|
1468
|
-
* @param
|
|
1574
|
+
* @param resources - FHIR Observation resources
|
|
1469
1575
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1470
1576
|
* @returns HTML string for rendering
|
|
1471
1577
|
*/
|
|
1472
|
-
static generateStaticNarrative(
|
|
1473
|
-
const templateUtilities = new TemplateUtilities(
|
|
1474
|
-
const observations =
|
|
1578
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1579
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1580
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1475
1581
|
observations.sort((a, b) => {
|
|
1476
1582
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1477
1583
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1514,28 +1620,21 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1514
1620
|
var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
1515
1621
|
/**
|
|
1516
1622
|
* Generate HTML narrative for Medical Device resources
|
|
1517
|
-
* @param
|
|
1623
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1518
1624
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1519
1625
|
* @returns HTML string for rendering
|
|
1520
1626
|
*/
|
|
1521
|
-
generateNarrative(
|
|
1522
|
-
|
|
1523
|
-
resource.entry.sort((a, b) => {
|
|
1524
|
-
const dateA = a.resource?.recordedOn;
|
|
1525
|
-
const dateB = b.resource?.recordedOn;
|
|
1526
|
-
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1527
|
-
});
|
|
1528
|
-
}
|
|
1529
|
-
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1627
|
+
generateNarrative(resources, timezone) {
|
|
1628
|
+
return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
|
|
1530
1629
|
}
|
|
1531
1630
|
/**
|
|
1532
1631
|
* Internal static implementation that actually generates the narrative
|
|
1533
|
-
* @param
|
|
1632
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1534
1633
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1535
1634
|
* @returns HTML string for rendering
|
|
1536
1635
|
*/
|
|
1537
|
-
static generateStaticNarrative(
|
|
1538
|
-
const templateUtilities = new TemplateUtilities(
|
|
1636
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1637
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1539
1638
|
let html = `
|
|
1540
1639
|
<table>
|
|
1541
1640
|
<thead>
|
|
@@ -1547,19 +1646,19 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1547
1646
|
</tr>
|
|
1548
1647
|
</thead>
|
|
1549
1648
|
<tbody>`;
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1649
|
+
const deviceStatements = resources.filter((resourceItem) => resourceItem.resourceType === "DeviceUseStatement").map((resourceItem) => resourceItem).sort((a, b) => {
|
|
1650
|
+
const dateA = a.recordedOn;
|
|
1651
|
+
const dateB = b.recordedOn;
|
|
1652
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1653
|
+
});
|
|
1654
|
+
for (const dus of deviceStatements) {
|
|
1655
|
+
html += `
|
|
1656
|
+
<tr id="${templateUtilities.narrativeLinkId(dus)}">
|
|
1657
|
+
<td>${templateUtilities.renderDevice(dus.device)}</td>
|
|
1658
|
+
<td>${dus.status || ""}</td>
|
|
1659
|
+
<td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
|
|
1660
|
+
<td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
|
|
1661
|
+
</tr>`;
|
|
1563
1662
|
}
|
|
1564
1663
|
html += `
|
|
1565
1664
|
</tbody>
|
|
@@ -1572,23 +1671,23 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1572
1671
|
var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
1573
1672
|
/**
|
|
1574
1673
|
* Generate HTML narrative for Diagnostic Results
|
|
1575
|
-
* @param
|
|
1674
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1576
1675
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1577
1676
|
* @returns HTML string for rendering
|
|
1578
1677
|
*/
|
|
1579
|
-
generateNarrative(
|
|
1580
|
-
return _DiagnosticResultsTemplate.generateStaticNarrative(
|
|
1678
|
+
generateNarrative(resources, timezone) {
|
|
1679
|
+
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
|
|
1581
1680
|
}
|
|
1582
1681
|
/**
|
|
1583
1682
|
* Internal static implementation that actually generates the narrative
|
|
1584
|
-
* @param
|
|
1683
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1585
1684
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1586
1685
|
* @returns HTML string for rendering
|
|
1587
1686
|
*/
|
|
1588
|
-
static generateStaticNarrative(
|
|
1589
|
-
const templateUtilities = new TemplateUtilities(
|
|
1687
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1688
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1590
1689
|
let html = "";
|
|
1591
|
-
const observations = this.getObservations(
|
|
1690
|
+
const observations = this.getObservations(resources);
|
|
1592
1691
|
if (observations.length > 0) {
|
|
1593
1692
|
observations.sort((a, b) => {
|
|
1594
1693
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
@@ -1597,7 +1696,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1597
1696
|
});
|
|
1598
1697
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1599
1698
|
}
|
|
1600
|
-
const diagnosticReports = this.getDiagnosticReports(
|
|
1699
|
+
const diagnosticReports = this.getDiagnosticReports(resources);
|
|
1601
1700
|
if (diagnosticReports.length > 0) {
|
|
1602
1701
|
diagnosticReports.sort((a, b) => {
|
|
1603
1702
|
const dateA = a.issued;
|
|
@@ -1609,26 +1708,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1609
1708
|
return html;
|
|
1610
1709
|
}
|
|
1611
1710
|
/**
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
static getObservations(
|
|
1617
|
-
|
|
1618
|
-
return [];
|
|
1619
|
-
}
|
|
1620
|
-
return resource.entry.filter((entry) => entry.resource?.resourceType === "Observation").map((entry) => entry.resource);
|
|
1711
|
+
* Get all Observation resources from the resource array
|
|
1712
|
+
* @param resources - FHIR resources array
|
|
1713
|
+
* @returns Array of Observation resources
|
|
1714
|
+
*/
|
|
1715
|
+
static getObservations(resources) {
|
|
1716
|
+
return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
|
|
1621
1717
|
}
|
|
1622
1718
|
/**
|
|
1623
|
-
*
|
|
1624
|
-
* @param
|
|
1719
|
+
* Get all DiagnosticReport resources from the resource array
|
|
1720
|
+
* @param resources - FHIR resources array
|
|
1625
1721
|
* @returns Array of DiagnosticReport resources
|
|
1626
1722
|
*/
|
|
1627
|
-
static getDiagnosticReports(
|
|
1628
|
-
|
|
1629
|
-
return [];
|
|
1630
|
-
}
|
|
1631
|
-
return resource.entry.filter((entry) => entry.resource?.resourceType === "DiagnosticReport").map((entry) => entry.resource);
|
|
1723
|
+
static getDiagnosticReports(resources) {
|
|
1724
|
+
return resources.filter((resourceItem) => resourceItem.resourceType === "DiagnosticReport").map((resourceItem) => resourceItem);
|
|
1632
1725
|
}
|
|
1633
1726
|
/**
|
|
1634
1727
|
* Render HTML table for Observation resources
|
|
@@ -1684,7 +1777,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1684
1777
|
<thead>
|
|
1685
1778
|
<tr>
|
|
1686
1779
|
<th>Report</th>
|
|
1687
|
-
<th>Status</th>
|
|
1688
1780
|
<th>Category</th>
|
|
1689
1781
|
<th>Result</th>
|
|
1690
1782
|
<th>Issued</th>
|
|
@@ -1699,7 +1791,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1699
1791
|
html += `
|
|
1700
1792
|
<tr id="${templateUtilities.narrativeLinkId(report)}">
|
|
1701
1793
|
<td>${templateUtilities.codeableConcept(report.code)}</td>
|
|
1702
|
-
<td>${report.status || ""}</td>
|
|
1703
1794
|
<td>${templateUtilities.firstFromCodeableConceptList(report.category)}</td>
|
|
1704
1795
|
<td>${resultCount}</td>
|
|
1705
1796
|
<td>${report.issued ? templateUtilities.renderTime(report.issued, timezone) : ""}</td>
|
|
@@ -1716,26 +1807,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1716
1807
|
var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
1717
1808
|
/**
|
|
1718
1809
|
* Generate HTML narrative for Procedure resources
|
|
1719
|
-
* @param
|
|
1810
|
+
* @param resources - FHIR Procedure resources
|
|
1720
1811
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1721
1812
|
* @returns HTML string for rendering
|
|
1722
1813
|
*/
|
|
1723
|
-
generateNarrative(
|
|
1724
|
-
|
|
1725
|
-
const dateA = a.
|
|
1726
|
-
const dateB = b.
|
|
1814
|
+
generateNarrative(resources, timezone) {
|
|
1815
|
+
resources.sort((a, b) => {
|
|
1816
|
+
const dateA = a.performedDateTime || a.performedPeriod?.start;
|
|
1817
|
+
const dateB = b.performedDateTime || b.performedPeriod?.start;
|
|
1727
1818
|
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1728
1819
|
});
|
|
1729
|
-
return _HistoryOfProceduresTemplate.generateStaticNarrative(
|
|
1820
|
+
return _HistoryOfProceduresTemplate.generateStaticNarrative(resources, timezone);
|
|
1730
1821
|
}
|
|
1731
1822
|
/**
|
|
1732
1823
|
* Internal static implementation that actually generates the narrative
|
|
1733
|
-
* @param
|
|
1824
|
+
* @param resources - FHIR Procedure resources
|
|
1734
1825
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1735
1826
|
* @returns HTML string for rendering
|
|
1736
1827
|
*/
|
|
1737
|
-
static generateStaticNarrative(
|
|
1738
|
-
const templateUtilities = new TemplateUtilities(
|
|
1828
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1829
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1739
1830
|
let html = `
|
|
1740
1831
|
<table>
|
|
1741
1832
|
<thead>
|
|
@@ -1746,16 +1837,14 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1746
1837
|
</tr>
|
|
1747
1838
|
</thead>
|
|
1748
1839
|
<tbody>`;
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
<
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
</tr>`;
|
|
1758
|
-
}
|
|
1840
|
+
for (const resourceItem of resources) {
|
|
1841
|
+
const proc = resourceItem;
|
|
1842
|
+
html += `
|
|
1843
|
+
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
1844
|
+
<td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
|
|
1845
|
+
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
1846
|
+
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
1847
|
+
</tr>`;
|
|
1759
1848
|
}
|
|
1760
1849
|
html += `
|
|
1761
1850
|
</tbody>
|
|
@@ -1768,22 +1857,22 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1768
1857
|
var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
1769
1858
|
/**
|
|
1770
1859
|
* Generate HTML narrative for Social History
|
|
1771
|
-
* @param
|
|
1860
|
+
* @param resources - FHIR Observation resources
|
|
1772
1861
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1773
1862
|
* @returns HTML string for rendering
|
|
1774
1863
|
*/
|
|
1775
|
-
generateNarrative(
|
|
1776
|
-
return _SocialHistoryTemplate.generateStaticNarrative(
|
|
1864
|
+
generateNarrative(resources, timezone) {
|
|
1865
|
+
return _SocialHistoryTemplate.generateStaticNarrative(resources, timezone);
|
|
1777
1866
|
}
|
|
1778
1867
|
/**
|
|
1779
1868
|
* Internal static implementation that actually generates the narrative
|
|
1780
|
-
* @param
|
|
1869
|
+
* @param resources - FHIR Observation resources
|
|
1781
1870
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1782
1871
|
* @returns HTML string for rendering
|
|
1783
1872
|
*/
|
|
1784
|
-
static generateStaticNarrative(
|
|
1785
|
-
const templateUtilities = new TemplateUtilities(
|
|
1786
|
-
const observations =
|
|
1873
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1874
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1875
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1787
1876
|
observations.sort((a, b) => {
|
|
1788
1877
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1789
1878
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1822,14 +1911,14 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1822
1911
|
var PastHistoryOfIllnessTemplate = class {
|
|
1823
1912
|
/**
|
|
1824
1913
|
* Generate HTML narrative for Past History of Illnesses
|
|
1825
|
-
* @param
|
|
1914
|
+
* @param resources - FHIR Condition resources
|
|
1826
1915
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1827
1916
|
* @returns HTML string for rendering
|
|
1828
1917
|
*/
|
|
1829
|
-
generateNarrative(
|
|
1830
|
-
const templateUtilities = new TemplateUtilities(
|
|
1918
|
+
generateNarrative(resources, timezone) {
|
|
1919
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1831
1920
|
let html = ``;
|
|
1832
|
-
const resolvedConditions =
|
|
1921
|
+
const resolvedConditions = resources.map((entry) => entry) || [];
|
|
1833
1922
|
resolvedConditions.sort((a, b) => {
|
|
1834
1923
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1835
1924
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1840,7 +1929,6 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1840
1929
|
<thead>
|
|
1841
1930
|
<tr>
|
|
1842
1931
|
<th>Problem</th>
|
|
1843
|
-
<th>Severity</th>
|
|
1844
1932
|
<th>Onset Date</th>
|
|
1845
1933
|
<th>Recorded Date</th>
|
|
1846
1934
|
<th>Resolved Date</th>
|
|
@@ -1851,7 +1939,6 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1851
1939
|
for (const cond of resolvedConditions) {
|
|
1852
1940
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1853
1941
|
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1854
|
-
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1855
1942
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1856
1943
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1857
1944
|
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
@@ -1868,13 +1955,13 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1868
1955
|
var PlanOfCareTemplate = class {
|
|
1869
1956
|
/**
|
|
1870
1957
|
* Generate HTML narrative for Plan of Care
|
|
1871
|
-
* @param
|
|
1958
|
+
* @param resources - FHIR CarePlan resources
|
|
1872
1959
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1873
1960
|
* @returns HTML string for rendering
|
|
1874
1961
|
*/
|
|
1875
|
-
generateNarrative(
|
|
1876
|
-
const templateUtilities = new TemplateUtilities(
|
|
1877
|
-
const carePlans =
|
|
1962
|
+
generateNarrative(resources, timezone) {
|
|
1963
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1964
|
+
const carePlans = resources.map((entry) => entry) || [];
|
|
1878
1965
|
carePlans.sort((a, b) => {
|
|
1879
1966
|
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1880
1967
|
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
@@ -1913,37 +2000,35 @@ var PlanOfCareTemplate = class {
|
|
|
1913
2000
|
var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
1914
2001
|
/**
|
|
1915
2002
|
* Generate HTML narrative for Functional Status
|
|
1916
|
-
* @param
|
|
2003
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
1917
2004
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1918
2005
|
* @returns HTML string for rendering
|
|
1919
2006
|
*/
|
|
1920
|
-
generateNarrative(
|
|
1921
|
-
return _FunctionalStatusTemplate.generateStaticNarrative(
|
|
2007
|
+
generateNarrative(resources, timezone) {
|
|
2008
|
+
return _FunctionalStatusTemplate.generateStaticNarrative(resources, timezone);
|
|
1922
2009
|
}
|
|
1923
2010
|
/**
|
|
1924
2011
|
* Internal static implementation that actually generates the narrative
|
|
1925
|
-
* @param
|
|
2012
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
1926
2013
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1927
2014
|
* @returns HTML string for rendering
|
|
1928
2015
|
*/
|
|
1929
|
-
static generateStaticNarrative(
|
|
1930
|
-
const templateUtilities = new TemplateUtilities(
|
|
2016
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2017
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1931
2018
|
let html = ``;
|
|
1932
2019
|
const activeConditions = [];
|
|
1933
2020
|
const clinicalImpressions = [];
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
activeConditions.push(cond);
|
|
1943
|
-
}
|
|
1944
|
-
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
1945
|
-
clinicalImpressions.push(entry.resource);
|
|
2021
|
+
for (const resourceItem of resources) {
|
|
2022
|
+
if (resourceItem.resourceType === "Condition") {
|
|
2023
|
+
const cond = resourceItem;
|
|
2024
|
+
const isResolved = cond.clinicalStatus?.coding?.some(
|
|
2025
|
+
(c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
|
|
2026
|
+
);
|
|
2027
|
+
if (!isResolved) {
|
|
2028
|
+
activeConditions.push(cond);
|
|
1946
2029
|
}
|
|
2030
|
+
} else if (resourceItem.resourceType === "ClinicalImpression") {
|
|
2031
|
+
clinicalImpressions.push(resourceItem);
|
|
1947
2032
|
}
|
|
1948
2033
|
}
|
|
1949
2034
|
activeConditions.sort((a, b) => {
|
|
@@ -1962,7 +2047,6 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
1962
2047
|
<thead>
|
|
1963
2048
|
<tr>
|
|
1964
2049
|
<th>Problem</th>
|
|
1965
|
-
<th>Severity</th>
|
|
1966
2050
|
<th>Onset Date</th>
|
|
1967
2051
|
<th>Recorded Date</th>
|
|
1968
2052
|
<th>Notes</th>
|
|
@@ -1972,7 +2056,6 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
1972
2056
|
for (const cond of activeConditions) {
|
|
1973
2057
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1974
2058
|
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1975
|
-
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1976
2059
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1977
2060
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1978
2061
|
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
@@ -2042,22 +2125,22 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
2042
2125
|
var PregnancyTemplate = class _PregnancyTemplate {
|
|
2043
2126
|
/**
|
|
2044
2127
|
* Generate HTML narrative for Pregnancy
|
|
2045
|
-
* @param
|
|
2128
|
+
* @param resources - FHIR Observation resources
|
|
2046
2129
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2047
2130
|
* @returns HTML string for rendering
|
|
2048
2131
|
*/
|
|
2049
|
-
generateNarrative(
|
|
2050
|
-
return _PregnancyTemplate.generateStaticNarrative(
|
|
2132
|
+
generateNarrative(resources, timezone) {
|
|
2133
|
+
return _PregnancyTemplate.generateStaticNarrative(resources, timezone);
|
|
2051
2134
|
}
|
|
2052
2135
|
/**
|
|
2053
2136
|
* Internal static implementation that actually generates the narrative
|
|
2054
|
-
* @param
|
|
2137
|
+
* @param resources - FHIR Observation resources
|
|
2055
2138
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2056
2139
|
* @returns HTML string for rendering
|
|
2057
2140
|
*/
|
|
2058
|
-
static generateStaticNarrative(
|
|
2059
|
-
const templateUtilities = new TemplateUtilities(
|
|
2060
|
-
const observations =
|
|
2141
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2142
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2143
|
+
const observations = resources.map((entry) => entry) || [];
|
|
2061
2144
|
observations.sort((a, b) => {
|
|
2062
2145
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2063
2146
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -2073,8 +2156,8 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2073
2156
|
</tr>
|
|
2074
2157
|
</thead>
|
|
2075
2158
|
<tbody>`;
|
|
2076
|
-
for (const
|
|
2077
|
-
const obs =
|
|
2159
|
+
for (const resource of observations) {
|
|
2160
|
+
const obs = resource;
|
|
2078
2161
|
html += `
|
|
2079
2162
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2080
2163
|
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
@@ -2093,29 +2176,27 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2093
2176
|
var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
2094
2177
|
/**
|
|
2095
2178
|
* Generate HTML narrative for Advance Directives
|
|
2096
|
-
* @param
|
|
2179
|
+
* @param resources - FHIR Consent resources
|
|
2097
2180
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2098
2181
|
* @returns HTML string for rendering
|
|
2099
2182
|
*/
|
|
2100
|
-
generateNarrative(
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
}
|
|
2108
|
-
return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
|
|
2183
|
+
generateNarrative(resources, timezone) {
|
|
2184
|
+
resources.sort((a, b) => {
|
|
2185
|
+
const dateA = new Date(a.dateTime || 0);
|
|
2186
|
+
const dateB = new Date(b.dateTime || 0);
|
|
2187
|
+
return dateB.getTime() - dateA.getTime();
|
|
2188
|
+
});
|
|
2189
|
+
return _AdvanceDirectivesTemplate.generateStaticNarrative(resources, timezone);
|
|
2109
2190
|
}
|
|
2110
2191
|
/**
|
|
2111
2192
|
* Internal static implementation that actually generates the narrative
|
|
2112
|
-
* @param
|
|
2193
|
+
* @param resources - FHIR Consent resources
|
|
2113
2194
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2114
2195
|
* @returns HTML string for rendering
|
|
2115
2196
|
*/
|
|
2116
2197
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2117
|
-
static generateStaticNarrative(
|
|
2118
|
-
const templateUtilities = new TemplateUtilities(
|
|
2198
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2199
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2119
2200
|
let html = `
|
|
2120
2201
|
<table>
|
|
2121
2202
|
<thead>
|
|
@@ -2127,17 +2208,15 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
2127
2208
|
</tr>
|
|
2128
2209
|
</thead>
|
|
2129
2210
|
<tbody>`;
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
<
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
</tr>`;
|
|
2140
|
-
}
|
|
2211
|
+
for (const resourceItem of resources) {
|
|
2212
|
+
const consent = resourceItem;
|
|
2213
|
+
html += `
|
|
2214
|
+
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
2215
|
+
<td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
|
|
2216
|
+
<td>${consent.status || ""}</td>
|
|
2217
|
+
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
2218
|
+
<td>${consent.dateTime || ""}</td>
|
|
2219
|
+
</tr>`;
|
|
2141
2220
|
}
|
|
2142
2221
|
html += `
|
|
2143
2222
|
</tbody>
|
|
@@ -2151,16 +2230,16 @@ var TypeScriptTemplateMapper = class {
|
|
|
2151
2230
|
/**
|
|
2152
2231
|
* Generates HTML narrative for a specific IPS section
|
|
2153
2232
|
* @param section - The IPS section
|
|
2154
|
-
* @param
|
|
2233
|
+
* @param resources - FHIR resources
|
|
2155
2234
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2156
2235
|
* @returns HTML string for rendering
|
|
2157
2236
|
*/
|
|
2158
|
-
static generateNarrative(section,
|
|
2237
|
+
static generateNarrative(section, resources, timezone) {
|
|
2159
2238
|
const templateClass = this.sectionToTemplate[section];
|
|
2160
2239
|
if (!templateClass) {
|
|
2161
2240
|
throw new Error(`No template found for section: ${section}`);
|
|
2162
2241
|
}
|
|
2163
|
-
return templateClass.generateNarrative(
|
|
2242
|
+
return templateClass.generateNarrative(resources, timezone);
|
|
2164
2243
|
}
|
|
2165
2244
|
};
|
|
2166
2245
|
// Map of section types to their template classes
|
|
@@ -2223,14 +2302,7 @@ var NarrativeGenerator = class {
|
|
|
2223
2302
|
return void 0;
|
|
2224
2303
|
}
|
|
2225
2304
|
try {
|
|
2226
|
-
const
|
|
2227
|
-
resourceType: "Bundle",
|
|
2228
|
-
type: "collection",
|
|
2229
|
-
entry: resources.map((resource) => ({
|
|
2230
|
-
resource
|
|
2231
|
-
}))
|
|
2232
|
-
};
|
|
2233
|
-
const content = TypeScriptTemplateMapper.generateNarrative(section, bundle, timezone);
|
|
2305
|
+
const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone);
|
|
2234
2306
|
if (!content) {
|
|
2235
2307
|
return void 0;
|
|
2236
2308
|
}
|
|
@@ -2315,13 +2387,16 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2315
2387
|
/**
|
|
2316
2388
|
* sets the patient resource for the IPS Composition.
|
|
2317
2389
|
* This is not needed if you are calling read_bundle, but can be used to set the patient resource directly.
|
|
2318
|
-
* @param
|
|
2390
|
+
* @param patients - FHIR Patient resource to set
|
|
2319
2391
|
*/
|
|
2320
|
-
setPatient(
|
|
2321
|
-
if (!
|
|
2392
|
+
setPatient(patients) {
|
|
2393
|
+
if (!Array.isArray(patients)) {
|
|
2394
|
+
patients = [patients];
|
|
2395
|
+
}
|
|
2396
|
+
if (patients.length === 0 || !patients.every((patient) => patient.resourceType === "Patient")) {
|
|
2322
2397
|
throw new Error("Invalid Patient resource");
|
|
2323
2398
|
}
|
|
2324
|
-
this.
|
|
2399
|
+
this.patients = patients;
|
|
2325
2400
|
return this;
|
|
2326
2401
|
}
|
|
2327
2402
|
/**
|
|
@@ -2379,21 +2454,26 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2379
2454
|
if (!bundle.entry) {
|
|
2380
2455
|
return this;
|
|
2381
2456
|
}
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2457
|
+
const patientEntries = [];
|
|
2458
|
+
const resources = [];
|
|
2459
|
+
bundle.entry.forEach((e) => {
|
|
2460
|
+
if (e.resource?.resourceType === "Patient") {
|
|
2461
|
+
patientEntries.push(e.resource);
|
|
2462
|
+
this.resources.add(e.resource);
|
|
2463
|
+
} else if (e.resource) {
|
|
2464
|
+
resources.push(e.resource);
|
|
2465
|
+
}
|
|
2466
|
+
});
|
|
2467
|
+
if (patientEntries.length === 0) {
|
|
2384
2468
|
throw new Error("Patient resource not found in the bundle");
|
|
2385
2469
|
}
|
|
2386
|
-
this.
|
|
2387
|
-
const resources = bundle.entry.map((e) => e.resource);
|
|
2470
|
+
this.patients = patientEntries;
|
|
2388
2471
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
let sectionResources = resources.filter(
|
|
2392
|
-
(r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
|
|
2393
|
-
);
|
|
2394
|
-
if (customFilter) {
|
|
2395
|
-
sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
|
|
2472
|
+
if (sectionType === "Patient" /* PATIENT */) {
|
|
2473
|
+
continue;
|
|
2396
2474
|
}
|
|
2475
|
+
const sectionFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2476
|
+
const sectionResources = resources.filter((resource) => sectionFilter(resource));
|
|
2397
2477
|
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2398
2478
|
}
|
|
2399
2479
|
return this;
|
|
@@ -2404,16 +2484,18 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2404
2484
|
* @param authorOrganizationName - Name of the authoring organization
|
|
2405
2485
|
* @param baseUrl - Base URL for the FHIR server (e.g., 'https://example.com/fhir')
|
|
2406
2486
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2487
|
+
* @param patientId - Optional patient ID to use as primary patient for composition reference
|
|
2407
2488
|
*/
|
|
2408
|
-
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone) {
|
|
2489
|
+
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId) {
|
|
2409
2490
|
if (baseUrl.endsWith("/")) {
|
|
2410
2491
|
baseUrl = baseUrl.slice(0, -1);
|
|
2411
2492
|
}
|
|
2412
|
-
if (!this.
|
|
2493
|
+
if (!this.patients) {
|
|
2413
2494
|
throw new Error("Patient resource must be set before building the bundle");
|
|
2414
2495
|
}
|
|
2496
|
+
const primaryPatientId = patientId ?? this.patients[0].id;
|
|
2415
2497
|
const composition = {
|
|
2416
|
-
id: `Composition-${
|
|
2498
|
+
id: `Composition-${primaryPatientId}`,
|
|
2417
2499
|
resourceType: "Composition",
|
|
2418
2500
|
status: "final",
|
|
2419
2501
|
type: {
|
|
@@ -2424,7 +2506,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2424
2506
|
}]
|
|
2425
2507
|
},
|
|
2426
2508
|
subject: {
|
|
2427
|
-
reference: `Patient/${
|
|
2509
|
+
reference: `Patient/${primaryPatientId}`
|
|
2428
2510
|
},
|
|
2429
2511
|
author: [{
|
|
2430
2512
|
reference: `Organization/${authorOrganizationId}`,
|
|
@@ -2436,7 +2518,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2436
2518
|
section: this.sections,
|
|
2437
2519
|
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2438
2520
|
"Patient" /* PATIENT */,
|
|
2439
|
-
|
|
2521
|
+
this.patients,
|
|
2440
2522
|
timezone,
|
|
2441
2523
|
true
|
|
2442
2524
|
)
|
|
@@ -2455,9 +2537,11 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2455
2537
|
fullUrl: `${baseUrl}/Composition/${composition.id}`,
|
|
2456
2538
|
resource: composition
|
|
2457
2539
|
});
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2540
|
+
this.patients.forEach((patient) => {
|
|
2541
|
+
bundle.entry?.push({
|
|
2542
|
+
fullUrl: `${baseUrl}/Patient/${patient.id}`,
|
|
2543
|
+
resource: patient
|
|
2544
|
+
});
|
|
2461
2545
|
});
|
|
2462
2546
|
this.resources.forEach((resource) => {
|
|
2463
2547
|
if (resource.resourceType !== "Patient") {
|