@imranq2/fhirpatientsummary 1.0.29 → 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 +749 -267
- package/dist/index.js +749 -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,39 @@ 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))
|
|
163
198
|
};
|
|
164
199
|
var IPSSectionSummaryIPSCompositionFilter = {
|
|
165
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")
|
|
@@ -180,9 +215,46 @@ var IPSSectionResourceHelper = class {
|
|
|
180
215
|
return IPSSectionSummaryIPSCompositionFilter[section];
|
|
181
216
|
}
|
|
182
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
|
+
}
|
|
183
229
|
|
|
184
230
|
// src/narratives/templates/typescript/TemplateUtilities.ts
|
|
185
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
|
|
186
258
|
var TemplateUtilities = class {
|
|
187
259
|
/**
|
|
188
260
|
* Constructor to initialize the TemplateUtilities with a FHIR resources
|
|
@@ -192,47 +264,63 @@ var TemplateUtilities = class {
|
|
|
192
264
|
this.resources = resources;
|
|
193
265
|
}
|
|
194
266
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* @
|
|
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
|
|
199
272
|
*/
|
|
200
|
-
|
|
201
|
-
if (!
|
|
202
|
-
|
|
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
|
+
}
|
|
203
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 "";
|
|
204
294
|
if (field) {
|
|
205
295
|
if (cc[field]) {
|
|
206
296
|
return cc[field];
|
|
207
|
-
} else if (cc.coding && cc.coding
|
|
208
|
-
|
|
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
|
+
}
|
|
209
302
|
}
|
|
210
303
|
}
|
|
211
|
-
if (cc.text)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (
|
|
215
|
-
return cc.coding[0].display;
|
|
216
|
-
} else if (cc.coding[0].code) {
|
|
217
|
-
return cc.coding[0].code;
|
|
218
|
-
}
|
|
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;
|
|
219
308
|
}
|
|
220
309
|
return "";
|
|
221
310
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
});
|
|
235
|
-
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})` : "";
|
|
236
324
|
}
|
|
237
325
|
/**
|
|
238
326
|
* Renders a Device reference
|
|
@@ -280,7 +368,7 @@ var TemplateUtilities = class {
|
|
|
280
368
|
return "";
|
|
281
369
|
}
|
|
282
370
|
if (medicationType.medicationCodeableConcept) {
|
|
283
|
-
return this.
|
|
371
|
+
return this.codeableConceptDisplay(medicationType.medicationCodeableConcept);
|
|
284
372
|
} else if (medicationType.medicationReference) {
|
|
285
373
|
return this.renderMedicationRef(medicationType.medicationReference);
|
|
286
374
|
}
|
|
@@ -305,7 +393,7 @@ var TemplateUtilities = class {
|
|
|
305
393
|
*/
|
|
306
394
|
renderMedicationCode(medication) {
|
|
307
395
|
if (medication && medication.code) {
|
|
308
|
-
return this.renderTextAsHtml(this.
|
|
396
|
+
return this.renderTextAsHtml(this.codeableConceptDisplay(medication.code));
|
|
309
397
|
}
|
|
310
398
|
return "";
|
|
311
399
|
}
|
|
@@ -474,7 +562,7 @@ var TemplateUtilities = class {
|
|
|
474
562
|
*/
|
|
475
563
|
firstFromCodeableConceptList(list) {
|
|
476
564
|
if (list && Array.isArray(list) && list[0]) {
|
|
477
|
-
return this.renderTextAsHtml(this.
|
|
565
|
+
return this.renderTextAsHtml(this.codeableConceptDisplay(list[0]));
|
|
478
566
|
}
|
|
479
567
|
return "";
|
|
480
568
|
}
|
|
@@ -814,16 +902,28 @@ var TemplateUtilities = class {
|
|
|
814
902
|
return this.renderMedicationCode(medicationSource);
|
|
815
903
|
}
|
|
816
904
|
if (typeof medicationSource === "object" && ("coding" in medicationSource || "text" in medicationSource)) {
|
|
817
|
-
return this.
|
|
905
|
+
return this.codeableConceptDisplay(medicationSource);
|
|
818
906
|
}
|
|
819
907
|
if (typeof medicationSource === "object" && "reference" in medicationSource) {
|
|
820
908
|
const medication = this.resolveReference(medicationSource);
|
|
821
909
|
if (medication && medication.code) {
|
|
822
|
-
return this.
|
|
910
|
+
return this.codeableConceptDisplay(medication.code);
|
|
823
911
|
}
|
|
824
912
|
}
|
|
825
913
|
return "";
|
|
826
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
|
+
}
|
|
827
927
|
/**
|
|
828
928
|
* Public method to render plain text as HTML, escaping special characters and replacing newlines with <br />.
|
|
829
929
|
* This method should be used whenever displaying user-supplied or FHIR resource text in HTML to prevent XSS vulnerabilities
|
|
@@ -987,6 +1087,21 @@ var TemplateUtilities = class {
|
|
|
987
1087
|
const denominatorUnit = valueRatio.denominator.unit ? ` ${valueRatio.denominator.unit}` : "";
|
|
988
1088
|
return `${numerator}${numeratorUnit} / ${denominator}${denominatorUnit}`;
|
|
989
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
|
+
}
|
|
990
1105
|
};
|
|
991
1106
|
|
|
992
1107
|
// src/constants.ts
|
|
@@ -1615,7 +1730,7 @@ var PatientTemplate = class _PatientTemplate {
|
|
|
1615
1730
|
const uniqueLanguages = /* @__PURE__ */ new Set();
|
|
1616
1731
|
const preferredLanguages = /* @__PURE__ */ new Set();
|
|
1617
1732
|
patient.communication.forEach((comm) => {
|
|
1618
|
-
const language = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1733
|
+
const language = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(comm.language));
|
|
1619
1734
|
if (language) {
|
|
1620
1735
|
if (comm.preferred) {
|
|
1621
1736
|
preferredLanguages.add(language);
|
|
@@ -1662,14 +1777,18 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1662
1777
|
<thead>
|
|
1663
1778
|
<tr>
|
|
1664
1779
|
<th>Allergen</th>
|
|
1780
|
+
<th>Code (System)</th>
|
|
1665
1781
|
<th>Criticality</th>
|
|
1666
1782
|
<th>Recorded Date</th>
|
|
1783
|
+
<th>Source</th>
|
|
1667
1784
|
</tr>
|
|
1668
1785
|
</thead>
|
|
1669
1786
|
<tbody>`;
|
|
1670
1787
|
for (const resourceItem of resources) {
|
|
1671
1788
|
for (const rowData of resourceItem.section ?? []) {
|
|
1789
|
+
const sectionCodeableConcept = rowData.code;
|
|
1672
1790
|
const data = {};
|
|
1791
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
1673
1792
|
for (const columnData of rowData.section ?? []) {
|
|
1674
1793
|
switch (columnData.title) {
|
|
1675
1794
|
case "Allergen Name":
|
|
@@ -1681,6 +1800,9 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1681
1800
|
case "Recorded Date":
|
|
1682
1801
|
data["recordedDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1683
1802
|
break;
|
|
1803
|
+
case "Source":
|
|
1804
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1805
|
+
break;
|
|
1684
1806
|
default:
|
|
1685
1807
|
break;
|
|
1686
1808
|
}
|
|
@@ -1688,9 +1810,11 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1688
1810
|
isSummaryCreated = true;
|
|
1689
1811
|
html += `
|
|
1690
1812
|
<tr>
|
|
1691
|
-
<td>${data["allergen"] ?? "
|
|
1692
|
-
|
|
1693
|
-
<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>
|
|
1694
1818
|
</tr>`;
|
|
1695
1819
|
}
|
|
1696
1820
|
}
|
|
@@ -1738,10 +1862,12 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1738
1862
|
<tr>
|
|
1739
1863
|
<th>Allergen</th>
|
|
1740
1864
|
<th>Status</th>
|
|
1865
|
+
<th>Code (System)</th>
|
|
1741
1866
|
<th>Category</th>
|
|
1742
1867
|
<th>Reaction</th>
|
|
1743
1868
|
<th>Onset Date</th>
|
|
1744
1869
|
<th>Comments</th>
|
|
1870
|
+
<th>Source</th>
|
|
1745
1871
|
</tr>
|
|
1746
1872
|
</thead>
|
|
1747
1873
|
<tbody>`;
|
|
@@ -1765,11 +1891,13 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1765
1891
|
<tr>
|
|
1766
1892
|
<th>Allergen</th>
|
|
1767
1893
|
<th>Status</th>
|
|
1894
|
+
<th>Code (System)</th>
|
|
1768
1895
|
<th>Category</th>
|
|
1769
1896
|
<th>Reaction</th>
|
|
1770
1897
|
<th>Onset Date</th>
|
|
1771
1898
|
<th>Comments</th>
|
|
1772
1899
|
<th>Resolved Date</th>
|
|
1900
|
+
<th>Source</th>
|
|
1773
1901
|
</tr>
|
|
1774
1902
|
</thead>
|
|
1775
1903
|
<tbody>`;
|
|
@@ -1800,14 +1928,16 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
|
|
|
1800
1928
|
for (const allergy of allergies) {
|
|
1801
1929
|
html += `
|
|
1802
1930
|
<tr id="${templateUtilities.narrativeLinkId(allergy.extension)}">
|
|
1803
|
-
<td class="Name"><span class="AllergenName">${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1804
|
-
<td class="Status">${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
1805
|
-
<td class="
|
|
1806
|
-
<td class="
|
|
1807
|
-
<td class="
|
|
1808
|
-
<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>`;
|
|
1809
1939
|
if (includeResolved) {
|
|
1810
|
-
let endDate = "
|
|
1940
|
+
let endDate = "";
|
|
1811
1941
|
if (allergy.extension && Array.isArray(allergy.extension)) {
|
|
1812
1942
|
const endDateExt = allergy.extension.find(
|
|
1813
1943
|
(ext) => ext.url === "http://hl7.org/fhir/StructureDefinition/allergyintolerance-resolutionDate"
|
|
@@ -1831,10 +1961,11 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1831
1961
|
* Generate HTML narrative for Medication resources
|
|
1832
1962
|
* @param resources - FHIR Medication resources
|
|
1833
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())
|
|
1834
1965
|
* @returns HTML string for rendering
|
|
1835
1966
|
*/
|
|
1836
|
-
generateNarrative(resources, timezone) {
|
|
1837
|
-
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone);
|
|
1967
|
+
generateNarrative(resources, timezone, now) {
|
|
1968
|
+
return _MedicationSummaryTemplate.generateStaticNarrative(resources, timezone, now);
|
|
1838
1969
|
}
|
|
1839
1970
|
/**
|
|
1840
1971
|
* Generate HTML narrative for Medication resources using summary
|
|
@@ -1847,23 +1978,27 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1847
1978
|
const templateUtilities = new TemplateUtilities(resources);
|
|
1848
1979
|
let isSummaryCreated = false;
|
|
1849
1980
|
const currentDate = now || /* @__PURE__ */ new Date();
|
|
1850
|
-
const
|
|
1981
|
+
const twoYearsAgo = new Date(currentDate.getFullYear(), currentDate.getMonth() - 24, currentDate.getDate());
|
|
1851
1982
|
let html = `
|
|
1852
1983
|
<div>
|
|
1853
1984
|
<table>
|
|
1854
1985
|
<thead>
|
|
1855
1986
|
<tr>
|
|
1856
1987
|
<th>Medication</th>
|
|
1988
|
+
<th>Code (System)</th>
|
|
1857
1989
|
<th>Status</th>
|
|
1858
1990
|
<th>Sig</th>
|
|
1859
1991
|
<th>Days of Supply</th>
|
|
1860
1992
|
<th>Refills</th>
|
|
1861
1993
|
<th>Start Date</th>
|
|
1994
|
+
<th>Source</th>
|
|
1862
1995
|
</tr>
|
|
1863
1996
|
</thead>
|
|
1864
1997
|
<tbody>`;
|
|
1998
|
+
let skippedMedications = 0;
|
|
1865
1999
|
for (const resourceItem of resources) {
|
|
1866
2000
|
for (const rowData of resourceItem.section ?? []) {
|
|
2001
|
+
const sectionCodeableConcept = rowData.code;
|
|
1867
2002
|
const data = {};
|
|
1868
2003
|
for (const columnData of rowData.section ?? []) {
|
|
1869
2004
|
switch (columnData.title) {
|
|
@@ -1888,6 +2023,9 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1888
2023
|
case "Authored On Date":
|
|
1889
2024
|
data["startDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
1890
2025
|
break;
|
|
2026
|
+
case "Source":
|
|
2027
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2028
|
+
break;
|
|
1891
2029
|
default:
|
|
1892
2030
|
break;
|
|
1893
2031
|
}
|
|
@@ -1899,16 +2037,21 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1899
2037
|
startDateObj = void 0;
|
|
1900
2038
|
}
|
|
1901
2039
|
}
|
|
1902
|
-
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) {
|
|
1903
2044
|
isSummaryCreated = true;
|
|
1904
2045
|
html += `
|
|
1905
2046
|
<tr>
|
|
1906
2047
|
<td>${templateUtilities.renderTextAsHtml(data["medication"])}</td>
|
|
2048
|
+
<td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
|
|
1907
2049
|
<td>${templateUtilities.renderTextAsHtml(data["status"])}</td>
|
|
1908
2050
|
<td>${templateUtilities.renderTextAsHtml(data["sig-prescriber"] || data["sig-pharmacy"])}</td>
|
|
1909
2051
|
<td>${templateUtilities.renderTextAsHtml(data["daysOfSupply"])}</td>
|
|
1910
2052
|
<td>${templateUtilities.renderTextAsHtml(data["refills"])}</td>
|
|
1911
2053
|
<td>${templateUtilities.renderTime(data["startDate"], timezone)}</td>
|
|
2054
|
+
<td>${templateUtilities.renderTextAsHtml(data["source"])}</td>
|
|
1912
2055
|
</tr>`;
|
|
1913
2056
|
}
|
|
1914
2057
|
}
|
|
@@ -1917,7 +2060,14 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1917
2060
|
</tbody>
|
|
1918
2061
|
</table>
|
|
1919
2062
|
</div>`;
|
|
1920
|
-
|
|
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;
|
|
1921
2071
|
}
|
|
1922
2072
|
/**
|
|
1923
2073
|
* Safely parse a date string and return a valid Date object or null
|
|
@@ -1935,21 +2085,42 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1935
2085
|
* Internal static implementation that actually generates the narrative
|
|
1936
2086
|
* @param resources - FHIR Medication resources
|
|
1937
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())
|
|
1938
2089
|
* @returns HTML string for rendering
|
|
1939
2090
|
*/
|
|
1940
|
-
|
|
1941
|
-
static generateStaticNarrative(resources, timezone) {
|
|
2091
|
+
static generateStaticNarrative(resources, timezone, now) {
|
|
1942
2092
|
const templateUtilities = new TemplateUtilities(resources);
|
|
1943
2093
|
let html = "";
|
|
1944
2094
|
const medicationRequests = this.getMedicationRequests(templateUtilities, resources);
|
|
1945
2095
|
const medicationStatements = this.getMedicationStatements(templateUtilities, resources);
|
|
1946
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 = [];
|
|
1947
2102
|
medicationRequests.forEach((mr) => {
|
|
1948
|
-
|
|
2103
|
+
allMedications.push({ type: "request", resource: mr.resource, extension: mr.extension });
|
|
1949
2104
|
});
|
|
1950
2105
|
medicationStatements.forEach((ms) => {
|
|
1951
|
-
|
|
2106
|
+
allMedications.push({ type: "statement", resource: ms.resource, extension: ms.extension });
|
|
1952
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
|
+
}
|
|
1953
2124
|
const sortMedications = (medications) => {
|
|
1954
2125
|
medications.sort((a, b) => {
|
|
1955
2126
|
let dateStringA;
|
|
@@ -1980,7 +2151,14 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
1980
2151
|
sortMedications(allActiveMedications);
|
|
1981
2152
|
html += this.renderCombinedMedications(templateUtilities, allActiveMedications);
|
|
1982
2153
|
}
|
|
1983
|
-
|
|
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 "";
|
|
1984
2162
|
}
|
|
1985
2163
|
/**
|
|
1986
2164
|
* Extract MedicationRequest resources
|
|
@@ -2025,10 +2203,12 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
2025
2203
|
<tr>
|
|
2026
2204
|
<th>Type</th>
|
|
2027
2205
|
<th>Medication</th>
|
|
2206
|
+
<th>Code (System)</th>
|
|
2028
2207
|
<th>Sig</th>
|
|
2029
2208
|
<th>Dispense Quantity</th>
|
|
2030
2209
|
<th>Refills</th>
|
|
2031
2210
|
<th>Start Date</th>
|
|
2211
|
+
<th>Source</th>
|
|
2032
2212
|
</tr>
|
|
2033
2213
|
</thead>
|
|
2034
2214
|
<tbody>`;
|
|
@@ -2037,27 +2217,31 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
2037
2217
|
let type;
|
|
2038
2218
|
let medicationName;
|
|
2039
2219
|
let sig;
|
|
2040
|
-
let dispenseQuantity = "
|
|
2041
|
-
let refills = "
|
|
2042
|
-
let startDate = "
|
|
2220
|
+
let dispenseQuantity = "";
|
|
2221
|
+
let refills = "";
|
|
2222
|
+
let startDate = "";
|
|
2223
|
+
let codeSystemDisplay = "";
|
|
2043
2224
|
if (medication.type === "request") {
|
|
2044
2225
|
const mr = medication.resource;
|
|
2045
2226
|
type = "Request";
|
|
2046
2227
|
medicationName = templateUtilities.getMedicationName(
|
|
2047
2228
|
mr.medicationReference || mr.medicationCodeableConcept
|
|
2048
2229
|
);
|
|
2049
|
-
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "
|
|
2230
|
+
sig = templateUtilities.concat(mr.dosageInstruction, "text") || "";
|
|
2050
2231
|
if (mr.dispenseRequest?.quantity) {
|
|
2051
2232
|
const quantity = mr.dispenseRequest.quantity;
|
|
2052
2233
|
if (quantity.value) {
|
|
2053
2234
|
dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
|
|
2054
2235
|
}
|
|
2055
2236
|
}
|
|
2056
|
-
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "
|
|
2237
|
+
refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "";
|
|
2057
2238
|
if (mr.dispenseRequest?.validityPeriod) {
|
|
2058
|
-
startDate = mr.dispenseRequest.validityPeriod.start || "
|
|
2239
|
+
startDate = mr.dispenseRequest.validityPeriod.start || "";
|
|
2059
2240
|
} else {
|
|
2060
|
-
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);
|
|
2061
2245
|
}
|
|
2062
2246
|
} else {
|
|
2063
2247
|
const ms = medication.resource;
|
|
@@ -2065,21 +2249,26 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
|
|
|
2065
2249
|
medicationName = templateUtilities.getMedicationName(
|
|
2066
2250
|
ms.medicationReference || ms.medicationCodeableConcept
|
|
2067
2251
|
);
|
|
2068
|
-
sig = templateUtilities.concat(ms.dosage, "text") || "
|
|
2252
|
+
sig = templateUtilities.concat(ms.dosage, "text") || "";
|
|
2069
2253
|
if (ms.effectiveDateTime) {
|
|
2070
2254
|
startDate = ms.effectiveDateTime;
|
|
2071
2255
|
} else if (ms.effectivePeriod) {
|
|
2072
|
-
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);
|
|
2073
2260
|
}
|
|
2074
2261
|
}
|
|
2075
2262
|
html += `
|
|
2076
2263
|
<tr${narrativeLinkId ? ` id="${narrativeLinkId}"` : ""}>
|
|
2077
2264
|
<td>${type}</td>
|
|
2078
2265
|
<td>${medicationName}<ul></ul></td>
|
|
2266
|
+
<td>${codeSystemDisplay}</td>
|
|
2079
2267
|
<td>${sig}</td>
|
|
2080
2268
|
<td>${dispenseQuantity}</td>
|
|
2081
2269
|
<td>${refills}</td>
|
|
2082
2270
|
<td>${startDate}</td>
|
|
2271
|
+
<td>${templateUtilities.getOwnerTag(medication.resource)}</td>
|
|
2083
2272
|
</tr>`;
|
|
2084
2273
|
}
|
|
2085
2274
|
html += `
|
|
@@ -2120,14 +2309,18 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2120
2309
|
<thead>
|
|
2121
2310
|
<tr>
|
|
2122
2311
|
<th>Immunization</th>
|
|
2312
|
+
<th>Code (System)</th>
|
|
2123
2313
|
<th>Status</th>
|
|
2124
2314
|
<th>Date</th>
|
|
2315
|
+
<th>Source</th>
|
|
2125
2316
|
</tr>
|
|
2126
2317
|
</thead>
|
|
2127
2318
|
<tbody>`;
|
|
2128
2319
|
for (const resourceItem of resources) {
|
|
2129
2320
|
for (const rowData of resourceItem.section ?? []) {
|
|
2321
|
+
const sectionCodeableConcept = rowData.code;
|
|
2130
2322
|
const data = {};
|
|
2323
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2131
2324
|
for (const columnData of rowData.section ?? []) {
|
|
2132
2325
|
switch (columnData.title) {
|
|
2133
2326
|
case "Immunization Name":
|
|
@@ -2139,6 +2332,9 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2139
2332
|
case "occurrenceDateTime":
|
|
2140
2333
|
data["occurrenceDateTime"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2141
2334
|
break;
|
|
2335
|
+
case "Source":
|
|
2336
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2337
|
+
break;
|
|
2142
2338
|
default:
|
|
2143
2339
|
break;
|
|
2144
2340
|
}
|
|
@@ -2147,9 +2343,11 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2147
2343
|
isSummaryCreated = true;
|
|
2148
2344
|
html += `
|
|
2149
2345
|
<tr>
|
|
2150
|
-
<td>${data["immunization"] ?? "
|
|
2151
|
-
<td>${data["
|
|
2152
|
-
<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>
|
|
2153
2351
|
</tr>`;
|
|
2154
2352
|
}
|
|
2155
2353
|
}
|
|
@@ -2173,12 +2371,14 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2173
2371
|
<thead>
|
|
2174
2372
|
<tr>
|
|
2175
2373
|
<th>Immunization</th>
|
|
2374
|
+
<th>Code (System)</th>
|
|
2176
2375
|
<th>Status</th>
|
|
2177
2376
|
<th>Dose Number</th>
|
|
2178
2377
|
<th>Manufacturer</th>
|
|
2179
2378
|
<th>Lot Number</th>
|
|
2180
2379
|
<th>Comments</th>
|
|
2181
2380
|
<th>Date</th>
|
|
2381
|
+
<th>Source</th>
|
|
2182
2382
|
</tr>
|
|
2183
2383
|
</thead>
|
|
2184
2384
|
<tbody>`;
|
|
@@ -2188,13 +2388,15 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
|
|
|
2188
2388
|
const imm = resourceItem;
|
|
2189
2389
|
html += `
|
|
2190
2390
|
<tr id="${templateUtilities.narrativeLinkId(imm)}">
|
|
2191
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
2391
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(imm.vaccineCode))}</td>
|
|
2392
|
+
<td>${templateUtilities.codeableConceptCoding(imm.vaccineCode)}</td>
|
|
2192
2393
|
<td>${imm.status || ""}</td>
|
|
2193
2394
|
<td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
|
|
2194
2395
|
<td>${templateUtilities.renderVaccineManufacturer(imm)}</td>
|
|
2195
2396
|
<td>${imm.lotNumber || ""}</td>
|
|
2196
2397
|
<td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
|
|
2197
2398
|
<td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
|
|
2399
|
+
<td>${templateUtilities.getOwnerTag(imm)}</td>
|
|
2198
2400
|
</tr>`;
|
|
2199
2401
|
}
|
|
2200
2402
|
}
|
|
@@ -2228,8 +2430,11 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
2228
2430
|
let html = ``;
|
|
2229
2431
|
const activeConditions = resources.map((entry) => entry) || [];
|
|
2230
2432
|
activeConditions.sort((a, b) => {
|
|
2231
|
-
|
|
2232
|
-
|
|
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();
|
|
2233
2438
|
return dateB - dateA;
|
|
2234
2439
|
});
|
|
2235
2440
|
html += `
|
|
@@ -2237,22 +2442,28 @@ var ProblemListTemplate = class _ProblemListTemplate {
|
|
|
2237
2442
|
<thead>
|
|
2238
2443
|
<tr>
|
|
2239
2444
|
<th>Problem</th>
|
|
2445
|
+
<th>Code (System)</th>
|
|
2240
2446
|
<th>Onset Date</th>
|
|
2241
2447
|
<th>Recorded Date</th>
|
|
2448
|
+
<th>Source</th>
|
|
2242
2449
|
</tr>
|
|
2243
2450
|
</thead>
|
|
2244
2451
|
<tbody>`;
|
|
2245
|
-
const
|
|
2452
|
+
const seenCodeAndSystems = /* @__PURE__ */ new Set();
|
|
2246
2453
|
for (const cond of activeConditions) {
|
|
2247
|
-
const
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
<td class="Name">${conditionCode}</td>
|
|
2252
|
-
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
2253
|
-
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
2254
|
-
</tr>`;
|
|
2454
|
+
const conditionDisplay = templateUtilities.codeableConceptDisplay(cond.code);
|
|
2455
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(cond.code);
|
|
2456
|
+
if (codeAndSystem && seenCodeAndSystems.has(codeAndSystem)) {
|
|
2457
|
+
continue;
|
|
2255
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>`;
|
|
2256
2467
|
}
|
|
2257
2468
|
html += `</tbody>
|
|
2258
2469
|
</table>`;
|
|
@@ -2285,16 +2496,19 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2285
2496
|
<table>
|
|
2286
2497
|
<thead>
|
|
2287
2498
|
<tr>
|
|
2288
|
-
<th>
|
|
2499
|
+
<th>Name</th>
|
|
2500
|
+
<th>Code (System)</th>
|
|
2289
2501
|
<th>Result</th>
|
|
2290
|
-
<th>Reference Range</th>
|
|
2291
2502
|
<th>Date</th>
|
|
2503
|
+
<th>Source</th>
|
|
2292
2504
|
</tr>
|
|
2293
2505
|
</thead>
|
|
2294
2506
|
<tbody>`;
|
|
2295
2507
|
for (const resourceItem of resources) {
|
|
2296
2508
|
for (const rowData of resourceItem.section ?? []) {
|
|
2509
|
+
const sectionCodeableConcept = rowData.code;
|
|
2297
2510
|
const data = {};
|
|
2511
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2298
2512
|
for (const columnData of rowData.section ?? []) {
|
|
2299
2513
|
const columnTitle = columnData.title;
|
|
2300
2514
|
if (columnTitle) {
|
|
@@ -2322,10 +2536,11 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2322
2536
|
isSummaryCreated = true;
|
|
2323
2537
|
html += `
|
|
2324
2538
|
<tr>
|
|
2325
|
-
<td>${data["Vital Name"] ?? "
|
|
2326
|
-
<td>${
|
|
2327
|
-
<td>${templateUtilities.
|
|
2328
|
-
<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>
|
|
2329
2544
|
</tr>`;
|
|
2330
2545
|
}
|
|
2331
2546
|
}
|
|
@@ -2353,26 +2568,30 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
|
|
|
2353
2568
|
<table>
|
|
2354
2569
|
<thead>
|
|
2355
2570
|
<tr>
|
|
2356
|
-
<th>
|
|
2571
|
+
<th>Name</th>
|
|
2572
|
+
<th>Code (System)</th>
|
|
2357
2573
|
<th>Result</th>
|
|
2358
2574
|
<th>Unit</th>
|
|
2359
2575
|
<th>Interpretation</th>
|
|
2360
2576
|
<th>Component(s)</th>
|
|
2361
2577
|
<th>Comments</th>
|
|
2362
2578
|
<th>Date</th>
|
|
2579
|
+
<th>Source</th>
|
|
2363
2580
|
</tr>
|
|
2364
2581
|
</thead>
|
|
2365
2582
|
<tbody>`;
|
|
2366
2583
|
for (const obs of observations) {
|
|
2367
2584
|
html += `
|
|
2368
2585
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2369
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
2586
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code, "display"))}</td>
|
|
2587
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
2370
2588
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2371
2589
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
2372
2590
|
<td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
|
|
2373
2591
|
<td>${templateUtilities.renderComponent(obs.component)}</td>
|
|
2374
2592
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
2375
2593
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
2594
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
2376
2595
|
</tr>`;
|
|
2377
2596
|
}
|
|
2378
2597
|
html += `
|
|
@@ -2445,10 +2664,11 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2445
2664
|
* Generate HTML narrative for Diagnostic Results
|
|
2446
2665
|
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
2447
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
|
|
2448
2668
|
* @returns HTML string for rendering
|
|
2449
2669
|
*/
|
|
2450
|
-
generateNarrative(resources, timezone) {
|
|
2451
|
-
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone);
|
|
2670
|
+
generateNarrative(resources, timezone, now) {
|
|
2671
|
+
return _DiagnosticResultsTemplate.generateStaticNarrative(resources, timezone, now);
|
|
2452
2672
|
}
|
|
2453
2673
|
/**
|
|
2454
2674
|
* Helper function to format observation data fields
|
|
@@ -2661,6 +2881,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2661
2881
|
break;
|
|
2662
2882
|
case "valueRange.high.value":
|
|
2663
2883
|
targetData["valueRangeHighValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
2884
|
+
targetData["valueType"] = "valueRange";
|
|
2664
2885
|
break;
|
|
2665
2886
|
case "valueRange.high.unit":
|
|
2666
2887
|
targetData["valueRangeHighUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
@@ -2675,6 +2896,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2675
2896
|
break;
|
|
2676
2897
|
case "valueRatio.denominator.value":
|
|
2677
2898
|
targetData["valueRatioDenominatorValue"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
2899
|
+
targetData["valueType"] = "valueRatio";
|
|
2678
2900
|
break;
|
|
2679
2901
|
case "valueRatio.denominator.unit":
|
|
2680
2902
|
targetData["valueRatioDenominatorUnit"] = templateUtilities.renderTextAsHtml(column.text?.div ?? "");
|
|
@@ -2712,10 +2934,71 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2712
2934
|
* Generate HTML narrative for Diagnostic Results & Observation resources using summary
|
|
2713
2935
|
* @param resources - FHIR Composition resources
|
|
2714
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
|
|
2715
2938
|
* @returns HTML string for rendering
|
|
2716
2939
|
*/
|
|
2717
|
-
generateSummaryNarrative(resources, timezone) {
|
|
2940
|
+
generateSummaryNarrative(resources, timezone, now) {
|
|
2718
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
|
+
}
|
|
2719
3002
|
let html = `
|
|
2720
3003
|
<div>`;
|
|
2721
3004
|
let observationhtml = `
|
|
@@ -2724,10 +3007,12 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2724
3007
|
<table>
|
|
2725
3008
|
<thead>
|
|
2726
3009
|
<tr>
|
|
2727
|
-
<th>
|
|
3010
|
+
<th>Name</th>
|
|
3011
|
+
<th>Code (System)</th>
|
|
2728
3012
|
<th>Result</th>
|
|
2729
3013
|
<th>Reference Range</th>
|
|
2730
3014
|
<th>Date</th>
|
|
3015
|
+
<th>Source</th>
|
|
2731
3016
|
</tr>
|
|
2732
3017
|
</thead>
|
|
2733
3018
|
<tbody>`;
|
|
@@ -2740,6 +3025,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2740
3025
|
<th>Report</th>
|
|
2741
3026
|
<th>Performer</th>
|
|
2742
3027
|
<th>Issued</th>
|
|
3028
|
+
<th>Source</th>
|
|
2743
3029
|
</tr>
|
|
2744
3030
|
</thead>
|
|
2745
3031
|
<tbody>`;
|
|
@@ -2747,7 +3033,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2747
3033
|
const diagnosticReportAdded = /* @__PURE__ */ new Set();
|
|
2748
3034
|
for (const resourceItem of resources) {
|
|
2749
3035
|
for (const rowData of resourceItem.section ?? []) {
|
|
3036
|
+
const sectionCodeableConcept = rowData.code;
|
|
2750
3037
|
const data = {};
|
|
3038
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2751
3039
|
const components = [];
|
|
2752
3040
|
for (const columnData of rowData.section ?? []) {
|
|
2753
3041
|
if (resourceItem.title === "Observation|Labs Summary Grouped by Lab Code") {
|
|
@@ -2757,7 +3045,13 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2757
3045
|
for (const nestedColumn of componentSection.section ?? []) {
|
|
2758
3046
|
this.extractSummaryObservationFields(nestedColumn, componentData, templateUtilities);
|
|
2759
3047
|
}
|
|
2760
|
-
|
|
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) {
|
|
2761
3055
|
components.push(componentData);
|
|
2762
3056
|
}
|
|
2763
3057
|
}
|
|
@@ -2778,6 +3072,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2778
3072
|
case "Status":
|
|
2779
3073
|
data["status"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2780
3074
|
break;
|
|
3075
|
+
case "Source":
|
|
3076
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
3077
|
+
break;
|
|
2781
3078
|
default:
|
|
2782
3079
|
break;
|
|
2783
3080
|
}
|
|
@@ -2785,6 +3082,12 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2785
3082
|
}
|
|
2786
3083
|
if (resourceItem.title === "Observation|Labs Summary Grouped by Lab Code") {
|
|
2787
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
|
+
}
|
|
2788
3091
|
if (!date && data["effectivePeriodStart"]) {
|
|
2789
3092
|
date = templateUtilities.renderTime(data["effectivePeriodStart"], timezone);
|
|
2790
3093
|
if (data["effectivePeriodEnd"]) {
|
|
@@ -2801,36 +3104,47 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2801
3104
|
observationhtml += `
|
|
2802
3105
|
<tr>
|
|
2803
3106
|
<td>${componentCode}</td>
|
|
2804
|
-
<td
|
|
2805
|
-
<td>${templateUtilities.renderTextAsHtml(component["
|
|
2806
|
-
<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>
|
|
2807
3112
|
</tr>`;
|
|
2808
3113
|
}
|
|
2809
3114
|
}
|
|
2810
3115
|
} else {
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
observationAdded.
|
|
2814
|
-
|
|
2815
|
-
|
|
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 += `
|
|
2816
3122
|
<tr>
|
|
2817
|
-
<td>${data["code"] ?? "
|
|
2818
|
-
<td>${templateUtilities.
|
|
2819
|
-
<td>${templateUtilities.renderTextAsHtml(data["
|
|
2820
|
-
<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>
|
|
2821
3129
|
</tr>`;
|
|
3130
|
+
}
|
|
2822
3131
|
}
|
|
2823
3132
|
}
|
|
2824
3133
|
} else if (resourceItem.title === "DiagnosticReportLab Summary Grouped by DiagnosticReport|Lab Code") {
|
|
2825
|
-
|
|
3134
|
+
let issuedDate = void 0;
|
|
3135
|
+
if (data["issued"]) {
|
|
3136
|
+
issuedDate = new Date(data["issued"]);
|
|
3137
|
+
}
|
|
3138
|
+
if (data["status"] === "final" && issuedDate && issuedDate >= twoYearsAgo) {
|
|
2826
3139
|
const reportName = data["report"] ?? "";
|
|
2827
3140
|
if (reportName && !diagnosticReportAdded.has(reportName)) {
|
|
2828
3141
|
diagnosticReportAdded.add(reportName);
|
|
2829
3142
|
diagnosticReporthtml += `
|
|
2830
3143
|
<tr>
|
|
2831
|
-
<td>${data["report"] ?? "
|
|
2832
|
-
<td>${data["performer"] ?? "
|
|
2833
|
-
<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>
|
|
2834
3148
|
</tr>`;
|
|
2835
3149
|
}
|
|
2836
3150
|
}
|
|
@@ -2843,6 +3157,10 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2843
3157
|
</tbody>
|
|
2844
3158
|
</table>
|
|
2845
3159
|
</div>`;
|
|
3160
|
+
if (skippedObservations > 0) {
|
|
3161
|
+
html += `
|
|
3162
|
+
<p><em>${skippedObservations} additional observations older than 2 years ago are present</em></p>`;
|
|
3163
|
+
}
|
|
2846
3164
|
}
|
|
2847
3165
|
if (diagnosticReportAdded.size > 0) {
|
|
2848
3166
|
html += diagnosticReporthtml;
|
|
@@ -2850,6 +3168,10 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2850
3168
|
</tbody>
|
|
2851
3169
|
</table>
|
|
2852
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
|
+
}
|
|
2853
3175
|
}
|
|
2854
3176
|
html += `
|
|
2855
3177
|
</div>`;
|
|
@@ -2859,12 +3181,32 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2859
3181
|
* Internal static implementation that actually generates the narrative
|
|
2860
3182
|
* @param resources - FHIR resources array containing Observation and DiagnosticReport resources
|
|
2861
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
|
|
2862
3185
|
* @returns HTML string for rendering
|
|
2863
3186
|
*/
|
|
2864
|
-
static generateStaticNarrative(resources, timezone) {
|
|
3187
|
+
static generateStaticNarrative(resources, timezone, now) {
|
|
2865
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);
|
|
2866
3192
|
let html = "";
|
|
2867
|
-
|
|
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);
|
|
2868
3210
|
if (observations.length > 0) {
|
|
2869
3211
|
observations.sort((a, b) => {
|
|
2870
3212
|
const dateA = this.getObservationDate(a);
|
|
@@ -2873,16 +3215,22 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2873
3215
|
});
|
|
2874
3216
|
this.filterObservationForLoincCodes(observations);
|
|
2875
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
|
+
}
|
|
2876
3222
|
}
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
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>`;
|
|
2886
3234
|
}
|
|
2887
3235
|
}
|
|
2888
3236
|
return html;
|
|
@@ -2927,15 +3275,16 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2927
3275
|
return obsDate;
|
|
2928
3276
|
}
|
|
2929
3277
|
/**
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
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) {
|
|
2935
3284
|
return resources.filter((resourceItem) => {
|
|
2936
3285
|
if (resourceItem.resourceType === "Observation") {
|
|
2937
3286
|
const obsDate = this.getObservationDate(resourceItem);
|
|
2938
|
-
if (obsDate && obsDate >=
|
|
3287
|
+
if (obsDate && obsDate >= twoYearsAgo) {
|
|
2939
3288
|
return true;
|
|
2940
3289
|
}
|
|
2941
3290
|
}
|
|
@@ -2943,12 +3292,21 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2943
3292
|
}).map((resourceItem) => resourceItem);
|
|
2944
3293
|
}
|
|
2945
3294
|
/**
|
|
2946
|
-
* Get all DiagnosticReport resources from the resource array
|
|
3295
|
+
* Get all DiagnosticReport resources from the resource array, filtered by twoYearsAgo
|
|
2947
3296
|
* @param resources - FHIR resources array
|
|
3297
|
+
* @param twoYearsAgo - Date object representing the cutoff
|
|
2948
3298
|
* @returns Array of DiagnosticReport resources
|
|
2949
3299
|
*/
|
|
2950
|
-
static getDiagnosticReports(resources) {
|
|
2951
|
-
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);
|
|
2952
3310
|
}
|
|
2953
3311
|
/**
|
|
2954
3312
|
* Render HTML table for Observation resources
|
|
@@ -2959,32 +3317,36 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
2959
3317
|
*/
|
|
2960
3318
|
static renderObservations(templateUtilities, observations, timezone) {
|
|
2961
3319
|
let html = "";
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
<h3>Observations</h3>`;
|
|
2965
|
-
}
|
|
3320
|
+
html += `
|
|
3321
|
+
<h3>Observations</h3>`;
|
|
2966
3322
|
html += `
|
|
2967
3323
|
<table>
|
|
2968
3324
|
<thead>
|
|
2969
3325
|
<tr>
|
|
2970
|
-
<th>
|
|
3326
|
+
<th>Name</th>
|
|
3327
|
+
<th>Code (System)</th>
|
|
2971
3328
|
<th>Result</th>
|
|
2972
3329
|
<th>Reference Range</th>
|
|
2973
3330
|
<th>Date</th>
|
|
3331
|
+
<th>Source</th>
|
|
2974
3332
|
</tr>
|
|
2975
3333
|
</thead>
|
|
2976
3334
|
<tbody>`;
|
|
2977
3335
|
const observationAdded = /* @__PURE__ */ new Set();
|
|
2978
3336
|
for (const obs of observations) {
|
|
2979
|
-
const
|
|
2980
|
-
|
|
2981
|
-
|
|
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);
|
|
2982
3342
|
html += `
|
|
2983
3343
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
2984
|
-
<td>${
|
|
3344
|
+
<td>${obsCodeDisplay}</td>
|
|
3345
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
2985
3346
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
2986
3347
|
<td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
|
|
2987
3348
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
3349
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
2988
3350
|
</tr>`;
|
|
2989
3351
|
}
|
|
2990
3352
|
}
|
|
@@ -3007,17 +3369,21 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
3007
3369
|
<thead>
|
|
3008
3370
|
<tr>
|
|
3009
3371
|
<th>Report</th>
|
|
3372
|
+
<th>Code (System)</th>
|
|
3010
3373
|
<th>Category</th>
|
|
3011
3374
|
<th>Result</th>
|
|
3012
3375
|
<th>Issued</th>
|
|
3376
|
+
<th>Source</th>
|
|
3013
3377
|
</tr>
|
|
3014
3378
|
</thead>
|
|
3015
3379
|
<tbody>`;
|
|
3016
3380
|
const diagnosticReportAdded = /* @__PURE__ */ new Set();
|
|
3017
3381
|
for (const report of reports) {
|
|
3018
|
-
const reportName = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3019
|
-
|
|
3382
|
+
const reportName = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(report.code));
|
|
3383
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(report.code);
|
|
3384
|
+
if (!diagnosticReportAdded.has(reportName) && !diagnosticReportAdded.has(codeAndSystem)) {
|
|
3020
3385
|
diagnosticReportAdded.add(reportName);
|
|
3386
|
+
diagnosticReportAdded.add(codeAndSystem);
|
|
3021
3387
|
let resultCount = "";
|
|
3022
3388
|
if (report.result && Array.isArray(report.result)) {
|
|
3023
3389
|
resultCount = `${report.result.length} result${report.result.length !== 1 ? "s" : ""}`;
|
|
@@ -3025,9 +3391,11 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
3025
3391
|
html += `
|
|
3026
3392
|
<tr id="${templateUtilities.narrativeLinkId(report)}">
|
|
3027
3393
|
<td>${reportName}</td>
|
|
3394
|
+
<td>${codeAndSystem}</td>
|
|
3028
3395
|
<td>${templateUtilities.firstFromCodeableConceptList(report.category)}</td>
|
|
3029
3396
|
<td>${resultCount}</td>
|
|
3030
3397
|
<td>${report.issued ? templateUtilities.renderTime(report.issued, timezone) : ""}</td>
|
|
3398
|
+
<td>${templateUtilities.getOwnerTag(report)}</td>
|
|
3031
3399
|
</tr>`;
|
|
3032
3400
|
}
|
|
3033
3401
|
}
|
|
@@ -3036,6 +3404,26 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
|
|
|
3036
3404
|
</table>`;
|
|
3037
3405
|
return html;
|
|
3038
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
|
+
}
|
|
3039
3427
|
};
|
|
3040
3428
|
|
|
3041
3429
|
// src/narratives/templates/typescript/HistoryOfProceduresTemplate.ts
|
|
@@ -3069,14 +3457,18 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3069
3457
|
<thead>
|
|
3070
3458
|
<tr>
|
|
3071
3459
|
<th>Procedure</th>
|
|
3460
|
+
<th>Code (System)</th>
|
|
3072
3461
|
<th>Performer</th>
|
|
3073
3462
|
<th>Date</th>
|
|
3463
|
+
<th>Source</th>
|
|
3074
3464
|
</tr>
|
|
3075
3465
|
</thead>
|
|
3076
3466
|
<tbody>`;
|
|
3077
3467
|
for (const resourceItem of resources) {
|
|
3078
3468
|
for (const rowData of resourceItem.section ?? []) {
|
|
3469
|
+
const sectionCodeableConcept = rowData.code;
|
|
3079
3470
|
const data = {};
|
|
3471
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
3080
3472
|
for (const columnData of rowData.section ?? []) {
|
|
3081
3473
|
switch (columnData.title) {
|
|
3082
3474
|
case "Procedure Name":
|
|
@@ -3088,6 +3480,9 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3088
3480
|
case "Performed Date":
|
|
3089
3481
|
data["date"] = columnData.text?.div ?? "";
|
|
3090
3482
|
break;
|
|
3483
|
+
case "Source":
|
|
3484
|
+
data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
3485
|
+
break;
|
|
3091
3486
|
default:
|
|
3092
3487
|
break;
|
|
3093
3488
|
}
|
|
@@ -3095,9 +3490,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3095
3490
|
isSummaryCreated = true;
|
|
3096
3491
|
html += `
|
|
3097
3492
|
<tr>
|
|
3098
|
-
<td>${data["procedure"] ?? "
|
|
3099
|
-
|
|
3100
|
-
<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>
|
|
3101
3498
|
</tr>`;
|
|
3102
3499
|
}
|
|
3103
3500
|
}
|
|
@@ -3120,8 +3517,10 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3120
3517
|
<thead>
|
|
3121
3518
|
<tr>
|
|
3122
3519
|
<th>Procedure</th>
|
|
3520
|
+
<th>Code (System)</th>
|
|
3123
3521
|
<th>Comments</th>
|
|
3124
3522
|
<th>Date</th>
|
|
3523
|
+
<th>Source</th>
|
|
3125
3524
|
</tr>
|
|
3126
3525
|
</thead>
|
|
3127
3526
|
<tbody>`;
|
|
@@ -3129,9 +3528,11 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
|
|
|
3129
3528
|
const proc = resourceItem;
|
|
3130
3529
|
html += `
|
|
3131
3530
|
<tr id="${templateUtilities.narrativeLinkId(proc)}">
|
|
3132
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3531
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(proc.code, "display"))}</td>
|
|
3532
|
+
<td>${templateUtilities.codeableConceptCoding(proc.code)}</td>
|
|
3133
3533
|
<td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
|
|
3134
3534
|
<td>${proc.performedDateTime ? templateUtilities.renderTime(proc.performedDateTime, timezone) : proc.performedPeriod ? templateUtilities.renderPeriod(proc.performedPeriod, timezone) : ""}</td>
|
|
3535
|
+
<td>${templateUtilities.getOwnerTag(proc)}</td>
|
|
3135
3536
|
</tr>`;
|
|
3136
3537
|
}
|
|
3137
3538
|
html += `
|
|
@@ -3170,22 +3571,26 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
|
|
|
3170
3571
|
<table>
|
|
3171
3572
|
<thead>
|
|
3172
3573
|
<tr>
|
|
3173
|
-
<th>
|
|
3574
|
+
<th>Name</th>
|
|
3575
|
+
<th>Code (System)</th>
|
|
3174
3576
|
<th>Result</th>
|
|
3175
3577
|
<th>Unit</th>
|
|
3176
3578
|
<th>Comments</th>
|
|
3177
3579
|
<th>Date</th>
|
|
3580
|
+
<th>Source</th>
|
|
3178
3581
|
</tr>
|
|
3179
3582
|
</thead>
|
|
3180
3583
|
<tbody>`;
|
|
3181
3584
|
for (const obs of observations) {
|
|
3182
3585
|
html += `
|
|
3183
3586
|
<tr id="${templateUtilities.narrativeLinkId(obs)}">
|
|
3184
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3587
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code))}</td>
|
|
3588
|
+
<td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
|
|
3185
3589
|
<td>${templateUtilities.extractObservationValue(obs)}</td>
|
|
3186
3590
|
<td>${templateUtilities.extractObservationValueUnit(obs)}</td>
|
|
3187
3591
|
<td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
|
|
3188
3592
|
<td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
|
|
3593
|
+
<td>${templateUtilities.getOwnerTag(obs)}</td>
|
|
3189
3594
|
</tr>`;
|
|
3190
3595
|
}
|
|
3191
3596
|
html += `
|
|
@@ -3201,14 +3606,26 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
3201
3606
|
* Generate HTML narrative for Past History of Illnesses
|
|
3202
3607
|
* @param resources - FHIR Condition resources
|
|
3203
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
|
|
3204
3610
|
* @returns HTML string for rendering
|
|
3205
3611
|
*/
|
|
3206
|
-
|
|
3207
|
-
generateNarrative(resources, timezone) {
|
|
3612
|
+
generateNarrative(resources, timezone, now) {
|
|
3208
3613
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3209
3614
|
let html = ``;
|
|
3210
3615
|
const resolvedConditions = resources.map((entry) => entry) || [];
|
|
3211
|
-
|
|
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) => {
|
|
3212
3629
|
const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
|
|
3213
3630
|
const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
|
|
3214
3631
|
return dateB - dateA;
|
|
@@ -3218,27 +3635,35 @@ var PastHistoryOfIllnessTemplate = class {
|
|
|
3218
3635
|
<thead>
|
|
3219
3636
|
<tr>
|
|
3220
3637
|
<th>Problem</th>
|
|
3638
|
+
<th>Code (System)</th>
|
|
3221
3639
|
<th>Onset Date</th>
|
|
3222
3640
|
<th>Recorded Date</th>
|
|
3223
3641
|
<th>Resolved Date</th>
|
|
3642
|
+
<th>Source</th>
|
|
3224
3643
|
</tr>
|
|
3225
3644
|
</thead>
|
|
3226
3645
|
<tbody>`;
|
|
3227
3646
|
const addedConditionCodes = /* @__PURE__ */ new Set();
|
|
3228
|
-
for (const cond of
|
|
3229
|
-
const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.
|
|
3647
|
+
for (const cond of filteredConditions) {
|
|
3648
|
+
const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(cond.code));
|
|
3230
3649
|
if (!addedConditionCodes.has(conditionCode)) {
|
|
3231
3650
|
addedConditionCodes.add(conditionCode);
|
|
3232
3651
|
html += `<tr id="${templateUtilities.narrativeLinkId(cond)}">
|
|
3233
3652
|
<td class="Name">${conditionCode}</td>
|
|
3653
|
+
<td class="CodeSystem">${templateUtilities.codeableConceptCoding(cond.code)}</td>
|
|
3234
3654
|
<td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
|
|
3235
3655
|
<td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
|
|
3236
3656
|
<td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
|
|
3657
|
+
<td class="Source">${templateUtilities.getOwnerTag(cond)}</td>
|
|
3237
3658
|
</tr>`;
|
|
3238
3659
|
}
|
|
3239
3660
|
}
|
|
3240
3661
|
html += `</tbody>
|
|
3241
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
|
+
}
|
|
3242
3667
|
return html;
|
|
3243
3668
|
}
|
|
3244
3669
|
};
|
|
@@ -3268,6 +3693,7 @@ var PlanOfCareTemplate = class {
|
|
|
3268
3693
|
<th>Comments</th>
|
|
3269
3694
|
<th>Planned Start</th>
|
|
3270
3695
|
<th>Planned End</th>
|
|
3696
|
+
<th>Source</th>
|
|
3271
3697
|
</tr>
|
|
3272
3698
|
</thead>
|
|
3273
3699
|
<tbody>`;
|
|
@@ -3279,6 +3705,7 @@ var PlanOfCareTemplate = class {
|
|
|
3279
3705
|
<td>${templateUtilities.concat(cp.note, "text")}</td>
|
|
3280
3706
|
<td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
|
|
3281
3707
|
<td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
|
|
3708
|
+
<td>${templateUtilities.getOwnerTag(cp)}</td>
|
|
3282
3709
|
</tr>`;
|
|
3283
3710
|
}
|
|
3284
3711
|
html += `
|
|
@@ -3304,6 +3731,7 @@ var PlanOfCareTemplate = class {
|
|
|
3304
3731
|
<th>Created</th>
|
|
3305
3732
|
<th>Planned Start</th>
|
|
3306
3733
|
<th>Planned End</th>
|
|
3734
|
+
<th>Source</th>
|
|
3307
3735
|
</tr>
|
|
3308
3736
|
</thead>
|
|
3309
3737
|
<tbody>`;
|
|
@@ -3321,10 +3749,11 @@ var PlanOfCareTemplate = class {
|
|
|
3321
3749
|
isSummaryCreated = true;
|
|
3322
3750
|
html += `
|
|
3323
3751
|
<tr>
|
|
3324
|
-
<td>${data["CarePlan Name"] ?? "
|
|
3325
|
-
<td>${templateUtilities.renderTime(data["created"], timezone) ?? "
|
|
3326
|
-
<td>${templateUtilities.renderTime(data["period.start"], timezone) ?? "
|
|
3327
|
-
<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>
|
|
3328
3757
|
</tr>`;
|
|
3329
3758
|
}
|
|
3330
3759
|
}
|
|
@@ -3355,77 +3784,54 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
3355
3784
|
*/
|
|
3356
3785
|
static generateStaticNarrative(resources, timezone) {
|
|
3357
3786
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3358
|
-
let html =
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
|
|
3370
|
-
const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
|
|
3371
|
-
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;
|
|
3372
3798
|
});
|
|
3373
|
-
|
|
3374
|
-
const
|
|
3375
|
-
|
|
3376
|
-
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);
|
|
3377
3802
|
});
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
const
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
}
|
|
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>`;
|
|
3400
3824
|
}
|
|
3401
|
-
html += `</tbody
|
|
3402
|
-
</table>`;
|
|
3825
|
+
html += `</tbody></table>`;
|
|
3403
3826
|
}
|
|
3404
3827
|
if (clinicalImpressions.length > 0) {
|
|
3405
|
-
html += `<
|
|
3406
|
-
<table>
|
|
3407
|
-
<thead>
|
|
3408
|
-
<tr>
|
|
3409
|
-
<th>Date</th>
|
|
3410
|
-
<th>Status</th>
|
|
3411
|
-
<th>Description</th>
|
|
3412
|
-
<th>Summary</th>
|
|
3413
|
-
<th>Findings</th>
|
|
3414
|
-
</tr>
|
|
3415
|
-
</thead>
|
|
3416
|
-
<tbody>`;
|
|
3828
|
+
html += `<table><thead><tr><th>Date</th><th>Status</th><th>Description</th><th>Summary</th><th>Findings</th></tr></thead><tbody>`;
|
|
3417
3829
|
for (const impression of clinicalImpressions) {
|
|
3418
3830
|
let formattedDate = "";
|
|
3419
3831
|
if (impression.effectiveDateTime) {
|
|
3420
|
-
formattedDate = templateUtilities.renderTime(
|
|
3421
|
-
impression.effectiveDateTime,
|
|
3422
|
-
timezone
|
|
3423
|
-
);
|
|
3832
|
+
formattedDate = templateUtilities.renderTime(impression.effectiveDateTime, timezone);
|
|
3424
3833
|
} else if (impression.effectivePeriod) {
|
|
3425
|
-
formattedDate = templateUtilities.renderPeriod(
|
|
3426
|
-
impression.effectivePeriod,
|
|
3427
|
-
timezone
|
|
3428
|
-
);
|
|
3834
|
+
formattedDate = templateUtilities.renderPeriod(impression.effectivePeriod, timezone);
|
|
3429
3835
|
} else if (impression.date) {
|
|
3430
3836
|
formattedDate = templateUtilities.renderDate(impression.date);
|
|
3431
3837
|
}
|
|
@@ -3433,23 +3839,24 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
3433
3839
|
if (impression.finding && impression.finding.length > 0) {
|
|
3434
3840
|
findingsHtml = "<ul>";
|
|
3435
3841
|
for (const finding of impression.finding) {
|
|
3436
|
-
const findingText = finding.itemCodeableConcept ? templateUtilities.
|
|
3842
|
+
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConceptDisplay(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
3437
3843
|
const cause = finding.basis || "";
|
|
3438
3844
|
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
3439
3845
|
}
|
|
3440
3846
|
findingsHtml += "</ul>";
|
|
3441
3847
|
}
|
|
3442
|
-
html +=
|
|
3443
|
-
<
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
</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>`;
|
|
3450
3855
|
}
|
|
3451
|
-
html += `</tbody
|
|
3452
|
-
|
|
3856
|
+
html += `</tbody></table>`;
|
|
3857
|
+
}
|
|
3858
|
+
if (functionalObservations.length === 0 && clinicalImpressions.length === 0) {
|
|
3859
|
+
html += `<p>No functional status information available.</p>`;
|
|
3453
3860
|
}
|
|
3454
3861
|
return html;
|
|
3455
3862
|
}
|
|
@@ -3474,34 +3881,109 @@ var PregnancyTemplate = class _PregnancyTemplate {
|
|
|
3474
3881
|
*/
|
|
3475
3882
|
static generateStaticNarrative(resources, timezone) {
|
|
3476
3883
|
const templateUtilities = new TemplateUtilities(resources);
|
|
3477
|
-
const
|
|
3478
|
-
|
|
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) => {
|
|
3479
3892
|
const dateA = a.effectiveDateTime || a.effectivePeriod?.start;
|
|
3480
3893
|
const dateB = b.effectiveDateTime || b.effectivePeriod?.start;
|
|
3481
|
-
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;
|
|
3482
3907
|
});
|
|
3908
|
+
if (!pregnancyStatusObs && !eddObs && historyObs.length === 0 && conditions.length === 0) {
|
|
3909
|
+
return `<p>No history of pregnancy found.</p>`;
|
|
3910
|
+
}
|
|
3483
3911
|
let html = `
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
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 }) {
|
|
3495
3924
|
html += `
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
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 });
|
|
3501
3983
|
}
|
|
3502
3984
|
html += `
|
|
3503
|
-
|
|
3504
|
-
|
|
3985
|
+
</tbody>
|
|
3986
|
+
</table>`;
|
|
3505
3987
|
return html;
|
|
3506
3988
|
}
|
|
3507
3989
|
};
|
|
@@ -3546,7 +4028,7 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
3546
4028
|
const consent = resourceItem;
|
|
3547
4029
|
html += `
|
|
3548
4030
|
<tr id="${templateUtilities.narrativeLinkId(consent)}">
|
|
3549
|
-
<td>${templateUtilities.renderTextAsHtml(templateUtilities.
|
|
4031
|
+
<td>${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(consent.scope, "display"))}</td>
|
|
3550
4032
|
<td>${consent.status || ""}</td>
|
|
3551
4033
|
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
3552
4034
|
<td>${consent.dateTime || ""}</td>
|
|
@@ -3579,7 +4061,7 @@ var TypeScriptTemplateMapper = class {
|
|
|
3579
4061
|
resources,
|
|
3580
4062
|
timezone,
|
|
3581
4063
|
now
|
|
3582
|
-
) : templateClass.generateNarrative(resources, timezone);
|
|
4064
|
+
) : templateClass.generateNarrative(resources, timezone, now);
|
|
3583
4065
|
}
|
|
3584
4066
|
};
|
|
3585
4067
|
// Map of section types to their template classes
|