@imranq2/fhirpatientsummary 1.0.28 → 1.0.30
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 +761 -267
- package/dist/index.js +761 -267
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -71,6 +71,13 @@ var PREGNANCY_LOINC_CODES = {
|
|
|
71
71
|
"33065-4": "Ectopic Pregnancy"
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
|
+
var PREGNANCY_SNOMED_CODES = [
|
|
75
|
+
// Add SNOMED CT codes for pregnancy history here, e.g.:
|
|
76
|
+
"72892002",
|
|
77
|
+
// Pregnancy (finding)
|
|
78
|
+
"77386006"
|
|
79
|
+
// History of pregnancy (situation)
|
|
80
|
+
];
|
|
74
81
|
var SOCIAL_HISTORY_LOINC_CODES = {
|
|
75
82
|
"72166-2": "Tobacco Use",
|
|
76
83
|
"74013-4": "Alcohol Use"
|
|
@@ -80,6 +87,29 @@ var BLOOD_PRESSURE_LOINC_CODES = {
|
|
|
80
87
|
SYSTOLIC: "8480-6",
|
|
81
88
|
DIASTOLIC: "8462-4"
|
|
82
89
|
};
|
|
90
|
+
var ESSENTIAL_LAB_PANELS = {
|
|
91
|
+
// Top 20 Most Ordered Panels
|
|
92
|
+
"24323-8": "Comprehensive Metabolic Panel (CMP)",
|
|
93
|
+
"24320-4": "Basic Metabolic Panel (BMP)",
|
|
94
|
+
"58410-2": "Complete Blood Count (CBC)",
|
|
95
|
+
"57021-8": "CBC with Differential",
|
|
96
|
+
"24331-1": "Lipid Panel",
|
|
97
|
+
"57698-3": "Lipid Panel with Direct LDL",
|
|
98
|
+
"24325-3": "Hepatic Function Panel",
|
|
99
|
+
"24362-6": "Renal Function Panel",
|
|
100
|
+
"24326-1": "Electrolyte Panel",
|
|
101
|
+
"24348-5": "Thyroid Panel",
|
|
102
|
+
"24356-8": "Urinalysis Complete",
|
|
103
|
+
"24352-7": "Iron Studies Panel",
|
|
104
|
+
"34714-6": "Coagulation Panel",
|
|
105
|
+
"24364-2": "Prenatal Panel",
|
|
106
|
+
"24108-3": "Acute Hepatitis Panel",
|
|
107
|
+
"24110-9": "Hepatitis B Panel",
|
|
108
|
+
"34574-4": "Arthritis Panel",
|
|
109
|
+
"24360-0": "Anemia Panel",
|
|
110
|
+
"80235-8": "Cardiac Markers Panel",
|
|
111
|
+
"69738-3": "CBC with Auto Differential"
|
|
112
|
+
};
|
|
83
113
|
|
|
84
114
|
// src/structures/ips_section_constants.ts
|
|
85
115
|
var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
|
|
@@ -104,34 +134,42 @@ var IPSSectionResourceFilters = {
|
|
|
104
134
|
// Only include completed immunizations
|
|
105
135
|
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
106
136
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
107
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c
|
|
137
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => codingMatches(c, "vital-signs", c.system))),
|
|
108
138
|
// Includes DeviceUseStatement. Device is needed for linked device details
|
|
109
139
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
|
|
110
|
-
// Only include finalized diagnostic reports
|
|
111
|
-
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) =>
|
|
140
|
+
// Only include finalized diagnostic reports and relevant observations
|
|
141
|
+
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "DiagnosticReport" && resource.status === "final" || resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => codingMatches(c, RESULT_SUMMARY_OBSERVATION_CATEGORIES, c.system))),
|
|
112
142
|
// Only include completed procedures
|
|
113
143
|
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
|
|
114
144
|
// Only include social history Observations
|
|
115
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code
|
|
116
|
-
// Only include pregnancy history Observations
|
|
117
|
-
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code
|
|
118
|
-
// Only include
|
|
119
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "
|
|
145
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && codeableConceptMatches(resource.code, Object.keys(SOCIAL_HISTORY_LOINC_CODES), "http://loinc.org"),
|
|
146
|
+
// Only include pregnancy history Observations or relevant Conditions
|
|
147
|
+
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS), "http://loinc.org") || codeableConceptMatches(resource.valueCodeableConcept, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME), "http://loinc.org") || codingMatches(resource.code?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct") || codingMatches(resource.valueCodeableConcept?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct")) || resource.resourceType === "Condition" && (codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS), "http://loinc.org") || codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME), "http://loinc.org") || codingMatches(resource.code?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct")),
|
|
148
|
+
// Only include Observations with LOINC 47420-5, category 'functional-status', or category display containing 'functional', and completed ClinicalImpressions
|
|
149
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Observation" && (codeableConceptMatches(resource.code, "47420-5", "http://loinc.org") || resource.category?.some(
|
|
150
|
+
(cat) => cat.coding?.some(
|
|
151
|
+
(c) => c.code === "functional-status" && c.system === "http://terminology.hl7.org/CodeSystem/observation-category" || typeof c.display === "string" && c.display.toLowerCase().includes("functional")
|
|
152
|
+
)
|
|
153
|
+
)) || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
|
|
120
154
|
// Only include resolved medical history Conditions
|
|
121
155
|
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
|
|
122
156
|
// Only include active care plans
|
|
123
157
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
124
158
|
// Only include active advance directives (Consent resources)
|
|
125
|
-
|
|
159
|
+
// TODO: disable this until we right logic to get these
|
|
160
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: () => false
|
|
126
161
|
};
|
|
127
162
|
var IPSSectionSummaryCompositionFilter = {
|
|
128
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
129
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
130
|
-
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
131
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
132
|
-
["MedicationSummarySection" /* MEDICATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
163
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "allergy_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
164
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "vital_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
165
|
+
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "careplan_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
166
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "immunization_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
167
|
+
["MedicationSummarySection" /* MEDICATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "medication_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
133
168
|
// [IPSSections.DIAGNOSTIC_REPORTS]: (resource) => resource.resourceType === 'Composition' && resource.type?.coding?.some((c: any) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && ["lab_summary_document", "diagnosticreportlab_summary_document"].includes(c.code)),
|
|
134
|
-
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
169
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "procedure_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
|
|
170
|
+
};
|
|
171
|
+
var IPSSectionSummaryIPSCompositionFilter = {
|
|
172
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "ips_vital_summary_document")
|
|
135
173
|
};
|
|
136
174
|
var IPSSectionResourceHelper = class {
|
|
137
175
|
static getResourceFilterForSection(section) {
|
|
@@ -145,10 +183,50 @@ var IPSSectionResourceHelper = class {
|
|
|
145
183
|
static getSummaryCompositionFilterForSection(section) {
|
|
146
184
|
return IPSSectionSummaryCompositionFilter[section];
|
|
147
185
|
}
|
|
186
|
+
static getSummaryIPSCompositionFilterForSection(section) {
|
|
187
|
+
return IPSSectionSummaryIPSCompositionFilter[section];
|
|
188
|
+
}
|
|
148
189
|
};
|
|
190
|
+
function codingMatches(coding, code, system) {
|
|
191
|
+
if (!coding || !coding.system) return false;
|
|
192
|
+
if (Array.isArray(code)) {
|
|
193
|
+
return coding.system === system && code.includes(coding.code ?? "");
|
|
194
|
+
}
|
|
195
|
+
return coding.system === system && coding.code === code;
|
|
196
|
+
}
|
|
197
|
+
function codeableConceptMatches(codeableConcept, code, system) {
|
|
198
|
+
if (!codeableConcept || !Array.isArray(codeableConcept.coding)) return false;
|
|
199
|
+
return codeableConcept.coding.some((coding) => codingMatches(coding, code, system));
|
|
200
|
+
}
|
|
149
201
|
|
|
150
202
|
// src/narratives/templates/typescript/TemplateUtilities.ts
|
|
151
203
|
import { DateTime } from "luxon";
|
|
204
|
+
|
|
205
|
+
// src/structures/codingSystemDisplayNames.ts
|
|
206
|
+
var CODING_SYSTEM_DISPLAY_NAMES = {
|
|
207
|
+
"http://snomed.info/sct": "SNOMED CT",
|
|
208
|
+
"http://loinc.org": "LOINC",
|
|
209
|
+
"http://hl7.org/fhir/sid/icd-10": "ICD-10",
|
|
210
|
+
"http://hl7.org/fhir/sid/icd-10-cm": "ICD-10-CM",
|
|
211
|
+
"http://hl7.org/fhir/sid/icd-9": "ICD-9",
|
|
212
|
+
"http://hl7.org/fhir/sid/cvx": "CVX",
|
|
213
|
+
"http://www.nlm.nih.gov/research/umls/rxnorm": "RxNorm",
|
|
214
|
+
"http://www.ama-assn.org/go/cpt": "CPT",
|
|
215
|
+
"http://unitsofmeasure.org": "UCUM",
|
|
216
|
+
"http://e-imo.com/products/problem-it": "IMO Problem IT",
|
|
217
|
+
"2.16.840.1.113883.6.285": "HCPCS Level II",
|
|
218
|
+
"https://fhir.cerner.com/4ff3b259-e48d-4066-8b35-a6a051f2802a/codeSet/72": "Cerner Code Set 72",
|
|
219
|
+
"http://hl7.org/fhir/sid/ndc": "NDC",
|
|
220
|
+
// Added mapping for NDC
|
|
221
|
+
"urn:oid:2.16.840.1.113883.12.292": "CVX",
|
|
222
|
+
"http://terminology.hl7.org/CodeSystem/data-absent-reason": "Data Absent Reason",
|
|
223
|
+
// Added mapping for Data Absent Reason
|
|
224
|
+
"2.16.840.1.113883.6.208": "NDDF"
|
|
225
|
+
// Add more as needed
|
|
226
|
+
};
|
|
227
|
+
var codingSystemDisplayNames_default = CODING_SYSTEM_DISPLAY_NAMES;
|
|
228
|
+
|
|
229
|
+
// src/narratives/templates/typescript/TemplateUtilities.ts
|
|
152
230
|
var TemplateUtilities = class {
|
|
153
231
|
/**
|
|
154
232
|
* Constructor to initialize the TemplateUtilities with a FHIR resources
|
|
@@ -158,47 +236,63 @@ var TemplateUtilities = class {
|
|
|
158
236
|
this.resources = resources;
|
|
159
237
|
}
|
|
160
238
|
/**
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* @
|
|
239
|
+
* Returns the preferred coding from a list of codings.
|
|
240
|
+
* If a coding has an extension with url 'https://fhir.icanbwell.com/4_0_0/StructureDefinition/intelligence' and valueCode 'preferred', returns that coding.
|
|
241
|
+
* Otherwise, returns the first coding if it exists, else null.
|
|
242
|
+
* @param codings Array of coding objects
|
|
243
|
+
* @returns The preferred coding object or null
|
|
165
244
|
*/
|
|
166
|
-
|
|
167
|
-
if (!
|
|
168
|
-
|
|
245
|
+
getPreferredCoding(codings) {
|
|
246
|
+
if (!Array.isArray(codings) || codings.length === 0) return null;
|
|
247
|
+
for (const coding of codings) {
|
|
248
|
+
if (Array.isArray(coding.extension)) {
|
|
249
|
+
for (const ext of coding.extension) {
|
|
250
|
+
if (ext.url === "https://fhir.icanbwell.com/4_0_0/StructureDefinition/intelligence" && ext.valueCode === "preferred") {
|
|
251
|
+
return coding;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
169
255
|
}
|
|
256
|
+
return codings[0] || null;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Returns the display value from a CodeableConcept
|
|
260
|
+
* @param cc - The CodeableConcept object
|
|
261
|
+
* @param field - Optional specific field to extract (e.g., 'text', 'display', 'code')
|
|
262
|
+
* @returns Display string or empty string
|
|
263
|
+
*/
|
|
264
|
+
codeableConceptDisplay(cc, field) {
|
|
265
|
+
if (!cc) return "";
|
|
170
266
|
if (field) {
|
|
171
267
|
if (cc[field]) {
|
|
172
268
|
return cc[field];
|
|
173
|
-
} else if (cc.coding && cc.coding
|
|
174
|
-
|
|
269
|
+
} else if (cc.coding && cc.coding.length > 0) {
|
|
270
|
+
const preferredCoding = this.getPreferredCoding(cc.coding);
|
|
271
|
+
if (preferredCoding && preferredCoding[field]) {
|
|
272
|
+
return preferredCoding[field];
|
|
273
|
+
}
|
|
175
274
|
}
|
|
176
275
|
}
|
|
177
|
-
if (cc.text)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
return cc.coding[0].display;
|
|
182
|
-
} else if (cc.coding[0].code) {
|
|
183
|
-
return cc.coding[0].code;
|
|
184
|
-
}
|
|
276
|
+
if (cc.text) return cc.text;
|
|
277
|
+
if (cc.coding && cc.coding.length > 0) {
|
|
278
|
+
const preferredCoding = this.getPreferredCoding(cc.coding);
|
|
279
|
+
if (preferredCoding && preferredCoding.display) return preferredCoding.display;
|
|
185
280
|
}
|
|
186
281
|
return "";
|
|
187
282
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
});
|
|
201
|
-
return resource ? resource : null;
|
|
283
|
+
/**
|
|
284
|
+
* Returns the code and system from a CodeableConcept
|
|
285
|
+
* @param cc - The CodeableConcept object
|
|
286
|
+
* @returns Object with code and system, or empty strings if not present
|
|
287
|
+
*/
|
|
288
|
+
codeableConceptCoding(cc) {
|
|
289
|
+
if (!cc || !cc.coding || !cc.coding.length) return "";
|
|
290
|
+
const preferredCoding = this.getPreferredCoding(cc.coding);
|
|
291
|
+
if (!preferredCoding) return "";
|
|
292
|
+
const code = preferredCoding.code || "";
|
|
293
|
+
const system = preferredCoding.system || "";
|
|
294
|
+
const systemDisplay = codingSystemDisplayNames_default[system] || system;
|
|
295
|
+
return code ? `${code} (${systemDisplay})` : "";
|
|
202
296
|
}
|
|
203
297
|
/**
|
|
204
298
|
* Renders a Device reference
|
|
@@ -246,7 +340,7 @@ var TemplateUtilities = class {
|
|
|
246
340
|
return "";
|
|
247
341
|
}
|
|
248
342
|
if (medicationType.medicationCodeableConcept) {
|
|
249
|
-
return this.
|
|
343
|
+
return this.codeableConceptDisplay(medicationType.medicationCodeableConcept);
|
|
250
344
|
} else if (medicationType.medicationReference) {
|
|
251
345
|
return this.renderMedicationRef(medicationType.medicationReference);
|
|
252
346
|
}
|
|
@@ -271,7 +365,7 @@ var TemplateUtilities = class {
|
|
|
271
365
|
*/
|
|
272
366
|
renderMedicationCode(medication) {
|
|
273
367
|
if (medication && medication.code) {
|
|
274
|
-
return this.renderTextAsHtml(this.
|
|
368
|
+
return this.renderTextAsHtml(this.codeableConceptDisplay(medication.code));
|
|
275
369
|
}
|
|
276
370
|
return "";
|
|
277
371
|
}
|
|
@@ -440,7 +534,7 @@ var TemplateUtilities = class {
|
|
|
440
534
|
*/
|
|
441
535
|
firstFromCodeableConceptList(list) {
|
|
442
536
|
if (list && Array.isArray(list) && list[0]) {
|
|
443
|
-
return this.renderTextAsHtml(this.
|
|
537
|
+
return this.renderTextAsHtml(this.codeableConceptDisplay(list[0]));
|
|
444
538
|
}
|
|
445
539
|
return "";
|
|
446
540
|
}
|
|
@@ -780,16 +874,28 @@ var TemplateUtilities = class {
|
|
|
780
874
|
return this.renderMedicationCode(medicationSource);
|
|
781
875
|
}
|
|
782
876
|
if (typeof medicationSource === "object" && ("coding" in medicationSource || "text" in medicationSource)) {
|
|
783
|
-
return this.
|
|
877
|
+
return this.codeableConceptDisplay(medicationSource);
|
|
784
878
|
}
|
|
785
879
|
if (typeof medicationSource === "object" && "reference" in medicationSource) {
|
|
786
880
|
const medication = this.resolveReference(medicationSource);
|
|
787
881
|
if (medication && medication.code) {
|
|
788
|
-
return this.
|
|
882
|
+
return this.codeableConceptDisplay(medication.code);
|
|
789
883
|
}
|
|
790
884
|
}
|
|
791
885
|
return "";
|
|
792
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Returns the owner tag from the resource meta.security array.
|
|
889
|
+
* @param resource - FHIR resource with meta tag
|
|
890
|
+
* @returns The owner code if found, otherwise undefined
|
|
891
|
+
*/
|
|
892
|
+
getOwnerTag(resource) {
|
|
893
|
+
if (!resource?.meta?.security) return "";
|
|
894
|
+
const ownerEntry = resource.meta.security.find(
|
|
895
|
+
(sec) => sec.system === "https://www.icanbwell.com/owner" && !!sec.code
|
|
896
|
+
);
|
|
897
|
+
return ownerEntry?.code;
|
|
898
|
+
}
|
|
793
899
|
/**
|
|
794
900
|
* Public method to render plain text as HTML, escaping special characters and replacing newlines with <br />.
|
|
795
901
|
* This method should be used whenever displaying user-supplied or FHIR resource text in HTML to prevent XSS vulnerabilities
|
|
@@ -953,6 +1059,21 @@ var TemplateUtilities = class {
|
|
|
953
1059
|
const denominatorUnit = valueRatio.denominator.unit ? ` ${valueRatio.denominator.unit}` : "";
|
|
954
1060
|
return `${numerator}${numeratorUnit} / ${denominator}${denominatorUnit}`;
|
|
955
1061
|
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Finds the resource that matches the reference
|
|
1064
|
+
* @param ref - Reference to a resource
|
|
1065
|
+
* @returns The resource or null
|
|
1066
|
+
*/
|
|
1067
|
+
resolveReference(ref) {
|
|
1068
|
+
if (!ref || !this.resources) {
|
|
1069
|
+
return null;
|
|
1070
|
+
}
|
|
1071
|
+
const refId = ref.reference?.split("/")[1];
|
|
1072
|
+
const refType = ref.reference?.split("/")[0];
|
|
1073
|
+
return this.resources.find(
|
|
1074
|
+
(resource) => resource.resourceType === refType && resource.id === refId
|
|
1075
|
+
) || null;
|
|
1076
|
+
}
|
|
956
1077
|
};
|
|
957
1078
|
|
|
958
1079
|
// src/constants.ts
|
|
@@ -1581,7 +1702,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
1581
1702
|
const uniqueLanguages = /* @__PURE__ */ new Set();
|
|
1582
1703
|
const preferredLanguages = /* @__PURE__ */ new Set();
|
|
1583
1704
|
patient.communication.forEach((comm) => {
|
|
1584
|
-
const language = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1705
|
+
const language = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(comm.language));
|
|
1585
1706
|
if (language) {
|
|
1586
1707
|
if (comm.preferred) {
|
|
1587
1708
|
preferredLanguages.add(language);
|
|
@@ -1628,14 +1749,18 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1628
1749
|
<thead>
|
|
1629
1750
|
<tr>
|
|
1630
1751
|
<th>Allergen</th>
|
|
1752
|
+
<th>Code (System)</th>
|
|
1631
1753
|
<th>Criticality</th>
|
|
1632
1754
|
<th>Recorded Date</th>
|
|
1755
|
+
<th>Source</th>
|
|
1633
1756
|
</tr>
|
|
1634
1757
|
</thead>
|
|
1635
1758
|
<tbody>`;
|
|
1636
1759
|
for (const resourceItem of resources) {
|
|
1637
1760
|
for (const rowData of resourceItem.section ?? []) {
|
|
1761
|
+
const sectionCodeableConcept = rowData.code;
|
|
1638
1762
|
const data = {};
|
|
1763
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
1639
1764
|
for (const columnData of rowData.section ?? []) {
|
|
1640
1765
|
switch (columnData.title) {
|
|
1641
1766
|
case "Allergen Name":
|
|
@@ -1647,6 +1772,9 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1647
1772
|
case "Recorded Date":
|
|
1648
1773
|
data["recordedDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1649
1774
|
break;
|
|
1775
|
+
case "Source":
|
|
1776
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1777
|
+
break;
|
|
1650
1778
|
default:
|
|
1651
1779
|
break;
|
|
1652
1780
|
}
|
|
@@ -1654,9 +1782,11 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1654
1782
|
isSummaryCreated = true;
|
|
1655
1783
|
html += `
|
|
1656
1784
|
<tr>
|
|
1657
|
-
<td>${data["allergen"] ?? "
|
|
1658
|
-
|
|
1659
|
-
<td>${
|
|
1785
|
+
<td>${data["allergen"] ?? ""}</td>
|
|
1786
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
1787
|
+
<td>${data["criticality"] ?? ""}</td>
|
|
1788
|
+
<td>${templateUtilities.renderTime(data["recordedDate"], timezone) ?? ""}</td>
|
|
1789
|
+
<td>${data["source"] ?? ""}</td>
|
|
1660
1790
|
</tr>`;
|
|
1661
1791
|
}
|
|
1662
1792
|
}
|
|
@@ -1704,10 +1834,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1704
1834
|
<tr>
|
|
1705
1835
|
<th>Allergen</th>
|
|
1706
1836
|
<th>Status</th>
|
|
1837
|
+
<th>Code (System)</th>
|
|
1707
1838
|
<th>Category</th>
|
|
1708
1839
|
<th>Reaction</th>
|
|
1709
1840
|
<th>Onset Date</th>
|
|
1710
1841
|
<th>Comments</th>
|
|
1842
|
+
<th>Source</th>
|
|
1711
1843
|
</tr>
|
|
1712
1844
|
</thead>
|
|
1713
1845
|
<tbody>`;
|
|
@@ -1731,11 +1863,13 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1731
1863
|
<tr>
|
|
1732
1864
|
<th>Allergen</th>
|
|
1733
1865
|
<th>Status</th>
|
|
1866
|
+
<th>Code (System)</th>
|
|
1734
1867
|
<th>Category</th>
|
|
1735
1868
|
<th>Reaction</th>
|
|
1736
1869
|
<th>Onset Date</th>
|
|
1737
1870
|
<th>Comments</th>
|
|
1738
1871
|
<th>Resolved Date</th>
|
|
1872
|
+
<th>Source</th>
|
|
1739
1873
|
</tr>
|
|
1740
1874
|
</thead>
|
|
1741
1875
|
<tbody>`;
|
|
@@ -1766,14 +1900,16 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1766
1900
|
for (const allergy of allergies) {
|
|
1767
1901
|
html += `
|
|
1768
1902
|
<tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
|
|
1769
|
-
<td class="Name"><span class="AllergenName">${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1770
|
-
<td class="Status">${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1771
|
-
<td class="
|
|
1772
|
-
<td class="
|
|
1773
|
-
<td class="
|
|
1774
|
-
<td class="
|
|
1903
|
+
<td class="Name"><span class="AllergenName">${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(allergy.code))}</span></td>
|
|
1904
|
+
<td class="Status">${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(allergy.clinicalStatus)) || ""}</td>
|
|
1905
|
+
<td class="CodeSystem">${templateUtilities.codeableConceptCoding(allergy.code)}</td>
|
|
1906
|
+
<td class="Category">${templateUtilities.renderTextAsHtml(templateUtilities.safeConcat(allergy.category)) || ""}</td>
|
|
1907
|
+
<td class="Reaction">${templateUtilities.renderTextAsHtml(templateUtilities.concatReactionManifestation(allergy.reaction)) || ""}</td>
|
|
1908
|
+
<td class="OnsetDate">${templateUtilities.renderTextAsHtml(templateUtilities.renderTime(allergy.onsetDateTime, timezone)) || ""}</td>
|
|
1909
|
+
<td class="Comments">${templateUtilities.renderNotes(allergy.note, timezone, { styled: true, warning: true })}</td>
|
|
1910
|
+
<td class="Source">${templateUtilities.getOwnerTag(allergy)}</td>`;
|
|
1775
1911
|
if (includeResolved) {
|
|
1776
|
-
let endDate = "
|
|
1912
|
+
let endDate = "";
|
|
1777
1913
|
if (allergy.extension && Array.isArray(allergy.extension)) {
|
|
1778
1914
|
const endDateExt = allergy.extension.find(
|
|
1779
1915
|
(ext) => ext.url === "http://hl7.org/fhir/StructureDefinition/allergyintolerance-resolutionDate"
|
|
@@ -1797,10 +1933,11 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1797
1933
|
* Generate HTML narrative for Medication resources
|
|
1798
1934
|
* @param resources - FHIR Medication resources
|
|
1799
1935
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1936
|
+
* @param now - Optional current date to use for calculations (defaults to new Date())
|
|
1800
1937
|
* @returns HTML string for rendering
|
|
1801
1938
|
*/
|
|
1802
|
-
generateNarrative(resources, timezone) {
|
|
1803
|
-
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
|
|
1939
|
+
generateNarrative(resources, timezone, now) {
|
|
1940
|
+
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone, now);
|
|
1804
1941
|
}
|
|
1805
1942
|
/**
|
|
1806
1943
|
* Generate HTML narrative for Medication resources using summary
|
|
@@ -1813,23 +1950,27 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1813
1950
|
const templateUtilities = new TemplateUtilities(resources);
|
|
1814
1951
|
let isSummaryCreated = false;
|
|
1815
1952
|
const currentDate = now || /* @__PURE__ */ new Date();
|
|
1816
|
-
const
|
|
1953
|
+
const twoYearsAgo = new Date(currentDate.getFullYear(), currentDate.getMonth() - 24, currentDate.getDate());
|
|
1817
1954
|
let html = `
|
|
1818
1955
|
<div>
|
|
1819
1956
|
<table>
|
|
1820
1957
|
<thead>
|
|
1821
1958
|
<tr>
|
|
1822
1959
|
<th>Medication</th>
|
|
1960
|
+
<th>Code (System)</th>
|
|
1823
1961
|
<th>Status</th>
|
|
1824
1962
|
<th>Sig</th>
|
|
1825
1963
|
<th>Days of Supply</th>
|
|
1826
1964
|
<th>Refills</th>
|
|
1827
1965
|
<th>Start Date</th>
|
|
1966
|
+
<th>Source</th>
|
|
1828
1967
|
</tr>
|
|
1829
1968
|
</thead>
|
|
1830
1969
|
<tbody>`;
|
|
1970
|
+
let skippedMedications = 0;
|
|
1831
1971
|
for (const resourceItem of resources) {
|
|
1832
1972
|
for (const rowData of resourceItem.section ?? []) {
|
|
1973
|
+
const sectionCodeableConcept = rowData.code;
|
|
1833
1974
|
const data = {};
|
|
1834
1975
|
for (const columnData of rowData.section ?? []) {
|
|
1835
1976
|
switch (columnData.title) {
|
|
@@ -1854,6 +1995,9 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1854
1995
|
case "Authored On Date":
|
|
1855
1996
|
data["startDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1856
1997
|
break;
|
|
1998
|
+
case "Source":
|
|
1999
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2000
|
+
break;
|
|
1857
2001
|
default:
|
|
1858
2002
|
break;
|
|
1859
2003
|
}
|
|
@@ -1865,16 +2009,21 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1865
2009
|
startDateObj = void 0;
|
|
1866
2010
|
}
|
|
1867
2011
|
}
|
|
1868
|
-
if (data["status"] === "active" || startDateObj && startDateObj >=
|
|
2012
|
+
if (!(data["status"] === "active" || startDateObj && startDateObj >= twoYearsAgo)) {
|
|
2013
|
+
skippedMedications++;
|
|
2014
|
+
}
|
|
2015
|
+
if (data["status"] === "active" || startDateObj && startDateObj >= twoYearsAgo) {
|
|
1869
2016
|
isSummaryCreated = true;
|
|
1870
2017
|
html += `
|
|
1871
2018
|
<tr>
|
|
1872
2019
|
<td>${templateUtilities.renderTextAsHtml(data["medication"])}</td>
|
|
2020
|
+
<td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
|
|
1873
2021
|
<td>${templateUtilities.renderTextAsHtml(data["status"])}</td>
|
|
1874
2022
|
<td>${templateUtilities.renderTextAsHtml(data["sig-prescriber"] || data["sig-pharmacy"])}</td>
|
|
1875
2023
|
<td>${templateUtilities.renderTextAsHtml(data["daysOfSupply"])}</td>
|
|
1876
2024
|
<td>${templateUtilities.renderTextAsHtml(data["refills"])}</td>
|
|
1877
2025
|
<td>${templateUtilities.renderTime(data["startDate"], timezone)}</td>
|
|
2026
|
+
<td>${templateUtilities.renderTextAsHtml(data["source"])}</td>
|
|
1878
2027
|
</tr>`;
|
|
1879
2028
|
}
|
|
1880
2029
|
}
|
|
@@ -1883,7 +2032,14 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1883
2032
|
</tbody>
|
|
1884
2033
|
</table>
|
|
1885
2034
|
</div>`;
|
|
1886
|
-
|
|
2035
|
+
if (skippedMedications > 0) {
|
|
2036
|
+
html += `
|
|
2037
|
+
<p><em>${skippedMedications} additional medications older than 2 years ago are present</em></p>`;
|
|
2038
|
+
}
|
|
2039
|
+
if (isSummaryCreated || skippedMedications > 0) {
|
|
2040
|
+
return html;
|
|
2041
|
+
}
|
|
2042
|
+
return void 0;
|
|
1887
2043
|
}
|
|
1888
2044
|
/**
|
|
1889
2045
|
* Safely parse a date string and return a valid Date object or null
|
|
@@ -1901,21 +2057,42 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1901
2057
|
* Internal static implementation that actually generates the narrative
|
|
1902
2058
|
* @param resources - FHIR Medication resources
|
|
1903
2059
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2060
|
+
* @param now - Optional current date to use for calculations (defaults to new Date())
|
|
1904
2061
|
* @returns HTML string for rendering
|
|
1905
2062
|
*/
|
|
1906
|
-
|
|
1907
|
-
static generateStaticNarrative(resources, timezone) {
|
|
2063
|
+
static generateStaticNarrative(resources, timezone, now) {
|
|
1908
2064
|
const templateUtilities = new TemplateUtilities(resources);
|
|
1909
2065
|
let html = "";
|
|
1910
2066
|
const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
|
|
1911
2067
|
const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
|
|
1912
2068
|
const allActiveMedications = [];
|
|
2069
|
+
const currentDate = now || /* @__PURE__ */ new Date();
|
|
2070
|
+
const twoYearsAgo = new Date(currentDate);
|
|
2071
|
+
twoYearsAgo.setFullYear(currentDate.getFullYear() - 2);
|
|
2072
|
+
let skippedMedications = 0;
|
|
2073
|
+
const allMedications = [];
|
|
1913
2074
|
medicationRequests.forEach((mr) => {
|
|
1914
|
-
|
|
2075
|
+
allMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1915
2076
|
});
|
|
1916
2077
|
medicationStatements.forEach((ms) => {
|
|
1917
|
-
|
|
2078
|
+
allMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1918
2079
|
});
|
|
2080
|
+
for (const med of allMedications) {
|
|
2081
|
+
let dateString;
|
|
2082
|
+
if (med.type === "request") {
|
|
2083
|
+
const mr = med.resource;
|
|
2084
|
+
dateString = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
2085
|
+
} else {
|
|
2086
|
+
const ms = med.resource;
|
|
2087
|
+
dateString = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
2088
|
+
}
|
|
2089
|
+
const dateObj = this.parseDate(dateString);
|
|
2090
|
+
if (!dateObj || dateObj < twoYearsAgo) {
|
|
2091
|
+
skippedMedications++;
|
|
2092
|
+
} else {
|
|
2093
|
+
allActiveMedications.push(med);
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
1919
2096
|
const sortMedications = (medications) => {
|
|
1920
2097
|
medications.sort((a, b) => {
|
|
1921
2098
|
let dateStringA;
|
|
@@ -1946,7 +2123,14 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1946
2123
|
sortMedications(allActiveMedications);
|
|
1947
2124
|
html += this.renderCombinedMedications(templateUtilities, allActiveMedications);
|
|
1948
2125
|
}
|
|
1949
|
-
|
|
2126
|
+
if (skippedMedications > 0) {
|
|
2127
|
+
html += `
|
|
2128
|
+
<p><em>${skippedMedications} additional medications older than 2 years ago are present</em></p>`;
|
|
2129
|
+
}
|
|
2130
|
+
if (allActiveMedications.length > 0 || skippedMedications > 0) {
|
|
2131
|
+
return html;
|
|
2132
|
+
}
|
|
2133
|
+
return "";
|
|
1950
2134
|
}
|
|
1951
2135
|
/**
|
|
1952
2136
|
* Extract MedicationRequest resources
|
|
@@ -1991,10 +2175,12 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1991
2175
|
<tr>
|
|
1992
2176
|
<th>Type</th>
|
|
1993
2177
|
<th>Medication</th>
|
|
2178
|
+
<th>Code (System)</th>
|
|
1994
2179
|
<th>Sig</th>
|
|
1995
2180
|
<th>Dispense Quantity</th>
|
|
1996
2181
|
<th>Refills</th>
|
|
1997
2182
|
<th>Start Date</th>
|
|
2183
|
+
<th>Source</th>
|
|
1998
2184
|
</tr>
|
|
1999
2185
|
</thead>
|
|
2000
2186
|
<tbody>`;
|
|
@@ -2003,27 +2189,31 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
2003
2189
|
let type;
|
|
2004
2190
|
let medicationName;
|
|
2005
2191
|
let sig;
|
|
2006
|
-
let dispenseQuantity = "
|
|
2007
|
-
let refills = "
|
|
2008
|
-
let startDate = "
|
|
2192
|
+
let dispenseQuantity = "";
|
|
2193
|
+
let refills = "";
|
|
2194
|
+
let startDate = "";
|
|
2195
|
+
let codeSystemDisplay = "";
|
|
2009
2196
|
if (medication.type === "request") {
|
|
2010
2197
|
const mr = medication.resource;
|
|
2011
2198
|
type = "Request";
|
|
2012
2199
|
medicationName = templateUtilities.getMedicationName(
|
|
2013
2200
|
mr.medicationReference || mr.medicationCodeableConcept
|
|
2014
2201
|
);
|
|
2015
|
-
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "
|
|
2202
|
+
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "";
|
|
2016
2203
|
if (mr.dispenseRequest?.quantity) {
|
|
2017
2204
|
const quantity = mr.dispenseRequest.quantity;
|
|
2018
2205
|
if (quantity.value) {
|
|
2019
2206
|
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
2020
2207
|
}
|
|
2021
2208
|
}
|
|
2022
|
-
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "
|
|
2209
|
+
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "";
|
|
2023
2210
|
if (mr.dispenseRequest?.validityPeriod) {
|
|
2024
|
-
startDate = mr.dispenseRequest.validityPeriod.start || "
|
|
2211
|
+
startDate = mr.dispenseRequest.validityPeriod.start || "";
|
|
2025
2212
|
} else {
|
|
2026
|
-
startDate = mr.authoredOn || "
|
|
2213
|
+
startDate = mr.authoredOn || "";
|
|
2214
|
+
}
|
|
2215
|
+
if (mr.medicationCodeableConcept && mr.medicationCodeableConcept.coding && mr.medicationCodeableConcept.coding[0]) {
|
|
2216
|
+
codeSystemDisplay = templateUtilities.codeableConceptCoding(mr.medicationCodeableConcept);
|
|
2027
2217
|
}
|
|
2028
2218
|
} else {
|
|
2029
2219
|
const ms = medication.resource;
|
|
@@ -2031,21 +2221,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
2031
2221
|
medicationName = templateUtilities.getMedicationName(
|
|
2032
2222
|
ms.medicationReference || ms.medicationCodeableConcept
|
|
2033
2223
|
);
|
|
2034
|
-
sig = templateUtilities.concat(ms.dosage, "text") || "
|
|
2224
|
+
sig = templateUtilities.concat(ms.dosage, "text") || "";
|
|
2035
2225
|
if (ms.effectiveDateTime) {
|
|
2036
2226
|
startDate = ms.effectiveDateTime;
|
|
2037
2227
|
} else if (ms.effectivePeriod) {
|
|
2038
|
-
startDate = ms.effectivePeriod.start || "
|
|
2228
|
+
startDate = ms.effectivePeriod.start || "";
|
|
2229
|
+
}
|
|
2230
|
+
if (ms.medicationCodeableConcept && ms.medicationCodeableConcept.coding && ms.medicationCodeableConcept.coding[0]) {
|
|
2231
|
+
codeSystemDisplay = templateUtilities.codeableConceptCoding(ms.medicationCodeableConcept);
|
|
2039
2232
|
}
|
|
2040
2233
|
}
|
|
2041
2234
|
html += `
|
|
2042
2235
|
<tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
|
|
2043
2236
|
<td>${type}</td>
|
|
2044
2237
|
<td>${medicationName}<ul></ul></td>
|
|
2238
|
+
<td>${codeSystemDisplay}</td>
|
|
2045
2239
|
<td>${sig}</td>
|
|
2046
2240
|
<td>${dispenseQuantity}</td>
|
|
2047
2241
|
<td>${refills}</td>
|
|
2048
2242
|
<td>${startDate}</td>
|
|
2243
|
+
<td>${templateUtilities.getOwnerTag(medication.resource)}</td>
|
|
2049
2244
|
</tr>`;
|
|
2050
2245
|
}
|
|
2051
2246
|
html += `
|
|
@@ -2086,14 +2281,18 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2086
2281
|
<thead>
|
|
2087
2282
|
<tr>
|
|
2088
2283
|
<th>Immunization</th>
|
|
2284
|
+
<th>Code (System)</th>
|
|
2089
2285
|
<th>Status</th>
|
|
2090
2286
|
<th>Date</th>
|
|
2287
|
+
<th>Source</th>
|
|
2091
2288
|
</tr>
|
|
2092
2289
|
</thead>
|
|
2093
2290
|
<tbody>`;
|
|
2094
2291
|
for (const resourceItem of resources) {
|
|
2095
2292
|
for (const rowData of resourceItem.section ?? []) {
|
|
2293
|
+
const sectionCodeableConcept = rowData.code;
|
|
2096
2294
|
const data = {};
|
|
2295
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2097
2296
|
for (const columnData of rowData.section ?? []) {
|
|
2098
2297
|
switch (columnData.title) {
|
|
2099
2298
|
case "Immunization Name":
|
|
@@ -2105,6 +2304,9 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2105
2304
|
case "occurrenceDateTime":
|
|
2106
2305
|
data["occurrenceDateTime"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2107
2306
|
break;
|
|
2307
|
+
case "Source":
|
|
2308
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2309
|
+
break;
|
|
2108
2310
|
default:
|
|
2109
2311
|
break;
|
|
2110
2312
|
}
|
|
@@ -2113,9 +2315,11 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2113
2315
|
isSummaryCreated = true;
|
|
2114
2316
|
html += `
|
|
2115
2317
|
<tr>
|
|
2116
|
-
<td>${data["immunization"] ?? "
|
|
2117
|
-
<td>${data["
|
|
2118
|
-
<td>${
|
|
2318
|
+
<td>${data["immunization"] ?? ""}</td>
|
|
2319
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
2320
|
+
<td>${data["status"] ?? ""}</td>
|
|
2321
|
+
<td>${templateUtilities.renderTime(data["occurrenceDateTime"], timezone) ?? ""}</td>
|
|
2322
|
+
<td>${data["source"] ?? ""}</td>
|
|
2119
2323
|
</tr>`;
|
|
2120
2324
|
}
|
|
2121
2325
|
}
|
|
@@ -2139,12 +2343,14 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2139
2343
|
<thead>
|
|
2140
2344
|
<tr>
|
|
2141
2345
|
<th>Immunization</th>
|
|
2346
|
+
<th>Code (System)</th>
|
|
2142
2347
|
<th>Status</th>
|
|
2143
2348
|
<th>Dose Number</th>
|
|
2144
2349
|
<th>Manufacturer</th>
|
|
2145
2350
|
<th>Lot Number</th>
|
|
2146
2351
|
<th>Comments</th>
|
|
2147
2352
|
<th>Date</th>
|
|
2353
|
+
<th>Source</th>
|
|
2148
2354
|
</tr>
|
|
2149
2355
|
</thead>
|
|
2150
2356
|
<tbody>`;
|
|
@@ -2154,13 +2360,15 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2154
2360
|
const imm = resourceItem;
|
|
2155
2361
|
html += `
|
|
2156
2362
|
<tr id="${templateUtilities.narrativeLinkId(imm)}">
|
|
2157
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
2363
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(imm.vaccineCode))}</td>
|
|
2364
|
+
<td>${templateUtilities.codeableConceptCoding(imm.vaccineCode)}</td>
|
|
2158
2365
|
<td>${imm.status || ""}</td>
|
|
2159
2366
|
<td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
|
|
2160
2367
|
<td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
|
|
2161
2368
|
<td>${imm.lotNumber || ""}</td>
|
|
2162
2369
|
<td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
|
|
2163
2370
|
<td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
|
|
2371
|
+
<td>${templateUtilities.getOwnerTag(imm)}</td>
|
|
2164
2372
|
</tr>`;
|
|
2165
2373
|
}
|
|
2166
2374
|
}
|
|
@@ -2194,8 +2402,11 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
2194
2402
|
let html = ``;
|
|
2195
2403
|
const activeConditions = resources.map((entry) => entry) || [];
|
|
2196
2404
|
activeConditions.sort((a, b) => {
|
|
2197
|
-
|
|
2198
|
-
|
|
2405
|
+
if (!a.recordedDate && b.recordedDate) return -1;
|
|
2406
|
+
if (a.recordedDate && !b.recordedDate) return 1;
|
|
2407
|
+
if (!a.recordedDate && !b.recordedDate) return 0;
|
|
2408
|
+
const dateA = new Date(a.recordedDate).getTime();
|
|
2409
|
+
const dateB = new Date(b.recordedDate).getTime();
|
|
2199
2410
|
return dateB - dateA;
|
|
2200
2411
|
});
|
|
2201
2412
|
html += `
|
|
@@ -2203,22 +2414,28 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
2203
2414
|
<thead>
|
|
2204
2415
|
<tr>
|
|
2205
2416
|
<th>Problem</th>
|
|
2417
|
+
<th>Code (System)</th>
|
|
2206
2418
|
<th>Onset Date</th>
|
|
2207
2419
|
<th>Recorded Date</th>
|
|
2420
|
+
<th>Source</th>
|
|
2208
2421
|
</tr>
|
|
2209
2422
|
</thead>
|
|
2210
2423
|
<tbody>`;
|
|
2211
|
-
const
|
|
2424
|
+
const seenCodeAndSystems = /* @__PURE__ */ new Set();
|
|
2212
2425
|
for (const cond of activeConditions) {
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
<td class="Name">${conditionCode}</td>
|
|
2218
|
-
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
2219
|
-
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
2220
|
-
</tr>`;
|
|
2426
|
+
const conditionDisplay = templateUtilities.codeableConceptDisplay(cond.code);
|
|
2427
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(cond.code);
|
|
2428
|
+
if (codeAndSystem && seenCodeAndSystems.has(codeAndSystem)) {
|
|
2429
|
+
continue;
|
|
2221
2430
|
}
|
|
2431
|
+
seenCodeAndSystems.add(codeAndSystem);
|
|
2432
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
2433
|
+
<td class="Name">${conditionDisplay}</td>
|
|
2434
|
+
<td class="CodeSystem">${codeAndSystem}</td>
|
|
2435
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
2436
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
2437
|
+
<td class="Source">${templateUtilities.getOwnerTag(cond)}</td>
|
|
2438
|
+
</tr>`;
|
|
2222
2439
|
}
|
|
2223
2440
|
html += `</tbody>
|
|
2224
2441
|
</table>`;
|
|
@@ -2251,16 +2468,19 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2251
2468
|
<table>
|
|
2252
2469
|
<thead>
|
|
2253
2470
|
<tr>
|
|
2254
|
-
<th>
|
|
2471
|
+
<th>Name</th>
|
|
2472
|
+
<th>Code (System)</th>
|
|
2255
2473
|
<th>Result</th>
|
|
2256
|
-
<th>Reference Range</th>
|
|
2257
2474
|
<th>Date</th>
|
|
2475
|
+
<th>Source</th>
|
|
2258
2476
|
</tr>
|
|
2259
2477
|
</thead>
|
|
2260
2478
|
<tbody>`;
|
|
2261
2479
|
for (const resourceItem of resources) {
|
|
2262
2480
|
for (const rowData of resourceItem.section ?? []) {
|
|
2481
|
+
const sectionCodeableConcept = rowData.code;
|
|
2263
2482
|
const data = {};
|
|
2483
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2264
2484
|
for (const columnData of rowData.section ?? []) {
|
|
2265
2485
|
const columnTitle = columnData.title;
|
|
2266
2486
|
if (columnTitle) {
|
|
@@ -2288,10 +2508,11 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2288
2508
|
isSummaryCreated = true;
|
|
2289
2509
|
html += `
|
|
2290
2510
|
<tr>
|
|
2291
|
-
<td>${data["Vital Name"] ?? "
|
|
2292
|
-
<td>${
|
|
2293
|
-
<td>${templateUtilities.
|
|
2294
|
-
<td>${templateUtilities.extractObservationSummaryEffectiveTime(data, timezone) ?? "
|
|
2511
|
+
<td>${data["Vital Name"] ?? ""}</td>
|
|
2512
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
2513
|
+
<td>${templateUtilities.extractObservationSummaryValue(data, timezone) ?? ""}</td>
|
|
2514
|
+
<td>${templateUtilities.extractObservationSummaryEffectiveTime(data, timezone) ?? ""}</td>
|
|
2515
|
+
<td>${data["Source"] ?? ""}</td>
|
|
2295
2516
|
</tr>`;
|
|
2296
2517
|
}
|
|
2297
2518
|
}
|
|
@@ -2319,26 +2540,30 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2319
2540
|
<table>
|
|
2320
2541
|
<thead>
|
|
2321
2542
|
<tr>
|
|
2322
|
-
<th>
|
|
2543
|
+
<th>Name</th>
|
|
2544
|
+
<th>Code (System)</th>
|
|
2323
2545
|
<th>Result</th>
|
|
2324
2546
|
<th>Unit</th>
|
|
2325
2547
|
<th>Interpretation</th>
|
|
2326
2548
|
<th>Component(s)</th>
|
|
2327
2549
|
<th>Comments</th>
|
|
2328
2550
|
<th>Date</th>
|
|
2551
|
+
<th>Source</th>
|
|
2329
2552
|
</tr>
|
|
2330
2553
|
</thead>
|
|
2331
2554
|
<tbody>`;
|
|
2332
2555
|
for (const obs of observations) {
|
|
2333
2556
|
html += `
|
|
2334
2557
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2335
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
2558
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code, "display"))}</td>
|
|
2559
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
2336
2560
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2337
2561
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
2338
2562
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
2339
2563
|
<td>${templateUtilities.renderComponent(obs.component)}</td>
|
|
2340
2564
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
2341
2565
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
2566
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
2342
2567
|
</tr>`;
|
|
2343
2568
|
}
|
|
2344
2569
|
html += `
|
|
@@ -2411,10 +2636,11 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2411
2636
|
* Generate HTML narrative for Diagnostic Results
|
|
2412
2637
|
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
2413
2638
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2639
|
+
* @param now - Optional current date for filtering
|
|
2414
2640
|
* @returns HTML string for rendering
|
|
2415
2641
|
*/
|
|
2416
|
-
generateNarrative(resources, timezone) {
|
|
2417
|
-
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
|
|
2642
|
+
generateNarrative(resources, timezone, now) {
|
|
2643
|
+
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone, now);
|
|
2418
2644
|
}
|
|
2419
2645
|
/**
|
|
2420
2646
|
* Helper function to format observation data fields
|
|
@@ -2627,6 +2853,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2627
2853
|
break;
|
|
2628
2854
|
case "valueRange.high.value":
|
|
2629
2855
|
targetData["valueRangeHighValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
2856
|
+
targetData["valueType"] = "valueRange";
|
|
2630
2857
|
break;
|
|
2631
2858
|
case "valueRange.high.unit":
|
|
2632
2859
|
targetData["valueRangeHighUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
@@ -2641,6 +2868,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2641
2868
|
break;
|
|
2642
2869
|
case "valueRatio.denominator.value":
|
|
2643
2870
|
targetData["valueRatioDenominatorValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
2871
|
+
targetData["valueType"] = "valueRatio";
|
|
2644
2872
|
break;
|
|
2645
2873
|
case "valueRatio.denominator.unit":
|
|
2646
2874
|
targetData["valueRatioDenominatorUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
@@ -2678,10 +2906,71 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2678
2906
|
* Generate HTML narrative for Diagnostic Results & Observation resources using summary
|
|
2679
2907
|
* @param resources - FHIR Composition resources
|
|
2680
2908
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2909
|
+
* @param now - Optional current date for filtering
|
|
2681
2910
|
* @returns HTML string for rendering
|
|
2682
2911
|
*/
|
|
2683
|
-
generateSummaryNarrative(resources, timezone) {
|
|
2912
|
+
generateSummaryNarrative(resources, timezone, now) {
|
|
2684
2913
|
const templateUtilities = new TemplateUtilities(resources);
|
|
2914
|
+
const currentDate = now || /* @__PURE__ */ new Date();
|
|
2915
|
+
const twoYearsAgo = new Date(currentDate);
|
|
2916
|
+
twoYearsAgo.setFullYear(currentDate.getFullYear() - 2);
|
|
2917
|
+
let skippedObservations = 0;
|
|
2918
|
+
let skippedDiagnosticReports = 0;
|
|
2919
|
+
for (const resourceItem of resources) {
|
|
2920
|
+
for (const rowData of resourceItem.section ?? []) {
|
|
2921
|
+
if (resourceItem.title === "Observation|Labs Summary Grouped by Lab Code") {
|
|
2922
|
+
for (const columnData of rowData.section ?? []) {
|
|
2923
|
+
if (columnData.text?.div === "Observation.component" && columnData.section) {
|
|
2924
|
+
for (const componentSection of columnData.section) {
|
|
2925
|
+
const componentData = {};
|
|
2926
|
+
for (const nestedColumn of componentSection.section ?? []) {
|
|
2927
|
+
this.extractSummaryObservationFields(nestedColumn, componentData, templateUtilities);
|
|
2928
|
+
}
|
|
2929
|
+
let compDate = void 0;
|
|
2930
|
+
if (componentData["effectiveDateTime"]) {
|
|
2931
|
+
compDate = new Date(componentData["effectiveDateTime"]);
|
|
2932
|
+
} else if (componentData["effectivePeriodStart"]) {
|
|
2933
|
+
compDate = new Date(componentData["effectivePeriodStart"]);
|
|
2934
|
+
}
|
|
2935
|
+
if (compDate && compDate < twoYearsAgo) {
|
|
2936
|
+
skippedObservations++;
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
} else {
|
|
2940
|
+
const data = {};
|
|
2941
|
+
this.extractSummaryObservationFields(columnData, data, templateUtilities);
|
|
2942
|
+
let obsDate = void 0;
|
|
2943
|
+
if (data["effectiveDateTime"]) {
|
|
2944
|
+
obsDate = new Date(data["effectiveDateTime"]);
|
|
2945
|
+
} else if (data["effectivePeriodStart"]) {
|
|
2946
|
+
obsDate = new Date(data["effectivePeriodStart"]);
|
|
2947
|
+
}
|
|
2948
|
+
if (obsDate && obsDate < twoYearsAgo) {
|
|
2949
|
+
skippedObservations++;
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
} else if (resourceItem.title === "DiagnosticReportLab Summary Grouped by DiagnosticReport|Lab Code") {
|
|
2954
|
+
let issuedDate = void 0;
|
|
2955
|
+
let status = void 0;
|
|
2956
|
+
let reportFound = false;
|
|
2957
|
+
for (const columnData of rowData.section ?? []) {
|
|
2958
|
+
if (columnData.title === "Issued Date") {
|
|
2959
|
+
issuedDate = columnData.text?.div ? new Date(columnData.text.div) : void 0;
|
|
2960
|
+
}
|
|
2961
|
+
if (columnData.title === "Status") {
|
|
2962
|
+
status = columnData.text?.div;
|
|
2963
|
+
}
|
|
2964
|
+
if (columnData.title === "Diagnostic Report Name") {
|
|
2965
|
+
reportFound = true;
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
if (status === "final" && issuedDate && issuedDate < twoYearsAgo && reportFound) {
|
|
2969
|
+
skippedDiagnosticReports++;
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2685
2974
|
let html = `
|
|
2686
2975
|
<div>`;
|
|
2687
2976
|
let observationhtml = `
|
|
@@ -2690,10 +2979,12 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2690
2979
|
<table>
|
|
2691
2980
|
<thead>
|
|
2692
2981
|
<tr>
|
|
2693
|
-
<th>
|
|
2982
|
+
<th>Name</th>
|
|
2983
|
+
<th>Code (System)</th>
|
|
2694
2984
|
<th>Result</th>
|
|
2695
2985
|
<th>Reference Range</th>
|
|
2696
2986
|
<th>Date</th>
|
|
2987
|
+
<th>Source</th>
|
|
2697
2988
|
</tr>
|
|
2698
2989
|
</thead>
|
|
2699
2990
|
<tbody>`;
|
|
@@ -2706,6 +2997,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2706
2997
|
<th>Report</th>
|
|
2707
2998
|
<th>Performer</th>
|
|
2708
2999
|
<th>Issued</th>
|
|
3000
|
+
<th>Source</th>
|
|
2709
3001
|
</tr>
|
|
2710
3002
|
</thead>
|
|
2711
3003
|
<tbody>`;
|
|
@@ -2713,7 +3005,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2713
3005
|
const diagnosticReportAdded = /* @__PURE__ */ new Set();
|
|
2714
3006
|
for (const resourceItem of resources) {
|
|
2715
3007
|
for (const rowData of resourceItem.section ?? []) {
|
|
3008
|
+
const sectionCodeableConcept = rowData.code;
|
|
2716
3009
|
const data = {};
|
|
3010
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2717
3011
|
const components = [];
|
|
2718
3012
|
for (const columnData of rowData.section ?? []) {
|
|
2719
3013
|
if (resourceItem.title === "Observation|Labs Summary Grouped by Lab Code") {
|
|
@@ -2723,7 +3017,13 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2723
3017
|
for (const nestedColumn of componentSection.section ?? []) {
|
|
2724
3018
|
this.extractSummaryObservationFields(nestedColumn, componentData, templateUtilities);
|
|
2725
3019
|
}
|
|
2726
|
-
|
|
3020
|
+
let compDate = void 0;
|
|
3021
|
+
if (componentData["effectiveDateTime"]) {
|
|
3022
|
+
compDate = new Date(componentData["effectiveDateTime"]);
|
|
3023
|
+
} else if (componentData["effectivePeriodStart"]) {
|
|
3024
|
+
compDate = new Date(componentData["effectivePeriodStart"]);
|
|
3025
|
+
}
|
|
3026
|
+
if (compDate && compDate >= twoYearsAgo && Object.keys(componentData).length > 0) {
|
|
2727
3027
|
components.push(componentData);
|
|
2728
3028
|
}
|
|
2729
3029
|
}
|
|
@@ -2744,6 +3044,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2744
3044
|
case "Status":
|
|
2745
3045
|
data["status"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2746
3046
|
break;
|
|
3047
|
+
case "Source":
|
|
3048
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
3049
|
+
break;
|
|
2747
3050
|
default:
|
|
2748
3051
|
break;
|
|
2749
3052
|
}
|
|
@@ -2751,6 +3054,12 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2751
3054
|
}
|
|
2752
3055
|
if (resourceItem.title === "Observation|Labs Summary Grouped by Lab Code") {
|
|
2753
3056
|
let date = data["effectiveDateTime"] ? templateUtilities.renderTime(data["effectiveDateTime"], timezone) : "";
|
|
3057
|
+
let obsDate = void 0;
|
|
3058
|
+
if (data["effectiveDateTime"]) {
|
|
3059
|
+
obsDate = new Date(data["effectiveDateTime"]);
|
|
3060
|
+
} else if (data["effectivePeriodStart"]) {
|
|
3061
|
+
obsDate = new Date(data["effectivePeriodStart"]);
|
|
3062
|
+
}
|
|
2754
3063
|
if (!date && data["effectivePeriodStart"]) {
|
|
2755
3064
|
date = templateUtilities.renderTime(data["effectivePeriodStart"], timezone);
|
|
2756
3065
|
if (data["effectivePeriodEnd"]) {
|
|
@@ -2767,36 +3076,47 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2767
3076
|
observationhtml += `
|
|
2768
3077
|
<tr>
|
|
2769
3078
|
<td>${componentCode}</td>
|
|
2770
|
-
<td
|
|
2771
|
-
<td>${templateUtilities.renderTextAsHtml(component["
|
|
2772
|
-
<td>${
|
|
3079
|
+
<td></td>
|
|
3080
|
+
<td>${templateUtilities.renderTextAsHtml(component["formattedValue"]) ?? ""}</td>
|
|
3081
|
+
<td>${templateUtilities.renderTextAsHtml(component["referenceRange"])?.trim() ?? ""}</td>
|
|
3082
|
+
<td>${date ?? ""}</td>
|
|
3083
|
+
<td>${data["source"] ?? ""}</td>
|
|
2773
3084
|
</tr>`;
|
|
2774
3085
|
}
|
|
2775
3086
|
}
|
|
2776
3087
|
} else {
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
observationAdded.
|
|
2780
|
-
|
|
2781
|
-
|
|
3088
|
+
if (obsDate && obsDate >= twoYearsAgo) {
|
|
3089
|
+
const code = data["code"] ?? "";
|
|
3090
|
+
if (code && !observationAdded.has(code)) {
|
|
3091
|
+
observationAdded.add(code);
|
|
3092
|
+
this.formatSummaryObservationData(data);
|
|
3093
|
+
observationhtml += `
|
|
2782
3094
|
<tr>
|
|
2783
|
-
<td>${data["code"] ?? "
|
|
2784
|
-
<td>${templateUtilities.
|
|
2785
|
-
<td>${templateUtilities.renderTextAsHtml(data["
|
|
2786
|
-
<td>${
|
|
3095
|
+
<td>${data["code"] ?? ""}</td>
|
|
3096
|
+
<td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
|
|
3097
|
+
<td>${templateUtilities.renderTextAsHtml(data["formattedValue"]) ?? ""}</td>
|
|
3098
|
+
<td>${templateUtilities.renderTextAsHtml(data["referenceRange"])?.trim() ?? ""}</td>
|
|
3099
|
+
<td>${date ?? ""}</td>
|
|
3100
|
+
<td>${data["source"] ?? ""}</td>
|
|
2787
3101
|
</tr>`;
|
|
3102
|
+
}
|
|
2788
3103
|
}
|
|
2789
3104
|
}
|
|
2790
3105
|
} else if (resourceItem.title === "DiagnosticReportLab Summary Grouped by DiagnosticReport|Lab Code") {
|
|
2791
|
-
|
|
3106
|
+
let issuedDate = void 0;
|
|
3107
|
+
if (data["issued"]) {
|
|
3108
|
+
issuedDate = new Date(data["issued"]);
|
|
3109
|
+
}
|
|
3110
|
+
if (data["status"] === "final" && issuedDate && issuedDate >= twoYearsAgo) {
|
|
2792
3111
|
const reportName = data["report"] ?? "";
|
|
2793
3112
|
if (reportName && !diagnosticReportAdded.has(reportName)) {
|
|
2794
3113
|
diagnosticReportAdded.add(reportName);
|
|
2795
3114
|
diagnosticReporthtml += `
|
|
2796
3115
|
<tr>
|
|
2797
|
-
<td>${data["report"] ?? "
|
|
2798
|
-
<td>${data["performer"] ?? "
|
|
2799
|
-
<td>${templateUtilities.renderTime(data["issued"], timezone) ?? "
|
|
3116
|
+
<td>${data["report"] ?? ""}</td>
|
|
3117
|
+
<td>${data["performer"] ?? ""}</td>
|
|
3118
|
+
<td>${templateUtilities.renderTime(data["issued"], timezone) ?? ""}</td>
|
|
3119
|
+
<td>${data["source"] ?? ""}</td>
|
|
2800
3120
|
</tr>`;
|
|
2801
3121
|
}
|
|
2802
3122
|
}
|
|
@@ -2809,6 +3129,10 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2809
3129
|
</tbody>
|
|
2810
3130
|
</table>
|
|
2811
3131
|
</div>`;
|
|
3132
|
+
if (skippedObservations > 0) {
|
|
3133
|
+
html += `
|
|
3134
|
+
<p><em>${skippedObservations} additional observations older than 2 years ago are present</em></p>`;
|
|
3135
|
+
}
|
|
2812
3136
|
}
|
|
2813
3137
|
if (diagnosticReportAdded.size > 0) {
|
|
2814
3138
|
html += diagnosticReporthtml;
|
|
@@ -2816,6 +3140,10 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2816
3140
|
</tbody>
|
|
2817
3141
|
</table>
|
|
2818
3142
|
</div>`;
|
|
3143
|
+
if (skippedDiagnosticReports > 0) {
|
|
3144
|
+
html += `
|
|
3145
|
+
<p><em>${skippedDiagnosticReports} additional diagnostic reports older than 2 years ago are present</em></p>`;
|
|
3146
|
+
}
|
|
2819
3147
|
}
|
|
2820
3148
|
html += `
|
|
2821
3149
|
</div>`;
|
|
@@ -2825,12 +3153,32 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2825
3153
|
* Internal static implementation that actually generates the narrative
|
|
2826
3154
|
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
2827
3155
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
3156
|
+
* @param now - Optional current date for filtering
|
|
2828
3157
|
* @returns HTML string for rendering
|
|
2829
3158
|
*/
|
|
2830
|
-
static generateStaticNarrative(resources, timezone) {
|
|
3159
|
+
static generateStaticNarrative(resources, timezone, now) {
|
|
2831
3160
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3161
|
+
const currentDate = now || /* @__PURE__ */ new Date();
|
|
3162
|
+
const twoYearsAgo = new Date(currentDate);
|
|
3163
|
+
twoYearsAgo.setFullYear(currentDate.getFullYear() - 2);
|
|
2832
3164
|
let html = "";
|
|
2833
|
-
|
|
3165
|
+
let skippedObservations = 0;
|
|
3166
|
+
let skippedDiagnosticReports = 0;
|
|
3167
|
+
for (const resourceItem of resources) {
|
|
3168
|
+
if (resourceItem.resourceType === "Observation") {
|
|
3169
|
+
const obsDate = this.getObservationDate(resourceItem);
|
|
3170
|
+
if (obsDate && obsDate < twoYearsAgo) {
|
|
3171
|
+
skippedObservations++;
|
|
3172
|
+
}
|
|
3173
|
+
} else if (resourceItem.resourceType === "DiagnosticReport") {
|
|
3174
|
+
const issued = resourceItem.issued;
|
|
3175
|
+
const status = resourceItem.status;
|
|
3176
|
+
if (status === "final" && issued && new Date(issued) < twoYearsAgo) {
|
|
3177
|
+
skippedDiagnosticReports++;
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
const observations = this.getObservations(resources, twoYearsAgo);
|
|
2834
3182
|
if (observations.length > 0) {
|
|
2835
3183
|
observations.sort((a, b) => {
|
|
2836
3184
|
const dateA = this.getObservationDate(a);
|
|
@@ -2839,16 +3187,22 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2839
3187
|
});
|
|
2840
3188
|
this.filterObservationForLoincCodes(observations);
|
|
2841
3189
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
3190
|
+
if (skippedObservations > 0) {
|
|
3191
|
+
html += `
|
|
3192
|
+
<p><em>${skippedObservations} additional observations older than 2 years ago are present</em></p>`;
|
|
3193
|
+
}
|
|
2842
3194
|
}
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
3195
|
+
const diagnosticReports = this.getDiagnosticReports(resources, twoYearsAgo).filter((resource) => !this.isPanelDiagnosticReport(resource));
|
|
3196
|
+
if (diagnosticReports.length > 0) {
|
|
3197
|
+
diagnosticReports.sort((a, b) => {
|
|
3198
|
+
const dateA = a.issued;
|
|
3199
|
+
const dateB = b.issued;
|
|
3200
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
3201
|
+
});
|
|
3202
|
+
html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
|
|
3203
|
+
if (skippedDiagnosticReports > 0) {
|
|
3204
|
+
html += `
|
|
3205
|
+
<p><em>${skippedDiagnosticReports} additional diagnostic reports older than 2 years ago are present</em></p>`;
|
|
2852
3206
|
}
|
|
2853
3207
|
}
|
|
2854
3208
|
return html;
|
|
@@ -2893,15 +3247,16 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2893
3247
|
return obsDate;
|
|
2894
3248
|
}
|
|
2895
3249
|
/**
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
3250
|
+
* Get all Observation resources from the resource array, filtered by twoYearsAgo
|
|
3251
|
+
* @param resources - FHIR resources array
|
|
3252
|
+
* @param twoYearsAgo - Date object representing the cutoff
|
|
3253
|
+
* @returns Array of Observation resources
|
|
3254
|
+
*/
|
|
3255
|
+
static getObservations(resources, twoYearsAgo) {
|
|
2901
3256
|
return resources.filter((resourceItem) => {
|
|
2902
3257
|
if (resourceItem.resourceType === "Observation") {
|
|
2903
3258
|
const obsDate = this.getObservationDate(resourceItem);
|
|
2904
|
-
if (obsDate && obsDate >=
|
|
3259
|
+
if (obsDate && obsDate >= twoYearsAgo) {
|
|
2905
3260
|
return true;
|
|
2906
3261
|
}
|
|
2907
3262
|
}
|
|
@@ -2909,12 +3264,21 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2909
3264
|
}).map((resourceItem) => resourceItem);
|
|
2910
3265
|
}
|
|
2911
3266
|
/**
|
|
2912
|
-
* Get all DiagnosticReport resources from the resource array
|
|
3267
|
+
* Get all DiagnosticReport resources from the resource array, filtered by twoYearsAgo
|
|
2913
3268
|
* @param resources - FHIR resources array
|
|
3269
|
+
* @param twoYearsAgo - Date object representing the cutoff
|
|
2914
3270
|
* @returns Array of DiagnosticReport resources
|
|
2915
3271
|
*/
|
|
2916
|
-
static getDiagnosticReports(resources) {
|
|
2917
|
-
return resources.filter((resourceItem) =>
|
|
3272
|
+
static getDiagnosticReports(resources, twoYearsAgo) {
|
|
3273
|
+
return resources.filter((resourceItem) => {
|
|
3274
|
+
if (resourceItem.resourceType === "DiagnosticReport") {
|
|
3275
|
+
const issued = resourceItem.issued;
|
|
3276
|
+
if (issued && new Date(issued) >= twoYearsAgo) {
|
|
3277
|
+
return true;
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
return false;
|
|
3281
|
+
}).map((resourceItem) => resourceItem);
|
|
2918
3282
|
}
|
|
2919
3283
|
/**
|
|
2920
3284
|
* Render HTML table for Observation resources
|
|
@@ -2925,32 +3289,36 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2925
3289
|
*/
|
|
2926
3290
|
static renderObservations(templateUtilities, observations, timezone) {
|
|
2927
3291
|
let html = "";
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
<h3>Observations</h3>`;
|
|
2931
|
-
}
|
|
3292
|
+
html += `
|
|
3293
|
+
<h3>Observations</h3>`;
|
|
2932
3294
|
html += `
|
|
2933
3295
|
<table>
|
|
2934
3296
|
<thead>
|
|
2935
3297
|
<tr>
|
|
2936
|
-
<th>
|
|
3298
|
+
<th>Name</th>
|
|
3299
|
+
<th>Code (System)</th>
|
|
2937
3300
|
<th>Result</th>
|
|
2938
3301
|
<th>Reference Range</th>
|
|
2939
3302
|
<th>Date</th>
|
|
3303
|
+
<th>Source</th>
|
|
2940
3304
|
</tr>
|
|
2941
3305
|
</thead>
|
|
2942
3306
|
<tbody>`;
|
|
2943
3307
|
const observationAdded = /* @__PURE__ */ new Set();
|
|
2944
3308
|
for (const obs of observations) {
|
|
2945
|
-
const
|
|
2946
|
-
|
|
2947
|
-
|
|
3309
|
+
const obsCodeDisplay = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code));
|
|
3310
|
+
const obsCodeAndSystem = templateUtilities.codeableConceptCoding(obs.code);
|
|
3311
|
+
if (!observationAdded.has(obsCodeDisplay) && !observationAdded.has(obsCodeAndSystem)) {
|
|
3312
|
+
observationAdded.add(obsCodeDisplay);
|
|
3313
|
+
observationAdded.add(obsCodeAndSystem);
|
|
2948
3314
|
html += `
|
|
2949
3315
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2950
|
-
<td>${
|
|
3316
|
+
<td>${obsCodeDisplay}</td>
|
|
3317
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
2951
3318
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2952
3319
|
<td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
|
|
2953
3320
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
3321
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
2954
3322
|
</tr>`;
|
|
2955
3323
|
}
|
|
2956
3324
|
}
|
|
@@ -2973,17 +3341,21 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2973
3341
|
<thead>
|
|
2974
3342
|
<tr>
|
|
2975
3343
|
<th>Report</th>
|
|
3344
|
+
<th>Code (System)</th>
|
|
2976
3345
|
<th>Category</th>
|
|
2977
3346
|
<th>Result</th>
|
|
2978
3347
|
<th>Issued</th>
|
|
3348
|
+
<th>Source</th>
|
|
2979
3349
|
</tr>
|
|
2980
3350
|
</thead>
|
|
2981
3351
|
<tbody>`;
|
|
2982
3352
|
const diagnosticReportAdded = /* @__PURE__ */ new Set();
|
|
2983
3353
|
for (const report of reports) {
|
|
2984
|
-
const reportName = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
2985
|
-
|
|
3354
|
+
const reportName = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(report.code));
|
|
3355
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(report.code);
|
|
3356
|
+
if (!diagnosticReportAdded.has(reportName) && !diagnosticReportAdded.has(codeAndSystem)) {
|
|
2986
3357
|
diagnosticReportAdded.add(reportName);
|
|
3358
|
+
diagnosticReportAdded.add(codeAndSystem);
|
|
2987
3359
|
let resultCount = "";
|
|
2988
3360
|
if (report.result && Array.isArray(report.result)) {
|
|
2989
3361
|
resultCount = `${report.result.length} result${report.result.length !== 1 ? "s" : ""}`;
|
|
@@ -2991,9 +3363,11 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2991
3363
|
html += `
|
|
2992
3364
|
<tr id="${templateUtilities.narrativeLinkId(report)}">
|
|
2993
3365
|
<td>${reportName}</td>
|
|
3366
|
+
<td>${codeAndSystem}</td>
|
|
2994
3367
|
<td>${templateUtilities.firstFromCodeableConceptList(report.category)}</td>
|
|
2995
3368
|
<td>${resultCount}</td>
|
|
2996
3369
|
<td>${report.issued ? templateUtilities.renderTime(report.issued, timezone) : ""}</td>
|
|
3370
|
+
<td>${templateUtilities.getOwnerTag(report)}</td>
|
|
2997
3371
|
</tr>`;
|
|
2998
3372
|
}
|
|
2999
3373
|
}
|
|
@@ -3002,6 +3376,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
3002
3376
|
</table>`;
|
|
3003
3377
|
return html;
|
|
3004
3378
|
}
|
|
3379
|
+
/**
|
|
3380
|
+
* Helper to determine if a DiagnosticReport is just a panel (i.e., only references Observations, has no conclusion, and code is a known panel code)
|
|
3381
|
+
* @returns true if the report is just a panel
|
|
3382
|
+
*/
|
|
3383
|
+
static isPanelDiagnosticReport(report) {
|
|
3384
|
+
return this.hasEssentialLabPanelLoinc(
|
|
3385
|
+
report
|
|
3386
|
+
);
|
|
3387
|
+
}
|
|
3388
|
+
/**
|
|
3389
|
+
* Check if a DiagnosticReport or Observation has a LOINC code in ESSENTIAL_LAB_PANELS
|
|
3390
|
+
* @param resource - DiagnosticReport or Observation
|
|
3391
|
+
* @returns true if any LOINC code is in ESSENTIAL_LAB_PANELS, false otherwise
|
|
3392
|
+
*/
|
|
3393
|
+
static hasEssentialLabPanelLoinc(resource) {
|
|
3394
|
+
const codings = resource?.code?.coding ?? [];
|
|
3395
|
+
return codings.some(
|
|
3396
|
+
(coding) => coding && coding.system && coding.code && coding.system.toLowerCase().includes("loinc") && Object.keys(ESSENTIAL_LAB_PANELS).includes(coding.code)
|
|
3397
|
+
);
|
|
3398
|
+
}
|
|
3005
3399
|
};
|
|
3006
3400
|
|
|
3007
3401
|
// src/narratives/templates/typescript/HistoryOfProceduresTemplate.ts
|
|
@@ -3035,14 +3429,18 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3035
3429
|
<thead>
|
|
3036
3430
|
<tr>
|
|
3037
3431
|
<th>Procedure</th>
|
|
3432
|
+
<th>Code (System)</th>
|
|
3038
3433
|
<th>Performer</th>
|
|
3039
3434
|
<th>Date</th>
|
|
3435
|
+
<th>Source</th>
|
|
3040
3436
|
</tr>
|
|
3041
3437
|
</thead>
|
|
3042
3438
|
<tbody>`;
|
|
3043
3439
|
for (const resourceItem of resources) {
|
|
3044
3440
|
for (const rowData of resourceItem.section ?? []) {
|
|
3441
|
+
const sectionCodeableConcept = rowData.code;
|
|
3045
3442
|
const data = {};
|
|
3443
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
3046
3444
|
for (const columnData of rowData.section ?? []) {
|
|
3047
3445
|
switch (columnData.title) {
|
|
3048
3446
|
case "Procedure Name":
|
|
@@ -3054,6 +3452,9 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3054
3452
|
case "Performed Date":
|
|
3055
3453
|
data["date"] = columnData.text?.div ?? "";
|
|
3056
3454
|
break;
|
|
3455
|
+
case "Source":
|
|
3456
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
3457
|
+
break;
|
|
3057
3458
|
default:
|
|
3058
3459
|
break;
|
|
3059
3460
|
}
|
|
@@ -3061,9 +3462,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3061
3462
|
isSummaryCreated = true;
|
|
3062
3463
|
html += `
|
|
3063
3464
|
<tr>
|
|
3064
|
-
<td>${data["procedure"] ?? "
|
|
3065
|
-
|
|
3066
|
-
<td>${
|
|
3465
|
+
<td>${data["procedure"] ?? ""}</td>
|
|
3466
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
3467
|
+
<td>${data["performer"] ?? ""}</td>
|
|
3468
|
+
<td>${templateUtilities.renderTime(data["date"], timezone) ?? ""}</td>
|
|
3469
|
+
<td>${data["source"] ?? ""}</td>
|
|
3067
3470
|
</tr>`;
|
|
3068
3471
|
}
|
|
3069
3472
|
}
|
|
@@ -3086,8 +3489,10 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3086
3489
|
<thead>
|
|
3087
3490
|
<tr>
|
|
3088
3491
|
<th>Procedure</th>
|
|
3492
|
+
<th>Code (System)</th>
|
|
3089
3493
|
<th>Comments</th>
|
|
3090
3494
|
<th>Date</th>
|
|
3495
|
+
<th>Source</th>
|
|
3091
3496
|
</tr>
|
|
3092
3497
|
</thead>
|
|
3093
3498
|
<tbody>`;
|
|
@@ -3095,9 +3500,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3095
3500
|
const proc = resourceItem;
|
|
3096
3501
|
html += `
|
|
3097
3502
|
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
3098
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3503
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(proc.code, "display"))}</td>
|
|
3504
|
+
<td>${templateUtilities.codeableConceptCoding(proc.code)}</td>
|
|
3099
3505
|
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
3100
3506
|
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
3507
|
+
<td>${templateUtilities.getOwnerTag(proc)}</td>
|
|
3101
3508
|
</tr>`;
|
|
3102
3509
|
}
|
|
3103
3510
|
html += `
|
|
@@ -3136,22 +3543,26 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
3136
3543
|
<table>
|
|
3137
3544
|
<thead>
|
|
3138
3545
|
<tr>
|
|
3139
|
-
<th>
|
|
3546
|
+
<th>Name</th>
|
|
3547
|
+
<th>Code (System)</th>
|
|
3140
3548
|
<th>Result</th>
|
|
3141
3549
|
<th>Unit</th>
|
|
3142
3550
|
<th>Comments</th>
|
|
3143
3551
|
<th>Date</th>
|
|
3552
|
+
<th>Source</th>
|
|
3144
3553
|
</tr>
|
|
3145
3554
|
</thead>
|
|
3146
3555
|
<tbody>`;
|
|
3147
3556
|
for (const obs of observations) {
|
|
3148
3557
|
html += `
|
|
3149
3558
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
3150
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3559
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code))}</td>
|
|
3560
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
3151
3561
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
3152
3562
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
3153
3563
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
3154
3564
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
3565
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
3155
3566
|
</tr>`;
|
|
3156
3567
|
}
|
|
3157
3568
|
html += `
|
|
@@ -3167,14 +3578,26 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
3167
3578
|
* Generate HTML narrative for Past History of Illnesses
|
|
3168
3579
|
* @param resources - FHIR Condition resources
|
|
3169
3580
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
3581
|
+
* @param now - Optional current date to use for generating relative dates in the narrative
|
|
3170
3582
|
* @returns HTML string for rendering
|
|
3171
3583
|
*/
|
|
3172
|
-
|
|
3173
|
-
generateNarrative(resources, timezone) {
|
|
3584
|
+
generateNarrative(resources, timezone, now) {
|
|
3174
3585
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3175
3586
|
let html = ``;
|
|
3176
3587
|
const resolvedConditions = resources.map((entry) => entry) || [];
|
|
3177
|
-
|
|
3588
|
+
const currentDate = now || /* @__PURE__ */ new Date();
|
|
3589
|
+
const fiveYearsAgo = new Date(currentDate);
|
|
3590
|
+
fiveYearsAgo.setFullYear(currentDate.getFullYear() - 5);
|
|
3591
|
+
let skippedConditions = 0;
|
|
3592
|
+
const filteredConditions = [];
|
|
3593
|
+
for (const cond of resolvedConditions) {
|
|
3594
|
+
if (cond.recordedDate && new Date(cond.recordedDate) >= fiveYearsAgo) {
|
|
3595
|
+
filteredConditions.push(cond);
|
|
3596
|
+
} else {
|
|
3597
|
+
skippedConditions++;
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
filteredConditions.sort((a, b) => {
|
|
3178
3601
|
const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
|
|
3179
3602
|
const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
|
|
3180
3603
|
return dateB - dateA;
|
|
@@ -3184,27 +3607,35 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
3184
3607
|
<thead>
|
|
3185
3608
|
<tr>
|
|
3186
3609
|
<th>Problem</th>
|
|
3610
|
+
<th>Code (System)</th>
|
|
3187
3611
|
<th>Onset Date</th>
|
|
3188
3612
|
<th>Recorded Date</th>
|
|
3189
3613
|
<th>Resolved Date</th>
|
|
3614
|
+
<th>Source</th>
|
|
3190
3615
|
</tr>
|
|
3191
3616
|
</thead>
|
|
3192
3617
|
<tbody>`;
|
|
3193
3618
|
const addedConditionCodes = /* @__PURE__ */ new Set();
|
|
3194
|
-
for (const cond of
|
|
3195
|
-
const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3619
|
+
for (const cond of filteredConditions) {
|
|
3620
|
+
const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(cond.code));
|
|
3196
3621
|
if (!addedConditionCodes.has(conditionCode)) {
|
|
3197
3622
|
addedConditionCodes.add(conditionCode);
|
|
3198
3623
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
3199
3624
|
<td class="Name">${conditionCode}</td>
|
|
3625
|
+
<td class="CodeSystem">${templateUtilities.codeableConceptCoding(cond.code)}</td>
|
|
3200
3626
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
3201
3627
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
3202
3628
|
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
3629
|
+
<td class="Source">${templateUtilities.getOwnerTag(cond)}</td>
|
|
3203
3630
|
</tr>`;
|
|
3204
3631
|
}
|
|
3205
3632
|
}
|
|
3206
3633
|
html += `</tbody>
|
|
3207
3634
|
</table>`;
|
|
3635
|
+
if (skippedConditions > 0) {
|
|
3636
|
+
html += `
|
|
3637
|
+
<p><em>${skippedConditions} additional past illnesses older than 5 years ago are present</em></p>`;
|
|
3638
|
+
}
|
|
3208
3639
|
return html;
|
|
3209
3640
|
}
|
|
3210
3641
|
};
|
|
@@ -3234,6 +3665,7 @@ var PlanOfCareTemplate = class {
|
|
|
3234
3665
|
<th>Comments</th>
|
|
3235
3666
|
<th>Planned Start</th>
|
|
3236
3667
|
<th>Planned End</th>
|
|
3668
|
+
<th>Source</th>
|
|
3237
3669
|
</tr>
|
|
3238
3670
|
</thead>
|
|
3239
3671
|
<tbody>`;
|
|
@@ -3245,6 +3677,7 @@ var PlanOfCareTemplate = class {
|
|
|
3245
3677
|
<td>${templateUtilities.concat(cp.note, "text")}</td>
|
|
3246
3678
|
<td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
|
|
3247
3679
|
<td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
|
|
3680
|
+
<td>${templateUtilities.getOwnerTag(cp)}</td>
|
|
3248
3681
|
</tr>`;
|
|
3249
3682
|
}
|
|
3250
3683
|
html += `
|
|
@@ -3270,6 +3703,7 @@ var PlanOfCareTemplate = class {
|
|
|
3270
3703
|
<th>Created</th>
|
|
3271
3704
|
<th>Planned Start</th>
|
|
3272
3705
|
<th>Planned End</th>
|
|
3706
|
+
<th>Source</th>
|
|
3273
3707
|
</tr>
|
|
3274
3708
|
</thead>
|
|
3275
3709
|
<tbody>`;
|
|
@@ -3287,10 +3721,11 @@ var PlanOfCareTemplate = class {
|
|
|
3287
3721
|
isSummaryCreated = true;
|
|
3288
3722
|
html += `
|
|
3289
3723
|
<tr>
|
|
3290
|
-
<td>${data["CarePlan Name"] ?? "
|
|
3291
|
-
<td>${templateUtilities.renderTime(data["created"], timezone) ?? "
|
|
3292
|
-
<td>${templateUtilities.renderTime(data["period.start"], timezone) ?? "
|
|
3293
|
-
<td>${templateUtilities.renderTime(data["period.end"], timezone) ?? "
|
|
3724
|
+
<td>${data["CarePlan Name"] ?? ""}</td>
|
|
3725
|
+
<td>${templateUtilities.renderTime(data["created"], timezone) ?? ""}</td>
|
|
3726
|
+
<td>${templateUtilities.renderTime(data["period.start"], timezone) ?? ""}</td>
|
|
3727
|
+
<td>${templateUtilities.renderTime(data["period.end"], timezone) ?? ""}</td>
|
|
3728
|
+
<td>${data["source"] ?? ""}</td>
|
|
3294
3729
|
</tr>`;
|
|
3295
3730
|
}
|
|
3296
3731
|
}
|
|
@@ -3321,77 +3756,54 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
3321
3756
|
*/
|
|
3322
3757
|
static generateStaticNarrative(resources, timezone) {
|
|
3323
3758
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3324
|
-
let html =
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
|
|
3336
|
-
const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
|
|
3337
|
-
return dateB - dateA;
|
|
3759
|
+
let html = "";
|
|
3760
|
+
let functionalObservations = resources.filter((r) => r.resourceType === "Observation").filter((r) => {
|
|
3761
|
+
const hasFunctionalLoinc = r.code?.coding?.some(
|
|
3762
|
+
(c) => c.system?.toLowerCase().includes("loinc") && c.code === "47420-5"
|
|
3763
|
+
);
|
|
3764
|
+
const hasFunctionalCategory = r.category?.some(
|
|
3765
|
+
(cat) => cat.coding?.some(
|
|
3766
|
+
(c) => c.code === "functional-status" || c.display?.toLowerCase().includes("functional")
|
|
3767
|
+
)
|
|
3768
|
+
);
|
|
3769
|
+
return hasFunctionalLoinc || hasFunctionalCategory;
|
|
3338
3770
|
});
|
|
3339
|
-
|
|
3340
|
-
const
|
|
3341
|
-
|
|
3342
|
-
return dateB - dateA;
|
|
3771
|
+
functionalObservations = functionalObservations.sort((a, b) => {
|
|
3772
|
+
const getObsDate = (obs) => obs.effectiveDateTime ? new Date(obs.effectiveDateTime).getTime() : obs.issued ? new Date(obs.issued).getTime() : 0;
|
|
3773
|
+
return getObsDate(b) - getObsDate(a);
|
|
3343
3774
|
});
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
const
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
}
|
|
3775
|
+
let clinicalImpressions = resources.filter((r) => r.resourceType === "ClinicalImpression").filter((r) => r.status === "completed");
|
|
3776
|
+
clinicalImpressions = clinicalImpressions.sort((a, b) => {
|
|
3777
|
+
const getImpressionDate = (ci) => ci.effectiveDateTime ? new Date(ci.effectiveDateTime).getTime() : ci.effectivePeriod?.end ? new Date(ci.effectivePeriod.end).getTime() : ci.date ? new Date(ci.date).getTime() : 0;
|
|
3778
|
+
return getImpressionDate(b) - getImpressionDate(a);
|
|
3779
|
+
});
|
|
3780
|
+
if (functionalObservations.length > 0) {
|
|
3781
|
+
html += `<table><thead><tr><th>Observation</th><th>Value</th><th>Date</th><th>Interpretation</th><th>Comments</th></tr></thead><tbody>`;
|
|
3782
|
+
for (const obs of functionalObservations) {
|
|
3783
|
+
const observation = obs;
|
|
3784
|
+
const obsName = templateUtilities.codeableConceptDisplay(observation.code);
|
|
3785
|
+
const value = templateUtilities.extractObservationValue(observation);
|
|
3786
|
+
const date = observation.effectiveDateTime ? templateUtilities.renderDate(observation.effectiveDateTime) : observation.issued ? templateUtilities.renderDate(observation.issued) : "";
|
|
3787
|
+
const interpretation = observation.interpretation ? templateUtilities.codeableConceptDisplay(observation.interpretation[0]) : "";
|
|
3788
|
+
const comments = observation.comment || observation.note?.map((n) => n.text).join("; ") || "";
|
|
3789
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(observation)}">
|
|
3790
|
+
<td>${obsName}</td>
|
|
3791
|
+
<td>${value ?? ""}</td>
|
|
3792
|
+
<td>${date}</td>
|
|
3793
|
+
<td>${interpretation}</td>
|
|
3794
|
+
<td>${comments}</td>
|
|
3795
|
+
</tr>`;
|
|
3366
3796
|
}
|
|
3367
|
-
html += `</tbody
|
|
3368
|
-
</table>`;
|
|
3797
|
+
html += `</tbody></table>`;
|
|
3369
3798
|
}
|
|
3370
3799
|
if (clinicalImpressions.length > 0) {
|
|
3371
|
-
html += `<
|
|
3372
|
-
<table>
|
|
3373
|
-
<thead>
|
|
3374
|
-
<tr>
|
|
3375
|
-
<th>Date</th>
|
|
3376
|
-
<th>Status</th>
|
|
3377
|
-
<th>Description</th>
|
|
3378
|
-
<th>Summary</th>
|
|
3379
|
-
<th>Findings</th>
|
|
3380
|
-
</tr>
|
|
3381
|
-
</thead>
|
|
3382
|
-
<tbody>`;
|
|
3800
|
+
html += `<table><thead><tr><th>Date</th><th>Status</th><th>Description</th><th>Summary</th><th>Findings</th></tr></thead><tbody>`;
|
|
3383
3801
|
for (const impression of clinicalImpressions) {
|
|
3384
3802
|
let formattedDate = "";
|
|
3385
3803
|
if (impression.effectiveDateTime) {
|
|
3386
|
-
formattedDate = templateUtilities.renderTime(
|
|
3387
|
-
impression.effectiveDateTime,
|
|
3388
|
-
timezone
|
|
3389
|
-
);
|
|
3804
|
+
formattedDate = templateUtilities.renderTime(impression.effectiveDateTime, timezone);
|
|
3390
3805
|
} else if (impression.effectivePeriod) {
|
|
3391
|
-
formattedDate = templateUtilities.renderPeriod(
|
|
3392
|
-
impression.effectivePeriod,
|
|
3393
|
-
timezone
|
|
3394
|
-
);
|
|
3806
|
+
formattedDate = templateUtilities.renderPeriod(impression.effectivePeriod, timezone);
|
|
3395
3807
|
} else if (impression.date) {
|
|
3396
3808
|
formattedDate = templateUtilities.renderDate(impression.date);
|
|
3397
3809
|
}
|
|
@@ -3399,23 +3811,24 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
3399
3811
|
if (impression.finding && impression.finding.length > 0) {
|
|
3400
3812
|
findingsHtml = "<ul>";
|
|
3401
3813
|
for (const finding of impression.finding) {
|
|
3402
|
-
const findingText = finding.itemCodeableConcept ? templateUtilities.
|
|
3814
|
+
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConceptDisplay(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
3403
3815
|
const cause = finding.basis || "";
|
|
3404
3816
|
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
3405
3817
|
}
|
|
3406
3818
|
findingsHtml += "</ul>";
|
|
3407
3819
|
}
|
|
3408
|
-
html +=
|
|
3409
|
-
<
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
</tr>`;
|
|
3820
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(impression)}">
|
|
3821
|
+
<td>${formattedDate}</td>
|
|
3822
|
+
<td>${impression.status || ""}</td>
|
|
3823
|
+
<td>${impression.description || ""}</td>
|
|
3824
|
+
<td>${impression.summary || ""}</td>
|
|
3825
|
+
<td>${findingsHtml}</td>
|
|
3826
|
+
</tr>`;
|
|
3416
3827
|
}
|
|
3417
|
-
html += `</tbody
|
|
3418
|
-
|
|
3828
|
+
html += `</tbody></table>`;
|
|
3829
|
+
}
|
|
3830
|
+
if (functionalObservations.length === 0 && clinicalImpressions.length === 0) {
|
|
3831
|
+
html += `<p>No functional status information available.</p>`;
|
|
3419
3832
|
}
|
|
3420
3833
|
return html;
|
|
3421
3834
|
}
|
|
@@ -3440,34 +3853,109 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
3440
3853
|
*/
|
|
3441
3854
|
static generateStaticNarrative(resources, timezone) {
|
|
3442
3855
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3443
|
-
const
|
|
3444
|
-
|
|
3856
|
+
const pregnancyHistoryFilter = IPSSectionResourceFilters["HistoryOfPregnancySection"];
|
|
3857
|
+
const filteredResources = pregnancyHistoryFilter ? resources.filter(pregnancyHistoryFilter) : resources;
|
|
3858
|
+
const observations = filteredResources.filter((r) => r.resourceType === "Observation");
|
|
3859
|
+
const conditions = filteredResources.filter((r) => r.resourceType === "Condition");
|
|
3860
|
+
const EDD_LOINC = "11778-8";
|
|
3861
|
+
const pregnancyStatusCodes = Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS);
|
|
3862
|
+
const pregnancyOutcomeCodes = Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME);
|
|
3863
|
+
const pregnancyStatusObs = observations.filter((obs) => obs.code?.coding?.some((c) => c.code && pregnancyStatusCodes.includes(c.code))).sort((a, b) => {
|
|
3445
3864
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
3446
3865
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
3447
|
-
return
|
|
3866
|
+
return dateB && dateA ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
3867
|
+
})[0];
|
|
3868
|
+
const eddObs = observations.filter((obs) => obs.code?.coding?.some((c) => c.code === EDD_LOINC)).sort((a, b) => {
|
|
3869
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
3870
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
3871
|
+
return dateB && dateA ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
3872
|
+
})[0];
|
|
3873
|
+
const historyObs = observations.filter(
|
|
3874
|
+
(obs) => obs.code?.coding?.some((c) => c.code && pregnancyOutcomeCodes.includes(c.code)) || obs.valueCodeableConcept?.coding?.some((c) => c.code && pregnancyOutcomeCodes.includes(c.code))
|
|
3875
|
+
).sort((a, b) => {
|
|
3876
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
3877
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
3878
|
+
return dateB && dateA ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
3448
3879
|
});
|
|
3880
|
+
if (!pregnancyStatusObs && !eddObs && historyObs.length === 0 && conditions.length === 0) {
|
|
3881
|
+
return `<p>No history of pregnancy found.</p>`;
|
|
3882
|
+
}
|
|
3449
3883
|
let html = `
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3884
|
+
<table>
|
|
3885
|
+
<thead>
|
|
3886
|
+
<tr>
|
|
3887
|
+
<th>Result</th>
|
|
3888
|
+
<th>Code (System)</th>
|
|
3889
|
+
<th>Comments</th>
|
|
3890
|
+
<th>Date</th>
|
|
3891
|
+
<th>Source</th>
|
|
3892
|
+
</tr>
|
|
3893
|
+
</thead>
|
|
3894
|
+
<tbody>`;
|
|
3895
|
+
function renderRow({ id, result, comments, date, codeSystem, owner }) {
|
|
3461
3896
|
html += `
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3897
|
+
<tr id="${id}">
|
|
3898
|
+
<td class="Result">${result}</td>
|
|
3899
|
+
<td class="CodeSystem">${codeSystem}</td>
|
|
3900
|
+
<td class="Comments">${comments}</td>
|
|
3901
|
+
<td class="Date">${date}</td>
|
|
3902
|
+
<td class="Source">${owner}</td>
|
|
3903
|
+
</tr>`;
|
|
3904
|
+
}
|
|
3905
|
+
const rowResources = [];
|
|
3906
|
+
if (pregnancyStatusObs) {
|
|
3907
|
+
const date = pregnancyStatusObs.effectiveDateTime || pregnancyStatusObs.effectivePeriod?.start;
|
|
3908
|
+
rowResources.push({ resource: pregnancyStatusObs, date, type: "status" });
|
|
3909
|
+
}
|
|
3910
|
+
if (eddObs) {
|
|
3911
|
+
const date = eddObs.effectiveDateTime || eddObs.effectivePeriod?.start;
|
|
3912
|
+
rowResources.push({ resource: eddObs, date, type: "edd" });
|
|
3913
|
+
}
|
|
3914
|
+
for (const obs of historyObs) {
|
|
3915
|
+
const date = obs.effectiveDateTime || obs.effectivePeriod?.start;
|
|
3916
|
+
rowResources.push({ resource: obs, date, type: "history" });
|
|
3917
|
+
}
|
|
3918
|
+
for (const cond of conditions) {
|
|
3919
|
+
const condition = cond;
|
|
3920
|
+
const date = condition.onsetDateTime || condition.onsetPeriod?.start;
|
|
3921
|
+
rowResources.push({ resource: condition, date, type: "condition" });
|
|
3922
|
+
}
|
|
3923
|
+
rowResources.sort((a, b) => {
|
|
3924
|
+
if (!a.date && !b.date) return 0;
|
|
3925
|
+
if (!a.date) return 1;
|
|
3926
|
+
if (!b.date) return -1;
|
|
3927
|
+
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
|
3928
|
+
});
|
|
3929
|
+
for (const { resource, date, type } of rowResources) {
|
|
3930
|
+
let result = "", comments = "", dateStr = "", codeSystem = "";
|
|
3931
|
+
const id = templateUtilities.narrativeLinkId(resource);
|
|
3932
|
+
if (type === "status") {
|
|
3933
|
+
result = templateUtilities.renderTextAsHtml(templateUtilities.extractPregnancyStatus(resource));
|
|
3934
|
+
comments = templateUtilities.renderNotes(resource.note, timezone);
|
|
3935
|
+
dateStr = date ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(date, timezone)) : "";
|
|
3936
|
+
codeSystem = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptCoding(resource.code));
|
|
3937
|
+
} else if (type === "edd") {
|
|
3938
|
+
result = "Estimated Delivery Date: " + templateUtilities.renderTextAsHtml(templateUtilities.extractObservationSummaryValue(resource, timezone));
|
|
3939
|
+
comments = templateUtilities.renderNotes(resource.note, timezone);
|
|
3940
|
+
dateStr = date ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(date, timezone)) : "";
|
|
3941
|
+
codeSystem = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptCoding(resource.code));
|
|
3942
|
+
} else if (type === "history") {
|
|
3943
|
+
result = templateUtilities.renderTextAsHtml(templateUtilities.extractPregnancyStatus(resource));
|
|
3944
|
+
comments = templateUtilities.renderNotes(resource.note, timezone);
|
|
3945
|
+
dateStr = date ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(date, timezone)) : "";
|
|
3946
|
+
codeSystem = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptCoding(resource.code));
|
|
3947
|
+
} else if (type === "condition") {
|
|
3948
|
+
result = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(resource.code));
|
|
3949
|
+
comments = templateUtilities.renderNotes(resource.note, timezone);
|
|
3950
|
+
dateStr = date ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(date, timezone)) : "";
|
|
3951
|
+
codeSystem = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptCoding(resource.code));
|
|
3952
|
+
}
|
|
3953
|
+
const owner = templateUtilities.getOwnerTag(resource);
|
|
3954
|
+
renderRow({ id, result, comments, date: dateStr, codeSystem, owner });
|
|
3467
3955
|
}
|
|
3468
3956
|
html += `
|
|
3469
|
-
|
|
3470
|
-
|
|
3957
|
+
</tbody>
|
|
3958
|
+
</table>`;
|
|
3471
3959
|
return html;
|
|
3472
3960
|
}
|
|
3473
3961
|
};
|
|
@@ -3512,7 +4000,7 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
3512
4000
|
const consent = resourceItem;
|
|
3513
4001
|
html += `
|
|
3514
4002
|
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
3515
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
4003
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(consent.scope, "display"))}</td>
|
|
3516
4004
|
<td>${consent.status || ""}</td>
|
|
3517
4005
|
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
3518
4006
|
<td>${consent.dateTime || ""}</td>
|
|
@@ -3545,7 +4033,7 @@ var TypeScriptTemplateMapper = class {
|
|
|
3545
4033
|
resources,
|
|
3546
4034
|
timezone,
|
|
3547
4035
|
now
|
|
3548
|
-
) : templateClass.generateNarrative(resources, timezone);
|
|
4036
|
+
) : templateClass.generateNarrative(resources, timezone, now);
|
|
3549
4037
|
}
|
|
3550
4038
|
};
|
|
3551
4039
|
// Map of section types to their template classes
|
|
@@ -3824,6 +4312,12 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
3824
4312
|
if (sectionType === "Patient" /* PATIENT */) {
|
|
3825
4313
|
continue;
|
|
3826
4314
|
}
|
|
4315
|
+
const summaryIPSCompositionFilter = useSummaryCompositions ? IPSSectionResourceHelper.getSummaryIPSCompositionFilterForSection(sectionType) : void 0;
|
|
4316
|
+
const sectionIPSSummary = summaryIPSCompositionFilter ? resources.filter((resource) => summaryIPSCompositionFilter(resource)) : [];
|
|
4317
|
+
if (sectionIPSSummary.length > 0) {
|
|
4318
|
+
await this.makeSectionFromSummaryAsync(sectionType, sectionIPSSummary, resources, timezone);
|
|
4319
|
+
continue;
|
|
4320
|
+
}
|
|
3827
4321
|
const summaryCompositionFilter = useSummaryCompositions ? IPSSectionResourceHelper.getSummaryCompositionFilterForSection(sectionType) : void 0;
|
|
3828
4322
|
const sectionSummary = summaryCompositionFilter ? resources.filter((resource) => summaryCompositionFilter(resource)) : [];
|
|
3829
4323
|
if (sectionSummary.length > 0) {
|