@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.cjs
CHANGED
|
@@ -110,33 +110,21 @@ var BLOOD_PRESSURE_LOINC_CODES = {
|
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
// src/structures/ips_section_resource_map.ts
|
|
113
|
-
var IPSSectionResourceMap = {
|
|
114
|
-
["Patient" /* PATIENT */]: ["Patient"],
|
|
115
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
116
|
-
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
117
|
-
// Medication resource is needed for identifying name of medication
|
|
118
|
-
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
119
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
120
|
-
// Immunization can include Organization as a related resource
|
|
121
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
122
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
123
|
-
// Device resource is used for medical devices name
|
|
124
|
-
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
125
|
-
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
126
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
|
|
127
|
-
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
128
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
129
|
-
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
130
|
-
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
131
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
132
|
-
};
|
|
133
113
|
var IPSSectionResourceFilters = {
|
|
114
|
+
// Patient section: only Patient resource
|
|
115
|
+
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient",
|
|
116
|
+
// Only include allergies
|
|
117
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance",
|
|
118
|
+
// includes MedicationRequest, MedicationStatement. Medication is needed for medication names
|
|
119
|
+
["MedicationSummarySection" /* MEDICATIONS */]: (resource) => ["MedicationRequest", "MedicationStatement", "Medication"].includes(resource.resourceType),
|
|
134
120
|
// Only include active conditions
|
|
135
121
|
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
136
122
|
// Only include completed immunizations
|
|
137
123
|
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
138
124
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
139
125
|
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "vital-signs")),
|
|
126
|
+
// Includes DeviceUseStatement. Device is needed for linked device details
|
|
127
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
|
|
140
128
|
// Only include finalized diagnostic reports
|
|
141
129
|
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
142
130
|
// Only include completed procedures
|
|
@@ -152,14 +140,9 @@ var IPSSectionResourceFilters = {
|
|
|
152
140
|
// Only include active care plans
|
|
153
141
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
154
142
|
// Only include active advance directives (Consent resources)
|
|
155
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
156
|
-
// Patient section: only Patient resource
|
|
157
|
-
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
|
|
143
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active"
|
|
158
144
|
};
|
|
159
145
|
var IPSSectionResourceHelper = class {
|
|
160
|
-
static getResourceTypesForSection(section) {
|
|
161
|
-
return IPSSectionResourceMap[section] || [];
|
|
162
|
-
}
|
|
163
146
|
static getResourceFilterForSection(section) {
|
|
164
147
|
return IPSSectionResourceFilters[section];
|
|
165
148
|
}
|
|
@@ -169,11 +152,11 @@ var IPSSectionResourceHelper = class {
|
|
|
169
152
|
var import_luxon = require("luxon");
|
|
170
153
|
var TemplateUtilities = class {
|
|
171
154
|
/**
|
|
172
|
-
* Constructor to initialize the TemplateUtilities with a FHIR
|
|
173
|
-
* @param
|
|
155
|
+
* Constructor to initialize the TemplateUtilities with a FHIR resources
|
|
156
|
+
* @param resources - FHIR resources
|
|
174
157
|
*/
|
|
175
|
-
constructor(
|
|
176
|
-
this.
|
|
158
|
+
constructor(resources) {
|
|
159
|
+
this.resources = resources;
|
|
177
160
|
}
|
|
178
161
|
/**
|
|
179
162
|
* Formats a CodeableConcept object
|
|
@@ -204,7 +187,7 @@ var TemplateUtilities = class {
|
|
|
204
187
|
return "";
|
|
205
188
|
}
|
|
206
189
|
resolveReference(ref) {
|
|
207
|
-
if (!ref || !this.
|
|
190
|
+
if (!ref || !this.resources) {
|
|
208
191
|
return null;
|
|
209
192
|
}
|
|
210
193
|
const referenceParts = ref.reference?.split("/");
|
|
@@ -213,10 +196,10 @@ var TemplateUtilities = class {
|
|
|
213
196
|
}
|
|
214
197
|
const referenceResourceType = referenceParts[0];
|
|
215
198
|
const referenceResourceId = referenceParts[1];
|
|
216
|
-
const resource = this.
|
|
217
|
-
return entry.
|
|
199
|
+
const resource = this.resources.find((entry) => {
|
|
200
|
+
return entry.resourceType === referenceResourceType && entry.id === referenceResourceId;
|
|
218
201
|
});
|
|
219
|
-
return resource ? resource
|
|
202
|
+
return resource ? resource : null;
|
|
220
203
|
}
|
|
221
204
|
/**
|
|
222
205
|
* Renders a Device reference
|
|
@@ -787,7 +770,7 @@ var TemplateUtilities = class {
|
|
|
787
770
|
if (parts.length === 2) {
|
|
788
771
|
const resourceType = parts[0];
|
|
789
772
|
const resourceId = parts[1];
|
|
790
|
-
const resource = this.
|
|
773
|
+
const resource = this.resources?.find((resource2) => resource2.resourceType === resourceType && resource2.id === resourceId);
|
|
791
774
|
if (resource) {
|
|
792
775
|
return `${resourceType}/${resourceId}`;
|
|
793
776
|
}
|
|
@@ -827,48 +810,96 @@ var TemplateUtilities = class {
|
|
|
827
810
|
// src/narratives/templates/typescript/PatientTemplate.ts
|
|
828
811
|
var PatientTemplate = class _PatientTemplate {
|
|
829
812
|
/**
|
|
830
|
-
* Generate HTML narrative for Patient
|
|
831
|
-
* @param
|
|
813
|
+
* Generate HTML narrative for Patient resources
|
|
814
|
+
* @param resources - FHIR Patient resources
|
|
832
815
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
833
816
|
* @returns HTML string for rendering
|
|
834
817
|
*/
|
|
835
|
-
generateNarrative(
|
|
836
|
-
return _PatientTemplate.generateStaticNarrative(
|
|
818
|
+
generateNarrative(resources, timezone) {
|
|
819
|
+
return _PatientTemplate.generateStaticNarrative(resources, timezone);
|
|
837
820
|
}
|
|
838
821
|
/**
|
|
839
822
|
* Internal static implementation that actually generates the narrative
|
|
840
|
-
* @param
|
|
823
|
+
* @param resources - FHIR Patient resources
|
|
841
824
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
842
825
|
* @returns HTML string for rendering
|
|
843
826
|
*/
|
|
844
827
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
845
|
-
static generateStaticNarrative(
|
|
846
|
-
const templateUtilities = new TemplateUtilities(
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
<
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
828
|
+
static generateStaticNarrative(resources, timezone) {
|
|
829
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
830
|
+
const combinedPatient = this.combinePatients(resources);
|
|
831
|
+
return `<div>
|
|
832
|
+
<ul>
|
|
833
|
+
<li><strong>Name(s):</strong>${this.renderNames(combinedPatient)}</li>
|
|
834
|
+
<li><strong>Gender:</strong>${combinedPatient.gender ? this.capitalize(combinedPatient.gender) : ""}</li>
|
|
835
|
+
<li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
|
|
836
|
+
<li><strong>Identifier(s):</strong>${this.renderIdentifiers(combinedPatient)}</li>
|
|
837
|
+
<li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
|
|
838
|
+
<li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
|
|
839
|
+
<li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
|
|
840
|
+
<li><strong>Deceased:</strong>${this.renderDeceased(combinedPatient)}</li>
|
|
841
|
+
<li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
|
|
842
|
+
</ul>
|
|
843
|
+
</div>`;
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Combines multiple patient resources into a single patient object
|
|
847
|
+
* Merges fields, preferring non-empty values
|
|
848
|
+
* @param patients - Array of patient resources
|
|
849
|
+
* @returns Combined patient resource
|
|
850
|
+
*/
|
|
851
|
+
static combinePatients(patients) {
|
|
852
|
+
if (patients.length === 1) {
|
|
853
|
+
return patients[0];
|
|
854
|
+
}
|
|
855
|
+
const combined = patients[0];
|
|
856
|
+
const allNames = [];
|
|
857
|
+
const allIdentifiers = [];
|
|
858
|
+
const allTelecom = [];
|
|
859
|
+
const allAddresses = [];
|
|
860
|
+
const allCommunication = [];
|
|
861
|
+
patients.forEach((patient) => {
|
|
862
|
+
if (patient.name) {
|
|
863
|
+
allNames.push(...patient.name);
|
|
865
864
|
}
|
|
866
|
-
|
|
867
|
-
|
|
865
|
+
if (patient.identifier) {
|
|
866
|
+
allIdentifiers.push(...patient.identifier);
|
|
867
|
+
}
|
|
868
|
+
if (patient.telecom) {
|
|
869
|
+
allTelecom.push(...patient.telecom);
|
|
870
|
+
}
|
|
871
|
+
if (patient.address) {
|
|
872
|
+
allAddresses.push(...patient.address);
|
|
873
|
+
}
|
|
874
|
+
if (patient.communication) {
|
|
875
|
+
allCommunication.push(...patient.communication);
|
|
876
|
+
}
|
|
877
|
+
if (!combined.gender && patient.gender) {
|
|
878
|
+
combined.gender = patient.gender;
|
|
879
|
+
}
|
|
880
|
+
if (!combined.birthDate && patient.birthDate) {
|
|
881
|
+
combined.birthDate = patient.birthDate;
|
|
882
|
+
}
|
|
883
|
+
if (!combined.maritalStatus && patient.maritalStatus) {
|
|
884
|
+
combined.maritalStatus = patient.maritalStatus;
|
|
885
|
+
}
|
|
886
|
+
if (!combined.deceasedBoolean && patient.deceasedBoolean !== void 0) {
|
|
887
|
+
combined.deceasedBoolean = patient.deceasedBoolean;
|
|
888
|
+
}
|
|
889
|
+
if (!combined.deceasedDateTime && patient.deceasedDateTime) {
|
|
890
|
+
combined.deceasedDateTime = patient.deceasedDateTime;
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
combined.name = allNames;
|
|
894
|
+
combined.identifier = allIdentifiers;
|
|
895
|
+
combined.telecom = allTelecom;
|
|
896
|
+
combined.address = allAddresses;
|
|
897
|
+
combined.communication = allCommunication;
|
|
898
|
+
return combined;
|
|
868
899
|
}
|
|
869
900
|
/**
|
|
870
901
|
* Renders patient names as HTML list items
|
|
871
|
-
* @param patient - Patient
|
|
902
|
+
* @param patient - Patient resources
|
|
872
903
|
* @returns HTML string of list items
|
|
873
904
|
*/
|
|
874
905
|
static renderNames(patient) {
|
|
@@ -888,7 +919,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
888
919
|
}
|
|
889
920
|
/**
|
|
890
921
|
* Renders patient identifiers as HTML list items
|
|
891
|
-
* @param patient - Patient
|
|
922
|
+
* @param patient - Patient resources
|
|
892
923
|
* @returns HTML string of list items
|
|
893
924
|
*/
|
|
894
925
|
static renderIdentifiers(patient) {
|
|
@@ -903,7 +934,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
903
934
|
}
|
|
904
935
|
/**
|
|
905
936
|
* Renders patient telecom information grouped by system
|
|
906
|
-
* @param patient - Patient
|
|
937
|
+
* @param patient - Patient resources
|
|
907
938
|
* @returns HTML string grouped by system
|
|
908
939
|
*/
|
|
909
940
|
static renderTelecom(patient) {
|
|
@@ -962,7 +993,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
962
993
|
}
|
|
963
994
|
/**
|
|
964
995
|
* Renders patient addresses as HTML list items
|
|
965
|
-
* @param patient - Patient
|
|
996
|
+
* @param patient - Patient resources
|
|
966
997
|
* @returns HTML string of list items
|
|
967
998
|
*/
|
|
968
999
|
static renderAddresses(patient) {
|
|
@@ -984,9 +1015,18 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
984
1015
|
if (address.city) {
|
|
985
1016
|
addressArray.push(address.city);
|
|
986
1017
|
}
|
|
1018
|
+
if (address.district) {
|
|
1019
|
+
addressArray.push(address.district);
|
|
1020
|
+
}
|
|
1021
|
+
if (address.state) {
|
|
1022
|
+
addressArray.push(address.state);
|
|
1023
|
+
}
|
|
987
1024
|
if (address.country) {
|
|
988
1025
|
addressArray.push(address.country);
|
|
989
1026
|
}
|
|
1027
|
+
if (address.postalCode) {
|
|
1028
|
+
addressArray.push(address.postalCode);
|
|
1029
|
+
}
|
|
990
1030
|
}
|
|
991
1031
|
const addressText = addressArray.join(", ").trim();
|
|
992
1032
|
if (addressText) {
|
|
@@ -997,7 +1037,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
997
1037
|
}
|
|
998
1038
|
/**
|
|
999
1039
|
* Renders patient deceased status
|
|
1000
|
-
* @param patient - Patient
|
|
1040
|
+
* @param patient - Patient resources
|
|
1001
1041
|
* @returns HTML string for deceased status
|
|
1002
1042
|
*/
|
|
1003
1043
|
static renderDeceased(patient) {
|
|
@@ -1012,7 +1052,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
1012
1052
|
/**
|
|
1013
1053
|
* Renders patient communication preferences as HTML list items
|
|
1014
1054
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1015
|
-
* @param patient - Patient
|
|
1055
|
+
* @param patient - Patient resources
|
|
1016
1056
|
* @returns HTML string of list items
|
|
1017
1057
|
*/
|
|
1018
1058
|
static renderCommunication(templateUtilities, patient) {
|
|
@@ -1047,32 +1087,30 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
1047
1087
|
var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
1048
1088
|
/**
|
|
1049
1089
|
* Generate HTML narrative for AllergyIntolerance resources
|
|
1050
|
-
* @param
|
|
1090
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
1051
1091
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1052
1092
|
* @returns HTML string for rendering
|
|
1053
1093
|
*/
|
|
1054
|
-
generateNarrative(
|
|
1055
|
-
return _AllergyIntoleranceTemplate.generateStaticNarrative(
|
|
1094
|
+
generateNarrative(resources, timezone) {
|
|
1095
|
+
return _AllergyIntoleranceTemplate.generateStaticNarrative(resources, timezone);
|
|
1056
1096
|
}
|
|
1057
1097
|
/**
|
|
1058
1098
|
* Internal static implementation that actually generates the narrative
|
|
1059
|
-
* @param
|
|
1099
|
+
* @param resources - FHIR resources array containing AllergyIntolerance resources
|
|
1060
1100
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1061
1101
|
* @returns HTML string for rendering
|
|
1062
1102
|
*/
|
|
1063
|
-
static generateStaticNarrative(
|
|
1064
|
-
const templateUtilities = new TemplateUtilities(
|
|
1103
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1104
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1065
1105
|
const activeAllergies = [];
|
|
1066
1106
|
const resolvedAllergies = [];
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
activeAllergies.push(allergy);
|
|
1075
|
-
}
|
|
1107
|
+
for (const resourceItem of resources) {
|
|
1108
|
+
const allergy = resourceItem;
|
|
1109
|
+
const isResolved = allergy.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code));
|
|
1110
|
+
if (isResolved) {
|
|
1111
|
+
resolvedAllergies.push(allergy);
|
|
1112
|
+
} else {
|
|
1113
|
+
activeAllergies.push(allergy);
|
|
1076
1114
|
}
|
|
1077
1115
|
}
|
|
1078
1116
|
activeAllergies.sort((a, b) => {
|
|
@@ -1185,12 +1223,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1185
1223
|
var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
1186
1224
|
/**
|
|
1187
1225
|
* Generate HTML narrative for Medication resources
|
|
1188
|
-
* @param
|
|
1226
|
+
* @param resources - FHIR Medication resources
|
|
1189
1227
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1190
1228
|
* @returns HTML string for rendering
|
|
1191
1229
|
*/
|
|
1192
|
-
generateNarrative(
|
|
1193
|
-
return _MedicationSummaryTemplate.generateStaticNarrative(
|
|
1230
|
+
generateNarrative(resources, timezone) {
|
|
1231
|
+
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
|
|
1194
1232
|
}
|
|
1195
1233
|
/**
|
|
1196
1234
|
* Safely parse a date string and return a valid Date object or null
|
|
@@ -1255,16 +1293,16 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1255
1293
|
}
|
|
1256
1294
|
/**
|
|
1257
1295
|
* Internal static implementation that actually generates the narrative
|
|
1258
|
-
* @param
|
|
1296
|
+
* @param resources - FHIR Medication resources
|
|
1259
1297
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1260
1298
|
* @returns HTML string for rendering
|
|
1261
1299
|
*/
|
|
1262
1300
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1263
|
-
static generateStaticNarrative(
|
|
1264
|
-
const templateUtilities = new TemplateUtilities(
|
|
1301
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1302
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1265
1303
|
let html = "";
|
|
1266
|
-
const medicationRequests = this.getMedicationRequests(templateUtilities,
|
|
1267
|
-
const medicationStatements = this.getMedicationStatements(templateUtilities,
|
|
1304
|
+
const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
|
|
1305
|
+
const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
|
|
1268
1306
|
const allActiveMedications = [];
|
|
1269
1307
|
const allInactiveMedications = [];
|
|
1270
1308
|
medicationRequests.forEach((mr) => {
|
|
@@ -1318,33 +1356,33 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1318
1356
|
return html;
|
|
1319
1357
|
}
|
|
1320
1358
|
/**
|
|
1321
|
-
* Extract MedicationRequest resources
|
|
1359
|
+
* Extract MedicationRequest resources
|
|
1322
1360
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1323
|
-
* @param
|
|
1361
|
+
* @param resources - FHIR Medication resources
|
|
1324
1362
|
* @returns Array of MedicationRequest resources
|
|
1325
1363
|
*/
|
|
1326
|
-
static getMedicationRequests(templateUtilities,
|
|
1327
|
-
if (
|
|
1364
|
+
static getMedicationRequests(templateUtilities, resources) {
|
|
1365
|
+
if (resources.length === 0) {
|
|
1328
1366
|
return [];
|
|
1329
1367
|
}
|
|
1330
|
-
return
|
|
1331
|
-
resource: entry
|
|
1332
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1368
|
+
return resources.filter((entry) => entry.resourceType === "MedicationRequest").map((entry) => ({
|
|
1369
|
+
resource: entry,
|
|
1370
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1333
1371
|
}));
|
|
1334
1372
|
}
|
|
1335
1373
|
/**
|
|
1336
|
-
* Extract MedicationStatement resources
|
|
1374
|
+
* Extract MedicationStatement resources
|
|
1337
1375
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1338
|
-
* @param
|
|
1376
|
+
* @param resources - FHIR Medication resources
|
|
1339
1377
|
* @returns Array of MedicationStatement resources
|
|
1340
1378
|
*/
|
|
1341
|
-
static getMedicationStatements(templateUtilities,
|
|
1342
|
-
if (
|
|
1379
|
+
static getMedicationStatements(templateUtilities, resources) {
|
|
1380
|
+
if (resources.length === 0) {
|
|
1343
1381
|
return [];
|
|
1344
1382
|
}
|
|
1345
|
-
return
|
|
1346
|
-
resource: entry
|
|
1347
|
-
extension: templateUtilities.narrativeLinkExtension(entry
|
|
1383
|
+
return resources.filter((entry) => entry.resourceType === "MedicationStatement").map((entry) => ({
|
|
1384
|
+
resource: entry,
|
|
1385
|
+
extension: templateUtilities.narrativeLinkExtension(entry)
|
|
1348
1386
|
}));
|
|
1349
1387
|
}
|
|
1350
1388
|
/**
|
|
@@ -1440,28 +1478,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1440
1478
|
var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
1441
1479
|
/**
|
|
1442
1480
|
* Generate HTML narrative for Immunization resources
|
|
1443
|
-
* @param
|
|
1481
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1444
1482
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1445
1483
|
* @returns HTML string for rendering
|
|
1446
1484
|
*/
|
|
1447
|
-
generateNarrative(
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
}
|
|
1455
|
-
return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
|
|
1485
|
+
generateNarrative(resources, timezone) {
|
|
1486
|
+
resources.sort((a, b) => {
|
|
1487
|
+
const dateA = a.occurrenceDateTime;
|
|
1488
|
+
const dateB = b.occurrenceDateTime;
|
|
1489
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1490
|
+
});
|
|
1491
|
+
return _ImmunizationsTemplate.generateStaticNarrative(resources, timezone);
|
|
1456
1492
|
}
|
|
1457
1493
|
/**
|
|
1458
1494
|
* Internal static implementation that actually generates the narrative
|
|
1459
|
-
* @param
|
|
1495
|
+
* @param resources - FHIR resources array containing Immunization resources
|
|
1460
1496
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1461
1497
|
* @returns HTML string for rendering
|
|
1462
1498
|
*/
|
|
1463
|
-
static generateStaticNarrative(
|
|
1464
|
-
const templateUtilities = new TemplateUtilities(
|
|
1499
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1500
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1465
1501
|
let html = `
|
|
1466
1502
|
<table>
|
|
1467
1503
|
<thead>
|
|
@@ -1476,21 +1512,20 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1476
1512
|
</tr>
|
|
1477
1513
|
</thead>
|
|
1478
1514
|
<tbody>`;
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
}
|
|
1515
|
+
const immunizations = resources.filter((resourceItem) => resourceItem.resourceType === "Immunization");
|
|
1516
|
+
if (immunizations.length > 0) {
|
|
1517
|
+
for (const resourceItem of immunizations) {
|
|
1518
|
+
const imm = resourceItem;
|
|
1519
|
+
html += `
|
|
1520
|
+
<tr id="${templateUtilities.narrativeLinkId(imm)}">
|
|
1521
|
+
<td>${templateUtilities.codeableConcept(imm.vaccineCode)}</td>
|
|
1522
|
+
<td>${imm.status || ""}</td>
|
|
1523
|
+
<td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
|
|
1524
|
+
<td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
|
|
1525
|
+
<td>${imm.lotNumber || ""}</td>
|
|
1526
|
+
<td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
|
|
1527
|
+
<td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
|
|
1528
|
+
</tr>`;
|
|
1494
1529
|
}
|
|
1495
1530
|
}
|
|
1496
1531
|
html += `
|
|
@@ -1504,23 +1539,23 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1504
1539
|
var ProblemListTemplate = class _ProblemListTemplate {
|
|
1505
1540
|
/**
|
|
1506
1541
|
* Generate HTML narrative for Problem List
|
|
1507
|
-
* @param
|
|
1542
|
+
* @param resources - FHIR Condition resources
|
|
1508
1543
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1509
1544
|
* @returns HTML string for rendering
|
|
1510
1545
|
*/
|
|
1511
|
-
generateNarrative(
|
|
1512
|
-
return _ProblemListTemplate.generateStaticNarrative(
|
|
1546
|
+
generateNarrative(resources, timezone) {
|
|
1547
|
+
return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
|
|
1513
1548
|
}
|
|
1514
1549
|
/**
|
|
1515
1550
|
* Internal static implementation that actually generates the narrative
|
|
1516
|
-
* @param
|
|
1551
|
+
* @param resources - FHIR Condition resources
|
|
1517
1552
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1518
1553
|
* @returns HTML string for rendering
|
|
1519
1554
|
*/
|
|
1520
|
-
static generateStaticNarrative(
|
|
1521
|
-
const templateUtilities = new TemplateUtilities(
|
|
1555
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1556
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1522
1557
|
let html = ``;
|
|
1523
|
-
const activeConditions =
|
|
1558
|
+
const activeConditions = resources.map((entry) => entry) || [];
|
|
1524
1559
|
activeConditions.sort((a, b) => {
|
|
1525
1560
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1526
1561
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1555,22 +1590,22 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1555
1590
|
var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
1556
1591
|
/**
|
|
1557
1592
|
* Generate HTML narrative for Vital Signs
|
|
1558
|
-
* @param
|
|
1593
|
+
* @param resources - FHIR Observation resources
|
|
1559
1594
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1560
1595
|
* @returns HTML string for rendering
|
|
1561
1596
|
*/
|
|
1562
|
-
generateNarrative(
|
|
1563
|
-
return _VitalSignsTemplate.generateStaticNarrative(
|
|
1597
|
+
generateNarrative(resources, timezone) {
|
|
1598
|
+
return _VitalSignsTemplate.generateStaticNarrative(resources, timezone);
|
|
1564
1599
|
}
|
|
1565
1600
|
/**
|
|
1566
1601
|
* Internal static implementation that actually generates the narrative
|
|
1567
|
-
* @param
|
|
1602
|
+
* @param resources - FHIR Observation resources
|
|
1568
1603
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1569
1604
|
* @returns HTML string for rendering
|
|
1570
1605
|
*/
|
|
1571
|
-
static generateStaticNarrative(
|
|
1572
|
-
const templateUtilities = new TemplateUtilities(
|
|
1573
|
-
const observations =
|
|
1606
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1607
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1608
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1574
1609
|
observations.sort((a, b) => {
|
|
1575
1610
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1576
1611
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1613,28 +1648,21 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1613
1648
|
var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
1614
1649
|
/**
|
|
1615
1650
|
* Generate HTML narrative for Medical Device resources
|
|
1616
|
-
* @param
|
|
1651
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1617
1652
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1618
1653
|
* @returns HTML string for rendering
|
|
1619
1654
|
*/
|
|
1620
|
-
generateNarrative(
|
|
1621
|
-
|
|
1622
|
-
resource.entry.sort((a, b) => {
|
|
1623
|
-
const dateA = a.resource?.recordedOn;
|
|
1624
|
-
const dateB = b.resource?.recordedOn;
|
|
1625
|
-
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1626
|
-
});
|
|
1627
|
-
}
|
|
1628
|
-
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1655
|
+
generateNarrative(resources, timezone) {
|
|
1656
|
+
return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
|
|
1629
1657
|
}
|
|
1630
1658
|
/**
|
|
1631
1659
|
* Internal static implementation that actually generates the narrative
|
|
1632
|
-
* @param
|
|
1660
|
+
* @param resources - FHIR resources array containing Device resources
|
|
1633
1661
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1634
1662
|
* @returns HTML string for rendering
|
|
1635
1663
|
*/
|
|
1636
|
-
static generateStaticNarrative(
|
|
1637
|
-
const templateUtilities = new TemplateUtilities(
|
|
1664
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1665
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1638
1666
|
let html = `
|
|
1639
1667
|
<table>
|
|
1640
1668
|
<thead>
|
|
@@ -1646,19 +1674,19 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1646
1674
|
</tr>
|
|
1647
1675
|
</thead>
|
|
1648
1676
|
<tbody>`;
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1677
|
+
const deviceStatements = resources.filter((resourceItem) => resourceItem.resourceType === "DeviceUseStatement").map((resourceItem) => resourceItem).sort((a, b) => {
|
|
1678
|
+
const dateA = a.recordedOn;
|
|
1679
|
+
const dateB = b.recordedOn;
|
|
1680
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1681
|
+
});
|
|
1682
|
+
for (const dus of deviceStatements) {
|
|
1683
|
+
html += `
|
|
1684
|
+
<tr id="${templateUtilities.narrativeLinkId(dus)}">
|
|
1685
|
+
<td>${templateUtilities.renderDevice(dus.device)}</td>
|
|
1686
|
+
<td>${dus.status || ""}</td>
|
|
1687
|
+
<td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
|
|
1688
|
+
<td>${templateUtilities.renderRecorded(dus.recordedOn, timezone)}</td>
|
|
1689
|
+
</tr>`;
|
|
1662
1690
|
}
|
|
1663
1691
|
html += `
|
|
1664
1692
|
</tbody>
|
|
@@ -1671,23 +1699,23 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1671
1699
|
var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
1672
1700
|
/**
|
|
1673
1701
|
* Generate HTML narrative for Diagnostic Results
|
|
1674
|
-
* @param
|
|
1702
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1675
1703
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1676
1704
|
* @returns HTML string for rendering
|
|
1677
1705
|
*/
|
|
1678
|
-
generateNarrative(
|
|
1679
|
-
return _DiagnosticResultsTemplate.generateStaticNarrative(
|
|
1706
|
+
generateNarrative(resources, timezone) {
|
|
1707
|
+
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
|
|
1680
1708
|
}
|
|
1681
1709
|
/**
|
|
1682
1710
|
* Internal static implementation that actually generates the narrative
|
|
1683
|
-
* @param
|
|
1711
|
+
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
1684
1712
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1685
1713
|
* @returns HTML string for rendering
|
|
1686
1714
|
*/
|
|
1687
|
-
static generateStaticNarrative(
|
|
1688
|
-
const templateUtilities = new TemplateUtilities(
|
|
1715
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1716
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1689
1717
|
let html = "";
|
|
1690
|
-
const observations = this.getObservations(
|
|
1718
|
+
const observations = this.getObservations(resources);
|
|
1691
1719
|
if (observations.length > 0) {
|
|
1692
1720
|
observations.sort((a, b) => {
|
|
1693
1721
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
@@ -1696,7 +1724,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1696
1724
|
});
|
|
1697
1725
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1698
1726
|
}
|
|
1699
|
-
const diagnosticReports = this.getDiagnosticReports(
|
|
1727
|
+
const diagnosticReports = this.getDiagnosticReports(resources);
|
|
1700
1728
|
if (diagnosticReports.length > 0) {
|
|
1701
1729
|
diagnosticReports.sort((a, b) => {
|
|
1702
1730
|
const dateA = a.issued;
|
|
@@ -1708,26 +1736,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1708
1736
|
return html;
|
|
1709
1737
|
}
|
|
1710
1738
|
/**
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
static getObservations(
|
|
1716
|
-
|
|
1717
|
-
return [];
|
|
1718
|
-
}
|
|
1719
|
-
return resource.entry.filter((entry) => entry.resource?.resourceType === "Observation").map((entry) => entry.resource);
|
|
1739
|
+
* Get all Observation resources from the resource array
|
|
1740
|
+
* @param resources - FHIR resources array
|
|
1741
|
+
* @returns Array of Observation resources
|
|
1742
|
+
*/
|
|
1743
|
+
static getObservations(resources) {
|
|
1744
|
+
return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
|
|
1720
1745
|
}
|
|
1721
1746
|
/**
|
|
1722
|
-
*
|
|
1723
|
-
* @param
|
|
1747
|
+
* Get all DiagnosticReport resources from the resource array
|
|
1748
|
+
* @param resources - FHIR resources array
|
|
1724
1749
|
* @returns Array of DiagnosticReport resources
|
|
1725
1750
|
*/
|
|
1726
|
-
static getDiagnosticReports(
|
|
1727
|
-
|
|
1728
|
-
return [];
|
|
1729
|
-
}
|
|
1730
|
-
return resource.entry.filter((entry) => entry.resource?.resourceType === "DiagnosticReport").map((entry) => entry.resource);
|
|
1751
|
+
static getDiagnosticReports(resources) {
|
|
1752
|
+
return resources.filter((resourceItem) => resourceItem.resourceType === "DiagnosticReport").map((resourceItem) => resourceItem);
|
|
1731
1753
|
}
|
|
1732
1754
|
/**
|
|
1733
1755
|
* Render HTML table for Observation resources
|
|
@@ -1813,26 +1835,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1813
1835
|
var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
1814
1836
|
/**
|
|
1815
1837
|
* Generate HTML narrative for Procedure resources
|
|
1816
|
-
* @param
|
|
1838
|
+
* @param resources - FHIR Procedure resources
|
|
1817
1839
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1818
1840
|
* @returns HTML string for rendering
|
|
1819
1841
|
*/
|
|
1820
|
-
generateNarrative(
|
|
1821
|
-
|
|
1822
|
-
const dateA = a.
|
|
1823
|
-
const dateB = b.
|
|
1842
|
+
generateNarrative(resources, timezone) {
|
|
1843
|
+
resources.sort((a, b) => {
|
|
1844
|
+
const dateA = a.performedDateTime || a.performedPeriod?.start;
|
|
1845
|
+
const dateB = b.performedDateTime || b.performedPeriod?.start;
|
|
1824
1846
|
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1825
1847
|
});
|
|
1826
|
-
return _HistoryOfProceduresTemplate.generateStaticNarrative(
|
|
1848
|
+
return _HistoryOfProceduresTemplate.generateStaticNarrative(resources, timezone);
|
|
1827
1849
|
}
|
|
1828
1850
|
/**
|
|
1829
1851
|
* Internal static implementation that actually generates the narrative
|
|
1830
|
-
* @param
|
|
1852
|
+
* @param resources - FHIR Procedure resources
|
|
1831
1853
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1832
1854
|
* @returns HTML string for rendering
|
|
1833
1855
|
*/
|
|
1834
|
-
static generateStaticNarrative(
|
|
1835
|
-
const templateUtilities = new TemplateUtilities(
|
|
1856
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1857
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1836
1858
|
let html = `
|
|
1837
1859
|
<table>
|
|
1838
1860
|
<thead>
|
|
@@ -1843,16 +1865,14 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1843
1865
|
</tr>
|
|
1844
1866
|
</thead>
|
|
1845
1867
|
<tbody>`;
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
<
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
</tr>`;
|
|
1855
|
-
}
|
|
1868
|
+
for (const resourceItem of resources) {
|
|
1869
|
+
const proc = resourceItem;
|
|
1870
|
+
html += `
|
|
1871
|
+
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
1872
|
+
<td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
|
|
1873
|
+
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
1874
|
+
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
1875
|
+
</tr>`;
|
|
1856
1876
|
}
|
|
1857
1877
|
html += `
|
|
1858
1878
|
</tbody>
|
|
@@ -1865,22 +1885,22 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1865
1885
|
var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
1866
1886
|
/**
|
|
1867
1887
|
* Generate HTML narrative for Social History
|
|
1868
|
-
* @param
|
|
1888
|
+
* @param resources - FHIR Observation resources
|
|
1869
1889
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1870
1890
|
* @returns HTML string for rendering
|
|
1871
1891
|
*/
|
|
1872
|
-
generateNarrative(
|
|
1873
|
-
return _SocialHistoryTemplate.generateStaticNarrative(
|
|
1892
|
+
generateNarrative(resources, timezone) {
|
|
1893
|
+
return _SocialHistoryTemplate.generateStaticNarrative(resources, timezone);
|
|
1874
1894
|
}
|
|
1875
1895
|
/**
|
|
1876
1896
|
* Internal static implementation that actually generates the narrative
|
|
1877
|
-
* @param
|
|
1897
|
+
* @param resources - FHIR Observation resources
|
|
1878
1898
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1879
1899
|
* @returns HTML string for rendering
|
|
1880
1900
|
*/
|
|
1881
|
-
static generateStaticNarrative(
|
|
1882
|
-
const templateUtilities = new TemplateUtilities(
|
|
1883
|
-
const observations =
|
|
1901
|
+
static generateStaticNarrative(resources, timezone) {
|
|
1902
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1903
|
+
const observations = resources.map((entry) => entry) || [];
|
|
1884
1904
|
observations.sort((a, b) => {
|
|
1885
1905
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1886
1906
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -1919,14 +1939,14 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1919
1939
|
var PastHistoryOfIllnessTemplate = class {
|
|
1920
1940
|
/**
|
|
1921
1941
|
* Generate HTML narrative for Past History of Illnesses
|
|
1922
|
-
* @param
|
|
1942
|
+
* @param resources - FHIR Condition resources
|
|
1923
1943
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1924
1944
|
* @returns HTML string for rendering
|
|
1925
1945
|
*/
|
|
1926
|
-
generateNarrative(
|
|
1927
|
-
const templateUtilities = new TemplateUtilities(
|
|
1946
|
+
generateNarrative(resources, timezone) {
|
|
1947
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1928
1948
|
let html = ``;
|
|
1929
|
-
const resolvedConditions =
|
|
1949
|
+
const resolvedConditions = resources.map((entry) => entry) || [];
|
|
1930
1950
|
resolvedConditions.sort((a, b) => {
|
|
1931
1951
|
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1932
1952
|
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
@@ -1963,13 +1983,13 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1963
1983
|
var PlanOfCareTemplate = class {
|
|
1964
1984
|
/**
|
|
1965
1985
|
* Generate HTML narrative for Plan of Care
|
|
1966
|
-
* @param
|
|
1986
|
+
* @param resources - FHIR CarePlan resources
|
|
1967
1987
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1968
1988
|
* @returns HTML string for rendering
|
|
1969
1989
|
*/
|
|
1970
|
-
generateNarrative(
|
|
1971
|
-
const templateUtilities = new TemplateUtilities(
|
|
1972
|
-
const carePlans =
|
|
1990
|
+
generateNarrative(resources, timezone) {
|
|
1991
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
1992
|
+
const carePlans = resources.map((entry) => entry) || [];
|
|
1973
1993
|
carePlans.sort((a, b) => {
|
|
1974
1994
|
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1975
1995
|
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
@@ -2008,37 +2028,35 @@ var PlanOfCareTemplate = class {
|
|
|
2008
2028
|
var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
2009
2029
|
/**
|
|
2010
2030
|
* Generate HTML narrative for Functional Status
|
|
2011
|
-
* @param
|
|
2031
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
2012
2032
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2013
2033
|
* @returns HTML string for rendering
|
|
2014
2034
|
*/
|
|
2015
|
-
generateNarrative(
|
|
2016
|
-
return _FunctionalStatusTemplate.generateStaticNarrative(
|
|
2035
|
+
generateNarrative(resources, timezone) {
|
|
2036
|
+
return _FunctionalStatusTemplate.generateStaticNarrative(resources, timezone);
|
|
2017
2037
|
}
|
|
2018
2038
|
/**
|
|
2019
2039
|
* Internal static implementation that actually generates the narrative
|
|
2020
|
-
* @param
|
|
2040
|
+
* @param resources - FHIR resources array containing Observation resources
|
|
2021
2041
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2022
2042
|
* @returns HTML string for rendering
|
|
2023
2043
|
*/
|
|
2024
|
-
static generateStaticNarrative(
|
|
2025
|
-
const templateUtilities = new TemplateUtilities(
|
|
2044
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2045
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2026
2046
|
let html = ``;
|
|
2027
2047
|
const activeConditions = [];
|
|
2028
2048
|
const clinicalImpressions = [];
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
activeConditions.push(cond);
|
|
2038
|
-
}
|
|
2039
|
-
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
2040
|
-
clinicalImpressions.push(entry.resource);
|
|
2049
|
+
for (const resourceItem of resources) {
|
|
2050
|
+
if (resourceItem.resourceType === "Condition") {
|
|
2051
|
+
const cond = resourceItem;
|
|
2052
|
+
const isResolved = cond.clinicalStatus?.coding?.some(
|
|
2053
|
+
(c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
|
|
2054
|
+
);
|
|
2055
|
+
if (!isResolved) {
|
|
2056
|
+
activeConditions.push(cond);
|
|
2041
2057
|
}
|
|
2058
|
+
} else if (resourceItem.resourceType === "ClinicalImpression") {
|
|
2059
|
+
clinicalImpressions.push(resourceItem);
|
|
2042
2060
|
}
|
|
2043
2061
|
}
|
|
2044
2062
|
activeConditions.sort((a, b) => {
|
|
@@ -2135,22 +2153,22 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
2135
2153
|
var PregnancyTemplate = class _PregnancyTemplate {
|
|
2136
2154
|
/**
|
|
2137
2155
|
* Generate HTML narrative for Pregnancy
|
|
2138
|
-
* @param
|
|
2156
|
+
* @param resources - FHIR Observation resources
|
|
2139
2157
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2140
2158
|
* @returns HTML string for rendering
|
|
2141
2159
|
*/
|
|
2142
|
-
generateNarrative(
|
|
2143
|
-
return _PregnancyTemplate.generateStaticNarrative(
|
|
2160
|
+
generateNarrative(resources, timezone) {
|
|
2161
|
+
return _PregnancyTemplate.generateStaticNarrative(resources, timezone);
|
|
2144
2162
|
}
|
|
2145
2163
|
/**
|
|
2146
2164
|
* Internal static implementation that actually generates the narrative
|
|
2147
|
-
* @param
|
|
2165
|
+
* @param resources - FHIR Observation resources
|
|
2148
2166
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2149
2167
|
* @returns HTML string for rendering
|
|
2150
2168
|
*/
|
|
2151
|
-
static generateStaticNarrative(
|
|
2152
|
-
const templateUtilities = new TemplateUtilities(
|
|
2153
|
-
const observations =
|
|
2169
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2170
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2171
|
+
const observations = resources.map((entry) => entry) || [];
|
|
2154
2172
|
observations.sort((a, b) => {
|
|
2155
2173
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2156
2174
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
@@ -2166,8 +2184,8 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2166
2184
|
</tr>
|
|
2167
2185
|
</thead>
|
|
2168
2186
|
<tbody>`;
|
|
2169
|
-
for (const
|
|
2170
|
-
const obs =
|
|
2187
|
+
for (const resource of observations) {
|
|
2188
|
+
const obs = resource;
|
|
2171
2189
|
html += `
|
|
2172
2190
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2173
2191
|
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
@@ -2186,29 +2204,27 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
2186
2204
|
var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
2187
2205
|
/**
|
|
2188
2206
|
* Generate HTML narrative for Advance Directives
|
|
2189
|
-
* @param
|
|
2207
|
+
* @param resources - FHIR Consent resources
|
|
2190
2208
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2191
2209
|
* @returns HTML string for rendering
|
|
2192
2210
|
*/
|
|
2193
|
-
generateNarrative(
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
}
|
|
2201
|
-
return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
|
|
2211
|
+
generateNarrative(resources, timezone) {
|
|
2212
|
+
resources.sort((a, b) => {
|
|
2213
|
+
const dateA = new Date(a.dateTime || 0);
|
|
2214
|
+
const dateB = new Date(b.dateTime || 0);
|
|
2215
|
+
return dateB.getTime() - dateA.getTime();
|
|
2216
|
+
});
|
|
2217
|
+
return _AdvanceDirectivesTemplate.generateStaticNarrative(resources, timezone);
|
|
2202
2218
|
}
|
|
2203
2219
|
/**
|
|
2204
2220
|
* Internal static implementation that actually generates the narrative
|
|
2205
|
-
* @param
|
|
2221
|
+
* @param resources - FHIR Consent resources
|
|
2206
2222
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2207
2223
|
* @returns HTML string for rendering
|
|
2208
2224
|
*/
|
|
2209
2225
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2210
|
-
static generateStaticNarrative(
|
|
2211
|
-
const templateUtilities = new TemplateUtilities(
|
|
2226
|
+
static generateStaticNarrative(resources, timezone) {
|
|
2227
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2212
2228
|
let html = `
|
|
2213
2229
|
<table>
|
|
2214
2230
|
<thead>
|
|
@@ -2220,17 +2236,15 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
2220
2236
|
</tr>
|
|
2221
2237
|
</thead>
|
|
2222
2238
|
<tbody>`;
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
<
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
</tr>`;
|
|
2233
|
-
}
|
|
2239
|
+
for (const resourceItem of resources) {
|
|
2240
|
+
const consent = resourceItem;
|
|
2241
|
+
html += `
|
|
2242
|
+
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
2243
|
+
<td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
|
|
2244
|
+
<td>${consent.status || ""}</td>
|
|
2245
|
+
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
2246
|
+
<td>${consent.dateTime || ""}</td>
|
|
2247
|
+
</tr>`;
|
|
2234
2248
|
}
|
|
2235
2249
|
html += `
|
|
2236
2250
|
</tbody>
|
|
@@ -2244,16 +2258,16 @@ var TypeScriptTemplateMapper = class {
|
|
|
2244
2258
|
/**
|
|
2245
2259
|
* Generates HTML narrative for a specific IPS section
|
|
2246
2260
|
* @param section - The IPS section
|
|
2247
|
-
* @param
|
|
2261
|
+
* @param resources - FHIR resources
|
|
2248
2262
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2249
2263
|
* @returns HTML string for rendering
|
|
2250
2264
|
*/
|
|
2251
|
-
static generateNarrative(section,
|
|
2265
|
+
static generateNarrative(section, resources, timezone) {
|
|
2252
2266
|
const templateClass = this.sectionToTemplate[section];
|
|
2253
2267
|
if (!templateClass) {
|
|
2254
2268
|
throw new Error(`No template found for section: ${section}`);
|
|
2255
2269
|
}
|
|
2256
|
-
return templateClass.generateNarrative(
|
|
2270
|
+
return templateClass.generateNarrative(resources, timezone);
|
|
2257
2271
|
}
|
|
2258
2272
|
};
|
|
2259
2273
|
// Map of section types to their template classes
|
|
@@ -2316,14 +2330,7 @@ var NarrativeGenerator = class {
|
|
|
2316
2330
|
return void 0;
|
|
2317
2331
|
}
|
|
2318
2332
|
try {
|
|
2319
|
-
const
|
|
2320
|
-
resourceType: "Bundle",
|
|
2321
|
-
type: "collection",
|
|
2322
|
-
entry: resources.map((resource) => ({
|
|
2323
|
-
resource
|
|
2324
|
-
}))
|
|
2325
|
-
};
|
|
2326
|
-
const content = TypeScriptTemplateMapper.generateNarrative(section, bundle, timezone);
|
|
2333
|
+
const content = TypeScriptTemplateMapper.generateNarrative(section, resources, timezone);
|
|
2327
2334
|
if (!content) {
|
|
2328
2335
|
return void 0;
|
|
2329
2336
|
}
|
|
@@ -2408,13 +2415,16 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2408
2415
|
/**
|
|
2409
2416
|
* sets the patient resource for the IPS Composition.
|
|
2410
2417
|
* This is not needed if you are calling read_bundle, but can be used to set the patient resource directly.
|
|
2411
|
-
* @param
|
|
2418
|
+
* @param patients - FHIR Patient resource to set
|
|
2412
2419
|
*/
|
|
2413
|
-
setPatient(
|
|
2414
|
-
if (!
|
|
2420
|
+
setPatient(patients) {
|
|
2421
|
+
if (!Array.isArray(patients)) {
|
|
2422
|
+
patients = [patients];
|
|
2423
|
+
}
|
|
2424
|
+
if (patients.length === 0 || !patients.every((patient) => patient.resourceType === "Patient")) {
|
|
2415
2425
|
throw new Error("Invalid Patient resource");
|
|
2416
2426
|
}
|
|
2417
|
-
this.
|
|
2427
|
+
this.patients = patients;
|
|
2418
2428
|
return this;
|
|
2419
2429
|
}
|
|
2420
2430
|
/**
|
|
@@ -2472,21 +2482,26 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2472
2482
|
if (!bundle.entry) {
|
|
2473
2483
|
return this;
|
|
2474
2484
|
}
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2485
|
+
const patientEntries = [];
|
|
2486
|
+
const resources = [];
|
|
2487
|
+
bundle.entry.forEach((e) => {
|
|
2488
|
+
if (e.resource?.resourceType === "Patient") {
|
|
2489
|
+
patientEntries.push(e.resource);
|
|
2490
|
+
this.resources.add(e.resource);
|
|
2491
|
+
} else if (e.resource) {
|
|
2492
|
+
resources.push(e.resource);
|
|
2493
|
+
}
|
|
2494
|
+
});
|
|
2495
|
+
if (patientEntries.length === 0) {
|
|
2477
2496
|
throw new Error("Patient resource not found in the bundle");
|
|
2478
2497
|
}
|
|
2479
|
-
this.
|
|
2480
|
-
const resources = bundle.entry.map((e) => e.resource);
|
|
2498
|
+
this.patients = patientEntries;
|
|
2481
2499
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
let sectionResources = resources.filter(
|
|
2485
|
-
(r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
|
|
2486
|
-
);
|
|
2487
|
-
if (customFilter) {
|
|
2488
|
-
sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
|
|
2500
|
+
if (sectionType === "Patient" /* PATIENT */) {
|
|
2501
|
+
continue;
|
|
2489
2502
|
}
|
|
2503
|
+
const sectionFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2504
|
+
const sectionResources = resources.filter((resource) => sectionFilter(resource));
|
|
2490
2505
|
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2491
2506
|
}
|
|
2492
2507
|
return this;
|
|
@@ -2497,16 +2512,18 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2497
2512
|
* @param authorOrganizationName - Name of the authoring organization
|
|
2498
2513
|
* @param baseUrl - Base URL for the FHIR server (e.g., 'https://example.com/fhir')
|
|
2499
2514
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2515
|
+
* @param patientId - Optional patient ID to use as primary patient for composition reference
|
|
2500
2516
|
*/
|
|
2501
|
-
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone) {
|
|
2517
|
+
async buildBundleAsync(authorOrganizationId, authorOrganizationName, baseUrl, timezone, patientId) {
|
|
2502
2518
|
if (baseUrl.endsWith("/")) {
|
|
2503
2519
|
baseUrl = baseUrl.slice(0, -1);
|
|
2504
2520
|
}
|
|
2505
|
-
if (!this.
|
|
2521
|
+
if (!this.patients) {
|
|
2506
2522
|
throw new Error("Patient resource must be set before building the bundle");
|
|
2507
2523
|
}
|
|
2524
|
+
const primaryPatientId = patientId ?? this.patients[0].id;
|
|
2508
2525
|
const composition = {
|
|
2509
|
-
id: `Composition-${
|
|
2526
|
+
id: `Composition-${primaryPatientId}`,
|
|
2510
2527
|
resourceType: "Composition",
|
|
2511
2528
|
status: "final",
|
|
2512
2529
|
type: {
|
|
@@ -2517,7 +2534,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2517
2534
|
}]
|
|
2518
2535
|
},
|
|
2519
2536
|
subject: {
|
|
2520
|
-
reference: `Patient/${
|
|
2537
|
+
reference: `Patient/${primaryPatientId}`
|
|
2521
2538
|
},
|
|
2522
2539
|
author: [{
|
|
2523
2540
|
reference: `Organization/${authorOrganizationId}`,
|
|
@@ -2529,7 +2546,7 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2529
2546
|
section: this.sections,
|
|
2530
2547
|
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2531
2548
|
"Patient" /* PATIENT */,
|
|
2532
|
-
|
|
2549
|
+
this.patients,
|
|
2533
2550
|
timezone,
|
|
2534
2551
|
true
|
|
2535
2552
|
)
|
|
@@ -2548,9 +2565,11 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2548
2565
|
fullUrl: `${baseUrl}/Composition/${composition.id}`,
|
|
2549
2566
|
resource: composition
|
|
2550
2567
|
});
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2568
|
+
this.patients.forEach((patient) => {
|
|
2569
|
+
bundle.entry?.push({
|
|
2570
|
+
fullUrl: `${baseUrl}/Patient/${patient.id}`,
|
|
2571
|
+
resource: patient
|
|
2572
|
+
});
|
|
2554
2573
|
});
|
|
2555
2574
|
this.resources.forEach((resource) => {
|
|
2556
2575
|
if (resource.resourceType !== "Patient") {
|