@imranq2/fhirpatientsummary 1.0.37 → 1.0.39

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.
Files changed (3) hide show
  1. package/dist/index.cjs +410 -103
  2. package/dist/index.js +410 -103
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -138,6 +138,85 @@ var ESSENTIAL_LAB_PANELS = {
138
138
  "80235-8": "Cardiac Markers Panel",
139
139
  "69738-3": "CBC with Auto Differential"
140
140
  };
141
+ var FUNCTIONAL_STATUS_SNOMED_CODES = {
142
+ "118233009": "Core Functional Status",
143
+ "364644000": "Core Functional Status",
144
+ "160245001": "Core Functional Status",
145
+ "21134002": "Disability & Impairment",
146
+ "2655002": "Disability & Impairment",
147
+ "2219003": "Disability & Impairment",
148
+ "962000": "Disability & Impairment",
149
+ "3089009": "Disability & Impairment",
150
+ "4197006": "Disability & Impairment",
151
+ "284545001": "Activities of Daily Living",
152
+ "719930002": "Activities of Daily Living",
153
+ "301438001": "Activities of Daily Living",
154
+ "282145008": "Mobility & Movement",
155
+ "165245003": "Mobility & Movement",
156
+ "713458007": "Mobility & Movement"
157
+ };
158
+ var FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES = {
159
+ "54522-8": "Functional Status Panel",
160
+ "54523-6": "ADL Assistance",
161
+ "45588-1": "Bed Mobility",
162
+ "45589-9": "Bed Mobility",
163
+ "45590-7": "Transfer",
164
+ "45591-5": "Transfer",
165
+ "45592-3": "Walk in room",
166
+ "45593-1": "Walk in room",
167
+ "45594-9": "Walk in corridor",
168
+ "45595-6": "Walk in corridor",
169
+ "45596-4": "Locomotion on unit",
170
+ "45597-2": "Locomotion on unit",
171
+ "45598-0": "Locomotion off unit",
172
+ "45599-8": "Locomotion off unit",
173
+ "45600-4": "Dressing",
174
+ "45601-2": "Dressing",
175
+ "45602-0": "Eating",
176
+ "45603-8": "Eating",
177
+ "45604-6": "Toilet use",
178
+ "45605-3": "Toilet use",
179
+ "45606-1": "Personal hygiene",
180
+ "45607-9": "Personal hygiene",
181
+ "46008-9": "Bathing",
182
+ "45608-7": "Bathing",
183
+ "45609-5": "Bathing",
184
+ "54524-4": "Balance during Transitions and Walking",
185
+ "54749-7": "Moving from Seated to Standing",
186
+ "54750-5": "Walking with Assistive Device",
187
+ "54751-3": "Turning Around While Walking",
188
+ "54753-9": "Surface-to-Surface Transfer",
189
+ "54525-1": "Functional Limitation in Range of Motion",
190
+ "54754-7": "Upper Extremity ROM",
191
+ "54755-4": "Lower Extremity ROM",
192
+ "54526-9": "Mobility Devices",
193
+ "54756-2": "Cane/Crutch",
194
+ "54757-0": "Walker",
195
+ "54758-8": "Wheelchair",
196
+ "54759-6": "Limb Prosthesis",
197
+ "54760-4": "No Mobility Devices",
198
+ "54527-7": "Functional Rehabilitation Potential",
199
+ "55123-4": "Resident Believes Capable of Increased Independence",
200
+ "45613-7": "Staff Believes Capable of Increased Independence"
201
+ };
202
+ var ADVANCED_DIRECTIVE_CATEGORY_CODES = {
203
+ acd: "Advance Directive",
204
+ dnr: "Do Not Resuscitate",
205
+ polst: "POLST",
206
+ hcd: "Health Care Directive"
207
+ };
208
+ var ADVANCED_DIRECTIVE_CATEGORY_SYSTEM = "http://terminology.hl7.org/CodeSystem/consentcategorycodes";
209
+ var ADVANCED_DIRECTIVE_LOINC_CODES = {
210
+ "45473-6": "Advance healthcare directive Completed",
211
+ "45474-4": "Do not resuscitate",
212
+ "45475-1": "Do not hospitalize",
213
+ "45476-9": "Organ donation",
214
+ "45477-7": "Autopsy request",
215
+ "45478-5": "Feeding restrictions",
216
+ "45479-3": "Medication restrictions",
217
+ "45480-1": "Other treatment restrictions"
218
+ };
219
+ var LOINC_SYSTEM = "http://loinc.org";
141
220
 
