@imranq2/fhirpatientsummary 1.0.11 → 1.0.13
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 +572 -632
- package/dist/index.d.cts +17 -39
- package/dist/index.d.ts +17 -39
- package/dist/index.js +572 -632
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -29,22 +29,20 @@ 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";
|
|
48
46
|
return IPSSections2;
|
|
49
47
|
})(IPSSections || {});
|
|
50
48
|
|
|
@@ -52,99 +50,106 @@ var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
|
|
|
52
50
|
var IPS_SECTION_LOINC_CODES = {
|
|
53
51
|
["Patient" /* PATIENT */]: "54126-4",
|
|
54
52
|
["AllergyIntoleranceSection" /* ALLERGIES */]: "48765-2",
|
|
55
|
-
["
|
|
53
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "10160-0",
|
|
56
54
|
["ProblemSection" /* PROBLEMS */]: "11450-4",
|
|
57
55
|
["ImmunizationSection" /* IMMUNIZATIONS */]: "11369-6",
|
|
58
56
|
["VitalSignsSection" /* VITAL_SIGNS */]: "8716-3",
|
|
59
57
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "46264-8",
|
|
60
|
-
["
|
|
61
|
-
["
|
|
62
|
-
["FamilyHistorySection" /* FAMILY_HISTORY */]: "10157-6",
|
|
58
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
|
|
59
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: "47519-4",
|
|
63
60
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: "29762-2",
|
|
64
|
-
["
|
|
61
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "10162-6",
|
|
65
62
|
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "47420-5",
|
|
66
|
-
["
|
|
67
|
-
["
|
|
68
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: "51848-0",
|
|
63
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "11348-0",
|
|
64
|
+
["PlanOfCareSection" /* CARE_PLAN */]: "18776-5",
|
|
69
65
|
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "42348-3"
|
|
70
66
|
};
|
|
71
67
|
var IPS_SECTION_DISPLAY_NAMES = {
|
|
72
68
|
["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
|
-
|
|
69
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and Intolerances",
|
|
70
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "Medication Summary",
|
|
71
|
+
["ProblemSection" /* PROBLEMS */]: "Problem List",
|
|
72
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: "Immunizations",
|
|
73
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "Results Summary",
|
|
74
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: "History of Procedures",
|
|
75
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "History of Medical Devices",
|
|
76
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: "Vital Signs",
|
|
77
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "Advance Directives",
|
|
78
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "Functional Status",
|
|
79
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "History of Pregnancies",
|
|
80
|
+
["PlanOfCareSection" /* CARE_PLAN */]: "Plan of Care",
|
|
81
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "History of Past Illness",
|
|
82
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History"
|
|
83
|
+
};
|
|
84
|
+
var PREGNANCY_LOINC_CODES = {
|
|
85
|
+
PREGNANCY_STATUS: {
|
|
86
|
+
"LA15173-0": "Pregnant",
|
|
87
|
+
"LA26683-5": "Not pregnant",
|
|
88
|
+
"LA4489-6": "Unknown"
|
|
89
|
+
},
|
|
90
|
+
PREGNANCY_OUTCOME: {
|
|
91
|
+
"11636-8": "Live Birth",
|
|
92
|
+
"11637-6": "Preterm Birth",
|
|
93
|
+
"11638-4": "Still Living Birth",
|
|
94
|
+
"11639-2": "Term Birth",
|
|
95
|
+
"11640-0": "Total Births",
|
|
96
|
+
"11612-9": "Abortions",
|
|
97
|
+
"11613-7": "Induced Abortions",
|
|
98
|
+
"11614-5": "Spontaneous Abortions",
|
|
99
|
+
"33065-4": "Ectopic Pregnancy"
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var SOCIAL_HISTORY_LOINC_CODES = {
|
|
103
|
+
"72166-2": "Tobacco Use",
|
|
104
|
+
"74013-4": "Alcohol Use"
|
|
89
105
|
};
|
|
90
106
|
|
|
91
107
|
// src/structures/ips_section_resource_map.ts
|
|
92
108
|
var IPSSectionResourceMap = {
|
|
93
109
|
["Patient" /* PATIENT */]: ["Patient"],
|
|
94
110
|
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
95
|
-
["
|
|
111
|
+
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
112
|
+
// Medication resource is needed for identifying name of medication
|
|
96
113
|
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
97
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization"],
|
|
114
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
115
|
+
// Immunization can include Organization as a related resource
|
|
98
116
|
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
99
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["Device"],
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
["
|
|
103
|
-
["FamilyHistorySection" /* FAMILY_HISTORY */]: ["FamilyMemberHistory"],
|
|
117
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
118
|
+
// Device resource is used for medical devices name
|
|
119
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
120
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
104
121
|
["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
|
|
122
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
123
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
124
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
125
|
+
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
126
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
116
127
|
};
|
|
117
128
|
var IPSSectionResourceFilters = {
|
|
118
129
|
// 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",
|
|
130
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string"),
|
|
122
131
|
// Only include active problems/conditions
|
|
123
|
-
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => c.code
|
|
132
|
+
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
124
133
|
// Only include completed immunizations
|
|
125
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed",
|
|
134
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
126
135
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
127
136
|
["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
137
|
// Only include finalized diagnostic reports
|
|
131
|
-
["
|
|
138
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
132
139
|
// Only include completed procedures
|
|
133
|
-
["
|
|
134
|
-
// Only include family history resources
|
|
135
|
-
["FamilyHistorySection" /* FAMILY_HISTORY */]: (resource) => resource.resourceType === "FamilyMemberHistory",
|
|
140
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
|
|
136
141
|
// Only include social history Observations (category.coding contains 'social-history')
|
|
137
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.
|
|
142
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LOINC_CODES).includes(c.code)),
|
|
138
143
|
// 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
|
-
["
|
|
144
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code?.coding?.some((c) => Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) || resource.valueCodeableConcept?.coding?.some((c) => Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code))),
|
|
145
|
+
// Only include active functional status Conditions or ClinicalImpressions
|
|
146
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string") || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
|
|
147
|
+
// Only include resolved medical history Conditions
|
|
148
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
|
|
144
149
|
// Only include active care plans
|
|
145
|
-
["
|
|
146
|
-
// Only include
|
|
147
|
-
["
|
|
150
|
+
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
151
|
+
// Only include active advance directives (Consent resources)
|
|
152
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active",
|
|
148
153
|
// Patient section: only Patient resource
|
|
149
154
|
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
|
|
150
155
|
};
|
|
@@ -186,8 +191,6 @@ var TemplateUtilities = class {
|
|
|
186
191
|
}
|
|
187
192
|
if (cc.text) {
|
|
188
193
|
return cc.text;
|
|
189
|
-
} else if ("display" in cc && cc.display) {
|
|
190
|
-
return cc.display;
|
|
191
194
|
} else if (cc.coding && cc.coding[0]) {
|
|
192
195
|
if (cc.coding[0].display) {
|
|
193
196
|
return cc.coding[0].display;
|
|
@@ -219,8 +222,8 @@ var TemplateUtilities = class {
|
|
|
219
222
|
*/
|
|
220
223
|
renderDevice(deviceRef) {
|
|
221
224
|
const device = deviceRef && this.resolveReference(deviceRef);
|
|
222
|
-
if (device && device.resourceType === "Device" && device.
|
|
223
|
-
return this.
|
|
225
|
+
if (device && device.resourceType === "Device" && device.deviceName && device.deviceName.length > 0) {
|
|
226
|
+
return this.safeConcat(device.deviceName, "name");
|
|
224
227
|
}
|
|
225
228
|
return "";
|
|
226
229
|
}
|
|
@@ -401,8 +404,12 @@ var TemplateUtilities = class {
|
|
|
401
404
|
}
|
|
402
405
|
const texts = [];
|
|
403
406
|
for (const item of list) {
|
|
404
|
-
if (item && item.manifestation && item.manifestation[0]
|
|
405
|
-
|
|
407
|
+
if (item && item.manifestation && item.manifestation[0]) {
|
|
408
|
+
if (item.manifestation[0].text) {
|
|
409
|
+
texts.push(item.manifestation[0].text);
|
|
410
|
+
} else if (item.manifestation[0].coding && item.manifestation[0].coding[0]?.display) {
|
|
411
|
+
texts.push(item.manifestation[0].coding[0].display);
|
|
412
|
+
}
|
|
406
413
|
}
|
|
407
414
|
}
|
|
408
415
|
return texts.join(", ");
|
|
@@ -561,6 +568,25 @@ var TemplateUtilities = class {
|
|
|
561
568
|
}
|
|
562
569
|
return null;
|
|
563
570
|
}
|
|
571
|
+
extractPregnancyStatus(observation) {
|
|
572
|
+
let status = "";
|
|
573
|
+
observation.code?.coding?.forEach((c) => {
|
|
574
|
+
if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) {
|
|
575
|
+
status = PREGNANCY_LOINC_CODES.PREGNANCY_STATUS[c.code];
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
if (observation.valueCodeableConcept) {
|
|
579
|
+
observation.valueCodeableConcept.coding?.forEach((c) => {
|
|
580
|
+
if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
|
|
581
|
+
if (status) {
|
|
582
|
+
status += " - ";
|
|
583
|
+
}
|
|
584
|
+
status += PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME[c.code];
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
return status;
|
|
589
|
+
}
|
|
564
590
|
formatQuantityValue(quantity) {
|
|
565
591
|
if (!quantity) return "";
|
|
566
592
|
const parts = [];
|
|
@@ -799,7 +825,6 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
799
825
|
const patient = entry.resource;
|
|
800
826
|
html += `
|
|
801
827
|
<div>
|
|
802
|
-
<h2>Patient Summary</h2>
|
|
803
828
|
<ul>
|
|
804
829
|
<li><strong>Name(s):</strong>${this.renderNames(patient)}</li>
|
|
805
830
|
<li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
|
|
@@ -949,10 +974,20 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
949
974
|
}
|
|
950
975
|
}
|
|
951
976
|
}
|
|
977
|
+
activeAllergies.sort((a, b) => {
|
|
978
|
+
const dateA = a.onsetDateTime;
|
|
979
|
+
const dateB = b.onsetDateTime;
|
|
980
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
981
|
+
});
|
|
982
|
+
resolvedAllergies.sort((a, b) => {
|
|
983
|
+
const dateA = a.onsetDateTime;
|
|
984
|
+
const dateB = b.onsetDateTime;
|
|
985
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
986
|
+
});
|
|
952
987
|
let html = "";
|
|
953
988
|
html += `
|
|
954
989
|
<div class="ActiveAllergies">
|
|
955
|
-
<h3>Active
|
|
990
|
+
<h3>Active</h3>
|
|
956
991
|
<table class="ActiveAllergyTable">
|
|
957
992
|
<thead>
|
|
958
993
|
<tr>
|
|
@@ -980,7 +1015,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
980
1015
|
</div>`;
|
|
981
1016
|
html += `
|
|
982
1017
|
<div class="ResolvedAllergies">
|
|
983
|
-
<h3>Resolved
|
|
1018
|
+
<h3>Resolved</h3>
|
|
984
1019
|
<table class="ResolvedAllergyTable">
|
|
985
1020
|
<thead>
|
|
986
1021
|
<tr>
|
|
@@ -1024,7 +1059,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1024
1059
|
<tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
|
|
1025
1060
|
<td class="Name"><span class="AllergenName">${templateUtilities.codeableConcept(allergy.code)}</span></td>
|
|
1026
1061
|
<td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
|
|
1027
|
-
<td class="Category">${templateUtilities.safeConcat(allergy.category
|
|
1062
|
+
<td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
|
|
1028
1063
|
<td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
|
|
1029
1064
|
<td class="Severity">${templateUtilities.safeConcat(allergy.reaction, "severity") || "-"}</td>
|
|
1030
1065
|
<td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
|
|
@@ -1059,6 +1094,67 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1059
1094
|
generateNarrative(resource, timezone) {
|
|
1060
1095
|
return _MedicationSummaryTemplate.generateStaticNarrative(resource, timezone);
|
|
1061
1096
|
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Safely parse a date string and return a valid Date object or null
|
|
1099
|
+
* @param dateString - The date string to parse
|
|
1100
|
+
* @returns Date object or null if invalid
|
|
1101
|
+
*/
|
|
1102
|
+
static parseDate(dateString) {
|
|
1103
|
+
if (!dateString || dateString.trim() === "") {
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
const date = new Date(dateString);
|
|
1107
|
+
return !isNaN(date.getTime()) ? date : null;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Determine if a MedicationRequest is active
|
|
1111
|
+
* @param medicationRequest - The MedicationRequest resource
|
|
1112
|
+
* @returns boolean indicating if the medication request is active
|
|
1113
|
+
*/
|
|
1114
|
+
static isActiveMedicationRequest(medicationRequest) {
|
|
1115
|
+
const status = medicationRequest.status?.toLowerCase();
|
|
1116
|
+
if (status === "active" || status === "unknown") {
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
if (status === "completed" || status === "cancelled" || status === "stopped" || status === "draft") {
|
|
1120
|
+
return false;
|
|
1121
|
+
}
|
|
1122
|
+
const endDate = medicationRequest.dispenseRequest?.validityPeriod?.end;
|
|
1123
|
+
if (!endDate) {
|
|
1124
|
+
return true;
|
|
1125
|
+
}
|
|
1126
|
+
const parsedEndDate = this.parseDate(endDate);
|
|
1127
|
+
if (!parsedEndDate) {
|
|
1128
|
+
return true;
|
|
1129
|
+
}
|
|
1130
|
+
return parsedEndDate.getTime() > Date.now();
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Determine if a MedicationStatement is active
|
|
1134
|
+
* @param medicationStatement - The MedicationStatement resource
|
|
1135
|
+
* @returns boolean indicating if the medication statement is active
|
|
1136
|
+
*/
|
|
1137
|
+
static isActiveMedicationStatement(medicationStatement) {
|
|
1138
|
+
const status = medicationStatement.status?.toLowerCase();
|
|
1139
|
+
if (status === "active" || status === "intended" || status === "unknown") {
|
|
1140
|
+
return true;
|
|
1141
|
+
}
|
|
1142
|
+
if (status === "completed" || status === "stopped" || status === "not-taken") {
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
let endDate;
|
|
1146
|
+
if (medicationStatement.effectivePeriod?.end) {
|
|
1147
|
+
endDate = medicationStatement.effectivePeriod.end;
|
|
1148
|
+
}
|
|
1149
|
+
if (!endDate) {
|
|
1150
|
+
return true;
|
|
1151
|
+
}
|
|
1152
|
+
const parsedEndDate = this.parseDate(endDate);
|
|
1153
|
+
if (!parsedEndDate) {
|
|
1154
|
+
return true;
|
|
1155
|
+
}
|
|
1156
|
+
return parsedEndDate.getTime() > Date.now();
|
|
1157
|
+
}
|
|
1062
1158
|
/**
|
|
1063
1159
|
* Internal static implementation that actually generates the narrative
|
|
1064
1160
|
* @param resource - FHIR Bundle containing Medication resources
|
|
@@ -1070,12 +1166,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1070
1166
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1071
1167
|
let html = "";
|
|
1072
1168
|
const medicationRequests = this.getMedicationRequests(templateUtilities, resource);
|
|
1073
|
-
if (medicationRequests.length > 0) {
|
|
1074
|
-
html += this.renderMedicationRequests(templateUtilities, medicationRequests);
|
|
1075
|
-
}
|
|
1076
1169
|
const medicationStatements = this.getMedicationStatements(templateUtilities, resource);
|
|
1077
|
-
|
|
1078
|
-
|
|
1170
|
+
const allActiveMedications = [];
|
|
1171
|
+
const allInactiveMedications = [];
|
|
1172
|
+
medicationRequests.forEach((mr) => {
|
|
1173
|
+
if (this.isActiveMedicationRequest(mr.resource)) {
|
|
1174
|
+
allActiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1175
|
+
} else {
|
|
1176
|
+
allInactiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
medicationStatements.forEach((ms) => {
|
|
1180
|
+
if (this.isActiveMedicationStatement(ms.resource)) {
|
|
1181
|
+
allActiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1182
|
+
} else {
|
|
1183
|
+
allInactiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
const sortMedications = (medications) => {
|
|
1187
|
+
medications.sort((a, b) => {
|
|
1188
|
+
let dateStringA;
|
|
1189
|
+
let dateStringB;
|
|
1190
|
+
if (a.type === "request") {
|
|
1191
|
+
const mr = a.resource;
|
|
1192
|
+
dateStringA = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
1193
|
+
} else {
|
|
1194
|
+
const ms = a.resource;
|
|
1195
|
+
dateStringA = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
1196
|
+
}
|
|
1197
|
+
if (b.type === "request") {
|
|
1198
|
+
const mr = b.resource;
|
|
1199
|
+
dateStringB = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
1200
|
+
} else {
|
|
1201
|
+
const ms = b.resource;
|
|
1202
|
+
dateStringB = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
1203
|
+
}
|
|
1204
|
+
const dateA = this.parseDate(dateStringA);
|
|
1205
|
+
const dateB = this.parseDate(dateStringB);
|
|
1206
|
+
if (!dateA && !dateB) return 0;
|
|
1207
|
+
if (!dateA) return 1;
|
|
1208
|
+
if (!dateB) return -1;
|
|
1209
|
+
return dateB.getTime() - dateA.getTime();
|
|
1210
|
+
});
|
|
1211
|
+
};
|
|
1212
|
+
if (allActiveMedications.length > 0) {
|
|
1213
|
+
sortMedications(allActiveMedications);
|
|
1214
|
+
html += this.renderCombinedMedications(templateUtilities, allActiveMedications, "Active Medications");
|
|
1215
|
+
}
|
|
1216
|
+
if (allInactiveMedications.length > 0) {
|
|
1217
|
+
sortMedications(allInactiveMedications);
|
|
1218
|
+
html += this.renderCombinedMedications(templateUtilities, allInactiveMedications, "Inactive Medications");
|
|
1079
1219
|
}
|
|
1080
1220
|
return html;
|
|
1081
1221
|
}
|
|
@@ -1110,16 +1250,19 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1110
1250
|
}));
|
|
1111
1251
|
}
|
|
1112
1252
|
/**
|
|
1113
|
-
* Render HTML table for MedicationRequest resources
|
|
1253
|
+
* Render HTML table for combined MedicationRequest and MedicationStatement resources
|
|
1114
1254
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1115
|
-
* @param medications - Array of
|
|
1255
|
+
* @param medications - Array of combined medication resources
|
|
1256
|
+
* @param sectionTitle - Title for the section
|
|
1116
1257
|
* @returns HTML string for rendering
|
|
1117
1258
|
*/
|
|
1118
|
-
static
|
|
1259
|
+
static renderCombinedMedications(templateUtilities, medications, sectionTitle) {
|
|
1119
1260
|
let html = `
|
|
1261
|
+
<h3>${sectionTitle}</h3>
|
|
1120
1262
|
<table>
|
|
1121
1263
|
<thead>
|
|
1122
1264
|
<tr>
|
|
1265
|
+
<th>Type</th>
|
|
1123
1266
|
<th>Medication</th>
|
|
1124
1267
|
<th>Sig</th>
|
|
1125
1268
|
<th>Dispense Quantity</th>
|
|
@@ -1130,86 +1273,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1130
1273
|
</tr>
|
|
1131
1274
|
</thead>
|
|
1132
1275
|
<tbody>`;
|
|
1133
|
-
for (const
|
|
1134
|
-
const narrativeLinkId = templateUtilities.narrativeLinkId(extension);
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
);
|
|
1139
|
-
const sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
|
|
1276
|
+
for (const medication of medications) {
|
|
1277
|
+
const narrativeLinkId = templateUtilities.narrativeLinkId(medication.extension);
|
|
1278
|
+
let type;
|
|
1279
|
+
let medicationName;
|
|
1280
|
+
let sig;
|
|
1140
1281
|
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() || "-";
|
|
1282
|
+
let refills = "-";
|
|
1148
1283
|
let startDate = "-";
|
|
1149
1284
|
let endDate = "-";
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1285
|
+
let status;
|
|
1286
|
+
if (medication.type === "request") {
|
|
1287
|
+
const mr = medication.resource;
|
|
1288
|
+
type = "Request";
|
|
1289
|
+
status = mr.status ? String(mr.status) : "-";
|
|
1290
|
+
medicationName = templateUtilities.getMedicationName(
|
|
1291
|
+
mr.medicationReference || mr.medicationCodeableConcept
|
|
1292
|
+
);
|
|
1293
|
+
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
|
|
1294
|
+
if (mr.dispenseRequest?.quantity) {
|
|
1295
|
+
const quantity = mr.dispenseRequest.quantity;
|
|
1296
|
+
if (quantity.value) {
|
|
1297
|
+
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
|
|
1301
|
+
if (mr.dispenseRequest?.validityPeriod) {
|
|
1302
|
+
startDate = mr.dispenseRequest.validityPeriod.start || "-";
|
|
1303
|
+
endDate = mr.dispenseRequest.validityPeriod.end || "-";
|
|
1304
|
+
} else {
|
|
1305
|
+
startDate = mr.authoredOn || "-";
|
|
1306
|
+
}
|
|
1153
1307
|
} 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 || "-";
|
|
1308
|
+
const ms = medication.resource;
|
|
1309
|
+
type = "Statement";
|
|
1310
|
+
status = ms.status ? String(ms.status) : "-";
|
|
1311
|
+
medicationName = templateUtilities.getMedicationName(
|
|
1312
|
+
ms.medicationReference || ms.medicationCodeableConcept
|
|
1313
|
+
);
|
|
1314
|
+
sig = templateUtilities.concat(ms.dosage, "text") || "-";
|
|
1315
|
+
if (ms.effectiveDateTime) {
|
|
1316
|
+
startDate = ms.effectiveDateTime;
|
|
1317
|
+
} else if (ms.effectivePeriod) {
|
|
1318
|
+
startDate = ms.effectivePeriod.start || "-";
|
|
1319
|
+
endDate = ms.effectivePeriod.end || "-";
|
|
1320
|
+
}
|
|
1209
1321
|
}
|
|
1210
1322
|
html += `
|
|
1211
1323
|
<tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
|
|
1212
|
-
<td>${
|
|
1324
|
+
<td>${type}</td>
|
|
1325
|
+
<td>${medicationName}<ul></ul></td>
|
|
1213
1326
|
<td>${sig}</td>
|
|
1214
1327
|
<td>${dispenseQuantity}</td>
|
|
1215
1328
|
<td>${refills}</td>
|
|
@@ -1234,6 +1347,13 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1234
1347
|
* @returns HTML string for rendering
|
|
1235
1348
|
*/
|
|
1236
1349
|
generateNarrative(resource, timezone) {
|
|
1350
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1351
|
+
resource.entry.sort((a, b) => {
|
|
1352
|
+
const dateA = a.resource?.occurrenceDateTime;
|
|
1353
|
+
const dateB = b.resource?.occurrenceDateTime;
|
|
1354
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1237
1357
|
return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
|
|
1238
1358
|
}
|
|
1239
1359
|
/**
|
|
@@ -1245,7 +1365,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1245
1365
|
static generateStaticNarrative(resource, timezone) {
|
|
1246
1366
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1247
1367
|
let html = `
|
|
1248
|
-
<h5>Immunizations</h5>
|
|
1249
1368
|
<table>
|
|
1250
1369
|
<thead>
|
|
1251
1370
|
<tr>
|
|
@@ -1303,81 +1422,35 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1303
1422
|
static generateStaticNarrative(resource, timezone) {
|
|
1304
1423
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1305
1424
|
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">
|
|
1425
|
+
const activeConditions = resource.entry?.map((entry) => entry.resource) || [];
|
|
1426
|
+
activeConditions.sort((a, b) => {
|
|
1427
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1428
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1429
|
+
return dateB - dateA;
|
|
1430
|
+
});
|
|
1431
|
+
html += `
|
|
1432
|
+
<table>
|
|
1356
1433
|
<thead>
|
|
1357
1434
|
<tr>
|
|
1358
1435
|
<th>Problem</th>
|
|
1359
|
-
<th>
|
|
1360
|
-
<th>
|
|
1361
|
-
<th>
|
|
1362
|
-
<th>Resolved Date</th>
|
|
1436
|
+
<th>Severity</th>
|
|
1437
|
+
<th>Onset Date</th>
|
|
1438
|
+
<th>Recorded Date</th>
|
|
1363
1439
|
<th>Notes</th>
|
|
1364
1440
|
</tr>
|
|
1365
1441
|
</thead>
|
|
1366
1442
|
<tbody>`;
|
|
1367
|
-
|
|
1368
|
-
|
|
1443
|
+
for (const cond of activeConditions) {
|
|
1444
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1369
1445
|
<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>
|
|
1446
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1447
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1448
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1449
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1375
1450
|
</tr>`;
|
|
1376
|
-
}
|
|
1377
|
-
html += `</tbody>
|
|
1378
|
-
</table>
|
|
1379
|
-
</div>`;
|
|
1380
1451
|
}
|
|
1452
|
+
html += `</tbody>
|
|
1453
|
+
</table>`;
|
|
1381
1454
|
return html;
|
|
1382
1455
|
}
|
|
1383
1456
|
};
|
|
@@ -1401,8 +1474,13 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1401
1474
|
*/
|
|
1402
1475
|
static generateStaticNarrative(resource, timezone) {
|
|
1403
1476
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1477
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
1478
|
+
observations.sort((a, b) => {
|
|
1479
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1480
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1481
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1482
|
+
});
|
|
1404
1483
|
let html = `
|
|
1405
|
-
<h5>Vital Signs</h5>
|
|
1406
1484
|
<table>
|
|
1407
1485
|
<thead>
|
|
1408
1486
|
<tr>
|
|
@@ -1416,13 +1494,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1416
1494
|
</tr>
|
|
1417
1495
|
</thead>
|
|
1418
1496
|
<tbody>`;
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
const obs = entry.resource;
|
|
1422
|
-
if (obs.resourceType === "Composition") {
|
|
1423
|
-
continue;
|
|
1424
|
-
}
|
|
1425
|
-
html += `
|
|
1497
|
+
for (const obs of observations) {
|
|
1498
|
+
html += `
|
|
1426
1499
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1427
1500
|
<td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
|
|
1428
1501
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
@@ -1430,9 +1503,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1430
1503
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
1431
1504
|
<td>${templateUtilities.renderComponent(obs.component)}</td>
|
|
1432
1505
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1433
|
-
<td>${templateUtilities.
|
|
1506
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1434
1507
|
</tr>`;
|
|
1435
|
-
}
|
|
1436
1508
|
}
|
|
1437
1509
|
html += `
|
|
1438
1510
|
</tbody>
|
|
@@ -1450,6 +1522,13 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1450
1522
|
* @returns HTML string for rendering
|
|
1451
1523
|
*/
|
|
1452
1524
|
generateNarrative(resource, timezone) {
|
|
1525
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1526
|
+
resource.entry.sort((a, b) => {
|
|
1527
|
+
const dateA = a.resource?.recordedOn;
|
|
1528
|
+
const dateB = b.resource?.recordedOn;
|
|
1529
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1453
1532
|
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1454
1533
|
}
|
|
1455
1534
|
/**
|
|
@@ -1461,7 +1540,6 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1461
1540
|
static generateStaticNarrative(resource, timezone) {
|
|
1462
1541
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1463
1542
|
let html = `
|
|
1464
|
-
<h5>Medical Devices</h5>
|
|
1465
1543
|
<table>
|
|
1466
1544
|
<thead>
|
|
1467
1545
|
<tr>
|
|
@@ -1515,10 +1593,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1515
1593
|
let html = "";
|
|
1516
1594
|
const observations = this.getObservations(resource);
|
|
1517
1595
|
if (observations.length > 0) {
|
|
1596
|
+
observations.sort((a, b) => {
|
|
1597
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1598
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1599
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1600
|
+
});
|
|
1518
1601
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1519
1602
|
}
|
|
1520
1603
|
const diagnosticReports = this.getDiagnosticReports(resource);
|
|
1521
1604
|
if (diagnosticReports.length > 0) {
|
|
1605
|
+
diagnosticReports.sort((a, b) => {
|
|
1606
|
+
const dateA = a.issued;
|
|
1607
|
+
const dateB = b.issued;
|
|
1608
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1609
|
+
});
|
|
1522
1610
|
html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
|
|
1523
1611
|
}
|
|
1524
1612
|
return html;
|
|
@@ -1554,7 +1642,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1554
1642
|
*/
|
|
1555
1643
|
static renderObservations(templateUtilities, observations, timezone) {
|
|
1556
1644
|
let html = `
|
|
1557
|
-
<h5>
|
|
1645
|
+
<h5>Observations</h5>
|
|
1558
1646
|
<table>
|
|
1559
1647
|
<thead>
|
|
1560
1648
|
<tr>
|
|
@@ -1577,7 +1665,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1577
1665
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
1578
1666
|
<td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
|
|
1579
1667
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1580
|
-
<td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
|
|
1668
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1581
1669
|
</tr>`;
|
|
1582
1670
|
}
|
|
1583
1671
|
html += `
|
|
@@ -1594,7 +1682,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1594
1682
|
*/
|
|
1595
1683
|
static renderDiagnosticReports(templateUtilities, reports, timezone) {
|
|
1596
1684
|
let html = `
|
|
1597
|
-
<h5>Diagnostic
|
|
1685
|
+
<h5>Diagnostic Reports</h5>
|
|
1598
1686
|
<table>
|
|
1599
1687
|
<thead>
|
|
1600
1688
|
<tr>
|
|
@@ -1636,6 +1724,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1636
1724
|
* @returns HTML string for rendering
|
|
1637
1725
|
*/
|
|
1638
1726
|
generateNarrative(resource, timezone) {
|
|
1727
|
+
resource.entry?.sort((a, b) => {
|
|
1728
|
+
const dateA = a.resource.performedDateTime || a.resource.performedPeriod?.start;
|
|
1729
|
+
const dateB = b.resource.performedDateTime || b.resource.performedPeriod?.start;
|
|
1730
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1731
|
+
});
|
|
1639
1732
|
return _HistoryOfProceduresTemplate.generateStaticNarrative(resource, timezone);
|
|
1640
1733
|
}
|
|
1641
1734
|
/**
|
|
@@ -1647,7 +1740,6 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1647
1740
|
static generateStaticNarrative(resource, timezone) {
|
|
1648
1741
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1649
1742
|
let html = `
|
|
1650
|
-
<h5>History Of Procedures</h5>
|
|
1651
1743
|
<table>
|
|
1652
1744
|
<thead>
|
|
1653
1745
|
<tr>
|
|
@@ -1660,14 +1752,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1660
1752
|
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1661
1753
|
for (const entry of resource.entry) {
|
|
1662
1754
|
const proc = entry.resource;
|
|
1663
|
-
if (proc.resourceType === "Composition") {
|
|
1664
|
-
continue;
|
|
1665
|
-
}
|
|
1666
1755
|
html += `
|
|
1667
1756
|
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
1668
1757
|
<td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
|
|
1669
1758
|
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
1670
|
-
<td>${templateUtilities.renderTime(proc.performedDateTime, timezone)}</td>
|
|
1759
|
+
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
1671
1760
|
</tr>`;
|
|
1672
1761
|
}
|
|
1673
1762
|
}
|
|
@@ -1697,8 +1786,13 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1697
1786
|
*/
|
|
1698
1787
|
static generateStaticNarrative(resource, timezone) {
|
|
1699
1788
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1789
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
1790
|
+
observations.sort((a, b) => {
|
|
1791
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1792
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1793
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1794
|
+
});
|
|
1700
1795
|
let html = `
|
|
1701
|
-
<h5>Social History</h5>
|
|
1702
1796
|
<table>
|
|
1703
1797
|
<thead>
|
|
1704
1798
|
<tr>
|
|
@@ -1710,21 +1804,15 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1710
1804
|
</tr>
|
|
1711
1805
|
</thead>
|
|
1712
1806
|
<tbody>`;
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
const obs = entry.resource;
|
|
1716
|
-
if (obs.resourceType === "Composition") {
|
|
1717
|
-
continue;
|
|
1718
|
-
}
|
|
1719
|
-
html += `
|
|
1807
|
+
for (const obs of observations) {
|
|
1808
|
+
html += `
|
|
1720
1809
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1721
|
-
<td>${templateUtilities.codeableConcept(obs.code
|
|
1810
|
+
<td>${templateUtilities.codeableConcept(obs.code)}</td>
|
|
1722
1811
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
1723
1812
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
1724
1813
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1725
|
-
<td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
|
|
1814
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1726
1815
|
</tr>`;
|
|
1727
|
-
}
|
|
1728
1816
|
}
|
|
1729
1817
|
html += `
|
|
1730
1818
|
</tbody>
|
|
@@ -1743,36 +1831,38 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1743
1831
|
*/
|
|
1744
1832
|
generateNarrative(resource, timezone) {
|
|
1745
1833
|
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
|
-
}
|
|
1834
|
+
let html = ``;
|
|
1835
|
+
const resolvedConditions = resource.entry?.map((entry) => entry.resource) || [];
|
|
1836
|
+
resolvedConditions.sort((a, b) => {
|
|
1837
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1838
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1839
|
+
return dateB - dateA;
|
|
1840
|
+
});
|
|
1773
1841
|
html += `
|
|
1774
|
-
|
|
1775
|
-
|
|
1842
|
+
<table>
|
|
1843
|
+
<thead>
|
|
1844
|
+
<tr>
|
|
1845
|
+
<th>Problem</th>
|
|
1846
|
+
<th>Severity</th>
|
|
1847
|
+
<th>Onset Date</th>
|
|
1848
|
+
<th>Recorded Date</th>
|
|
1849
|
+
<th>Resolved Date</th>
|
|
1850
|
+
<th>Notes</th>
|
|
1851
|
+
</tr>
|
|
1852
|
+
</thead>
|
|
1853
|
+
<tbody>`;
|
|
1854
|
+
for (const cond of resolvedConditions) {
|
|
1855
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1856
|
+
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1857
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1858
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1859
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1860
|
+
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
1861
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1862
|
+
</tr>`;
|
|
1863
|
+
}
|
|
1864
|
+
html += `</tbody>
|
|
1865
|
+
</table>`;
|
|
1776
1866
|
return html;
|
|
1777
1867
|
}
|
|
1778
1868
|
};
|
|
@@ -1785,15 +1875,19 @@ var PlanOfCareTemplate = class {
|
|
|
1785
1875
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1786
1876
|
* @returns HTML string for rendering
|
|
1787
1877
|
*/
|
|
1788
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1789
1878
|
generateNarrative(resource, timezone) {
|
|
1790
1879
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1880
|
+
const carePlans = resource.entry?.map((entry) => entry.resource) || [];
|
|
1881
|
+
carePlans.sort((a, b) => {
|
|
1882
|
+
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1883
|
+
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
1884
|
+
return endB - endA;
|
|
1885
|
+
});
|
|
1791
1886
|
let html = `
|
|
1792
|
-
<h5>Plan of Care</h5>
|
|
1793
1887
|
<table>
|
|
1794
1888
|
<thead>
|
|
1795
1889
|
<tr>
|
|
1796
|
-
<th>
|
|
1890
|
+
<th>Description</th>
|
|
1797
1891
|
<th>Intent</th>
|
|
1798
1892
|
<th>Comments</th>
|
|
1799
1893
|
<th>Planned Start</th>
|
|
@@ -1801,21 +1895,15 @@ var PlanOfCareTemplate = class {
|
|
|
1801
1895
|
</tr>
|
|
1802
1896
|
</thead>
|
|
1803
1897
|
<tbody>`;
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
const cp = entry.resource;
|
|
1807
|
-
if (cp.resourceType === "Composition") {
|
|
1808
|
-
continue;
|
|
1809
|
-
}
|
|
1810
|
-
html += `
|
|
1898
|
+
for (const cp of carePlans) {
|
|
1899
|
+
html += `
|
|
1811
1900
|
<tr id="${templateUtilities.narrativeLinkId(cp)}">
|
|
1812
|
-
<td>${cp.description || ""}</td>
|
|
1813
|
-
<td>${cp.intent ||
|
|
1901
|
+
<td>${cp.description || cp.title || ""}</td>
|
|
1902
|
+
<td>${cp.intent || ""}</td>
|
|
1814
1903
|
<td>${templateUtilities.concat(cp.note, "text")}</td>
|
|
1815
|
-
<td>${cp.period?.start
|
|
1816
|
-
<td>${cp.period?.end
|
|
1904
|
+
<td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
|
|
1905
|
+
<td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
|
|
1817
1906
|
</tr>`;
|
|
1818
|
-
}
|
|
1819
1907
|
}
|
|
1820
1908
|
html += `
|
|
1821
1909
|
</tbody>
|
|
@@ -1843,38 +1931,112 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
1843
1931
|
*/
|
|
1844
1932
|
static generateStaticNarrative(resource, timezone) {
|
|
1845
1933
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1846
|
-
let html =
|
|
1847
|
-
|
|
1934
|
+
let html = ``;
|
|
1935
|
+
const activeConditions = [];
|
|
1936
|
+
const clinicalImpressions = [];
|
|
1937
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1938
|
+
for (const entry of resource.entry) {
|
|
1939
|
+
if (entry.resource?.resourceType === "Condition") {
|
|
1940
|
+
const cond = entry.resource;
|
|
1941
|
+
const isResolved = cond.clinicalStatus?.coding?.some(
|
|
1942
|
+
(c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
|
|
1943
|
+
);
|
|
1944
|
+
if (!isResolved) {
|
|
1945
|
+
activeConditions.push(cond);
|
|
1946
|
+
}
|
|
1947
|
+
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
1948
|
+
clinicalImpressions.push(entry.resource);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
activeConditions.sort((a, b) => {
|
|
1953
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1954
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1955
|
+
return dateB - dateA;
|
|
1956
|
+
});
|
|
1957
|
+
clinicalImpressions.sort((a, b) => {
|
|
1958
|
+
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;
|
|
1959
|
+
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;
|
|
1960
|
+
return dateB - dateA;
|
|
1961
|
+
});
|
|
1962
|
+
if (activeConditions.length > 0) {
|
|
1963
|
+
html += `<h3>Conditions</h3>
|
|
1964
|
+
<table>
|
|
1965
|
+
<thead>
|
|
1966
|
+
<tr>
|
|
1967
|
+
<th>Problem</th>
|
|
1968
|
+
<th>Severity</th>
|
|
1969
|
+
<th>Onset Date</th>
|
|
1970
|
+
<th>Recorded Date</th>
|
|
1971
|
+
<th>Notes</th>
|
|
1972
|
+
</tr>
|
|
1973
|
+
</thead>
|
|
1974
|
+
<tbody>`;
|
|
1975
|
+
for (const cond of activeConditions) {
|
|
1976
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1977
|
+
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1978
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1979
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1980
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1981
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
1982
|
+
</tr>`;
|
|
1983
|
+
}
|
|
1984
|
+
html += `</tbody>
|
|
1985
|
+
</table>`;
|
|
1986
|
+
}
|
|
1987
|
+
if (clinicalImpressions.length > 0) {
|
|
1988
|
+
html += `<h3>Clinical Impressions</h3>
|
|
1848
1989
|
<table>
|
|
1849
1990
|
<thead>
|
|
1850
1991
|
<tr>
|
|
1851
|
-
<th>Assessment</th>
|
|
1852
|
-
<th>Status</th>
|
|
1853
|
-
<th>Finding</th>
|
|
1854
|
-
<th>Comments</th>
|
|
1855
1992
|
<th>Date</th>
|
|
1993
|
+
<th>Status</th>
|
|
1994
|
+
<th>Description</th>
|
|
1995
|
+
<th>Summary</th>
|
|
1996
|
+
<th>Findings</th>
|
|
1997
|
+
<th>Notes</th>
|
|
1856
1998
|
</tr>
|
|
1857
1999
|
</thead>
|
|
1858
2000
|
<tbody>`;
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2001
|
+
for (const impression of clinicalImpressions) {
|
|
2002
|
+
let formattedDate = "";
|
|
2003
|
+
if (impression.effectiveDateTime) {
|
|
2004
|
+
formattedDate = templateUtilities.renderTime(
|
|
2005
|
+
impression.effectiveDateTime,
|
|
2006
|
+
timezone
|
|
2007
|
+
);
|
|
2008
|
+
} else if (impression.effectivePeriod) {
|
|
2009
|
+
formattedDate = templateUtilities.renderPeriod(
|
|
2010
|
+
impression.effectivePeriod,
|
|
2011
|
+
timezone
|
|
2012
|
+
);
|
|
2013
|
+
} else if (impression.date) {
|
|
2014
|
+
formattedDate = templateUtilities.renderDate(impression.date);
|
|
1864
2015
|
}
|
|
2016
|
+
let findingsHtml = "";
|
|
2017
|
+
if (impression.finding && impression.finding.length > 0) {
|
|
2018
|
+
findingsHtml = "<ul>";
|
|
2019
|
+
for (const finding of impression.finding) {
|
|
2020
|
+
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
2021
|
+
const cause = finding.basis || "";
|
|
2022
|
+
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
2023
|
+
}
|
|
2024
|
+
findingsHtml += "</ul>";
|
|
2025
|
+
}
|
|
2026
|
+
const notes = templateUtilities.renderNotes(impression.note, timezone);
|
|
1865
2027
|
html += `
|
|
1866
|
-
<tr id="${templateUtilities.narrativeLinkId(
|
|
1867
|
-
<td>${
|
|
1868
|
-
<td>${
|
|
1869
|
-
<td>${
|
|
1870
|
-
<td>${
|
|
1871
|
-
<td>${
|
|
2028
|
+
<tr id="${templateUtilities.narrativeLinkId(impression)}">
|
|
2029
|
+
<td>${formattedDate}</td>
|
|
2030
|
+
<td>${impression.status || ""}</td>
|
|
2031
|
+
<td>${impression.description || ""}</td>
|
|
2032
|
+
<td>${impression.summary || ""}</td>
|
|
2033
|
+
<td>${findingsHtml}</td>
|
|
2034
|
+
<td>${notes}</td>
|
|
1872
2035
|
</tr>`;
|
|
1873
2036
|
}
|
|
2037
|
+
html += `</tbody>
|
|
2038
|
+
</table>`;
|
|
1874
2039
|
}
|
|
1875
|
-
html += `
|
|
1876
|
-
</tbody>
|
|
1877
|
-
</table>`;
|
|
1878
2040
|
return html;
|
|
1879
2041
|
}
|
|
1880
2042
|
};
|
|
@@ -1898,32 +2060,30 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
1898
2060
|
*/
|
|
1899
2061
|
static generateStaticNarrative(resource, timezone) {
|
|
1900
2062
|
const templateUtilities = new TemplateUtilities(resource);
|
|
2063
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
2064
|
+
observations.sort((a, b) => {
|
|
2065
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2066
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
2067
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
2068
|
+
});
|
|
1901
2069
|
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 += `
|
|
2070
|
+
<table>
|
|
2071
|
+
<thead>
|
|
2072
|
+
<tr>
|
|
2073
|
+
<th>Result</th>
|
|
2074
|
+
<th>Comments</th>
|
|
2075
|
+
<th>Date</th>
|
|
2076
|
+
</tr>
|
|
2077
|
+
</thead>
|
|
2078
|
+
<tbody>`;
|
|
2079
|
+
for (const resource2 of observations) {
|
|
2080
|
+
const obs = resource2;
|
|
2081
|
+
html += `
|
|
1920
2082
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1921
|
-
<td>${templateUtilities.
|
|
1922
|
-
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2083
|
+
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
1923
2084
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1924
|
-
<td>${templateUtilities.
|
|
2085
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1925
2086
|
</tr>`;
|
|
1926
|
-
}
|
|
1927
2087
|
}
|
|
1928
2088
|
html += `
|
|
1929
2089
|
</tbody>
|
|
@@ -1941,6 +2101,13 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1941
2101
|
* @returns HTML string for rendering
|
|
1942
2102
|
*/
|
|
1943
2103
|
generateNarrative(resource, timezone) {
|
|
2104
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
2105
|
+
resource.entry.sort((a, b) => {
|
|
2106
|
+
const dateA = new Date(a.resource.dateTime || 0);
|
|
2107
|
+
const dateB = new Date(b.resource.dateTime || 0);
|
|
2108
|
+
return dateB.getTime() - dateA.getTime();
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
1944
2111
|
return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
|
|
1945
2112
|
}
|
|
1946
2113
|
/**
|
|
@@ -1953,7 +2120,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1953
2120
|
static generateStaticNarrative(resource, timezone) {
|
|
1954
2121
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1955
2122
|
let html = `
|
|
1956
|
-
<h5>Advance Directives</h5>
|
|
1957
2123
|
<table>
|
|
1958
2124
|
<thead>
|
|
1959
2125
|
<tr>
|
|
@@ -1967,9 +2133,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1967
2133
|
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1968
2134
|
for (const entry of resource.entry) {
|
|
1969
2135
|
const consent = entry.resource;
|
|
1970
|
-
if (consent.resourceType === "Composition") {
|
|
1971
|
-
continue;
|
|
1972
|
-
}
|
|
1973
2136
|
html += `
|
|
1974
2137
|
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
1975
2138
|
<td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
|
|
@@ -1986,171 +2149,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1986
2149
|
}
|
|
1987
2150
|
};
|
|
1988
2151
|
|
|
1989
|
-
// src/narratives/templates/typescript/FamilyHistoryTemplate.ts
|
|
1990
|
-
var FamilyHistoryTemplate = class {
|
|
1991
|
-
/**
|
|
1992
|
-
* Generate HTML narrative for Family History
|
|
1993
|
-
* @param resource - FHIR Bundle containing FamilyMemberHistory resources
|
|
1994
|
-
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1995
|
-
* @returns HTML string for rendering
|
|
1996
|
-
*/
|
|
1997
|
-
generateNarrative(resource, timezone) {
|
|
1998
|
-
const templateUtilities = new TemplateUtilities(resource);
|
|
1999
|
-
let html = `
|
|
2000
|
-
<h5>Family History</h5>
|
|
2001
|
-
<table>
|
|
2002
|
-
<thead>
|
|
2003
|
-
<tr>
|
|
2004
|
-
<th>Relationship</th>
|
|
2005
|
-
<th>Condition</th>
|
|
2006
|
-
<th>Status</th>
|
|
2007
|
-
<th>Onset</th>
|
|
2008
|
-
<th>Notes</th>
|
|
2009
|
-
</tr>
|
|
2010
|
-
</thead>
|
|
2011
|
-
<tbody>`;
|
|
2012
|
-
if (resource.entry && Array.isArray(resource.entry)) {
|
|
2013
|
-
let hasFamilyHistory = false;
|
|
2014
|
-
for (const entry of resource.entry) {
|
|
2015
|
-
const familyHistory = entry.resource;
|
|
2016
|
-
if (!familyHistory || familyHistory.resourceType !== "FamilyMemberHistory") {
|
|
2017
|
-
continue;
|
|
2018
|
-
}
|
|
2019
|
-
hasFamilyHistory = true;
|
|
2020
|
-
const fmh = familyHistory;
|
|
2021
|
-
const relationship = templateUtilities.codeableConcept(fmh.relationship, "display");
|
|
2022
|
-
if (fmh.condition && Array.isArray(fmh.condition)) {
|
|
2023
|
-
for (const condition of fmh.condition) {
|
|
2024
|
-
const conditionCode = templateUtilities.codeableConcept(condition.code, "display");
|
|
2025
|
-
const status = fmh.status || "";
|
|
2026
|
-
let onset = "";
|
|
2027
|
-
if (condition.onsetAge) {
|
|
2028
|
-
onset = templateUtilities.renderOnset(condition.onsetAge, timezone);
|
|
2029
|
-
}
|
|
2030
|
-
const notes = condition.note ? templateUtilities.renderNotes(condition.note, timezone) : templateUtilities.renderNotes(fmh.note, timezone);
|
|
2031
|
-
html += `
|
|
2032
|
-
<tr id="${templateUtilities.narrativeLinkId(fmh)}">
|
|
2033
|
-
<td>${relationship}</td>
|
|
2034
|
-
<td>${conditionCode}</td>
|
|
2035
|
-
<td>${status}</td>
|
|
2036
|
-
<td>${onset}</td>
|
|
2037
|
-
<td>${notes}</td>
|
|
2038
|
-
</tr>`;
|
|
2039
|
-
}
|
|
2040
|
-
} else {
|
|
2041
|
-
html += `
|
|
2042
|
-
<tr id="${templateUtilities.narrativeLinkId(fmh)}">
|
|
2043
|
-
<td>${relationship}</td>
|
|
2044
|
-
<td>Not specified</td>
|
|
2045
|
-
<td>${fmh.status || ""}</td>
|
|
2046
|
-
<td></td>
|
|
2047
|
-
<td>${templateUtilities.renderNotes(fmh.note, timezone)}</td>
|
|
2048
|
-
</tr>`;
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
if (!hasFamilyHistory) {
|
|
2052
|
-
html += `
|
|
2053
|
-
<tr>
|
|
2054
|
-
<td colspan="5">No family history recorded</td>
|
|
2055
|
-
</tr>`;
|
|
2056
|
-
}
|
|
2057
|
-
} else {
|
|
2058
|
-
html += `
|
|
2059
|
-
<tr>
|
|
2060
|
-
<td colspan="5">No family history recorded</td>
|
|
2061
|
-
</tr>`;
|
|
2062
|
-
}
|
|
2063
|
-
html += `
|
|
2064
|
-
</tbody>
|
|
2065
|
-
</table>`;
|
|
2066
|
-
return html;
|
|
2067
|
-
}
|
|
2068
|
-
};
|
|
2069
|
-
|
|
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
2152
|
// src/narratives/templates/typescript/TypeScriptTemplateMapper.ts
|
|
2155
2153
|
var TypeScriptTemplateMapper = class {
|
|
2156
2154
|
/**
|
|
@@ -2173,20 +2171,18 @@ var TypeScriptTemplateMapper = class {
|
|
|
2173
2171
|
TypeScriptTemplateMapper.sectionToTemplate = {
|
|
2174
2172
|
["Patient" /* PATIENT */]: new PatientTemplate(),
|
|
2175
2173
|
["AllergyIntoleranceSection" /* ALLERGIES */]: new AllergyIntoleranceTemplate(),
|
|
2176
|
-
["
|
|
2174
|
+
["MedicationSummarySection" /* MEDICATIONS */]: new MedicationSummaryTemplate(),
|
|
2177
2175
|
["ImmunizationSection" /* IMMUNIZATIONS */]: new ImmunizationsTemplate(),
|
|
2178
2176
|
["ProblemSection" /* PROBLEMS */]: new ProblemListTemplate(),
|
|
2179
2177
|
["VitalSignsSection" /* VITAL_SIGNS */]: new VitalSignsTemplate(),
|
|
2180
2178
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: new MedicalDevicesTemplate(),
|
|
2181
|
-
["
|
|
2182
|
-
["
|
|
2183
|
-
["FamilyHistorySection" /* FAMILY_HISTORY */]: new FamilyHistoryTemplate(),
|
|
2179
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
|
|
2180
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
|
|
2184
2181
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: new SocialHistoryTemplate(),
|
|
2185
|
-
["
|
|
2182
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
|
|
2186
2183
|
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: new FunctionalStatusTemplate(),
|
|
2187
|
-
["
|
|
2188
|
-
["
|
|
2189
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: new ClinicalImpressionTemplate(),
|
|
2184
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: new PastHistoryOfIllnessTemplate(),
|
|
2185
|
+
["PlanOfCareSection" /* CARE_PLAN */]: new PlanOfCareTemplate(),
|
|
2190
2186
|
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: new AdvanceDirectivesTemplate()
|
|
2191
2187
|
};
|
|
2192
2188
|
|
|
@@ -2223,10 +2219,9 @@ var NarrativeGenerator = class {
|
|
|
2223
2219
|
* @param section - IPS section type
|
|
2224
2220
|
* @param resources - Array of domain resources
|
|
2225
2221
|
* @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
2222
|
* @returns Generated HTML content or undefined if no resources
|
|
2228
2223
|
*/
|
|
2229
|
-
static async generateNarrativeContentAsync(section, resources, timezone
|
|
2224
|
+
static async generateNarrativeContentAsync(section, resources, timezone) {
|
|
2230
2225
|
if (!resources || resources.length === 0) {
|
|
2231
2226
|
return void 0;
|
|
2232
2227
|
}
|
|
@@ -2242,11 +2237,7 @@ var NarrativeGenerator = class {
|
|
|
2242
2237
|
if (!content) {
|
|
2243
2238
|
return void 0;
|
|
2244
2239
|
}
|
|
2245
|
-
|
|
2246
|
-
return await this.wrapInXhtmlAsync(content);
|
|
2247
|
-
} else {
|
|
2248
|
-
return await this.minifyHtmlAsync(content);
|
|
2249
|
-
}
|
|
2240
|
+
return content;
|
|
2250
2241
|
} catch (error) {
|
|
2251
2242
|
console.error(`Error generating narrative for section ${section}:`, error);
|
|
2252
2243
|
return `<div class="error">Error generating narrative: ${error instanceof Error ? error.message : String(error)}</div>`;
|
|
@@ -2293,35 +2284,35 @@ var NarrativeGenerator = class {
|
|
|
2293
2284
|
* @param resources - Array of domain resources
|
|
2294
2285
|
* @param timezone - Optional timezone to use for date formatting
|
|
2295
2286
|
* @param minify - Whether to minify the HTML content (default: true)
|
|
2296
|
-
* @param wrapInXhtml - Whether to wrap the content in XHTML div
|
|
2297
2287
|
* @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
|
|
2298
2288
|
*/
|
|
2299
|
-
static async generateNarrativeAsync(section, resources, timezone, minify = true
|
|
2300
|
-
const content = await this.generateNarrativeContentAsync(section, resources, timezone
|
|
2289
|
+
static async generateNarrativeAsync(section, resources, timezone, minify = true) {
|
|
2290
|
+
const content = await this.generateNarrativeContentAsync(section, resources, timezone);
|
|
2301
2291
|
if (!content) {
|
|
2302
2292
|
return void 0;
|
|
2303
2293
|
}
|
|
2304
2294
|
return await this.createNarrativeAsync(content, minify);
|
|
2305
2295
|
}
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2296
|
+
};
|
|
2297
|
+
|
|
2298
|
+
// src/structures/ips_mandatory_sections.ts
|
|
2299
|
+
var IPSMandatorySections = ((IPSMandatorySections2) => {
|
|
2300
|
+
IPSMandatorySections2[IPSMandatorySections2["PATIENT"] = "Patient" /* PATIENT */] = "PATIENT";
|
|
2301
|
+
IPSMandatorySections2[IPSMandatorySections2["PROBLEMS"] = "ProblemSection" /* PROBLEMS */] = "PROBLEMS";
|
|
2302
|
+
IPSMandatorySections2[IPSMandatorySections2["ALLERGIES"] = "AllergyIntoleranceSection" /* ALLERGIES */] = "ALLERGIES";
|
|
2303
|
+
IPSMandatorySections2[IPSMandatorySections2["MEDICATIONS"] = "MedicationSummarySection" /* MEDICATIONS */] = "MEDICATIONS";
|
|
2304
|
+
return IPSMandatorySections2;
|
|
2305
|
+
})(IPSMandatorySections || {});
|
|
2306
|
+
var IPSMissingMandatorySectionContent = {
|
|
2307
|
+
["ProblemSection" /* PROBLEMS */]: "There is no information available about the subject's health problems or disabilities.",
|
|
2308
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: "There is no information available regarding the subject's allergy conditions.",
|
|
2309
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "There is no information available about the subject's medication use or administration."
|
|
2318
2310
|
};
|
|
2319
2311
|
|
|
2320
2312
|
// src/generators/fhir_summary_generator.ts
|
|
2321
2313
|
var ComprehensiveIPSCompositionBuilder = class {
|
|
2322
2314
|
constructor() {
|
|
2323
2315
|
this.sections = [];
|
|
2324
|
-
this.mandatorySectionsAdded = /* @__PURE__ */ new Set();
|
|
2325
2316
|
this.resources = /* @__PURE__ */ new Set();
|
|
2326
2317
|
}
|
|
2327
2318
|
/**
|
|
@@ -2339,35 +2330,35 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2339
2330
|
/**
|
|
2340
2331
|
* Adds a section to the composition with async HTML minification
|
|
2341
2332
|
* @param sectionType - IPS section type
|
|
2342
|
-
* @param
|
|
2333
|
+
* @param validResources - Array of domain resources
|
|
2343
2334
|
* @param timezone - Optional timezone to use for date formatting
|
|
2344
|
-
* @param options - Optional configuration options
|
|
2345
2335
|
*/
|
|
2346
|
-
async addSectionAsync(sectionType,
|
|
2347
|
-
const validResources = resources;
|
|
2336
|
+
async addSectionAsync(sectionType, validResources, timezone) {
|
|
2348
2337
|
for (const resource of validResources) {
|
|
2349
2338
|
this.resources.add(resource);
|
|
2350
2339
|
}
|
|
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
2340
|
if (sectionType !== "Patient" /* PATIENT */) {
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2341
|
+
let narrative = void 0;
|
|
2342
|
+
if (validResources.length > 0) {
|
|
2343
|
+
narrative = await NarrativeGenerator.generateNarrativeAsync(
|
|
2344
|
+
sectionType,
|
|
2345
|
+
validResources,
|
|
2346
|
+
timezone,
|
|
2347
|
+
true
|
|
2348
|
+
);
|
|
2349
|
+
} else if (sectionType in IPSMandatorySections) {
|
|
2350
|
+
narrative = await NarrativeGenerator.createNarrativeAsync(
|
|
2351
|
+
IPSMissingMandatorySectionContent[sectionType]
|
|
2352
|
+
);
|
|
2353
|
+
} else {
|
|
2354
|
+
return this;
|
|
2355
|
+
}
|
|
2365
2356
|
const sectionEntry = {
|
|
2366
2357
|
title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
|
|
2367
2358
|
code: {
|
|
2368
2359
|
coding: [{
|
|
2369
2360
|
system: "http://loinc.org",
|
|
2370
|
-
code:
|
|
2361
|
+
code: IPS_SECTION_LOINC_CODES[sectionType],
|
|
2371
2362
|
display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
2372
2363
|
}],
|
|
2373
2364
|
text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
@@ -2378,9 +2369,6 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2378
2369
|
display: resource.resourceType
|
|
2379
2370
|
}))
|
|
2380
2371
|
};
|
|
2381
|
-
if (!options?.isOptional) {
|
|
2382
|
-
this.mandatorySectionsAdded.add(sectionType);
|
|
2383
|
-
}
|
|
2384
2372
|
this.sections.push(sectionEntry);
|
|
2385
2373
|
}
|
|
2386
2374
|
return this;
|
|
@@ -2399,43 +2387,20 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2399
2387
|
throw new Error("Patient resource not found in the bundle");
|
|
2400
2388
|
}
|
|
2401
2389
|
this.patient = patientEntry.resource;
|
|
2390
|
+
const resources = bundle.entry.map((e) => e.resource);
|
|
2402
2391
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2403
2392
|
const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
|
|
2404
2393
|
const customFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2405
|
-
let
|
|
2394
|
+
let sectionResources = resources.filter(
|
|
2395
|
+
(r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
|
|
2396
|
+
);
|
|
2406
2397
|
if (customFilter) {
|
|
2407
|
-
|
|
2408
|
-
}
|
|
2409
|
-
if (resources.length > 0) {
|
|
2410
|
-
await this.addSectionAsync(sectionType, resources, timezone, {
|
|
2411
|
-
isOptional: true
|
|
2412
|
-
});
|
|
2398
|
+
sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
|
|
2413
2399
|
}
|
|
2400
|
+
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2414
2401
|
}
|
|
2415
2402
|
return this;
|
|
2416
2403
|
}
|
|
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
2404
|
/**
|
|
2440
2405
|
* Builds a complete FHIR Bundle containing the Composition and all resources.
|
|
2441
2406
|
* @param authorOrganizationId - ID of the authoring organization (e.g., hospital or clinic)
|
|
@@ -2472,7 +2437,12 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2472
2437
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2473
2438
|
title: "International Patient Summary",
|
|
2474
2439
|
section: this.sections,
|
|
2475
|
-
text: await
|
|
2440
|
+
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2441
|
+
"Patient" /* PATIENT */,
|
|
2442
|
+
[this.patient],
|
|
2443
|
+
timezone,
|
|
2444
|
+
true
|
|
2445
|
+
)
|
|
2476
2446
|
};
|
|
2477
2447
|
const bundle = {
|
|
2478
2448
|
resourceType: "Bundle",
|
|
@@ -2513,41 +2483,11 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2513
2483
|
return bundle;
|
|
2514
2484
|
}
|
|
2515
2485
|
/**
|
|
2516
|
-
*
|
|
2517
|
-
* @
|
|
2518
|
-
* @private
|
|
2486
|
+
* Returns the Composition sections without creating a full bundle.
|
|
2487
|
+
* @returns Array of TCompositionSection
|
|
2519
2488
|
*/
|
|
2520
|
-
|
|
2521
|
-
|
|
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
|
-
};
|
|
2489
|
+
getSections() {
|
|
2490
|
+
return this.sections;
|
|
2551
2491
|
}
|
|
2552
2492
|
};
|
|
2553
2493
|
|