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