@imranq2/fhirpatientsummary 1.0.16 → 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 +335 -316
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +335 -316
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -82,33 +82,21 @@ var BLOOD_PRESSURE_LOINC_CODES = {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
// src/structures/ips_section_resource_map.ts
|
|
85
|
-
var IPSSectionResourceMap = {
|
|
86
|
-
["Patient" /* PATIENT */]: ["Patient"],
|
|
87
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
88
|
-
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
89
|
-
// Medication resource is needed for identifying name of medication
|
|
90
|
-
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
91
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
92
|
-
// Immunization can include Organization as a related resource
|
|
93
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
94
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
95
|
-
// Device resource is used for medical devices name
|
|
96
|
-
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
97
|
-
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
98
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
|
|
99
|
-
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
100
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
101
|
-
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
102
|
-
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
103
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
104
|
-
};
|
|
105
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),
|
|
106
92
|
// Only include active conditions
|
|
107
93
|
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
108
94
|
// Only include completed immunizations
|
|
109
95
|
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
110
96
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
111
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),
|
|
112
100
|
// Only include finalized diagnostic reports
|
|
113
101
|
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
114
102
|
// Only include completed procedures
|
|
@@ -124,14 +112,9 @@ var IPSSectionResourceFilters = {
|
|
|
124
112
|
// Only include active care plans
|
|
125
113
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
126
114
|
// Only include active advance directives (Consent resources)
|
|
127
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
128
|
-
// Patient section: only Patient resource
|
|
129
|
-
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
|
|
115
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
130
116
|
};
|
|
131
117
|
var IPSSectionResourceHelper = class {
|
|
132
|
-
static getResourceTypesForSection(section) {
|
|
133
|
-
return IPSSectionResourceMap[section] || [];
|
|
134
|
-
}
|
|
135
118
|
static getResourceFilterForSection(section) {
|
|
136
119
|
return IPSSectionResourceFilters[section];
|
|
137
120
|
}
|
|
@@ -141,11 +124,11 @@ var IPSSectionResourceHelper = class {
|
|
|
141
124
|
import { DateTime } from "luxon";
|
|
142
125
|
var TemplateUtilities = class {
|
|
143
126
|
/**
|
|
144
|
-
* Constructor to initialize the TemplateUtilities with a FHIR
|
|
145
|
-
* @param
|
|
127
|
+
* Constructor to initialize the TemplateUtilities with a FHIR resources
|
|
128
|
+
* @param resources - FHIR resources
|
|
146
129
|
*/
|
|
147
|
-
constructor(
|
|
148
|
-
this.
|
|
130
|
+
constructor(resources) {
|
|
131
|
+
this.resources = resources;
|
|
149
132
|
}
|
|
150
133
|
/**
|
|
151
134
|
* Formats a CodeableConcept object
|
|
@@ -176,7 +159,7 @@ var TemplateUtilities = class {
|
|
|
176
159
|
return "";
|
|
177
160
|
}
|
|
178
161
|
resolveReference(ref) {
|
|
179
|
-
if (!ref || !this.
|
|
162
|
+
if (!ref || !this.resources) {
|
|
180
163
|
return null;
|
|
181
164
|
}
|
|
182
165
|
const referenceParts = ref.reference?.split("/");
|
|
@@ -185,10 +168,10 @@ var TemplateUtilities = class {
|
|
|
185
168
|
}
|
|
186
169
|
const referenceResourceType = referenceParts[0];
|
|
187
170
|
const referenceResourceId = referenceParts[1];
|
|
188
|
-
const resource = this.
|
|
189
|
-
return entry.
|
|
171
|
+
const resource = this.resources.find((entry) => {
|
|
172
|
+
return entry.resourceType === referenceResourceType && entry.id === referenceResourceId;
|
|
190
173
|
});
|
|
191
|
-
return resource ? resource
|
|
174
|
+
return resource ? resource : null;
|
|
192
175
|
}
|
|
193
176
|
/**
|
|
194
177
|
* Renders a Device reference
|
|
@@ -759,7 +742,7 @@ var TemplateUtilities = class {
|
|
|
759
742
|
if (parts.length === 2) {
|
|
760
743
|
const resourceType = parts[0];
|
|
761
744
|
const resourceId = parts[1];
|
|
762
|
-
const resource = this.
|
|
745
|
+
const resource = this.resources?.find((resource2) => resource2.resourceType === resourceType && resource2.id === resourceId);
|
|
763
746
|
if (resource) {
|
|
764
747
|
return `${resourceType}/${resourceId}`;
|
|
765
748
|
}
|
|
@@ -799,48 +782,96 @@ var TemplateUtilities = class {
|
|
|
799
782
|
// src/narratives/templates/typescript/PatientTemplate.ts
|
|
800
783
|
var PatientTemplate = class _PatientTemplate {
|
|
801
784
|
/**
|
|
802
|
-
* Generate HTML narrative for Patient
|
|
803
|
-
* @param
|
|
785
|
+
* Generate HTML narrative for Patient resources
|
|
786
|
+
* @param resources - FHIR Patient resources
|
|
804
787
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
805
788
|
* @returns HTML string for rendering
|
|
806
789
|
*/
|
|
807
|
-
generateNarrative(
|
|
808
|
-
return _PatientTemplate.generateStaticNarrative(
|
|
790
|
+
generateNarrative(resources, timezone) {
|
|
791
|
+
return _PatientTemplate.generateStaticNarrative(resources, timezone);
|
|
809
792
|
}
|
|
810
793
|
/**
|
|
811
794
|
* Internal static implementation that actually generates the narrative
|
|
812
|
-
* @param
|
|
795
|
+
* @param resources - FHIR Patient resources
|
|
813
796
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
814
797
|
* @returns HTML string for rendering
|
|
815
798
|
*/
|
|
816
799
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
817
|
-
static generateStaticNarrative(
|
|
818
|
-
const templateUtilities = new TemplateUtilities(
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
<
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
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);
|
|
837
836
|
}
|
|
838
|
-
|
|
839
|
-
|
|
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;
|
|
840
871
|
}
|
|
841
872
|
/**
|
|
842
873
|
* Renders patient names as HTML list items
|
|
843
|
-
* @param patient - Patient
|
|
874
|
+
* @param patient - Patient resources
|
|
844
875
|
* @returns HTML string of list items
|
|
845
876
|
*/
|
|
846
877
|
static renderNames(patient) {
|
|
@@ -860,7 +891,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
860
891
|
}
|
|
861
892
|
/**
|
|
862
893
|
* Renders patient identifiers as HTML list items
|
|
863
|
-
* @param patient - Patient
|
|
894
|
+
* @param patient - Patient resources
|
|
864
895
|
* @returns HTML string of list items
|
|
865
896
|
*/
|
|
866
897
|
static renderIdentifiers(patient) {
|
|
@@ -875,7 +906,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
875
906
|
}
|
|
876
907
|
/**
|
|
877
908
|
* Renders patient telecom information grouped by system
|
|
878
|
-
* @param patient - Patient
|
|
909
|
+
* @param patient - Patient resources
|
|
879
910
|
* @returns HTML string grouped by system
|
|
880
911
|
*/
|
|
881
912
|
static renderTelecom(patient) {
|
|
@@ -934,7 +965,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
934
965
|
}
|
|
935
966
|
/**
|
|
936
967
|
* Renders patient addresses as HTML list items
|
|
937
|
-
* @param patient - Patient
|
|
968
|
+
* @param patient - Patient resources
|
|
938
969
|
* @returns HTML string of list items
|
|
939
970
|
*/
|
|
940
971
|
static renderAddresses(patient) {
|
|
@@ -956,9 +987,18 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
956
987
|
if (address.city) {
|
|
957
988
|
addressArray.push(address.city);
|
|
958
989
|
}
|
|
990
|
+
if (address.district) {
|
|
991
|
+
addressArray.push(address.district);
|
|
992
|
+
}
|
|
993
|
+
if (address.state) {
|
|
994
|
+
addressArray.push(address.state);
|
|
995
|
+
}
|
|
959
996
|
if (address.country) {
|
|
960
997
|
addressArray.push(address.country);
|
|
961
998
|
}
|
|
999
|
+
if (address.postalCode) {
|
|
1000
|
+
addressArray.push(address.postalCode);
|
|
1001
|
+
}
|
|
962
1002
|
}
|
|
963
1003
|
const addressText = addressArray.join(", ").trim();
|
|
964
1004
|
if (addressText) {
|
|
@@ -969,7 +1009,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
969
1009
|
}
|
|
970
1010
|
/**
|
|
971
1011
|
* Renders patient deceased status
|
|
972
|
-
* @param patient - Patient
|
|
1012
|
+
* @param patient - Patient resources
|
|
973
1013
|
* @returns HTML string for deceased status
|
|
974
1014
|
*/
|
|
975
1015
|
static renderDeceased(patient) {
|
|
@@ -984,7 +1024,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
984
1024
|
/**
|
|
985
1025
|
* Renders patient communication preferences as HTML list items
|
|
986
1026
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
987
|
-
* @param patient - Patient
|
|
1027
|
+
* @param patient - Patient resources
|
|
988
1028
|
* @returns HTML string of list items
|
|
989
1029
|
*/
|
|
990
1030
|
static renderCommunication(templateUtilities, patient) {
|
|
@@ -1019,32 +1059,30 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
1019
1059
|
var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
1020
1060
|
/**
|
|
1021
1061
|
* Generate HTML narrative for AllergyIntolerance resources
|
|
1022
|
-
* @param
|
|
1062
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
1023
1063
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1024
1064
|
* @returns HTML string for rendering
|
|
1025
1065
|
*/
|
|
1026
|
-
generateNarrative(
|
|
1027
|
-
return _AllergyIntoleranceTemplate.generateStaticNarrative(
|
|
1066
|
+
generateNarrative(resources, timezone) {
|
|
1067
|
+
return _AllergyIntoleranceTemplate.generateStaticNarrative(resources, timezone);
|
|
1028
1068
|
}
|
|
1029
1069
|
/**
|
|
1030
1070
|
* Internal static implementation that actually generates the narrative
|
|
1031
|
-
* @param
|
|
1071
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
1032
1072
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1033
1073
|
* @returns HTML string for rendering
|
|
1034
1074
|
*/
|
|
1035
|
-
static generateStaticNarrative(
|
|
1036
|
-
const templateUtilities = new TemplateUtilities(
|
|
1075
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1076
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1037
1077
|
const activeAllergies = [];
|
|
1038
1078
|
const resolvedAllergies = [];
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
activeAllergies.push(allergy);
|
|
1047
|
-
}
|
|
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);
|
|
1048
1086
|
}
|
|
1049
1087
|
}
|
|
1050
1088
|
activeAllergies.sort((a, b) => {
|
|
@@ -1157,12 +1195,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1157
1195
|
var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
1158
1196
|
/**
|
|
1159
1197
|
* Generate HTML narrative for Medication resources
|
|
1160
|
-
* @param
|
|
1198
|
+
* @param resources - FHIR Medication resources
|
|
1161
1199
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1162
1200
|
* @returns HTML string for rendering
|
|
1163
1201
|
*/
|
|
1164
|
-
generateNarrative(
|
|
1165
|
-
return _MedicationSummaryTemplate.generateStaticNarrative(
|
|
1202
|
+
generateNarrative(resources, timezone) {
|
|
1203
|
+
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
|
|
1166
1204
|
}
|
|
1167
1205
|
/**
|
|
1168
1206
|
* Safely parse a date string and return a valid Date object or null
|
|
@@ -1227,16 +1265,16 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1227
1265
|
}
|
|
1228
1266
|
/**
|
|
1229
1267
|
* Internal static implementation that actually generates the narrative
|
|
1230
|
-
* @param
|
|
1268
|
+
* @param resources - FHIR Medication resources
|
|
1231
1269
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1232
1270
|
* @returns HTML string for rendering
|
|
1233
1271
|
*/
|
|
1234
1272
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1235
|
-
static generateStaticNarrative(
|
|
1236
|
-
const templateUtilities = new TemplateUtilities(
|
|
1273
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1274
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1237
1275
|
let html = "";
|
|
1238
|
-
const medicationRequests = this.getMedicationRequests(templateUtilities,
|
|
1239
|
-
const medicationStatements = this.getMedicationStatements(templateUtilities,
|
|
1276
|
+
const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
|
|
1277
|
+
const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
|
|
1240
1278
|
const allActiveMedications = [];
|
|
1241
1279
|
const allInactiveMedications = [];
|
|
1242
1280
|
medicationRequests.forEach((mr) => {
|
|
@@ -1290,33 +1328,33 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1290
1328
|
return html;
|
|
1291
1329
|
}
|
|
1292
1330
|
/**
|
|
1293
|
-
* Extract MedicationRequest resources
|
|
1331
|
+
* Extract MedicationRequest resources
|
|
1294
1332
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1295
|
-
* @param
|
|
1333
|
+
* @param resources - FHIR Medication resources
|
|
1296
1334
|
* @returns Array of MedicationRequest resources
|
|
1297
1335
|
*/
|
|
1298
|
-
static getMedicationRequests(templateUtilities,
|
|
1299
|
-
if (
|
|
1336
|
+
static getMedicationRequests(templateUtilities, resources) {
|
|
1337
|
+
if (resources.length === 0) {
|
|
1300
1338
|
return [];
|
|
1301
1339
|
}
|
|
1302
|
-
return
|
|
1303
|
-
resource: entry
|
|
1304
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1340
|
+
return resources.filter((entry) => entry.resourceType === "MedicationRequest").map((entry) => ({
|
|
1341
|
+
resource: entry,
|
|
1342
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1305
1343
|
}));
|
|
1306
1344
|
}
|
|
1307
1345
|
/**
|
|
1308
|
-
* Extract MedicationStatement resources
|
|
1346
|
+
* Extract MedicationStatement resources
|
|
1309
1347
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1310
|
-
* @param
|
|
1348
|
+
* @param resources - FHIR Medication resources
|
|
1311
1349
|
* @returns Array of MedicationStatement resources
|
|
1312
1350
|
*/
|
|
1313
|
-
static getMedicationStatements(templateUtilities,
|
|
1314
|
-
if (
|
|
1351
|
+
static getMedicationStatements(templateUtilities, resources) {
|
|
1352
|
+
if (resources.length === 0) {
|
|
1315
1353
|
return [];
|
|
1316
1354
|
}
|
|
1317
|
-
return
|
|
1318
|
-
resource: entry
|
|
1319
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1355
|
+
return resources.filter((entry) => entry.resourceType === "MedicationStatement").map((entry) => ({
|
|
1356
|
+
resource: entry,
|
|
1357
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1320
1358
|
}));
|
|
1321
1359
|
}
|
|
1322
1360
|
/**
|
|
@@ -1412,28 +1450,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1412
1450
|
var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
1413
1451
|
/**
|
|
1414
1452
|
* Generate HTML narrative for Immunization resources
|
|
1415
|
-
* @param
|
|
1453
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1416
1454
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1417
1455
|
* @returns HTML string for rendering
|
|
1418
1456
|
*/
|
|
1419
|
-
generateNarrative(
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
}
|
|
1427
|
-
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);
|
|
1428
1464
|
}
|
|
1429
1465
|
/**
|
|
1430
1466
|
* Internal static implementation that actually generates the narrative
|
|
1431
|
-
* @param
|
|
1467
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1432
1468
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1433
1469
|
* @returns HTML string for rendering
|
|
1434
1470
|
*/
|
|
1435
|
-
static generateStaticNarrative(
|
|
1436
|
-
const templateUtilities = new TemplateUtilities(
|
|
1471
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1472
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1437
1473
|
let html = `
|
|
1438
1474
|
<table>
|
|
1439
1475
|
<thead>
|
|
@@ -1448,21 +1484,20 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1448
1484
|
</tr>
|
|
1449
1485
|
</thead>
|
|
1450
1486
|
<tbody>`;
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
}
|
|
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>`;
|
|
1466
1501
|
}
|
|
1467
1502
|
}
|
|
1468
1503
|
html += `
|
|
@@ -1476,23 +1511,23 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1476
1511
|
var ProblemListTemplate = class _ProblemListTemplate {
|
|
1477
1512
|
/**
|
|
1478
1513
|
* Generate HTML narrative for Problem List
|
|
1479
|
-
* @param
|
|
1514
|
+
* @param resources - FHIR Condition resources
|
|
1480
1515
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1481
1516
|
* @returns HTML string for rendering
|
|
1482
1517
|
*/
|
|
1483
|
-
generateNarrative(
|
|
1484
|
-
return _ProblemListTemplate.generateStaticNarrative(
|
|
1518
|
+
generateNarrative(resources, timezone) {
|
|
1519
|
+
return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
|
|
1485
1520
|
}
|
|
1486
1521
|
/**
|
|
1487
1522
|
* Internal static implementation that actually generates the narrative
|
|
1488
|
-
* @param
|
|
1523
|
+
* @param resources - FHIR Condition resources
|
|
1489
1524
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1490
1525
|
* @returns HTML string for rendering
|
|
1491
1526
|
*/
|
|
1492
|
-
static generateStaticNarrative(
|
|
1493
|
-
const templateUtilities = new TemplateUtilities(
|
|
1527
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1528
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1494
1529
|
let html = ``;
|
|
1495
|
-
const activeConditions =
|
|
1530
|
+
const activeConditions = resources.map((entry) => entry) || [];
|
|
1496
1531
|
activeConditions.sort((a, b) => {
|
|
1497
1532
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1498
1533
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1527,22 +1562,22 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1527
1562
|
var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
1528
1563
|
/**
|
|
1529
1564
|
* Generate HTML narrative for Vital Signs
|
|
1530
|
-
* @param
|
|
1565
|
+
* @param resources - FHIR Observation resources
|
|
1531
1566
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1532
1567
|
* @returns HTML string for rendering
|
|
1533
1568
|
*/
|
|
1534
|
-
generateNarrative(
|
|
1535
|
-
return _VitalSignsTemplate.generateStaticNarrative(
|
|
1569
|
+
generateNarrative(resources, timezone) {
|
|
1570
|
+
return _VitalSignsTemplate.generateStaticNarrative(resources, timezone);
|
|
1536
1571
|
}
|
|
1537
1572
|
/**
|
|
1538
1573
|
* Internal static implementation that actually generates the narrative
|
|
1539
|
-
* @param
|
|
1574
|
+
* @param resources - FHIR Observation resources
|
|
1540
1575
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1541
1576
|
* @returns HTML string for rendering
|
|
1542
1577
|
*/
|
|
1543
|
-
static generateStaticNarrative(
|
|
1544
|
-
const templateUtilities = new TemplateUtilities(
|
|
1545
|
-
const observations =
|
|
1578
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1579
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1580
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1546
1581
|
observations.sort((a, b) => {
|
|
1547
1582
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1548
1583
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1585,28 +1620,21 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1585
1620
|
var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
1586
1621
|
/**
|
|
1587
1622
|
* Generate HTML narrative for Medical Device resources
|
|
1588
|
-
* @param
|
|
1623
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1589
1624
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1590
1625
|
* @returns HTML string for rendering
|
|
1591
1626
|
*/
|
|
1592
|
-
generateNarrative(
|
|
1593
|
-
|
|
1594
|
-
resource.entry.sort((a, b) => {
|
|
1595
|
-
const dateA = a.resource?.recordedOn;
|
|
1596
|
-
const dateB = b.resource?.recordedOn;
|
|
1597
|
-
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1627
|
+
generateNarrative(resources, timezone) {
|
|
1628
|
+
return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
|
|
1601
1629
|
}
|
|
1602
1630
|
/**
|
|
1603
1631
|
* Internal static implementation that actually generates the narrative
|
|
1604
|
-
* @param
|
|
1632
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1605
1633
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1606
1634
|
* @returns HTML string for rendering
|
|
1607
1635
|
*/
|
|
1608
|
-
static generateStaticNarrative(
|
|
1609
|
-
const templateUtilities = new TemplateUtilities(
|
|
1636
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1637
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1610
1638
|
let html = `
|
|
1611
1639
|
<table>
|
|
1612
1640
|
<thead>
|
|
@@ -1618,19 +1646,19 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1618
1646
|
</tr>
|
|
1619
1647
|
</thead>
|
|
1620
1648
|
<tbody>`;
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
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>`;
|
|
1634
1662
|
}
|
|
1635
1663
|
html += `
|
|
1636
1664
|
</tbody>
|
|
@@ -1643,23 +1671,23 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1643
1671
|
var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
1644
1672
|
/**
|
|
1645
1673
|
* Generate HTML narrative for Diagnostic Results
|
|
1646
|
-
* @param
|
|
1674
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1647
1675
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1648
1676
|
* @returns HTML string for rendering
|
|
1649
1677
|
*/
|
|
1650
|
-
generateNarrative(
|
|
1651
|
-
return _DiagnosticResultsTemplate.generateStaticNarrative(
|
|
1678
|
+
generateNarrative(resources, timezone) {
|
|
1679
|
+
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
|
|
1652
1680
|
}
|
|
1653
1681
|
/**
|
|
1654
1682
|
* Internal static implementation that actually generates the narrative
|
|
1655
|
-
* @param
|
|
1683
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1656
1684
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1657
1685
|
* @returns HTML string for rendering
|
|
1658
1686
|
*/
|
|
1659
|
-
static generateStaticNarrative(
|
|
1660
|
-
const templateUtilities = new TemplateUtilities(
|
|
1687
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1688
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1661
1689
|
let html = "";
|
|
1662
|
-
const observations = this.getObservations(
|
|
1690
|
+
const observations = this.getObservations(resources);
|
|
1663
1691
|
if (observations.length > 0) {
|
|
1664
1692
|
observations.sort((a, b) => {
|
|
1665
1693
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
@@ -1668,7 +1696,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1668
1696
|
});
|
|
1669
1697
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1670
1698
|
}
|
|
1671
|
-
const diagnosticReports = this.getDiagnosticReports(
|
|
1699
|
+
const diagnosticReports = this.getDiagnosticReports(resources);
|
|
1672
1700
|
if (diagnosticReports.length > 0) {
|
|
1673
1701
|
diagnosticReports.sort((a, b) => {
|
|
1674
1702
|
const dateA = a.issued;
|
|
@@ -1680,26 +1708,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1680
1708
|
return html;
|
|
1681
1709
|
}
|
|
1682
1710
|
/**
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
static getObservations(
|
|
1688
|
-
|
|
1689
|
-
return [];
|
|
1690
|
-
}
|
|
1691
|
-
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);
|
|
1692
1717
|
}
|
|
1693
1718
|
/**
|
|
1694
|
-
*
|
|
1695
|
-
* @param
|
|
1719
|
+
* Get all DiagnosticReport resources from the resource array
|
|
1720
|
+
* @param resources - FHIR resources array
|
|
1696
1721
|
* @returns Array of DiagnosticReport resources
|
|
1697
1722
|
*/
|
|
1698
|
-
static getDiagnosticReports(
|
|
1699
|
-
|
|
1700
|
-
return [];
|
|
1701
|
-
}
|
|
1702
|
-
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);
|
|
1703
1725
|
}
|
|
1704
1726
|
/**
|
|
1705
1727
|
* Render HTML table for Observation resources
|
|
@@ -1785,26 +1807,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1785
1807
|
var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
1786
1808
|
/**
|
|
1787
1809
|
* Generate HTML narrative for Procedure resources
|
|
1788
|
-
* @param
|
|
1810
|
+
* @param resources - FHIR Procedure resources
|
|
1789
1811
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1790
1812
|
* @returns HTML string for rendering
|
|
1791
1813
|
*/
|
|
1792
|
-
generateNarrative(
|
|
1793
|
-
|
|
1794
|
-
const dateA = a.
|
|
1795
|
-
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;
|
|
1796
1818
|
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1797
1819
|
});
|
|
1798
|
-
return _HistoryOfProceduresTemplate.generateStaticNarrative(
|
|
1820
|
+
return _HistoryOfProceduresTemplate.generateStaticNarrative(resources, timezone);
|
|
1799
1821
|
}
|
|
1800
1822
|
/**
|
|
1801
1823
|
* Internal static implementation that actually generates the narrative
|
|
1802
|
-
* @param
|
|
1824
|
+
* @param resources - FHIR Procedure resources
|
|
1803
1825
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1804
1826
|
* @returns HTML string for rendering
|
|
1805
1827
|
*/
|
|
1806
|
-
static generateStaticNarrative(
|
|
1807
|
-
const templateUtilities = new TemplateUtilities(
|
|
1828
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1829
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1808
1830
|
let html = `
|
|
1809
1831
|
<table>
|
|
1810
1832
|
<thead>
|
|
@@ -1815,16 +1837,14 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1815
1837
|
</tr>
|
|
1816
1838
|
</thead>
|
|
1817
1839
|
<tbody>`;
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
<
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
</tr>`;
|
|
1827
|
-
}
|
|
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>`;
|
|
1828
1848
|
}
|
|
1829
1849
|
html += `
|
|
1830
1850
|
</tbody>
|
|
@@ -1837,22 +1857,22 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1837
1857
|
var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
1838
1858
|
/**
|
|
1839
1859
|
* Generate HTML narrative for Social History
|
|
1840
|
-
* @param
|
|
1860
|
+
* @param resources - FHIR Observation resources
|
|
1841
1861
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1842
1862
|
* @returns HTML string for rendering
|
|
1843
1863
|
*/
|
|
1844
|
-
generateNarrative(
|
|
1845
|
-
return _SocialHistoryTemplate.generateStaticNarrative(
|
|
1864
|
+
generateNarrative(resources, timezone) {
|
|
1865
|
+
return _SocialHistoryTemplate.generateStaticNarrative(resources, timezone);
|
|
1846
1866
|
}
|
|
1847
1867
|
/**
|
|
1848
1868
|
* Internal static implementation that actually generates the narrative
|
|
1849
|
-
* @param
|
|
1869
|
+
* @param resources - FHIR Observation resources
|
|
1850
1870
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1851
1871
|
* @returns HTML string for rendering
|
|
1852
1872
|
*/
|
|
1853
|
-
static generateStaticNarrative(
|
|
1854
|
-
const templateUtilities = new TemplateUtilities(
|
|
1855
|
-
const observations =
|
|
1873
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1874
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1875
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1856
1876
|
observations.sort((a, b) => {
|
|
1857
1877
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1858
1878
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1891,14 +1911,14 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1891
1911
|
var PastHistoryOfIllnessTemplate = class {
|
|
1892
1912
|
/**
|
|
1893
1913
|
* Generate HTML narrative for Past History of Illnesses
|
|
1894
|
-
* @param
|
|
1914
|
+
* @param resources - FHIR Condition resources
|
|
1895
1915
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1896
1916
|
* @returns HTML string for rendering
|
|
1897
1917
|
*/
|
|
1898
|
-
generateNarrative(
|
|
1899
|
-
const templateUtilities = new TemplateUtilities(
|
|
1918
|
+
generateNarrative(resources, timezone) {
|
|
1919
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1900
1920
|
let html = ``;
|
|
1901
|
-
const resolvedConditions =
|
|
1921
|
+
const resolvedConditions = resources.map((entry) => entry) || [];
|
|
1902
1922
|
resolvedConditions.sort((a, b) => {
|
|
1903
1923
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1904
1924
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1935,13 +1955,13 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1935
1955
|
var PlanOfCareTemplate = class {
|
|
1936
1956
|
/**
|
|
1937
1957
|
* Generate HTML narrative for Plan of Care
|
|
1938
|
-
* @param
|
|
1958
|
+
* @param resources - FHIR CarePlan resources
|
|
1939
1959
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1940
1960
|
* @returns HTML string for rendering
|
|
1941
1961
|
*/
|
|
1942
|
-
generateNarrative(
|
|
1943
|
-
const templateUtilities = new TemplateUtilities(
|
|
1944
|
-
const carePlans =
|
|
1962
|
+
generateNarrative(resources, timezone) {
|
|
1963
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1964
|
+
const carePlans = resources.map((entry) => entry) || [];
|
|
1945
1965
|
carePlans.sort((a, b) => {
|
|
1946
1966
|
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1947
1967
|
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
@@ -1980,37 +2000,35 @@ var PlanOfCareTemplate = class {
|
|
|
1980
2000
|
var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
1981
2001
|
/**
|
|
1982
2002
|
* Generate HTML narrative for Functional Status
|
|
1983
|
-
* @param
|
|
2003
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
1984
2004
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1985
2005
|
* @returns HTML string for rendering
|
|
1986
2006
|
*/
|
|
1987
|
-
generateNarrative(
|
|
1988
|
-
return _FunctionalStatusTemplate.generateStaticNarrative(
|
|
2007
|
+
generateNarrative(resources, timezone) {
|
|
2008
|
+
return _FunctionalStatusTemplate.generateStaticNarrative(resources, timezone);
|
|
1989
2009
|
}
|
|
1990
2010
|
/**
|
|
1991
2011
|
* Internal static implementation that actually generates the narrative
|
|
1992
|
-
* @param
|
|
2012
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
1993
2013
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1994
2014
|
* @returns HTML string for rendering
|
|
1995
2015
|
*/
|
|
1996
|
-
static generateStaticNarrative(
|
|
1997
|
-
const templateUtilities = new TemplateUtilities(
|
|
2016
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2017
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1998
2018
|
let html = ``;
|
|
1999
2019
|
const activeConditions = [];
|
|
2000
2020
|
const clinicalImpressions = [];
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
activeConditions.push(cond);
|
|
2010
|
-
}
|
|
2011
|
-
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
2012
|
-
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);
|
|
2013
2029
|
}
|
|
2030
|
+
} else if (resourceItem.resourceType === "ClinicalImpression") {
|
|
2031
|
+
clinicalImpressions.push(resourceItem);
|
|
2014
2032
|
}
|
|
2015
2033
|
}
|
|
2016
2034
|
activeConditions.sort((a, b) => {
|
|
@@ -2107,22 +2125,22 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
2107
2125
|
var PregnancyTemplate = class _PregnancyTemplate {
|
|
2108
2126
|
/**
|
|
2109
2127
|
* Generate HTML narrative for Pregnancy
|
|
2110
|
-
* @param
|
|
2128
|
+
* @param resources - FHIR Observation resources
|
|
2111
2129
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2112
2130
|
* @returns HTML string for rendering
|
|
2113
2131
|
*/
|
|
2114
|
-
generateNarrative(
|
|
2115
|
-
return _PregnancyTemplate.generateStaticNarrative(
|
|
2132
|
+
generateNarrative(resources, timezone) {
|
|
2133
|
+
return _PregnancyTemplate.generateStaticNarrative(resources, timezone);
|
|
2116
2134
|
}
|
|
2117
2135
|
/**
|
|
2118
2136
|
* Internal static implementation that actually generates the narrative
|
|
2119
|
-
* @param
|
|
2137
|
+
* @param resources - FHIR Observation resources
|
|
2120
2138
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2121
2139
|
* @returns HTML string for rendering
|
|
2122
2140
|
*/
|
|
2123
|
-
static generateStaticNarrative(
|
|
2124
|
-
const templateUtilities = new TemplateUtilities(
|
|
2125
|
-
const observations =
|
|
2141
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2142
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2143
|
+
const observations = resources.map((entry) => entry) || [];
|
|
2126
2144
|
observations.sort((a, b) => {
|
|
2127
2145
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2128
2146
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -2138,8 +2156,8 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2138
2156
|
</tr>
|
|
2139
2157
|
</thead>
|
|
2140
2158
|
<tbody>`;
|
|
2141
|
-
for (const
|
|
2142
|
-
const obs =
|
|
2159
|
+
for (const resource of observations) {
|
|
2160
|
+
const obs = resource;
|
|
2143
2161
|
html += `
|
|
2144
2162
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2145
2163
|
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
@@ -2158,29 +2176,27 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2158
2176
|
var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
2159
2177
|
/**
|
|
2160
2178
|
* Generate HTML narrative for Advance Directives
|
|
2161
|
-
* @param
|
|
2179
|
+
* @param resources - FHIR Consent resources
|
|
2162
2180
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2163
2181
|
* @returns HTML string for rendering
|
|
2164
2182
|
*/
|
|
2165
|
-
generateNarrative(
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
}
|
|
2173
|
-
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);
|
|
2174
2190
|
}
|
|
2175
2191
|
/**
|
|
2176
2192
|
* Internal static implementation that actually generates the narrative
|
|
2177
|
-
* @param
|
|
2193
|
+
* @param resources - FHIR Consent resources
|
|
2178
2194
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2179
2195
|
* @returns HTML string for rendering
|
|
2180
2196
|
*/
|
|
2181
2197
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2182
|
-
static generateStaticNarrative(
|
|
2183
|
-
const templateUtilities = new TemplateUtilities(
|
|
2198
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2199
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2184
2200
|
let html = `
|
|
2185
2201
|
<table>
|
|
2186
2202
|
<thead>
|
|
@@ -2192,17 +2208,15 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
2192
2208
|
</tr>
|
|
2193
2209
|
</thead>
|
|
2194
2210
|
<tbody>`;
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
<
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
</tr>`;
|
|
2205
|
-
}
|
|
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>`;
|
|
2206
2220
|
}
|
|
2207
2221
|
html += `
|
|
2208
2222
|
</tbody>
|
|
@@ -2216,16 +2230,16 @@ var TypeScriptTemplateMapper = class {
|
|
|
2216
2230
|
/**
|
|
2217
2231
|
* Generates HTML narrative for a specific IPS section
|
|
2218
2232
|
* @param section - The IPS section
|
|
2219
|
-
* @param
|
|
2233
|
+
* @param resources - FHIR resources
|
|
2220
2234
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2221
2235
|
* @returns HTML string for rendering
|
|
2222
2236
|
*/
|
|
2223
|
-
static generateNarrative(section,
|
|
2237
|
+
static generateNarrative(section, resources, timezone) {
|
|
2224
2238
|
const templateClass = this.sectionToTemplate[section];
|
|
2225
2239
|
if (!templateClass) {
|
|
2226
2240
|
throw new Error(`No template found for section: ${section}`);
|
|
2227
2241
|
}
|
|
2228
|
-
return templateClass.generateNarrative(
|
|
2242
|
+
return templateClass.generateNarrative(resources, timezone);
|
|
2229
2243
|
}
|
|
2230
2244
|
};
|
|
2231
2245
|
// Map of section types to their template classes
|
|
@@ -2288,14 +2302,7 @@ var NarrativeGenerator = class {
|
|
|
2288
2302
|
return void 0;
|
|
2289
2303
|
}
|
|
2290
2304
|
try {
|
|
2291
|
-
const
|
|
2292
|
-
resourceType: "Bundle",
|
|
2293
|
-
type: "collection",
|
|
2294
|
-
entry: resources.map((resource) => ({
|
|
2295
|
-
resource
|
|
2296
|
-
}))
|
|
2297
|
-
};
|
|
2298
|
-
const content = TypeScriptTemplateMapper.generateNarrative(section, bundle, timezone);
|
|
2305
|
+
const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone);
|
|
2299
2306
|
if (!content) {
|
|
2300
2307
|
return void 0;
|
|
2301
2308
|
}
|
|
@@ -2380,13 +2387,16 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2380
2387
|
/**
|
|
2381
2388
|
* sets the patient resource for the IPS Composition.
|
|
2382
2389
|
* This is not needed if you are calling read_bundle, but can be used to set the patient resource directly.
|
|
2383
|
-
* @param
|
|
2390
|
+
* @param patients - FHIR Patient resource to set
|
|
2384
2391
|
*/
|
|
2385
|
-
setPatient(
|
|
2386
|
-
if (!
|
|
2392
|
+
setPatient(patients) {
|
|
2393
|
+
if (!Array.isArray(patients)) {
|
|
2394
|
+
patients = [patients];
|
|
2395
|
+
}
|
|
2396
|
+
if (patients.length === 0 || !patients.every((patient) => patient.resourceType === "Patient")) {
|
|
2387
2397
|
throw new Error("Invalid Patient resource");
|
|
2388
2398
|
}
|
|
2389
|
-
this.
|
|
2399
|
+
this.patients = patients;
|
|
2390
2400
|
return this;
|
|
2391
2401
|
}
|
|
2392
2402
|
/**
|
|
@@ -2444,21 +2454,26 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2444
2454
|
if (!bundle.entry) {
|
|
2445
2455
|
return this;
|
|
2446
2456
|
}
|
|
2447
|
-
const
|
|
2448
|
-
|
|
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) {
|
|
2449
2468
|
throw new Error("Patient resource not found in the bundle");
|
|
2450
2469
|
}
|
|
2451
|
-
this.
|
|
2452
|
-
const resources = bundle.entry.map((e) => e.resource);
|
|
2470
|
+
this.patients = patientEntries;
|
|
2453
2471
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
let sectionResources = resources.filter(
|
|
2457
|
-
(r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
|
|
2458
|
-
);
|
|
2459
|
-
if (customFilter) {
|
|
2460
|
-
sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
|
|
2472
|
+
if (sectionType === "Patient" /* PATIENT */) {
|
|
2473
|
+
continue;
|
|
2461
2474
|
}
|
|
2475
|
+
const sectionFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2476
|
+
const sectionResources = resources.filter((resource) => sectionFilter(resource));
|
|
2462
2477
|
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2463
2478
|
}
|
|
2464
2479
|
return this;
|
|
@@ -2469,16 +2484,18 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2469
2484
|
* @param authorOrganizationName - Name of the authoring organization
|
|
2470
2485
|
* @param baseUrl - Base URL for the FHIR server (e.g., 'https://example.com/fhir')
|
|
2471
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
|
|
2472
2488
|
*/
|
|
2473
|
-
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone) {
|
|
2489
|
+
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId) {
|
|
2474
2490
|
if (baseUrl.endsWith("/")) {
|
|
2475
2491
|
baseUrl = baseUrl.slice(0, -1);
|
|
2476
2492
|
}
|
|
2477
|
-
if (!this.
|
|
2493
|
+
if (!this.patients) {
|
|
2478
2494
|
throw new Error("Patient resource must be set before building the bundle");
|
|
2479
2495
|
}
|
|
2496
|
+
const primaryPatientId = patientId ?? this.patients[0].id;
|
|
2480
2497
|
const composition = {
|
|
2481
|
-
id: `Composition-${
|
|
2498
|
+
id: `Composition-${primaryPatientId}`,
|
|
2482
2499
|
resourceType: "Composition",
|
|
2483
2500
|
status: "final",
|
|
2484
2501
|
type: {
|
|
@@ -2489,7 +2506,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2489
2506
|
}]
|
|
2490
2507
|
},
|
|
2491
2508
|
subject: {
|
|
2492
|
-
reference: `Patient/${
|
|
2509
|
+
reference: `Patient/${primaryPatientId}`
|
|
2493
2510
|
},
|
|
2494
2511
|
author: [{
|
|
2495
2512
|
reference: `Organization/${authorOrganizationId}`,
|
|
@@ -2501,7 +2518,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2501
2518
|
section: this.sections,
|
|
2502
2519
|
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2503
2520
|
"Patient" /* PATIENT */,
|
|
2504
|
-
|
|
2521
|
+
this.patients,
|
|
2505
2522
|
timezone,
|
|
2506
2523
|
true
|
|
2507
2524
|
)
|
|
@@ -2520,9 +2537,11 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2520
2537
|
fullUrl: `${baseUrl}/Composition/${composition.id}`,
|
|
2521
2538
|
resource: composition
|
|
2522
2539
|
});
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2540
|
+
this.patients.forEach((patient) => {
|
|
2541
|
+
bundle.entry?.push({
|
|
2542
|
+
fullUrl: `${baseUrl}/Patient/${patient.id}`,
|
|
2543
|
+
resource: patient
|
|
2544
|
+
});
|
|
2526
2545
|
});
|
|
2527
2546
|
this.resources.forEach((resource) => {
|
|
2528
2547
|
if (resource.resourceType !== "Patient") {
|