142
221
  // src/structures/ips_section_constants.ts
143
222
  var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
@@ -173,19 +252,16 @@ var IPSSectionResourceFilters = {
173
252
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && codeableConceptMatches(resource.code, Object.keys(SOCIAL_HISTORY_LOINC_CODES), "http://loinc.org"),
174
253
  // Only include pregnancy history Observations or relevant Conditions
175
254
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS), "http://loinc.org") || codeableConceptMatches(resource.valueCodeableConcept, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME), "http://loinc.org") || codingMatches(resource.code?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct") || codingMatches(resource.valueCodeableConcept?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct")) || resource.resourceType === "Condition" && (codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS), "http://loinc.org") || codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME), "http://loinc.org") || codingMatches(resource.code?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct")),
176
- // Only include Observations with LOINC 47420-5, category 'functional-status', or category display containing 'functional', and completed ClinicalImpressions
177
- ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Observation" && (codeableConceptMatches(resource.code, "47420-5", "http://loinc.org") || resource.category?.some(
178
- (cat) => cat.coding?.some(
179
- (c) => c.code === "functional-status" && c.system === "http://terminology.hl7.org/CodeSystem/observation-category" || typeof c.display === "string" && c.display.toLowerCase().includes("functional")
180
- )
255
+ // Only include Condition with Functional Status LOINC and SNOMED codes, category code 'problem-list-item', and completed ClinicalImpressions
256
+ ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && ((codeableConceptMatches(resource.code, Object.keys(FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES), "http://loinc.org") || codeableConceptMatches(resource.code, Object.keys(FUNCTIONAL_STATUS_SNOMED_CODES), "http://snomed.info/sct")) && resource.clinicalStatus?.coding?.some((c) => c.code === "active") && resource.category?.some(
257
+ (cat) => cat.coding?.some((c) => c.code === "problem-list-item")
181
258
  )) || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
182
259
  // Only include resolved medical history Conditions
183
260
  ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
184
261
  // Only include active care plans
185
262
  ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
186
263
  // Only include active advance directives (Consent resources)
187
- // TODO: disable this until we right logic to get these
188
- ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: () => false
264
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active" && resource.scope?.coding?.some((c) => codingMatches(c, "adr", "http://terminology.hl7.org/CodeSystem/consentscope"))
189
265
  };
