@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.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
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";
|
|
20
18
|
return IPSSections2;
|
|
21
19
|
})(IPSSections || {});
|
|
22
20
|
|
|
@@ -24,99 +22,106 @@ var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
|
|
|
24
22
|
var IPS_SECTION_LOINC_CODES = {
|
|
25
23
|
["Patient" /* PATIENT */]: "54126-4",
|
|
26
24
|
["AllergyIntoleranceSection" /* ALLERGIES */]: "48765-2",
|
|
27
|
-
["
|
|
25
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "10160-0",
|
|
28
26
|
["ProblemSection" /* PROBLEMS */]: "11450-4",
|
|
29
27
|
["ImmunizationSection" /* IMMUNIZATIONS */]: "11369-6",
|
|
30
28
|
["VitalSignsSection" /* VITAL_SIGNS */]: "8716-3",
|
|
31
29
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "46264-8",
|
|
32
|
-
["
|
|
33
|
-
["
|
|
34
|
-
["FamilyHistorySection" /* FAMILY_HISTORY */]: "10157-6",
|
|
30
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
|
|
31
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: "47519-4",
|
|
35
32
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: "29762-2",
|
|
36
|
-
["
|
|
33
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "10162-6",
|
|
37
34
|
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "47420-5",
|
|
38
|
-
["
|
|
39
|
-
["
|
|
40
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: "51848-0",
|
|
35
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "11348-0",
|
|
36
|
+
["PlanOfCareSection" /* CARE_PLAN */]: "18776-5",
|
|
41
37
|
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "42348-3"
|
|
42
38
|
};
|
|
43
39
|
var IPS_SECTION_DISPLAY_NAMES = {
|
|
44
40
|
["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
|
-
|
|
41
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: "Allergies and Intolerances",
|
|
42
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "Medication Summary",
|
|
43
|
+
["ProblemSection" /* PROBLEMS */]: "Problem List",
|
|
44
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: "Immunizations",
|
|
45
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "Results Summary",
|
|
46
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: "History of Procedures",
|
|
47
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "History of Medical Devices",
|
|
48
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: "Vital Signs",
|
|
49
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: "Advance Directives",
|
|
50
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "Functional Status",
|
|
51
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "History of Pregnancies",
|
|
52
|
+
["PlanOfCareSection" /* CARE_PLAN */]: "Plan of Care",
|
|
53
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "History of Past Illness",
|
|
54
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History"
|
|
55
|
+
};
|
|
56
|
+
var PREGNANCY_LOINC_CODES = {
|
|
57
|
+
PREGNANCY_STATUS: {
|
|
58
|
+
"LA15173-0": "Pregnant",
|
|
59
|
+
"LA26683-5": "Not pregnant",
|
|
60
|
+
"LA4489-6": "Unknown"
|
|
61
|
+
},
|
|
62
|
+
PREGNANCY_OUTCOME: {
|
|
63
|
+
"11636-8": "Live Birth",
|
|
64
|
+
"11637-6": "Preterm Birth",
|
|
65
|
+
"11638-4": "Still Living Birth",
|
|
66
|
+
"11639-2": "Term Birth",
|
|
67
|
+
"11640-0": "Total Births",
|
|
68
|
+
"11612-9": "Abortions",
|
|
69
|
+
"11613-7": "Induced Abortions",
|
|
70
|
+
"11614-5": "Spontaneous Abortions",
|
|
71
|
+
"33065-4": "Ectopic Pregnancy"
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var SOCIAL_HISTORY_LOINC_CODES = {
|
|
75
|
+
"72166-2": "Tobacco Use",
|
|
76
|
+
"74013-4": "Alcohol Use"
|
|
61
77
|
};
|
|
62
78
|
|
|
63
79
|
// src/structures/ips_section_resource_map.ts
|
|
64
80
|
var IPSSectionResourceMap = {
|
|
65
81
|
["Patient" /* PATIENT */]: ["Patient"],
|
|
66
82
|
["AllergyIntoleranceSection" /* ALLERGIES */]: ["AllergyIntolerance"],
|
|
67
|
-
["
|
|
83
|
+
["MedicationSummarySection" /* MEDICATIONS */]: ["MedicationRequest", "MedicationStatement", "Medication"],
|
|
84
|
+
// Medication resource is needed for identifying name of medication
|
|
68
85
|
["ProblemSection" /* PROBLEMS */]: ["Condition"],
|
|
69
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization"],
|
|
86
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: ["Immunization", "Organization"],
|
|
87
|
+
// Immunization can include Organization as a related resource
|
|
70
88
|
["VitalSignsSection" /* VITAL_SIGNS */]: ["Observation"],
|
|
71
|
-
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["Device"],
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
["
|
|
75
|
-
["FamilyHistorySection" /* FAMILY_HISTORY */]: ["FamilyMemberHistory"],
|
|
89
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: ["DeviceUseStatement", "Device"],
|
|
90
|
+
// Device resource is used for medical devices name
|
|
91
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
|
|
92
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
|
|
76
93
|
["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
|
|
94
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
|
|
95
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
|
|
96
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: ["Condition"],
|
|
97
|
+
["PlanOfCareSection" /* CARE_PLAN */]: ["CarePlan"],
|
|
98
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: ["Consent"]
|
|
88
99
|
};
|
|
89
100
|
var IPSSectionResourceFilters = {
|
|
90
101
|
// 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",
|
|
102
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "AllergyIntolerance" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string"),
|
|
94
103
|
// Only include active problems/conditions
|
|
95
|
-
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => c.code
|
|
104
|
+
["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => !["inactive", "resolved"].includes(c.code)),
|
|
96
105
|
// Only include completed immunizations
|
|
97
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed",
|
|
106
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
98
107
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
99
108
|
["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
109
|
// Only include finalized diagnostic reports
|
|
103
|
-
["
|
|
110
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
|
|
104
111
|
// Only include completed procedures
|
|
105
|
-
["
|
|
106
|
-
// Only include family history resources
|
|
107
|
-
["FamilyHistorySection" /* FAMILY_HISTORY */]: (resource) => resource.resourceType === "FamilyMemberHistory",
|
|
112
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
|
|
108
113
|
// Only include social history Observations (category.coding contains 'social-history')
|
|
109
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.
|
|
114
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LOINC_CODES).includes(c.code)),
|
|
110
115
|
// 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
|
-
["
|
|
116
|
+
["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))),
|
|
117
|
+
// Only include active functional status Conditions or ClinicalImpressions
|
|
118
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string") || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
|
|
119
|
+
// Only include resolved medical history Conditions
|
|
120
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
|
|
116
121
|
// Only include active care plans
|
|
117
|
-
["
|
|
118
|
-
// Only include
|
|
119
|
-
["
|
|
122
|
+
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
123
|
+
// Only include active advance directives (Consent resources)
|
|
124
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active",
|
|
120
125
|
// Patient section: only Patient resource
|
|
121
126
|
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Patient"
|
|
122
127
|
};
|
|
@@ -158,8 +163,6 @@ var TemplateUtilities = class {
|
|
|
158
163
|
}
|
|
159
164
|
if (cc.text) {
|
|
160
165
|
return cc.text;
|
|
161
|
-
} else if ("display" in cc && cc.display) {
|
|
162
|
-
return cc.display;
|
|
163
166
|
} else if (cc.coding && cc.coding[0]) {
|
|
164
167
|
if (cc.coding[0].display) {
|
|
165
168
|
return cc.coding[0].display;
|
|
@@ -191,8 +194,8 @@ var TemplateUtilities = class {
|
|
|
191
194
|
*/
|
|
192
195
|
renderDevice(deviceRef) {
|
|
193
196
|
const device = deviceRef && this.resolveReference(deviceRef);
|
|
194
|
-
if (device && device.resourceType === "Device" && device.
|
|
195
|
-
return this.
|
|
197
|
+
if (device && device.resourceType === "Device" && device.deviceName && device.deviceName.length > 0) {
|
|
198
|
+
return this.safeConcat(device.deviceName, "name");
|
|
196
199
|
}
|
|
197
200
|
return "";
|
|
198
201
|
}
|
|
@@ -373,8 +376,12 @@ var TemplateUtilities = class {
|
|
|
373
376
|
}
|
|
374
377
|
const texts = [];
|
|
375
378
|
for (const item of list) {
|
|
376
|
-
if (item && item.manifestation && item.manifestation[0]
|
|
377
|
-
|
|
379
|
+
if (item && item.manifestation && item.manifestation[0]) {
|
|
380
|
+
if (item.manifestation[0].text) {
|
|
381
|
+
texts.push(item.manifestation[0].text);
|
|
382
|
+
} else if (item.manifestation[0].coding && item.manifestation[0].coding[0]?.display) {
|
|
383
|
+
texts.push(item.manifestation[0].coding[0].display);
|
|
384
|
+
}
|
|
378
385
|
}
|
|
379
386
|
}
|
|
380
387
|
return texts.join(", ");
|
|
@@ -533,6 +540,25 @@ var TemplateUtilities = class {
|
|
|
533
540
|
}
|
|
534
541
|
return null;
|
|
535
542
|
}
|
|
543
|
+
extractPregnancyStatus(observation) {
|
|
544
|
+
let status = "";
|
|
545
|
+
observation.code?.coding?.forEach((c) => {
|
|
546
|
+
if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) {
|
|
547
|
+
status = PREGNANCY_LOINC_CODES.PREGNANCY_STATUS[c.code];
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
if (observation.valueCodeableConcept) {
|
|
551
|
+
observation.valueCodeableConcept.coding?.forEach((c) => {
|
|
552
|
+
if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
|
|
553
|
+
if (status) {
|
|
554
|
+
status += " - ";
|
|
555
|
+
}
|
|
556
|
+
status += PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME[c.code];
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
return status;
|
|
561
|
+
}
|
|
536
562
|
formatQuantityValue(quantity) {
|
|
537
563
|
if (!quantity) return "";
|
|
538
564
|
const parts = [];
|
|
@@ -771,7 +797,6 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
771
797
|
const patient = entry.resource;
|
|
772
798
|
html += `
|
|
773
799
|
<div>
|
|
774
|
-
<h2>Patient Summary</h2>
|
|
775
800
|
<ul>
|
|
776
801
|
<li><strong>Name(s):</strong>${this.renderNames(patient)}</li>
|
|
777
802
|
<li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
|
|
@@ -921,10 +946,20 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
921
946
|
}
|
|
922
947
|
}
|
|
923
948
|
}
|
|
949
|
+
activeAllergies.sort((a, b) => {
|
|
950
|
+
const dateA = a.onsetDateTime;
|
|
951
|
+
const dateB = b.onsetDateTime;
|
|
952
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
953
|
+
});
|
|
954
|
+
resolvedAllergies.sort((a, b) => {
|
|
955
|
+
const dateA = a.onsetDateTime;
|
|
956
|
+
const dateB = b.onsetDateTime;
|
|
957
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
958
|
+
});
|
|
924
959
|
let html = "";
|
|
925
960
|
html += `
|
|
926
961
|
<div class="ActiveAllergies">
|
|
927
|
-
<h3>Active
|
|
962
|
+
<h3>Active</h3>
|
|
928
963
|
<table class="ActiveAllergyTable">
|
|
929
964
|
<thead>
|
|
930
965
|
<tr>
|
|
@@ -952,7 +987,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
952
987
|
</div>`;
|
|
953
988
|
html += `
|
|
954
989
|
<div class="ResolvedAllergies">
|
|
955
|
-
<h3>Resolved
|
|
990
|
+
<h3>Resolved</h3>
|
|
956
991
|
<table class="ResolvedAllergyTable">
|
|
957
992
|
<thead>
|
|
958
993
|
<tr>
|
|
@@ -996,7 +1031,7 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
996
1031
|
<tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
|
|
997
1032
|
<td class="Name"><span class="AllergenName">${templateUtilities.codeableConcept(allergy.code)}</span></td>
|
|
998
1033
|
<td class="Status">${templateUtilities.codeableConcept(allergy.clinicalStatus) || "-"}</td>
|
|
999
|
-
<td class="Category">${templateUtilities.safeConcat(allergy.category
|
|
1034
|
+
<td class="Category">${templateUtilities.safeConcat(allergy.category) || "-"}</td>
|
|
1000
1035
|
<td class="Reaction">${templateUtilities.concatReactionManifestation(allergy.reaction) || "-"}</td>
|
|
1001
1036
|
<td class="Severity">${templateUtilities.safeConcat(allergy.reaction, "severity") || "-"}</td>
|
|
1002
1037
|
<td class="OnsetDate">${templateUtilities.renderTime(allergy.onsetDateTime, timezone) || "-"}</td>
|
|
@@ -1031,6 +1066,67 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1031
1066
|
generateNarrative(resource, timezone) {
|
|
1032
1067
|
return _MedicationSummaryTemplate.generateStaticNarrative(resource, timezone);
|
|
1033
1068
|
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Safely parse a date string and return a valid Date object or null
|
|
1071
|
+
* @param dateString - The date string to parse
|
|
1072
|
+
* @returns Date object or null if invalid
|
|
1073
|
+
*/
|
|
1074
|
+
static parseDate(dateString) {
|
|
1075
|
+
if (!dateString || dateString.trim() === "") {
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
const date = new Date(dateString);
|
|
1079
|
+
return !isNaN(date.getTime()) ? date : null;
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Determine if a MedicationRequest is active
|
|
1083
|
+
* @param medicationRequest - The MedicationRequest resource
|
|
1084
|
+
* @returns boolean indicating if the medication request is active
|
|
1085
|
+
*/
|
|
1086
|
+
static isActiveMedicationRequest(medicationRequest) {
|
|
1087
|
+
const status = medicationRequest.status?.toLowerCase();
|
|
1088
|
+
if (status === "active" || status === "unknown") {
|
|
1089
|
+
return true;
|
|
1090
|
+
}
|
|
1091
|
+
if (status === "completed" || status === "cancelled" || status === "stopped" || status === "draft") {
|
|
1092
|
+
return false;
|
|
1093
|
+
}
|
|
1094
|
+
const endDate = medicationRequest.dispenseRequest?.validityPeriod?.end;
|
|
1095
|
+
if (!endDate) {
|
|
1096
|
+
return true;
|
|
1097
|
+
}
|
|
1098
|
+
const parsedEndDate = this.parseDate(endDate);
|
|
1099
|
+
if (!parsedEndDate) {
|
|
1100
|
+
return true;
|
|
1101
|
+
}
|
|
1102
|
+
return parsedEndDate.getTime() > Date.now();
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Determine if a MedicationStatement is active
|
|
1106
|
+
* @param medicationStatement - The MedicationStatement resource
|
|
1107
|
+
* @returns boolean indicating if the medication statement is active
|
|
1108
|
+
*/
|
|
1109
|
+
static isActiveMedicationStatement(medicationStatement) {
|
|
1110
|
+
const status = medicationStatement.status?.toLowerCase();
|
|
1111
|
+
if (status === "active" || status === "intended" || status === "unknown") {
|
|
1112
|
+
return true;
|
|
1113
|
+
}
|
|
1114
|
+
if (status === "completed" || status === "stopped" || status === "not-taken") {
|
|
1115
|
+
return false;
|
|
1116
|
+
}
|
|
1117
|
+
let endDate;
|
|
1118
|
+
if (medicationStatement.effectivePeriod?.end) {
|
|
1119
|
+
endDate = medicationStatement.effectivePeriod.end;
|
|
1120
|
+
}
|
|
1121
|
+
if (!endDate) {
|
|
1122
|
+
return true;
|
|
1123
|
+
}
|
|
1124
|
+
const parsedEndDate = this.parseDate(endDate);
|
|
1125
|
+
if (!parsedEndDate) {
|
|
1126
|
+
return true;
|
|
1127
|
+
}
|
|
1128
|
+
return parsedEndDate.getTime() > Date.now();
|
|
1129
|
+
}
|
|
1034
1130
|
/**
|
|
1035
1131
|
* Internal static implementation that actually generates the narrative
|
|
1036
1132
|
* @param resource - FHIR Bundle containing Medication resources
|
|
@@ -1042,12 +1138,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1042
1138
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1043
1139
|
let html = "";
|
|
1044
1140
|
const medicationRequests = this.getMedicationRequests(templateUtilities, resource);
|
|
1045
|
-
if (medicationRequests.length > 0) {
|
|
1046
|
-
html += this.renderMedicationRequests(templateUtilities, medicationRequests);
|
|
1047
|
-
}
|
|
1048
1141
|
const medicationStatements = this.getMedicationStatements(templateUtilities, resource);
|
|
1049
|
-
|
|
1050
|
-
|
|
1142
|
+
const allActiveMedications = [];
|
|
1143
|
+
const allInactiveMedications = [];
|
|
1144
|
+
medicationRequests.forEach((mr) => {
|
|
1145
|
+
if (this.isActiveMedicationRequest(mr.resource)) {
|
|
1146
|
+
allActiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1147
|
+
} else {
|
|
1148
|
+
allInactiveMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
medicationStatements.forEach((ms) => {
|
|
1152
|
+
if (this.isActiveMedicationStatement(ms.resource)) {
|
|
1153
|
+
allActiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1154
|
+
} else {
|
|
1155
|
+
allInactiveMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
const sortMedications = (medications) => {
|
|
1159
|
+
medications.sort((a, b) => {
|
|
1160
|
+
let dateStringA;
|
|
1161
|
+
let dateStringB;
|
|
1162
|
+
if (a.type === "request") {
|
|
1163
|
+
const mr = a.resource;
|
|
1164
|
+
dateStringA = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
1165
|
+
} else {
|
|
1166
|
+
const ms = a.resource;
|
|
1167
|
+
dateStringA = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
1168
|
+
}
|
|
1169
|
+
if (b.type === "request") {
|
|
1170
|
+
const mr = b.resource;
|
|
1171
|
+
dateStringB = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
1172
|
+
} else {
|
|
1173
|
+
const ms = b.resource;
|
|
1174
|
+
dateStringB = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
1175
|
+
}
|
|
1176
|
+
const dateA = this.parseDate(dateStringA);
|
|
1177
|
+
const dateB = this.parseDate(dateStringB);
|
|
1178
|
+
if (!dateA && !dateB) return 0;
|
|
1179
|
+
if (!dateA) return 1;
|
|
1180
|
+
if (!dateB) return -1;
|
|
1181
|
+
return dateB.getTime() - dateA.getTime();
|
|
1182
|
+
});
|
|
1183
|
+
};
|
|
1184
|
+
if (allActiveMedications.length > 0) {
|
|
1185
|
+
sortMedications(allActiveMedications);
|
|
1186
|
+
html += this.renderCombinedMedications(templateUtilities, allActiveMedications, "Active Medications");
|
|
1187
|
+
}
|
|
1188
|
+
if (allInactiveMedications.length > 0) {
|
|
1189
|
+
sortMedications(allInactiveMedications);
|
|
1190
|
+
html += this.renderCombinedMedications(templateUtilities, allInactiveMedications, "Inactive Medications");
|
|
1051
1191
|
}
|
|
1052
1192
|
return html;
|
|
1053
1193
|
}
|
|
@@ -1082,16 +1222,19 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1082
1222
|
}));
|
|
1083
1223
|
}
|
|
1084
1224
|
/**
|
|
1085
|
-
* Render HTML table for MedicationRequest resources
|
|
1225
|
+
* Render HTML table for combined MedicationRequest and MedicationStatement resources
|
|
1086
1226
|
* @param templateUtilities - Instance of TemplateUtilities for utility functions
|
|
1087
|
-
* @param medications - Array of
|
|
1227
|
+
* @param medications - Array of combined medication resources
|
|
1228
|
+
* @param sectionTitle - Title for the section
|
|
1088
1229
|
* @returns HTML string for rendering
|
|
1089
1230
|
*/
|
|
1090
|
-
static
|
|
1231
|
+
static renderCombinedMedications(templateUtilities, medications, sectionTitle) {
|
|
1091
1232
|
let html = `
|
|
1233
|
+
<h3>${sectionTitle}</h3>
|
|
1092
1234
|
<table>
|
|
1093
1235
|
<thead>
|
|
1094
1236
|
<tr>
|
|
1237
|
+
<th>Type</th>
|
|
1095
1238
|
<th>Medication</th>
|
|
1096
1239
|
<th>Sig</th>
|
|
1097
1240
|
<th>Dispense Quantity</th>
|
|
@@ -1102,86 +1245,56 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1102
1245
|
</tr>
|
|
1103
1246
|
</thead>
|
|
1104
1247
|
<tbody>`;
|
|
1105
|
-
for (const
|
|
1106
|
-
const narrativeLinkId = templateUtilities.narrativeLinkId(extension);
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
);
|
|
1111
|
-
const sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
|
|
1248
|
+
for (const medication of medications) {
|
|
1249
|
+
const narrativeLinkId = templateUtilities.narrativeLinkId(medication.extension);
|
|
1250
|
+
let type;
|
|
1251
|
+
let medicationName;
|
|
1252
|
+
let sig;
|
|
1112
1253
|
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() || "-";
|
|
1254
|
+
let refills = "-";
|
|
1120
1255
|
let startDate = "-";
|
|
1121
1256
|
let endDate = "-";
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1257
|
+
let status;
|
|
1258
|
+
if (medication.type === "request") {
|
|
1259
|
+
const mr = medication.resource;
|
|
1260
|
+
type = "Request";
|
|
1261
|
+
status = mr.status ? String(mr.status) : "-";
|
|
1262
|
+
medicationName = templateUtilities.getMedicationName(
|
|
1263
|
+
mr.medicationReference || mr.medicationCodeableConcept
|
|
1264
|
+
);
|
|
1265
|
+
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "-";
|
|
1266
|
+
if (mr.dispenseRequest?.quantity) {
|
|
1267
|
+
const quantity = mr.dispenseRequest.quantity;
|
|
1268
|
+
if (quantity.value) {
|
|
1269
|
+
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "-";
|
|
1273
|
+
if (mr.dispenseRequest?.validityPeriod) {
|
|
1274
|
+
startDate = mr.dispenseRequest.validityPeriod.start || "-";
|
|
1275
|
+
endDate = mr.dispenseRequest.validityPeriod.end || "-";
|
|
1276
|
+
} else {
|
|
1277
|
+
startDate = mr.authoredOn || "-";
|
|
1278
|
+
}
|
|
1125
1279
|
} 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 || "-";
|
|
1280
|
+
const ms = medication.resource;
|
|
1281
|
+
type = "Statement";
|
|
1282
|
+
status = ms.status ? String(ms.status) : "-";
|
|
1283
|
+
medicationName = templateUtilities.getMedicationName(
|
|
1284
|
+
ms.medicationReference || ms.medicationCodeableConcept
|
|
1285
|
+
);
|
|
1286
|
+
sig = templateUtilities.concat(ms.dosage, "text") || "-";
|
|
1287
|
+
if (ms.effectiveDateTime) {
|
|
1288
|
+
startDate = ms.effectiveDateTime;
|
|
1289
|
+
} else if (ms.effectivePeriod) {
|
|
1290
|
+
startDate = ms.effectivePeriod.start || "-";
|
|
1291
|
+
endDate = ms.effectivePeriod.end || "-";
|
|
1292
|
+
}
|
|
1181
1293
|
}
|
|
1182
1294
|
html += `
|
|
1183
1295
|
<tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
|
|
1184
|
-
<td>${
|
|
1296
|
+
<td>${type}</td>
|
|
1297
|
+
<td>${medicationName}<ul></ul></td>
|
|
1185
1298
|
<td>${sig}</td>
|
|
1186
1299
|
<td>${dispenseQuantity}</td>
|
|
1187
1300
|
<td>${refills}</td>
|
|
@@ -1206,6 +1319,13 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1206
1319
|
* @returns HTML string for rendering
|
|
1207
1320
|
*/
|
|
1208
1321
|
generateNarrative(resource, timezone) {
|
|
1322
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1323
|
+
resource.entry.sort((a, b) => {
|
|
1324
|
+
const dateA = a.resource?.occurrenceDateTime;
|
|
1325
|
+
const dateB = b.resource?.occurrenceDateTime;
|
|
1326
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1209
1329
|
return _ImmunizationsTemplate.generateStaticNarrative(resource, timezone);
|
|
1210
1330
|
}
|
|
1211
1331
|
/**
|
|
@@ -1217,7 +1337,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
1217
1337
|
static generateStaticNarrative(resource, timezone) {
|
|
1218
1338
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1219
1339
|
let html = `
|
|
1220
|
-
<h5>Immunizations</h5>
|
|
1221
1340
|
<table>
|
|
1222
1341
|
<thead>
|
|
1223
1342
|
<tr>
|
|
@@ -1275,81 +1394,35 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
1275
1394
|
static generateStaticNarrative(resource, timezone) {
|
|
1276
1395
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1277
1396
|
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">
|
|
1397
|
+
const activeConditions = resource.entry?.map((entry) => entry.resource) || [];
|
|
1398
|
+
activeConditions.sort((a, b) => {
|
|
1399
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1400
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1401
|
+
return dateB - dateA;
|
|
1402
|
+
});
|
|
1403
|
+
html += `
|
|
1404
|
+
<table>
|
|
1328
1405
|
<thead>
|
|
1329
1406
|
<tr>
|
|
1330
1407
|
<th>Problem</th>
|
|
1331
|
-
<th>
|
|
1332
|
-
<th>
|
|
1333
|
-
<th>
|
|
1334
|
-
<th>Resolved Date</th>
|
|
1408
|
+
<th>Severity</th>
|
|
1409
|
+
<th>Onset Date</th>
|
|
1410
|
+
<th>Recorded Date</th>
|
|
1335
1411
|
<th>Notes</th>
|
|
1336
1412
|
</tr>
|
|
1337
1413
|
</thead>
|
|
1338
1414
|
<tbody>`;
|
|
1339
|
-
|
|
1340
|
-
|
|
1415
|
+
for (const cond of activeConditions) {
|
|
1416
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1341
1417
|
<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>
|
|
1418
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1419
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1420
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1421
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1347
1422
|
</tr>`;
|
|
1348
|
-
}
|
|
1349
|
-
html += `</tbody>
|
|
1350
|
-
</table>
|
|
1351
|
-
</div>`;
|
|
1352
1423
|
}
|
|
1424
|
+
html += `</tbody>
|
|
1425
|
+
</table>`;
|
|
1353
1426
|
return html;
|
|
1354
1427
|
}
|
|
1355
1428
|
};
|
|
@@ -1373,8 +1446,13 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1373
1446
|
*/
|
|
1374
1447
|
static generateStaticNarrative(resource, timezone) {
|
|
1375
1448
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1449
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
1450
|
+
observations.sort((a, b) => {
|
|
1451
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1452
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1453
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1454
|
+
});
|
|
1376
1455
|
let html = `
|
|
1377
|
-
<h5>Vital Signs</h5>
|
|
1378
1456
|
<table>
|
|
1379
1457
|
<thead>
|
|
1380
1458
|
<tr>
|
|
@@ -1388,13 +1466,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1388
1466
|
</tr>
|
|
1389
1467
|
</thead>
|
|
1390
1468
|
<tbody>`;
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
const obs = entry.resource;
|
|
1394
|
-
if (obs.resourceType === "Composition") {
|
|
1395
|
-
continue;
|
|
1396
|
-
}
|
|
1397
|
-
html += `
|
|
1469
|
+
for (const obs of observations) {
|
|
1470
|
+
html += `
|
|
1398
1471
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1399
1472
|
<td>${templateUtilities.codeableConcept(obs.code, "display")}</td>
|
|
1400
1473
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
@@ -1402,9 +1475,8 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
1402
1475
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
1403
1476
|
<td>${templateUtilities.renderComponent(obs.component)}</td>
|
|
1404
1477
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1405
|
-
<td>${templateUtilities.
|
|
1478
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1406
1479
|
</tr>`;
|
|
1407
|
-
}
|
|
1408
1480
|
}
|
|
1409
1481
|
html += `
|
|
1410
1482
|
</tbody>
|
|
@@ -1422,6 +1494,13 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1422
1494
|
* @returns HTML string for rendering
|
|
1423
1495
|
*/
|
|
1424
1496
|
generateNarrative(resource, timezone) {
|
|
1497
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1498
|
+
resource.entry.sort((a, b) => {
|
|
1499
|
+
const dateA = a.resource?.recordedOn;
|
|
1500
|
+
const dateB = b.resource?.recordedOn;
|
|
1501
|
+
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1425
1504
|
return _MedicalDevicesTemplate.generateStaticNarrative(resource, timezone);
|
|
1426
1505
|
}
|
|
1427
1506
|
/**
|
|
@@ -1433,7 +1512,6 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
1433
1512
|
static generateStaticNarrative(resource, timezone) {
|
|
1434
1513
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1435
1514
|
let html = `
|
|
1436
|
-
<h5>Medical Devices</h5>
|
|
1437
1515
|
<table>
|
|
1438
1516
|
<thead>
|
|
1439
1517
|
<tr>
|
|
@@ -1487,10 +1565,20 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1487
1565
|
let html = "";
|
|
1488
1566
|
const observations = this.getObservations(resource);
|
|
1489
1567
|
if (observations.length > 0) {
|
|
1568
|
+
observations.sort((a, b) => {
|
|
1569
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1570
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1571
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1572
|
+
});
|
|
1490
1573
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
1491
1574
|
}
|
|
1492
1575
|
const diagnosticReports = this.getDiagnosticReports(resource);
|
|
1493
1576
|
if (diagnosticReports.length > 0) {
|
|
1577
|
+
diagnosticReports.sort((a, b) => {
|
|
1578
|
+
const dateA = a.issued;
|
|
1579
|
+
const dateB = b.issued;
|
|
1580
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1581
|
+
});
|
|
1494
1582
|
html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
|
|
1495
1583
|
}
|
|
1496
1584
|
return html;
|
|
@@ -1526,7 +1614,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1526
1614
|
*/
|
|
1527
1615
|
static renderObservations(templateUtilities, observations, timezone) {
|
|
1528
1616
|
let html = `
|
|
1529
|
-
<h5>
|
|
1617
|
+
<h5>Observations</h5>
|
|
1530
1618
|
<table>
|
|
1531
1619
|
<thead>
|
|
1532
1620
|
<tr>
|
|
@@ -1549,7 +1637,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1549
1637
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
1550
1638
|
<td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
|
|
1551
1639
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1552
|
-
<td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
|
|
1640
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1553
1641
|
</tr>`;
|
|
1554
1642
|
}
|
|
1555
1643
|
html += `
|
|
@@ -1566,7 +1654,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
1566
1654
|
*/
|
|
1567
1655
|
static renderDiagnosticReports(templateUtilities, reports, timezone) {
|
|
1568
1656
|
let html = `
|
|
1569
|
-
<h5>Diagnostic
|
|
1657
|
+
<h5>Diagnostic Reports</h5>
|
|
1570
1658
|
<table>
|
|
1571
1659
|
<thead>
|
|
1572
1660
|
<tr>
|
|
@@ -1608,6 +1696,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1608
1696
|
* @returns HTML string for rendering
|
|
1609
1697
|
*/
|
|
1610
1698
|
generateNarrative(resource, timezone) {
|
|
1699
|
+
resource.entry?.sort((a, b) => {
|
|
1700
|
+
const dateA = a.resource.performedDateTime || a.resource.performedPeriod?.start;
|
|
1701
|
+
const dateB = b.resource.performedDateTime || b.resource.performedPeriod?.start;
|
|
1702
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1703
|
+
});
|
|
1611
1704
|
return _HistoryOfProceduresTemplate.generateStaticNarrative(resource, timezone);
|
|
1612
1705
|
}
|
|
1613
1706
|
/**
|
|
@@ -1619,7 +1712,6 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1619
1712
|
static generateStaticNarrative(resource, timezone) {
|
|
1620
1713
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1621
1714
|
let html = `
|
|
1622
|
-
<h5>History Of Procedures</h5>
|
|
1623
1715
|
<table>
|
|
1624
1716
|
<thead>
|
|
1625
1717
|
<tr>
|
|
@@ -1632,14 +1724,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
1632
1724
|
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1633
1725
|
for (const entry of resource.entry) {
|
|
1634
1726
|
const proc = entry.resource;
|
|
1635
|
-
if (proc.resourceType === "Composition") {
|
|
1636
|
-
continue;
|
|
1637
|
-
}
|
|
1638
1727
|
html += `
|
|
1639
1728
|
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
1640
1729
|
<td>${templateUtilities.codeableConcept(proc.code, "display")}</td>
|
|
1641
1730
|
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
1642
|
-
<td>${templateUtilities.renderTime(proc.performedDateTime, timezone)}</td>
|
|
1731
|
+
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
1643
1732
|
</tr>`;
|
|
1644
1733
|
}
|
|
1645
1734
|
}
|
|
@@ -1669,8 +1758,13 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1669
1758
|
*/
|
|
1670
1759
|
static generateStaticNarrative(resource, timezone) {
|
|
1671
1760
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1761
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
1762
|
+
observations.sort((a, b) => {
|
|
1763
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
1764
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
1765
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
1766
|
+
});
|
|
1672
1767
|
let html = `
|
|
1673
|
-
<h5>Social History</h5>
|
|
1674
1768
|
<table>
|
|
1675
1769
|
<thead>
|
|
1676
1770
|
<tr>
|
|
@@ -1682,21 +1776,15 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
1682
1776
|
</tr>
|
|
1683
1777
|
</thead>
|
|
1684
1778
|
<tbody>`;
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
const obs = entry.resource;
|
|
1688
|
-
if (obs.resourceType === "Composition") {
|
|
1689
|
-
continue;
|
|
1690
|
-
}
|
|
1691
|
-
html += `
|
|
1779
|
+
for (const obs of observations) {
|
|
1780
|
+
html += `
|
|
1692
1781
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1693
|
-
<td>${templateUtilities.codeableConcept(obs.code
|
|
1782
|
+
<td>${templateUtilities.codeableConcept(obs.code)}</td>
|
|
1694
1783
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
1695
1784
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
1696
1785
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1697
|
-
<td>${templateUtilities.renderTime(obs.effectiveDateTime, timezone)}</td>
|
|
1786
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1698
1787
|
</tr>`;
|
|
1699
|
-
}
|
|
1700
1788
|
}
|
|
1701
1789
|
html += `
|
|
1702
1790
|
</tbody>
|
|
@@ -1715,36 +1803,38 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
1715
1803
|
*/
|
|
1716
1804
|
generateNarrative(resource, timezone) {
|
|
1717
1805
|
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
|
-
}
|
|
1806
|
+
let html = ``;
|
|
1807
|
+
const resolvedConditions = resource.entry?.map((entry) => entry.resource) || [];
|
|
1808
|
+
resolvedConditions.sort((a, b) => {
|
|
1809
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1810
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1811
|
+
return dateB - dateA;
|
|
1812
|
+
});
|
|
1745
1813
|
html += `
|
|
1746
|
-
|
|
1747
|
-
|
|
1814
|
+
<table>
|
|
1815
|
+
<thead>
|
|
1816
|
+
<tr>
|
|
1817
|
+
<th>Problem</th>
|
|
1818
|
+
<th>Severity</th>
|
|
1819
|
+
<th>Onset Date</th>
|
|
1820
|
+
<th>Recorded Date</th>
|
|
1821
|
+
<th>Resolved Date</th>
|
|
1822
|
+
<th>Notes</th>
|
|
1823
|
+
</tr>
|
|
1824
|
+
</thead>
|
|
1825
|
+
<tbody>`;
|
|
1826
|
+
for (const cond of resolvedConditions) {
|
|
1827
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1828
|
+
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1829
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1830
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1831
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1832
|
+
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
1833
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone)}</td>
|
|
1834
|
+
</tr>`;
|
|
1835
|
+
}
|
|
1836
|
+
html += `</tbody>
|
|
1837
|
+
</table>`;
|
|
1748
1838
|
return html;
|
|
1749
1839
|
}
|
|
1750
1840
|
};
|
|
@@ -1757,15 +1847,19 @@ var PlanOfCareTemplate = class {
|
|
|
1757
1847
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1758
1848
|
* @returns HTML string for rendering
|
|
1759
1849
|
*/
|
|
1760
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1761
1850
|
generateNarrative(resource, timezone) {
|
|
1762
1851
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1852
|
+
const carePlans = resource.entry?.map((entry) => entry.resource) || [];
|
|
1853
|
+
carePlans.sort((a, b) => {
|
|
1854
|
+
const endA = a.period?.end ? new Date(a.period?.end).getTime() : 0;
|
|
1855
|
+
const endB = b.period?.end ? new Date(b.period?.end).getTime() : 0;
|
|
1856
|
+
return endB - endA;
|
|
1857
|
+
});
|
|
1763
1858
|
let html = `
|
|
1764
|
-
<h5>Plan of Care</h5>
|
|
1765
1859
|
<table>
|
|
1766
1860
|
<thead>
|
|
1767
1861
|
<tr>
|
|
1768
|
-
<th>
|
|
1862
|
+
<th>Description</th>
|
|
1769
1863
|
<th>Intent</th>
|
|
1770
1864
|
<th>Comments</th>
|
|
1771
1865
|
<th>Planned Start</th>
|
|
@@ -1773,21 +1867,15 @@ var PlanOfCareTemplate = class {
|
|
|
1773
1867
|
</tr>
|
|
1774
1868
|
</thead>
|
|
1775
1869
|
<tbody>`;
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
const cp = entry.resource;
|
|
1779
|
-
if (cp.resourceType === "Composition") {
|
|
1780
|
-
continue;
|
|
1781
|
-
}
|
|
1782
|
-
html += `
|
|
1870
|
+
for (const cp of carePlans) {
|
|
1871
|
+
html += `
|
|
1783
1872
|
<tr id="${templateUtilities.narrativeLinkId(cp)}">
|
|
1784
|
-
<td>${cp.description || ""}</td>
|
|
1785
|
-
<td>${cp.intent ||
|
|
1873
|
+
<td>${cp.description || cp.title || ""}</td>
|
|
1874
|
+
<td>${cp.intent || ""}</td>
|
|
1786
1875
|
<td>${templateUtilities.concat(cp.note, "text")}</td>
|
|
1787
|
-
<td>${cp.period?.start
|
|
1788
|
-
<td>${cp.period?.end
|
|
1876
|
+
<td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
|
|
1877
|
+
<td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
|
|
1789
1878
|
</tr>`;
|
|
1790
|
-
}
|
|
1791
1879
|
}
|
|
1792
1880
|
html += `
|
|
1793
1881
|
</tbody>
|
|
@@ -1815,38 +1903,112 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
1815
1903
|
*/
|
|
1816
1904
|
static generateStaticNarrative(resource, timezone) {
|
|
1817
1905
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1818
|
-
let html =
|
|
1819
|
-
|
|
1906
|
+
let html = ``;
|
|
1907
|
+
const activeConditions = [];
|
|
1908
|
+
const clinicalImpressions = [];
|
|
1909
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1910
|
+
for (const entry of resource.entry) {
|
|
1911
|
+
if (entry.resource?.resourceType === "Condition") {
|
|
1912
|
+
const cond = entry.resource;
|
|
1913
|
+
const isResolved = cond.clinicalStatus?.coding?.some(
|
|
1914
|
+
(c) => c.code === "resolved" || c.code === "inactive" || c.display?.toLowerCase().includes("resolved")
|
|
1915
|
+
);
|
|
1916
|
+
if (!isResolved) {
|
|
1917
|
+
activeConditions.push(cond);
|
|
1918
|
+
}
|
|
1919
|
+
} else if (entry.resource?.resourceType === "ClinicalImpression") {
|
|
1920
|
+
clinicalImpressions.push(entry.resource);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
activeConditions.sort((a, b) => {
|
|
1925
|
+
const dateA = a.onsetDateTime ? new Date(a.onsetDateTime).getTime() : 0;
|
|
1926
|
+
const dateB = b.onsetDateTime ? new Date(b.onsetDateTime).getTime() : 0;
|
|
1927
|
+
return dateB - dateA;
|
|
1928
|
+
});
|
|
1929
|
+
clinicalImpressions.sort((a, b) => {
|
|
1930
|
+
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;
|
|
1931
|
+
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;
|
|
1932
|
+
return dateB - dateA;
|
|
1933
|
+
});
|
|
1934
|
+
if (activeConditions.length > 0) {
|
|
1935
|
+
html += `<h3>Conditions</h3>
|
|
1936
|
+
<table>
|
|
1937
|
+
<thead>
|
|
1938
|
+
<tr>
|
|
1939
|
+
<th>Problem</th>
|
|
1940
|
+
<th>Severity</th>
|
|
1941
|
+
<th>Onset Date</th>
|
|
1942
|
+
<th>Recorded Date</th>
|
|
1943
|
+
<th>Notes</th>
|
|
1944
|
+
</tr>
|
|
1945
|
+
</thead>
|
|
1946
|
+
<tbody>`;
|
|
1947
|
+
for (const cond of activeConditions) {
|
|
1948
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
1949
|
+
<td class="Name">${templateUtilities.codeableConcept(cond.code)}</td>
|
|
1950
|
+
<td class="Severity">${templateUtilities.codeableConcept(cond.severity)}</td>
|
|
1951
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
1952
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
1953
|
+
<td class="Notes">${templateUtilities.renderNotes(cond.note, timezone, { styled: true, warning: true })}</td>
|
|
1954
|
+
</tr>`;
|
|
1955
|
+
}
|
|
1956
|
+
html += `</tbody>
|
|
1957
|
+
</table>`;
|
|
1958
|
+
}
|
|
1959
|
+
if (clinicalImpressions.length > 0) {
|
|
1960
|
+
html += `<h3>Clinical Impressions</h3>
|
|
1820
1961
|
<table>
|
|
1821
1962
|
<thead>
|
|
1822
1963
|
<tr>
|
|
1823
|
-
<th>Assessment</th>
|
|
1824
|
-
<th>Status</th>
|
|
1825
|
-
<th>Finding</th>
|
|
1826
|
-
<th>Comments</th>
|
|
1827
1964
|
<th>Date</th>
|
|
1965
|
+
<th>Status</th>
|
|
1966
|
+
<th>Description</th>
|
|
1967
|
+
<th>Summary</th>
|
|
1968
|
+
<th>Findings</th>
|
|
1969
|
+
<th>Notes</th>
|
|
1828
1970
|
</tr>
|
|
1829
1971
|
</thead>
|
|
1830
1972
|
<tbody>`;
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1973
|
+
for (const impression of clinicalImpressions) {
|
|
1974
|
+
let formattedDate = "";
|
|
1975
|
+
if (impression.effectiveDateTime) {
|
|
1976
|
+
formattedDate = templateUtilities.renderTime(
|
|
1977
|
+
impression.effectiveDateTime,
|
|
1978
|
+
timezone
|
|
1979
|
+
);
|
|
1980
|
+
} else if (impression.effectivePeriod) {
|
|
1981
|
+
formattedDate = templateUtilities.renderPeriod(
|
|
1982
|
+
impression.effectivePeriod,
|
|
1983
|
+
timezone
|
|
1984
|
+
);
|
|
1985
|
+
} else if (impression.date) {
|
|
1986
|
+
formattedDate = templateUtilities.renderDate(impression.date);
|
|
1836
1987
|
}
|
|
1988
|
+
let findingsHtml = "";
|
|
1989
|
+
if (impression.finding && impression.finding.length > 0) {
|
|
1990
|
+
findingsHtml = "<ul>";
|
|
1991
|
+
for (const finding of impression.finding) {
|
|
1992
|
+
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConcept(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
1993
|
+
const cause = finding.basis || "";
|
|
1994
|
+
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
1995
|
+
}
|
|
1996
|
+
findingsHtml += "</ul>";
|
|
1997
|
+
}
|
|
1998
|
+
const notes = templateUtilities.renderNotes(impression.note, timezone);
|
|
1837
1999
|
html += `
|
|
1838
|
-
<tr id="${templateUtilities.narrativeLinkId(
|
|
1839
|
-
<td>${
|
|
1840
|
-
<td>${
|
|
1841
|
-
<td>${
|
|
1842
|
-
<td>${
|
|
1843
|
-
<td>${
|
|
2000
|
+
<tr id="${templateUtilities.narrativeLinkId(impression)}">
|
|
2001
|
+
<td>${formattedDate}</td>
|
|
2002
|
+
<td>${impression.status || ""}</td>
|
|
2003
|
+
<td>${impression.description || ""}</td>
|
|
2004
|
+
<td>${impression.summary || ""}</td>
|
|
2005
|
+
<td>${findingsHtml}</td>
|
|
2006
|
+
<td>${notes}</td>
|
|
1844
2007
|
</tr>`;
|
|
1845
2008
|
}
|
|
2009
|
+
html += `</tbody>
|
|
2010
|
+
</table>`;
|
|
1846
2011
|
}
|
|
1847
|
-
html += `
|
|
1848
|
-
</tbody>
|
|
1849
|
-
</table>`;
|
|
1850
2012
|
return html;
|
|
1851
2013
|
}
|
|
1852
2014
|
};
|
|
@@ -1870,32 +2032,30 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
1870
2032
|
*/
|
|
1871
2033
|
static generateStaticNarrative(resource, timezone) {
|
|
1872
2034
|
const templateUtilities = new TemplateUtilities(resource);
|
|
2035
|
+
const observations = resource.entry?.map((entry) => entry.resource) || [];
|
|
2036
|
+
observations.sort((a, b) => {
|
|
2037
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
2038
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
2039
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
2040
|
+
});
|
|
1873
2041
|
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 += `
|
|
2042
|
+
<table>
|
|
2043
|
+
<thead>
|
|
2044
|
+
<tr>
|
|
2045
|
+
<th>Result</th>
|
|
2046
|
+
<th>Comments</th>
|
|
2047
|
+
<th>Date</th>
|
|
2048
|
+
</tr>
|
|
2049
|
+
</thead>
|
|
2050
|
+
<tbody>`;
|
|
2051
|
+
for (const resource2 of observations) {
|
|
2052
|
+
const obs = resource2;
|
|
2053
|
+
html += `
|
|
1892
2054
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
1893
|
-
<td>${templateUtilities.
|
|
1894
|
-
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2055
|
+
<td>${templateUtilities.extractPregnancyStatus(obs)}</td>
|
|
1895
2056
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
1896
|
-
<td>${templateUtilities.
|
|
2057
|
+
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
1897
2058
|
</tr>`;
|
|
1898
|
-
}
|
|
1899
2059
|
}
|
|
1900
2060
|
html += `
|
|
1901
2061
|
</tbody>
|
|
@@ -1913,6 +2073,13 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1913
2073
|
* @returns HTML string for rendering
|
|
1914
2074
|
*/
|
|
1915
2075
|
generateNarrative(resource, timezone) {
|
|
2076
|
+
if (resource.entry && Array.isArray(resource.entry)) {
|
|
2077
|
+
resource.entry.sort((a, b) => {
|
|
2078
|
+
const dateA = new Date(a.resource.dateTime || 0);
|
|
2079
|
+
const dateB = new Date(b.resource.dateTime || 0);
|
|
2080
|
+
return dateB.getTime() - dateA.getTime();
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
1916
2083
|
return _AdvanceDirectivesTemplate.generateStaticNarrative(resource, timezone);
|
|
1917
2084
|
}
|
|
1918
2085
|
/**
|
|
@@ -1925,7 +2092,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1925
2092
|
static generateStaticNarrative(resource, timezone) {
|
|
1926
2093
|
const templateUtilities = new TemplateUtilities(resource);
|
|
1927
2094
|
let html = `
|
|
1928
|
-
<h5>Advance Directives</h5>
|
|
1929
2095
|
<table>
|
|
1930
2096
|
<thead>
|
|
1931
2097
|
<tr>
|
|
@@ -1939,9 +2105,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1939
2105
|
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1940
2106
|
for (const entry of resource.entry) {
|
|
1941
2107
|
const consent = entry.resource;
|
|
1942
|
-
if (consent.resourceType === "Composition") {
|
|
1943
|
-
continue;
|
|
1944
|
-
}
|
|
1945
2108
|
html += `
|
|
1946
2109
|
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
1947
2110
|
<td>${templateUtilities.codeableConcept(consent.scope, "display")}</td>
|
|
@@ -1958,171 +2121,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
1958
2121
|
}
|
|
1959
2122
|
};
|
|
1960
2123
|
|
|
1961
|
-
// src/narratives/templates/typescript/FamilyHistoryTemplate.ts
|
|
1962
|
-
var FamilyHistoryTemplate = class {
|
|
1963
|
-
/**
|
|
1964
|
-
* Generate HTML narrative for Family History
|
|
1965
|
-
* @param resource - FHIR Bundle containing FamilyMemberHistory resources
|
|
1966
|
-
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1967
|
-
* @returns HTML string for rendering
|
|
1968
|
-
*/
|
|
1969
|
-
generateNarrative(resource, timezone) {
|
|
1970
|
-
const templateUtilities = new TemplateUtilities(resource);
|
|
1971
|
-
let html = `
|
|
1972
|
-
<h5>Family History</h5>
|
|
1973
|
-
<table>
|
|
1974
|
-
<thead>
|
|
1975
|
-
<tr>
|
|
1976
|
-
<th>Relationship</th>
|
|
1977
|
-
<th>Condition</th>
|
|
1978
|
-
<th>Status</th>
|
|
1979
|
-
<th>Onset</th>
|
|
1980
|
-
<th>Notes</th>
|
|
1981
|
-
</tr>
|
|
1982
|
-
</thead>
|
|
1983
|
-
<tbody>`;
|
|
1984
|
-
if (resource.entry && Array.isArray(resource.entry)) {
|
|
1985
|
-
let hasFamilyHistory = false;
|
|
1986
|
-
for (const entry of resource.entry) {
|
|
1987
|
-
const familyHistory = entry.resource;
|
|
1988
|
-
if (!familyHistory || familyHistory.resourceType !== "FamilyMemberHistory") {
|
|
1989
|
-
continue;
|
|
1990
|
-
}
|
|
1991
|
-
hasFamilyHistory = true;
|
|
1992
|
-
const fmh = familyHistory;
|
|
1993
|
-
const relationship = templateUtilities.codeableConcept(fmh.relationship, "display");
|
|
1994
|
-
if (fmh.condition && Array.isArray(fmh.condition)) {
|
|
1995
|
-
for (const condition of fmh.condition) {
|
|
1996
|
-
const conditionCode = templateUtilities.codeableConcept(condition.code, "display");
|
|
1997
|
-
const status = fmh.status || "";
|
|
1998
|
-
let onset = "";
|
|
1999
|
-
if (condition.onsetAge) {
|
|
2000
|
-
onset = templateUtilities.renderOnset(condition.onsetAge, timezone);
|
|
2001
|
-
}
|
|
2002
|
-
const notes = condition.note ? templateUtilities.renderNotes(condition.note, timezone) : templateUtilities.renderNotes(fmh.note, timezone);
|
|
2003
|
-
html += `
|
|
2004
|
-
<tr id="${templateUtilities.narrativeLinkId(fmh)}">
|
|
2005
|
-
<td>${relationship}</td>
|
|
2006
|
-
<td>${conditionCode}</td>
|
|
2007
|
-
<td>${status}</td>
|
|
2008
|
-
<td>${onset}</td>
|
|
2009
|
-
<td>${notes}</td>
|
|
2010
|
-
</tr>`;
|
|
2011
|
-
}
|
|
2012
|
-
} else {
|
|
2013
|
-
html += `
|
|
2014
|
-
<tr id="${templateUtilities.narrativeLinkId(fmh)}">
|
|
2015
|
-
<td>${relationship}</td>
|
|
2016
|
-
<td>Not specified</td>
|
|
2017
|
-
<td>${fmh.status || ""}</td>
|
|
2018
|
-
<td></td>
|
|
2019
|
-
<td>${templateUtilities.renderNotes(fmh.note, timezone)}</td>
|
|
2020
|
-
</tr>`;
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
if (!hasFamilyHistory) {
|
|
2024
|
-
html += `
|
|
2025
|
-
<tr>
|
|
2026
|
-
<td colspan="5">No family history recorded</td>
|
|
2027
|
-
</tr>`;
|
|
2028
|
-
}
|
|
2029
|
-
} else {
|
|
2030
|
-
html += `
|
|
2031
|
-
<tr>
|
|
2032
|
-
<td colspan="5">No family history recorded</td>
|
|
2033
|
-
</tr>`;
|
|
2034
|
-
}
|
|
2035
|
-
html += `
|
|
2036
|
-
</tbody>
|
|
2037
|
-
</table>`;
|
|
2038
|
-
return html;
|
|
2039
|
-
}
|
|
2040
|
-
};
|
|
2041
|
-
|
|
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
2124
|
// src/narratives/templates/typescript/TypeScriptTemplateMapper.ts
|
|
2127
2125
|
var TypeScriptTemplateMapper = class {
|
|
2128
2126
|
/**
|
|
@@ -2145,20 +2143,18 @@ var TypeScriptTemplateMapper = class {
|
|
|
2145
2143
|
TypeScriptTemplateMapper.sectionToTemplate = {
|
|
2146
2144
|
["Patient" /* PATIENT */]: new PatientTemplate(),
|
|
2147
2145
|
["AllergyIntoleranceSection" /* ALLERGIES */]: new AllergyIntoleranceTemplate(),
|
|
2148
|
-
["
|
|
2146
|
+
["MedicationSummarySection" /* MEDICATIONS */]: new MedicationSummaryTemplate(),
|
|
2149
2147
|
["ImmunizationSection" /* IMMUNIZATIONS */]: new ImmunizationsTemplate(),
|
|
2150
2148
|
["ProblemSection" /* PROBLEMS */]: new ProblemListTemplate(),
|
|
2151
2149
|
["VitalSignsSection" /* VITAL_SIGNS */]: new VitalSignsTemplate(),
|
|
2152
2150
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: new MedicalDevicesTemplate(),
|
|
2153
|
-
["
|
|
2154
|
-
["
|
|
2155
|
-
["FamilyHistorySection" /* FAMILY_HISTORY */]: new FamilyHistoryTemplate(),
|
|
2151
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
|
|
2152
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
|
|
2156
2153
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: new SocialHistoryTemplate(),
|
|
2157
|
-
["
|
|
2154
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
|
|
2158
2155
|
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: new FunctionalStatusTemplate(),
|
|
2159
|
-
["
|
|
2160
|
-
["
|
|
2161
|
-
["ClinicalImpressionSection" /* CLINICAL_IMPRESSION */]: new ClinicalImpressionTemplate(),
|
|
2156
|
+
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: new PastHistoryOfIllnessTemplate(),
|
|
2157
|
+
["PlanOfCareSection" /* CARE_PLAN */]: new PlanOfCareTemplate(),
|
|
2162
2158
|
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: new AdvanceDirectivesTemplate()
|
|
2163
2159
|
};
|
|
2164
2160
|
|
|
@@ -2195,10 +2191,9 @@ var NarrativeGenerator = class {
|
|
|
2195
2191
|
* @param section - IPS section type
|
|
2196
2192
|
* @param resources - Array of domain resources
|
|
2197
2193
|
* @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
2194
|
* @returns Generated HTML content or undefined if no resources
|
|
2200
2195
|
*/
|
|
2201
|
-
static async generateNarrativeContentAsync(section, resources, timezone
|
|
2196
|
+
static async generateNarrativeContentAsync(section, resources, timezone) {
|
|
2202
2197
|
if (!resources || resources.length === 0) {
|
|
2203
2198
|
return void 0;
|
|
2204
2199
|
}
|
|
@@ -2214,11 +2209,7 @@ var NarrativeGenerator = class {
|
|
|
2214
2209
|
if (!content) {
|
|
2215
2210
|
return void 0;
|
|
2216
2211
|
}
|
|
2217
|
-
|
|
2218
|
-
return await this.wrapInXhtmlAsync(content);
|
|
2219
|
-
} else {
|
|
2220
|
-
return await this.minifyHtmlAsync(content);
|
|
2221
|
-
}
|
|
2212
|
+
return content;
|
|
2222
2213
|
} catch (error) {
|
|
2223
2214
|
console.error(`Error generating narrative for section ${section}:`, error);
|
|
2224
2215
|
return `<div class="error">Error generating narrative: ${error instanceof Error ? error.message : String(error)}</div>`;
|
|
@@ -2265,35 +2256,35 @@ var NarrativeGenerator = class {
|
|
|
2265
2256
|
* @param resources - Array of domain resources
|
|
2266
2257
|
* @param timezone - Optional timezone to use for date formatting
|
|
2267
2258
|
* @param minify - Whether to minify the HTML content (default: true)
|
|
2268
|
-
* @param wrapInXhtml - Whether to wrap the content in XHTML div
|
|
2269
2259
|
* @returns Promise that resolves to a FHIR Narrative object or undefined if no resources
|
|
2270
2260
|
*/
|
|
2271
|
-
static async generateNarrativeAsync(section, resources, timezone, minify = true
|
|
2272
|
-
const content = await this.generateNarrativeContentAsync(section, resources, timezone
|
|
2261
|
+
static async generateNarrativeAsync(section, resources, timezone, minify = true) {
|
|
2262
|
+
const content = await this.generateNarrativeContentAsync(section, resources, timezone);
|
|
2273
2263
|
if (!content) {
|
|
2274
2264
|
return void 0;
|
|
2275
2265
|
}
|
|
2276
2266
|
return await this.createNarrativeAsync(content, minify);
|
|
2277
2267
|
}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2268
|
+
};
|
|
2269
|
+
|
|
2270
|
+
// src/structures/ips_mandatory_sections.ts
|
|
2271
|
+
var IPSMandatorySections = ((IPSMandatorySections2) => {
|
|
2272
|
+
IPSMandatorySections2[IPSMandatorySections2["PATIENT"] = "Patient" /* PATIENT */] = "PATIENT";
|
|
2273
|
+
IPSMandatorySections2[IPSMandatorySections2["PROBLEMS"] = "ProblemSection" /* PROBLEMS */] = "PROBLEMS";
|
|
2274
|
+
IPSMandatorySections2[IPSMandatorySections2["ALLERGIES"] = "AllergyIntoleranceSection" /* ALLERGIES */] = "ALLERGIES";
|
|
2275
|
+
IPSMandatorySections2[IPSMandatorySections2["MEDICATIONS"] = "MedicationSummarySection" /* MEDICATIONS */] = "MEDICATIONS";
|
|
2276
|
+
return IPSMandatorySections2;
|
|
2277
|
+
})(IPSMandatorySections || {});
|
|
2278
|
+
var IPSMissingMandatorySectionContent = {
|
|
2279
|
+
["ProblemSection" /* PROBLEMS */]: "There is no information available about the subject's health problems or disabilities.",
|
|
2280
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: "There is no information available regarding the subject's allergy conditions.",
|
|
2281
|
+
["MedicationSummarySection" /* MEDICATIONS */]: "There is no information available about the subject's medication use or administration."
|
|
2290
2282
|
};
|
|
2291
2283
|
|
|
2292
2284
|
// src/generators/fhir_summary_generator.ts
|
|
2293
2285
|
var ComprehensiveIPSCompositionBuilder = class {
|
|
2294
2286
|
constructor() {
|
|
2295
2287
|
this.sections = [];
|
|
2296
|
-
this.mandatorySectionsAdded = /* @__PURE__ */ new Set();
|
|
2297
2288
|
this.resources = /* @__PURE__ */ new Set();
|
|
2298
2289
|
}
|
|
2299
2290
|
/**
|
|
@@ -2311,35 +2302,35 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2311
2302
|
/**
|
|
2312
2303
|
* Adds a section to the composition with async HTML minification
|
|
2313
2304
|
* @param sectionType - IPS section type
|
|
2314
|
-
* @param
|
|
2305
|
+
* @param validResources - Array of domain resources
|
|
2315
2306
|
* @param timezone - Optional timezone to use for date formatting
|
|
2316
|
-
* @param options - Optional configuration options
|
|
2317
2307
|
*/
|
|
2318
|
-
async addSectionAsync(sectionType,
|
|
2319
|
-
const validResources = resources;
|
|
2308
|
+
async addSectionAsync(sectionType, validResources, timezone) {
|
|
2320
2309
|
for (const resource of validResources) {
|
|
2321
2310
|
this.resources.add(resource);
|
|
2322
2311
|
}
|
|
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
2312
|
if (sectionType !== "Patient" /* PATIENT */) {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2313
|
+
let narrative = void 0;
|
|
2314
|
+
if (validResources.length > 0) {
|
|
2315
|
+
narrative = await NarrativeGenerator.generateNarrativeAsync(
|
|
2316
|
+
sectionType,
|
|
2317
|
+
validResources,
|
|
2318
|
+
timezone,
|
|
2319
|
+
true
|
|
2320
|
+
);
|
|
2321
|
+
} else if (sectionType in IPSMandatorySections) {
|
|
2322
|
+
narrative = await NarrativeGenerator.createNarrativeAsync(
|
|
2323
|
+
IPSMissingMandatorySectionContent[sectionType]
|
|
2324
|
+
);
|
|
2325
|
+
} else {
|
|
2326
|
+
return this;
|
|
2327
|
+
}
|
|
2337
2328
|
const sectionEntry = {
|
|
2338
2329
|
title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
|
|
2339
2330
|
code: {
|
|
2340
2331
|
coding: [{
|
|
2341
2332
|
system: "http://loinc.org",
|
|
2342
|
-
code:
|
|
2333
|
+
code: IPS_SECTION_LOINC_CODES[sectionType],
|
|
2343
2334
|
display: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
2344
2335
|
}],
|
|
2345
2336
|
text: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType
|
|
@@ -2350,9 +2341,6 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2350
2341
|
display: resource.resourceType
|
|
2351
2342
|
}))
|
|
2352
2343
|
};
|
|
2353
|
-
if (!options?.isOptional) {
|
|
2354
|
-
this.mandatorySectionsAdded.add(sectionType);
|
|
2355
|
-
}
|
|
2356
2344
|
this.sections.push(sectionEntry);
|
|
2357
2345
|
}
|
|
2358
2346
|
return this;
|
|
@@ -2371,43 +2359,20 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2371
2359
|
throw new Error("Patient resource not found in the bundle");
|
|
2372
2360
|
}
|
|
2373
2361
|
this.patient = patientEntry.resource;
|
|
2362
|
+
const resources = bundle.entry.map((e) => e.resource);
|
|
2374
2363
|
for (const sectionType of Object.values(IPSSections)) {
|
|
2375
2364
|
const resourceTypesForSection = IPSSectionResourceHelper.getResourceTypesForSection(sectionType);
|
|
2376
2365
|
const customFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
|
|
2377
|
-
let
|
|
2366
|
+
let sectionResources = resources.filter(
|
|
2367
|
+
(r) => r && typeof r.resourceType === "string" && resourceTypesForSection.includes(r.resourceType)
|
|
2368
|
+
);
|
|
2378
2369
|
if (customFilter) {
|
|
2379
|
-
|
|
2380
|
-
}
|
|
2381
|
-
if (resources.length > 0) {
|
|
2382
|
-
await this.addSectionAsync(sectionType, resources, timezone, {
|
|
2383
|
-
isOptional: true
|
|
2384
|
-
});
|
|
2370
|
+
sectionResources = sectionResources.filter((resource) => resource && customFilter(resource));
|
|
2385
2371
|
}
|
|
2372
|
+
await this.addSectionAsync(sectionType, sectionResources, timezone);
|
|
2386
2373
|
}
|
|
2387
2374
|
return this;
|
|
2388
2375
|
}
|
|
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
2376
|
/**
|
|
2412
2377
|
* Builds a complete FHIR Bundle containing the Composition and all resources.
|
|
2413
2378
|
* @param authorOrganizationId - ID of the authoring organization (e.g., hospital or clinic)
|
|
@@ -2444,7 +2409,12 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2444
2409
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2445
2410
|
title: "International Patient Summary",
|
|
2446
2411
|
section: this.sections,
|
|
2447
|
-
text: await
|
|
2412
|
+
text: await NarrativeGenerator.generateNarrativeAsync(
|
|
2413
|
+
"Patient" /* PATIENT */,
|
|
2414
|
+
[this.patient],
|
|
2415
|
+
timezone,
|
|
2416
|
+
true
|
|
2417
|
+
)
|
|
2448
2418
|
};
|
|
2449
2419
|
const bundle = {
|
|
2450
2420
|
resourceType: "Bundle",
|
|
@@ -2485,41 +2455,11 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
2485
2455
|
return bundle;
|
|
2486
2456
|
}
|
|
2487
2457
|
/**
|
|
2488
|
-
*
|
|
2489
|
-
* @
|
|
2490
|
-
* @private
|
|
2458
|
+
* Returns the Composition sections without creating a full bundle.
|
|
2459
|
+
* @returns Array of TCompositionSection
|
|
2491
2460
|
*/
|
|
2492
|
-
|
|
2493
|
-
|
|
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
|
-
};
|
|
2461
|
+
getSections() {
|
|
2462
|
+
return this.sections;
|
|
2523
2463
|
}
|
|
2524
2464
|
};
|
|
2525
2465
|
|