@imranq2/fhirpatientsummary 1.0.33 → 1.0.34

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/README.md CHANGED
@@ -64,6 +64,44 @@ console.log(JSON.stringify(bundle, null, 2));
64
64
  - Use `makeSectionAsync(sectionType, resources, timezone)` to add each IPS section, or use `read_bundle(fhirBundle, timezone)` to extract all supported sections from a FHIR Bundle.
65
65
  - Use `build_bundle` to generate the final FHIR Bundle.
66
66
 
67
+ ## Patient Summary Composition
68
+
69
+ When generating patient summaries from FHIR Bundles, the library can use pre-computed summary compositions instead of raw resources. Summary compositions are specially structured FHIR Composition resources that contain curated, filtered, or aggregated data for specific sections.
70
+
71
+ ### How Summary Compositions Work
72
+
73
+ The `readBundleAsync` method supports a `useSummaryCompositions` parameter. When enabled, the library follows this priority order when processing each IPS section:
74
+
75
+ 1. **IPS-Specific Composition** (highest priority): Compositions with IPS-specific type codes (e.g., `ips_patient_summary_document`, `ips_vital_summary_document`)
76
+ 2. **Summary Composition** (medium priority): Compositions with summary type codes (e.g., `allergy_summary_document`, `condition_summary_document`, `medication_summary_document`)
77
+ 3. **Raw Resources** (fallback): Individual FHIR resources when no composition is available
78
+
79
+ This priority order ensures that the most refined and curated data is used when available, while still supporting raw resource processing as a fallback.
80
+
81
+ ### Environment Variables for enabling Composition Summary
82
+
83
+ The following environment variables can be used to configure the behavior of the patient summary generator:
84
+
85
+ #### SUMMARY_IPS_COMPOSITION_SECTIONS
86
+
87
+ Controls which IPS sections should include IPS-specific composition.
88
+
89
+ - **Default**: Disabled
90
+ - **Format**: Comma-separated list of section names
91
+ - **Example**: `SUMMARY_IPS_COMPOSITION_SECTIONS=Patient,VitalSignsSection`
92
+
93
+ When set to `all`, all supported sections will use IPS composition. To enable only specific sections, provide a comma-separated list of [section names](src/structures/ips_sections.ts).
94
+
95
+ #### SUMMARY_COMPOSITION_SECTIONS
96
+
97
+ Controls which IPS sections should include summary composition.
98
+
99
+ - **Default**: Disabled
100
+ - **Format**: Comma-separated list of section names
101
+ - **Example**: `SUMMARY_COMPOSITION_SECTIONS=AllergyIntoleranceSection,ProblemSection,MedicationSummarySection`
102
+
103
+ When set to `all`, all supported sections will use summary composition. To enable only specific sections, provide a comma-separated list of [section names](src/structures/ips_sections.ts).
104
+
67
105
  ## Running Tests
68
106
 
69
107
  To run the test suite:
package/dist/index.cjs CHANGED
@@ -189,6 +189,8 @@ var IPSSectionResourceFilters = {
189
189
  };