190
266
  var IPSSectionSummaryCompositionFilter = {
191
267
  ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "allergy_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
@@ -201,8 +277,10 @@ var IPSSectionSummaryCompositionFilter = {
201
277
  var IPSSectionSummaryIPSCompositionFilter = {
202
278
  ["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_patient_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
203
279
  ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_vital_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
280
+ ["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_advanced_directives_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
204
281
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_social_history_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
205
- ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, ["ips_functional_status_condition_summary_document", "ips_functional_status_clinical_impression_summary_document"], IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
282
+ ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, ["ips_functional_status_condition_summary_document", "ips_functional_status_clinical_impression_summary_document"], IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
283
+ ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_medical_device_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
206
284
  };
207
285
  var IPSSectionResourceHelper = class {
208
286
  static getResourceFilterForSection(section) {
@@ -334,13 +412,26 @@ var TemplateUtilities = class {
334
412
  return code ? `${code} (${systemDisplay})` : "";
335
413
  }
336
414
  /**
337
- * Renders a Device reference
415
+ * Resolves and returns a Device resource from a Device Reference.
416
+ * Returns null if the reference cannot be resolved or is not a Device.
338
417
  * @param deviceRef - Reference to a Device resource
339
- * @returns Formatted device description
418
+ * @returns The resolved Device resource or null
419
+ */
420
+ getDeviceFromReference(deviceRef) {
421
+ if (!deviceRef) return null;
422
+ const device = this.resolveReference(deviceRef);
423
+ return device && device.resourceType === "Device" ? device : null;
424
+ }
425
+ /**
426
+ * Renders a Device name from a Device reference.
427
+ * Uses getDeviceFromReference to resolve the Device and returns a concatenated device.deviceName.
428
+ * Returns an empty string if no deviceName is available. The returned text is HTML-safe.
429
+ * @param deviceRef - Reference to a Device resource
430
+ * @returns Sanitized device name or empty string
340
431
  */
341
432
  renderDevice(deviceRef) {
342
- const device = deviceRef && this.resolveReference(deviceRef);
343
- if (device && device.resourceType === "Device" && device.deviceName && device.deviceName.length > 0) {
433
+ const device = this.getDeviceFromReference(deviceRef);
434
+ if (device && device.deviceName && device.deviceName.length > 0) {
344
435
  return this.safeConcat(device.deviceName, "name");
345
436
  }
346
437
  return "";
@@ -2756,11 +2847,64 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
2756
2847
  return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
2757
2848
  }
2758
2849
  /**
2759
- * Internal static implementation that actually generates the narrative
2760
- * @param resources - FHIR resources array containing Device resources
2850
+ * Generate HTML narrative for history of medical devices using summary
2851
+ * @param resources - FHIR Composition resources
2761
2852
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2762
2853
  * @returns HTML string for rendering
2763
2854
  */
2855
+ generateSummaryNarrative(resources, timezone) {
2856
+ const templateUtilities = new TemplateUtilities(resources);
2857
+ let isSummaryCreated = false;
2858
+ let html = `<p>This list includes all information about the patient's medical devices history, sorted by recorded date (most recent first).</p>
2859
+ `;
2860
+ html += `
2861
+ <table>
2862
+ <thead>
2863
+ <tr>
2864
+ <th>Device</th>
2865
+ <th>Code (System)</th>
2866
+ <th>Status</th>
2867
+ <th>Comments</th>
2868
+ <th>Date Recorded</th>
2869
+ <th>Source</th>
2870
+ </tr>
2871
+ </thead>
2872
+ <tbody>`;
2873
+ for (const resourceItem of resources) {
2874
+ for (const rowData of resourceItem.section ?? []) {
2875
+ const sectionCodeableConcept = rowData.code;
2876
+ const data = {};
2877
+ data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
2878
+ for (const columnData of rowData.section ?? []) {
2879
+ const columnTitle = columnData.title;
2880
+ if (columnTitle) {
2881
+ data[columnTitle] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2882
+ }
2883
+ }
2884
+ if (data["Device Name"]?.toLowerCase() === "unknown") {
2885
+ continue;
2886
+ }
2887
+ isSummaryCreated = true;
2888
+ html += `
2889
+ <tr>
2890
+ <td>${templateUtilities.capitalizeFirstLetter(data["Device Name"] ?? "")}</td>
2891
+ <td>${data["codeSystem"] ?? ""}</td>
2892
+ <td>${templateUtilities.renderTextAsHtml(data["Status"] ?? "")}</td>
2893
+ <td>${templateUtilities.renderTextAsHtml(data["Notes"] ?? "")}</td>
2894
+ <td>${templateUtilities.renderTime(data["Recorded On"] ?? "", timezone)}</td>
2895
+ <td>${data["Source"] ?? ""}</td>
2896
+ </tr>`;
2897
+ }
2898
+ }
2899
+ html += `
2900
+ </tbody>
2901
+ </table>`;
2902
+ return isSummaryCreated ? html : void 0;
2903
+ }
2904
+ /**
2905
+ * Internal static implementation that generates the narrative from DeviceUseStatement resources.
2906
+ * The “Code (System)” column renders Device.type as "code (SystemDisplay)" using TemplateUtilities.codeableConceptCoding.
2907
+ */
2764
2908
  static generateStaticNarrative(resources, timezone) {
2765
2909
  const templateUtilities = new TemplateUtilities(resources);
2766
2910
  let html = `<p>This list includes all DeviceUseStatement resources, sorted by recorded date (most recent first).</p>
@@ -2768,6 +2912,7 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
2768
2912
  <thead>
2769
2913
  <tr>
2770
2914
  <th>Device</th>
2915
+ <th>Code (System)</th>
2771
2916
  <th>Status</th>
2772
2917
  <th>Comments</th>
2773
2918
  <th>Date Recorded</th>
@@ -2780,26 +2925,32 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
2780
2925
  const dateB = b.recordedOn;
2781
2926
  return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2782
2927
  });
2783
- let isDeviceAdded = false;
2928
+ const devicesAdded = /* @__PURE__ */ new Set();
2784
2929
  for (const dus of deviceStatements) {
2785
- const deviceName = templateUtilities.renderTextAsHtml(templateUtilities.renderDevice(dus.device));
2786
- if (deviceName?.toLowerCase() === "unknown") {
2930
+ const deviceName = templateUtilities.renderDevice(dus.device);
2931
+ if (deviceName?.toLowerCase() === "unknown" || devicesAdded.has(deviceName)) {
2932
+ continue;
2933
+ }
2934
+ const device = templateUtilities.getDeviceFromReference(dus.device);
2935
+ const codeSystem = templateUtilities.codeableConceptCoding(device?.type);
2936
+ if (!codeSystem) {
2787
2937
  continue;
2788
2938
  }
2789
- isDeviceAdded = true;
2939
+ devicesAdded.add(deviceName);
2790
2940
  html += `
2791
2941
  <tr>
2792
2942
  <td>${templateUtilities.capitalizeFirstLetter(deviceName)}</td>
2943
+ <td>${codeSystem}</td>
2793
2944
  <td>${templateUtilities.renderTextAsHtml(dus.status || "")}</td>
2794
2945
  <td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
2795
- <td>${templateUtilities.renderTextAsHtml(templateUtilities.renderRecorded(dus.recordedOn, timezone))}</td>
2946
+ <td>${templateUtilities.renderTime(dus.recordedOn, timezone)}</td>
2796
2947
  <td>${templateUtilities.getOwnerTag(dus)}</td>
2797
2948
  </tr>`;
2798
2949
  }
2799
2950
  html += `
2800
2951
  </tbody>
2801
2952
  </table>`;
2802
- return isDeviceAdded ? html : void 0;
2953
+ return devicesAdded.size > 0 ? html : void 0;
2803
2954
  }
2804
2955
  };
2805
2956
 
@@ -4103,7 +4254,7 @@ var PlanOfCareTemplate = class {
4103
4254
  var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
4104
4255
  /**
4105
4256
  * Generate HTML narrative for Functional Status
4106
- * @param resources - FHIR resources array containing Observation resources
4257
+ * @param resources - FHIR resources array containing Condition and ClinicalImpression resources
4107
4258
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
4108
4259
  * @returns HTML string for rendering
4109
4260
  */
@@ -4143,8 +4294,8 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
4143
4294
  <thead>
4144
4295
  <tr>
4145
4296
  <th>Name</th>
4146
- <th>Date</th>
4147
4297
  <th>Code (System)</th>
4298
+ <th>Date</th>
4148
4299
  <th>Description</th>
4149
4300
  <th>Summary</th>
4150
4301
  <th>Source</th>
@@ -4210,8 +4361,8 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
4210
4361
  clinicalImpressionsHtml += `
4211
4362
  <tr>
4212
4363
  <td>${templateUtilities.capitalizeFirstLetter(name)}</td>
4213
- <td>${date ?? ""}</td>
4214
4364
  <td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
4365
+ <td>${date ?? ""}</td>
4215
4366
  <td>${data["Description"] ?? ""}</td>
4216
4367
  <td>${data["Summary"] ?? ""}</td>
4217
4368
  <td>${data["Source"] ?? ""}</td>
@@ -4240,92 +4391,156 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
4240
4391
  }
4241
4392
  /**
4242
4393
  * Internal static implementation that actually generates the narrative
4243
- * @param resources - FHIR resources array containing Observation resources
4394
+ * @param resources - FHIR resources array containing Condition and ClinicalImpression resources
4244
4395
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
4245
4396
  * @returns HTML string for rendering
4246
4397
  */
4247
4398
  static generateStaticNarrative(resources, timezone) {
4248
4399
  const templateUtilities = new TemplateUtilities(resources);
4249
- let html = `<p>This section summarizes key observations and assessments related to the person's functional status and ability to perform daily activities.</p>`;
4250
- let functionalObservations = resources.filter((r) => r.resourceType === "Observation").filter((r) => {
4251
- const hasFunctionalLoinc = r.code?.coding?.some(
4252
- (c) => c.system?.toLowerCase().includes("loinc") && c.code === "47420-5"
4253
- );
4254
- const hasFunctionalCategory = r.category?.some(
4255
- (cat) => cat.coding?.some(
4256
- (c) => c.code === "functional-status" || c.display?.toLowerCase().includes("functional")
4257
- )
4258
- );
4259
- return hasFunctionalLoinc || hasFunctionalCategory;
4260
- });
4261
- functionalObservations = functionalObservations.sort((a, b) => {
4262
- const getObsDate = (obs) => obs.effectiveDateTime ? new Date(obs.effectiveDateTime).getTime() : obs.issued ? new Date(obs.issued).getTime() : 0;
4263
- return getObsDate(b) - getObsDate(a);
4264
- });
4265
- let clinicalImpressions = resources.filter((r) => r.resourceType === "ClinicalImpression").filter((r) => r.status === "completed");
4266
- clinicalImpressions = clinicalImpressions.sort((a, b) => {
4267
- 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;
4268
- return getImpressionDate(b) - getImpressionDate(a);
4400
+ let html = `<div>
4401
+ <p>This section summarizes key conditions and assessments related to the person's functional status and ability to perform daily activities.</p>`;
4402
+ let conditionHtml = `
4403
+ <div>
4404
+ <h3>Conditions</h3>
4405
+ <table>
4406
+ <thead>
4407
+ <tr>
4408
+ <th>Problem</th>
4409
+ <th>Code (System)</th>
4410
+ <th>Onset Date</th>
4411
+ <th>Recorded Date</th>
4412
+ <th>Source</th>
4413
+ </tr>
4414
+ </thead>
4415
+ <tbody>`;
4416
+ let clinicalImpressionsHtml = `
4417
+ <div>
4418
+ <h3>Clinical Impressions</h3>
4419
+ <table>
4420
+ <thead>
4421
+ <tr>
4422
+ <th>Name</th>
4423
+ <th>Code (System)</th>
4424
+ <th>Date</th>
4425
+ <th>Description</th>
4426
+ <th>Summary</th>
4427
+ <th>Source</th>
4428
+ </tr>
4429
+ </thead>
4430
+ <tbody>`;
4431
+ const conditions = resources.filter((entry) => entry.resourceType === "Condition").map((entry) => entry);
4432
+ conditions.sort((a, b) => {
4433
+ const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
4434
+ const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
4435
+ return dateB - dateA;
4269
4436
  });
4270
- if (functionalObservations.length > 0) {
4271
- html += `<table><thead><tr><th>Observation</th><th>Value</th><th>Date</th><th>Interpretation</th><th>Comments</th><th>Source</th></tr></thead><tbody>`;
4272
- for (const obs of functionalObservations) {
4273
- const observation = obs;
4274
- const obsName = templateUtilities.codeableConceptDisplay(observation.code);
4275
- if (obsName?.toLowerCase() === "unknown") {
4276
- continue;
4437
+ const addedConditions = /* @__PURE__ */ new Set();
4438
+ for (const cond of conditions) {
4439
+ const functionalStatusName = this.getFunctionalStatusNameFromCode(cond.code);
4440
+ const problem = templateUtilities.codeableConceptDisplay(cond.code) || functionalStatusName;
4441
+ const codeAndSystem = templateUtilities.codeableConceptCoding(cond.code);
4442
+ if (!codeAndSystem || !problem || !functionalStatusName || addedConditions.has(functionalStatusName)) {
4443
+ continue;
4444
+ }
4445
+ if (problem?.toLowerCase() === "unknown") {
4446
+ continue;
4447
+ }
4448
+ addedConditions.add(functionalStatusName);
4449
+ let date = cond.onsetDateTime ? templateUtilities.renderTime(cond.onsetDateTime, timezone) : "";
4450
+ if (!date && cond.onsetPeriod?.start) {
4451
+ date = templateUtilities.renderTime(
4452
+ cond.onsetPeriod?.start,
4453
+ timezone
4454
+ );
4455
+ if (cond.onsetPeriod?.end) {
4456
+ date += " - " + templateUtilities.renderTime(cond.onsetPeriod?.end, timezone);
4277
4457
  }
4278
- const value = templateUtilities.extractObservationValue(observation);
4279
- const date = observation.effectiveDateTime ? templateUtilities.renderDate(observation.effectiveDateTime) : observation.issued ? templateUtilities.renderDate(observation.issued) : "";
4280
- const interpretation = observation.interpretation ? templateUtilities.codeableConceptDisplay(observation.interpretation[0]) : "";
4281
- const comments = observation.comment || observation.note?.map((n) => n.text).join("; ") || "";
4282
- html += `<tr>
4283
- <td>${templateUtilities.capitalizeFirstLetter(obsName)}</td>
4284
- <td>${value ?? ""}</td>
4458
+ }
4459
+ conditionHtml += `<tr>
4460
+ <td>${templateUtilities.capitalizeFirstLetter(problem)}</td>
4461
+ <td>${codeAndSystem}</td>
4285
4462
  <td>${date}</td>
4286
- <td>${interpretation}</td>
4287
- <td>${comments}</td>
4288
- <td>${templateUtilities.getOwnerTag(observation)}</td>
4463
+ <td>${templateUtilities.renderTime(cond.recordedDate, timezone)}</td>
4464
+ <td>${templateUtilities.getOwnerTag(cond)}</td>
4289
4465
  </tr>`;
4466
+ }
4467
+ const clinicalImpressions = resources.filter((entry) => entry.resourceType === "ClinicalImpression").map((entry) => entry);
4468
+ clinicalImpressions.sort((a, b) => {
4469
+ const dateA = this.getClinicalImpressionEffectiveDate(a);
4470
+ const dateB = this.getClinicalImpressionEffectiveDate(b);
4471
+ return dateB && dateA ? dateB.getTime() - dateA.getTime() : 0;
4472
+ });
4473
+ const addedClinicalImpressions = /* @__PURE__ */ new Set();
4474
+ for (const impression of clinicalImpressions) {
4475
+ const name = templateUtilities.codeableConceptDisplay(impression.code);
4476
+ const codeAndSystem = templateUtilities.codeableConceptCoding(impression.code);
4477
+ if (!codeAndSystem || addedClinicalImpressions.has(name)) {
4478
+ continue;
4290
4479
  }
4291
- html += `</tbody></table>`;
4292
- }
4293
- if (clinicalImpressions.length > 0) {
4294
- html += `<table><thead><tr><th>Date</th><th>Status</th><th>Description</th><th>Summary</th><th>Findings</th><th>Source</th></tr></thead><tbody>`;
4295
- for (const impression of clinicalImpressions) {
4296
- let formattedDate = "";
4297
- if (impression.effectiveDateTime) {
4298
- formattedDate = templateUtilities.renderTime(impression.effectiveDateTime, timezone);
4299
- } else if (impression.effectivePeriod) {
4300
- formattedDate = templateUtilities.renderPeriod(impression.effectivePeriod, timezone);
4301
- } else if (impression.date) {
4302
- formattedDate = templateUtilities.renderDate(impression.date);
4303
- }
4304
- let findingsHtml = "";
4305
- if (impression.finding && impression.finding.length > 0) {
4306
- findingsHtml = "<ul>";
4307
- for (const finding of impression.finding) {
4308
- const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConceptDisplay(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
4309
- const cause = finding.basis || "";
4310
- findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
4311
- }
4312
- findingsHtml += "</ul>";
4480
+ if (!name || name?.toLowerCase() === "unknown") {
4481
+ continue;
4482
+ }
4483
+ addedClinicalImpressions.add(name);
4484
+ let date = impression.effectiveDateTime ? templateUtilities.renderTime(impression.effectiveDateTime, timezone) : "";
4485
+ if (!date && impression.effectivePeriod?.start) {
4486
+ date = templateUtilities.renderTime(
4487
+ impression.effectivePeriod?.start,
4488
+ timezone
4489
+ );
4490
+ if (impression.effectivePeriod?.end) {
4491
+ date += " - " + templateUtilities.renderTime(impression.effectivePeriod?.end, timezone);
4313
4492
  }
4314
- html += `<tr>
4315
- <td>${formattedDate}</td>
4316
- <td>${impression.status || ""}</td>
4493
+ }
4494
+ clinicalImpressionsHtml += `<tr>
4495
+ <td>${templateUtilities.capitalizeFirstLetter(name)}</td>
4496
+ <td>${codeAndSystem}</td>
4497
+ <td>${date}</td>
4317
4498
  <td>${impression.description || ""}</td>
4318
4499
  <td>${impression.summary || ""}</td>
4319
- <td>${findingsHtml}</td>
4320
4500
  <td>${templateUtilities.getOwnerTag(impression)}</td>
4321
4501
  </tr>`;
4502
+ }
4503
+ if (addedConditions.size > 0) {
4504
+ html += conditionHtml;
4505
+ html += `
4506
+ </tbody>
4507
+ </table>
4508
+ </div>`;
4509
+ }
4510
+ if (addedClinicalImpressions.size > 0) {
4511
+ html += clinicalImpressionsHtml;
4512
+ html += `
4513
+ </tbody>
4514
+ </table>
4515
+ </div>`;
4516
+ }
4517
+ html += `
4518
+ </div>`;
4519
+ return addedConditions.size > 0 || addedClinicalImpressions.size > 0 ? html : void 0;
4520
+ }
4521
+ static getFunctionalStatusNameFromCode(cc) {
4522
+ if (!cc) return "";
4523
+ for (const coding of cc.coding || []) {
4524
+ let functionalStatusName = FUNCTIONAL_STATUS_SNOMED_CODES[coding.code];
4525
+ if (functionalStatusName) {
4526
+ return functionalStatusName;
4527
+ }
4528
+ functionalStatusName = FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES[coding.code];
4529
+ if (functionalStatusName) {
4530
+ return functionalStatusName;
4322
4531
  }
4323
- html += `</tbody></table>`;
4324
4532
  }
4325
- if (functionalObservations.length === 0 && clinicalImpressions.length === 0) {
4326
- html += `<p>No functional status information available.</p>`;
4533
+ }
4534
+ static getClinicalImpressionEffectiveDate(impression) {
4535
+ if (impression.effectiveDateTime) {
4536
+ return new Date(impression.effectiveDateTime);
4537
+ } else if (impression.effectivePeriod) {
4538
+ if (impression.effectivePeriod.start) {
4539
+ return new Date(impression.effectivePeriod.start);
4540
+ } else if (impression.effectivePeriod.end) {
4541
+ return new Date(impression.effectivePeriod.end);
4542
+ }
4327
4543
  }
4328
- return html;
4329
4544
  }
4330
4545
  };
4331
4546
 
@@ -4476,7 +4691,78 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
4476
4691
  const dateB = new Date(b.dateTime || 0);
4477
4692
  return dateB.getTime() - dateA.getTime();
4478
4693
  });
4479
- return _AdvanceDirectivesTemplate.generateStaticNarrative(resources, timezone);
4694
+ return _AdvanceDirectivesTemplate.generateStaticNarrative(
4695
+ resources,
4696
+ timezone
4697
+ );
4698
+ }
4699
+ /**
4700
+ * Generate HTML narrative for advance directives using summary
4701
+ * @param resources - FHIR Composition resources
4702
+ * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
4703
+ * @returns HTML string for rendering
4704
+ */
4705
+ generateSummaryNarrative(resources, timezone) {
4706
+ const templateUtilities = new TemplateUtilities(resources);
4707
+ let isSummaryCreated = false;
4708
+ let html = `<p>This list includes all Consent resources, sorted by date (most recent first).</p>
4709
+ `;
4710
+ html += `
4711
+ <div>
4712
+ <table>
4713
+ <thead>
4714
+ <tr>
4715
+ <th>Directive</th>
4716
+ <th>Code (System)</th>
4717
+ <th>Action Controlled</th>
4718
+ <th>Policy Rule</th>
4719
+ <th>Date</th>
4720
+ <th>Source</th>
4721
+ </tr>
4722
+ </thead>
4723
+ <tbody>`;
4724
+ for (const resourceItem of resources) {
4725
+ for (const rowData of resourceItem.section ?? []) {
4726
+ const sectionCodeableConcept = rowData.code;
4727
+ const data = {};
4728
+ data["codeSystem"] = templateUtilities.codeableConceptCoding(
4729
+ sectionCodeableConcept
4730
+ );
4731
+ for (const columnData of rowData.section ?? []) {
4732
+ const columnTitle = columnData.title;
4733
+ if (columnTitle) {
4734
+ if (columnTitle === "DateTime") {
4735
+ data[columnTitle] = templateUtilities.renderTime(
4736
+ columnData.text?.div ?? "",
4737
+ timezone
4738
+ );
4739
+ } else {
4740
+ data[columnTitle] = templateUtilities.renderTextAsHtml(
4741
+ columnData.text?.div ?? ""
4742
+ );
4743
+ }
4744
+ }
4745
+ }
4746
+ if (data["Advanced Directive Name"]?.toLowerCase() === "unknown") {
4747
+ continue;
4748
+ }
4749
+ isSummaryCreated = true;
4750
+ html += `
4751
+ <tr>
4752
+ <td>${templateUtilities.capitalizeFirstLetter(data["Advanced Directive Name"] ?? "")}</td>
4753
+ <td>${data["codeSystem"] ?? ""}</td>
4754
+ <td>${data["Provision Action"] ?? ""}</td>
4755
+ <td>${data["Policy Rule"] ?? ""}</td>
4756
+ <td>${data["DateTime"] ?? ""}</td>
4757
+ <td>${data["Source"] ?? ""}</td>
4758
+ </tr>`;
4759
+ }
4760
+ }
4761
+ html += `
4762
+ </tbody>
4763
+ </table>
4764
+ </div>`;
4765
+ return isSummaryCreated ? html : void 0;
4480
4766
  }
4481
4767
  /**
4482
4768
  * Internal static implementation that actually generates the narrative
@@ -4484,7 +4770,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
4484
4770
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
4485
4771
  * @returns HTML string for rendering
4486
4772
  */
4487
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
4488
4773
  static generateStaticNarrative(resources, timezone) {
4489
4774
  const templateUtilities = new TemplateUtilities(resources);
4490
4775
  let html = `<p>This list includes all Consent resources, sorted by date (most recent first).</p>
@@ -4492,35 +4777,57 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
4492
4777
  html += `<table>
4493
4778
  <thead>
4494
4779
  <tr>
4495
- <th>Scope</th>
4496
- <th>Status</th>
4780
+ <th>Directive</th>
4781
+ <th>Code (System)</th>
4497
4782
  <th>Action Controlled</th>
4783
+ <th>Policy Rule</th>
4498
4784
  <th>Date</th>
4499
4785
  <th>Source</th>
4500
4786
  </tr>
4501
4787
  </thead>
4502
4788
  <tbody>`;
4503
- let isConsentAdded = false;
4789
+ const consentAdded = /* @__PURE__ */ new Set();
4504
4790
  for (const resourceItem of resources) {
4505
4791
  const consent = resourceItem;
4506
- const consentScope = templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(consent.scope, "display")));
4507
- if (!consentScope || consentScope.toLowerCase() === "unknown") {
4792
+ let consentDirective = {};
4793
+ for (const category of consent.category || []) {
4794
+ for (const coding of category.coding || []) {
4795
+ if (coding.system === ADVANCED_DIRECTIVE_CATEGORY_SYSTEM && coding.code && coding.code in ADVANCED_DIRECTIVE_CATEGORY_CODES) {
4796
+ consentDirective = {
4797
+ name: ADVANCED_DIRECTIVE_CATEGORY_CODES[coding.code],
4798
+ system: coding.system,
4799
+ code: coding.code
4800
+ };
4801
+ break;
4802
+ } else if (coding.system === LOINC_SYSTEM && coding.code && coding.code in ADVANCED_DIRECTIVE_LOINC_CODES) {
4803
+ consentDirective = {
4804
+ name: ADVANCED_DIRECTIVE_LOINC_CODES[coding.code],
4805
+ system: coding.system,
4806
+ code: coding.code
4807
+ };
4808
+ break;
4809
+ }
4810
+ }
4811
+ }
4812
+ if (!consentDirective.name || !consentDirective.code || !consentDirective.system || consentDirective.name.toLowerCase() === "unknown" || consentAdded.has(consentDirective.name)) {
4508
4813
  continue;
4509
4814
  }
4510
- isConsentAdded = true;
4815
+ consentAdded.add(consentDirective.name);
4816
+ const consentCodeSystem = `${consentDirective.code} (${codingSystemDisplayNames_default[consentDirective.system] || consentDirective.system})`;
4511
4817
  html += `
4512
4818
  <tr>
4513
- <td>${consentScope}</td>
4514
- <td>${consent.status || ""}</td>
4819
+ <td>${consentDirective.name}</td>
4820
+ <td>${consentCodeSystem || ""}</td>
4515
4821
  <td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
4516
- <td>${consent.dateTime || ""}</td>
4822
+ <td>${consent.policyRule ? templateUtilities.formatCodeableConceptValue(consent.policyRule) : ""}</td>
4823
+ <td>${templateUtilities.renderTime(consent.dateTime, timezone) || ""}</td>
4517
4824
  <td>${templateUtilities.getOwnerTag(consent)}</td>
4518
4825
  </tr>`;
4519
4826
  }
4520
4827
  html += `
4521
4828
  </tbody>
4522
4829
  </table>`;
4523
- return isConsentAdded ? html : void 0;
4830
+ return consentAdded.size > 0 ? html : void 0;
4524
4831
  }
4525
4832
  };
4526
4833