@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.cjs
CHANGED
|
@@ -99,6 +99,13 @@ var PREGNANCY_LOINC_CODES = {
|
|
|
99
99
|
"33065-4": "Ectopic Pregnancy"
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
|
+
var PREGNANCY_SNOMED_CODES = [
|
|
103
|
+
// Add SNOMED CT codes for pregnancy history here, e.g.:
|
|
104
|
+
"72892002",
|
|
105
|
+
// Pregnancy (finding)
|
|
106
|
+
"77386006"
|
|
107
|
+
// History of pregnancy (situation)
|
|
108
|
+
];
|
|
102
109
|
var SOCIAL_HISTORY_LOINC_CODES = {
|
|
103
110
|
"72166-2": "Tobacco Use",
|
|
104
111
|
"74013-4": "Alcohol Use"
|
|
@@ -108,6 +115,29 @@ var BLOOD_PRESSURE_LOINC_CODES = {
|
|
|
108
115
|
SYSTOLIC: "8480-6",
|
|
109
116
|
DIASTOLIC: "8462-4"
|
|
110
117
|
};
|
|
118
|
+
var ESSENTIAL_LAB_PANELS = {
|
|
119
|
+
// Top 20 Most Ordered Panels
|
|
120
|
+
"24323-8": "Comprehensive Metabolic Panel (CMP)",
|
|
121
|
+
"24320-4": "Basic Metabolic Panel (BMP)",
|
|
122
|
+
"58410-2": "Complete Blood Count (CBC)",
|
|
123
|
+
"57021-8": "CBC with Differential",
|
|
124
|
+
"24331-1": "Lipid Panel",
|
|
125
|
+
"57698-3": "Lipid Panel with Direct LDL",
|
|
126
|
+
"24325-3": "Hepatic Function Panel",
|
|
127
|
+
"24362-6": "Renal Function Panel",
|
|
128
|
+
"24326-1": "Electrolyte Panel",
|
|
129
|
+
"24348-5": "Thyroid Panel",
|
|
130
|
+
"24356-8": "Urinalysis Complete",
|
|
131
|
+
"24352-7": "Iron Studies Panel",
|
|
132
|
+
"34714-6": "Coagulation Panel",
|
|
133
|
+
"24364-2": "Prenatal Panel",
|
|
134
|
+
"24108-3": "Acute Hepatitis Panel",
|
|
135
|
+
"24110-9": "Hepatitis B Panel",
|
|
136
|
+
"34574-4": "Arthritis Panel",
|
|
137
|
+
"24360-0": "Anemia Panel",
|
|
138
|
+
"80235-8": "Cardiac Markers Panel",
|
|
139
|
+
"69738-3": "CBC with Auto Differential"
|
|
140
|
+
};
|
|
111
141
|
|
|
112
142
|
// src/structures/ips_section_constants.ts
|
|
113
143
|
var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
|
|
@@ -132,34 +162,42 @@ var IPSSectionResourceFilters = {
|
|
|
132
162
|
// Only include completed immunizations
|
|
133
163
|
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Immunization" && resource.status === "completed" || resource.resourceType === "Organization",
|
|
134
164
|
// Only include vital sign Observations (category.coding contains 'vital-signs')
|
|
135
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => c
|
|
165
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => codingMatches(c, "vital-signs", c.system))),
|
|
136
166
|
// Includes DeviceUseStatement. Device is needed for linked device details
|
|
137
167
|
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
|
|
138
|
-
// Only include finalized diagnostic reports
|
|
139
|
-
["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) =>
|
|
168
|
+
// Only include finalized diagnostic reports and relevant observations
|
|
169
|
+
["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))),
|
|
140
170
|
// Only include completed procedures
|
|
141
171
|
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
|
|
142
172
|
// Only include social history Observations
|
|
143
|
-
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code
|
|
144
|
-
// Only include pregnancy history Observations
|
|
145
|
-
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code
|
|
146
|
-
// Only include
|
|
147
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "
|
|
173
|
+
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && codeableConceptMatches(resource.code, Object.keys(SOCIAL_HISTORY_LOINC_CODES), "http://loinc.org"),
|
|
174
|
+
// Only include pregnancy history Observations or relevant Conditions
|
|
175
|
+
["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")),
|
|
176
|
+
// Only include Observations with LOINC 47420-5, category 'functional-status', or category display containing 'functional', and completed ClinicalImpressions
|
|
177
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Observation" && (codeableConceptMatches(resource.code, "47420-5", "http://loinc.org") || resource.category?.some(
|
|
178
|
+
(cat) => cat.coding?.some(
|
|
179
|
+
(c) => c.code === "functional-status" && c.system === "http://terminology.hl7.org/CodeSystem/observation-category" || typeof c.display === "string" && c.display.toLowerCase().includes("functional")
|
|
180
|
+
)
|
|
181
|
+
)) || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
|
|
148
182
|
// Only include resolved medical history Conditions
|
|
149
183
|
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
|
|
150
184
|
// Only include active care plans
|
|
151
185
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
152
186
|
// Only include active advance directives (Consent resources)
|
|
153
|
-
|
|
187
|
+
// TODO: disable this until we right logic to get these
|
|
188
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: () => false
|
|
154
189
|
};
|
|
155
190
|
var IPSSectionSummaryCompositionFilter = {
|
|
156
|
-
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
157
|
-
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
158
|
-
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
159
|
-
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
160
|
-
["MedicationSummarySection" /* MEDICATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
191
|
+
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "allergy_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
192
|
+
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "vital_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
193
|
+
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "careplan_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
194
|
+
["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "immunization_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
195
|
+
["MedicationSummarySection" /* MEDICATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "medication_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
161
196
|
// [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)),
|
|
162
|
-
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c
|
|
197
|
+
["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "procedure_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
|
|
198
|
+
};
|
|
199
|
+
var IPSSectionSummaryIPSCompositionFilter = {
|
|
200
|
+
["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")
|
|
163
201
|
};
|
|
164
202
|
var IPSSectionResourceHelper = class {
|
|
165
203
|
static getResourceFilterForSection(section) {
|
|
@@ -173,10 +211,50 @@ var IPSSectionResourceHelper = class {
|
|
|
173
211
|
static getSummaryCompositionFilterForSection(section) {
|
|
174
212
|
return IPSSectionSummaryCompositionFilter[section];
|
|
175
213
|
}
|
|
214
|
+
static getSummaryIPSCompositionFilterForSection(section) {
|
|
215
|
+
return IPSSectionSummaryIPSCompositionFilter[section];
|
|
216
|
+
}
|
|
176
217
|
};
|
|
218
|
+
function codingMatches(coding, code, system) {
|
|
219
|
+
if (!coding || !coding.system) return false;
|
|
220
|
+
if (Array.isArray(code)) {
|
|
221
|
+
return coding.system === system && code.includes(coding.code ?? "");
|
|
222
|
+
}
|
|
223
|
+
return coding.system === system && coding.code === code;
|
|
224
|
+
}
|
|
225
|
+
function codeableConceptMatches(codeableConcept, code, system) {
|
|
226
|
+
if (!codeableConcept || !Array.isArray(codeableConcept.coding)) return false;
|
|
227
|
+
return codeableConcept.coding.some((coding) => codingMatches(coding, code, system));
|
|
228
|
+
}
|
|
177
229
|
|
|
178
230
|
// src/narratives/templates/typescript/TemplateUtilities.ts
|
|
179
231
|
var import_luxon = require("luxon");
|
|
232
|
+
|
|
233
|
+
// src/structures/codingSystemDisplayNames.ts
|
|
234
|
+
var CODING_SYSTEM_DISPLAY_NAMES = {
|
|
235
|
+
"http://snomed.info/sct": "SNOMED CT",
|
|
236
|
+
"http://loinc.org": "LOINC",
|
|
237
|
+
"http://hl7.org/fhir/sid/icd-10": "ICD-10",
|
|
238
|
+
"http://hl7.org/fhir/sid/icd-10-cm": "ICD-10-CM",
|
|
239
|
+
"http://hl7.org/fhir/sid/icd-9": "ICD-9",
|
|
240
|
+
"http://hl7.org/fhir/sid/cvx": "CVX",
|
|
241
|
+
"http://www.nlm.nih.gov/research/umls/rxnorm": "RxNorm",
|
|
242
|
+
"http://www.ama-assn.org/go/cpt": "CPT",
|
|
243
|
+
"http://unitsofmeasure.org": "UCUM",
|
|
244
|
+
"http://e-imo.com/products/problem-it": "IMO Problem IT",
|
|
245
|
+
"2.16.840.1.113883.6.285": "HCPCS Level II",
|
|
246
|
+
"https://fhir.cerner.com/4ff3b259-e48d-4066-8b35-a6a051f2802a/codeSet/72": "Cerner Code Set 72",
|
|
247
|
+
"http://hl7.org/fhir/sid/ndc": "NDC",
|
|
248
|
+
// Added mapping for NDC
|
|
249
|
+
"urn:oid:2.16.840.1.113883.12.292": "CVX",
|
|
250
|
+
"http://terminology.hl7.org/CodeSystem/data-absent-reason": "Data Absent Reason",
|
|
251
|
+
// Added mapping for Data Absent Reason
|
|
252
|
+
"2.16.840.1.113883.6.208": "NDDF"
|
|
253
|
+
// Add more as needed
|
|
254
|
+
};
|
|
255
|
+
var codingSystemDisplayNames_default = CODING_SYSTEM_DISPLAY_NAMES;
|
|
256
|
+
|
|
257
|
+
// src/narratives/templates/typescript/TemplateUtilities.ts
|
|
180
258
|
var TemplateUtilities = class {
|
|
181
259
|
/**
|
|
182
260
|
* Constructor to initialize the TemplateUtilities with a FHIR resources
|
|
@@ -186,47 +264,63 @@ var TemplateUtilities = class {
|
|
|
186
264
|
this.resources = resources;
|
|
187
265
|
}
|
|
188
266
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* @
|
|
267
|
+
* Returns the preferred coding from a list of codings.
|
|
268
|
+
* If a coding has an extension with url 'https://fhir.icanbwell.com/4_0_0/StructureDefinition/intelligence' and valueCode 'preferred', returns that coding.
|
|
269
|
+
* Otherwise, returns the first coding if it exists, else null.
|
|
270
|
+
* @param codings Array of coding objects
|
|
271
|
+
* @returns The preferred coding object or null
|
|
193
272
|
*/
|
|
194
|
-
|
|
195
|
-
if (!
|
|
196
|
-
|
|
273
|
+
getPreferredCoding(codings) {
|
|
274
|
+
if (!Array.isArray(codings) || codings.length === 0) return null;
|
|
275
|
+
for (const coding of codings) {
|
|
276
|
+
if (Array.isArray(coding.extension)) {
|
|
277
|
+
for (const ext of coding.extension) {
|
|
278
|
+
if (ext.url === "https://fhir.icanbwell.com/4_0_0/StructureDefinition/intelligence" && ext.valueCode === "preferred") {
|
|
279
|
+
return coding;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
197
283
|
}
|
|
284
|
+
return codings[0] || null;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Returns the display value from a CodeableConcept
|
|
288
|
+
* @param cc - The CodeableConcept object
|
|
289
|
+
* @param field - Optional specific field to extract (e.g., 'text', 'display', 'code')
|
|
290
|
+
* @returns Display string or empty string
|
|
291
|
+
*/
|
|
292
|
+
codeableConceptDisplay(cc, field) {
|
|
293
|
+
if (!cc) return "";
|
|
198
294
|
if (field) {
|
|
199
295
|
if (cc[field]) {
|
|
200
296
|
return cc[field];
|
|
201
|
-
} else if (cc.coding && cc.coding
|
|
202
|
-
|
|
297
|
+
} else if (cc.coding && cc.coding.length > 0) {
|
|
298
|
+
const preferredCoding = this.getPreferredCoding(cc.coding);
|
|
299
|
+
if (preferredCoding && preferredCoding[field]) {
|
|
300
|
+
return preferredCoding[field];
|
|
301
|
+
}
|
|
203
302
|
}
|
|
204
303
|
}
|
|
205
|
-
if (cc.text)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (
|
|
209
|
-
return cc.coding[0].display;
|
|
210
|
-
} else if (cc.coding[0].code) {
|
|
211
|
-
return cc.coding[0].code;
|
|
212
|
-
}
|
|
304
|
+
if (cc.text) return cc.text;
|
|
305
|
+
if (cc.coding && cc.coding.length > 0) {
|
|
306
|
+
const preferredCoding = this.getPreferredCoding(cc.coding);
|
|
307
|
+
if (preferredCoding && preferredCoding.display) return preferredCoding.display;
|
|
213
308
|
}
|
|
214
309
|
return "";
|
|
215
310
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
});
|
|
229
|
-
return resource ? resource : null;
|
|
311
|
+
/**
|
|
312
|
+
* Returns the code and system from a CodeableConcept
|
|
313
|
+
* @param cc - The CodeableConcept object
|
|
314
|
+
* @returns Object with code and system, or empty strings if not present
|
|
315
|
+
*/
|
|
316
|
+
codeableConceptCoding(cc) {
|
|
317
|
+
if (!cc || !cc.coding || !cc.coding.length) return "";
|
|
318
|
+
const preferredCoding = this.getPreferredCoding(cc.coding);
|
|
319
|
+
if (!preferredCoding) return "";
|
|
320
|
+
const code = preferredCoding.code || "";
|
|
321
|
+
const system = preferredCoding.system || "";
|
|
322
|
+
const systemDisplay = codingSystemDisplayNames_default[system] || system;
|
|
323
|
+
return code ? `${code} (${systemDisplay})` : "";
|
|
230
324
|
}
|
|
231
325
|
/**
|
|
232
326
|
* Renders a Device reference
|
|
@@ -274,7 +368,7 @@ var TemplateUtilities = class {
|
|
|
274
368
|
return "";
|
|
275
369
|
}
|
|
276
370
|
if (medicationType.medicationCodeableConcept) {
|
|
277
|
-
return this.
|
|
371
|
+
return this.codeableConceptDisplay(medicationType.medicationCodeableConcept);
|
|
278
372
|
} else if (medicationType.medicationReference) {
|
|
279
373
|
return this.renderMedicationRef(medicationType.medicationReference);
|
|
280
374
|
}
|
|
@@ -299,7 +393,7 @@ var TemplateUtilities = class {
|
|
|
299
393
|
*/
|
|
300
394
|
renderMedicationCode(medication) {
|
|
301
395
|
if (medication && medication.code) {
|
|
302
|
-
return this.renderTextAsHtml(this.
|
|
396
|
+
return this.renderTextAsHtml(this.codeableConceptDisplay(medication.code));
|
|
303
397
|
}
|
|
304
398
|
return "";
|
|
305
399
|
}
|
|
@@ -468,7 +562,7 @@ var TemplateUtilities = class {
|
|
|
468
562
|
*/
|
|
469
563
|
firstFromCodeableConceptList(list) {
|
|
470
564
|
if (list && Array.isArray(list) && list[0]) {
|
|
471
|
-
return this.renderTextAsHtml(this.
|
|
565
|
+
return this.renderTextAsHtml(this.codeableConceptDisplay(list[0]));
|
|
472
566
|
}
|
|
473
567
|
return "";
|
|
474
568
|
}
|
|
@@ -808,16 +902,28 @@ var TemplateUtilities = class {
|
|
|
808
902
|
return this.renderMedicationCode(medicationSource);
|
|
809
903
|
}
|
|
810
904
|
if (typeof medicationSource === "object" && ("coding" in medicationSource || "text" in medicationSource)) {
|
|
811
|
-
return this.
|
|
905
|
+
return this.codeableConceptDisplay(medicationSource);
|
|
812
906
|
}
|
|
813
907
|
if (typeof medicationSource === "object" && "reference" in medicationSource) {
|
|
814
908
|
const medication = this.resolveReference(medicationSource);
|
|
815
909
|
if (medication && medication.code) {
|
|
816
|
-
return this.
|
|
910
|
+
return this.codeableConceptDisplay(medication.code);
|
|
817
911
|
}
|
|
818
912
|
}
|
|
819
913
|
return "";
|
|
820
914
|
}
|
|
915
|
+
/**
|
|
916
|
+
* Returns the owner tag from the resource meta.security array.
|
|
917
|
+
* @param resource - FHIR resource with meta tag
|
|
918
|
+
* @returns The owner code if found, otherwise undefined
|
|
919
|
+
*/
|
|
920
|
+
getOwnerTag(resource) {
|
|
921
|
+
if (!resource?.meta?.security) return "";
|
|
922
|
+
const ownerEntry = resource.meta.security.find(
|
|
923
|
+
(sec) => sec.system === "https://www.icanbwell.com/owner" && !!sec.code
|
|
924
|
+
);
|
|
925
|
+
return ownerEntry?.code;
|
|
926
|
+
}
|
|
821
927
|
/**
|
|
822
928
|
* Public method to render plain text as HTML, escaping special characters and replacing newlines with <br />.
|
|
823
929
|
* This method should be used whenever displaying user-supplied or FHIR resource text in HTML to prevent XSS vulnerabilities
|
|
@@ -981,6 +1087,21 @@ var TemplateUtilities = class {
|
|
|
981
1087
|
const denominatorUnit = valueRatio.denominator.unit ? ` ${valueRatio.denominator.unit}` : "";
|
|
982
1088
|
return `${numerator}${numeratorUnit} / ${denominator}${denominatorUnit}`;
|
|
983
1089
|
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Finds the resource that matches the reference
|
|
1092
|
+
* @param ref - Reference to a resource
|
|
1093
|
+
* @returns The resource or null
|
|
1094
|
+
*/
|
|
1095
|
+
resolveReference(ref) {
|
|
1096
|
+
if (!ref || !this.resources) {
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
const refId = ref.reference?.split("/")[1];
|
|
1100
|
+
const refType = ref.reference?.split("/")[0];
|
|
1101
|
+
return this.resources.find(
|
|
1102
|
+
(resource) => resource.resourceType === refType && resource.id === refId
|
|
1103
|
+
) || null;
|
|
1104
|
+
}
|
|
984
1105
|
};
|
|
985
1106
|
|
|
986
1107
|
// src/constants.ts
|
|
@@ -1609,7 +1730,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
1609
1730
|
const uniqueLanguages = /* @__PURE__ */ new Set();
|
|
1610
1731
|
const preferredLanguages = /* @__PURE__ */ new Set();
|
|
1611
1732
|
patient.communication.forEach((comm) => {
|
|
1612
|
-
const language = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1733
|
+
const language = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(comm.language));
|
|
1613
1734
|
if (language) {
|
|
1614
1735
|
if (comm.preferred) {
|
|
1615
1736
|
preferredLanguages.add(language);
|
|
@@ -1656,14 +1777,18 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1656
1777
|
<thead>
|
|
1657
1778
|
<tr>
|
|
1658
1779
|
<th>Allergen</th>
|
|
1780
|
+
<th>Code (System)</th>
|
|
1659
1781
|
<th>Criticality</th>
|
|
1660
1782
|
<th>Recorded Date</th>
|
|
1783
|
+
<th>Source</th>
|
|
1661
1784
|
</tr>
|
|
1662
1785
|
</thead>
|
|
1663
1786
|
<tbody>`;
|
|
1664
1787
|
for (const resourceItem of resources) {
|
|
1665
1788
|
for (const rowData of resourceItem.section ?? []) {
|
|
1789
|
+
const sectionCodeableConcept = rowData.code;
|
|
1666
1790
|
const data = {};
|
|
1791
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
1667
1792
|
for (const columnData of rowData.section ?? []) {
|
|
1668
1793
|
switch (columnData.title) {
|
|
1669
1794
|
case "Allergen Name":
|
|
@@ -1675,6 +1800,9 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1675
1800
|
case "Recorded Date":
|
|
1676
1801
|
data["recordedDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1677
1802
|
break;
|
|
1803
|
+
case "Source":
|
|
1804
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1805
|
+
break;
|
|
1678
1806
|
default:
|
|
1679
1807
|
break;
|
|
1680
1808
|
}
|
|
@@ -1682,9 +1810,11 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1682
1810
|
isSummaryCreated = true;
|
|
1683
1811
|
html += `
|
|
1684
1812
|
<tr>
|
|
1685
|
-
<td>${data["allergen"] ?? "
|
|
1686
|
-
|
|
1687
|
-
<td>${
|
|
1813
|
+
<td>${data["allergen"] ?? ""}</td>
|
|
1814
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
1815
|
+
<td>${data["criticality"] ?? ""}</td>
|
|
1816
|
+
<td>${templateUtilities.renderTime(data["recordedDate"], timezone) ?? ""}</td>
|
|
1817
|
+
<td>${data["source"] ?? ""}</td>
|
|
1688
1818
|
</tr>`;
|
|
1689
1819
|
}
|
|
1690
1820
|
}
|
|
@@ -1732,10 +1862,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1732
1862
|
<tr>
|
|
1733
1863
|
<th>Allergen</th>
|
|
1734
1864
|
<th>Status</th>
|
|
1865
|
+
<th>Code (System)</th>
|
|
1735
1866
|
<th>Category</th>
|
|
1736
1867
|
<th>Reaction</th>
|
|
1737
1868
|
<th>Onset Date</th>
|
|
1738
1869
|
<th>Comments</th>
|
|
1870
|
+
<th>Source</th>
|
|
1739
1871
|
</tr>
|
|
1740
1872
|
</thead>
|
|
1741
1873
|
<tbody>`;
|
|
@@ -1759,11 +1891,13 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1759
1891
|
<tr>
|
|
1760
1892
|
<th>Allergen</th>
|
|
1761
1893
|
<th>Status</th>
|
|
1894
|
+
<th>Code (System)</th>
|
|
1762
1895
|
<th>Category</th>
|
|
1763
1896
|
<th>Reaction</th>
|
|
1764
1897
|
<th>Onset Date</th>
|
|
1765
1898
|
<th>Comments</th>
|
|
1766
1899
|
<th>Resolved Date</th>
|
|
1900
|
+
<th>Source</th>
|
|
1767
1901
|
</tr>
|
|
1768
1902
|
</thead>
|
|
1769
1903
|
<tbody>`;
|
|
@@ -1794,14 +1928,16 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1794
1928
|
for (const allergy of allergies) {
|
|
1795
1929
|
html += `
|
|
1796
1930
|
<tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
|
|
1797
|
-
<td class="Name"><span class="AllergenName">${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1798
|
-
<td class="Status">${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1799
|
-
<td class="
|
|
1800
|
-
<td class="
|
|
1801
|
-
<td class="
|
|
1802
|
-
<td class="
|
|
1931
|
+
<td class="Name"><span class="AllergenName">${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(allergy.code))}</span></td>
|
|
1932
|
+
<td class="Status">${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(allergy.clinicalStatus)) || ""}</td>
|
|
1933
|
+
<td class="CodeSystem">${templateUtilities.codeableConceptCoding(allergy.code)}</td>
|
|
1934
|
+
<td class="Category">${templateUtilities.renderTextAsHtml(templateUtilities.safeConcat(allergy.category)) || ""}</td>
|
|
1935
|
+
<td class="Reaction">${templateUtilities.renderTextAsHtml(templateUtilities.concatReactionManifestation(allergy.reaction)) || ""}</td>
|
|
1936
|
+
<td class="OnsetDate">${templateUtilities.renderTextAsHtml(templateUtilities.renderTime(allergy.onsetDateTime, timezone)) || ""}</td>
|
|
1937
|
+
<td class="Comments">${templateUtilities.renderNotes(allergy.note, timezone, { styled: true, warning: true })}</td>
|
|
1938
|
+
<td class="Source">${templateUtilities.getOwnerTag(allergy)}</td>`;
|
|
1803
1939
|
if (includeResolved) {
|
|
1804
|
-
let endDate = "
|
|
1940
|
+
let endDate = "";
|
|
1805
1941
|
if (allergy.extension && Array.isArray(allergy.extension)) {
|
|
1806
1942
|
const endDateExt = allergy.extension.find(
|
|
1807
1943
|
(ext) => ext.url === "http://hl7.org/fhir/StructureDefinition/allergyintolerance-resolutionDate"
|
|
@@ -1825,10 +1961,11 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1825
1961
|
* Generate HTML narrative for Medication resources
|
|
1826
1962
|
* @param resources - FHIR Medication resources
|
|
1827
1963
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
1964
|
+
* @param now - Optional current date to use for calculations (defaults to new Date())
|
|
1828
1965
|
* @returns HTML string for rendering
|
|
1829
1966
|
*/
|
|
1830
|
-
generateNarrative(resources, timezone) {
|
|
1831
|
-
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
|
|
1967
|
+
generateNarrative(resources, timezone, now) {
|
|
1968
|
+
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone, now);
|
|
1832
1969
|
}
|
|
1833
1970
|
/**
|
|
1834
1971
|
* Generate HTML narrative for Medication resources using summary
|
|
@@ -1841,23 +1978,27 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1841
1978
|
const templateUtilities = new TemplateUtilities(resources);
|
|
1842
1979
|
let isSummaryCreated = false;
|
|
1843
1980
|
const currentDate = now || /* @__PURE__ */ new Date();
|
|
1844
|
-
const
|
|
1981
|
+
const twoYearsAgo = new Date(currentDate.getFullYear(), currentDate.getMonth() - 24, currentDate.getDate());
|
|
1845
1982
|
let html = `
|
|
1846
1983
|
<div>
|
|
1847
1984
|
<table>
|
|
1848
1985
|
<thead>
|
|
1849
1986
|
<tr>
|
|
1850
1987
|
<th>Medication</th>
|
|
1988
|
+
<th>Code (System)</th>
|
|
1851
1989
|
<th>Status</th>
|
|
1852
1990
|
<th>Sig</th>
|
|
1853
1991
|
<th>Days of Supply</th>
|
|
1854
1992
|
<th>Refills</th>
|
|
1855
1993
|
<th>Start Date</th>
|
|
1994
|
+
<th>Source</th>
|
|
1856
1995
|
</tr>
|
|
1857
1996
|
</thead>
|
|
1858
1997
|
<tbody>`;
|
|
1998
|
+
let skippedMedications = 0;
|
|
1859
1999
|
for (const resourceItem of resources) {
|
|
1860
2000
|
for (const rowData of resourceItem.section ?? []) {
|
|
2001
|
+
const sectionCodeableConcept = rowData.code;
|
|
1861
2002
|
const data = {};
|
|
1862
2003
|
for (const columnData of rowData.section ?? []) {
|
|
1863
2004
|
switch (columnData.title) {
|
|
@@ -1882,6 +2023,9 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1882
2023
|
case "Authored On Date":
|
|
1883
2024
|
data["startDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1884
2025
|
break;
|
|
2026
|
+
case "Source":
|
|
2027
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2028
|
+
break;
|
|
1885
2029
|
default:
|
|
1886
2030
|
break;
|
|
1887
2031
|
}
|
|
@@ -1893,16 +2037,21 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1893
2037
|
startDateObj = void 0;
|
|
1894
2038
|
}
|
|
1895
2039
|
}
|
|
1896
|
-
if (data["status"] === "active" || startDateObj && startDateObj >=
|
|
2040
|
+
if (!(data["status"] === "active" || startDateObj && startDateObj >= twoYearsAgo)) {
|
|
2041
|
+
skippedMedications++;
|
|
2042
|
+
}
|
|
2043
|
+
if (data["status"] === "active" || startDateObj && startDateObj >= twoYearsAgo) {
|
|
1897
2044
|
isSummaryCreated = true;
|
|
1898
2045
|
html += `
|
|
1899
2046
|
<tr>
|
|
1900
2047
|
<td>${templateUtilities.renderTextAsHtml(data["medication"])}</td>
|
|
2048
|
+
<td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
|
|
1901
2049
|
<td>${templateUtilities.renderTextAsHtml(data["status"])}</td>
|
|
1902
2050
|
<td>${templateUtilities.renderTextAsHtml(data["sig-prescriber"] || data["sig-pharmacy"])}</td>
|
|
1903
2051
|
<td>${templateUtilities.renderTextAsHtml(data["daysOfSupply"])}</td>
|
|
1904
2052
|
<td>${templateUtilities.renderTextAsHtml(data["refills"])}</td>
|
|
1905
2053
|
<td>${templateUtilities.renderTime(data["startDate"], timezone)}</td>
|
|
2054
|
+
<td>${templateUtilities.renderTextAsHtml(data["source"])}</td>
|
|
1906
2055
|
</tr>`;
|
|
1907
2056
|
}
|
|
1908
2057
|
}
|
|
@@ -1911,7 +2060,14 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1911
2060
|
</tbody>
|
|
1912
2061
|
</table>
|
|
1913
2062
|
</div>`;
|
|
1914
|
-
|
|
2063
|
+
if (skippedMedications > 0) {
|
|
2064
|
+
html += `
|
|
2065
|
+
<p><em>${skippedMedications} additional medications older than 2 years ago are present</em></p>`;
|
|
2066
|
+
}
|
|
2067
|
+
if (isSummaryCreated || skippedMedications > 0) {
|
|
2068
|
+
return html;
|
|
2069
|
+
}
|
|
2070
|
+
return void 0;
|
|
1915
2071
|
}
|
|
1916
2072
|
/**
|
|
1917
2073
|
* Safely parse a date string and return a valid Date object or null
|
|
@@ -1929,21 +2085,42 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1929
2085
|
* Internal static implementation that actually generates the narrative
|
|
1930
2086
|
* @param resources - FHIR Medication resources
|
|
1931
2087
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2088
|
+
* @param now - Optional current date to use for calculations (defaults to new Date())
|
|
1932
2089
|
* @returns HTML string for rendering
|
|
1933
2090
|
*/
|
|
1934
|
-
|
|
1935
|
-
static generateStaticNarrative(resources, timezone) {
|
|
2091
|
+
static generateStaticNarrative(resources, timezone, now) {
|
|
1936
2092
|
const templateUtilities = new TemplateUtilities(resources);
|
|
1937
2093
|
let html = "";
|
|
1938
2094
|
const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
|
|
1939
2095
|
const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
|
|
1940
2096
|
const allActiveMedications = [];
|
|
2097
|
+
const currentDate = now || /* @__PURE__ */ new Date();
|
|
2098
|
+
const twoYearsAgo = new Date(currentDate);
|
|
2099
|
+
twoYearsAgo.setFullYear(currentDate.getFullYear() - 2);
|
|
2100
|
+
let skippedMedications = 0;
|
|
2101
|
+
const allMedications = [];
|
|
1941
2102
|
medicationRequests.forEach((mr) => {
|
|
1942
|
-
|
|
2103
|
+
allMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1943
2104
|
});
|
|
1944
2105
|
medicationStatements.forEach((ms) => {
|
|
1945
|
-
|
|
2106
|
+
allMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1946
2107
|
});
|
|
2108
|
+
for (const med of allMedications) {
|
|
2109
|
+
let dateString;
|
|
2110
|
+
if (med.type === "request") {
|
|
2111
|
+
const mr = med.resource;
|
|
2112
|
+
dateString = mr.dispenseRequest?.validityPeriod?.start || mr.authoredOn;
|
|
2113
|
+
} else {
|
|
2114
|
+
const ms = med.resource;
|
|
2115
|
+
dateString = ms.effectiveDateTime || ms.effectivePeriod?.start;
|
|
2116
|
+
}
|
|
2117
|
+
const dateObj = this.parseDate(dateString);
|
|
2118
|
+
if (!dateObj || dateObj < twoYearsAgo) {
|
|
2119
|
+
skippedMedications++;
|
|
2120
|
+
} else {
|
|
2121
|
+
allActiveMedications.push(med);
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
1947
2124
|
const sortMedications = (medications) => {
|
|
1948
2125
|
medications.sort((a, b) => {
|
|
1949
2126
|
let dateStringA;
|
|
@@ -1974,7 +2151,14 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1974
2151
|
sortMedications(allActiveMedications);
|
|
1975
2152
|
html += this.renderCombinedMedications(templateUtilities, allActiveMedications);
|
|
1976
2153
|
}
|
|
1977
|
-
|
|
2154
|
+
if (skippedMedications > 0) {
|
|
2155
|
+
html += `
|
|
2156
|
+
<p><em>${skippedMedications} additional medications older than 2 years ago are present</em></p>`;
|
|
2157
|
+
}
|
|
2158
|
+
if (allActiveMedications.length > 0 || skippedMedications > 0) {
|
|
2159
|
+
return html;
|
|
2160
|
+
}
|
|
2161
|
+
return "";
|
|
1978
2162
|
}
|
|
1979
2163
|
/**
|
|
1980
2164
|
* Extract MedicationRequest resources
|
|
@@ -2019,10 +2203,12 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
2019
2203
|
<tr>
|
|
2020
2204
|
<th>Type</th>
|
|
2021
2205
|
<th>Medication</th>
|
|
2206
|
+
<th>Code (System)</th>
|
|
2022
2207
|
<th>Sig</th>
|
|
2023
2208
|
<th>Dispense Quantity</th>
|
|
2024
2209
|
<th>Refills</th>
|
|
2025
2210
|
<th>Start Date</th>
|
|
2211
|
+
<th>Source</th>
|
|
2026
2212
|
</tr>
|
|
2027
2213
|
</thead>
|
|
2028
2214
|
<tbody>`;
|
|
@@ -2031,27 +2217,31 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
2031
2217
|
let type;
|
|
2032
2218
|
let medicationName;
|
|
2033
2219
|
let sig;
|
|
2034
|
-
let dispenseQuantity = "
|
|
2035
|
-
let refills = "
|
|
2036
|
-
let startDate = "
|
|
2220
|
+
let dispenseQuantity = "";
|
|
2221
|
+
let refills = "";
|
|
2222
|
+
let startDate = "";
|
|
2223
|
+
let codeSystemDisplay = "";
|
|
2037
2224
|
if (medication.type === "request") {
|
|
2038
2225
|
const mr = medication.resource;
|
|
2039
2226
|
type = "Request";
|
|
2040
2227
|
medicationName = templateUtilities.getMedicationName(
|
|
2041
2228
|
mr.medicationReference || mr.medicationCodeableConcept
|
|
2042
2229
|
);
|
|
2043
|
-
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "
|
|
2230
|
+
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "";
|
|
2044
2231
|
if (mr.dispenseRequest?.quantity) {
|
|
2045
2232
|
const quantity = mr.dispenseRequest.quantity;
|
|
2046
2233
|
if (quantity.value) {
|
|
2047
2234
|
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
2048
2235
|
}
|
|
2049
2236
|
}
|
|
2050
|
-
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "
|
|
2237
|
+
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "";
|
|
2051
2238
|
if (mr.dispenseRequest?.validityPeriod) {
|
|
2052
|
-
startDate = mr.dispenseRequest.validityPeriod.start || "
|
|
2239
|
+
startDate = mr.dispenseRequest.validityPeriod.start || "";
|
|
2053
2240
|
} else {
|
|
2054
|
-
startDate = mr.authoredOn || "
|
|
2241
|
+
startDate = mr.authoredOn || "";
|
|
2242
|
+
}
|
|
2243
|
+
if (mr.medicationCodeableConcept && mr.medicationCodeableConcept.coding && mr.medicationCodeableConcept.coding[0]) {
|
|
2244
|
+
codeSystemDisplay = templateUtilities.codeableConceptCoding(mr.medicationCodeableConcept);
|
|
2055
2245
|
}
|
|
2056
2246
|
} else {
|
|
2057
2247
|
const ms = medication.resource;
|
|
@@ -2059,21 +2249,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
2059
2249
|
medicationName = templateUtilities.getMedicationName(
|
|
2060
2250
|
ms.medicationReference || ms.medicationCodeableConcept
|
|
2061
2251
|
);
|
|
2062
|
-
sig = templateUtilities.concat(ms.dosage, "text") || "
|
|
2252
|
+
sig = templateUtilities.concat(ms.dosage, "text") || "";
|
|
2063
2253
|
if (ms.effectiveDateTime) {
|
|
2064
2254
|
startDate = ms.effectiveDateTime;
|
|
2065
2255
|
} else if (ms.effectivePeriod) {
|
|
2066
|
-
startDate = ms.effectivePeriod.start || "
|
|
2256
|
+
startDate = ms.effectivePeriod.start || "";
|
|
2257
|
+
}
|
|
2258
|
+
if (ms.medicationCodeableConcept && ms.medicationCodeableConcept.coding && ms.medicationCodeableConcept.coding[0]) {
|
|
2259
|
+
codeSystemDisplay = templateUtilities.codeableConceptCoding(ms.medicationCodeableConcept);
|
|
2067
2260
|
}
|
|
2068
2261
|
}
|
|
2069
2262
|
html += `
|
|
2070
2263
|
<tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
|
|
2071
2264
|
<td>${type}</td>
|
|
2072
2265
|
<td>${medicationName}<ul></ul></td>
|
|
2266
|
+
<td>${codeSystemDisplay}</td>
|
|
2073
2267
|
<td>${sig}</td>
|
|
2074
2268
|
<td>${dispenseQuantity}</td>
|
|
2075
2269
|
<td>${refills}</td>
|
|
2076
2270
|
<td>${startDate}</td>
|
|
2271
|
+
<td>${templateUtilities.getOwnerTag(medication.resource)}</td>
|
|
2077
2272
|
</tr>`;
|
|
2078
2273
|
}
|
|
2079
2274
|
html += `
|
|
@@ -2114,14 +2309,18 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2114
2309
|
<thead>
|
|
2115
2310
|
<tr>
|
|
2116
2311
|
<th>Immunization</th>
|
|
2312
|
+
<th>Code (System)</th>
|
|
2117
2313
|
<th>Status</th>
|
|
2118
2314
|
<th>Date</th>
|
|
2315
|
+
<th>Source</th>
|
|
2119
2316
|
</tr>
|
|
2120
2317
|
</thead>
|
|
2121
2318
|
<tbody>`;
|
|
2122
2319
|
for (const resourceItem of resources) {
|
|
2123
2320
|
for (const rowData of resourceItem.section ?? []) {
|
|
2321
|
+
const sectionCodeableConcept = rowData.code;
|
|
2124
2322
|
const data = {};
|
|
2323
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2125
2324
|
for (const columnData of rowData.section ?? []) {
|
|
2126
2325
|
switch (columnData.title) {
|
|
2127
2326
|
case "Immunization Name":
|
|
@@ -2133,6 +2332,9 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2133
2332
|
case "occurrenceDateTime":
|
|
2134
2333
|
data["occurrenceDateTime"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2135
2334
|
break;
|
|
2335
|
+
case "Source":
|
|
2336
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2337
|
+
break;
|
|
2136
2338
|
default:
|
|
2137
2339
|
break;
|
|
2138
2340
|
}
|
|
@@ -2141,9 +2343,11 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2141
2343
|
isSummaryCreated = true;
|
|
2142
2344
|
html += `
|
|
2143
2345
|
<tr>
|
|
2144
|
-
<td>${data["immunization"] ?? "
|
|
2145
|
-
<td>${data["
|
|
2146
|
-
<td>${
|
|
2346
|
+
<td>${data["immunization"] ?? ""}</td>
|
|
2347
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
2348
|
+
<td>${data["status"] ?? ""}</td>
|
|
2349
|
+
<td>${templateUtilities.renderTime(data["occurrenceDateTime"], timezone) ?? ""}</td>
|
|
2350
|
+
<td>${data["source"] ?? ""}</td>
|
|
2147
2351
|
</tr>`;
|
|
2148
2352
|
}
|
|
2149
2353
|
}
|
|
@@ -2167,12 +2371,14 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2167
2371
|
<thead>
|
|
2168
2372
|
<tr>
|
|
2169
2373
|
<th>Immunization</th>
|
|
2374
|
+
<th>Code (System)</th>
|
|
2170
2375
|
<th>Status</th>
|
|
2171
2376
|
<th>Dose Number</th>
|
|
2172
2377
|
<th>Manufacturer</th>
|
|
2173
2378
|
<th>Lot Number</th>
|
|
2174
2379
|
<th>Comments</th>
|
|
2175
2380
|
<th>Date</th>
|
|
2381
|
+
<th>Source</th>
|
|
2176
2382
|
</tr>
|
|
2177
2383
|
</thead>
|
|
2178
2384
|
<tbody>`;
|
|
@@ -2182,13 +2388,15 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2182
2388
|
const imm = resourceItem;
|
|
2183
2389
|
html += `
|
|
2184
2390
|
<tr id="${templateUtilities.narrativeLinkId(imm)}">
|
|
2185
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
2391
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(imm.vaccineCode))}</td>
|
|
2392
|
+
<td>${templateUtilities.codeableConceptCoding(imm.vaccineCode)}</td>
|
|
2186
2393
|
<td>${imm.status || ""}</td>
|
|
2187
2394
|
<td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
|
|
2188
2395
|
<td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
|
|
2189
2396
|
<td>${imm.lotNumber || ""}</td>
|
|
2190
2397
|
<td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
|
|
2191
2398
|
<td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
|
|
2399
|
+
<td>${templateUtilities.getOwnerTag(imm)}</td>
|
|
2192
2400
|
</tr>`;
|
|
2193
2401
|
}
|
|
2194
2402
|
}
|
|
@@ -2222,8 +2430,11 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
2222
2430
|
let html = ``;
|
|
2223
2431
|
const activeConditions = resources.map((entry) => entry) || [];
|
|
2224
2432
|
activeConditions.sort((a, b) => {
|
|
2225
|
-
|
|
2226
|
-
|
|
2433
|
+
if (!a.recordedDate && b.recordedDate) return -1;
|
|
2434
|
+
if (a.recordedDate && !b.recordedDate) return 1;
|
|
2435
|
+
if (!a.recordedDate && !b.recordedDate) return 0;
|
|
2436
|
+
const dateA = new Date(a.recordedDate).getTime();
|
|
2437
|
+
const dateB = new Date(b.recordedDate).getTime();
|
|
2227
2438
|
return dateB - dateA;
|
|
2228
2439
|
});
|
|
2229
2440
|
html += `
|
|
@@ -2231,22 +2442,28 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
2231
2442
|
<thead>
|
|
2232
2443
|
<tr>
|
|
2233
2444
|
<th>Problem</th>
|
|
2445
|
+
<th>Code (System)</th>
|
|
2234
2446
|
<th>Onset Date</th>
|
|
2235
2447
|
<th>Recorded Date</th>
|
|
2448
|
+
<th>Source</th>
|
|
2236
2449
|
</tr>
|
|
2237
2450
|
</thead>
|
|
2238
2451
|
<tbody>`;
|
|
2239
|
-
const
|
|
2452
|
+
const seenCodeAndSystems = /* @__PURE__ */ new Set();
|
|
2240
2453
|
for (const cond of activeConditions) {
|
|
2241
|
-
const
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
<td class="Name">${conditionCode}</td>
|
|
2246
|
-
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
2247
|
-
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
2248
|
-
</tr>`;
|
|
2454
|
+
const conditionDisplay = templateUtilities.codeableConceptDisplay(cond.code);
|
|
2455
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(cond.code);
|
|
2456
|
+
if (codeAndSystem && seenCodeAndSystems.has(codeAndSystem)) {
|
|
2457
|
+
continue;
|
|
2249
2458
|
}
|
|
2459
|
+
seenCodeAndSystems.add(codeAndSystem);
|
|
2460
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
2461
|
+
<td class="Name">${conditionDisplay}</td>
|
|
2462
|
+
<td class="CodeSystem">${codeAndSystem}</td>
|
|
2463
|
+
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
2464
|
+
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
2465
|
+
<td class="Source">${templateUtilities.getOwnerTag(cond)}</td>
|
|
2466
|
+
</tr>`;
|
|
2250
2467
|
}
|
|
2251
2468
|
html += `</tbody>
|
|
2252
2469
|
</table>`;
|
|
@@ -2279,16 +2496,19 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2279
2496
|
<table>
|
|
2280
2497
|
<thead>
|
|
2281
2498
|
<tr>
|
|
2282
|
-
<th>
|
|
2499
|
+
<th>Name</th>
|
|
2500
|
+
<th>Code (System)</th>
|
|
2283
2501
|
<th>Result</th>
|
|
2284
|
-
<th>Reference Range</th>
|
|
2285
2502
|
<th>Date</th>
|
|
2503
|
+
<th>Source</th>
|
|
2286
2504
|
</tr>
|
|
2287
2505
|
</thead>
|
|
2288
2506
|
<tbody>`;
|
|
2289
2507
|
for (const resourceItem of resources) {
|
|
2290
2508
|
for (const rowData of resourceItem.section ?? []) {
|
|
2509
|
+
const sectionCodeableConcept = rowData.code;
|
|
2291
2510
|
const data = {};
|
|
2511
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2292
2512
|
for (const columnData of rowData.section ?? []) {
|
|
2293
2513
|
const columnTitle = columnData.title;
|
|
2294
2514
|
if (columnTitle) {
|
|
@@ -2316,10 +2536,11 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2316
2536
|
isSummaryCreated = true;
|
|
2317
2537
|
html += `
|
|
2318
2538
|
<tr>
|
|
2319
|
-
<td>${data["Vital Name"] ?? "
|
|
2320
|
-
<td>${
|
|
2321
|
-
<td>${templateUtilities.
|
|
2322
|
-
<td>${templateUtilities.extractObservationSummaryEffectiveTime(data, timezone) ?? "
|
|
2539
|
+
<td>${data["Vital Name"] ?? ""}</td>
|
|
2540
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
2541
|
+
<td>${templateUtilities.extractObservationSummaryValue(data, timezone) ?? ""}</td>
|
|
2542
|
+
<td>${templateUtilities.extractObservationSummaryEffectiveTime(data, timezone) ?? ""}</td>
|
|
2543
|
+
<td>${data["Source"] ?? ""}</td>
|
|
2323
2544
|
</tr>`;
|
|
2324
2545
|
}
|
|
2325
2546
|
}
|
|
@@ -2347,26 +2568,30 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2347
2568
|
<table>
|
|
2348
2569
|
<thead>
|
|
2349
2570
|
<tr>
|
|
2350
|
-
<th>
|
|
2571
|
+
<th>Name</th>
|
|
2572
|
+
<th>Code (System)</th>
|
|
2351
2573
|
<th>Result</th>
|
|
2352
2574
|
<th>Unit</th>
|
|
2353
2575
|
<th>Interpretation</th>
|
|
2354
2576
|
<th>Component(s)</th>
|
|
2355
2577
|
<th>Comments</th>
|
|
2356
2578
|
<th>Date</th>
|
|
2579
|
+
<th>Source</th>
|
|
2357
2580
|
</tr>
|
|
2358
2581
|
</thead>
|
|
2359
2582
|
<tbody>`;
|
|
2360
2583
|
for (const obs of observations) {
|
|
2361
2584
|
html += `
|
|
2362
2585
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2363
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
2586
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code, "display"))}</td>
|
|
2587
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
2364
2588
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2365
2589
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
2366
2590
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
2367
2591
|
<td>${templateUtilities.renderComponent(obs.component)}</td>
|
|
2368
2592
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
2369
2593
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
2594
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
2370
2595
|
</tr>`;
|
|
2371
2596
|
}
|
|
2372
2597
|
html += `
|
|
@@ -2439,10 +2664,11 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2439
2664
|
* Generate HTML narrative for Diagnostic Results
|
|
2440
2665
|
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
2441
2666
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2667
|
+
* @param now - Optional current date for filtering
|
|
2442
2668
|
* @returns HTML string for rendering
|
|
2443
2669
|
*/
|
|
2444
|
-
generateNarrative(resources, timezone) {
|
|
2445
|
-
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
|
|
2670
|
+
generateNarrative(resources, timezone, now) {
|
|
2671
|
+
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone, now);
|
|
2446
2672
|
}
|
|
2447
2673
|
/**
|
|
2448
2674
|
* Helper function to format observation data fields
|
|
@@ -2655,6 +2881,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2655
2881
|
break;
|
|
2656
2882
|
case "valueRange.high.value":
|
|
2657
2883
|
targetData["valueRangeHighValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
2884
|
+
targetData["valueType"] = "valueRange";
|
|
2658
2885
|
break;
|
|
2659
2886
|
case "valueRange.high.unit":
|
|
2660
2887
|
targetData["valueRangeHighUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
@@ -2669,6 +2896,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2669
2896
|
break;
|
|
2670
2897
|
case "valueRatio.denominator.value":
|
|
2671
2898
|
targetData["valueRatioDenominatorValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
2899
|
+
targetData["valueType"] = "valueRatio";
|
|
2672
2900
|
break;
|
|
2673
2901
|
case "valueRatio.denominator.unit":
|
|
2674
2902
|
targetData["valueRatioDenominatorUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
@@ -2706,10 +2934,71 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2706
2934
|
* Generate HTML narrative for Diagnostic Results & Observation resources using summary
|
|
2707
2935
|
* @param resources - FHIR Composition resources
|
|
2708
2936
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2937
|
+
* @param now - Optional current date for filtering
|
|
2709
2938
|
* @returns HTML string for rendering
|
|
2710
2939
|
*/
|
|
2711
|
-
generateSummaryNarrative(resources, timezone) {
|
|
2940
|
+
generateSummaryNarrative(resources, timezone, now) {
|
|
2712
2941
|
const templateUtilities = new TemplateUtilities(resources);
|
|
2942
|
+
const currentDate = now || /* @__PURE__ */ new Date();
|
|
2943
|
+
const twoYearsAgo = new Date(currentDate);
|
|
2944
|
+
twoYearsAgo.setFullYear(currentDate.getFullYear() - 2);
|
|
2945
|
+
let skippedObservations = 0;
|
|
2946
|
+
let skippedDiagnosticReports = 0;
|
|
2947
|
+
for (const resourceItem of resources) {
|
|
2948
|
+
for (const rowData of resourceItem.section ?? []) {
|
|
2949
|
+
if (resourceItem.title === "Observation|Labs Summary Grouped by Lab Code") {
|
|
2950
|
+
for (const columnData of rowData.section ?? []) {
|
|
2951
|
+
if (columnData.text?.div === "Observation.component" && columnData.section) {
|
|
2952
|
+
for (const componentSection of columnData.section) {
|
|
2953
|
+
const componentData = {};
|
|
2954
|
+
for (const nestedColumn of componentSection.section ?? []) {
|
|
2955
|
+
this.extractSummaryObservationFields(nestedColumn, componentData, templateUtilities);
|
|
2956
|
+
}
|
|
2957
|
+
let compDate = void 0;
|
|
2958
|
+
if (componentData["effectiveDateTime"]) {
|
|
2959
|
+
compDate = new Date(componentData["effectiveDateTime"]);
|
|
2960
|
+
} else if (componentData["effectivePeriodStart"]) {
|
|
2961
|
+
compDate = new Date(componentData["effectivePeriodStart"]);
|
|
2962
|
+
}
|
|
2963
|
+
if (compDate && compDate < twoYearsAgo) {
|
|
2964
|
+
skippedObservations++;
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
} else {
|
|
2968
|
+
const data = {};
|
|
2969
|
+
this.extractSummaryObservationFields(columnData, data, templateUtilities);
|
|
2970
|
+
let obsDate = void 0;
|
|
2971
|
+
if (data["effectiveDateTime"]) {
|
|
2972
|
+
obsDate = new Date(data["effectiveDateTime"]);
|
|
2973
|
+
} else if (data["effectivePeriodStart"]) {
|
|
2974
|
+
obsDate = new Date(data["effectivePeriodStart"]);
|
|
2975
|
+
}
|
|
2976
|
+
if (obsDate && obsDate < twoYearsAgo) {
|
|
2977
|
+
skippedObservations++;
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
} else if (resourceItem.title === "DiagnosticReportLab Summary Grouped by DiagnosticReport|Lab Code") {
|
|
2982
|
+
let issuedDate = void 0;
|
|
2983
|
+
let status = void 0;
|
|
2984
|
+
let reportFound = false;
|
|
2985
|
+
for (const columnData of rowData.section ?? []) {
|
|
2986
|
+
if (columnData.title === "Issued Date") {
|
|
2987
|
+
issuedDate = columnData.text?.div ? new Date(columnData.text.div) : void 0;
|
|
2988
|
+
}
|
|
2989
|
+
if (columnData.title === "Status") {
|
|
2990
|
+
status = columnData.text?.div;
|
|
2991
|
+
}
|
|
2992
|
+
if (columnData.title === "Diagnostic Report Name") {
|
|
2993
|
+
reportFound = true;
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
if (status === "final" && issuedDate && issuedDate < twoYearsAgo && reportFound) {
|
|
2997
|
+
skippedDiagnosticReports++;
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
2713
3002
|
let html = `
|
|
2714
3003
|
<div>`;
|
|
2715
3004
|
let observationhtml = `
|
|
@@ -2718,10 +3007,12 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2718
3007
|
<table>
|
|
2719
3008
|
<thead>
|
|
2720
3009
|
<tr>
|
|
2721
|
-
<th>
|
|
3010
|
+
<th>Name</th>
|
|
3011
|
+
<th>Code (System)</th>
|
|
2722
3012
|
<th>Result</th>
|
|
2723
3013
|
<th>Reference Range</th>
|
|
2724
3014
|
<th>Date</th>
|
|
3015
|
+
<th>Source</th>
|
|
2725
3016
|
</tr>
|
|
2726
3017
|
</thead>
|
|
2727
3018
|
<tbody>`;
|
|
@@ -2734,6 +3025,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2734
3025
|
<th>Report</th>
|
|
2735
3026
|
<th>Performer</th>
|
|
2736
3027
|
<th>Issued</th>
|
|
3028
|
+
<th>Source</th>
|
|
2737
3029
|
</tr>
|
|
2738
3030
|
</thead>
|
|
2739
3031
|
<tbody>`;
|
|
@@ -2741,7 +3033,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2741
3033
|
const diagnosticReportAdded = /* @__PURE__ */ new Set();
|
|
2742
3034
|
for (const resourceItem of resources) {
|
|
2743
3035
|
for (const rowData of resourceItem.section ?? []) {
|
|
3036
|
+
const sectionCodeableConcept = rowData.code;
|
|
2744
3037
|
const data = {};
|
|
3038
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2745
3039
|
const components = [];
|
|
2746
3040
|
for (const columnData of rowData.section ?? []) {
|
|
2747
3041
|
if (resourceItem.title === "Observation|Labs Summary Grouped by Lab Code") {
|
|
@@ -2751,7 +3045,13 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2751
3045
|
for (const nestedColumn of componentSection.section ?? []) {
|
|
2752
3046
|
this.extractSummaryObservationFields(nestedColumn, componentData, templateUtilities);
|
|
2753
3047
|
}
|
|
2754
|
-
|
|
3048
|
+
let compDate = void 0;
|
|
3049
|
+
if (componentData["effectiveDateTime"]) {
|
|
3050
|
+
compDate = new Date(componentData["effectiveDateTime"]);
|
|
3051
|
+
} else if (componentData["effectivePeriodStart"]) {
|
|
3052
|
+
compDate = new Date(componentData["effectivePeriodStart"]);
|
|
3053
|
+
}
|
|
3054
|
+
if (compDate && compDate >= twoYearsAgo && Object.keys(componentData).length > 0) {
|
|
2755
3055
|
components.push(componentData);
|
|
2756
3056
|
}
|
|
2757
3057
|
}
|
|
@@ -2772,6 +3072,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2772
3072
|
case "Status":
|
|
2773
3073
|
data["status"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2774
3074
|
break;
|
|
3075
|
+
case "Source":
|
|
3076
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
3077
|
+
break;
|
|
2775
3078
|
default:
|
|
2776
3079
|
break;
|
|
2777
3080
|
}
|
|
@@ -2779,6 +3082,12 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2779
3082
|
}
|
|
2780
3083
|
if (resourceItem.title === "Observation|Labs Summary Grouped by Lab Code") {
|
|
2781
3084
|
let date = data["effectiveDateTime"] ? templateUtilities.renderTime(data["effectiveDateTime"], timezone) : "";
|
|
3085
|
+
let obsDate = void 0;
|
|
3086
|
+
if (data["effectiveDateTime"]) {
|
|
3087
|
+
obsDate = new Date(data["effectiveDateTime"]);
|
|
3088
|
+
} else if (data["effectivePeriodStart"]) {
|
|
3089
|
+
obsDate = new Date(data["effectivePeriodStart"]);
|
|
3090
|
+
}
|
|
2782
3091
|
if (!date && data["effectivePeriodStart"]) {
|
|
2783
3092
|
date = templateUtilities.renderTime(data["effectivePeriodStart"], timezone);
|
|
2784
3093
|
if (data["effectivePeriodEnd"]) {
|
|
@@ -2795,36 +3104,47 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2795
3104
|
observationhtml += `
|
|
2796
3105
|
<tr>
|
|
2797
3106
|
<td>${componentCode}</td>
|
|
2798
|
-
<td
|
|
2799
|
-
<td>${templateUtilities.renderTextAsHtml(component["
|
|
2800
|
-
<td>${
|
|
3107
|
+
<td></td>
|
|
3108
|
+
<td>${templateUtilities.renderTextAsHtml(component["formattedValue"]) ?? ""}</td>
|
|
3109
|
+
<td>${templateUtilities.renderTextAsHtml(component["referenceRange"])?.trim() ?? ""}</td>
|
|
3110
|
+
<td>${date ?? ""}</td>
|
|
3111
|
+
<td>${data["source"] ?? ""}</td>
|
|
2801
3112
|
</tr>`;
|
|
2802
3113
|
}
|
|
2803
3114
|
}
|
|
2804
3115
|
} else {
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
observationAdded.
|
|
2808
|
-
|
|
2809
|
-
|
|
3116
|
+
if (obsDate && obsDate >= twoYearsAgo) {
|
|
3117
|
+
const code = data["code"] ?? "";
|
|
3118
|
+
if (code && !observationAdded.has(code)) {
|
|
3119
|
+
observationAdded.add(code);
|
|
3120
|
+
this.formatSummaryObservationData(data);
|
|
3121
|
+
observationhtml += `
|
|
2810
3122
|
<tr>
|
|
2811
|
-
<td>${data["code"] ?? "
|
|
2812
|
-
<td>${templateUtilities.
|
|
2813
|
-
<td>${templateUtilities.renderTextAsHtml(data["
|
|
2814
|
-
<td>${
|
|
3123
|
+
<td>${data["code"] ?? ""}</td>
|
|
3124
|
+
<td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
|
|
3125
|
+
<td>${templateUtilities.renderTextAsHtml(data["formattedValue"]) ?? ""}</td>
|
|
3126
|
+
<td>${templateUtilities.renderTextAsHtml(data["referenceRange"])?.trim() ?? ""}</td>
|
|
3127
|
+
<td>${date ?? ""}</td>
|
|
3128
|
+
<td>${data["source"] ?? ""}</td>
|
|
2815
3129
|
</tr>`;
|
|
3130
|
+
}
|
|
2816
3131
|
}
|
|
2817
3132
|
}
|
|
2818
3133
|
} else if (resourceItem.title === "DiagnosticReportLab Summary Grouped by DiagnosticReport|Lab Code") {
|
|
2819
|
-
|
|
3134
|
+
let issuedDate = void 0;
|
|
3135
|
+
if (data["issued"]) {
|
|
3136
|
+
issuedDate = new Date(data["issued"]);
|
|
3137
|
+
}
|
|
3138
|
+
if (data["status"] === "final" && issuedDate && issuedDate >= twoYearsAgo) {
|
|
2820
3139
|
const reportName = data["report"] ?? "";
|
|
2821
3140
|
if (reportName && !diagnosticReportAdded.has(reportName)) {
|
|
2822
3141
|
diagnosticReportAdded.add(reportName);
|
|
2823
3142
|
diagnosticReporthtml += `
|
|
2824
3143
|
<tr>
|
|
2825
|
-
<td>${data["report"] ?? "
|
|
2826
|
-
<td>${data["performer"] ?? "
|
|
2827
|
-
<td>${templateUtilities.renderTime(data["issued"], timezone) ?? "
|
|
3144
|
+
<td>${data["report"] ?? ""}</td>
|
|
3145
|
+
<td>${data["performer"] ?? ""}</td>
|
|
3146
|
+
<td>${templateUtilities.renderTime(data["issued"], timezone) ?? ""}</td>
|
|
3147
|
+
<td>${data["source"] ?? ""}</td>
|
|
2828
3148
|
</tr>`;
|
|
2829
3149
|
}
|
|
2830
3150
|
}
|
|
@@ -2837,6 +3157,10 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2837
3157
|
</tbody>
|
|
2838
3158
|
</table>
|
|
2839
3159
|
</div>`;
|
|
3160
|
+
if (skippedObservations > 0) {
|
|
3161
|
+
html += `
|
|
3162
|
+
<p><em>${skippedObservations} additional observations older than 2 years ago are present</em></p>`;
|
|
3163
|
+
}
|
|
2840
3164
|
}
|
|
2841
3165
|
if (diagnosticReportAdded.size > 0) {
|
|
2842
3166
|
html += diagnosticReporthtml;
|
|
@@ -2844,6 +3168,10 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2844
3168
|
</tbody>
|
|
2845
3169
|
</table>
|
|
2846
3170
|
</div>`;
|
|
3171
|
+
if (skippedDiagnosticReports > 0) {
|
|
3172
|
+
html += `
|
|
3173
|
+
<p><em>${skippedDiagnosticReports} additional diagnostic reports older than 2 years ago are present</em></p>`;
|
|
3174
|
+
}
|
|
2847
3175
|
}
|
|
2848
3176
|
html += `
|
|
2849
3177
|
</div>`;
|
|
@@ -2853,12 +3181,32 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2853
3181
|
* Internal static implementation that actually generates the narrative
|
|
2854
3182
|
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
2855
3183
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
3184
|
+
* @param now - Optional current date for filtering
|
|
2856
3185
|
* @returns HTML string for rendering
|
|
2857
3186
|
*/
|
|
2858
|
-
static generateStaticNarrative(resources, timezone) {
|
|
3187
|
+
static generateStaticNarrative(resources, timezone, now) {
|
|
2859
3188
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3189
|
+
const currentDate = now || /* @__PURE__ */ new Date();
|
|
3190
|
+
const twoYearsAgo = new Date(currentDate);
|
|
3191
|
+
twoYearsAgo.setFullYear(currentDate.getFullYear() - 2);
|
|
2860
3192
|
let html = "";
|
|
2861
|
-
|
|
3193
|
+
let skippedObservations = 0;
|
|
3194
|
+
let skippedDiagnosticReports = 0;
|
|
3195
|
+
for (const resourceItem of resources) {
|
|
3196
|
+
if (resourceItem.resourceType === "Observation") {
|
|
3197
|
+
const obsDate = this.getObservationDate(resourceItem);
|
|
3198
|
+
if (obsDate && obsDate < twoYearsAgo) {
|
|
3199
|
+
skippedObservations++;
|
|
3200
|
+
}
|
|
3201
|
+
} else if (resourceItem.resourceType === "DiagnosticReport") {
|
|
3202
|
+
const issued = resourceItem.issued;
|
|
3203
|
+
const status = resourceItem.status;
|
|
3204
|
+
if (status === "final" && issued && new Date(issued) < twoYearsAgo) {
|
|
3205
|
+
skippedDiagnosticReports++;
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
const observations = this.getObservations(resources, twoYearsAgo);
|
|
2862
3210
|
if (observations.length > 0) {
|
|
2863
3211
|
observations.sort((a, b) => {
|
|
2864
3212
|
const dateA = this.getObservationDate(a);
|
|
@@ -2867,16 +3215,22 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2867
3215
|
});
|
|
2868
3216
|
this.filterObservationForLoincCodes(observations);
|
|
2869
3217
|
html += this.renderObservations(templateUtilities, observations, timezone);
|
|
3218
|
+
if (skippedObservations > 0) {
|
|
3219
|
+
html += `
|
|
3220
|
+
<p><em>${skippedObservations} additional observations older than 2 years ago are present</em></p>`;
|
|
3221
|
+
}
|
|
2870
3222
|
}
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
3223
|
+
const diagnosticReports = this.getDiagnosticReports(resources, twoYearsAgo).filter((resource) => !this.isPanelDiagnosticReport(resource));
|
|
3224
|
+
if (diagnosticReports.length > 0) {
|
|
3225
|
+
diagnosticReports.sort((a, b) => {
|
|
3226
|
+
const dateA = a.issued;
|
|
3227
|
+
const dateB = b.issued;
|
|
3228
|
+
return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
3229
|
+
});
|
|
3230
|
+
html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
|
|
3231
|
+
if (skippedDiagnosticReports > 0) {
|
|
3232
|
+
html += `
|
|
3233
|
+
<p><em>${skippedDiagnosticReports} additional diagnostic reports older than 2 years ago are present</em></p>`;
|
|
2880
3234
|
}
|
|
2881
3235
|
}
|
|
2882
3236
|
return html;
|
|
@@ -2921,15 +3275,16 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2921
3275
|
return obsDate;
|
|
2922
3276
|
}
|
|
2923
3277
|
/**
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
3278
|
+
* Get all Observation resources from the resource array, filtered by twoYearsAgo
|
|
3279
|
+
* @param resources - FHIR resources array
|
|
3280
|
+
* @param twoYearsAgo - Date object representing the cutoff
|
|
3281
|
+
* @returns Array of Observation resources
|
|
3282
|
+
*/
|
|
3283
|
+
static getObservations(resources, twoYearsAgo) {
|
|
2929
3284
|
return resources.filter((resourceItem) => {
|
|
2930
3285
|
if (resourceItem.resourceType === "Observation") {
|
|
2931
3286
|
const obsDate = this.getObservationDate(resourceItem);
|
|
2932
|
-
if (obsDate && obsDate >=
|
|
3287
|
+
if (obsDate && obsDate >= twoYearsAgo) {
|
|
2933
3288
|
return true;
|
|
2934
3289
|
}
|
|
2935
3290
|
}
|
|
@@ -2937,12 +3292,21 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2937
3292
|
}).map((resourceItem) => resourceItem);
|
|
2938
3293
|
}
|
|
2939
3294
|
/**
|
|
2940
|
-
* Get all DiagnosticReport resources from the resource array
|
|
3295
|
+
* Get all DiagnosticReport resources from the resource array, filtered by twoYearsAgo
|
|
2941
3296
|
* @param resources - FHIR resources array
|
|
3297
|
+
* @param twoYearsAgo - Date object representing the cutoff
|
|
2942
3298
|
* @returns Array of DiagnosticReport resources
|
|
2943
3299
|
*/
|
|
2944
|
-
static getDiagnosticReports(resources) {
|
|
2945
|
-
return resources.filter((resourceItem) =>
|
|
3300
|
+
static getDiagnosticReports(resources, twoYearsAgo) {
|
|
3301
|
+
return resources.filter((resourceItem) => {
|
|
3302
|
+
if (resourceItem.resourceType === "DiagnosticReport") {
|
|
3303
|
+
const issued = resourceItem.issued;
|
|
3304
|
+
if (issued && new Date(issued) >= twoYearsAgo) {
|
|
3305
|
+
return true;
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
return false;
|
|
3309
|
+
}).map((resourceItem) => resourceItem);
|
|
2946
3310
|
}
|
|
2947
3311
|
/**
|
|
2948
3312
|
* Render HTML table for Observation resources
|
|
@@ -2953,32 +3317,36 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2953
3317
|
*/
|
|
2954
3318
|
static renderObservations(templateUtilities, observations, timezone) {
|
|
2955
3319
|
let html = "";
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
<h3>Observations</h3>`;
|
|
2959
|
-
}
|
|
3320
|
+
html += `
|
|
3321
|
+
<h3>Observations</h3>`;
|
|
2960
3322
|
html += `
|
|
2961
3323
|
<table>
|
|
2962
3324
|
<thead>
|
|
2963
3325
|
<tr>
|
|
2964
|
-
<th>
|
|
3326
|
+
<th>Name</th>
|
|
3327
|
+
<th>Code (System)</th>
|
|
2965
3328
|
<th>Result</th>
|
|
2966
3329
|
<th>Reference Range</th>
|
|
2967
3330
|
<th>Date</th>
|
|
3331
|
+
<th>Source</th>
|
|
2968
3332
|
</tr>
|
|
2969
3333
|
</thead>
|
|
2970
3334
|
<tbody>`;
|
|
2971
3335
|
const observationAdded = /* @__PURE__ */ new Set();
|
|
2972
3336
|
for (const obs of observations) {
|
|
2973
|
-
const
|
|
2974
|
-
|
|
2975
|
-
|
|
3337
|
+
const obsCodeDisplay = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code));
|
|
3338
|
+
const obsCodeAndSystem = templateUtilities.codeableConceptCoding(obs.code);
|
|
3339
|
+
if (!observationAdded.has(obsCodeDisplay) && !observationAdded.has(obsCodeAndSystem)) {
|
|
3340
|
+
observationAdded.add(obsCodeDisplay);
|
|
3341
|
+
observationAdded.add(obsCodeAndSystem);
|
|
2976
3342
|
html += `
|
|
2977
3343
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2978
|
-
<td>${
|
|
3344
|
+
<td>${obsCodeDisplay}</td>
|
|
3345
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
2979
3346
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2980
3347
|
<td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
|
|
2981
3348
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
3349
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
2982
3350
|
</tr>`;
|
|
2983
3351
|
}
|
|
2984
3352
|
}
|
|
@@ -3001,17 +3369,21 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
3001
3369
|
<thead>
|
|
3002
3370
|
<tr>
|
|
3003
3371
|
<th>Report</th>
|
|
3372
|
+
<th>Code (System)</th>
|
|
3004
3373
|
<th>Category</th>
|
|
3005
3374
|
<th>Result</th>
|
|
3006
3375
|
<th>Issued</th>
|
|
3376
|
+
<th>Source</th>
|
|
3007
3377
|
</tr>
|
|
3008
3378
|
</thead>
|
|
3009
3379
|
<tbody>`;
|
|
3010
3380
|
const diagnosticReportAdded = /* @__PURE__ */ new Set();
|
|
3011
3381
|
for (const report of reports) {
|
|
3012
|
-
const reportName = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3013
|
-
|
|
3382
|
+
const reportName = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(report.code));
|
|
3383
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(report.code);
|
|
3384
|
+
if (!diagnosticReportAdded.has(reportName) && !diagnosticReportAdded.has(codeAndSystem)) {
|
|
3014
3385
|
diagnosticReportAdded.add(reportName);
|
|
3386
|
+
diagnosticReportAdded.add(codeAndSystem);
|
|
3015
3387
|
let resultCount = "";
|
|
3016
3388
|
if (report.result && Array.isArray(report.result)) {
|
|
3017
3389
|
resultCount = `${report.result.length} result${report.result.length !== 1 ? "s" : ""}`;
|
|
@@ -3019,9 +3391,11 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
3019
3391
|
html += `
|
|
3020
3392
|
<tr id="${templateUtilities.narrativeLinkId(report)}">
|
|
3021
3393
|
<td>${reportName}</td>
|
|
3394
|
+
<td>${codeAndSystem}</td>
|
|
3022
3395
|
<td>${templateUtilities.firstFromCodeableConceptList(report.category)}</td>
|
|
3023
3396
|
<td>${resultCount}</td>
|
|
3024
3397
|
<td>${report.issued ? templateUtilities.renderTime(report.issued, timezone) : ""}</td>
|
|
3398
|
+
<td>${templateUtilities.getOwnerTag(report)}</td>
|
|
3025
3399
|
</tr>`;
|
|
3026
3400
|
}
|
|
3027
3401
|
}
|
|
@@ -3030,6 +3404,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
3030
3404
|
</table>`;
|
|
3031
3405
|
return html;
|
|
3032
3406
|
}
|
|
3407
|
+
/**
|
|
3408
|
+
* 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)
|
|
3409
|
+
* @returns true if the report is just a panel
|
|
3410
|
+
*/
|
|
3411
|
+
static isPanelDiagnosticReport(report) {
|
|
3412
|
+
return this.hasEssentialLabPanelLoinc(
|
|
3413
|
+
report
|
|
3414
|
+
);
|
|
3415
|
+
}
|
|
3416
|
+
/**
|
|
3417
|
+
* Check if a DiagnosticReport or Observation has a LOINC code in ESSENTIAL_LAB_PANELS
|
|
3418
|
+
* @param resource - DiagnosticReport or Observation
|
|
3419
|
+
* @returns true if any LOINC code is in ESSENTIAL_LAB_PANELS, false otherwise
|
|
3420
|
+
*/
|
|
3421
|
+
static hasEssentialLabPanelLoinc(resource) {
|
|
3422
|
+
const codings = resource?.code?.coding ?? [];
|
|
3423
|
+
return codings.some(
|
|
3424
|
+
(coding) => coding && coding.system && coding.code && coding.system.toLowerCase().includes("loinc") && Object.keys(ESSENTIAL_LAB_PANELS).includes(coding.code)
|
|
3425
|
+
);
|
|
3426
|
+
}
|
|
3033
3427
|
};
|
|
3034
3428
|
|
|
3035
3429
|
// src/narratives/templates/typescript/HistoryOfProceduresTemplate.ts
|
|
@@ -3063,14 +3457,18 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3063
3457
|
<thead>
|
|
3064
3458
|
<tr>
|
|
3065
3459
|
<th>Procedure</th>
|
|
3460
|
+
<th>Code (System)</th>
|
|
3066
3461
|
<th>Performer</th>
|
|
3067
3462
|
<th>Date</th>
|
|
3463
|
+
<th>Source</th>
|
|
3068
3464
|
</tr>
|
|
3069
3465
|
</thead>
|
|
3070
3466
|
<tbody>`;
|
|
3071
3467
|
for (const resourceItem of resources) {
|
|
3072
3468
|
for (const rowData of resourceItem.section ?? []) {
|
|
3469
|
+
const sectionCodeableConcept = rowData.code;
|
|
3073
3470
|
const data = {};
|
|
3471
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
3074
3472
|
for (const columnData of rowData.section ?? []) {
|
|
3075
3473
|
switch (columnData.title) {
|
|
3076
3474
|
case "Procedure Name":
|
|
@@ -3082,6 +3480,9 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3082
3480
|
case "Performed Date":
|
|
3083
3481
|
data["date"] = columnData.text?.div ?? "";
|
|
3084
3482
|
break;
|
|
3483
|
+
case "Source":
|
|
3484
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
3485
|
+
break;
|
|
3085
3486
|
default:
|
|
3086
3487
|
break;
|
|
3087
3488
|
}
|
|
@@ -3089,9 +3490,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3089
3490
|
isSummaryCreated = true;
|
|
3090
3491
|
html += `
|
|
3091
3492
|
<tr>
|
|
3092
|
-
<td>${data["procedure"] ?? "
|
|
3093
|
-
|
|
3094
|
-
<td>${
|
|
3493
|
+
<td>${data["procedure"] ?? ""}</td>
|
|
3494
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
3495
|
+
<td>${data["performer"] ?? ""}</td>
|
|
3496
|
+
<td>${templateUtilities.renderTime(data["date"], timezone) ?? ""}</td>
|
|
3497
|
+
<td>${data["source"] ?? ""}</td>
|
|
3095
3498
|
</tr>`;
|
|
3096
3499
|
}
|
|
3097
3500
|
}
|
|
@@ -3114,8 +3517,10 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3114
3517
|
<thead>
|
|
3115
3518
|
<tr>
|
|
3116
3519
|
<th>Procedure</th>
|
|
3520
|
+
<th>Code (System)</th>
|
|
3117
3521
|
<th>Comments</th>
|
|
3118
3522
|
<th>Date</th>
|
|
3523
|
+
<th>Source</th>
|
|
3119
3524
|
</tr>
|
|
3120
3525
|
</thead>
|
|
3121
3526
|
<tbody>`;
|
|
@@ -3123,9 +3528,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3123
3528
|
const proc = resourceItem;
|
|
3124
3529
|
html += `
|
|
3125
3530
|
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
3126
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3531
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(proc.code, "display"))}</td>
|
|
3532
|
+
<td>${templateUtilities.codeableConceptCoding(proc.code)}</td>
|
|
3127
3533
|
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
3128
3534
|
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
3535
|
+
<td>${templateUtilities.getOwnerTag(proc)}</td>
|
|
3129
3536
|
</tr>`;
|
|
3130
3537
|
}
|
|
3131
3538
|
html += `
|
|
@@ -3164,22 +3571,26 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
3164
3571
|
<table>
|
|
3165
3572
|
<thead>
|
|
3166
3573
|
<tr>
|
|
3167
|
-
<th>
|
|
3574
|
+
<th>Name</th>
|
|
3575
|
+
<th>Code (System)</th>
|
|
3168
3576
|
<th>Result</th>
|
|
3169
3577
|
<th>Unit</th>
|
|
3170
3578
|
<th>Comments</th>
|
|
3171
3579
|
<th>Date</th>
|
|
3580
|
+
<th>Source</th>
|
|
3172
3581
|
</tr>
|
|
3173
3582
|
</thead>
|
|
3174
3583
|
<tbody>`;
|
|
3175
3584
|
for (const obs of observations) {
|
|
3176
3585
|
html += `
|
|
3177
3586
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
3178
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3587
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code))}</td>
|
|
3588
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
3179
3589
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
3180
3590
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
3181
3591
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
3182
3592
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
3593
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
3183
3594
|
</tr>`;
|
|
3184
3595
|
}
|
|
3185
3596
|
html += `
|
|
@@ -3195,14 +3606,26 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
3195
3606
|
* Generate HTML narrative for Past History of Illnesses
|
|
3196
3607
|
* @param resources - FHIR Condition resources
|
|
3197
3608
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
3609
|
+
* @param now - Optional current date to use for generating relative dates in the narrative
|
|
3198
3610
|
* @returns HTML string for rendering
|
|
3199
3611
|
*/
|
|
3200
|
-
|
|
3201
|
-
generateNarrative(resources, timezone) {
|
|
3612
|
+
generateNarrative(resources, timezone, now) {
|
|
3202
3613
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3203
3614
|
let html = ``;
|
|
3204
3615
|
const resolvedConditions = resources.map((entry) => entry) || [];
|
|
3205
|
-
|
|
3616
|
+
const currentDate = now || /* @__PURE__ */ new Date();
|
|
3617
|
+
const fiveYearsAgo = new Date(currentDate);
|
|
3618
|
+
fiveYearsAgo.setFullYear(currentDate.getFullYear() - 5);
|
|
3619
|
+
let skippedConditions = 0;
|
|
3620
|
+
const filteredConditions = [];
|
|
3621
|
+
for (const cond of resolvedConditions) {
|
|
3622
|
+
if (cond.recordedDate && new Date(cond.recordedDate) >= fiveYearsAgo) {
|
|
3623
|
+
filteredConditions.push(cond);
|
|
3624
|
+
} else {
|
|
3625
|
+
skippedConditions++;
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
filteredConditions.sort((a, b) => {
|
|
3206
3629
|
const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
|
|
3207
3630
|
const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
|
|
3208
3631
|
return dateB - dateA;
|
|
@@ -3212,27 +3635,35 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
3212
3635
|
<thead>
|
|
3213
3636
|
<tr>
|
|
3214
3637
|
<th>Problem</th>
|
|
3638
|
+
<th>Code (System)</th>
|
|
3215
3639
|
<th>Onset Date</th>
|
|
3216
3640
|
<th>Recorded Date</th>
|
|
3217
3641
|
<th>Resolved Date</th>
|
|
3642
|
+
<th>Source</th>
|
|
3218
3643
|
</tr>
|
|
3219
3644
|
</thead>
|
|
3220
3645
|
<tbody>`;
|
|
3221
3646
|
const addedConditionCodes = /* @__PURE__ */ new Set();
|
|
3222
|
-
for (const cond of
|
|
3223
|
-
const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3647
|
+
for (const cond of filteredConditions) {
|
|
3648
|
+
const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(cond.code));
|
|
3224
3649
|
if (!addedConditionCodes.has(conditionCode)) {
|
|
3225
3650
|
addedConditionCodes.add(conditionCode);
|
|
3226
3651
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
3227
3652
|
<td class="Name">${conditionCode}</td>
|
|
3653
|
+
<td class="CodeSystem">${templateUtilities.codeableConceptCoding(cond.code)}</td>
|
|
3228
3654
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
3229
3655
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
3230
3656
|
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
3657
|
+
<td class="Source">${templateUtilities.getOwnerTag(cond)}</td>
|
|
3231
3658
|
</tr>`;
|
|
3232
3659
|
}
|
|
3233
3660
|
}
|
|
3234
3661
|
html += `</tbody>
|
|
3235
3662
|
</table>`;
|
|
3663
|
+
if (skippedConditions > 0) {
|
|
3664
|
+
html += `
|
|
3665
|
+
<p><em>${skippedConditions} additional past illnesses older than 5 years ago are present</em></p>`;
|
|
3666
|
+
}
|
|
3236
3667
|
return html;
|
|
3237
3668
|
}
|
|
3238
3669
|
};
|
|
@@ -3262,6 +3693,7 @@ var PlanOfCareTemplate = class {
|
|
|
3262
3693
|
<th>Comments</th>
|
|
3263
3694
|
<th>Planned Start</th>
|
|
3264
3695
|
<th>Planned End</th>
|
|
3696
|
+
<th>Source</th>
|
|
3265
3697
|
</tr>
|
|
3266
3698
|
</thead>
|
|
3267
3699
|
<tbody>`;
|
|
@@ -3273,6 +3705,7 @@ var PlanOfCareTemplate = class {
|
|
|
3273
3705
|
<td>${templateUtilities.concat(cp.note, "text")}</td>
|
|
3274
3706
|
<td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
|
|
3275
3707
|
<td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
|
|
3708
|
+
<td>${templateUtilities.getOwnerTag(cp)}</td>
|
|
3276
3709
|
</tr>`;
|
|
3277
3710
|
}
|
|
3278
3711
|
html += `
|
|
@@ -3298,6 +3731,7 @@ var PlanOfCareTemplate = class {
|
|
|
3298
3731
|
<th>Created</th>
|
|
3299
3732
|
<th>Planned Start</th>
|
|
3300
3733
|
<th>Planned End</th>
|
|
3734
|
+
<th>Source</th>
|
|
3301
3735
|
</tr>
|
|
3302
3736
|
</thead>
|
|
3303
3737
|
<tbody>`;
|
|
@@ -3315,10 +3749,11 @@ var PlanOfCareTemplate = class {
|
|
|
3315
3749
|
isSummaryCreated = true;
|
|
3316
3750
|
html += `
|
|
3317
3751
|
<tr>
|
|
3318
|
-
<td>${data["CarePlan Name"] ?? "
|
|
3319
|
-
<td>${templateUtilities.renderTime(data["created"], timezone) ?? "
|
|
3320
|
-
<td>${templateUtilities.renderTime(data["period.start"], timezone) ?? "
|
|
3321
|
-
<td>${templateUtilities.renderTime(data["period.end"], timezone) ?? "
|
|
3752
|
+
<td>${data["CarePlan Name"] ?? ""}</td>
|
|
3753
|
+
<td>${templateUtilities.renderTime(data["created"], timezone) ?? ""}</td>
|
|
3754
|
+
<td>${templateUtilities.renderTime(data["period.start"], timezone) ?? ""}</td>
|
|
3755
|
+
<td>${templateUtilities.renderTime(data["period.end"], timezone) ?? ""}</td>
|
|
3756
|
+
<td>${data["source"] ?? ""}</td>
|
|
3322
3757
|
</tr>`;
|
|
3323
3758
|
}
|
|
3324
3759
|
}
|
|
@@ -3349,77 +3784,54 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
3349
3784
|
*/
|
|
3350
3785
|
static generateStaticNarrative(resources, timezone) {
|
|
3351
3786
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3352
|
-
let html =
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
|
|
3364
|
-
const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
|
|
3365
|
-
return dateB - dateA;
|
|
3787
|
+
let html = "";
|
|
3788
|
+
let functionalObservations = resources.filter((r) => r.resourceType === "Observation").filter((r) => {
|
|
3789
|
+
const hasFunctionalLoinc = r.code?.coding?.some(
|
|
3790
|
+
(c) => c.system?.toLowerCase().includes("loinc") && c.code === "47420-5"
|
|
3791
|
+
);
|
|
3792
|
+
const hasFunctionalCategory = r.category?.some(
|
|
3793
|
+
(cat) => cat.coding?.some(
|
|
3794
|
+
(c) => c.code === "functional-status" || c.display?.toLowerCase().includes("functional")
|
|
3795
|
+
)
|
|
3796
|
+
);
|
|
3797
|
+
return hasFunctionalLoinc || hasFunctionalCategory;
|
|
3366
3798
|
});
|
|
3367
|
-
|
|
3368
|
-
const
|
|
3369
|
-
|
|
3370
|
-
return dateB - dateA;
|
|
3799
|
+
functionalObservations = functionalObservations.sort((a, b) => {
|
|
3800
|
+
const getObsDate = (obs) => obs.effectiveDateTime ? new Date(obs.effectiveDateTime).getTime() : obs.issued ? new Date(obs.issued).getTime() : 0;
|
|
3801
|
+
return getObsDate(b) - getObsDate(a);
|
|
3371
3802
|
});
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
const
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
}
|
|
3803
|
+
let clinicalImpressions = resources.filter((r) => r.resourceType === "ClinicalImpression").filter((r) => r.status === "completed");
|
|
3804
|
+
clinicalImpressions = clinicalImpressions.sort((a, b) => {
|
|
3805
|
+
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;
|
|
3806
|
+
return getImpressionDate(b) - getImpressionDate(a);
|
|
3807
|
+
});
|
|
3808
|
+
if (functionalObservations.length > 0) {
|
|
3809
|
+
html += `<table><thead><tr><th>Observation</th><th>Value</th><th>Date</th><th>Interpretation</th><th>Comments</th></tr></thead><tbody>`;
|
|
3810
|
+
for (const obs of functionalObservations) {
|
|
3811
|
+
const observation = obs;
|
|
3812
|
+
const obsName = templateUtilities.codeableConceptDisplay(observation.code);
|
|
3813
|
+
const value = templateUtilities.extractObservationValue(observation);
|
|
3814
|
+
const date = observation.effectiveDateTime ? templateUtilities.renderDate(observation.effectiveDateTime) : observation.issued ? templateUtilities.renderDate(observation.issued) : "";
|
|
3815
|
+
const interpretation = observation.interpretation ? templateUtilities.codeableConceptDisplay(observation.interpretation[0]) : "";
|
|
3816
|
+
const comments = observation.comment || observation.note?.map((n) => n.text).join("; ") || "";
|
|
3817
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(observation)}">
|
|
3818
|
+
<td>${obsName}</td>
|
|
3819
|
+
<td>${value ?? ""}</td>
|
|
3820
|
+
<td>${date}</td>
|
|
3821
|
+
<td>${interpretation}</td>
|
|
3822
|
+
<td>${comments}</td>
|
|
3823
|
+
</tr>`;
|
|
3394
3824
|
}
|
|
3395
|
-
html += `</tbody
|
|
3396
|
-
</table>`;
|
|
3825
|
+
html += `</tbody></table>`;
|
|
3397
3826
|
}
|
|
3398
3827
|
if (clinicalImpressions.length > 0) {
|
|
3399
|
-
html += `<
|
|
3400
|
-
<table>
|
|
3401
|
-
<thead>
|
|
3402
|
-
<tr>
|
|
3403
|
-
<th>Date</th>
|
|
3404
|
-
<th>Status</th>
|
|
3405
|
-
<th>Description</th>
|
|
3406
|
-
<th>Summary</th>
|
|
3407
|
-
<th>Findings</th>
|
|
3408
|
-
</tr>
|
|
3409
|
-
</thead>
|
|
3410
|
-
<tbody>`;
|
|
3828
|
+
html += `<table><thead><tr><th>Date</th><th>Status</th><th>Description</th><th>Summary</th><th>Findings</th></tr></thead><tbody>`;
|
|
3411
3829
|
for (const impression of clinicalImpressions) {
|
|
3412
3830
|
let formattedDate = "";
|
|
3413
3831
|
if (impression.effectiveDateTime) {
|
|
3414
|
-
formattedDate = templateUtilities.renderTime(
|
|
3415
|
-
impression.effectiveDateTime,
|
|
3416
|
-
timezone
|
|
3417
|
-
);
|
|
3832
|
+
formattedDate = templateUtilities.renderTime(impression.effectiveDateTime, timezone);
|
|
3418
3833
|
} else if (impression.effectivePeriod) {
|
|
3419
|
-
formattedDate = templateUtilities.renderPeriod(
|
|
3420
|
-
impression.effectivePeriod,
|
|
3421
|
-
timezone
|
|
3422
|
-
);
|
|
3834
|
+
formattedDate = templateUtilities.renderPeriod(impression.effectivePeriod, timezone);
|
|
3423
3835
|
} else if (impression.date) {
|
|
3424
3836
|
formattedDate = templateUtilities.renderDate(impression.date);
|
|
3425
3837
|
}
|
|
@@ -3427,23 +3839,24 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
3427
3839
|
if (impression.finding && impression.finding.length > 0) {
|
|
3428
3840
|
findingsHtml = "<ul>";
|
|
3429
3841
|
for (const finding of impression.finding) {
|
|
3430
|
-
const findingText = finding.itemCodeableConcept ? templateUtilities.
|
|
3842
|
+
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConceptDisplay(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
3431
3843
|
const cause = finding.basis || "";
|
|
3432
3844
|
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
3433
3845
|
}
|
|
3434
3846
|
findingsHtml += "</ul>";
|
|
3435
3847
|
}
|
|
3436
|
-
html +=
|
|
3437
|
-
<
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
</tr>`;
|
|
3848
|
+
html += `<tr id="${templateUtilities.narrativeLinkId(impression)}">
|
|
3849
|
+
<td>${formattedDate}</td>
|
|
3850
|
+
<td>${impression.status || ""}</td>
|
|
3851
|
+
<td>${impression.description || ""}</td>
|
|
3852
|
+
<td>${impression.summary || ""}</td>
|
|
3853
|
+
<td>${findingsHtml}</td>
|
|
3854
|
+
</tr>`;
|
|
3444
3855
|
}
|
|
3445
|
-
html += `</tbody
|
|
3446
|
-
|
|
3856
|
+
html += `</tbody></table>`;
|
|
3857
|
+
}
|
|
3858
|
+
if (functionalObservations.length === 0 && clinicalImpressions.length === 0) {
|
|
3859
|
+
html += `<p>No functional status information available.</p>`;
|
|
3447
3860
|
}
|
|
3448
3861
|
return html;
|
|
3449
3862
|
}
|
|
@@ -3468,34 +3881,109 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
3468
3881
|
*/
|
|
3469
3882
|
static generateStaticNarrative(resources, timezone) {
|
|
3470
3883
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3471
|
-
const
|
|
3472
|
-
|
|
3884
|
+
const pregnancyHistoryFilter = IPSSectionResourceFilters["HistoryOfPregnancySection"];
|
|
3885
|
+
const filteredResources = pregnancyHistoryFilter ? resources.filter(pregnancyHistoryFilter) : resources;
|
|
3886
|
+
const observations = filteredResources.filter((r) => r.resourceType === "Observation");
|
|
3887
|
+
const conditions = filteredResources.filter((r) => r.resourceType === "Condition");
|
|
3888
|
+
const EDD_LOINC = "11778-8";
|
|
3889
|
+
const pregnancyStatusCodes = Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS);
|
|
3890
|
+
const pregnancyOutcomeCodes = Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME);
|
|
3891
|
+
const pregnancyStatusObs = observations.filter((obs) => obs.code?.coding?.some((c) => c.code && pregnancyStatusCodes.includes(c.code))).sort((a, b) => {
|
|
3473
3892
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
3474
3893
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
3475
|
-
return
|
|
3894
|
+
return dateB && dateA ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
3895
|
+
})[0];
|
|
3896
|
+
const eddObs = observations.filter((obs) => obs.code?.coding?.some((c) => c.code === EDD_LOINC)).sort((a, b) => {
|
|
3897
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
3898
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
3899
|
+
return dateB && dateA ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
3900
|
+
})[0];
|
|
3901
|
+
const historyObs = observations.filter(
|
|
3902
|
+
(obs) => obs.code?.coding?.some((c) => c.code && pregnancyOutcomeCodes.includes(c.code)) || obs.valueCodeableConcept?.coding?.some((c) => c.code && pregnancyOutcomeCodes.includes(c.code))
|
|
3903
|
+
).sort((a, b) => {
|
|
3904
|
+
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
3905
|
+
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
3906
|
+
return dateB && dateA ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
3476
3907
|
});
|
|
3908
|
+
if (!pregnancyStatusObs && !eddObs && historyObs.length === 0 && conditions.length === 0) {
|
|
3909
|
+
return `<p>No history of pregnancy found.</p>`;
|
|
3910
|
+
}
|
|
3477
3911
|
let html = `
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3912
|
+
<table>
|
|
3913
|
+
<thead>
|
|
3914
|
+
<tr>
|
|
3915
|
+
<th>Result</th>
|
|
3916
|
+
<th>Code (System)</th>
|
|
3917
|
+
<th>Comments</th>
|
|
3918
|
+
<th>Date</th>
|
|
3919
|
+
<th>Source</th>
|
|
3920
|
+
</tr>
|
|
3921
|
+
</thead>
|
|
3922
|
+
<tbody>`;
|
|
3923
|
+
function renderRow({ id, result, comments, date, codeSystem, owner }) {
|
|
3489
3924
|
html += `
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3925
|
+
<tr id="${id}">
|
|
3926
|
+
<td class="Result">${result}</td>
|
|
3927
|
+
<td class="CodeSystem">${codeSystem}</td>
|
|
3928
|
+
<td class="Comments">${comments}</td>
|
|
3929
|
+
<td class="Date">${date}</td>
|
|
3930
|
+
<td class="Source">${owner}</td>
|
|
3931
|
+
</tr>`;
|
|
3932
|
+
}
|
|
3933
|
+
const rowResources = [];
|
|
3934
|
+
if (pregnancyStatusObs) {
|
|
3935
|
+
const date = pregnancyStatusObs.effectiveDateTime || pregnancyStatusObs.effectivePeriod?.start;
|
|
3936
|
+
rowResources.push({ resource: pregnancyStatusObs, date, type: "status" });
|
|
3937
|
+
}
|
|
3938
|
+
if (eddObs) {
|
|
3939
|
+
const date = eddObs.effectiveDateTime || eddObs.effectivePeriod?.start;
|
|
3940
|
+
rowResources.push({ resource: eddObs, date, type: "edd" });
|
|
3941
|
+
}
|
|
3942
|
+
for (const obs of historyObs) {
|
|
3943
|
+
const date = obs.effectiveDateTime || obs.effectivePeriod?.start;
|
|
3944
|
+
rowResources.push({ resource: obs, date, type: "history" });
|
|
3945
|
+
}
|
|
3946
|
+
for (const cond of conditions) {
|
|
3947
|
+
const condition = cond;
|
|
3948
|
+
const date = condition.onsetDateTime || condition.onsetPeriod?.start;
|
|
3949
|
+
rowResources.push({ resource: condition, date, type: "condition" });
|
|
3950
|
+
}
|
|
3951
|
+
rowResources.sort((a, b) => {
|
|
3952
|
+
if (!a.date && !b.date) return 0;
|
|
3953
|
+
if (!a.date) return 1;
|
|
3954
|
+
if (!b.date) return -1;
|
|
3955
|
+
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
|
3956
|
+
});
|
|
3957
|
+
for (const { resource, date, type } of rowResources) {
|
|
3958
|
+
let result = "", comments = "", dateStr = "", codeSystem = "";
|
|
3959
|
+
const id = templateUtilities.narrativeLinkId(resource);
|
|
3960
|
+
if (type === "status") {
|
|
3961
|
+
result = templateUtilities.renderTextAsHtml(templateUtilities.extractPregnancyStatus(resource));
|
|
3962
|
+
comments = templateUtilities.renderNotes(resource.note, timezone);
|
|
3963
|
+
dateStr = date ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(date, timezone)) : "";
|
|
3964
|
+
codeSystem = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptCoding(resource.code));
|
|
3965
|
+
} else if (type === "edd") {
|
|
3966
|
+
result = "Estimated Delivery Date: " + templateUtilities.renderTextAsHtml(templateUtilities.extractObservationSummaryValue(resource, timezone));
|
|
3967
|
+
comments = templateUtilities.renderNotes(resource.note, timezone);
|
|
3968
|
+
dateStr = date ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(date, timezone)) : "";
|
|
3969
|
+
codeSystem = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptCoding(resource.code));
|
|
3970
|
+
} else if (type === "history") {
|
|
3971
|
+
result = templateUtilities.renderTextAsHtml(templateUtilities.extractPregnancyStatus(resource));
|
|
3972
|
+
comments = templateUtilities.renderNotes(resource.note, timezone);
|
|
3973
|
+
dateStr = date ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(date, timezone)) : "";
|
|
3974
|
+
codeSystem = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptCoding(resource.code));
|
|
3975
|
+
} else if (type === "condition") {
|
|
3976
|
+
result = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(resource.code));
|
|
3977
|
+
comments = templateUtilities.renderNotes(resource.note, timezone);
|
|
3978
|
+
dateStr = date ? templateUtilities.renderTextAsHtml(templateUtilities.renderTime(date, timezone)) : "";
|
|
3979
|
+
codeSystem = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptCoding(resource.code));
|
|
3980
|
+
}
|
|
3981
|
+
const owner = templateUtilities.getOwnerTag(resource);
|
|
3982
|
+
renderRow({ id, result, comments, date: dateStr, codeSystem, owner });
|
|
3495
3983
|
}
|
|
3496
3984
|
html += `
|
|
3497
|
-
|
|
3498
|
-
|
|
3985
|
+
</tbody>
|
|
3986
|
+
</table>`;
|
|
3499
3987
|
return html;
|
|
3500
3988
|
}
|
|
3501
3989
|
};
|
|
@@ -3540,7 +4028,7 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
3540
4028
|
const consent = resourceItem;
|
|
3541
4029
|
html += `
|
|
3542
4030
|
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
3543
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
4031
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(consent.scope, "display"))}</td>
|
|
3544
4032
|
<td>${consent.status || ""}</td>
|
|
3545
4033
|
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
3546
4034
|
<td>${consent.dateTime || ""}</td>
|
|
@@ -3573,7 +4061,7 @@ var TypeScriptTemplateMapper = class {
|
|
|
3573
4061
|
resources,
|
|
3574
4062
|
timezone,
|
|
3575
4063
|
now
|
|
3576
|
-
) : templateClass.generateNarrative(resources, timezone);
|
|
4064
|
+
) : templateClass.generateNarrative(resources, timezone, now);
|
|
3577
4065
|
}
|
|
3578
4066
|
};
|
|
3579
4067
|
// Map of section types to their template classes
|
|
@@ -3852,6 +4340,12 @@ var ComprehensiveIPSCompositionBuilder = class {
|
|
|
3852
4340
|
if (sectionType === "Patient" /* PATIENT */) {
|
|
3853
4341
|
continue;
|
|
3854
4342
|
}
|
|
4343
|
+
const summaryIPSCompositionFilter = useSummaryCompositions ? IPSSectionResourceHelper.getSummaryIPSCompositionFilterForSection(sectionType) : void 0;
|
|
4344
|
+
const sectionIPSSummary = summaryIPSCompositionFilter ? resources.filter((resource) => summaryIPSCompositionFilter(resource)) : [];
|
|
4345
|
+
if (sectionIPSSummary.length > 0) {
|
|
4346
|
+
await this.makeSectionFromSummaryAsync(sectionType, sectionIPSSummary, resources, timezone);
|
|
4347
|
+
continue;
|
|
4348
|
+
}
|
|
3855
4349
|
const summaryCompositionFilter = useSummaryCompositions ? IPSSectionResourceHelper.getSummaryCompositionFilterForSection(sectionType) : void 0;
|
|
3856
4350
|
const sectionSummary = summaryCompositionFilter ? resources.filter((resource) => summaryCompositionFilter(resource)) : [];
|
|
3857
4351
|
if (sectionSummary.length > 0) {
|