@imranq2/fhirpatientsummary 1.0.10 → 1.0.12
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 +570 -550
- package/dist/index.d.cts +15 -57
- package/dist/index.d.ts +15 -57
- package/dist/index.js +570 -550
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -29,22 +29,21 @@ module.exports = __toCommonJS(index_exports);
|
|
|
29
29
|
// src/structures/ips_sections.ts
|
|
30
30
|
var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
|
|
31
31
|
IPSSections2["PATIENT"] = "Patient";
|
|
32
|
-
IPSSections2["ALLERGIES"] = "AllergyIntoleranceSection";
|
|
33
|
-
IPSSections2["MEDICATIONS"] = "MedicationSection";
|
|
34
32
|
IPSSections2["PROBLEMS"] = "ProblemSection";
|
|
33
|
+
IPSSections2["ALLERGIES"] = "AllergyIntoleranceSection";
|
|
34
|
+
IPSSections2["MEDICATIONS"] = "MedicationSummarySection";
|
|
35
35
|
IPSSections2["IMMUNIZATIONS"] = "ImmunizationSection";
|
|
36
|
-
IPSSections2["
|
|
36
|
+
IPSSections2["DIAGNOSTIC_REPORTS"] = "ResultsSection";
|
|
37
|
+
IPSSections2["PROCEDURES"] = "HistoryOfProceduresSection";
|
|
37
38
|
IPSSections2["MEDICAL_DEVICES"] = "MedicalDeviceSection";
|
|
38
|
-
IPSSections2["DIAGNOSTIC_REPORTS"] = "DiagnosticReportSection";
|
|
39
|
-
IPSSections2["PROCEDURES"] = "ProcedureSection";
|
|
40
|
-
IPSSections2["FAMILY_HISTORY"] = "FamilyHistorySection";
|
|
41
|
-
IPSSections2["SOCIAL_HISTORY"] = "SocialHistorySection";
|
|
42
|
-
IPSSections2["PREGNANCY_HISTORY"] = "PregnancyHistorySection";
|
|
43
|
-
IPSSections2["FUNCTIONAL_STATUS"] = "FunctionalStatusSection";
|
|
44
|
-
IPSSections2["MEDICAL_HISTORY"] = "MedicalHistorySection";
|
|
45
|
-
IPSSections2["CARE_PLAN"] = "CarePlanSection";
|
|
46
|
-
IPSSections2["CLINICAL_IMPRESSION"] = "ClinicalImpressionSection";
|
|
47
39
|
IPSSections2["ADVANCE_DIRECTIVES"] = "AdvanceDirectivesSection";
|
|
40
|
+
IPSSections2["FUNCTIONAL_STATUS"] = "FunctionalStatusSection";
|
|
41
|
+
IPSSections2["PREGNANCY_HISTORY"] = "HistoryOfPregnancySection";
|
|
42
|
+
IPSSections2["CARE_PLAN"] = "PlanOfCareSection";
|
|
43
|
+
IPSSections2["MEDICAL_HISTORY"] = "HistoryOfPastIllnessSection";
|
|
44
|
+
IPSSections2["SOCIAL_HISTORY"] = "SocialHistorySection";
|
|
45
|
+
IPSSections2["VITAL_SIGNS"] = "VitalSignsSection";
|
|
46
|
+
IPSSections2["FAMILY_HISTORY"] = "FamilyHistorySection";
|
|
48
47
|
return IPSSections2;
|
|
49
48
|
})(IPSSections || {});
|
|
50
49
|
|
|
@@ -52,99 +51,111 @@ var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
|
|
|
52
51
|
var IPS_SECTION_LOINC_CODES = {
|
|
53
52
|
["Patient" /* PATIENT */]: "54126-4",
|
|
54
53
|
["AllergyIntoleranceSection" /* ALLERGIES */]: "48765-2",
|
|
55
|
-
["
|
|
54
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "10160-0",
|
|
56
55
|
["ProblemSection" /* PROBLEMS */]: "11450-4",
|
|
57
56
|
["ImmunizationSection" /* IMMUNIZATIONS */]: "11369-6",
|
|
58
57
|
["VitalSignsSection" /* VITAL_SIGNS */]: "8716-3",
|
|
59
58
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "46264-8",
|
|
60
|
-
["
|
|
61
|
-
["
|
|
59
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
|
|
60
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: "47519-4",
|
|
62
61
|
["FamilyHistorySection" /* FAMILY_HISTORY */]: "10157-6",
|
|
63
62
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: "29762-2",
|
|
64
|
-
["
|
|
63
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "10162-6",
|
|
65
64
|
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "47420-5",
|
|
66
|
-
["
|
|
67
|
-
["
|
|
68
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: "51848-0",
|
|
65
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "11348-0",
|
|
66
|
+
["PlanOfCareSection" /* CARE_PLAN */]: "18776-5",
|
|
69
67
|
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "42348-3"
|
|
70
68
|
};
|
|
71
69
|
var IPS_SECTION_DISPLAY_NAMES = {
|
|
72
70
|
["Patient" /* PATIENT */]: "Patient summary Document",
|
|
73
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and
|
|
74
|
-
["
|
|
75
|
-
["ProblemSection" /* PROBLEMS */]: "Problem
|
|
76
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: "
|
|
77
|
-
["
|
|
78
|
-
["
|
|
79
|
-
["
|
|
80
|
-
["
|
|
81
|
-
["
|
|
82
|
-
["
|
|
83
|
-
["
|
|
84
|
-
["
|
|
85
|
-
["
|
|
86
|
-
["
|
|
87
|
-
["
|
|
88
|
-
|
|
71
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and Intolerances",
|
|
72
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "Medication Summary",
|
|
73
|
+
["ProblemSection" /* PROBLEMS */]: "Problem List",
|
|
74
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: "Immunizations",
|
|
75
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "Results Summary",
|
|
76
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: "History of Procedures",
|
|
77
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "History of Medical Devices",
|
|
78
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: "Vital Signs",
|
|
79
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "Advance Directives",
|
|
80
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "Functional Status",
|
|
81
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "History of Pregnancies",
|
|
82
|
+
["PlanOfCareSection" /* CARE_PLAN */]: "Plan of Care",
|
|
83
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "History of Past Illness",
|
|
84
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History",
|
|
85
|
+
["FamilyHistorySection" /* FAMILY_HISTORY */]: "History of Family Member Diseases"
|
|
86
|
+
};
|
|
87
|
+
var PREGNANCY_LONIC_CODES = {
|
|
88
|
+
PREGNANCY_STATUS: {
|
|
89
|
+
"LA15173-0": "Pregnant",
|
|
90
|
+
"LA26683-5": "Not pregnant",
|
|
91
|
+
"LA4489-6": "Unknown"
|
|
92
|
+
},
|
|
93
|
+
PREGNANCY_OUTCOME: {
|
|
94
|
+
"11636-8": "Live Birth",
|
|
95
|
+
"11637-6": "Preterm Birth",
|
|
96
|
+
"11638-4": "Still Living Birth",
|
|
97
|
+
"11639-2": "Term Birth",
|
|
98
|
+
"11640-0": "Total Births",
|
|
99
|
+
"11612-9": "Abortions",
|
|
100
|
+
"11613-7": "Induced Abortions",
|
|
101
|
+
"11614-5": "Spontaneous Abortions",
|
|
102
|
+
"33065-4": "Ectopic Pregnancy"
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var SOCIAL_HISTORY_LONIC_CODES = {
|
|
106
|
+
"72166-2": "Tobacco Use",
|
|
107
|
+
"74013-4": "Alcohol Use"
|
|
89
108
|
};
|
|
90
109
|
|
|
91
110
|
// src/structures/ips_section_resource_map.ts
|
|
92
111
|
var IPSSectionResourceMap = {
|
|
93
112
|
["Patient" /* PATIENT */]: ["Patient"],
|
|
94
113
|
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
95
|
-
["
|
|
114
|
+
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
115
|
+
// Medication resource is needed for identifying name of medication
|
|
96
116
|
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
97
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization"],
|
|
117
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
118
|
+
// Immunization can include Organization as a related resource
|
|
98
119
|
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
99
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["Device"],
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
["
|
|
120
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
121
|
+
// Device resource is used for medical devices name
|
|
122
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
123
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
103
124
|
["FamilyHistorySection" /* FAMILY_HISTORY */]: ["FamilyMemberHistory"],
|
|
104
125
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
|
|
105
|
-
|
|
106
|
-
["
|
|
107
|
-
|
|
108
|
-
["
|
|
109
|
-
|
|
110
|
-
["MedicalHistorySection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
111
|
-
// Medical history is often Condition
|
|
112
|
-
["CarePlanSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
113
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: ["ClinicalImpression"],
|
|
114
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["DocumentReference"]
|
|
115
|
-
// Advance directives are often stored as DocumentReference
|
|
126
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
127
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
128
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
129
|
+
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
130
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
116
131
|
};
|
|
117
132
|
var IPSSectionResourceFilters = {
|
|
118
133
|
// Only include active allergies
|
|
119
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => c.code === "
|
|
120
|
-
// Only include active medication requests/statements
|
|
121
|
-
["MedicationSection" /* MEDICATIONS */]: (resource) => resource.resourceType === "MedicationRequest" && resource.status === "active" || resource.resourceType === "MedicationStatement" && resource.status === "active",
|
|
134
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string"),
|
|
122
135
|
// Only include active problems/conditions
|
|
123
|
-
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => c.code
|
|
136
|
+
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
124
137
|
// Only include completed immunizations
|
|
125
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed",
|
|
138
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
126
139
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
127
140
|
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "vital-signs")),
|
|
128
|
-
// Only include active devices
|
|
129
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => resource.resourceType === "Device" && resource.status === "active",
|
|
130
141
|
// Only include finalized diagnostic reports
|
|
131
|
-
["
|
|
142
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
132
143
|
// Only include completed procedures
|
|
133
|
-
["
|
|
144
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
|
|
134
145
|
// Only include family history resources
|
|
135
146
|
["FamilyHistorySection" /* FAMILY_HISTORY */]: (resource) => resource.resourceType === "FamilyMemberHistory",
|
|
136
147
|
// Only include social history Observations (category.coding contains 'social-history')
|
|
137
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.
|
|
148
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LONIC_CODES).includes(c.code)),
|
|
138
149
|
// Only include pregnancy history Observations (category.coding contains 'pregnancy')
|
|
139
|
-
["
|
|
140
|
-
// Only include functional status
|
|
141
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "
|
|
142
|
-
// Only include
|
|
143
|
-
["
|
|
150
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code?.coding?.some((c) => Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) || resource.valueCodeableConcept?.coding?.some((c) => Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code))),
|
|
151
|
+
// Only include active functional status Conditions or ClinicalImpressions
|
|
152
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string") || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
|
|
153
|
+
// Only include resolved medical history Conditions
|
|
154
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
|
|
144
155
|
// Only include active care plans
|
|
145
|
-
["
|
|
146
|
-
// Only include
|
|
147
|
-
["
|
|
156
|
+
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
157
|
+
// Only include active advance directives (Consent resources)
|
|
158
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active",
|
|
148
159
|
// Patient section: only Patient resource
|
|
149
160
|
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
|
|
150
161
|
};
|
|
@@ -186,8 +197,6 @@ var TemplateUtilities = class {
|
|
|
186
197
|
}
|
|
187
198
|
if (cc.text) {
|
|
188
199
|
return cc.text;
|
|
189
|
-
} else if ("display" in cc && cc.display) {
|
|
190
|
-
return cc.display;
|
|
191
200
|
} else if (cc.coding && cc.coding[0]) {
|
|
192
201
|
if (cc.coding[0].display) {
|
|
193
202
|
return cc.coding[0].display;
|
|
@@ -219,8 +228,8 @@ var TemplateUtilities = class {
|
|
|
219
228
|
*/
|
|
220
229
|
renderDevice(deviceRef) {
|
|
221
230
|
const device = deviceRef && this.resolveReference(deviceRef);
|
|
222
|
-
if (device && device.resourceType === "Device" && device.
|
|
223
|
-
return this.
|
|
231
|
+
if (device && device.resourceType === "Device" && device.deviceName && device.deviceName.length > 0) {
|
|
232
|
+
return this.safeConcat(device.deviceName, "name");
|
|
224
233
|
}
|
|
225
234
|
return "";
|
|
226
235
|
}
|
|
@@ -401,8 +410,12 @@ var TemplateUtilities = class {
|
|
|
401
410
|
}
|
|
402
411
|
const texts = [];
|
|
403
412
|
for (const item of list) {
|
|
404
|
-
if (item && item.manifestation && item.manifestation[0]
|
|
405
|
-
|
|
413
|
+
if (item && item.manifestation && item.manifestation[0]) {
|
|
414
|
+
if (item.manifestation[0].text) {
|
|
415
|
+
texts.push(item.manifestation[0].text);
|
|
416
|
+
} else if (item.manifestation[0].coding && item.manifestation[0].coding[0]?.display) {
|
|
417
|
+
texts.push(item.manifestation[0].coding[0].display);
|
|
418
|
+
}
|
|
406
419
|
}
|
|
407
420
|
}
|
|
408
421
|
return texts.join(", ");
|
|
@@ -561,10 +574,29 @@ var TemplateUtilities = class {
|
|
|
561
574
|
}
|
|
562
575
|
return null;
|
|
563
576
|
}
|
|
577
|
+
extractPregnancyStatus(observation) {
|
|
578
|
+
let status = "";
|
|
579
|
+
observation.code?.coding?.forEach((c) => {
|
|
580
|
+
if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) {
|
|
581
|
+
status = PREGNANCY_LONIC_CODES.PREGNANCY_STATUS[c.code];
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
if (observation.valueCodeableConcept) {
|
|
585
|
+
observation.valueCodeableConcept.coding?.forEach((c) => {
|
|
586
|
+
if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
|
|
587
|
+
if (status) {
|
|
588
|
+
status += " - ";
|
|
589
|
+
}
|
|
590
|
+
status += PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME[c.code];
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
return status;
|
|
595
|
+
}
|
|
564
596
|
formatQuantityValue(quantity) {
|
|
565
597
|
if (!quantity) return "";
|
|
566
598
|
const parts = [];
|
|
567
|
-
if (quantity.comparator) parts.push(quantity.comparator);
|
|
599
|
+
if (quantity.comparator) parts.push(`${quantity.comparator}`.replace(/</g, "<").replace(/>/g, ">"));
|
|
568
600
|
if (quantity.value !== void 0) parts.push(quantity.value.toString());
|
|
569
601
|
if (quantity.unit) parts.push(quantity.unit);
|
|
570
602
|
return parts.join(" ").trim();
|
|
@@ -799,7 +831,6 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
799
831
|
const patient = entry.resource;
|
|
800
832
|
html += `
|
|
801
833
|
<div>
|
|
802
|
-
<h2>Patient Summary</h2>
|
|
803
834
|
<ul>
|
|
804
835
|
<li><strong>Name(s):</strong>${this.renderNames(patient)}</li>
|
|
805
836
|
<li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
|
|
@@ -949,10 +980,20 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
949
980
|
}
|
|
950
981
|
}
|
|
951
982
|
}
|
|
983
|
+
activeAllergies.sort((a, b) => {
|
|
984
|
+
const dateA = a.onsetDateTime;
|
|
985
|
+
const dateB = b.onsetDateTime;
|
|
986
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
987
|
+
});
|
|
988
|
+
resolvedAllergies.sort((a, b) => {
|
|
989
|
+
const dateA = a.onsetDateTime;
|
|
990
|
+
const dateB = b.onsetDateTime;
|
|
991
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
992
|
+
});
|
|
952
993
|
let html = "";
|
|
953
994
|
html += `
|
|
954
995
|
<div class="ActiveAllergies">
|
|
955
|
-
<h3>Active
|
|
996
|
+
<h3>Active</h3>
|
|
956
997
|
<table class="ActiveAllergyTable">
|
|
957
998
|
<thead>
|
|
958
999
|
<tr>
|
|
@@ -980,7 +1021,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
980
1021
|
</div>`;
|
|
981
1022
|
html += `
|
|
982
1023
|
<div class="ResolvedAllergies">
|
|
983
|
-
<h3>Resolved
|
|
1024
|
+
<h3>Resolved</h3>
|
|
984
1025
|
<table class="ResolvedAllergyTable">
|
|
985
1026
|
<thead>
|
|
986
1027
|
<tr>
|
|
@@ -1024,7 +1065,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1024
1065
|
<tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
|
|
1025
1066
|
<td class="Name"><span class="AllergenName">${templateUtilities.codeableConcept(allergy.code)}</span></td>
|
|
1026
1067
|
<td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
|
|
1027
|
-
<td class="Category">${templateUtilities.safeConcat(allergy.category
|
|
1068
|
+
<td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
|
|
1028
1069
|
<td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
|
|
1029
1070
|
<td class="Severity">${templateUtilities.safeConcat(allergy.reaction, "severity") || "-"}</td>
|
|
1030
1071
|
<td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
|
|
@@ -1059,6 +1100,67 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1059
1100
|
generateNarrative(resource, timezone) {
|
|
1060
1101
|
return _MedicationSummaryTemplate.generateStaticNarrative(resource, timezone);
|
|
1061
1102
|
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Safely parse a date string and return a valid Date object or null
|
|
1105
|
+
* @param dateString - The date string to parse
|
|
1106
|
+
* @returns Date object or null if invalid
|
|
1107
|
+
*/
|
|
1108
|
+
static parseDate(dateString) {
|
|
1109
|
+
if (!dateString || dateString.trim() === "") {
|
|
1110
|
+
return null;
|
|
1111
|
+
}
|
|
1112
|
+
const date = new Date(dateString);
|
|
1113
|
+
return !isNaN(date.getTime()) ? date : null;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Determine if a MedicationRequest is active
|
|
1117
|
+
* @param medicationRequest - The MedicationRequest resource
|
|
1118
|
+
* @returns boolean indicating if the medication request is active
|
|
1119
|
+
*/
|
|
1120
|
+
static isActiveMedicationRequest(medicationRequest) {
|
|
1121
|
+
const status = medicationRequest.status?.toLowerCase();
|
|
1122
|
+
if (status === "active" || status === "unknown") {
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
if (status === "completed" || status === "cancelled" || status === "stopped" || status === "draft") {
|
|
1126
|
+
return false;
|
|
1127
|
+
}
|
|
1128
|
+
const endDate = medicationRequest.dispenseRequest?.validityPeriod?.end;
|
|
1129
|
+
if (!endDate) {
|
|
1130
|
+
return true;
|
|
1131
|
+
}
|
|
1132
|
+
const parsedEndDate = this.parseDate(endDate);
|
|
1133
|
+
if (!parsedEndDate) {
|
|
1134
|
+
return true;
|
|
1135
|
+
}
|
|
1136
|
+
return parsedEndDate.getTime() > Date.now();
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Determine if a MedicationStatement is active
|
|
1140
|
+
* @param medicationStatement - The MedicationStatement resource
|
|
1141
|
+
* @returns boolean indicating if the medication statement is active
|
|
1142
|
+
*/
|
|
1143
|
+
static isActiveMedicationStatement(medicationStatement) {
|
|
1144
|
+
const status = medicationStatement.status?.toLowerCase();
|
|
1145
|
+
if (status === "active" || status === "intended" || status === "unknown") {
|
|
1146
|
+
return true;
|
|
1147
|
+
}
|
|
1148
|
+
if (status === "completed" || status === "stopped" || status === "not-taken") {
|
|
1149
|
+
return false;
|
|
1150
|
+
}
|
|
1151
|
+
let endDate;
|
|
1152
|
+
if (medicationStatement.effectivePeriod?.end) {
|
|
1153
|
+
endDate = medicationStatement.effectivePeriod.end;
|
|
1154
|
+
}
|
|
1155
|
+
if (!endDate) {
|
|
1156
|
+
return true;
|
|
1157
|
+
}
|
|
1158
|
+
const parsedEndDate = this.parseDate(endDate);
|
|
1159
|
+
if (!parsedEndDate) {
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
1162
|
+
return parsedEndDate.getTime() > Date.now();
|
|
1163
|
+
}
|
|
1062
1164
|
/**
|
|
1063
1165
|
* Internal static implementation that actually generates the narrative
|
|
1064
1166
|
* @param resource - FHIR Bundle containing Medication resources
|
|
@@ -1070,12 +1172,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1070
1172
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1071
1173
|
let html = "";
|
|
1072
1174
|
const medicationRequests = this.getMedicationRequests(templateUtilities, resource);
|
|
1073
|
-
if (medicationRequests.length > 0) {
|
|
1074
|
-
html += this.renderMedicationRequests(templateUtilities, medicationRequests);
|
|
1075
|
-
}
|
|
1076
1175
|
const medicationStatements = this.getMedicationStatements(templateUtilities, resource);
|
|
1077
|
-
|
|
1078
|
-
|
|
1176
|
+
const allActiveMedications = [];
|
|
1177
|
+
const allInactiveMedications = [];
|
|
1178
|
+
medicationRequests.forEach((mr) => {
|
|
1179
|
+
if (this.isActiveMedicationRequest(mr.resource)) {
|
|
1180
|
+
allActiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1181
|
+
} else {
|
|
1182
|
+
allInactiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1183
|
+
}
|
|
1184
|
+
});
|
|
1185
|
+
medicationStatements.forEach((ms) => {
|
|
1186
|
+
if (this.isActiveMedicationStatement(ms.resource)) {
|
|
1187
|
+
allActiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1188
|
+
} else {
|
|
1189
|
+
allInactiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
const sortMedications = (medications) => {
|
|
1193
|
+
medications.sort((a, b) => {
|
|
1194
|
+
let dateStringA;
|
|
1195
|
+
let dateStringB;
|
|
1196
|
+
if (a.type === "request") {
|
|
1197
|
+
const mr = a.resource;
|
|
1198
|
+
dateStringA = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
1199
|
+
} else {
|
|
1200
|
+
const ms = a.resource;
|
|
1201
|
+
dateStringA = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
1202
|
+
}
|
|
1203
|
+
if (b.type === "request") {
|
|
1204
|
+
const mr = b.resource;
|
|
1205
|
+
dateStringB = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
1206
|
+
} else {
|
|
1207
|
+
const ms = b.resource;
|
|
1208
|
+
dateStringB = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
1209
|
+
}
|
|
1210
|
+
const dateA = this.parseDate(dateStringA);
|
|
1211
|
+
const dateB = this.parseDate(dateStringB);
|
|
1212
|
+
if (!dateA && !dateB) return 0;
|
|
1213
|
+
if (!dateA) return 1;
|
|
1214
|
+
if (!dateB) return -1;
|
|
1215
|
+
return dateB.getTime() - dateA.getTime();
|
|
1216
|
+
});
|
|
1217
|
+
};
|
|
1218
|
+
if (allActiveMedications.length > 0) {
|
|
1219
|
+
sortMedications(allActiveMedications);
|
|
1220
|
+
html += this.renderCombinedMedications(templateUtilities, allActiveMedications, "Active Medications");
|
|
1221
|
+
}
|
|
1222
|
+
if (allInactiveMedications.length > 0) {
|
|
1223
|
+
sortMedications(allInactiveMedications);
|
|
1224
|
+
html += this.renderCombinedMedications(templateUtilities, allInactiveMedications, "Inactive Medications");
|
|
1079
1225
|
}
|
|
1080
1226
|
return html;
|
|
1081
1227
|
}
|
|
@@ -1110,16 +1256,19 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1110
1256
|
}));
|
|
1111
1257
|
}
|
|
1112
1258
|
/**
|
|
1113
|
-
* Render HTML table for MedicationRequest resources
|
|
1259
|
+
* Render HTML table for combined MedicationRequest and MedicationStatement resources
|
|
1114
1260
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1115
|
-
* @param medications - Array of
|
|
1261
|
+
* @param medications - Array of combined medication resources
|
|
1262
|
+
* @param sectionTitle - Title for the section
|
|
1116
1263
|
* @returns HTML string for rendering
|
|
1117
1264
|
*/
|
|
1118
|
-
static
|
|
1265
|
+
static renderCombinedMedications(templateUtilities, medications, sectionTitle) {
|
|
1119
1266
|
let html = `
|
|
1267
|
+
<h3>${sectionTitle}</h3>
|
|
1120
1268
|
<table>
|
|
1121
1269
|
<thead>
|
|
1122
1270
|
<tr>
|
|
1271
|
+
<th>Type</th>
|
|
1123
1272
|
<th>Medication</th>
|
|
1124
1273
|
<th>Sig</th>
|
|
1125
1274
|
<th>Dispense Quantity</th>
|
|
@@ -1130,86 +1279,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1130
1279
|
</tr>
|
|
1131
1280
|
</thead>
|
|
1132
1281
|
<tbody>`;
|
|
1133
|
-
for (const
|
|
1134
|
-
const narrativeLinkId = templateUtilities.narrativeLinkId(extension);
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
);
|
|
1139
|
-
const sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
|
|
1282
|
+
for (const medication of medications) {
|
|
1283
|
+
const narrativeLinkId = templateUtilities.narrativeLinkId(medication.extension);
|
|
1284
|
+
let type;
|
|
1285
|
+
let medicationName;
|
|
1286
|
+
let sig;
|
|
1140
1287
|
let dispenseQuantity = "-";
|
|
1141
|
-
|
|
1142
|
-
const quantity = mr.dispenseRequest.quantity;
|
|
1143
|
-
if (quantity.value) {
|
|
1144
|
-
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
const refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
|
|
1288
|
+
let refills = "-";
|
|
1148
1289
|
let startDate = "-";
|
|
1149
1290
|
let endDate = "-";
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1291
|
+
let status;
|
|
1292
|
+
if (medication.type === "request") {
|
|
1293
|
+
const mr = medication.resource;
|
|
1294
|
+
type = "Request";
|
|
1295
|
+
status = mr.status ? String(mr.status) : "-";
|
|
1296
|
+
medicationName = templateUtilities.getMedicationName(
|
|
1297
|
+
mr.medicationReference || mr.medicationCodeableConcept
|
|
1298
|
+
);
|
|
1299
|
+
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
|
|
1300
|
+
if (mr.dispenseRequest?.quantity) {
|
|
1301
|
+
const quantity = mr.dispenseRequest.quantity;
|
|
1302
|
+
if (quantity.value) {
|
|
1303
|
+
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
|
|
1307
|
+
if (mr.dispenseRequest?.validityPeriod) {
|
|
1308
|
+
startDate = mr.dispenseRequest.validityPeriod.start || "-";
|
|
1309
|
+
endDate = mr.dispenseRequest.validityPeriod.end || "-";
|
|
1310
|
+
} else {
|
|
1311
|
+
startDate = mr.authoredOn || "-";
|
|
1312
|
+
}
|
|
1153
1313
|
} else {
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
html += `
|
|
1168
|
-
</tbody>
|
|
1169
|
-
</table>`;
|
|
1170
|
-
return html;
|
|
1171
|
-
}
|
|
1172
|
-
/**
|
|
1173
|
-
* Render HTML table for MedicationStatement resources
|
|
1174
|
-
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1175
|
-
* @param medications - Array of MedicationStatement resources
|
|
1176
|
-
* @returns HTML string for rendering
|
|
1177
|
-
*/
|
|
1178
|
-
static renderMedicationStatements(templateUtilities, medications) {
|
|
1179
|
-
let html = `
|
|
1180
|
-
<table>
|
|
1181
|
-
<thead>
|
|
1182
|
-
<tr>
|
|
1183
|
-
<th>Medication</th>
|
|
1184
|
-
<th>Sig</th>
|
|
1185
|
-
<th>Dispense Quantity</th>
|
|
1186
|
-
<th>Refills</th>
|
|
1187
|
-
<th>Start Date</th>
|
|
1188
|
-
<th>End Date</th>
|
|
1189
|
-
<th>Status</th>
|
|
1190
|
-
</tr>
|
|
1191
|
-
</thead>
|
|
1192
|
-
<tbody>`;
|
|
1193
|
-
for (const { resource: ms, extension } of medications) {
|
|
1194
|
-
const narrativeLinkId = templateUtilities.narrativeLinkId(extension);
|
|
1195
|
-
const status = ms.status ? String(ms.status) : "-";
|
|
1196
|
-
const medication = templateUtilities.getMedicationName(
|
|
1197
|
-
ms.medicationReference || ms.medicationCodeableConcept
|
|
1198
|
-
);
|
|
1199
|
-
const sig = templateUtilities.concat(ms.dosage, "text") || "-";
|
|
1200
|
-
const dispenseQuantity = "-";
|
|
1201
|
-
const refills = "-";
|
|
1202
|
-
let startDate = "-";
|
|
1203
|
-
let endDate = "-";
|
|
1204
|
-
if (ms.effectiveDateTime) {
|
|
1205
|
-
startDate = ms.effectiveDateTime;
|
|
1206
|
-
} else if (ms.effectivePeriod) {
|
|
1207
|
-
startDate = ms.effectivePeriod.start || "-";
|
|
1208
|
-
endDate = ms.effectivePeriod.end || "-";
|
|
1314
|
+
const ms = medication.resource;
|
|
1315
|
+
type = "Statement";
|
|
1316
|
+
status = ms.status ? String(ms.status) : "-";
|
|
1317
|
+
medicationName = templateUtilities.getMedicationName(
|
|
1318
|
+
ms.medicationReference || ms.medicationCodeableConcept
|
|
1319
|
+
);
|
|
1320
|
+
sig = templateUtilities.concat(ms.dosage, "text") || "-";
|
|
1321
|
+
if (ms.effectiveDateTime) {
|
|
1322
|
+
startDate = ms.effectiveDateTime;
|
|
1323
|
+
} else if (ms.effectivePeriod) {
|
|
1324
|
+
startDate = ms.effectivePeriod.start || "-";
|
|
1325
|
+
endDate = ms.effectivePeriod.end || "-";
|
|
1326
|
+
}
|
|
1209
1327
|
}
|
|
1210
1328
|
html += `
|
|
1211
1329
|
<tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
|
|
1212
|
-
<td>${
|
|
1330
|
+
<td>${type}</td>
|
|
1331
|
+
<td>${medicationName}<ul></ul></td>
|
|
1213
1332
|
<td>${sig}</td>
|
|
1214
1333
|
<td>${dispenseQuantity}</td>
|
|
1215
1334
|
<td>${refills}</td>
|
|
@@ -1234,6 +1353,13 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1234
1353
|
* @returns HTML string for rendering
|
|
1235
1354
|
*/
|
|
1236
1355
|
generateNarrative(resource, timezone) {
|
|
1356
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1357
|
+
resource.entry.sort((a, b) => {
|
|
1358
|
+
const dateA = a.resource?.occurrenceDateTime;
|
|
1359
|
+
const dateB = b.resource?.occurrenceDateTime;
|
|
1360
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1237
1363
|
return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
|
|
1238
1364
|
}
|
|
1239
1365
|
/**
|
|
@@ -1245,7 +1371,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1245
1371
|
static generateStaticNarrative(resource, timezone) {
|
|
1246
1372
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1247
1373
|
let html = `
|
|
1248
|
-
<h5>Immunizations</h5>
|
|
1249
1374
|
<table>
|
|
1250
1375
|
<thead>
|
|
1251
1376
|
<tr>
|
|
@@ -1303,81 +1428,35 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1303
1428
|
static generateStaticNarrative(resource, timezone) {
|
|
1304
1429
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1305
1430
|
let html = ``;
|
|
1306
|
-
const activeConditions = [];
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
const isResolved = cond.clinicalStatus?.coding?.some((c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved"));
|
|
1315
|
-
if (isResolved) {
|
|
1316
|
-
resolvedConditions.push(cond);
|
|
1317
|
-
} else {
|
|
1318
|
-
activeConditions.push(cond);
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
if (activeConditions.length > 0) {
|
|
1323
|
-
html += `<div class="ActiveProblems">
|
|
1324
|
-
<h3>Active Problems</h3>
|
|
1325
|
-
<table class="ActiveProblemTable">
|
|
1326
|
-
<thead>
|
|
1327
|
-
<tr>
|
|
1328
|
-
<th>Problem</th>
|
|
1329
|
-
<th>Priority</th>
|
|
1330
|
-
<th>Noted Date</th>
|
|
1331
|
-
<th>Diagnosed Date</th>
|
|
1332
|
-
<th>Notes</th>
|
|
1333
|
-
</tr>
|
|
1334
|
-
</thead>
|
|
1335
|
-
<tbody>`;
|
|
1336
|
-
for (const cond of activeConditions) {
|
|
1337
|
-
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1338
|
-
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1339
|
-
<td class="Priority">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1340
|
-
<td class="NotedDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1341
|
-
<td class="DiagnosedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1342
|
-
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
1343
|
-
</tr>`;
|
|
1344
|
-
}
|
|
1345
|
-
html += `</tbody>
|
|
1346
|
-
</table>
|
|
1347
|
-
</div>`;
|
|
1348
|
-
}
|
|
1349
|
-
if (activeConditions.length > 0 && resolvedConditions.length > 0) {
|
|
1350
|
-
html += `<br />`;
|
|
1351
|
-
}
|
|
1352
|
-
if (resolvedConditions.length > 0) {
|
|
1353
|
-
html += `<div class="ResolvedProblems">
|
|
1354
|
-
<h3>Resolved Problems</h3>
|
|
1355
|
-
<table class="ResolvedProblemTable">
|
|
1431
|
+
const activeConditions = resource.entry?.map((entry) => entry.resource) || [];
|
|
1432
|
+
activeConditions.sort((a, b) => {
|
|
1433
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1434
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1435
|
+
return dateB - dateA;
|
|
1436
|
+
});
|
|
1437
|
+
html += `
|
|
1438
|
+
<table>
|
|
1356
1439
|
<thead>
|
|
1357
1440
|
<tr>
|
|
1358
1441
|
<th>Problem</th>
|
|
1359
|
-
<th>
|
|
1360
|
-
<th>
|
|
1361
|
-
<th>
|
|
1362
|
-
<th>Resolved Date</th>
|
|
1442
|
+
<th>Severity</th>
|
|
1443
|
+
<th>Onset Date</th>
|
|
1444
|
+
<th>Recorded Date</th>
|
|
1363
1445
|
<th>Notes</th>
|
|
1364
1446
|
</tr>
|
|
1365
1447
|
</thead>
|
|
1366
1448
|
<tbody>`;
|
|
1367
|
-
|
|
1368
|
-
|
|
1449
|
+
for (const cond of activeConditions) {
|
|
1450
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1369
1451
|
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1370
|
-
<td class="
|
|
1371
|
-
<td class="
|
|
1372
|
-
<td class="
|
|
1373
|
-
<td class="
|
|
1374
|
-
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
1452
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1453
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1454
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1455
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1375
1456
|
</tr>`;
|
|
1376
|
-
}
|
|
1377
|
-
html += `</tbody>
|
|
1378
|
-
</table>
|
|
1379
|
-
</div>`;
|
|
1380
1457
|
}
|
|
1458
|
+
html += `</tbody>
|
|
1459
|
+
</table>`;
|
|
1381
1460
|
return html;
|
|
1382
1461
|
}
|
|
1383
1462
|
};
|
|
@@ -1401,8 +1480,13 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1401
1480
|
*/
|
|
1402
1481
|
static generateStaticNarrative(resource, timezone) {
|
|
1403
1482
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1483
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
1484
|
+
observations.sort((a, b) => {
|
|
1485
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1486
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1487
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1488
|
+
});
|
|
1404
1489
|
let html = `
|
|
1405
|
-
<h5>Vital Signs</h5>
|
|
1406
1490
|
<table>
|
|
1407
1491
|
<thead>
|
|
1408
1492
|
<tr>
|
|
@@ -1416,13 +1500,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1416
1500
|
</tr>
|
|
1417
1501
|
</thead>
|
|
1418
1502
|
<tbody>`;
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
const obs = entry.resource;
|
|
1422
|
-
if (obs.resourceType === "Composition") {
|
|
1423
|
-
continue;
|
|
1424
|
-
}
|
|
1425
|
-
html += `
|
|
1503
|
+
for (const obs of observations) {
|
|
1504
|
+
html += `
|
|
1426
1505
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1427
1506
|
<td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
|
|
1428
1507
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
@@ -1430,9 +1509,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1430
1509
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
1431
1510
|
<td>${templateUtilities.renderComponent(obs.component)}</td>
|
|
1432
1511
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1433
|
-
<td>${templateUtilities.
|
|
1512
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1434
1513
|
</tr>`;
|
|
1435
|
-
}
|
|
1436
1514
|
}
|
|
1437
1515
|
html += `
|
|
1438
1516
|
</tbody>
|
|
@@ -1450,6 +1528,13 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1450
1528
|
* @returns HTML string for rendering
|
|
1451
1529
|
*/
|
|
1452
1530
|
generateNarrative(resource, timezone) {
|
|
1531
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1532
|
+
resource.entry.sort((a, b) => {
|
|
1533
|
+
const dateA = a.resource?.recordedOn;
|
|
1534
|
+
const dateB = b.resource?.recordedOn;
|
|
1535
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1453
1538
|
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1454
1539
|
}
|
|
1455
1540
|
/**
|
|
@@ -1461,7 +1546,6 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1461
1546
|
static generateStaticNarrative(resource, timezone) {
|
|
1462
1547
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1463
1548
|
let html = `
|
|
1464
|
-
<h5>Medical Devices</h5>
|
|
1465
1549
|
<table>
|
|
1466
1550
|
<thead>
|
|
1467
1551
|
<tr>
|
|
@@ -1515,10 +1599,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1515
1599
|
let html = "";
|
|
1516
1600
|
const observations = this.getObservations(resource);
|
|
1517
1601
|
if (observations.length > 0) {
|
|
1602
|
+
observations.sort((a, b) => {
|
|
1603
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1604
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1605
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1606
|
+
});
|
|
1518
1607
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1519
1608
|
}
|
|
1520
1609
|
const diagnosticReports = this.getDiagnosticReports(resource);
|
|
1521
1610
|
if (diagnosticReports.length > 0) {
|
|
1611
|
+
diagnosticReports.sort((a, b) => {
|
|
1612
|
+
const dateA = a.issued;
|
|
1613
|
+
const dateB = b.issued;
|
|
1614
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1615
|
+
});
|
|
1522
1616
|
html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
|
|
1523
1617
|
}
|
|
1524
1618
|
return html;
|
|
@@ -1554,7 +1648,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1554
1648
|
*/
|
|
1555
1649
|
static renderObservations(templateUtilities, observations, timezone) {
|
|
1556
1650
|
let html = `
|
|
1557
|
-
<h5>
|
|
1651
|
+
<h5>Observations</h5>
|
|
1558
1652
|
<table>
|
|
1559
1653
|
<thead>
|
|
1560
1654
|
<tr>
|
|
@@ -1577,7 +1671,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1577
1671
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
1578
1672
|
<td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
|
|
1579
1673
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1580
|
-
<td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
|
|
1674
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1581
1675
|
</tr>`;
|
|
1582
1676
|
}
|
|
1583
1677
|
html += `
|
|
@@ -1594,7 +1688,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1594
1688
|
*/
|
|
1595
1689
|
static renderDiagnosticReports(templateUtilities, reports, timezone) {
|
|
1596
1690
|
let html = `
|
|
1597
|
-
<h5>Diagnostic
|
|
1691
|
+
<h5>Diagnostic Reports</h5>
|
|
1598
1692
|
<table>
|
|
1599
1693
|
<thead>
|
|
1600
1694
|
<tr>
|
|
@@ -1636,6 +1730,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1636
1730
|
* @returns HTML string for rendering
|
|
1637
1731
|
*/
|
|
1638
1732
|
generateNarrative(resource, timezone) {
|
|
1733
|
+
resource.entry?.sort((a, b) => {
|
|
1734
|
+
const dateA = a.resource.performedDateTime || a.resource.performedPeriod?.start;
|
|
1735
|
+
const dateB = b.resource.performedDateTime || b.resource.performedPeriod?.start;
|
|
1736
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1737
|
+
});
|
|
1639
1738
|
return _HistoryOfProceduresTemplate.generateStaticNarrative(resource, timezone);
|
|
1640
1739
|
}
|
|
1641
1740
|
/**
|
|
@@ -1647,7 +1746,6 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1647
1746
|
static generateStaticNarrative(resource, timezone) {
|
|
1648
1747
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1649
1748
|
let html = `
|
|
1650
|
-
<h5>History Of Procedures</h5>
|
|
1651
1749
|
<table>
|
|
1652
1750
|
<thead>
|
|
1653
1751
|
<tr>
|
|
@@ -1660,14 +1758,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1660
1758
|
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1661
1759
|
for (const entry of resource.entry) {
|
|
1662
1760
|
const proc = entry.resource;
|
|
1663
|
-
if (proc.resourceType === "Composition") {
|
|
1664
|
-
continue;
|
|
1665
|
-
}
|
|
1666
1761
|
html += `
|
|
1667
1762
|
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
1668
1763
|
<td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
|
|
1669
1764
|
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
1670
|
-
<td>${templateUtilities.renderTime(proc.performedDateTime, timezone)}</td>
|
|
1765
|
+
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
1671
1766
|
</tr>`;
|
|
1672
1767
|
}
|
|
1673
1768
|
}
|
|
@@ -1697,8 +1792,13 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1697
1792
|
*/
|
|
1698
1793
|
static generateStaticNarrative(resource, timezone) {
|
|
1699
1794
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1795
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
1796
|
+
observations.sort((a, b) => {
|
|
1797
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1798
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1799
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1800
|
+
});
|
|
1700
1801
|
let html = `
|
|
1701
|
-
<h5>Social History</h5>
|
|
1702
1802
|
<table>
|
|
1703
1803
|
<thead>
|
|
1704
1804
|
<tr>
|
|
@@ -1710,21 +1810,15 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1710
1810
|
</tr>
|
|
1711
1811
|
</thead>
|
|
1712
1812
|
<tbody>`;
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
const obs = entry.resource;
|
|
1716
|
-
if (obs.resourceType === "Composition") {
|
|
1717
|
-
continue;
|
|
1718
|
-
}
|
|
1719
|
-
html += `
|
|
1813
|
+
for (const obs of observations) {
|
|
1814
|
+
html += `
|
|
1720
1815
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1721
|
-
<td>${templateUtilities.codeableConcept(obs.code
|
|
1816
|
+
<td>${templateUtilities.codeableConcept(obs.code)}</td>
|
|
1722
1817
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
1723
1818
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
1724
1819
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1725
|
-
<td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
|
|
1820
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1726
1821
|
</tr>`;
|
|
1727
|
-
}
|
|
1728
1822
|
}
|
|
1729
1823
|
html += `
|
|
1730
1824
|
</tbody>
|
|
@@ -1743,36 +1837,38 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1743
1837
|
*/
|
|
1744
1838
|
generateNarrative(resource, timezone) {
|
|
1745
1839
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1746
|
-
let html =
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
<th>Comments</th>
|
|
1754
|
-
<th>Date</th>
|
|
1755
|
-
</tr>
|
|
1756
|
-
</thead>
|
|
1757
|
-
<tbody>`;
|
|
1758
|
-
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1759
|
-
for (const entry of resource.entry) {
|
|
1760
|
-
const cond = entry.resource;
|
|
1761
|
-
if (cond.resourceType === "Composition") {
|
|
1762
|
-
continue;
|
|
1763
|
-
}
|
|
1764
|
-
html += `
|
|
1765
|
-
<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1766
|
-
<td>${templateUtilities.codeableConcept(cond.code, "display")}</td>
|
|
1767
|
-
<td>${templateUtilities.codeableConcept(cond.clinicalStatus, "code")}</td>
|
|
1768
|
-
<td>${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1769
|
-
<td>${templateUtilities.renderTime(cond.onsetDateTime, timezone)}</td>
|
|
1770
|
-
</tr>`;
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1840
|
+
let html = ``;
|
|
1841
|
+
const resolvedConditions = resource.entry?.map((entry) => entry.resource) || [];
|
|
1842
|
+
resolvedConditions.sort((a, b) => {
|
|
1843
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1844
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1845
|
+
return dateB - dateA;
|
|
1846
|
+
});
|
|
1773
1847
|
html += `
|
|
1774
|
-
|
|
1775
|
-
|
|
1848
|
+
<table>
|
|
1849
|
+
<thead>
|
|
1850
|
+
<tr>
|
|
1851
|
+
<th>Problem</th>
|
|
1852
|
+
<th>Severity</th>
|
|
1853
|
+
<th>Onset Date</th>
|
|
1854
|
+
<th>Recorded Date</th>
|
|
1855
|
+
<th>Resolved Date</th>
|
|
1856
|
+
<th>Notes</th>
|
|
1857
|
+
</tr>
|
|
1858
|
+
</thead>
|
|
1859
|
+
<tbody>`;
|
|
1860
|
+
for (const cond of resolvedConditions) {
|
|
1861
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1862
|
+
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1863
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1864
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1865
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1866
|
+
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
1867
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1868
|
+
</tr>`;
|
|
1869
|
+
}
|
|
1870
|
+
html += `</tbody>
|
|
1871
|
+
</table>`;
|
|
1776
1872
|
return html;
|
|
1777
1873
|
}
|
|
1778
1874
|
};
|
|
@@ -1785,15 +1881,19 @@ var PlanOfCareTemplate = class {
|
|
|
1785
1881
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1786
1882
|
* @returns HTML string for rendering
|
|
1787
1883
|
*/
|
|
1788
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1789
1884
|
generateNarrative(resource, timezone) {
|
|
1790
1885
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1886
|
+
const carePlans = resource.entry?.map((entry) => entry.resource) || [];
|
|
1887
|
+
carePlans.sort((a, b) => {
|
|
1888
|
+
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1889
|
+
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
1890
|
+
return endB - endA;
|
|
1891
|
+
});
|
|
1791
1892
|
let html = `
|
|
1792
|
-
<h5>Plan of Care</h5>
|
|
1793
1893
|
<table>
|
|
1794
1894
|
<thead>
|
|
1795
1895
|
<tr>
|
|
1796
|
-
<th>
|
|
1896
|
+
<th>Description</th>
|
|
1797
1897
|
<th>Intent</th>
|
|
1798
1898
|
<th>Comments</th>
|
|
1799
1899
|
<th>Planned Start</th>
|
|
@@ -1801,21 +1901,15 @@ var PlanOfCareTemplate = class {
|
|
|
1801
1901
|
</tr>
|
|
1802
1902
|
</thead>
|
|
1803
1903
|
<tbody>`;
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
const cp = entry.resource;
|
|
1807
|
-
if (cp.resourceType === "Composition") {
|
|
1808
|
-
continue;
|
|
1809
|
-
}
|
|
1810
|
-
html += `
|
|
1904
|
+
for (const cp of carePlans) {
|
|
1905
|
+
html += `
|
|
1811
1906
|
<tr id="${templateUtilities.narrativeLinkId(cp)}">
|
|
1812
|
-
<td>${cp.description || ""}</td>
|
|
1813
|
-
<td>${cp.intent ||
|
|
1907
|
+
<td>${cp.description || cp.title || ""}</td>
|
|
1908
|
+
<td>${cp.intent || ""}</td>
|
|
1814
1909
|
<td>${templateUtilities.concat(cp.note, "text")}</td>
|
|
1815
|
-
<td>${cp.period?.start
|
|
1816
|
-
<td>${cp.period?.end
|
|
1910
|
+
<td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
|
|
1911
|
+
<td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
|
|
1817
1912
|
</tr>`;
|
|
1818
|
-
}
|
|
1819
1913
|
}
|
|
1820
1914
|
html += `
|
|
1821
1915
|
</tbody>
|
|
@@ -1843,38 +1937,111 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
1843
1937
|
*/
|
|
1844
1938
|
static generateStaticNarrative(resource, timezone) {
|
|
1845
1939
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1846
|
-
let html =
|
|
1847
|
-
|
|
1940
|
+
let html = ``;
|
|
1941
|
+
const activeConditions = [];
|
|
1942
|
+
const clinicalImpressions = [];
|
|
1943
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1944
|
+
for (const entry of resource.entry) {
|
|
1945
|
+
if (entry.resource?.resourceType === "Condition") {
|
|
1946
|
+
const cond = entry.resource;
|
|
1947
|
+
const isResolved = cond.clinicalStatus?.coding?.some(
|
|
1948
|
+
(c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
|
|
1949
|
+
);
|
|
1950
|
+
if (!isResolved) {
|
|
1951
|
+
activeConditions.push(cond);
|
|
1952
|
+
}
|
|
1953
|
+
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
1954
|
+
clinicalImpressions.push(entry.resource);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
activeConditions.sort((a, b) => {
|
|
1959
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1960
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1961
|
+
return dateB - dateA;
|
|
1962
|
+
});
|
|
1963
|
+
clinicalImpressions.sort((a, b) => {
|
|
1964
|
+
const dateA = a.effectiveDateTime ? new Date(a.effectiveDateTime).getTime() : a.effectivePeriod?.start ? new Date(a.effectivePeriod.start).getTime() : a.date ? new Date(a.date).getTime() : 0;
|
|
1965
|
+
const dateB = b.effectiveDateTime ? new Date(b.effectiveDateTime).getTime() : b.effectivePeriod?.start ? new Date(b.effectivePeriod.start).getTime() : b.date ? new Date(b.date).getTime() : 0;
|
|
1966
|
+
return dateB - dateA;
|
|
1967
|
+
});
|
|
1968
|
+
if (activeConditions.length > 0) {
|
|
1969
|
+
html += `<h3>Conditions</h3>
|
|
1970
|
+
<table>
|
|
1971
|
+
<thead>
|
|
1972
|
+
<tr>
|
|
1973
|
+
<th>Problem</th>
|
|
1974
|
+
<th>Severity</th>
|
|
1975
|
+
<th>Onset Date</th>
|
|
1976
|
+
<th>Recorded Date</th>
|
|
1977
|
+
<th>Notes</th>
|
|
1978
|
+
</tr>
|
|
1979
|
+
</thead>
|
|
1980
|
+
<tbody>`;
|
|
1981
|
+
for (const cond of activeConditions) {
|
|
1982
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1983
|
+
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1984
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1985
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1986
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1987
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
1988
|
+
</tr>`;
|
|
1989
|
+
}
|
|
1990
|
+
html += `</tbody>
|
|
1991
|
+
</table>
|
|
1992
|
+
</div>`;
|
|
1993
|
+
}
|
|
1994
|
+
if (clinicalImpressions.length > 0) {
|
|
1995
|
+
html += `<h3>Clinical Impressions</h3>
|
|
1848
1996
|
<table>
|
|
1849
1997
|
<thead>
|
|
1850
1998
|
<tr>
|
|
1851
|
-
<th>Assessment</th>
|
|
1852
|
-
<th>Status</th>
|
|
1853
|
-
<th>Finding</th>
|
|
1854
|
-
<th>Comments</th>
|
|
1855
1999
|
<th>Date</th>
|
|
2000
|
+
<th>Status</th>
|
|
2001
|
+
<th>Description</th>
|
|
2002
|
+
<th>Summary</th>
|
|
2003
|
+
<th>Findings</th>
|
|
2004
|
+
<th>Notes</th>
|
|
1856
2005
|
</tr>
|
|
1857
2006
|
</thead>
|
|
1858
2007
|
<tbody>`;
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2008
|
+
for (const impression of clinicalImpressions) {
|
|
2009
|
+
let formattedDate = "";
|
|
2010
|
+
if (impression.effectiveDateTime) {
|
|
2011
|
+
formattedDate = templateUtilities.renderTime(
|
|
2012
|
+
impression.effectiveDateTime,
|
|
2013
|
+
timezone
|
|
2014
|
+
);
|
|
2015
|
+
} else if (impression.effectivePeriod) {
|
|
2016
|
+
formattedDate = templateUtilities.renderPeriod(
|
|
2017
|
+
impression.effectivePeriod,
|
|
2018
|
+
timezone
|
|
2019
|
+
);
|
|
2020
|
+
} else if (impression.date) {
|
|
2021
|
+
formattedDate = templateUtilities.renderDate(impression.date);
|
|
2022
|
+
}
|
|
2023
|
+
let findingsHtml = "";
|
|
2024
|
+
if (impression.finding && impression.finding.length > 0) {
|
|
2025
|
+
findingsHtml = "<ul>";
|
|
2026
|
+
for (const finding of impression.finding) {
|
|
2027
|
+
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
2028
|
+
const cause = finding.basis || "";
|
|
2029
|
+
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
2030
|
+
}
|
|
2031
|
+
findingsHtml += "</ul>";
|
|
1864
2032
|
}
|
|
2033
|
+
const notes = templateUtilities.renderNotes(impression.note, timezone);
|
|
1865
2034
|
html += `
|
|
1866
|
-
<tr id="${templateUtilities.narrativeLinkId(
|
|
1867
|
-
<td>${
|
|
1868
|
-
<td>${
|
|
1869
|
-
<td>${
|
|
1870
|
-
<td>${
|
|
1871
|
-
<td>${
|
|
2035
|
+
<tr id="${templateUtilities.narrativeLinkId(impression)}">
|
|
2036
|
+
<td>${formattedDate}</td>
|
|
2037
|
+
<td>${impression.status || ""}</td>
|
|
2038
|
+
<td>${impression.description || ""}</td>
|
|
2039
|
+
<td>${impression.summary || ""}</td>
|
|
2040
|
+
<td>${findingsHtml}</td>
|
|
2041
|
+
<td>${notes}</td>
|
|
1872
2042
|
</tr>`;
|
|
1873
2043
|
}
|
|
1874
2044
|
}
|
|
1875
|
-
html += `
|
|
1876
|
-
</tbody>
|
|
1877
|
-
</table>`;
|
|
1878
2045
|
return html;
|
|
1879
2046
|
}
|
|
1880
2047
|
};
|
|
@@ -1898,32 +2065,30 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
1898
2065
|
*/
|
|
1899
2066
|
static generateStaticNarrative(resource, timezone) {
|
|
1900
2067
|
const templateUtilities = new TemplateUtilities(resource);
|
|
2068
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
2069
|
+
observations.sort((a, b) => {
|
|
2070
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2071
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
2072
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
2073
|
+
});
|
|
1901
2074
|
let html = `
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
for (const entry of resource.entry) {
|
|
1915
|
-
const obs = entry.resource;
|
|
1916
|
-
if (obs.resourceType === "Composition") {
|
|
1917
|
-
continue;
|
|
1918
|
-
}
|
|
1919
|
-
html += `
|
|
2075
|
+
<table>
|
|
2076
|
+
<thead>
|
|
2077
|
+
<tr>
|
|
2078
|
+
<th>Result</th>
|
|
2079
|
+
<th>Comments</th>
|
|
2080
|
+
<th>Date</th>
|
|
2081
|
+
</tr>
|
|
2082
|
+
</thead>
|
|
2083
|
+
<tbody>`;
|
|
2084
|
+
for (const resource2 of observations) {
|
|
2085
|
+
const obs = resource2;
|
|
2086
|
+
html += `
|
|
1920
2087
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1921
|
-
<td>${templateUtilities.
|
|
1922
|
-
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2088
|
+
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
1923
2089
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1924
|
-
<td>${templateUtilities.
|
|
2090
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1925
2091
|
</tr>`;
|
|
1926
|
-
}
|
|
1927
2092
|
}
|
|
1928
2093
|
html += `
|
|
1929
2094
|
</tbody>
|
|
@@ -1941,6 +2106,13 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1941
2106
|
* @returns HTML string for rendering
|
|
1942
2107
|
*/
|
|
1943
2108
|
generateNarrative(resource, timezone) {
|
|
2109
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
2110
|
+
resource.entry.sort((a, b) => {
|
|
2111
|
+
const dateA = new Date(a.resource.dateTime || 0);
|
|
2112
|
+
const dateB = new Date(b.resource.dateTime || 0);
|
|
2113
|
+
return dateB.getTime() - dateA.getTime();
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
1944
2116
|
return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
|
|
1945
2117
|
}
|
|
1946
2118
|
/**
|
|
@@ -1953,7 +2125,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1953
2125
|
static generateStaticNarrative(resource, timezone) {
|
|
1954
2126
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1955
2127
|
let html = `
|
|
1956
|
-
<h5>Advance Directives</h5>
|
|
1957
2128
|
<table>
|
|
1958
2129
|
<thead>
|
|
1959
2130
|
<tr>
|
|
@@ -1967,9 +2138,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1967
2138
|
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1968
2139
|
for (const entry of resource.entry) {
|
|
1969
2140
|
const consent = entry.resource;
|
|
1970
|
-
if (consent.resourceType === "Composition") {
|
|
1971
|
-
continue;
|
|
1972
|
-
}
|
|
1973
2141
|
html += `
|
|
1974
2142
|
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
1975
2143
|
<td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
|
|
@@ -2067,90 +2235,6 @@ var FamilyHistoryTemplate = class {
|
|
|
2067
2235
|
}
|
|
2068
2236
|
};
|
|
2069
2237
|
|
|
2070
|
-
// src/narratives/templates/typescript/ClinicalImpressionTemplate.ts
|
|
2071
|
-
var ClinicalImpressionTemplate = class {
|
|
2072
|
-
/**
|
|
2073
|
-
* Generate HTML narrative for Clinical Impressions
|
|
2074
|
-
* @param resource - FHIR Bundle containing ClinicalImpression resources
|
|
2075
|
-
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2076
|
-
* @returns HTML string for rendering
|
|
2077
|
-
*/
|
|
2078
|
-
generateNarrative(resource, timezone) {
|
|
2079
|
-
const templateUtilities = new TemplateUtilities(resource);
|
|
2080
|
-
let html = `
|
|
2081
|
-
<h5>Clinical Impressions</h5>
|
|
2082
|
-
<table>
|
|
2083
|
-
<thead>
|
|
2084
|
-
<tr>
|
|
2085
|
-
<th>Date</th>
|
|
2086
|
-
<th>Status</th>
|
|
2087
|
-
<th>Description</th>
|
|
2088
|
-
<th>Summary</th>
|
|
2089
|
-
<th>Findings</th>
|
|
2090
|
-
<th>Notes</th>
|
|
2091
|
-
</tr>
|
|
2092
|
-
</thead>
|
|
2093
|
-
<tbody>`;
|
|
2094
|
-
if (resource.entry && Array.isArray(resource.entry)) {
|
|
2095
|
-
let hasClinicalImpressions = false;
|
|
2096
|
-
for (const entry of resource.entry) {
|
|
2097
|
-
const res = entry.resource;
|
|
2098
|
-
if (!res || res.resourceType !== "ClinicalImpression") {
|
|
2099
|
-
continue;
|
|
2100
|
-
}
|
|
2101
|
-
hasClinicalImpressions = true;
|
|
2102
|
-
const impression = res;
|
|
2103
|
-
let formattedDate = "";
|
|
2104
|
-
if (impression.effectiveDateTime) {
|
|
2105
|
-
formattedDate = templateUtilities.renderTime(impression.effectiveDateTime, timezone);
|
|
2106
|
-
} else if (impression.effectivePeriod) {
|
|
2107
|
-
formattedDate = templateUtilities.renderPeriod(impression.effectivePeriod, timezone);
|
|
2108
|
-
} else if (impression.date) {
|
|
2109
|
-
formattedDate = templateUtilities.renderDate(impression.date);
|
|
2110
|
-
}
|
|
2111
|
-
const status = impression.status || "";
|
|
2112
|
-
const description = impression.description || "";
|
|
2113
|
-
const summary = impression.summary || "";
|
|
2114
|
-
let findingsHtml = "";
|
|
2115
|
-
if (impression.finding && impression.finding.length > 0) {
|
|
2116
|
-
findingsHtml = "<ul>";
|
|
2117
|
-
for (const finding of impression.finding) {
|
|
2118
|
-
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept, "display") : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
2119
|
-
const cause = finding.basis || "";
|
|
2120
|
-
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
2121
|
-
}
|
|
2122
|
-
findingsHtml += "</ul>";
|
|
2123
|
-
}
|
|
2124
|
-
const notes = templateUtilities.renderNotes(impression.note, timezone);
|
|
2125
|
-
html += `
|
|
2126
|
-
<tr id="${templateUtilities.narrativeLinkId(impression)}">
|
|
2127
|
-
<td>${formattedDate}</td>
|
|
2128
|
-
<td>${status}</td>
|
|
2129
|
-
<td>${description}</td>
|
|
2130
|
-
<td>${summary}</td>
|
|
2131
|
-
<td>${findingsHtml}</td>
|
|
2132
|
-
<td>${notes}</td>
|
|
2133
|
-
</tr>`;
|
|
2134
|
-
}
|
|
2135
|
-
if (!hasClinicalImpressions) {
|
|
2136
|
-
html += `
|
|
2137
|
-
<tr>
|
|
2138
|
-
<td colspan="6">No clinical impressions recorded</td>
|
|
2139
|
-
</tr>`;
|
|
2140
|
-
}
|
|
2141
|
-
} else {
|
|
2142
|
-
html += `
|
|
2143
|
-
<tr>
|
|
2144
|
-
<td colspan="6">No clinical impressions recorded</td>
|
|
2145
|
-
</tr>`;
|
|
2146
|
-
}
|
|
2147
|
-
html += `
|
|
2148
|
-
</tbody>
|
|
2149
|
-
</table>`;
|
|
2150
|
-
return html;
|
|
2151
|
-
}
|
|
2152
|
-
};
|
|
2153
|
-
|
|
2154
2238
|
// src/narratives/templates/typescript/TypeScriptTemplateMapper.ts
|
|
2155
2239
|
var TypeScriptTemplateMapper = class {
|
|
2156
2240
|
/**
|
|
@@ -2173,20 +2257,19 @@ var TypeScriptTemplateMapper = class {
|
|
|
2173
2257
|
TypeScriptTemplateMapper.sectionToTemplate = {
|
|
2174
2258
|
["Patient" /* PATIENT */]: new PatientTemplate(),
|
|
2175
2259
|
["AllergyIntoleranceSection" /* ALLERGIES */]: new AllergyIntoleranceTemplate(),
|
|
2176
|
-
["
|
|
2260
|
+
["MedicationSummarySection" /* MEDICATIONS */]: new MedicationSummaryTemplate(),
|
|
2177
2261
|
["ImmunizationSection" /* IMMUNIZATIONS */]: new ImmunizationsTemplate(),
|
|
2178
2262
|
["ProblemSection" /* PROBLEMS */]: new ProblemListTemplate(),
|
|
2179
2263
|
["VitalSignsSection" /* VITAL_SIGNS */]: new VitalSignsTemplate(),
|
|
2180
2264
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: new MedicalDevicesTemplate(),
|
|
2181
|
-
["
|
|
2182
|
-
["
|
|
2265
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
|
|
2266
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
|
|
2183
2267
|
["FamilyHistorySection" /* FAMILY_HISTORY */]: new FamilyHistoryTemplate(),
|
|
2184
2268
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: new SocialHistoryTemplate(),
|
|
2185
|
-
["
|
|
2269
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
|
|
2186
2270
|
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: new FunctionalStatusTemplate(),
|
|
2187
|
-
["
|
|
2188
|
-
["
|
|
2189
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: new ClinicalImpressionTemplate(),
|
|
2271
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: new PastHistoryOfIllnessTemplate(),
|
|
2272
|
+
["PlanOfCareSection" /* CARE_PLAN */]: new PlanOfCareTemplate(),
|
|
2190
2273
|
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: new AdvanceDirectivesTemplate()
|
|
2191
2274
|
};
|
|
2192
2275
|
|
|
@@ -2223,10 +2306,9 @@ var NarrativeGenerator = class {
|
|
|
2223
2306
|
* @param section - IPS section type
|
|
2224
2307
|
* @param resources - Array of domain resources
|
|
2225
2308
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2226
|
-
* @param wrapInXhtml - Whether to wrap the content in XHTML div
|
|
2227
2309
|
* @returns Generated HTML content or undefined if no resources
|
|
2228
2310
|
*/
|
|
2229
|
-
static async generateNarrativeContentAsync(section, resources, timezone
|
|
2311
|
+
static async generateNarrativeContentAsync(section, resources, timezone) {
|
|
2230
2312
|
if (!resources || resources.length === 0) {
|
|
2231
2313
|
return void 0;
|
|
2232
2314
|
}
|
|
@@ -2242,11 +2324,7 @@ var NarrativeGenerator = class {
|
|
|
2242
2324
|
if (!content) {
|
|
2243
2325
|
return void 0;
|
|
2244
2326
|
}
|
|
2245
|
-
|
|
2246
|
-
return await this.wrapInXhtmlAsync(content);
|
|
2247
|
-
} else {
|
|
2248
|
-
return await this.minifyHtmlAsync(content);
|
|
2249
|
-
}
|
|
2327
|
+
return content;
|
|
2250
2328
|
} catch (error) {
|
|
2251
2329
|
console.error(`Error generating narrative for section ${section}:`, error);
|
|
2252
2330
|
return `<div class="error">Error generating narrative: ${error instanceof Error ? error.message : String(error)}</div>`;
|
|
@@ -2293,35 +2371,35 @@ var NarrativeGenerator = class {
|
|
|
2293
2371
|
* @param resources - Array of domain resources
|
|
2294
2372
|
* @param timezone - Optional timezone to use for date formatting
|
|
2295
2373
|
* @param minify - Whether to minify the HTML content (default: true)
|
|
2296
|
-
* @param wrapInXhtml - Whether to wrap the content in XHTML div
|
|
2297
2374
|
* @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
|
|
2298
2375
|
*/
|
|
2299
|
-
static async generateNarrativeAsync(section, resources, timezone, minify = true
|
|
2300
|
-
const content = await this.generateNarrativeContentAsync(section, resources, timezone
|
|
2376
|
+
static async generateNarrativeAsync(section, resources, timezone, minify = true) {
|
|
2377
|
+
const content = await this.generateNarrativeContentAsync(section, resources, timezone);
|
|
2301
2378
|
if (!content) {
|
|
2302
2379
|
return void 0;
|
|
2303
2380
|
}
|
|
2304
2381
|
return await this.createNarrativeAsync(content, minify);
|
|
2305
2382
|
}
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2383
|
+
};
|
|
2384
|
+
|
|
2385
|
+
// src/structures/ips_mandatory_sections.ts
|
|
2386
|
+
var IPSMandatorySections = ((IPSMandatorySections2) => {
|
|
2387
|
+
IPSMandatorySections2[IPSMandatorySections2["PATIENT"] = "Patient" /* PATIENT */] = "PATIENT";
|
|
2388
|
+
IPSMandatorySections2[IPSMandatorySections2["PROBLEMS"] = "ProblemSection" /* PROBLEMS */] = "PROBLEMS";
|
|
2389
|
+
IPSMandatorySections2[IPSMandatorySections2["ALLERGIES"] = "AllergyIntoleranceSection" /* ALLERGIES */] = "ALLERGIES";
|
|
2390
|
+
IPSMandatorySections2[IPSMandatorySections2["MEDICATIONS"] = "MedicationSummarySection" /* MEDICATIONS */] = "MEDICATIONS";
|
|
2391
|
+
return IPSMandatorySections2;
|
|
2392
|
+
})(IPSMandatorySections || {});
|
|
2393
|
+
var IPSMissingMandatorySectionContent = {
|
|
2394
|
+
["ProblemSection" /* PROBLEMS */]: "There is no information available about the subject's health problems or disabilities.",
|
|
2395
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: "There is no information available regarding the subject's allergy conditions.",
|
|
2396
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "There is no information available about the subject's medication use or administration."
|
|
2318
2397
|
};
|
|
2319
2398
|
|
|
2320
2399
|
// src/generators/fhir_summary_generator.ts
|
|
2321
2400
|
var ComprehensiveIPSCompositionBuilder = class {
|
|
2322
2401
|
constructor() {
|
|
2323
2402
|
this.sections = [];
|
|
2324
|
-
this.mandatorySectionsAdded = /* @__PURE__ */ new Set();
|
|
2325
2403
|
this.resources = /* @__PURE__ */ new Set();
|
|
2326
2404
|
}
|
|
2327
2405
|
/**
|
|
@@ -2339,35 +2417,35 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2339
2417
|
/**
|
|
2340
2418
|
* Adds a section to the composition with async HTML minification
|
|
2341
2419
|
* @param sectionType - IPS section type
|
|
2342
|
-
* @param
|
|
2420
|
+
* @param validResources - Array of domain resources
|
|
2343
2421
|
* @param timezone - Optional timezone to use for date formatting
|
|
2344
|
-
* @param options - Optional configuration options
|
|
2345
2422
|
*/
|
|
2346
|
-
async addSectionAsync(sectionType,
|
|
2347
|
-
const validResources = resources;
|
|
2423
|
+
async addSectionAsync(sectionType, validResources, timezone) {
|
|
2348
2424
|
for (const resource of validResources) {
|
|
2349
2425
|
this.resources.add(resource);
|
|
2350
2426
|
}
|
|
2351
|
-
if (validResources.length === 0) {
|
|
2352
|
-
if (!options?.isOptional) {
|
|
2353
|
-
throw new Error(`No valid resources for mandatory section: ${sectionType}`);
|
|
2354
|
-
}
|
|
2355
|
-
return this;
|
|
2356
|
-
}
|
|
2357
2427
|
if (sectionType !== "Patient" /* PATIENT */) {
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2428
|
+
let narrative = void 0;
|
|
2429
|
+
if (validResources.length > 0) {
|
|
2430
|
+
narrative = await NarrativeGenerator.generateNarrativeAsync(
|
|
2431
|
+
sectionType,
|
|
2432
|
+
validResources,
|
|
2433
|
+
timezone,
|
|
2434
|
+
true
|
|
2435
|
+
);
|
|
2436
|
+
} else if (sectionType in IPSMandatorySections) {
|
|
2437
|
+
narrative = await NarrativeGenerator.createNarrativeAsync(
|
|
2438
|
+
IPSMissingMandatorySectionContent[sectionType]
|
|
2439
|
+
);
|
|
2440
|
+
} else {
|
|
2441
|
+
return this;
|
|
2442
|
+
}
|
|
2365
2443
|
const sectionEntry = {
|
|
2366
2444
|
title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
|
|
2367
2445
|
code: {
|
|
2368
2446
|
coding: [{
|
|
2369
2447
|
system: "http://loinc.org",
|
|
2370
|
-
code:
|
|
2448
|
+
code: IPS_SECTION_LOINC_CODES[sectionType],
|
|
2371
2449
|
display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
2372
2450
|
}],
|
|
2373
2451
|
text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
@@ -2378,9 +2456,6 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2378
2456
|
display: resource.resourceType
|
|
2379
2457
|
}))
|
|
2380
2458
|
};
|
|
2381
|
-
if (!options?.isOptional) {
|
|
2382
|
-
this.mandatorySectionsAdded.add(sectionType);
|
|
2383
|
-
}
|
|
2384
2459
|
this.sections.push(sectionEntry);
|
|
2385
2460
|
}
|
|
2386
2461
|
return this;
|
|
@@ -2399,43 +2474,20 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2399
2474
|
throw new Error("Patient resource not found in the bundle");
|
|
2400
2475
|
}
|
|
2401
2476
|
this.patient = patientEntry.resource;
|
|
2477
|
+
const resources = bundle.entry.map((e) => e.resource);
|
|
2402
2478
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2403
2479
|
const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
|
|
2404
2480
|
const customFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2405
|
-
let
|
|
2481
|
+
let sectionResources = resources.filter(
|
|
2482
|
+
(r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
|
|
2483
|
+
);
|
|
2406
2484
|
if (customFilter) {
|
|
2407
|
-
|
|
2408
|
-
}
|
|
2409
|
-
if (resources.length > 0) {
|
|
2410
|
-
await this.addSectionAsync(sectionType, resources, timezone, {
|
|
2411
|
-
isOptional: true
|
|
2412
|
-
});
|
|
2485
|
+
sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
|
|
2413
2486
|
}
|
|
2487
|
+
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2414
2488
|
}
|
|
2415
2489
|
return this;
|
|
2416
2490
|
}
|
|
2417
|
-
/**
|
|
2418
|
-
* Builds the final Composition sections, ensuring all mandatory sections are present.
|
|
2419
|
-
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2420
|
-
*/
|
|
2421
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2422
|
-
build(timezone) {
|
|
2423
|
-
const mandatorySections = [
|
|
2424
|
-
"AllergyIntoleranceSection" /* ALLERGIES */,
|
|
2425
|
-
"MedicationSection" /* MEDICATIONS */,
|
|
2426
|
-
"ProblemSection" /* PROBLEMS */,
|
|
2427
|
-
"ImmunizationSection" /* IMMUNIZATIONS */
|
|
2428
|
-
];
|
|
2429
|
-
const missingMandatorySections = mandatorySections.filter(
|
|
2430
|
-
(section) => !this.mandatorySectionsAdded.has(section)
|
|
2431
|
-
);
|
|
2432
|
-
if (missingMandatorySections.length > 0) {
|
|
2433
|
-
throw new Error(
|
|
2434
|
-
`Missing mandatory IPS sections: ${missingMandatorySections.join(", ")}`
|
|
2435
|
-
);
|
|
2436
|
-
}
|
|
2437
|
-
return this.sections;
|
|
2438
|
-
}
|
|
2439
2491
|
/**
|
|
2440
2492
|
* Builds a complete FHIR Bundle containing the Composition and all resources.
|
|
2441
2493
|
* @param authorOrganizationId - ID of the authoring organization (e.g., hospital or clinic)
|
|
@@ -2472,7 +2524,12 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2472
2524
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2473
2525
|
title: "International Patient Summary",
|
|
2474
2526
|
section: this.sections,
|
|
2475
|
-
text: await
|
|
2527
|
+
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2528
|
+
"Patient" /* PATIENT */,
|
|
2529
|
+
[this.patient],
|
|
2530
|
+
timezone,
|
|
2531
|
+
false
|
|
2532
|
+
)
|
|
2476
2533
|
};
|
|
2477
2534
|
const bundle = {
|
|
2478
2535
|
resourceType: "Bundle",
|
|
@@ -2512,43 +2569,6 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2512
2569
|
});
|
|
2513
2570
|
return bundle;
|
|
2514
2571
|
}
|
|
2515
|
-
/**
|
|
2516
|
-
* Creates a narrative for the composition based on the patient and sections.
|
|
2517
|
-
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2518
|
-
* @private
|
|
2519
|
-
*/
|
|
2520
|
-
async createCompositionNarrativeAsync(timezone) {
|
|
2521
|
-
const patient = this.patient;
|
|
2522
|
-
let fullNarrativeContent = "";
|
|
2523
|
-
const patientNarrative = await NarrativeGenerator.generateNarrativeContentAsync(
|
|
2524
|
-
"Patient" /* PATIENT */,
|
|
2525
|
-
[patient],
|
|
2526
|
-
timezone,
|
|
2527
|
-
false
|
|
2528
|
-
);
|
|
2529
|
-
fullNarrativeContent = fullNarrativeContent.concat(patientNarrative || "");
|
|
2530
|
-
for (const sectionType of Object.values(IPSSections)) {
|
|
2531
|
-
if (sectionType === "Patient" /* PATIENT */) {
|
|
2532
|
-
continue;
|
|
2533
|
-
}
|
|
2534
|
-
const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
|
|
2535
|
-
const allResources = Array.from(this.resources);
|
|
2536
|
-
const resources = allResources.filter((r) => resourceTypesForSection.includes(r.resourceType));
|
|
2537
|
-
if (resources.length > 0) {
|
|
2538
|
-
const sectionNarrative = await NarrativeGenerator.generateNarrativeContentAsync(
|
|
2539
|
-
sectionType,
|
|
2540
|
-
resources,
|
|
2541
|
-
timezone,
|
|
2542
|
-
false
|
|
2543
|
-
);
|
|
2544
|
-
fullNarrativeContent = fullNarrativeContent.concat(sectionNarrative || "");
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
|
-
return {
|
|
2548
|
-
status: "generated",
|
|
2549
|
-
div: await NarrativeGenerator.wrapInXhtmlAsync(fullNarrativeContent, true)
|
|
2550
|
-
};
|
|
2551
|
-
}
|
|
2552
2572
|
};
|
|
2553
2573
|
|
|
2554
2574
|
// src/index.ts
|