190
190
  var IPSSectionSummaryCompositionFilter = {
191
191
  ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "allergy_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
192
+ ["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "condition_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
193
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "condition_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
192
194
  ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "vital_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
193
195
  ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "careplan_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
194
196
  ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "immunization_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
@@ -210,10 +212,16 @@ var IPSSectionResourceHelper = class {
210
212
  return resources.filter(filter);
211
213
  }
212
214
  static getSummaryCompositionFilterForSection(section) {
213
- return IPSSectionSummaryCompositionFilter[section];
215
+ const sectionCompositionEnabled = process.env.SUMMARY_COMPOSITION_SECTIONS ? process.env.SUMMARY_COMPOSITION_SECTIONS.split(",").some(
216
+ (s) => s.trim().toLowerCase() === section.toString().toLowerCase() || s.trim().toLowerCase() === "all"
217
+ ) : false;
218
+ return sectionCompositionEnabled ? IPSSectionSummaryCompositionFilter[section] : void 0;
214
219
  }
215
220
  static getSummaryIPSCompositionFilterForSection(section) {
216
- return IPSSectionSummaryIPSCompositionFilter[section];
221
+ const sectionIPSCompositionEnabled = process.env.SUMMARY_IPS_COMPOSITION_SECTIONS ? process.env.SUMMARY_IPS_COMPOSITION_SECTIONS.split(",").some(
222
+ (s) => s.trim().toLowerCase() === section.toString().toLowerCase() || s.trim().toLowerCase() === "all"
223
+ ) : false;
224
+ return sectionIPSCompositionEnabled ? IPSSectionSummaryIPSCompositionFilter[section] : void 0;
217
225
  }
218
226
  };
219
227
  function codingMatches(coding, code, system) {
@@ -1478,8 +1486,8 @@ var PatientTemplate = class _PatientTemplate {
1478
1486
  <li><strong>Date of Birth:</strong>${data["Date of Birth"] || ""}</li>
1479
1487
  <li><strong>Telecom:</strong>${data["Telecom"] || ""}</li>
1480
1488
  <li><strong>Address(es):</strong>${data["Address"] || ""}</li>
1481
- <li><strong>Marital Status:</strong> ${data["Marital Status"] || ""}</li>
1482
- <li><strong>Deceased:</strong>${data["Deceased"] || ""}</li>
1489
+ ${data["Marital Status"] ? `<li><strong>Marital Status:</strong>${data["Marital Status"]}</li>` : ""}
1490
+ ${data["Deceased"] ? `<li><strong>Deceased:</strong>${data["Deceased"]}</li>` : ""}
1483
1491
  <li><strong>Language(s):</strong>${data["Communication"] || ""}</li>
1484
1492
  </ul>
1485
1493
  </div>`;
@@ -1495,6 +1503,7 @@ var PatientTemplate = class _PatientTemplate {
1495
1503
  static generateStaticNarrative(resources, timezone) {
1496
1504
  const templateUtilities = new TemplateUtilities(resources);
1497
1505
  const combinedPatient = this.combinePatients(resources);
1506
+ const deceasedText = this.renderDeceased(combinedPatient);
1498
1507
  let html = `<p>This section merges all Patient resources into a single combined patient record, preferring non-empty values for each field.</p>`;
1499
1508
  html += `<div>
1500
1509
  <ul>
@@ -1503,8 +1512,8 @@ var PatientTemplate = class _PatientTemplate {
1503
1512
  <li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
1504
1513
  <li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
1505
1514
  <li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
1506
- <li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
1507
- <li><strong>Deceased:</strong>${this.renderDeceased(combinedPatient)}</li>
1515
+ ${combinedPatient.maritalStatus?.text ? `<li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus.text}</li>` : ""}
1516
+ ${deceasedText ? `<li><strong>Deceased:</strong>${deceasedText}</li>` : ""}
1508
1517
  <li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
1509
1518
  </ul>
1510
1519
  </div>`;
@@ -2446,6 +2455,59 @@ var ProblemListTemplate = class _ProblemListTemplate {
2446
2455
  generateNarrative(resources, timezone) {
2447
2456
  return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
2448
2457
  }
2458
+ /**
2459
+ * Generate HTML narrative for Condition resources using summary
2460
+ * @param resources - FHIR Composition resources
2461
+ * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2462
+ * @returns HTML string for rendering
2463
+ */
2464
+ generateSummaryNarrative(resources, timezone) {
2465
+ const templateUtilities = new TemplateUtilities(resources);
2466
+ let isSummaryCreated = false;
2467
+ let html = `<p>This list includes patient problems, sorted by recorded date (most recent first)</p>
2468
+ <div>
2469
+ <table>
2470
+ <thead>
2471
+ <tr>
2472
+ <th>Problem</th>
2473
+ <th>Code (System)</th>
2474
+ <th>Is Chronic</th>
2475
+ <th>Onset Date</th>
2476
+ <th>Last Confirmed Date</th>
2477
+ </tr>
2478
+ </thead>
2479
+ <tbody>`;
2480
+ for (const resourceItem of resources) {
2481
+ for (const rowData of resourceItem.section ?? []) {
2482
+ const sectionCodeableConcept = rowData.code;
2483
+ const data = {};
2484
+ for (const columnData of rowData.section ?? []) {
2485
+ if (columnData.title) {
2486
+ data[columnData.title] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2487
+ }
2488
+ }
2489
+ if (data["Condition Name"]?.toLowerCase() === "unknown") {
2490
+ continue;
2491
+ }
2492
+ if (data["Status"] === "active") {
2493
+ isSummaryCreated = true;
2494
+ html += `
2495
+ <tr>
2496
+ <td>${templateUtilities.capitalizeFirstLetter(data["Condition Name"] ?? "")}</td>
2497
+ <td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
2498
+ <td>${data["Is Chronic"] ?? ""}</td>
2499
+ <td>${templateUtilities.renderTime(data["Onset Date"], timezone) ?? ""}</td>
2500
+ <td>${templateUtilities.renderTime(data["Last Confirmed Date"], timezone) ?? ""}</td>
2501
+ </tr>`;
2502
+ }
2503
+ }
2504
+ }
2505
+ html += `
2506
+ </tbody>
2507
+ </table>
2508
+ </div>`;
2509
+ return isSummaryCreated ? html : void 0;
2510
+ }
2449
2511
  /**
2450
2512
  * Internal static implementation that actually generates the narrative
2451
2513
  * @param resources - FHIR Condition resources
@@ -3731,6 +3793,73 @@ var PastHistoryOfIllnessTemplate = class {
3731
3793
  }
3732
3794
  return html;
3733
3795
  }
3796
+ /**
3797
+ * Generate HTML narrative for Condition resources using summary
3798
+ * @param resources - FHIR Composition resources
3799
+ * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
3800
+ * @returns HTML string for rendering
3801
+ */
3802
+ generateSummaryNarrative(resources, timezone, now) {
3803
+ const templateUtilities = new TemplateUtilities(resources);
3804
+ let isSummaryCreated = false;
3805
+ const currentDate = now || /* @__PURE__ */ new Date();
3806
+ const fiveYearsAgo = new Date(currentDate);
3807
+ fiveYearsAgo.setFullYear(currentDate.getFullYear() - 5);
3808
+ let skippedConditions = 0;
3809
+ let html = `<p>This list includes past problems for the patient with a recorded date within the last 5 years, sorted by recorded date (most recent first).</p>
3810
+ <div>
3811
+ <table>
3812
+ <thead>
3813
+ <tr>
3814
+ <th>Problem</th>
3815
+ <th>Code (System)</th>
3816
+ <th>Is Chronic</th>
3817
+ <th>Onset Date</th>
3818
+ <th>Last Confirmed Date</th>
3819
+ <th>Resolved Date</th>
3820
+ </tr>
3821
+ </thead>
3822
+ <tbody>`;
3823
+ for (const resourceItem of resources) {
3824
+ for (const rowData of resourceItem.section ?? []) {
3825
+ const sectionCodeableConcept = rowData.code;
3826
+ const data = {};
3827
+ for (const columnData of rowData.section ?? []) {
3828
+ if (columnData.title) {
3829
+ data[columnData.title] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
3830
+ }
3831
+ }
3832
+ if (data["Condition Name"]?.toLowerCase() === "unknown") {
3833
+ continue;
3834
+ }
3835
+ if (data["Status"] === "inactive") {
3836
+ if (data["Last Confirmed Date"] && new Date(data["Last Confirmed Date"]) < fiveYearsAgo) {
3837
+ skippedConditions++;
3838
+ continue;
3839
+ }
3840
+ isSummaryCreated = true;
3841
+ html += `
3842
+ <tr>
3843
+ <td>${templateUtilities.capitalizeFirstLetter(data["Condition Name"] ?? "")}</td>
3844
+ <td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
3845
+ <td>${data["Is Chronic"] ?? ""}</td>
3846
+ <td>${templateUtilities.renderTime(data["Onset Date"], timezone) ?? ""}</td>
3847
+ <td>${templateUtilities.renderTime(data["Last Confirmed Date"], timezone) ?? ""}</td>
3848
+ <td>${templateUtilities.renderTime(data["Resolved Date"], timezone) ?? ""}</td>
3849
+ </tr>`;
3850
+ }
3851
+ }
3852
+ }
3853
+ html += `
3854
+ </tbody>
3855
+ </table>`;
3856
+ if (skippedConditions > 0) {
3857
+ html += `
3858
+ <p><em>${skippedConditions} additional past illnesses older than 5 years ago are present</em></p>`;
3859
+ }
3860
+ html += `</div>`;
3861
+ return isSummaryCreated ? html : void 0;
3862
+ }
3734
3863
  };
3735
3864
 
3736
3865
  // src/narratives/templates/typescript/PlanOfCareTemplate.ts
package/dist/index.js CHANGED
@@ -161,6 +161,8 @@ var IPSSectionResourceFilters = {
161
161
  };
162
162
  var IPSSectionSummaryCompositionFilter = {
163
163
  ["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "allergy_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
164
+ ["ProblemSection" /* PROBLEMS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "condition_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
165
+ ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "condition_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
164
166
  ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "vital_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
165
167
  ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "careplan_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
166
168
  ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "immunization_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
@@ -182,10 +184,16 @@ var IPSSectionResourceHelper = class {
182
184
  return resources.filter(filter);
183
185
  }
184
186
  static getSummaryCompositionFilterForSection(section) {
185
- return IPSSectionSummaryCompositionFilter[section];
187
+ const sectionCompositionEnabled = process.env.SUMMARY_COMPOSITION_SECTIONS ? process.env.SUMMARY_COMPOSITION_SECTIONS.split(",").some(
188
+ (s) => s.trim().toLowerCase() === section.toString().toLowerCase() || s.trim().toLowerCase() === "all"
189
+ ) : false;
190
+ return sectionCompositionEnabled ? IPSSectionSummaryCompositionFilter[section] : void 0;
186
191
  }
187
192
  static getSummaryIPSCompositionFilterForSection(section) {
188
- return IPSSectionSummaryIPSCompositionFilter[section];
193
+ const sectionIPSCompositionEnabled = process.env.SUMMARY_IPS_COMPOSITION_SECTIONS ? process.env.SUMMARY_IPS_COMPOSITION_SECTIONS.split(",").some(
194
+ (s) => s.trim().toLowerCase() === section.toString().toLowerCase() || s.trim().toLowerCase() === "all"
195
+ ) : false;
196
+ return sectionIPSCompositionEnabled ? IPSSectionSummaryIPSCompositionFilter[section] : void 0;
189
197
  }
190
198
  };
191
199
  function codingMatches(coding, code, system) {
@@ -1450,8 +1458,8 @@ var PatientTemplate = class _PatientTemplate {
1450
1458
  <li><strong>Date of Birth:</strong>${data["Date of Birth"] || ""}</li>
1451
1459
  <li><strong>Telecom:</strong>${data["Telecom"] || ""}</li>
1452
1460
  <li><strong>Address(es):</strong>${data["Address"] || ""}</li>
1453
- <li><strong>Marital Status:</strong> ${data["Marital Status"] || ""}</li>
1454
- <li><strong>Deceased:</strong>${data["Deceased"] || ""}</li>
1461
+ ${data["Marital Status"] ? `<li><strong>Marital Status:</strong>${data["Marital Status"]}</li>` : ""}
1462
+ ${data["Deceased"] ? `<li><strong>Deceased:</strong>${data["Deceased"]}</li>` : ""}
1455
1463
  <li><strong>Language(s):</strong>${data["Communication"] || ""}</li>
1456
1464
  </ul>
1457
1465
  </div>`;
@@ -1467,6 +1475,7 @@ var PatientTemplate = class _PatientTemplate {
1467
1475
  static generateStaticNarrative(resources, timezone) {
1468
1476
  const templateUtilities = new TemplateUtilities(resources);
1469
1477
  const combinedPatient = this.combinePatients(resources);
1478
+ const deceasedText = this.renderDeceased(combinedPatient);
1470
1479
  let html = `<p>This section merges all Patient resources into a single combined patient record, preferring non-empty values for each field.</p>`;
1471
1480
  html += `<div>
1472
1481
  <ul>
@@ -1475,8 +1484,8 @@ var PatientTemplate = class _PatientTemplate {
1475
1484
  <li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
1476
1485
  <li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
1477
1486
  <li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
1478
- <li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
1479
- <li><strong>Deceased:</strong>${this.renderDeceased(combinedPatient)}</li>
1487
+ ${combinedPatient.maritalStatus?.text ? `<li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus.text}</li>` : ""}
1488
+ ${deceasedText ? `<li><strong>Deceased:</strong>${deceasedText}</li>` : ""}
1480
1489
  <li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
1481
1490
  </ul>
1482
1491
  </div>`;
@@ -2418,6 +2427,59 @@ var ProblemListTemplate = class _ProblemListTemplate {
2418
2427
  generateNarrative(resources, timezone) {
2419
2428
  return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
2420
2429
  }
2430
+ /**
2431
+ * Generate HTML narrative for Condition resources using summary
2432
+ * @param resources - FHIR Composition resources
2433
+ * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2434
+ * @returns HTML string for rendering
2435
+ */
2436
+ generateSummaryNarrative(resources, timezone) {
2437
+ const templateUtilities = new TemplateUtilities(resources);
2438
+ let isSummaryCreated = false;
2439
+ let html = `<p>This list includes patient problems, sorted by recorded date (most recent first)</p>
2440
+ <div>
2441
+ <table>
2442
+ <thead>
2443
+ <tr>
2444
+ <th>Problem</th>
2445
+ <th>Code (System)</th>
2446
+ <th>Is Chronic</th>
2447
+ <th>Onset Date</th>
2448
+ <th>Last Confirmed Date</th>
2449
+ </tr>
2450
+ </thead>
2451
+ <tbody>`;
2452
+ for (const resourceItem of resources) {
2453
+ for (const rowData of resourceItem.section ?? []) {
2454
+ const sectionCodeableConcept = rowData.code;
2455
+ const data = {};
2456
+ for (const columnData of rowData.section ?? []) {
2457
+ if (columnData.title) {
2458
+ data[columnData.title] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2459
+ }
2460
+ }
2461
+ if (data["Condition Name"]?.toLowerCase() === "unknown") {
2462
+ continue;
2463
+ }
2464
+ if (data["Status"] === "active") {
2465
+ isSummaryCreated = true;
2466
+ html += `
2467
+ <tr>
2468
+ <td>${templateUtilities.capitalizeFirstLetter(data["Condition Name"] ?? "")}</td>
2469
+ <td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
2470
+ <td>${data["Is Chronic"] ?? ""}</td>
2471
+ <td>${templateUtilities.renderTime(data["Onset Date"], timezone) ?? ""}</td>
2472
+ <td>${templateUtilities.renderTime(data["Last Confirmed Date"], timezone) ?? ""}</td>
2473
+ </tr>`;
2474
+ }
2475
+ }
2476
+ }
2477
+ html += `
2478
+ </tbody>
2479
+ </table>
2480
+ </div>`;
2481
+ return isSummaryCreated ? html : void 0;
2482
+ }
2421
2483
  /**
2422
2484
  * Internal static implementation that actually generates the narrative
2423
2485
  * @param resources - FHIR Condition resources
@@ -3703,6 +3765,73 @@ var PastHistoryOfIllnessTemplate = class {
3703
3765
  }
3704
3766
  return html;
3705
3767
  }
3768
+ /**
3769
+ * Generate HTML narrative for Condition resources using summary
3770
+ * @param resources - FHIR Composition resources
3771
+ * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
3772
+ * @returns HTML string for rendering
3773
+ */
3774
+ generateSummaryNarrative(resources, timezone, now) {
3775
+ const templateUtilities = new TemplateUtilities(resources);
3776
+ let isSummaryCreated = false;
3777
+ const currentDate = now || /* @__PURE__ */ new Date();
3778
+ const fiveYearsAgo = new Date(currentDate);
3779
+ fiveYearsAgo.setFullYear(currentDate.getFullYear() - 5);
3780
+ let skippedConditions = 0;
3781
+ let html = `<p>This list includes past problems for the patient with a recorded date within the last 5 years, sorted by recorded date (most recent first).</p>
3782
+ <div>
3783
+ <table>
3784
+ <thead>
3785
+ <tr>
3786
+ <th>Problem</th>
3787
+ <th>Code (System)</th>
3788
+ <th>Is Chronic</th>
3789
+ <th>Onset Date</th>
3790
+ <th>Last Confirmed Date</th>
3791
+ <th>Resolved Date</th>
3792
+ </tr>
3793
+ </thead>
3794
+ <tbody>`;
3795
+ for (const resourceItem of resources) {
3796
+ for (const rowData of resourceItem.section ?? []) {
3797
+ const sectionCodeableConcept = rowData.code;
3798
+ const data = {};
3799
+ for (const columnData of rowData.section ?? []) {
3800
+ if (columnData.title) {
3801
+ data[columnData.title] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
3802
+ }
3803
+ }
3804
+ if (data["Condition Name"]?.toLowerCase() === "unknown") {
3805
+ continue;
3806
+ }
3807
+ if (data["Status"] === "inactive") {
3808
+ if (data["Last Confirmed Date"] && new Date(data["Last Confirmed Date"]) < fiveYearsAgo) {
3809
+ skippedConditions++;
3810
+ continue;
3811
+ }
3812
+ isSummaryCreated = true;
3813
+ html += `
3814
+ <tr>
3815
+ <td>${templateUtilities.capitalizeFirstLetter(data["Condition Name"] ?? "")}</td>
3816
+ <td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
3817
+ <td>${data["Is Chronic"] ?? ""}</td>
3818
+ <td>${templateUtilities.renderTime(data["Onset Date"], timezone) ?? ""}</td>
3819
+ <td>${templateUtilities.renderTime(data["Last Confirmed Date"], timezone) ?? ""}</td>
3820
+ <td>${templateUtilities.renderTime(data["Resolved Date"], timezone) ?? ""}</td>
3821
+ </tr>`;
3822
+ }
3823
+ }
3824
+ }
3825
+ html += `
3826
+ </tbody>
3827
+ </table>`;
3828
+ if (skippedConditions > 0) {
3829
+ html += `
3830
+ <p><em>${skippedConditions} additional past illnesses older than 5 years ago are present</em></p>`;
3831
+ }
3832
+ html += `</div>`;
3833
+ return isSummaryCreated ? html : void 0;
3834
+ }
3706
3835
  };
3707
3836
 
3708
3837
  // src/narratives/templates/typescript/PlanOfCareTemplate.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imranq2/fhirpatientsummary",
3
- "version": "1.0.33",
3
+ "version": "1.0.34",
4
4
  "description": "A template for creating npm packages using TypeScript and VSCode",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",