@imranq2/fhirpatientsummary 1.0.32 → 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/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)),
@@ -197,7 +199,8 @@ var IPSSectionSummaryCompositionFilter = {
197
199
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "procedure_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
198
200
  };
199
201
  var IPSSectionSummaryIPSCompositionFilter = {
200
- ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "ips_vital_summary_document")
202
+ ["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_patient_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
203
+ ["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_vital_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
201
204
  };
202
205
  var IPSSectionResourceHelper = class {
203
206
  static getResourceFilterForSection(section) {
@@ -209,10 +212,16 @@ var IPSSectionResourceHelper = class {
209
212
  return resources.filter(filter);
210
213
  }
211
214
  static getSummaryCompositionFilterForSection(section) {
212
- 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;
213
219
  }
214
220
  static getSummaryIPSCompositionFilterForSection(section) {
215
- 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;
216
225
  }
217
226
  };
218
227
  function codingMatches(coding, code, system) {
@@ -912,18 +921,6 @@ var TemplateUtilities = class {
912
921
  }
913
922
  return "";
914
923
  }
915
- /**
916
- * Returns the owner tag from the resource meta.security array.
917
- * @param resource - FHIR resource with meta tag
918
- * @returns The owner code if found, otherwise undefined
919
- */
920
- getOwnerTag(resource) {
921
- if (!resource?.meta?.security) return "";
922
- const ownerEntry = resource.meta.security.find(
923
- (sec) => sec.system === "https://www.icanbwell.com/owner" && !!sec.code
924
- );
925
- return ownerEntry?.code;
926
- }
927
924
  /**
928
925
  * Public method to render plain text as HTML, escaping special characters and replacing newlines with <br />.
929
926
  * This method should be used whenever displaying user-supplied or FHIR resource text in HTML to prevent XSS vulnerabilities
@@ -944,6 +941,17 @@ var TemplateUtilities = class {
944
941
  }
945
942
  return text.charAt(0).toUpperCase() + text.slice(1);
946
943
  }
944
+ renderListSectionData(sectionData) {
945
+ if (!sectionData || !Array.isArray(sectionData) || sectionData.length === 0) {
946
+ return "";
947
+ }
948
+ const items = [];
949
+ for (const section of sectionData) {
950
+ items.push(this.renderTextAsHtml(section.text?.div || ""));
951
+ }
952
+ const listString = Array.from(items).map((item) => `<li>${item}</li>`).join("");
953
+ return `<ul>${listString}</ul>`;
954
+ }
947
955
  /**
948
956
  * Renders note elements from a FHIR resource in a standardized format
949
957
  * Can render as simple comma-separated text or as styled HTML with timestamps
@@ -1431,6 +1439,60 @@ var PatientTemplate = class _PatientTemplate {
1431
1439
  generateNarrative(resources, timezone) {
1432
1440
  return _PatientTemplate.generateStaticNarrative(resources, timezone);
1433
1441
  }
1442
+ /**
1443
+ * Generate HTML narrative for Patient resources using summary
1444
+ * @param resources - FHIR Composition resources
1445
+ * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
1446
+ * @returns HTML string for rendering
1447
+ */
1448
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1449
+ generateSummaryNarrative(resources, timezone) {
1450
+ const templateUtilities = new TemplateUtilities(resources);
1451
+ const compositionResources = resources[0];
1452
+ const data = {};
1453
+ for (const rowData of compositionResources.section ?? []) {
1454
+ for (const columnData of rowData.section ?? []) {
1455
+ switch (columnData.title) {
1456
+ case "Name":
1457
+ case "Address":
1458
+ case "Communication":
1459
+ data[columnData.title] = templateUtilities.renderListSectionData(columnData.section ?? []);
1460
+ break;
1461
+ case "Telecom": {
1462
+ const telecomStringParts = [];
1463
+ for (const telecomData of columnData.section ?? []) {
1464
+ const telecomSystem = telecomData?.title;
1465
+ const telecomValue = templateUtilities.renderListSectionData(telecomData.section ?? []);
1466
+ if (telecomSystem && telecomValue) {
1467
+ telecomStringParts.push(`<li><strong>${telecomSystem}:</strong>${telecomValue}</li>`);
1468
+ }
1469
+ }
1470
+ data["Telecom"] = `<ul>${telecomStringParts.join("")}</ul>`;
1471
+ break;
1472
+ }
1473
+ default:
1474
+ if (columnData.title) {
1475
+ data[columnData.title] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1476
+ }
1477
+ break;
1478
+ }
1479
+ }
1480
+ }
1481
+ let html = `<p>This section merges all Patient resources into a single combined patient record, preferring non-empty values for each field.</p>`;
1482
+ html += `<div>
1483
+ <ul>
1484
+ <li><strong>Name(s):</strong>${data["Name"] || ""}</li>
1485
+ <li><strong>Gender:</strong>${data["Gender"] || ""}</li>
1486
+ <li><strong>Date of Birth:</strong>${data["Date of Birth"] || ""}</li>
1487
+ <li><strong>Telecom:</strong>${data["Telecom"] || ""}</li>
1488
+ <li><strong>Address(es):</strong>${data["Address"] || ""}</li>
1489
+ ${data["Marital Status"] ? `<li><strong>Marital Status:</strong>${data["Marital Status"]}</li>` : ""}
1490
+ ${data["Deceased"] ? `<li><strong>Deceased:</strong>${data["Deceased"]}</li>` : ""}
1491
+ <li><strong>Language(s):</strong>${data["Communication"] || ""}</li>
1492
+ </ul>
1493
+ </div>`;
1494
+ return html;
1495
+ }
1434
1496
  /**
1435
1497
  * Internal static implementation that actually generates the narrative
1436
1498
  * @param resources - FHIR Patient resources
@@ -1441,6 +1503,7 @@ var PatientTemplate = class _PatientTemplate {
1441
1503
  static generateStaticNarrative(resources, timezone) {
1442
1504
  const templateUtilities = new TemplateUtilities(resources);
1443
1505
  const combinedPatient = this.combinePatients(resources);
1506
+ const deceasedText = this.renderDeceased(combinedPatient);
1444
1507
  let html = `<p>This section merges all Patient resources into a single combined patient record, preferring non-empty values for each field.</p>`;
1445
1508
  html += `<div>
1446
1509
  <ul>
@@ -1449,8 +1512,8 @@ var PatientTemplate = class _PatientTemplate {
1449
1512
  <li><strong>Date of Birth:</strong>${combinedPatient.birthDate || ""}</li>
1450
1513
  <li><strong>Telecom:</strong><ul>${this.renderTelecom(combinedPatient)}</ul></li>
1451
1514
  <li><strong>Address(es):</strong>${this.renderAddresses(combinedPatient)}</li>
1452
- <li><strong>Marital Status:</strong> ${combinedPatient.maritalStatus?.text || ""}</li>
1453
- <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>` : ""}
1454
1517
  <li><strong>Language(s):</strong>${this.renderCommunication(templateUtilities, combinedPatient)}</li>
1455
1518
  </ul>
1456
1519
  </div>`;
@@ -1524,7 +1587,8 @@ var PatientTemplate = class _PatientTemplate {
1524
1587
  }
1525
1588
  }
1526
1589
  });
1527
- return Array.from(uniqueNames).map((nameText) => `<ul><li>${nameText}</li></ul>`).join("");
1590
+ const namesHtml = Array.from(uniqueNames).map((nameText) => `<li>${nameText}</li>`).join("");
1591
+ return `<ul>${namesHtml}</ul>`;
1528
1592
  }
1529
1593
  /**
1530
1594
  * Renders patient telecom information grouped by system
@@ -1628,7 +1692,8 @@ var PatientTemplate = class _PatientTemplate {
1628
1692
  }
1629
1693
  });
1630
1694
  const deduplicatedAddresses = this.deduplicateSimilarAddresses(Array.from(uniqueAddresses));
1631
- return deduplicatedAddresses.map((addressText) => `<ul><li>${addressText}</li></ul>`).join("");
1695
+ const addressesHtml = deduplicatedAddresses.map((addressText) => `<li>${addressText}</li>`).join("");
1696
+ return `<ul>${addressesHtml}</ul>`;
1632
1697
  }
1633
1698
  /**
1634
1699
  * Calculates the similarity between two strings using Levenshtein distance
@@ -1746,7 +1811,8 @@ var PatientTemplate = class _PatientTemplate {
1746
1811
  uniqueLanguages.add(language);
1747
1812
  }
1748
1813
  });
1749
- return Array.from(uniqueLanguages).map((language) => `<ul><li>${language}${preferredLanguages.has(language) ? " (preferred)" : ""}</li></ul>`).join("");
1814
+ const languagesHtml = Array.from(uniqueLanguages).map((language) => `<li>${language}${preferredLanguages.has(language) ? " (preferred)" : ""}</li>`).join("");
1815
+ return `<ul>${languagesHtml}</ul>`;
1750
1816
  }
1751
1817
  /**
1752
1818
  * Capitalizes first letter of a string
@@ -1790,7 +1856,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1790
1856
  <th>Code (System)</th>
1791
1857
  <th>Criticality</th>
1792
1858
  <th>Recorded Date</th>
1793
- <th>Source</th>
1794
1859
  </tr>
1795
1860
  </thead>
1796
1861
  <tbody>`;
@@ -1810,13 +1875,13 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1810
1875
  case "Recorded Date":
1811
1876
  data["recordedDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1812
1877
  break;
1813
- case "Source":
1814
- data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
1815
- break;
1816
1878
  default:
1817
1879
  break;
1818
1880
  }
1819
1881
  }
1882
+ if (data["allergen"]?.toLowerCase() === "unknown") {
1883
+ continue;
1884
+ }
1820
1885
  isSummaryCreated = true;
1821
1886
  html += `
1822
1887
  <tr>
@@ -1824,7 +1889,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1824
1889
  <td>${data["codeSystem"] ?? ""}</td>
1825
1890
  <td>${data["criticality"] ?? ""}</td>
1826
1891
  <td>${templateUtilities.renderTime(data["recordedDate"], timezone) ?? ""}</td>
1827
- <td>${data["source"] ?? ""}</td>
1828
1892
  </tr>`;
1829
1893
  }
1830
1894
  }
@@ -1878,7 +1942,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1878
1942
  <th>Reaction</th>
1879
1943
  <th>Onset Date</th>
1880
1944
  <th>Comments</th>
1881
- <th>Source</th>
1882
1945
  </tr>
1883
1946
  </thead>
1884
1947
  <tbody>`;
@@ -1908,7 +1971,6 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1908
1971
  <th>Onset Date</th>
1909
1972
  <th>Comments</th>
1910
1973
  <th>Resolved Date</th>
1911
- <th>Source</th>
1912
1974
  </tr>
1913
1975
  </thead>
1914
1976
  <tbody>`;
@@ -1937,16 +1999,19 @@ var AllergyIntoleranceTemplate = class _AllergyIntoleranceTemplate {
1937
1999
  static generateAllergyRows(allergies, templateUtilities, timezone, includeResolved = false) {
1938
2000
  let html = "";
1939
2001
  for (const allergy of allergies) {
2002
+ const allergenName = templateUtilities.codeableConceptDisplay(allergy.code);
2003
+ if (allergenName?.toLowerCase() === "unknown") {
2004
+ continue;
2005
+ }
1940
2006
  html += `
1941
2007
  <tr>
1942
- <td class="Name"><span class="AllergenName">${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(allergy.code)))}</span></td>
2008
+ <td class="Name"><span class="AllergenName">${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(allergenName))}</span></td>
1943
2009
  <td class="Status">${templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(allergy.clinicalStatus)) || ""}</td>
1944
2010
  <td class="CodeSystem">${templateUtilities.codeableConceptCoding(allergy.code)}</td>
1945
2011
  <td class="Category">${templateUtilities.renderTextAsHtml(templateUtilities.safeConcat(allergy.category)) || ""}</td>
1946
2012
  <td class="Reaction">${templateUtilities.renderTextAsHtml(templateUtilities.concatReactionManifestation(allergy.reaction)) || ""}</td>
1947
2013
  <td class="OnsetDate">${templateUtilities.renderTextAsHtml(templateUtilities.renderTime(allergy.onsetDateTime, timezone)) || ""}</td>
1948
- <td class="Comments">${templateUtilities.renderNotes(allergy.note, timezone, { styled: true, warning: true })}</td>
1949
- <td class="Source">${templateUtilities.getOwnerTag(allergy)}</td>`;
2014
+ <td class="Comments">${templateUtilities.renderNotes(allergy.note, timezone, { styled: true, warning: true })}</td>`;
1950
2015
  if (includeResolved) {
1951
2016
  let endDate = "";
1952
2017
  if (allergy.extension && Array.isArray(allergy.extension)) {
@@ -2001,9 +2066,7 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2001
2066
  <th>Status</th>
2002
2067
  <th>Sig</th>
2003
2068
  <th>Days of Supply</th>
2004
- <th>Refills</th>
2005
2069
  <th>Start Date</th>
2006
- <th>Source</th>
2007
2070
  </tr>
2008
2071
  </thead>
2009
2072
  <tbody>`;
@@ -2029,15 +2092,9 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2029
2092
  case "Days Of Supply":
2030
2093
  data["daysOfSupply"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2031
2094
  break;
2032
- case "Refills Remaining":
2033
- data["refills"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2034
- break;
2035
2095
  case "Authored On Date":
2036
2096
  data["startDate"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2037
2097
  break;
2038
- case "Source":
2039
- data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2040
- break;
2041
2098
  default:
2042
2099
  break;
2043
2100
  }
@@ -2053,6 +2110,9 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2053
2110
  skippedMedications++;
2054
2111
  }
2055
2112
  if (data["status"] === "active" || startDateObj && startDateObj >= twoYearsAgo) {
2113
+ if (data["medication"]?.toLowerCase() === "unknown") {
2114
+ continue;
2115
+ }
2056
2116
  isSummaryCreated = true;
2057
2117
  html += `
2058
2118
  <tr>
@@ -2061,9 +2121,7 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2061
2121
  <td>${templateUtilities.renderTextAsHtml(data["status"])}</td>
2062
2122
  <td>${templateUtilities.renderTextAsHtml(data["sig-prescriber"] || data["sig-pharmacy"])}</td>
2063
2123
  <td>${templateUtilities.renderTextAsHtml(data["daysOfSupply"])}</td>
2064
- <td>${templateUtilities.renderTextAsHtml(data["refills"])}</td>
2065
2124
  <td>${templateUtilities.renderTime(data["startDate"], timezone)}</td>
2066
- <td>${templateUtilities.renderTextAsHtml(data["source"])}</td>
2067
2125
  </tr>`;
2068
2126
  }
2069
2127
  }
@@ -2188,9 +2246,7 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2188
2246
  <th>Code (System)</th>
2189
2247
  <th>Sig</th>
2190
2248
  <th>Dispense Quantity</th>
2191
- <th>Refills</th>
2192
2249
  <th>Start Date</th>
2193
- <th>Source</th>
2194
2250
  </tr>
2195
2251
  </thead>
2196
2252
  <tbody>`;
@@ -2199,7 +2255,6 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2199
2255
  let medicationName;
2200
2256
  let sig;
2201
2257
  let dispenseQuantity = "";
2202
- let refills = "";
2203
2258
  let startDate = "";
2204
2259
  let codeSystemDisplay = "";
2205
2260
  if (medication.type === "request") {
@@ -2215,7 +2270,6 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2215
2270
  dispenseQuantity = `${quantity.value} ${quantity.unit || quantity.code || ""}`.trim();
2216
2271
  }
2217
2272
  }
2218
- refills = mr.dispenseRequest?.numberOfRepeatsAllowed?.toString() || "";
2219
2273
  if (mr.dispenseRequest?.validityPeriod) {
2220
2274
  startDate = mr.dispenseRequest.validityPeriod.start || "";
2221
2275
  } else {
@@ -2240,6 +2294,9 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2240
2294
  codeSystemDisplay = templateUtilities.codeableConceptCoding(ms.medicationCodeableConcept);
2241
2295
  }
2242
2296
  }
2297
+ if (medicationName?.toLowerCase() === "unknown") {
2298
+ continue;
2299
+ }
2243
2300
  html += `
2244
2301
  <tr>
2245
2302
  <td>${type}</td>
@@ -2247,9 +2304,7 @@ var MedicationSummaryTemplate = class _MedicationSummaryTemplate {
2247
2304
  <td>${codeSystemDisplay}</td>
2248
2305
  <td>${sig}</td>
2249
2306
  <td>${dispenseQuantity}</td>
2250
- <td>${refills}</td>
2251
2307
  <td>${startDate}</td>
2252
- <td>${templateUtilities.getOwnerTag(medication.resource)}</td>
2253
2308
  </tr>`;
2254
2309
  }
2255
2310
  html += `
@@ -2294,7 +2349,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
2294
2349
  <th>Code (System)</th>
2295
2350
  <th>Status</th>
2296
2351
  <th>Date</th>
2297
- <th>Source</th>
2298
2352
  </tr>
2299
2353
  </thead>
2300
2354
  <tbody>`;
@@ -2314,14 +2368,14 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
2314
2368
  case "occurrenceDateTime":
2315
2369
  data["occurrenceDateTime"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2316
2370
  break;
2317
- case "Source":
2318
- data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2319
- break;
2320
2371
  default:
2321
2372
  break;
2322
2373
  }
2323
2374
  }
2324
2375
  if (data["status"] === "completed") {
2376
+ if (data["immunization"]?.toLowerCase() === "unknown") {
2377
+ continue;
2378
+ }
2325
2379
  isSummaryCreated = true;
2326
2380
  html += `
2327
2381
  <tr>
@@ -2329,7 +2383,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
2329
2383
  <td>${data["codeSystem"] ?? ""}</td>
2330
2384
  <td>${data["status"] ?? ""}</td>
2331
2385
  <td>${templateUtilities.renderTime(data["occurrenceDateTime"], timezone) ?? ""}</td>
2332
- <td>${data["source"] ?? ""}</td>
2333
2386
  </tr>`;
2334
2387
  }
2335
2388
  }
@@ -2360,7 +2413,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
2360
2413
  <th>Lot Number</th>
2361
2414
  <th>Comments</th>
2362
2415
  <th>Date</th>
2363
- <th>Source</th>
2364
2416
  </tr>
2365
2417
  </thead>
2366
2418
  <tbody>`;
@@ -2368,9 +2420,13 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
2368
2420
  if (immunizations.length > 0) {
2369
2421
  for (const resourceItem of immunizations) {
2370
2422
  const imm = resourceItem;
2423
+ const immunizationName = templateUtilities.codeableConceptDisplay(imm.vaccineCode);
2424
+ if (immunizationName?.toLowerCase() === "unknown") {
2425
+ continue;
2426
+ }
2371
2427
  html += `
2372
2428
  <tr>
2373
- <td>${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(imm.vaccineCode)))}</td>
2429
+ <td>${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(immunizationName))}</td>
2374
2430
  <td>${templateUtilities.codeableConceptCoding(imm.vaccineCode)}</td>
2375
2431
  <td>${imm.status || ""}</td>
2376
2432
  <td>${templateUtilities.concatDoseNumber(imm.protocolApplied)}</td>
@@ -2378,7 +2434,6 @@ var ImmunizationsTemplate = class _ImmunizationsTemplate {
2378
2434
  <td>${imm.lotNumber || ""}</td>
2379
2435
  <td>${templateUtilities.renderNotes(imm.note, timezone)}</td>
2380
2436
  <td>${templateUtilities.renderTime(imm.occurrenceDateTime, timezone)}</td>
2381
- <td>${templateUtilities.getOwnerTag(imm)}</td>
2382
2437
  </tr>`;
2383
2438
  }
2384
2439
  }
@@ -2400,6 +2455,59 @@ var ProblemListTemplate = class _ProblemListTemplate {
2400
2455
  generateNarrative(resources, timezone) {
2401
2456
  return _ProblemListTemplate.generateStaticNarrative(resources, timezone);
2402
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
+ }
2403
2511
  /**
2404
2512
  * Internal static implementation that actually generates the narrative
2405
2513
  * @param resources - FHIR Condition resources
@@ -2428,7 +2536,6 @@ var ProblemListTemplate = class _ProblemListTemplate {
2428
2536
  <th>Code (System)</th>
2429
2537
  <th>Onset Date</th>
2430
2538
  <th>Recorded Date</th>
2431
- <th>Source</th>
2432
2539
  </tr>
2433
2540
  </thead>
2434
2541
  <tbody>`;
@@ -2439,13 +2546,15 @@ var ProblemListTemplate = class _ProblemListTemplate {
2439
2546
  if (codeAndSystem && seenCodeAndSystems.has(codeAndSystem)) {
2440
2547
  continue;
2441
2548
  }
2549
+ if (conditionDisplay?.toLowerCase() === "unknown") {
2550
+ continue;
2551
+ }
2442
2552
  seenCodeAndSystems.add(codeAndSystem);
2443
2553
  html += `<tr>
2444
2554
  <td class="Name">${templateUtilities.capitalizeFirstLetter(conditionDisplay)}</td>
2445
2555
  <td class="CodeSystem">${codeAndSystem}</td>
2446
2556
  <td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
2447
2557
  <td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
2448
- <td class="Source">${templateUtilities.getOwnerTag(cond)}</td>
2449
2558
  </tr>`;
2450
2559
  }
2451
2560
  html += `</tbody>
@@ -2485,7 +2594,6 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
2485
2594
  <th>Code (System)</th>
2486
2595
  <th>Result</th>
2487
2596
  <th>Date</th>
2488
- <th>Source</th>
2489
2597
  </tr>
2490
2598
  </thead>
2491
2599
  <tbody>`;
@@ -2518,6 +2626,9 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
2518
2626
  data[columnTitle] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
2519
2627
  }
2520
2628
  }
2629
+ if (data["Vital Name"]?.toLowerCase() === "unknown") {
2630
+ continue;
2631
+ }
2521
2632
  isSummaryCreated = true;
2522
2633
  html += `
2523
2634
  <tr>
@@ -2525,7 +2636,6 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
2525
2636
  <td>${data["codeSystem"] ?? ""}</td>
2526
2637
  <td>${templateUtilities.extractObservationSummaryValue(data, timezone) ?? ""}</td>
2527
2638
  <td>${templateUtilities.extractObservationSummaryEffectiveTime(data, timezone) ?? ""}</td>
2528
- <td>${data["Source"] ?? ""}</td>
2529
2639
  </tr>`;
2530
2640
  }
2531
2641
  }
@@ -2563,14 +2673,17 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
2563
2673
  <th>Component(s)</th>
2564
2674
  <th>Comments</th>
2565
2675
  <th>Date</th>
2566
- <th>Source</th>
2567
2676
  </tr>
2568
2677
  </thead>
2569
2678
  <tbody>`;
2570
2679
  for (const obs of observations) {
2680
+ const vitalName = templateUtilities.codeableConceptDisplay(obs.code, "display");
2681
+ if (vitalName?.toLowerCase() === "unknown") {
2682
+ continue;
2683
+ }
2571
2684
  html += `
2572
2685
  <tr>
2573
- <td>${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code, "display")))}</td>
2686
+ <td>${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(vitalName))}</td>
2574
2687
  <td>${templateUtilities.codeableConceptCoding(obs.code)}</td>
2575
2688
  <td>${templateUtilities.extractObservationValue(obs)}</td>
2576
2689
  <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
@@ -2578,7 +2691,6 @@ var VitalSignsTemplate = class _VitalSignsTemplate {
2578
2691
  <td>${templateUtilities.renderComponent(obs.component)}</td>
2579
2692
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
2580
2693
  <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
2581
- <td>${templateUtilities.getOwnerTag(obs)}</td>
2582
2694
  </tr>`;
2583
2695
  }
2584
2696
  html += `
@@ -2625,10 +2737,14 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
2625
2737
  });
2626
2738
  let isDeviceAdded = false;
2627
2739
  for (const dus of deviceStatements) {
2740
+ const deviceName = templateUtilities.renderTextAsHtml(templateUtilities.renderDevice(dus.device));
2741
+ if (deviceName?.toLowerCase() === "unknown") {
2742
+ continue;
2743
+ }
2628
2744
  isDeviceAdded = true;
2629
2745
  html += `
2630
2746
  <tr>
2631
- <td>${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(templateUtilities.renderDevice(dus.device)))}</td>
2747
+ <td>${templateUtilities.capitalizeFirstLetter(deviceName)}</td>
2632
2748
  <td>${templateUtilities.renderTextAsHtml(dus.status || "")}</td>
2633
2749
  <td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
2634
2750
  <td>${templateUtilities.renderTextAsHtml(templateUtilities.renderRecorded(dus.recordedOn, timezone))}</td>
@@ -3002,7 +3118,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3002
3118
  <th>Result</th>
3003
3119
  <th>Reference Range</th>
3004
3120
  <th>Date</th>
3005
- <th>Source</th>
3006
3121
  </tr>
3007
3122
  </thead>
3008
3123
  <tbody>`;
@@ -3016,7 +3131,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3016
3131
  <th>Report</th>
3017
3132
  <th>Performer</th>
3018
3133
  <th>Issued</th>
3019
- <th>Source</th>
3020
3134
  </tr>
3021
3135
  </thead>
3022
3136
  <tbody>`;
@@ -3063,8 +3177,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3063
3177
  case "Status":
3064
3178
  data["status"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
3065
3179
  break;
3066
- case "Source":
3067
- data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
3068
3180
  break;
3069
3181
  default:
3070
3182
  break;
@@ -3090,6 +3202,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3090
3202
  for (const component of components) {
3091
3203
  const componentCode = `${groupName}${component["code"] ?? ""}`;
3092
3204
  if (componentCode && !observationAdded.has(componentCode)) {
3205
+ if (component["code"]?.toLowerCase() === "unknown") {
3206
+ continue;
3207
+ }
3093
3208
  observationAdded.add(componentCode);
3094
3209
  this.formatSummaryObservationData(component);
3095
3210
  observationhtml += `
@@ -3099,7 +3214,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3099
3214
  <td>${templateUtilities.renderTextAsHtml(component["formattedValue"]) ?? ""}</td>
3100
3215
  <td>${templateUtilities.renderTextAsHtml(component["referenceRange"])?.trim() ?? ""}</td>
3101
3216
  <td>${date ?? ""}</td>
3102
- <td>${data["source"] ?? ""}</td>
3103
3217
  </tr>`;
3104
3218
  }
3105
3219
  }
@@ -3107,6 +3221,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3107
3221
  if (obsDate && obsDate >= twoYearsAgo) {
3108
3222
  const code = data["code"] ?? "";
3109
3223
  if (code && !observationAdded.has(code)) {
3224
+ if (code.toLowerCase() === "unknown") {
3225
+ continue;
3226
+ }
3110
3227
  observationAdded.add(code);
3111
3228
  this.formatSummaryObservationData(data);
3112
3229
  observationhtml += `
@@ -3116,7 +3233,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3116
3233
  <td>${templateUtilities.renderTextAsHtml(data["formattedValue"]) ?? ""}</td>
3117
3234
  <td>${templateUtilities.renderTextAsHtml(data["referenceRange"])?.trim() ?? ""}</td>
3118
3235
  <td>${date ?? ""}</td>
3119
- <td>${data["source"] ?? ""}</td>
3120
3236
  </tr>`;
3121
3237
  }
3122
3238
  }
@@ -3129,13 +3245,15 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3129
3245
  if (data["status"] === "final" && issuedDate && issuedDate >= twoYearsAgo) {
3130
3246
  const reportName = data["report"] ?? "";
3131
3247
  if (reportName && !diagnosticReportAdded.has(reportName)) {
3248
+ if (reportName.toLowerCase() === "unknown") {
3249
+ continue;
3250
+ }
3132
3251
  diagnosticReportAdded.add(reportName);
3133
3252
  diagnosticReporthtml += `
3134
3253
  <tr>
3135
3254
  <td>${templateUtilities.capitalizeFirstLetter(data["report"] ?? "")}</td>
3136
3255
  <td>${data["performer"] ?? ""}</td>
3137
3256
  <td>${templateUtilities.renderTime(data["issued"], timezone) ?? ""}</td>
3138
- <td>${data["source"] ?? ""}</td>
3139
3257
  </tr>`;
3140
3258
  }
3141
3259
  }
@@ -3320,7 +3438,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3320
3438
  <th>Result</th>
3321
3439
  <th>Reference Range</th>
3322
3440
  <th>Date</th>
3323
- <th>Source</th>
3324
3441
  </tr>
3325
3442
  </thead>
3326
3443
  <tbody>`;
@@ -3329,6 +3446,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3329
3446
  const obsCodeDisplay = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code));
3330
3447
  const obsCodeAndSystem = templateUtilities.codeableConceptCoding(obs.code);
3331
3448
  if (!observationAdded.has(obsCodeDisplay) && !observationAdded.has(obsCodeAndSystem)) {
3449
+ if (obsCodeDisplay?.toLowerCase() === "unknown") {
3450
+ continue;
3451
+ }
3332
3452
  observationAdded.add(obsCodeDisplay);
3333
3453
  observationAdded.add(obsCodeAndSystem);
3334
3454
  html += `
@@ -3338,7 +3458,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3338
3458
  <td>${templateUtilities.extractObservationValue(obs)}</td>
3339
3459
  <td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
3340
3460
  <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
3341
- <td>${templateUtilities.getOwnerTag(obs)}</td>
3342
3461
  </tr>`;
3343
3462
  }
3344
3463
  }
@@ -3365,7 +3484,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3365
3484
  <th>Category</th>
3366
3485
  <th>Result</th>
3367
3486
  <th>Issued</th>
3368
- <th>Source</th>
3369
3487
  </tr>
3370
3488
  </thead>
3371
3489
  <tbody>`;
@@ -3374,6 +3492,9 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3374
3492
  const reportName = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(report.code));
3375
3493
  const codeAndSystem = templateUtilities.codeableConceptCoding(report.code);
3376
3494
  if (!diagnosticReportAdded.has(reportName) && !diagnosticReportAdded.has(codeAndSystem)) {
3495
+ if (reportName?.toLowerCase() === "unknown") {
3496
+ continue;
3497
+ }
3377
3498
  diagnosticReportAdded.add(reportName);
3378
3499
  diagnosticReportAdded.add(codeAndSystem);
3379
3500
  let resultCount = "";
@@ -3387,7 +3508,6 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
3387
3508
  <td>${templateUtilities.firstFromCodeableConceptList(report.category)}</td>
3388
3509
  <td>${resultCount}</td>
3389
3510
  <td>${report.issued ? templateUtilities.renderTime(report.issued, timezone) : ""}</td>
3390
- <td>${templateUtilities.getOwnerTag(report)}</td>
3391
3511
  </tr>`;
3392
3512
  }
3393
3513
  }
@@ -3454,7 +3574,6 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
3454
3574
  <th>Code (System)</th>
3455
3575
  <th>Performer</th>
3456
3576
  <th>Date</th>
3457
- <th>Source</th>
3458
3577
  </tr>
3459
3578
  </thead>
3460
3579
  <tbody>`;
@@ -3474,13 +3593,14 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
3474
3593
  case "Performed Date":
3475
3594
  data["date"] = columnData.text?.div ?? "";
3476
3595
  break;
3477
- case "Source":
3478
- data["source"] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
3479
3596
  break;
3480
3597
  default:
3481
3598
  break;
3482
3599
  }
3483
3600
  }
3601
+ if (data["procedure"]?.toLowerCase() === "unknown") {
3602
+ continue;
3603
+ }
3484
3604
  isSummaryCreated = true;
3485
3605
  html += `
3486
3606
  <tr>
@@ -3488,7 +3608,6 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
3488
3608
  <td>${data["codeSystem"] ?? ""}</td>
3489
3609
  <td>${data["performer"] ?? ""}</td>
3490
3610
  <td>${templateUtilities.renderTime(data["date"], timezone) ?? ""}</td>
3491
- <td>${data["source"] ?? ""}</td>
3492
3611
  </tr>`;
3493
3612
  }
3494
3613
  }
@@ -3516,19 +3635,21 @@ var HistoryOfProceduresTemplate = class _HistoryOfProceduresTemplate {
3516
3635
  <th>Code (System)</th>
3517
3636
  <th>Comments</th>
3518
3637
  <th>Date</th>
3519
- <th>Source</th>
3520
3638
  </tr>
3521
3639
  </thead>
3522
3640
  <tbody>`;
3523
3641
  for (const resourceItem of resources) {
3524
3642
  const proc = resourceItem;
3643
+ const procedureName = templateUtilities.codeableConceptDisplay(proc.code, "display");
3644
+ if (procedureName?.toLowerCase() === "unknown") {
3645
+ continue;
3646
+ }
3525
3647
  html += `
3526
3648
  <tr>
3527
- <td>${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(proc.code, "display")))}</td>
3649
+ <td>${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(procedureName))}</td>
3528
3650
  <td>${templateUtilities.codeableConceptCoding(proc.code)}</td>
3529
3651
  <td>${templateUtilities.renderNotes(proc.note, timezone)}</td>
3530
3652
  <td>${templateUtilities.renderTime(proc.performedDateTime || proc.performedPeriod?.start, timezone)}</td>
3531
- <td>${templateUtilities.getOwnerTag(proc)}</td>
3532
3653
  </tr>`;
3533
3654
  }
3534
3655
  html += `
@@ -3575,7 +3696,6 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
3575
3696
  <th>Unit</th>
3576
3697
  <th>Comments</th>
3577
3698
  <th>Date</th>
3578
- <th>Source</th>
3579
3699
  </tr>
3580
3700
  </thead>
3581
3701
  <tbody>`;
@@ -3583,6 +3703,9 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
3583
3703
  for (const obs of observations) {
3584
3704
  const obsName = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(obs.code));
3585
3705
  if (!addedObservations.has(obsName)) {
3706
+ if (obsName?.toLowerCase() === "unknown") {
3707
+ continue;
3708
+ }
3586
3709
  addedObservations.add(obsName);
3587
3710
  html += `
3588
3711
  <tr>
@@ -3592,7 +3715,6 @@ var SocialHistoryTemplate = class _SocialHistoryTemplate {
3592
3715
  <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
3593
3716
  <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
3594
3717
  <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
3595
- <td>${templateUtilities.getOwnerTag(obs)}</td>
3596
3718
  </tr>`;
3597
3719
  }
3598
3720
  }
@@ -3643,7 +3765,6 @@ var PastHistoryOfIllnessTemplate = class {
3643
3765
  <th>Onset Date</th>
3644
3766
  <th>Recorded Date</th>
3645
3767
  <th>Resolved Date</th>
3646
- <th>Source</th>
3647
3768
  </tr>
3648
3769
  </thead>
3649
3770
  <tbody>`;
@@ -3651,6 +3772,9 @@ var PastHistoryOfIllnessTemplate = class {
3651
3772
  for (const cond of filteredConditions) {
3652
3773
  const conditionCode = templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(cond.code));
3653
3774
  if (!addedConditionCodes.has(conditionCode)) {
3775
+ if (conditionCode?.toLowerCase() === "unknown") {
3776
+ continue;
3777
+ }
3654
3778
  addedConditionCodes.add(conditionCode);
3655
3779
  html += `<tr>
3656
3780
  <td class="Name">${templateUtilities.capitalizeFirstLetter(conditionCode)}</td>
@@ -3658,7 +3782,6 @@ var PastHistoryOfIllnessTemplate = class {
3658
3782
  <td class="OnsetDate">${templateUtilities.renderDate(cond.onsetDateTime)}</td>
3659
3783
  <td class="RecordedDate">${templateUtilities.renderDate(cond.recordedDate)}</td>
3660
3784
  <td class="ResolvedDate">${templateUtilities.renderDate(cond.abatementDateTime)}</td>
3661
- <td class="Source">${templateUtilities.getOwnerTag(cond)}</td>
3662
3785
  </tr>`;
3663
3786
  }
3664
3787
  }
@@ -3670,6 +3793,73 @@ var PastHistoryOfIllnessTemplate = class {
3670
3793
  }
3671
3794
  return html;
3672
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
+ }
3673
3863
  };
3674
3864
 
3675
3865
  // src/narratives/templates/typescript/PlanOfCareTemplate.ts
@@ -3699,19 +3889,21 @@ var PlanOfCareTemplate = class {
3699
3889
  <th>Comments</th>
3700
3890
  <th>Planned Start</th>
3701
3891
  <th>Planned End</th>
3702
- <th>Source</th>
3703
3892
  </tr>
3704
3893
  </thead>
3705
3894
  <tbody>`;
3706
3895
  for (const cp of carePlans) {
3896
+ const carePlanName = cp.description || cp.title || "";
3897
+ if (carePlanName.toLowerCase() === "unknown") {
3898
+ continue;
3899
+ }
3707
3900
  html += `
3708
3901
  <tr>
3709
- <td>${templateUtilities.capitalizeFirstLetter(cp.description || cp.title || "")}</td>
3902
+ <td>${templateUtilities.capitalizeFirstLetter(carePlanName)}</td>
3710
3903
  <td>${cp.intent || ""}</td>
3711
3904
  <td>${templateUtilities.concat(cp.note, "text")}</td>
3712
3905
  <td>${cp.period?.start ? templateUtilities.renderTime(cp.period?.start, timezone) : ""}</td>
3713
3906
  <td>${cp.period?.end ? templateUtilities.renderTime(cp.period?.end, timezone) : ""}</td>
3714
- <td>${templateUtilities.getOwnerTag(cp)}</td>
3715
3907
  </tr>`;
3716
3908
  }
3717
3909
  html += `
@@ -3739,7 +3931,6 @@ var PlanOfCareTemplate = class {
3739
3931
  <th>Created</th>
3740
3932
  <th>Planned Start</th>
3741
3933
  <th>Planned End</th>
3742
- <th>Source</th>
3743
3934
  </tr>
3744
3935
  </thead>
3745
3936
  <tbody>`;
@@ -3754,6 +3945,9 @@ var PlanOfCareTemplate = class {
3754
3945
  if (data["status"] !== "active") {
3755
3946
  continue;
3756
3947
  }
3948
+ if (data["CarePlan Name"]?.toLowerCase() === "unknown") {
3949
+ continue;
3950
+ }
3757
3951
  isSummaryCreated = true;
3758
3952
  html += `
3759
3953
  <tr>
@@ -3761,7 +3955,6 @@ var PlanOfCareTemplate = class {
3761
3955
  <td>${templateUtilities.renderTime(data["created"], timezone) ?? ""}</td>
3762
3956
  <td>${templateUtilities.renderTime(data["period.start"], timezone) ?? ""}</td>
3763
3957
  <td>${templateUtilities.renderTime(data["period.end"], timezone) ?? ""}</td>
3764
- <td>${data["source"] ?? ""}</td>
3765
3958
  </tr>`;
3766
3959
  }
3767
3960
  }
@@ -3818,6 +4011,9 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
3818
4011
  for (const obs of functionalObservations) {
3819
4012
  const observation = obs;
3820
4013
  const obsName = templateUtilities.codeableConceptDisplay(observation.code);
4014
+ if (obsName?.toLowerCase() === "unknown") {
4015
+ continue;
4016
+ }
3821
4017
  const value = templateUtilities.extractObservationValue(observation);
3822
4018
  const date = observation.effectiveDateTime ? templateUtilities.renderDate(observation.effectiveDateTime) : observation.issued ? templateUtilities.renderDate(observation.issued) : "";
3823
4019
  const interpretation = observation.interpretation ? templateUtilities.codeableConceptDisplay(observation.interpretation[0]) : "";
@@ -3925,18 +4121,19 @@ var PregnancyTemplate = class _PregnancyTemplate {
3925
4121
  <th>Code (System)</th>
3926
4122
  <th>Comments</th>
3927
4123
  <th>Date</th>
3928
- <th>Source</th>
3929
4124
  </tr>
3930
4125
  </thead>
3931
4126
  <tbody>`;
3932
- function renderRow({ result, comments, date, codeSystem, owner }) {
4127
+ function renderRow({ result, comments, date, codeSystem }) {
4128
+ if (result?.toLowerCase() === "unknown") {
4129
+ return;
4130
+ }
3933
4131
  html += `
3934
4132
  <tr>
3935
4133
  <td class="Result">${templateUtilities.capitalizeFirstLetter(result)}</td>
3936
4134
  <td class="CodeSystem">${codeSystem}</td>
3937
4135
  <td class="Comments">${comments}</td>
3938
4136
  <td class="Date">${date}</td>
3939
- <td class="Source">${owner}</td>
3940
4137
  </tr>`;
3941
4138
  }
3942
4139
  const rowResources = [];
@@ -3990,8 +4187,7 @@ var PregnancyTemplate = class _PregnancyTemplate {
3990
4187
  const rowKey = `${result}|${codeSystem}`;
3991
4188
  if (!addedRows.has(rowKey)) {
3992
4189
  addedRows.add(rowKey);
3993
- const owner = templateUtilities.getOwnerTag(resource);
3994
- renderRow({ result, comments, date: dateStr, codeSystem, owner });
4190
+ renderRow({ result, comments, date: dateStr, codeSystem });
3995
4191
  }
3996
4192
  }
3997
4193
  html += `
@@ -4038,11 +4234,17 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
4038
4234
  </tr>
4039
4235
  </thead>
4040
4236
  <tbody>`;
4237
+ let isConsentAdded = false;
4041
4238
  for (const resourceItem of resources) {
4042
4239
  const consent = resourceItem;
4240
+ const consentScope = templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(consent.scope, "display")));
4241
+ if (!consentScope || consentScope.toLowerCase() === "unknown") {
4242
+ continue;
4243
+ }
4244
+ isConsentAdded = true;
4043
4245
  html += `
4044
4246
  <tr>
4045
- <td>${templateUtilities.capitalizeFirstLetter(templateUtilities.renderTextAsHtml(templateUtilities.codeableConceptDisplay(consent.scope, "display")))}</td>
4247
+ <td>${consentScope}</td>
4046
4248
  <td>${consent.status || ""}</td>
4047
4249
  <td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
4048
4250
  <td>${consent.dateTime || ""}</td>
@@ -4051,7 +4253,7 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
4051
4253
  html += `
4052
4254
  </tbody>
4053
4255
  </table>`;
4054
- return html;
4256
+ return isConsentAdded ? html : void 0;
4055
4257
  }
4056
4258
  };
4057
4259
 
@@ -4246,6 +4448,10 @@ var ComprehensiveIPSCompositionBuilder = class {
4246
4448
  * @param validResources - Array of domain resources
4247
4449
  */
4248
4450
  addSectionAsync(narrative, sectionType, validResources) {
4451
+ if (sectionType === "Patient" /* PATIENT */) {
4452
+ this.patientSummary = narrative;
4453
+ return this;
4454
+ }
4249
4455
  const sectionEntry = {
4250
4456
  title: IPS_SECTION_DISPLAY_NAMES[sectionType] || sectionType,
4251
4457
  code: {
@@ -4275,26 +4481,24 @@ var ComprehensiveIPSCompositionBuilder = class {
4275
4481
  for (const resource of validResources) {
4276
4482
  this.resources.add(resource);
4277
4483
  }
4278
- if (sectionType !== "Patient" /* PATIENT */) {
4279
- let narrative = void 0;
4280
- if (validResources.length > 0) {
4281
- narrative = await NarrativeGenerator.generateNarrativeAsync(
4282
- sectionType,
4283
- validResources,
4284
- timezone,
4285
- true
4286
- );
4287
- }
4288
- if (!narrative && sectionType in IPSMandatorySections) {
4289
- narrative = await NarrativeGenerator.createNarrativeAsync(
4290
- IPSMissingMandatorySectionContent[sectionType]
4291
- );
4292
- }
4293
- if (!narrative) {
4294
- return this;
4295
- }
4296
- this.addSectionAsync(narrative, sectionType, validResources);
4484
+ let narrative = void 0;
4485
+ if (validResources.length > 0) {
4486
+ narrative = await NarrativeGenerator.generateNarrativeAsync(
4487
+ sectionType,
4488
+ validResources,
4489
+ timezone,
4490
+ true
4491
+ );
4492
+ }
4493
+ if (!narrative && sectionType in IPSMandatorySections) {
4494
+ narrative = await NarrativeGenerator.createNarrativeAsync(
4495
+ IPSMissingMandatorySectionContent[sectionType]
4496
+ );
4497
+ }
4498
+ if (!narrative) {
4499
+ return this;
4297
4500
  }
4501
+ this.addSectionAsync(narrative, sectionType, validResources);
4298
4502
  return this;
4299
4503
  }
4300
4504
  async makeSectionFromSummaryAsync(sectionType, summaryCompositions, resources, timezone) {
@@ -4332,17 +4536,18 @@ var ComprehensiveIPSCompositionBuilder = class {
4332
4536
  * @param timezone - Optional timezone to use for date formatting
4333
4537
  * @param useSummaryCompositions - Whether to use summary compositions (default: false)
4334
4538
  */
4335
- async readBundleAsync(bundle, timezone, useSummaryCompositions = false) {
4539
+ async readBundleAsync(bundle, timezone, useSummaryCompositions = false, consoleLogger = console) {
4336
4540
  if (!bundle.entry) {
4337
4541
  return this;
4338
4542
  }
4339
4543
  const patientEntries = [];
4340
4544
  const resources = [];
4341
4545
  bundle.entry.forEach((e) => {
4342
- if (e.resource?.resourceType === "Patient") {
4343
- patientEntries.push(e.resource);
4344
- this.resources.add(e.resource);
4345
- } else if (e.resource) {
4546
+ if (e.resource) {
4547
+ if (e.resource.resourceType === "Patient") {
4548
+ patientEntries.push(e.resource);
4549
+ this.resources.add(e.resource);
4550
+ }
4346
4551
  resources.push(e.resource);
4347
4552
  }
4348
4553
  });
@@ -4351,23 +4556,20 @@ var ComprehensiveIPSCompositionBuilder = class {
4351
4556
  }
4352
4557
  this.patients = patientEntries;
4353
4558
  for (const sectionType of Object.values(IPSSections)) {
4354
- if (sectionType === "Patient" /* PATIENT */) {
4355
- continue;
4356
- }
4357
4559
  const summaryIPSCompositionFilter = useSummaryCompositions ? IPSSectionResourceHelper.getSummaryIPSCompositionFilterForSection(sectionType) : void 0;
4358
4560
  const sectionIPSSummary = summaryIPSCompositionFilter ? resources.filter((resource) => summaryIPSCompositionFilter(resource)) : [];
4359
4561
  if (sectionIPSSummary.length > 0) {
4360
- console.log(`Using IPS summary composition for section: ${sectionType}`);
4562
+ consoleLogger.info(`Using IPS summary composition for section: ${sectionType}`);
4361
4563
  await this.makeSectionFromSummaryAsync(sectionType, sectionIPSSummary, resources, timezone);
4362
4564
  continue;
4363
4565
  }
4364
4566
  const summaryCompositionFilter = useSummaryCompositions ? IPSSectionResourceHelper.getSummaryCompositionFilterForSection(sectionType) : void 0;
4365
4567
  const sectionSummary = summaryCompositionFilter ? resources.filter((resource) => summaryCompositionFilter(resource)) : [];
4366
4568
  if (sectionSummary.length > 0) {
4367
- console.log(`Using summary composition for section: ${sectionType}`);
4569
+ consoleLogger.info(`Using summary composition for section: ${sectionType}`);
4368
4570
  await this.makeSectionFromSummaryAsync(sectionType, sectionSummary, resources, timezone);
4369
4571
  } else {
4370
- console.log(`Using individual resources for section: ${sectionType}`);
4572
+ consoleLogger.info(`Using individual resources for section: ${sectionType}`);
4371
4573
  const sectionFilter = IPSSectionResourceHelper.getResourceFilterForSection(sectionType);
4372
4574
  const sectionResources = resources.filter((resource) => sectionFilter(resource));
4373
4575
  await this.makeSectionAsync(sectionType, sectionResources, timezone);
@@ -4391,6 +4593,9 @@ var ComprehensiveIPSCompositionBuilder = class {
4391
4593
  if (!this.patients) {
4392
4594
  throw new Error("Patient resource must be set before building the bundle");
4393
4595
  }
4596
+ if (!this.patientSummary) {
4597
+ throw new Error("Patient summary narrative must be set before building the bundle");
4598
+ }
4394
4599
  const primaryPatientId = patientId ?? this.patients[0].id;
4395
4600
  const composition = {
4396
4601
  id: `Composition-${primaryPatientId}`,
@@ -4414,14 +4619,7 @@ var ComprehensiveIPSCompositionBuilder = class {
4414
4619
  date: (now || /* @__PURE__ */ new Date()).toISOString(),
4415
4620
  title: "International Patient Summary",
4416
4621
  section: this.sections,
4417
- text: await NarrativeGenerator.generateNarrativeAsync(
4418
- "Patient" /* PATIENT */,
4419
- this.patients,
4420
- timezone,
4421
- true,
4422
- false,
4423
- now
4424
- )
4622
+ text: this.patientSummary
4425
4623
  };
4426
4624
  const bundle = {
4427
4625
  resourceType: "Bundle",