@imranq2/fhirpatientsummary 1.0.11 → 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 +569 -549
- package/dist/index.d.cts +15 -57
- package/dist/index.d.ts +15 -57
- package/dist/index.js +569 -549
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
// src/structures/ips_sections.ts
|
|
2
2
|
var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
|
|
3
3
|
IPSSections2["PATIENT"] = "Patient";
|
|
4
|
-
IPSSections2["ALLERGIES"] = "AllergyIntoleranceSection";
|
|
5
|
-
IPSSections2["MEDICATIONS"] = "MedicationSection";
|
|
6
4
|
IPSSections2["PROBLEMS"] = "ProblemSection";
|
|
5
|
+
IPSSections2["ALLERGIES"] = "AllergyIntoleranceSection";
|
|
6
|
+
IPSSections2["MEDICATIONS"] = "MedicationSummarySection";
|
|
7
7
|
IPSSections2["IMMUNIZATIONS"] = "ImmunizationSection";
|
|
8
|
-
IPSSections2["
|
|
8
|
+
IPSSections2["DIAGNOSTIC_REPORTS"] = "ResultsSection";
|
|
9
|
+
IPSSections2["PROCEDURES"] = "HistoryOfProceduresSection";
|
|
9
10
|
IPSSections2["MEDICAL_DEVICES"] = "MedicalDeviceSection";
|
|
10
|
-
IPSSections2["DIAGNOSTIC_REPORTS"] = "DiagnosticReportSection";
|
|
11
|
-
IPSSections2["PROCEDURES"] = "ProcedureSection";
|
|
12
|
-
IPSSections2["FAMILY_HISTORY"] = "FamilyHistorySection";
|
|
13
|
-
IPSSections2["SOCIAL_HISTORY"] = "SocialHistorySection";
|
|
14
|
-
IPSSections2["PREGNANCY_HISTORY"] = "PregnancyHistorySection";
|
|
15
|
-
IPSSections2["FUNCTIONAL_STATUS"] = "FunctionalStatusSection";
|
|
16
|
-
IPSSections2["MEDICAL_HISTORY"] = "MedicalHistorySection";
|
|
17
|
-
IPSSections2["CARE_PLAN"] = "CarePlanSection";
|
|
18
|
-
IPSSections2["CLINICAL_IMPRESSION"] = "ClinicalImpressionSection";
|
|
19
11
|
IPSSections2["ADVANCE_DIRECTIVES"] = "AdvanceDirectivesSection";
|
|
12
|
+
IPSSections2["FUNCTIONAL_STATUS"] = "FunctionalStatusSection";
|
|
13
|
+
IPSSections2["PREGNANCY_HISTORY"] = "HistoryOfPregnancySection";
|
|
14
|
+
IPSSections2["CARE_PLAN"] = "PlanOfCareSection";
|
|
15
|
+
IPSSections2["MEDICAL_HISTORY"] = "HistoryOfPastIllnessSection";
|
|
16
|
+
IPSSections2["SOCIAL_HISTORY"] = "SocialHistorySection";
|
|
17
|
+
IPSSections2["VITAL_SIGNS"] = "VitalSignsSection";
|
|
18
|
+
IPSSections2["FAMILY_HISTORY"] = "FamilyHistorySection";
|
|
20
19
|
return IPSSections2;
|
|
21
20
|
})(IPSSections || {});
|
|
22
21
|
|
|
@@ -24,99 +23,111 @@ var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
|
|
|
24
23
|
var IPS_SECTION_LOINC_CODES = {
|
|
25
24
|
["Patient" /* PATIENT */]: "54126-4",
|
|
26
25
|
["AllergyIntoleranceSection" /* ALLERGIES */]: "48765-2",
|
|
27
|
-
["
|
|
26
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "10160-0",
|
|
28
27
|
["ProblemSection" /* PROBLEMS */]: "11450-4",
|
|
29
28
|
["ImmunizationSection" /* IMMUNIZATIONS */]: "11369-6",
|
|
30
29
|
["VitalSignsSection" /* VITAL_SIGNS */]: "8716-3",
|
|
31
30
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "46264-8",
|
|
32
|
-
["
|
|
33
|
-
["
|
|
31
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
|
|
32
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: "47519-4",
|
|
34
33
|
["FamilyHistorySection" /* FAMILY_HISTORY */]: "10157-6",
|
|
35
34
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: "29762-2",
|
|
36
|
-
["
|
|
35
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "10162-6",
|
|
37
36
|
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "47420-5",
|
|
38
|
-
["
|
|
39
|
-
["
|
|
40
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: "51848-0",
|
|
37
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "11348-0",
|
|
38
|
+
["PlanOfCareSection" /* CARE_PLAN */]: "18776-5",
|
|
41
39
|
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "42348-3"
|
|
42
40
|
};
|
|
43
41
|
var IPS_SECTION_DISPLAY_NAMES = {
|
|
44
42
|
["Patient" /* PATIENT */]: "Patient summary Document",
|
|
45
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and
|
|
46
|
-
["
|
|
47
|
-
["ProblemSection" /* PROBLEMS */]: "Problem
|
|
48
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: "
|
|
49
|
-
["
|
|
50
|
-
["
|
|
51
|
-
["
|
|
52
|
-
["
|
|
53
|
-
["
|
|
54
|
-
["
|
|
55
|
-
["
|
|
56
|
-
["
|
|
57
|
-
["
|
|
58
|
-
["
|
|
59
|
-
["
|
|
60
|
-
|
|
43
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and Intolerances",
|
|
44
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "Medication Summary",
|
|
45
|
+
["ProblemSection" /* PROBLEMS */]: "Problem List",
|
|
46
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: "Immunizations",
|
|
47
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "Results Summary",
|
|
48
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: "History of Procedures",
|
|
49
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "History of Medical Devices",
|
|
50
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: "Vital Signs",
|
|
51
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "Advance Directives",
|
|
52
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "Functional Status",
|
|
53
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "History of Pregnancies",
|
|
54
|
+
["PlanOfCareSection" /* CARE_PLAN */]: "Plan of Care",
|
|
55
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "History of Past Illness",
|
|
56
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History",
|
|
57
|
+
["FamilyHistorySection" /* FAMILY_HISTORY */]: "History of Family Member Diseases"
|
|
58
|
+
};
|
|
59
|
+
var PREGNANCY_LONIC_CODES = {
|
|
60
|
+
PREGNANCY_STATUS: {
|
|
61
|
+
"LA15173-0": "Pregnant",
|
|
62
|
+
"LA26683-5": "Not pregnant",
|
|
63
|
+
"LA4489-6": "Unknown"
|
|
64
|
+
},
|
|
65
|
+
PREGNANCY_OUTCOME: {
|
|
66
|
+
"11636-8": "Live Birth",
|
|
67
|
+
"11637-6": "Preterm Birth",
|
|
68
|
+
"11638-4": "Still Living Birth",
|
|
69
|
+
"11639-2": "Term Birth",
|
|
70
|
+
"11640-0": "Total Births",
|
|
71
|
+
"11612-9": "Abortions",
|
|
72
|
+
"11613-7": "Induced Abortions",
|
|
73
|
+
"11614-5": "Spontaneous Abortions",
|
|
74
|
+
"33065-4": "Ectopic Pregnancy"
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var SOCIAL_HISTORY_LONIC_CODES = {
|
|
78
|
+
"72166-2": "Tobacco Use",
|
|
79
|
+
"74013-4": "Alcohol Use"
|
|
61
80
|
};
|
|
62
81
|
|
|
63
82
|
// src/structures/ips_section_resource_map.ts
|
|
64
83
|
var IPSSectionResourceMap = {
|
|
65
84
|
["Patient" /* PATIENT */]: ["Patient"],
|
|
66
85
|
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
67
|
-
["
|
|
86
|
+
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
87
|
+
// Medication resource is needed for identifying name of medication
|
|
68
88
|
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
69
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization"],
|
|
89
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
90
|
+
// Immunization can include Organization as a related resource
|
|
70
91
|
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
71
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["Device"],
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
["
|
|
92
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
93
|
+
// Device resource is used for medical devices name
|
|
94
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
95
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
75
96
|
["FamilyHistorySection" /* FAMILY_HISTORY */]: ["FamilyMemberHistory"],
|
|
76
97
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
|
|
77
|
-
|
|
78
|
-
["
|
|
79
|
-
|
|
80
|
-
["
|
|
81
|
-
|
|
82
|
-
["MedicalHistorySection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
83
|
-
// Medical history is often Condition
|
|
84
|
-
["CarePlanSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
85
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: ["ClinicalImpression"],
|
|
86
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["DocumentReference"]
|
|
87
|
-
// Advance directives are often stored as DocumentReference
|
|
98
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
99
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
100
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
101
|
+
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
102
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
88
103
|
};
|
|
89
104
|
var IPSSectionResourceFilters = {
|
|
90
105
|
// Only include active allergies
|
|
91
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => c.code === "
|
|
92
|
-
// Only include active medication requests/statements
|
|
93
|
-
["MedicationSection" /* MEDICATIONS */]: (resource) => resource.resourceType === "MedicationRequest" && resource.status === "active" || resource.resourceType === "MedicationStatement" && resource.status === "active",
|
|
106
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string"),
|
|
94
107
|
// Only include active problems/conditions
|
|
95
|
-
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => c.code
|
|
108
|
+
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
96
109
|
// Only include completed immunizations
|
|
97
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed",
|
|
110
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
98
111
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
99
112
|
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c.code === "vital-signs")),
|
|
100
|
-
// Only include active devices
|
|
101
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => resource.resourceType === "Device" && resource.status === "active",
|
|
102
113
|
// Only include finalized diagnostic reports
|
|
103
|
-
["
|
|
114
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
104
115
|
// Only include completed procedures
|
|
105
|
-
["
|
|
116
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
|
|
106
117
|
// Only include family history resources
|
|
107
118
|
["FamilyHistorySection" /* FAMILY_HISTORY */]: (resource) => resource.resourceType === "FamilyMemberHistory",
|
|
108
119
|
// Only include social history Observations (category.coding contains 'social-history')
|
|
109
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.
|
|
120
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LONIC_CODES).includes(c.code)),
|
|
110
121
|
// Only include pregnancy history Observations (category.coding contains 'pregnancy')
|
|
111
|
-
["
|
|
112
|
-
// Only include functional status
|
|
113
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "
|
|
114
|
-
// Only include
|
|
115
|
-
["
|
|
122
|
+
["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))),
|
|
123
|
+
// Only include active functional status Conditions or ClinicalImpressions
|
|
124
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string") || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
|
|
125
|
+
// Only include resolved medical history Conditions
|
|
126
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
|
|
116
127
|
// Only include active care plans
|
|
117
|
-
["
|
|
118
|
-
// Only include
|
|
119
|
-
["
|
|
128
|
+
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
129
|
+
// Only include active advance directives (Consent resources)
|
|
130
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active",
|
|
120
131
|
// Patient section: only Patient resource
|
|
121
132
|
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
|
|
122
133
|
};
|
|
@@ -158,8 +169,6 @@ var TemplateUtilities = class {
|
|
|
158
169
|
}
|
|
159
170
|
if (cc.text) {
|
|
160
171
|
return cc.text;
|
|
161
|
-
} else if ("display" in cc && cc.display) {
|
|
162
|
-
return cc.display;
|
|
163
172
|
} else if (cc.coding && cc.coding[0]) {
|
|
164
173
|
if (cc.coding[0].display) {
|
|
165
174
|
return cc.coding[0].display;
|
|
@@ -191,8 +200,8 @@ var TemplateUtilities = class {
|
|
|
191
200
|
*/
|
|
192
201
|
renderDevice(deviceRef) {
|
|
193
202
|
const device = deviceRef && this.resolveReference(deviceRef);
|
|
194
|
-
if (device && device.resourceType === "Device" && device.
|
|
195
|
-
return this.
|
|
203
|
+
if (device && device.resourceType === "Device" && device.deviceName && device.deviceName.length > 0) {
|
|
204
|
+
return this.safeConcat(device.deviceName, "name");
|
|
196
205
|
}
|
|
197
206
|
return "";
|
|
198
207
|
}
|
|
@@ -373,8 +382,12 @@ var TemplateUtilities = class {
|
|
|
373
382
|
}
|
|
374
383
|
const texts = [];
|
|
375
384
|
for (const item of list) {
|
|
376
|
-
if (item && item.manifestation && item.manifestation[0]
|
|
377
|
-
|
|
385
|
+
if (item && item.manifestation && item.manifestation[0]) {
|
|
386
|
+
if (item.manifestation[0].text) {
|
|
387
|
+
texts.push(item.manifestation[0].text);
|
|
388
|
+
} else if (item.manifestation[0].coding && item.manifestation[0].coding[0]?.display) {
|
|
389
|
+
texts.push(item.manifestation[0].coding[0].display);
|
|
390
|
+
}
|
|
378
391
|
}
|
|
379
392
|
}
|
|
380
393
|
return texts.join(", ");
|
|
@@ -533,6 +546,25 @@ var TemplateUtilities = class {
|
|
|
533
546
|
}
|
|
534
547
|
return null;
|
|
535
548
|
}
|
|
549
|
+
extractPregnancyStatus(observation) {
|
|
550
|
+
let status = "";
|
|
551
|
+
observation.code?.coding?.forEach((c) => {
|
|
552
|
+
if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) {
|
|
553
|
+
status = PREGNANCY_LONIC_CODES.PREGNANCY_STATUS[c.code];
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
if (observation.valueCodeableConcept) {
|
|
557
|
+
observation.valueCodeableConcept.coding?.forEach((c) => {
|
|
558
|
+
if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
|
|
559
|
+
if (status) {
|
|
560
|
+
status += " - ";
|
|
561
|
+
}
|
|
562
|
+
status += PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME[c.code];
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return status;
|
|
567
|
+
}
|
|
536
568
|
formatQuantityValue(quantity) {
|
|
537
569
|
if (!quantity) return "";
|
|
538
570
|
const parts = [];
|
|
@@ -771,7 +803,6 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
771
803
|
const patient = entry.resource;
|
|
772
804
|
html += `
|
|
773
805
|
<div>
|
|
774
|
-
<h2>Patient Summary</h2>
|
|
775
806
|
<ul>
|
|
776
807
|
<li><strong>Name(s):</strong>${this.renderNames(patient)}</li>
|
|
777
808
|
<li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
|
|
@@ -921,10 +952,20 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
921
952
|
}
|
|
922
953
|
}
|
|
923
954
|
}
|
|
955
|
+
activeAllergies.sort((a, b) => {
|
|
956
|
+
const dateA = a.onsetDateTime;
|
|
957
|
+
const dateB = b.onsetDateTime;
|
|
958
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
959
|
+
});
|
|
960
|
+
resolvedAllergies.sort((a, b) => {
|
|
961
|
+
const dateA = a.onsetDateTime;
|
|
962
|
+
const dateB = b.onsetDateTime;
|
|
963
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
964
|
+
});
|
|
924
965
|
let html = "";
|
|
925
966
|
html += `
|
|
926
967
|
<div class="ActiveAllergies">
|
|
927
|
-
<h3>Active
|
|
968
|
+
<h3>Active</h3>
|
|
928
969
|
<table class="ActiveAllergyTable">
|
|
929
970
|
<thead>
|
|
930
971
|
<tr>
|
|
@@ -952,7 +993,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
952
993
|
</div>`;
|
|
953
994
|
html += `
|
|
954
995
|
<div class="ResolvedAllergies">
|
|
955
|
-
<h3>Resolved
|
|
996
|
+
<h3>Resolved</h3>
|
|
956
997
|
<table class="ResolvedAllergyTable">
|
|
957
998
|
<thead>
|
|
958
999
|
<tr>
|
|
@@ -996,7 +1037,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
996
1037
|
<tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
|
|
997
1038
|
<td class="Name"><span class="AllergenName">${templateUtilities.codeableConcept(allergy.code)}</span></td>
|
|
998
1039
|
<td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
|
|
999
|
-
<td class="Category">${templateUtilities.safeConcat(allergy.category
|
|
1040
|
+
<td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
|
|
1000
1041
|
<td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
|
|
1001
1042
|
<td class="Severity">${templateUtilities.safeConcat(allergy.reaction, "severity") || "-"}</td>
|
|
1002
1043
|
<td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
|
|
@@ -1031,6 +1072,67 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1031
1072
|
generateNarrative(resource, timezone) {
|
|
1032
1073
|
return _MedicationSummaryTemplate.generateStaticNarrative(resource, timezone);
|
|
1033
1074
|
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Safely parse a date string and return a valid Date object or null
|
|
1077
|
+
* @param dateString - The date string to parse
|
|
1078
|
+
* @returns Date object or null if invalid
|
|
1079
|
+
*/
|
|
1080
|
+
static parseDate(dateString) {
|
|
1081
|
+
if (!dateString || dateString.trim() === "") {
|
|
1082
|
+
return null;
|
|
1083
|
+
}
|
|
1084
|
+
const date = new Date(dateString);
|
|
1085
|
+
return !isNaN(date.getTime()) ? date : null;
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Determine if a MedicationRequest is active
|
|
1089
|
+
* @param medicationRequest - The MedicationRequest resource
|
|
1090
|
+
* @returns boolean indicating if the medication request is active
|
|
1091
|
+
*/
|
|
1092
|
+
static isActiveMedicationRequest(medicationRequest) {
|
|
1093
|
+
const status = medicationRequest.status?.toLowerCase();
|
|
1094
|
+
if (status === "active" || status === "unknown") {
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
1097
|
+
if (status === "completed" || status === "cancelled" || status === "stopped" || status === "draft") {
|
|
1098
|
+
return false;
|
|
1099
|
+
}
|
|
1100
|
+
const endDate = medicationRequest.dispenseRequest?.validityPeriod?.end;
|
|
1101
|
+
if (!endDate) {
|
|
1102
|
+
return true;
|
|
1103
|
+
}
|
|
1104
|
+
const parsedEndDate = this.parseDate(endDate);
|
|
1105
|
+
if (!parsedEndDate) {
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1108
|
+
return parsedEndDate.getTime() > Date.now();
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Determine if a MedicationStatement is active
|
|
1112
|
+
* @param medicationStatement - The MedicationStatement resource
|
|
1113
|
+
* @returns boolean indicating if the medication statement is active
|
|
1114
|
+
*/
|
|
1115
|
+
static isActiveMedicationStatement(medicationStatement) {
|
|
1116
|
+
const status = medicationStatement.status?.toLowerCase();
|
|
1117
|
+
if (status === "active" || status === "intended" || status === "unknown") {
|
|
1118
|
+
return true;
|
|
1119
|
+
}
|
|
1120
|
+
if (status === "completed" || status === "stopped" || status === "not-taken") {
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
let endDate;
|
|
1124
|
+
if (medicationStatement.effectivePeriod?.end) {
|
|
1125
|
+
endDate = medicationStatement.effectivePeriod.end;
|
|
1126
|
+
}
|
|
1127
|
+
if (!endDate) {
|
|
1128
|
+
return true;
|
|
1129
|
+
}
|
|
1130
|
+
const parsedEndDate = this.parseDate(endDate);
|
|
1131
|
+
if (!parsedEndDate) {
|
|
1132
|
+
return true;
|
|
1133
|
+
}
|
|
1134
|
+
return parsedEndDate.getTime() > Date.now();
|
|
1135
|
+
}
|
|
1034
1136
|
/**
|
|
1035
1137
|
* Internal static implementation that actually generates the narrative
|
|
1036
1138
|
* @param resource - FHIR Bundle containing Medication resources
|
|
@@ -1042,12 +1144,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1042
1144
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1043
1145
|
let html = "";
|
|
1044
1146
|
const medicationRequests = this.getMedicationRequests(templateUtilities, resource);
|
|
1045
|
-
if (medicationRequests.length > 0) {
|
|
1046
|
-
html += this.renderMedicationRequests(templateUtilities, medicationRequests);
|
|
1047
|
-
}
|
|
1048
1147
|
const medicationStatements = this.getMedicationStatements(templateUtilities, resource);
|
|
1049
|
-
|
|
1050
|
-
|
|
1148
|
+
const allActiveMedications = [];
|
|
1149
|
+
const allInactiveMedications = [];
|
|
1150
|
+
medicationRequests.forEach((mr) => {
|
|
1151
|
+
if (this.isActiveMedicationRequest(mr.resource)) {
|
|
1152
|
+
allActiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1153
|
+
} else {
|
|
1154
|
+
allInactiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1157
|
+
medicationStatements.forEach((ms) => {
|
|
1158
|
+
if (this.isActiveMedicationStatement(ms.resource)) {
|
|
1159
|
+
allActiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1160
|
+
} else {
|
|
1161
|
+
allInactiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
const sortMedications = (medications) => {
|
|
1165
|
+
medications.sort((a, b) => {
|
|
1166
|
+
let dateStringA;
|
|
1167
|
+
let dateStringB;
|
|
1168
|
+
if (a.type === "request") {
|
|
1169
|
+
const mr = a.resource;
|
|
1170
|
+
dateStringA = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
1171
|
+
} else {
|
|
1172
|
+
const ms = a.resource;
|
|
1173
|
+
dateStringA = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
1174
|
+
}
|
|
1175
|
+
if (b.type === "request") {
|
|
1176
|
+
const mr = b.resource;
|
|
1177
|
+
dateStringB = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
1178
|
+
} else {
|
|
1179
|
+
const ms = b.resource;
|
|
1180
|
+
dateStringB = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
1181
|
+
}
|
|
1182
|
+
const dateA = this.parseDate(dateStringA);
|
|
1183
|
+
const dateB = this.parseDate(dateStringB);
|
|
1184
|
+
if (!dateA && !dateB) return 0;
|
|
1185
|
+
if (!dateA) return 1;
|
|
1186
|
+
if (!dateB) return -1;
|
|
1187
|
+
return dateB.getTime() - dateA.getTime();
|
|
1188
|
+
});
|
|
1189
|
+
};
|
|
1190
|
+
if (allActiveMedications.length > 0) {
|
|
1191
|
+
sortMedications(allActiveMedications);
|
|
1192
|
+
html += this.renderCombinedMedications(templateUtilities, allActiveMedications, "Active Medications");
|
|
1193
|
+
}
|
|
1194
|
+
if (allInactiveMedications.length > 0) {
|
|
1195
|
+
sortMedications(allInactiveMedications);
|
|
1196
|
+
html += this.renderCombinedMedications(templateUtilities, allInactiveMedications, "Inactive Medications");
|
|
1051
1197
|
}
|
|
1052
1198
|
return html;
|
|
1053
1199
|
}
|
|
@@ -1082,16 +1228,19 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1082
1228
|
}));
|
|
1083
1229
|
}
|
|
1084
1230
|
/**
|
|
1085
|
-
* Render HTML table for MedicationRequest resources
|
|
1231
|
+
* Render HTML table for combined MedicationRequest and MedicationStatement resources
|
|
1086
1232
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1087
|
-
* @param medications - Array of
|
|
1233
|
+
* @param medications - Array of combined medication resources
|
|
1234
|
+
* @param sectionTitle - Title for the section
|
|
1088
1235
|
* @returns HTML string for rendering
|
|
1089
1236
|
*/
|
|
1090
|
-
static
|
|
1237
|
+
static renderCombinedMedications(templateUtilities, medications, sectionTitle) {
|
|
1091
1238
|
let html = `
|
|
1239
|
+
<h3>${sectionTitle}</h3>
|
|
1092
1240
|
<table>
|
|
1093
1241
|
<thead>
|
|
1094
1242
|
<tr>
|
|
1243
|
+
<th>Type</th>
|
|
1095
1244
|
<th>Medication</th>
|
|
1096
1245
|
<th>Sig</th>
|
|
1097
1246
|
<th>Dispense Quantity</th>
|
|
@@ -1102,86 +1251,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1102
1251
|
</tr>
|
|
1103
1252
|
</thead>
|
|
1104
1253
|
<tbody>`;
|
|
1105
|
-
for (const
|
|
1106
|
-
const narrativeLinkId = templateUtilities.narrativeLinkId(extension);
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
);
|
|
1111
|
-
const sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
|
|
1254
|
+
for (const medication of medications) {
|
|
1255
|
+
const narrativeLinkId = templateUtilities.narrativeLinkId(medication.extension);
|
|
1256
|
+
let type;
|
|
1257
|
+
let medicationName;
|
|
1258
|
+
let sig;
|
|
1112
1259
|
let dispenseQuantity = "-";
|
|
1113
|
-
|
|
1114
|
-
const quantity = mr.dispenseRequest.quantity;
|
|
1115
|
-
if (quantity.value) {
|
|
1116
|
-
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
const refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
|
|
1260
|
+
let refills = "-";
|
|
1120
1261
|
let startDate = "-";
|
|
1121
1262
|
let endDate = "-";
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1263
|
+
let status;
|
|
1264
|
+
if (medication.type === "request") {
|
|
1265
|
+
const mr = medication.resource;
|
|
1266
|
+
type = "Request";
|
|
1267
|
+
status = mr.status ? String(mr.status) : "-";
|
|
1268
|
+
medicationName = templateUtilities.getMedicationName(
|
|
1269
|
+
mr.medicationReference || mr.medicationCodeableConcept
|
|
1270
|
+
);
|
|
1271
|
+
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
|
|
1272
|
+
if (mr.dispenseRequest?.quantity) {
|
|
1273
|
+
const quantity = mr.dispenseRequest.quantity;
|
|
1274
|
+
if (quantity.value) {
|
|
1275
|
+
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
|
|
1279
|
+
if (mr.dispenseRequest?.validityPeriod) {
|
|
1280
|
+
startDate = mr.dispenseRequest.validityPeriod.start || "-";
|
|
1281
|
+
endDate = mr.dispenseRequest.validityPeriod.end || "-";
|
|
1282
|
+
} else {
|
|
1283
|
+
startDate = mr.authoredOn || "-";
|
|
1284
|
+
}
|
|
1125
1285
|
} else {
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
html += `
|
|
1140
|
-
</tbody>
|
|
1141
|
-
</table>`;
|
|
1142
|
-
return html;
|
|
1143
|
-
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Render HTML table for MedicationStatement resources
|
|
1146
|
-
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1147
|
-
* @param medications - Array of MedicationStatement resources
|
|
1148
|
-
* @returns HTML string for rendering
|
|
1149
|
-
*/
|
|
1150
|
-
static renderMedicationStatements(templateUtilities, medications) {
|
|
1151
|
-
let html = `
|
|
1152
|
-
<table>
|
|
1153
|
-
<thead>
|
|
1154
|
-
<tr>
|
|
1155
|
-
<th>Medication</th>
|
|
1156
|
-
<th>Sig</th>
|
|
1157
|
-
<th>Dispense Quantity</th>
|
|
1158
|
-
<th>Refills</th>
|
|
1159
|
-
<th>Start Date</th>
|
|
1160
|
-
<th>End Date</th>
|
|
1161
|
-
<th>Status</th>
|
|
1162
|
-
</tr>
|
|
1163
|
-
</thead>
|
|
1164
|
-
<tbody>`;
|
|
1165
|
-
for (const { resource: ms, extension } of medications) {
|
|
1166
|
-
const narrativeLinkId = templateUtilities.narrativeLinkId(extension);
|
|
1167
|
-
const status = ms.status ? String(ms.status) : "-";
|
|
1168
|
-
const medication = templateUtilities.getMedicationName(
|
|
1169
|
-
ms.medicationReference || ms.medicationCodeableConcept
|
|
1170
|
-
);
|
|
1171
|
-
const sig = templateUtilities.concat(ms.dosage, "text") || "-";
|
|
1172
|
-
const dispenseQuantity = "-";
|
|
1173
|
-
const refills = "-";
|
|
1174
|
-
let startDate = "-";
|
|
1175
|
-
let endDate = "-";
|
|
1176
|
-
if (ms.effectiveDateTime) {
|
|
1177
|
-
startDate = ms.effectiveDateTime;
|
|
1178
|
-
} else if (ms.effectivePeriod) {
|
|
1179
|
-
startDate = ms.effectivePeriod.start || "-";
|
|
1180
|
-
endDate = ms.effectivePeriod.end || "-";
|
|
1286
|
+
const ms = medication.resource;
|
|
1287
|
+
type = "Statement";
|
|
1288
|
+
status = ms.status ? String(ms.status) : "-";
|
|
1289
|
+
medicationName = templateUtilities.getMedicationName(
|
|
1290
|
+
ms.medicationReference || ms.medicationCodeableConcept
|
|
1291
|
+
);
|
|
1292
|
+
sig = templateUtilities.concat(ms.dosage, "text") || "-";
|
|
1293
|
+
if (ms.effectiveDateTime) {
|
|
1294
|
+
startDate = ms.effectiveDateTime;
|
|
1295
|
+
} else if (ms.effectivePeriod) {
|
|
1296
|
+
startDate = ms.effectivePeriod.start || "-";
|
|
1297
|
+
endDate = ms.effectivePeriod.end || "-";
|
|
1298
|
+
}
|
|
1181
1299
|
}
|
|
1182
1300
|
html += `
|
|
1183
1301
|
<tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
|
|
1184
|
-
<td>${
|
|
1302
|
+
<td>${type}</td>
|
|
1303
|
+
<td>${medicationName}<ul></ul></td>
|
|
1185
1304
|
<td>${sig}</td>
|
|
1186
1305
|
<td>${dispenseQuantity}</td>
|
|
1187
1306
|
<td>${refills}</td>
|
|
@@ -1206,6 +1325,13 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1206
1325
|
* @returns HTML string for rendering
|
|
1207
1326
|
*/
|
|
1208
1327
|
generateNarrative(resource, timezone) {
|
|
1328
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1329
|
+
resource.entry.sort((a, b) => {
|
|
1330
|
+
const dateA = a.resource?.occurrenceDateTime;
|
|
1331
|
+
const dateB = b.resource?.occurrenceDateTime;
|
|
1332
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1209
1335
|
return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
|
|
1210
1336
|
}
|
|
1211
1337
|
/**
|
|
@@ -1217,7 +1343,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1217
1343
|
static generateStaticNarrative(resource, timezone) {
|
|
1218
1344
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1219
1345
|
let html = `
|
|
1220
|
-
<h5>Immunizations</h5>
|
|
1221
1346
|
<table>
|
|
1222
1347
|
<thead>
|
|
1223
1348
|
<tr>
|
|
@@ -1275,81 +1400,35 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1275
1400
|
static generateStaticNarrative(resource, timezone) {
|
|
1276
1401
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1277
1402
|
let html = ``;
|
|
1278
|
-
const activeConditions = [];
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
const isResolved = cond.clinicalStatus?.coding?.some((c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved"));
|
|
1287
|
-
if (isResolved) {
|
|
1288
|
-
resolvedConditions.push(cond);
|
|
1289
|
-
} else {
|
|
1290
|
-
activeConditions.push(cond);
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
if (activeConditions.length > 0) {
|
|
1295
|
-
html += `<div class="ActiveProblems">
|
|
1296
|
-
<h3>Active Problems</h3>
|
|
1297
|
-
<table class="ActiveProblemTable">
|
|
1298
|
-
<thead>
|
|
1299
|
-
<tr>
|
|
1300
|
-
<th>Problem</th>
|
|
1301
|
-
<th>Priority</th>
|
|
1302
|
-
<th>Noted Date</th>
|
|
1303
|
-
<th>Diagnosed Date</th>
|
|
1304
|
-
<th>Notes</th>
|
|
1305
|
-
</tr>
|
|
1306
|
-
</thead>
|
|
1307
|
-
<tbody>`;
|
|
1308
|
-
for (const cond of activeConditions) {
|
|
1309
|
-
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1310
|
-
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1311
|
-
<td class="Priority">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1312
|
-
<td class="NotedDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1313
|
-
<td class="DiagnosedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1314
|
-
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
1315
|
-
</tr>`;
|
|
1316
|
-
}
|
|
1317
|
-
html += `</tbody>
|
|
1318
|
-
</table>
|
|
1319
|
-
</div>`;
|
|
1320
|
-
}
|
|
1321
|
-
if (activeConditions.length > 0 && resolvedConditions.length > 0) {
|
|
1322
|
-
html += `<br />`;
|
|
1323
|
-
}
|
|
1324
|
-
if (resolvedConditions.length > 0) {
|
|
1325
|
-
html += `<div class="ResolvedProblems">
|
|
1326
|
-
<h3>Resolved Problems</h3>
|
|
1327
|
-
<table class="ResolvedProblemTable">
|
|
1403
|
+
const activeConditions = resource.entry?.map((entry) => entry.resource) || [];
|
|
1404
|
+
activeConditions.sort((a, b) => {
|
|
1405
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1406
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1407
|
+
return dateB - dateA;
|
|
1408
|
+
});
|
|
1409
|
+
html += `
|
|
1410
|
+
<table>
|
|
1328
1411
|
<thead>
|
|
1329
1412
|
<tr>
|
|
1330
1413
|
<th>Problem</th>
|
|
1331
|
-
<th>
|
|
1332
|
-
<th>
|
|
1333
|
-
<th>
|
|
1334
|
-
<th>Resolved Date</th>
|
|
1414
|
+
<th>Severity</th>
|
|
1415
|
+
<th>Onset Date</th>
|
|
1416
|
+
<th>Recorded Date</th>
|
|
1335
1417
|
<th>Notes</th>
|
|
1336
1418
|
</tr>
|
|
1337
1419
|
</thead>
|
|
1338
1420
|
<tbody>`;
|
|
1339
|
-
|
|
1340
|
-
|
|
1421
|
+
for (const cond of activeConditions) {
|
|
1422
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1341
1423
|
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1342
|
-
<td class="
|
|
1343
|
-
<td class="
|
|
1344
|
-
<td class="
|
|
1345
|
-
<td class="
|
|
1346
|
-
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
1424
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1425
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1426
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1427
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1347
1428
|
</tr>`;
|
|
1348
|
-
}
|
|
1349
|
-
html += `</tbody>
|
|
1350
|
-
</table>
|
|
1351
|
-
</div>`;
|
|
1352
1429
|
}
|
|
1430
|
+
html += `</tbody>
|
|
1431
|
+
</table>`;
|
|
1353
1432
|
return html;
|
|
1354
1433
|
}
|
|
1355
1434
|
};
|
|
@@ -1373,8 +1452,13 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1373
1452
|
*/
|
|
1374
1453
|
static generateStaticNarrative(resource, timezone) {
|
|
1375
1454
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1455
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
1456
|
+
observations.sort((a, b) => {
|
|
1457
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1458
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1459
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1460
|
+
});
|
|
1376
1461
|
let html = `
|
|
1377
|
-
<h5>Vital Signs</h5>
|
|
1378
1462
|
<table>
|
|
1379
1463
|
<thead>
|
|
1380
1464
|
<tr>
|
|
@@ -1388,13 +1472,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1388
1472
|
</tr>
|
|
1389
1473
|
</thead>
|
|
1390
1474
|
<tbody>`;
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
const obs = entry.resource;
|
|
1394
|
-
if (obs.resourceType === "Composition") {
|
|
1395
|
-
continue;
|
|
1396
|
-
}
|
|
1397
|
-
html += `
|
|
1475
|
+
for (const obs of observations) {
|
|
1476
|
+
html += `
|
|
1398
1477
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1399
1478
|
<td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
|
|
1400
1479
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
@@ -1402,9 +1481,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1402
1481
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
1403
1482
|
<td>${templateUtilities.renderComponent(obs.component)}</td>
|
|
1404
1483
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1405
|
-
<td>${templateUtilities.
|
|
1484
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1406
1485
|
</tr>`;
|
|
1407
|
-
}
|
|
1408
1486
|
}
|
|
1409
1487
|
html += `
|
|
1410
1488
|
</tbody>
|
|
@@ -1422,6 +1500,13 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1422
1500
|
* @returns HTML string for rendering
|
|
1423
1501
|
*/
|
|
1424
1502
|
generateNarrative(resource, timezone) {
|
|
1503
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1504
|
+
resource.entry.sort((a, b) => {
|
|
1505
|
+
const dateA = a.resource?.recordedOn;
|
|
1506
|
+
const dateB = b.resource?.recordedOn;
|
|
1507
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1425
1510
|
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1426
1511
|
}
|
|
1427
1512
|
/**
|
|
@@ -1433,7 +1518,6 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1433
1518
|
static generateStaticNarrative(resource, timezone) {
|
|
1434
1519
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1435
1520
|
let html = `
|
|
1436
|
-
<h5>Medical Devices</h5>
|
|
1437
1521
|
<table>
|
|
1438
1522
|
<thead>
|
|
1439
1523
|
<tr>
|
|
@@ -1487,10 +1571,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1487
1571
|
let html = "";
|
|
1488
1572
|
const observations = this.getObservations(resource);
|
|
1489
1573
|
if (observations.length > 0) {
|
|
1574
|
+
observations.sort((a, b) => {
|
|
1575
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1576
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1577
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1578
|
+
});
|
|
1490
1579
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1491
1580
|
}
|
|
1492
1581
|
const diagnosticReports = this.getDiagnosticReports(resource);
|
|
1493
1582
|
if (diagnosticReports.length > 0) {
|
|
1583
|
+
diagnosticReports.sort((a, b) => {
|
|
1584
|
+
const dateA = a.issued;
|
|
1585
|
+
const dateB = b.issued;
|
|
1586
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1587
|
+
});
|
|
1494
1588
|
html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
|
|
1495
1589
|
}
|
|
1496
1590
|
return html;
|
|
@@ -1526,7 +1620,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1526
1620
|
*/
|
|
1527
1621
|
static renderObservations(templateUtilities, observations, timezone) {
|
|
1528
1622
|
let html = `
|
|
1529
|
-
<h5>
|
|
1623
|
+
<h5>Observations</h5>
|
|
1530
1624
|
<table>
|
|
1531
1625
|
<thead>
|
|
1532
1626
|
<tr>
|
|
@@ -1549,7 +1643,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1549
1643
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
1550
1644
|
<td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
|
|
1551
1645
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1552
|
-
<td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
|
|
1646
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1553
1647
|
</tr>`;
|
|
1554
1648
|
}
|
|
1555
1649
|
html += `
|
|
@@ -1566,7 +1660,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1566
1660
|
*/
|
|
1567
1661
|
static renderDiagnosticReports(templateUtilities, reports, timezone) {
|
|
1568
1662
|
let html = `
|
|
1569
|
-
<h5>Diagnostic
|
|
1663
|
+
<h5>Diagnostic Reports</h5>
|
|
1570
1664
|
<table>
|
|
1571
1665
|
<thead>
|
|
1572
1666
|
<tr>
|
|
@@ -1608,6 +1702,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1608
1702
|
* @returns HTML string for rendering
|
|
1609
1703
|
*/
|
|
1610
1704
|
generateNarrative(resource, timezone) {
|
|
1705
|
+
resource.entry?.sort((a, b) => {
|
|
1706
|
+
const dateA = a.resource.performedDateTime || a.resource.performedPeriod?.start;
|
|
1707
|
+
const dateB = b.resource.performedDateTime || b.resource.performedPeriod?.start;
|
|
1708
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1709
|
+
});
|
|
1611
1710
|
return _HistoryOfProceduresTemplate.generateStaticNarrative(resource, timezone);
|
|
1612
1711
|
}
|
|
1613
1712
|
/**
|
|
@@ -1619,7 +1718,6 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1619
1718
|
static generateStaticNarrative(resource, timezone) {
|
|
1620
1719
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1621
1720
|
let html = `
|
|
1622
|
-
<h5>History Of Procedures</h5>
|
|
1623
1721
|
<table>
|
|
1624
1722
|
<thead>
|
|
1625
1723
|
<tr>
|
|
@@ -1632,14 +1730,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1632
1730
|
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1633
1731
|
for (const entry of resource.entry) {
|
|
1634
1732
|
const proc = entry.resource;
|
|
1635
|
-
if (proc.resourceType === "Composition") {
|
|
1636
|
-
continue;
|
|
1637
|
-
}
|
|
1638
1733
|
html += `
|
|
1639
1734
|
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
1640
1735
|
<td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
|
|
1641
1736
|
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
1642
|
-
<td>${templateUtilities.renderTime(proc.performedDateTime, timezone)}</td>
|
|
1737
|
+
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
1643
1738
|
</tr>`;
|
|
1644
1739
|
}
|
|
1645
1740
|
}
|
|
@@ -1669,8 +1764,13 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1669
1764
|
*/
|
|
1670
1765
|
static generateStaticNarrative(resource, timezone) {
|
|
1671
1766
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1767
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
1768
|
+
observations.sort((a, b) => {
|
|
1769
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1770
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1771
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1772
|
+
});
|
|
1672
1773
|
let html = `
|
|
1673
|
-
<h5>Social History</h5>
|
|
1674
1774
|
<table>
|
|
1675
1775
|
<thead>
|
|
1676
1776
|
<tr>
|
|
@@ -1682,21 +1782,15 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1682
1782
|
</tr>
|
|
1683
1783
|
</thead>
|
|
1684
1784
|
<tbody>`;
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
const obs = entry.resource;
|
|
1688
|
-
if (obs.resourceType === "Composition") {
|
|
1689
|
-
continue;
|
|
1690
|
-
}
|
|
1691
|
-
html += `
|
|
1785
|
+
for (const obs of observations) {
|
|
1786
|
+
html += `
|
|
1692
1787
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1693
|
-
<td>${templateUtilities.codeableConcept(obs.code
|
|
1788
|
+
<td>${templateUtilities.codeableConcept(obs.code)}</td>
|
|
1694
1789
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
1695
1790
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
1696
1791
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1697
|
-
<td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
|
|
1792
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1698
1793
|
</tr>`;
|
|
1699
|
-
}
|
|
1700
1794
|
}
|
|
1701
1795
|
html += `
|
|
1702
1796
|
</tbody>
|
|
@@ -1715,36 +1809,38 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1715
1809
|
*/
|
|
1716
1810
|
generateNarrative(resource, timezone) {
|
|
1717
1811
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1718
|
-
let html =
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
<th>Comments</th>
|
|
1726
|
-
<th>Date</th>
|
|
1727
|
-
</tr>
|
|
1728
|
-
</thead>
|
|
1729
|
-
<tbody>`;
|
|
1730
|
-
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1731
|
-
for (const entry of resource.entry) {
|
|
1732
|
-
const cond = entry.resource;
|
|
1733
|
-
if (cond.resourceType === "Composition") {
|
|
1734
|
-
continue;
|
|
1735
|
-
}
|
|
1736
|
-
html += `
|
|
1737
|
-
<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1738
|
-
<td>${templateUtilities.codeableConcept(cond.code, "display")}</td>
|
|
1739
|
-
<td>${templateUtilities.codeableConcept(cond.clinicalStatus, "code")}</td>
|
|
1740
|
-
<td>${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1741
|
-
<td>${templateUtilities.renderTime(cond.onsetDateTime, timezone)}</td>
|
|
1742
|
-
</tr>`;
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1812
|
+
let html = ``;
|
|
1813
|
+
const resolvedConditions = resource.entry?.map((entry) => entry.resource) || [];
|
|
1814
|
+
resolvedConditions.sort((a, b) => {
|
|
1815
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1816
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1817
|
+
return dateB - dateA;
|
|
1818
|
+
});
|
|
1745
1819
|
html += `
|
|
1746
|
-
|
|
1747
|
-
|
|
1820
|
+
<table>
|
|
1821
|
+
<thead>
|
|
1822
|
+
<tr>
|
|
1823
|
+
<th>Problem</th>
|
|
1824
|
+
<th>Severity</th>
|
|
1825
|
+
<th>Onset Date</th>
|
|
1826
|
+
<th>Recorded Date</th>
|
|
1827
|
+
<th>Resolved Date</th>
|
|
1828
|
+
<th>Notes</th>
|
|
1829
|
+
</tr>
|
|
1830
|
+
</thead>
|
|
1831
|
+
<tbody>`;
|
|
1832
|
+
for (const cond of resolvedConditions) {
|
|
1833
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1834
|
+
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1835
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1836
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1837
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1838
|
+
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
1839
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1840
|
+
</tr>`;
|
|
1841
|
+
}
|
|
1842
|
+
html += `</tbody>
|
|
1843
|
+
</table>`;
|
|
1748
1844
|
return html;
|
|
1749
1845
|
}
|
|
1750
1846
|
};
|
|
@@ -1757,15 +1853,19 @@ var PlanOfCareTemplate = class {
|
|
|
1757
1853
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1758
1854
|
* @returns HTML string for rendering
|
|
1759
1855
|
*/
|
|
1760
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1761
1856
|
generateNarrative(resource, timezone) {
|
|
1762
1857
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1858
|
+
const carePlans = resource.entry?.map((entry) => entry.resource) || [];
|
|
1859
|
+
carePlans.sort((a, b) => {
|
|
1860
|
+
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1861
|
+
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
1862
|
+
return endB - endA;
|
|
1863
|
+
});
|
|
1763
1864
|
let html = `
|
|
1764
|
-
<h5>Plan of Care</h5>
|
|
1765
1865
|
<table>
|
|
1766
1866
|
<thead>
|
|
1767
1867
|
<tr>
|
|
1768
|
-
<th>
|
|
1868
|
+
<th>Description</th>
|
|
1769
1869
|
<th>Intent</th>
|
|
1770
1870
|
<th>Comments</th>
|
|
1771
1871
|
<th>Planned Start</th>
|
|
@@ -1773,21 +1873,15 @@ var PlanOfCareTemplate = class {
|
|
|
1773
1873
|
</tr>
|
|
1774
1874
|
</thead>
|
|
1775
1875
|
<tbody>`;
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
const cp = entry.resource;
|
|
1779
|
-
if (cp.resourceType === "Composition") {
|
|
1780
|
-
continue;
|
|
1781
|
-
}
|
|
1782
|
-
html += `
|
|
1876
|
+
for (const cp of carePlans) {
|
|
1877
|
+
html += `
|
|
1783
1878
|
<tr id="${templateUtilities.narrativeLinkId(cp)}">
|
|
1784
|
-
<td>${cp.description || ""}</td>
|
|
1785
|
-
<td>${cp.intent ||
|
|
1879
|
+
<td>${cp.description || cp.title || ""}</td>
|
|
1880
|
+
<td>${cp.intent || ""}</td>
|
|
1786
1881
|
<td>${templateUtilities.concat(cp.note, "text")}</td>
|
|
1787
|
-
<td>${cp.period?.start
|
|
1788
|
-
<td>${cp.period?.end
|
|
1882
|
+
<td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
|
|
1883
|
+
<td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
|
|
1789
1884
|
</tr>`;
|
|
1790
|
-
}
|
|
1791
1885
|
}
|
|
1792
1886
|
html += `
|
|
1793
1887
|
</tbody>
|
|
@@ -1815,38 +1909,111 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
1815
1909
|
*/
|
|
1816
1910
|
static generateStaticNarrative(resource, timezone) {
|
|
1817
1911
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1818
|
-
let html =
|
|
1819
|
-
|
|
1912
|
+
let html = ``;
|
|
1913
|
+
const activeConditions = [];
|
|
1914
|
+
const clinicalImpressions = [];
|
|
1915
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1916
|
+
for (const entry of resource.entry) {
|
|
1917
|
+
if (entry.resource?.resourceType === "Condition") {
|
|
1918
|
+
const cond = entry.resource;
|
|
1919
|
+
const isResolved = cond.clinicalStatus?.coding?.some(
|
|
1920
|
+
(c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
|
|
1921
|
+
);
|
|
1922
|
+
if (!isResolved) {
|
|
1923
|
+
activeConditions.push(cond);
|
|
1924
|
+
}
|
|
1925
|
+
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
1926
|
+
clinicalImpressions.push(entry.resource);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
activeConditions.sort((a, b) => {
|
|
1931
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1932
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1933
|
+
return dateB - dateA;
|
|
1934
|
+
});
|
|
1935
|
+
clinicalImpressions.sort((a, b) => {
|
|
1936
|
+
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;
|
|
1937
|
+
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;
|
|
1938
|
+
return dateB - dateA;
|
|
1939
|
+
});
|
|
1940
|
+
if (activeConditions.length > 0) {
|
|
1941
|
+
html += `<h3>Conditions</h3>
|
|
1942
|
+
<table>
|
|
1943
|
+
<thead>
|
|
1944
|
+
<tr>
|
|
1945
|
+
<th>Problem</th>
|
|
1946
|
+
<th>Severity</th>
|
|
1947
|
+
<th>Onset Date</th>
|
|
1948
|
+
<th>Recorded Date</th>
|
|
1949
|
+
<th>Notes</th>
|
|
1950
|
+
</tr>
|
|
1951
|
+
</thead>
|
|
1952
|
+
<tbody>`;
|
|
1953
|
+
for (const cond of activeConditions) {
|
|
1954
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1955
|
+
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1956
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1957
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1958
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1959
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
1960
|
+
</tr>`;
|
|
1961
|
+
}
|
|
1962
|
+
html += `</tbody>
|
|
1963
|
+
</table>
|
|
1964
|
+
</div>`;
|
|
1965
|
+
}
|
|
1966
|
+
if (clinicalImpressions.length > 0) {
|
|
1967
|
+
html += `<h3>Clinical Impressions</h3>
|
|
1820
1968
|
<table>
|
|
1821
1969
|
<thead>
|
|
1822
1970
|
<tr>
|
|
1823
|
-
<th>Assessment</th>
|
|
1824
|
-
<th>Status</th>
|
|
1825
|
-
<th>Finding</th>
|
|
1826
|
-
<th>Comments</th>
|
|
1827
1971
|
<th>Date</th>
|
|
1972
|
+
<th>Status</th>
|
|
1973
|
+
<th>Description</th>
|
|
1974
|
+
<th>Summary</th>
|
|
1975
|
+
<th>Findings</th>
|
|
1976
|
+
<th>Notes</th>
|
|
1828
1977
|
</tr>
|
|
1829
1978
|
</thead>
|
|
1830
1979
|
<tbody>`;
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1980
|
+
for (const impression of clinicalImpressions) {
|
|
1981
|
+
let formattedDate = "";
|
|
1982
|
+
if (impression.effectiveDateTime) {
|
|
1983
|
+
formattedDate = templateUtilities.renderTime(
|
|
1984
|
+
impression.effectiveDateTime,
|
|
1985
|
+
timezone
|
|
1986
|
+
);
|
|
1987
|
+
} else if (impression.effectivePeriod) {
|
|
1988
|
+
formattedDate = templateUtilities.renderPeriod(
|
|
1989
|
+
impression.effectivePeriod,
|
|
1990
|
+
timezone
|
|
1991
|
+
);
|
|
1992
|
+
} else if (impression.date) {
|
|
1993
|
+
formattedDate = templateUtilities.renderDate(impression.date);
|
|
1994
|
+
}
|
|
1995
|
+
let findingsHtml = "";
|
|
1996
|
+
if (impression.finding && impression.finding.length > 0) {
|
|
1997
|
+
findingsHtml = "<ul>";
|
|
1998
|
+
for (const finding of impression.finding) {
|
|
1999
|
+
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
2000
|
+
const cause = finding.basis || "";
|
|
2001
|
+
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
2002
|
+
}
|
|
2003
|
+
findingsHtml += "</ul>";
|
|
1836
2004
|
}
|
|
2005
|
+
const notes = templateUtilities.renderNotes(impression.note, timezone);
|
|
1837
2006
|
html += `
|
|
1838
|
-
<tr id="${templateUtilities.narrativeLinkId(
|
|
1839
|
-
<td>${
|
|
1840
|
-
<td>${
|
|
1841
|
-
<td>${
|
|
1842
|
-
<td>${
|
|
1843
|
-
<td>${
|
|
2007
|
+
<tr id="${templateUtilities.narrativeLinkId(impression)}">
|
|
2008
|
+
<td>${formattedDate}</td>
|
|
2009
|
+
<td>${impression.status || ""}</td>
|
|
2010
|
+
<td>${impression.description || ""}</td>
|
|
2011
|
+
<td>${impression.summary || ""}</td>
|
|
2012
|
+
<td>${findingsHtml}</td>
|
|
2013
|
+
<td>${notes}</td>
|
|
1844
2014
|
</tr>`;
|
|
1845
2015
|
}
|
|
1846
2016
|
}
|
|
1847
|
-
html += `
|
|
1848
|
-
</tbody>
|
|
1849
|
-
</table>`;
|
|
1850
2017
|
return html;
|
|
1851
2018
|
}
|
|
1852
2019
|
};
|
|
@@ -1870,32 +2037,30 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
1870
2037
|
*/
|
|
1871
2038
|
static generateStaticNarrative(resource, timezone) {
|
|
1872
2039
|
const templateUtilities = new TemplateUtilities(resource);
|
|
2040
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
2041
|
+
observations.sort((a, b) => {
|
|
2042
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2043
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
2044
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
2045
|
+
});
|
|
1873
2046
|
let html = `
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
for (const entry of resource.entry) {
|
|
1887
|
-
const obs = entry.resource;
|
|
1888
|
-
if (obs.resourceType === "Composition") {
|
|
1889
|
-
continue;
|
|
1890
|
-
}
|
|
1891
|
-
html += `
|
|
2047
|
+
<table>
|
|
2048
|
+
<thead>
|
|
2049
|
+
<tr>
|
|
2050
|
+
<th>Result</th>
|
|
2051
|
+
<th>Comments</th>
|
|
2052
|
+
<th>Date</th>
|
|
2053
|
+
</tr>
|
|
2054
|
+
</thead>
|
|
2055
|
+
<tbody>`;
|
|
2056
|
+
for (const resource2 of observations) {
|
|
2057
|
+
const obs = resource2;
|
|
2058
|
+
html += `
|
|
1892
2059
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1893
|
-
<td>${templateUtilities.
|
|
1894
|
-
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2060
|
+
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
1895
2061
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1896
|
-
<td>${templateUtilities.
|
|
2062
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1897
2063
|
</tr>`;
|
|
1898
|
-
}
|
|
1899
2064
|
}
|
|
1900
2065
|
html += `
|
|
1901
2066
|
</tbody>
|
|
@@ -1913,6 +2078,13 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1913
2078
|
* @returns HTML string for rendering
|
|
1914
2079
|
*/
|
|
1915
2080
|
generateNarrative(resource, timezone) {
|
|
2081
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
2082
|
+
resource.entry.sort((a, b) => {
|
|
2083
|
+
const dateA = new Date(a.resource.dateTime || 0);
|
|
2084
|
+
const dateB = new Date(b.resource.dateTime || 0);
|
|
2085
|
+
return dateB.getTime() - dateA.getTime();
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
1916
2088
|
return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
|
|
1917
2089
|
}
|
|
1918
2090
|
/**
|
|
@@ -1925,7 +2097,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1925
2097
|
static generateStaticNarrative(resource, timezone) {
|
|
1926
2098
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1927
2099
|
let html = `
|
|
1928
|
-
<h5>Advance Directives</h5>
|
|
1929
2100
|
<table>
|
|
1930
2101
|
<thead>
|
|
1931
2102
|
<tr>
|
|
@@ -1939,9 +2110,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1939
2110
|
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1940
2111
|
for (const entry of resource.entry) {
|
|
1941
2112
|
const consent = entry.resource;
|
|
1942
|
-
if (consent.resourceType === "Composition") {
|
|
1943
|
-
continue;
|
|
1944
|
-
}
|
|
1945
2113
|
html += `
|
|
1946
2114
|
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
1947
2115
|
<td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
|
|
@@ -2039,90 +2207,6 @@ var FamilyHistoryTemplate = class {
|
|
|
2039
2207
|
}
|
|
2040
2208
|
};
|
|
2041
2209
|
|
|
2042
|
-
// src/narratives/templates/typescript/ClinicalImpressionTemplate.ts
|
|
2043
|
-
var ClinicalImpressionTemplate = class {
|
|
2044
|
-
/**
|
|
2045
|
-
* Generate HTML narrative for Clinical Impressions
|
|
2046
|
-
* @param resource - FHIR Bundle containing ClinicalImpression resources
|
|
2047
|
-
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2048
|
-
* @returns HTML string for rendering
|
|
2049
|
-
*/
|
|
2050
|
-
generateNarrative(resource, timezone) {
|
|
2051
|
-
const templateUtilities = new TemplateUtilities(resource);
|
|
2052
|
-
let html = `
|
|
2053
|
-
<h5>Clinical Impressions</h5>
|
|
2054
|
-
<table>
|
|
2055
|
-
<thead>
|
|
2056
|
-
<tr>
|
|
2057
|
-
<th>Date</th>
|
|
2058
|
-
<th>Status</th>
|
|
2059
|
-
<th>Description</th>
|
|
2060
|
-
<th>Summary</th>
|
|
2061
|
-
<th>Findings</th>
|
|
2062
|
-
<th>Notes</th>
|
|
2063
|
-
</tr>
|
|
2064
|
-
</thead>
|
|
2065
|
-
<tbody>`;
|
|
2066
|
-
if (resource.entry && Array.isArray(resource.entry)) {
|
|
2067
|
-
let hasClinicalImpressions = false;
|
|
2068
|
-
for (const entry of resource.entry) {
|
|
2069
|
-
const res = entry.resource;
|
|
2070
|
-
if (!res || res.resourceType !== "ClinicalImpression") {
|
|
2071
|
-
continue;
|
|
2072
|
-
}
|
|
2073
|
-
hasClinicalImpressions = true;
|
|
2074
|
-
const impression = res;
|
|
2075
|
-
let formattedDate = "";
|
|
2076
|
-
if (impression.effectiveDateTime) {
|
|
2077
|
-
formattedDate = templateUtilities.renderTime(impression.effectiveDateTime, timezone);
|
|
2078
|
-
} else if (impression.effectivePeriod) {
|
|
2079
|
-
formattedDate = templateUtilities.renderPeriod(impression.effectivePeriod, timezone);
|
|
2080
|
-
} else if (impression.date) {
|
|
2081
|
-
formattedDate = templateUtilities.renderDate(impression.date);
|
|
2082
|
-
}
|
|
2083
|
-
const status = impression.status || "";
|
|
2084
|
-
const description = impression.description || "";
|
|
2085
|
-
const summary = impression.summary || "";
|
|
2086
|
-
let findingsHtml = "";
|
|
2087
|
-
if (impression.finding && impression.finding.length > 0) {
|
|
2088
|
-
findingsHtml = "<ul>";
|
|
2089
|
-
for (const finding of impression.finding) {
|
|
2090
|
-
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept, "display") : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
2091
|
-
const cause = finding.basis || "";
|
|
2092
|
-
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
2093
|
-
}
|
|
2094
|
-
findingsHtml += "</ul>";
|
|
2095
|
-
}
|
|
2096
|
-
const notes = templateUtilities.renderNotes(impression.note, timezone);
|
|
2097
|
-
html += `
|
|
2098
|
-
<tr id="${templateUtilities.narrativeLinkId(impression)}">
|
|
2099
|
-
<td>${formattedDate}</td>
|
|
2100
|
-
<td>${status}</td>
|
|
2101
|
-
<td>${description}</td>
|
|
2102
|
-
<td>${summary}</td>
|
|
2103
|
-
<td>${findingsHtml}</td>
|
|
2104
|
-
<td>${notes}</td>
|
|
2105
|
-
</tr>`;
|
|
2106
|
-
}
|
|
2107
|
-
if (!hasClinicalImpressions) {
|
|
2108
|
-
html += `
|
|
2109
|
-
<tr>
|
|
2110
|
-
<td colspan="6">No clinical impressions recorded</td>
|
|
2111
|
-
</tr>`;
|
|
2112
|
-
}
|
|
2113
|
-
} else {
|
|
2114
|
-
html += `
|
|
2115
|
-
<tr>
|
|
2116
|
-
<td colspan="6">No clinical impressions recorded</td>
|
|
2117
|
-
</tr>`;
|
|
2118
|
-
}
|
|
2119
|
-
html += `
|
|
2120
|
-
</tbody>
|
|
2121
|
-
</table>`;
|
|
2122
|
-
return html;
|
|
2123
|
-
}
|
|
2124
|
-
};
|
|
2125
|
-
|
|
2126
2210
|
// src/narratives/templates/typescript/TypeScriptTemplateMapper.ts
|
|
2127
2211
|
var TypeScriptTemplateMapper = class {
|
|
2128
2212
|
/**
|
|
@@ -2145,20 +2229,19 @@ var TypeScriptTemplateMapper = class {
|
|
|
2145
2229
|
TypeScriptTemplateMapper.sectionToTemplate = {
|
|
2146
2230
|
["Patient" /* PATIENT */]: new PatientTemplate(),
|
|
2147
2231
|
["AllergyIntoleranceSection" /* ALLERGIES */]: new AllergyIntoleranceTemplate(),
|
|
2148
|
-
["
|
|
2232
|
+
["MedicationSummarySection" /* MEDICATIONS */]: new MedicationSummaryTemplate(),
|
|
2149
2233
|
["ImmunizationSection" /* IMMUNIZATIONS */]: new ImmunizationsTemplate(),
|
|
2150
2234
|
["ProblemSection" /* PROBLEMS */]: new ProblemListTemplate(),
|
|
2151
2235
|
["VitalSignsSection" /* VITAL_SIGNS */]: new VitalSignsTemplate(),
|
|
2152
2236
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: new MedicalDevicesTemplate(),
|
|
2153
|
-
["
|
|
2154
|
-
["
|
|
2237
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
|
|
2238
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
|
|
2155
2239
|
["FamilyHistorySection" /* FAMILY_HISTORY */]: new FamilyHistoryTemplate(),
|
|
2156
2240
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: new SocialHistoryTemplate(),
|
|
2157
|
-
["
|
|
2241
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
|
|
2158
2242
|
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: new FunctionalStatusTemplate(),
|
|
2159
|
-
["
|
|
2160
|
-
["
|
|
2161
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: new ClinicalImpressionTemplate(),
|
|
2243
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: new PastHistoryOfIllnessTemplate(),
|
|
2244
|
+
["PlanOfCareSection" /* CARE_PLAN */]: new PlanOfCareTemplate(),
|
|
2162
2245
|
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: new AdvanceDirectivesTemplate()
|
|
2163
2246
|
};
|
|
2164
2247
|
|
|
@@ -2195,10 +2278,9 @@ var NarrativeGenerator = class {
|
|
|
2195
2278
|
* @param section - IPS section type
|
|
2196
2279
|
* @param resources - Array of domain resources
|
|
2197
2280
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2198
|
-
* @param wrapInXhtml - Whether to wrap the content in XHTML div
|
|
2199
2281
|
* @returns Generated HTML content or undefined if no resources
|
|
2200
2282
|
*/
|
|
2201
|
-
static async generateNarrativeContentAsync(section, resources, timezone
|
|
2283
|
+
static async generateNarrativeContentAsync(section, resources, timezone) {
|
|
2202
2284
|
if (!resources || resources.length === 0) {
|
|
2203
2285
|
return void 0;
|
|
2204
2286
|
}
|
|
@@ -2214,11 +2296,7 @@ var NarrativeGenerator = class {
|
|
|
2214
2296
|
if (!content) {
|
|
2215
2297
|
return void 0;
|
|
2216
2298
|
}
|
|
2217
|
-
|
|
2218
|
-
return await this.wrapInXhtmlAsync(content);
|
|
2219
|
-
} else {
|
|
2220
|
-
return await this.minifyHtmlAsync(content);
|
|
2221
|
-
}
|
|
2299
|
+
return content;
|
|
2222
2300
|
} catch (error) {
|
|
2223
2301
|
console.error(`Error generating narrative for section ${section}:`, error);
|
|
2224
2302
|
return `<div class="error">Error generating narrative: ${error instanceof Error ? error.message : String(error)}</div>`;
|
|
@@ -2265,35 +2343,35 @@ var NarrativeGenerator = class {
|
|
|
2265
2343
|
* @param resources - Array of domain resources
|
|
2266
2344
|
* @param timezone - Optional timezone to use for date formatting
|
|
2267
2345
|
* @param minify - Whether to minify the HTML content (default: true)
|
|
2268
|
-
* @param wrapInXhtml - Whether to wrap the content in XHTML div
|
|
2269
2346
|
* @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
|
|
2270
2347
|
*/
|
|
2271
|
-
static async generateNarrativeAsync(section, resources, timezone, minify = true
|
|
2272
|
-
const content = await this.generateNarrativeContentAsync(section, resources, timezone
|
|
2348
|
+
static async generateNarrativeAsync(section, resources, timezone, minify = true) {
|
|
2349
|
+
const content = await this.generateNarrativeContentAsync(section, resources, timezone);
|
|
2273
2350
|
if (!content) {
|
|
2274
2351
|
return void 0;
|
|
2275
2352
|
}
|
|
2276
2353
|
return await this.createNarrativeAsync(content, minify);
|
|
2277
2354
|
}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2355
|
+
};
|
|
2356
|
+
|
|
2357
|
+
// src/structures/ips_mandatory_sections.ts
|
|
2358
|
+
var IPSMandatorySections = ((IPSMandatorySections2) => {
|
|
2359
|
+
IPSMandatorySections2[IPSMandatorySections2["PATIENT"] = "Patient" /* PATIENT */] = "PATIENT";
|
|
2360
|
+
IPSMandatorySections2[IPSMandatorySections2["PROBLEMS"] = "ProblemSection" /* PROBLEMS */] = "PROBLEMS";
|
|
2361
|
+
IPSMandatorySections2[IPSMandatorySections2["ALLERGIES"] = "AllergyIntoleranceSection" /* ALLERGIES */] = "ALLERGIES";
|
|
2362
|
+
IPSMandatorySections2[IPSMandatorySections2["MEDICATIONS"] = "MedicationSummarySection" /* MEDICATIONS */] = "MEDICATIONS";
|
|
2363
|
+
return IPSMandatorySections2;
|
|
2364
|
+
})(IPSMandatorySections || {});
|
|
2365
|
+
var IPSMissingMandatorySectionContent = {
|
|
2366
|
+
["ProblemSection" /* PROBLEMS */]: "There is no information available about the subject's health problems or disabilities.",
|
|
2367
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: "There is no information available regarding the subject's allergy conditions.",
|
|
2368
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "There is no information available about the subject's medication use or administration."
|
|
2290
2369
|
};
|
|
2291
2370
|
|
|
2292
2371
|
// src/generators/fhir_summary_generator.ts
|
|
2293
2372
|
var ComprehensiveIPSCompositionBuilder = class {
|
|
2294
2373
|
constructor() {
|
|
2295
2374
|
this.sections = [];
|
|
2296
|
-
this.mandatorySectionsAdded = /* @__PURE__ */ new Set();
|
|
2297
2375
|
this.resources = /* @__PURE__ */ new Set();
|
|
2298
2376
|
}
|
|
2299
2377
|
/**
|
|
@@ -2311,35 +2389,35 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2311
2389
|
/**
|
|
2312
2390
|
* Adds a section to the composition with async HTML minification
|
|
2313
2391
|
* @param sectionType - IPS section type
|
|
2314
|
-
* @param
|
|
2392
|
+
* @param validResources - Array of domain resources
|
|
2315
2393
|
* @param timezone - Optional timezone to use for date formatting
|
|
2316
|
-
* @param options - Optional configuration options
|
|
2317
2394
|
*/
|
|
2318
|
-
async addSectionAsync(sectionType,
|
|
2319
|
-
const validResources = resources;
|
|
2395
|
+
async addSectionAsync(sectionType, validResources, timezone) {
|
|
2320
2396
|
for (const resource of validResources) {
|
|
2321
2397
|
this.resources.add(resource);
|
|
2322
2398
|
}
|
|
2323
|
-
if (validResources.length === 0) {
|
|
2324
|
-
if (!options?.isOptional) {
|
|
2325
|
-
throw new Error(`No valid resources for mandatory section: ${sectionType}`);
|
|
2326
|
-
}
|
|
2327
|
-
return this;
|
|
2328
|
-
}
|
|
2329
2399
|
if (sectionType !== "Patient" /* PATIENT */) {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2400
|
+
let narrative = void 0;
|
|
2401
|
+
if (validResources.length > 0) {
|
|
2402
|
+
narrative = await NarrativeGenerator.generateNarrativeAsync(
|
|
2403
|
+
sectionType,
|
|
2404
|
+
validResources,
|
|
2405
|
+
timezone,
|
|
2406
|
+
true
|
|
2407
|
+
);
|
|
2408
|
+
} else if (sectionType in IPSMandatorySections) {
|
|
2409
|
+
narrative = await NarrativeGenerator.createNarrativeAsync(
|
|
2410
|
+
IPSMissingMandatorySectionContent[sectionType]
|
|
2411
|
+
);
|
|
2412
|
+
} else {
|
|
2413
|
+
return this;
|
|
2414
|
+
}
|
|
2337
2415
|
const sectionEntry = {
|
|
2338
2416
|
title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
|
|
2339
2417
|
code: {
|
|
2340
2418
|
coding: [{
|
|
2341
2419
|
system: "http://loinc.org",
|
|
2342
|
-
code:
|
|
2420
|
+
code: IPS_SECTION_LOINC_CODES[sectionType],
|
|
2343
2421
|
display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
2344
2422
|
}],
|
|
2345
2423
|
text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
@@ -2350,9 +2428,6 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2350
2428
|
display: resource.resourceType
|
|
2351
2429
|
}))
|
|
2352
2430
|
};
|
|
2353
|
-
if (!options?.isOptional) {
|
|
2354
|
-
this.mandatorySectionsAdded.add(sectionType);
|
|
2355
|
-
}
|
|
2356
2431
|
this.sections.push(sectionEntry);
|
|
2357
2432
|
}
|
|
2358
2433
|
return this;
|
|
@@ -2371,43 +2446,20 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2371
2446
|
throw new Error("Patient resource not found in the bundle");
|
|
2372
2447
|
}
|
|
2373
2448
|
this.patient = patientEntry.resource;
|
|
2449
|
+
const resources = bundle.entry.map((e) => e.resource);
|
|
2374
2450
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2375
2451
|
const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
|
|
2376
2452
|
const customFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2377
|
-
let
|
|
2453
|
+
let sectionResources = resources.filter(
|
|
2454
|
+
(r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
|
|
2455
|
+
);
|
|
2378
2456
|
if (customFilter) {
|
|
2379
|
-
|
|
2380
|
-
}
|
|
2381
|
-
if (resources.length > 0) {
|
|
2382
|
-
await this.addSectionAsync(sectionType, resources, timezone, {
|
|
2383
|
-
isOptional: true
|
|
2384
|
-
});
|
|
2457
|
+
sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
|
|
2385
2458
|
}
|
|
2459
|
+
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2386
2460
|
}
|
|
2387
2461
|
return this;
|
|
2388
2462
|
}
|
|
2389
|
-
/**
|
|
2390
|
-
* Builds the final Composition sections, ensuring all mandatory sections are present.
|
|
2391
|
-
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2392
|
-
*/
|
|
2393
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2394
|
-
build(timezone) {
|
|
2395
|
-
const mandatorySections = [
|
|
2396
|
-
"AllergyIntoleranceSection" /* ALLERGIES */,
|
|
2397
|
-
"MedicationSection" /* MEDICATIONS */,
|
|
2398
|
-
"ProblemSection" /* PROBLEMS */,
|
|
2399
|
-
"ImmunizationSection" /* IMMUNIZATIONS */
|
|
2400
|
-
];
|
|
2401
|
-
const missingMandatorySections = mandatorySections.filter(
|
|
2402
|
-
(section) => !this.mandatorySectionsAdded.has(section)
|
|
2403
|
-
);
|
|
2404
|
-
if (missingMandatorySections.length > 0) {
|
|
2405
|
-
throw new Error(
|
|
2406
|
-
`Missing mandatory IPS sections: ${missingMandatorySections.join(", ")}`
|
|
2407
|
-
);
|
|
2408
|
-
}
|
|
2409
|
-
return this.sections;
|
|
2410
|
-
}
|
|
2411
2463
|
/**
|
|
2412
2464
|
* Builds a complete FHIR Bundle containing the Composition and all resources.
|
|
2413
2465
|
* @param authorOrganizationId - ID of the authoring organization (e.g., hospital or clinic)
|
|
@@ -2444,7 +2496,12 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2444
2496
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2445
2497
|
title: "International Patient Summary",
|
|
2446
2498
|
section: this.sections,
|
|
2447
|
-
text: await
|
|
2499
|
+
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2500
|
+
"Patient" /* PATIENT */,
|
|
2501
|
+
[this.patient],
|
|
2502
|
+
timezone,
|
|
2503
|
+
false
|
|
2504
|
+
)
|
|
2448
2505
|
};
|
|
2449
2506
|
const bundle = {
|
|
2450
2507
|
resourceType: "Bundle",
|
|
@@ -2484,43 +2541,6 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2484
2541
|
});
|
|
2485
2542
|
return bundle;
|
|
2486
2543
|
}
|
|
2487
|
-
/**
|
|
2488
|
-
* Creates a narrative for the composition based on the patient and sections.
|
|
2489
|
-
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2490
|
-
* @private
|
|
2491
|
-
*/
|
|
2492
|
-
async createCompositionNarrativeAsync(timezone) {
|
|
2493
|
-
const patient = this.patient;
|
|
2494
|
-
let fullNarrativeContent = "";
|
|
2495
|
-
const patientNarrative = await NarrativeGenerator.generateNarrativeContentAsync(
|
|
2496
|
-
"Patient" /* PATIENT */,
|
|
2497
|
-
[patient],
|
|
2498
|
-
timezone,
|
|
2499
|
-
false
|
|
2500
|
-
);
|
|
2501
|
-
fullNarrativeContent = fullNarrativeContent.concat(patientNarrative || "");
|
|
2502
|
-
for (const sectionType of Object.values(IPSSections)) {
|
|
2503
|
-
if (sectionType === "Patient" /* PATIENT */) {
|
|
2504
|
-
continue;
|
|
2505
|
-
}
|
|
2506
|
-
const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
|
|
2507
|
-
const allResources = Array.from(this.resources);
|
|
2508
|
-
const resources = allResources.filter((r) => resourceTypesForSection.includes(r.resourceType));
|
|
2509
|
-
if (resources.length > 0) {
|
|
2510
|
-
const sectionNarrative = await NarrativeGenerator.generateNarrativeContentAsync(
|
|
2511
|
-
sectionType,
|
|
2512
|
-
resources,
|
|
2513
|
-
timezone,
|
|
2514
|
-
false
|
|
2515
|
-
);
|
|
2516
|
-
fullNarrativeContent = fullNarrativeContent.concat(sectionNarrative || "");
|
|
2517
|
-
}
|
|
2518
|
-
}
|
|
2519
|
-
return {
|
|
2520
|
-
status: "generated",
|
|
2521
|
-
div: await NarrativeGenerator.wrapInXhtmlAsync(fullNarrativeContent, true)
|
|
2522
|
-
};
|
|
2523
|
-
}
|
|
2524
2544
|
};
|
|
2525
2545
|
|
|
2526
2546
|
// src/index.ts
|