@imranq2/fhirpatientsummary 1.0.12 → 1.0.14

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
@@ -43,7 +43,6 @@ var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
43
43
  IPSSections2["MEDICAL_HISTORY"] = "HistoryOfPastIllnessSection";
44
44
  IPSSections2["SOCIAL_HISTORY"] = "SocialHistorySection";
45
45
  IPSSections2["VITAL_SIGNS"] = "VitalSignsSection";
46
- IPSSections2["FAMILY_HISTORY"] = "FamilyHistorySection";
47
46
  return IPSSections2;
48
47
  })(IPSSections || {});
49
48
 
@@ -58,7 +57,6 @@ var IPS_SECTION_LOINC_CODES = {
58
57
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "46264-8",
59
58
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
60
59
  ["HistoryOfProceduresSection" /* PROCEDURES */]: "47519-4",
61
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: "10157-6",
62
60
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: "29762-2",
63
61
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "10162-6",
64
62
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "47420-5",
@@ -81,10 +79,9 @@ var IPS_SECTION_DISPLAY_NAMES = {
81
79
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "History of Pregnancies",
82
80
  ["PlanOfCareSection" /* CARE_PLAN */]: "Plan of Care",
83
81
  ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "History of Past Illness",
84
- ["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History",
85
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: "History of Family Member Diseases"
82
+ ["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History"
86
83
  };
87
- var PREGNANCY_LONIC_CODES = {
84
+ var PREGNANCY_LOINC_CODES = {
88
85
  PREGNANCY_STATUS: {
89
86
  "LA15173-0": "Pregnant",
90
87
  "LA26683-5": "Not pregnant",
@@ -102,7 +99,7 @@ var PREGNANCY_LONIC_CODES = {
102
99
  "33065-4": "Ectopic Pregnancy"
103
100
  }
104
101
  };
105
- var SOCIAL_HISTORY_LONIC_CODES = {
102
+ var SOCIAL_HISTORY_LOINC_CODES = {
106
103
  "72166-2": "Tobacco Use",
107
104
  "74013-4": "Alcohol Use"
108
105
  };
@@ -121,7 +118,6 @@ var IPSSectionResourceMap = {
121
118
  // Device resource is used for medical devices name
122
119
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
123
120
  ["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
124
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: ["FamilyMemberHistory"],
125
121
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
126
122
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
127
123
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
@@ -142,12 +138,10 @@ var IPSSectionResourceFilters = {
142
138
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
143
139
  // Only include completed procedures
144
140
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
145
- // Only include family history resources
146
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: (resource) => resource.resourceType === "FamilyMemberHistory",
147
141
  // Only include social history Observations (category.coding contains 'social-history')
148
- ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LONIC_CODES).includes(c.code)),
142
+ ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LOINC_CODES).includes(c.code)),
149
143
  // Only include pregnancy history Observations (category.coding contains 'pregnancy')
150
- ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code?.coding?.some((c) => Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) || resource.valueCodeableConcept?.coding?.some((c) => Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code))),
144
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code?.coding?.some((c) => Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) || resource.valueCodeableConcept?.coding?.some((c) => Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code))),
151
145
  // Only include active functional status Conditions or ClinicalImpressions
152
146
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string") || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
153
147
  // Only include resolved medical history Conditions
@@ -577,17 +571,17 @@ var TemplateUtilities = class {
577
571
  extractPregnancyStatus(observation) {
578
572
  let status = "";
579
573
  observation.code?.coding?.forEach((c) => {
580
- if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) {
581
- status = PREGNANCY_LONIC_CODES.PREGNANCY_STATUS[c.code];
574
+ if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) {
575
+ status = PREGNANCY_LOINC_CODES.PREGNANCY_STATUS[c.code];
582
576
  }
583
577
  });
584
578
  if (observation.valueCodeableConcept) {
585
579
  observation.valueCodeableConcept.coding?.forEach((c) => {
586
- if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
580
+ if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
587
581
  if (status) {
588
582
  status += " - ";
589
583
  }
590
- status += PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME[c.code];
584
+ status += PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME[c.code];
591
585
  }
592
586
  });
593
587
  }
@@ -836,7 +830,7 @@ var PatientTemplate = class _PatientTemplate {
836
830
  <li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
837
831
  <li><strong>Date of Birth:</strong>${patient.birthDate || ""}</li>
838
832
  <li><strong>Identifier(s):</strong>${this.renderIdentifiers(patient)}</li>
839
- <li><strong>Telecom:</strong>${this.renderTelecom(patient)}</li>
833
+ <li><strong>Telecom:</strong><ul>${this.renderTelecom(patient)}</ul></li>
840
834
  <li><strong>Address(es):</strong>${this.renderAddresses(patient)}</li>
841
835
  <li><strong>Marital Status:</strong> ${patient.maritalStatus?.text || ""}</li>
842
836
  <li><strong>Deceased:</strong>${this.renderDeceased(patient)}</li>
@@ -856,10 +850,14 @@ var PatientTemplate = class _PatientTemplate {
856
850
  if (!patient.name || patient.name.length === 0) {
857
851
  return "";
858
852
  }
859
- return patient.name.map((name) => {
860
- const nameText = name.text || ((name.given || []).join(" ") + " " + (name.family || "")).trim();
861
- return `<ul><li>${nameText}</li></ul>`;
862
- }).join("");
853
+ const uniqueNames = /* @__PURE__ */ new Set();
854
+ patient.name.forEach((name) => {
855
+ if (name.use !== "old") {
856
+ const nameText = name.text || ((name.given || []).join(" ") + " " + (name.family || "")).trim();
857
+ uniqueNames.add(nameText);
858
+ }
859
+ });
860
+ return Array.from(uniqueNames).map((nameText) => `<ul><li>${nameText}</li></ul>`).join("");
863
861
  }
864
862
  /**
865
863
  * Renders patient identifiers as HTML list items
@@ -877,19 +875,38 @@ var PatientTemplate = class _PatientTemplate {
877
875
  }).join("");
878
876
  }
879
877
  /**
880
- * Renders patient telecom information as HTML list items
878
+ * Renders patient telecom information grouped by system
881
879
  * @param patient - Patient resource
882
- * @returns HTML string of list items
880
+ * @returns HTML string grouped by system
883
881
  */
884
882
  static renderTelecom(patient) {
885
883
  if (!patient.telecom || patient.telecom.length === 0) {
886
884
  return "";
887
885
  }
888
- return patient.telecom.map((telecom) => {
889
- const system = telecom.system ? this.capitalize(telecom.system) : "";
890
- const value = telecom.value || "";
891
- const use = telecom.use ? ` (${telecom.use})` : "";
892
- return `<ul><li>${system}: ${value}${use}</li></ul>`;
886
+ const systemPriority = ["email", "phone", "pager", "sms", "fax", "url", "other"];
887
+ const telecomBySystem = /* @__PURE__ */ new Map();
888
+ patient.telecom.forEach((telecom) => {
889
+ if (telecom.system && telecom.value) {
890
+ const system = telecom.system.toLowerCase();
891
+ if (!telecomBySystem.has(system)) {
892
+ telecomBySystem.set(system, /* @__PURE__ */ new Set());
893
+ }
894
+ telecomBySystem.get(system).add(telecom.value);
895
+ }
896
+ });
897
+ return Array.from(telecomBySystem.entries()).sort(([systemA], [systemB]) => {
898
+ const priorityA = systemPriority.indexOf(systemA);
899
+ const priorityB = systemPriority.indexOf(systemB);
900
+ if (priorityA !== -1 && priorityB !== -1) {
901
+ return priorityA - priorityB;
902
+ }
903
+ if (priorityA !== -1) return -1;
904
+ if (priorityB !== -1) return 1;
905
+ return systemA.localeCompare(systemB);
906
+ }).map(([system, values]) => {
907
+ const systemLabel = this.capitalize(system);
908
+ const valueList = Array.from(values).map((value) => `<li>${value}</li>`).join("");
909
+ return `<li><strong>${systemLabel}:</strong><ul>${valueList}</ul></li>`;
893
910
  }).join("");
894
911
  }
895
912
  /**
@@ -901,10 +918,14 @@ var PatientTemplate = class _PatientTemplate {
901
918
  if (!patient.address || patient.address.length === 0) {
902
919
  return "";
903
920
  }
904
- return patient.address.map((address) => {
921
+ const uniqueAddresses = /* @__PURE__ */ new Set();
922
+ patient.address.forEach((address) => {
905
923
  const addressText = address.text || ((address.line || []).join(", ") + ", " + (address.city || "") + ", " + (address.country || "")).trim();
906
- return `<ul><li>${addressText}</li></ul>`;
907
- }).join("");
924
+ if (addressText) {
925
+ uniqueAddresses.add(addressText);
926
+ }
927
+ });
928
+ return Array.from(uniqueAddresses).map((addressText) => `<ul><li>${addressText}</li></ul>`).join("");
908
929
  }
909
930
  /**
910
931
  * Renders patient deceased status
@@ -1648,7 +1669,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1648
1669
  */
1649
1670
  static renderObservations(templateUtilities, observations, timezone) {
1650
1671
  let html = `
1651
- <h5>Observations</h5>
1672
+ <h3>Observations</h3>
1652
1673
  <table>
1653
1674
  <thead>
1654
1675
  <tr>
@@ -1688,7 +1709,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1688
1709
  */
1689
1710
  static renderDiagnosticReports(templateUtilities, reports, timezone) {
1690
1711
  let html = `
1691
- <h5>Diagnostic Reports</h5>
1712
+ <h3>Diagnostic Reports</h3>
1692
1713
  <table>
1693
1714
  <thead>
1694
1715
  <tr>
@@ -1988,8 +2009,7 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
1988
2009
  </tr>`;
1989
2010
  }
1990
2011
  html += `</tbody>
1991
- </table>
1992
- </div>`;
2012
+ </table>`;
1993
2013
  }
1994
2014
  if (clinicalImpressions.length > 0) {
1995
2015
  html += `<h3>Clinical Impressions</h3>
@@ -2041,6 +2061,8 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
2041
2061
  <td>${notes}</td>
2042
2062
  </tr>`;
2043
2063
  }
2064
+ html += `</tbody>
2065
+ </table>`;
2044
2066
  }
2045
2067
  return html;
2046
2068
  }
@@ -2154,87 +2176,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
2154
2176
  }
2155
2177
  };
2156
2178
 
2157
- // src/narratives/templates/typescript/FamilyHistoryTemplate.ts
2158
- var FamilyHistoryTemplate = class {
2159
- /**
2160
- * Generate HTML narrative for Family History
2161
- * @param resource - FHIR Bundle containing FamilyMemberHistory resources
2162
- * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2163
- * @returns HTML string for rendering
2164
- */
2165
- generateNarrative(resource, timezone) {
2166
- const templateUtilities = new TemplateUtilities(resource);
2167
- let html = `
2168
- <h5>Family History</h5>
2169
- <table>
2170
- <thead>
2171
- <tr>
2172
- <th>Relationship</th>
2173
- <th>Condition</th>
2174
- <th>Status</th>
2175
- <th>Onset</th>
2176
- <th>Notes</th>
2177
- </tr>
2178
- </thead>
2179
- <tbody>`;
2180
- if (resource.entry && Array.isArray(resource.entry)) {
2181
- let hasFamilyHistory = false;
2182
- for (const entry of resource.entry) {
2183
- const familyHistory = entry.resource;
2184
- if (!familyHistory || familyHistory.resourceType !== "FamilyMemberHistory") {
2185
- continue;
2186
- }
2187
- hasFamilyHistory = true;
2188
- const fmh = familyHistory;
2189
- const relationship = templateUtilities.codeableConcept(fmh.relationship, "display");
2190
- if (fmh.condition && Array.isArray(fmh.condition)) {
2191
- for (const condition of fmh.condition) {
2192
- const conditionCode = templateUtilities.codeableConcept(condition.code, "display");
2193
- const status = fmh.status || "";
2194
- let onset = "";
2195
- if (condition.onsetAge) {
2196
- onset = templateUtilities.renderOnset(condition.onsetAge, timezone);
2197
- }
2198
- const notes = condition.note ? templateUtilities.renderNotes(condition.note, timezone) : templateUtilities.renderNotes(fmh.note, timezone);
2199
- html += `
2200
- <tr id="${templateUtilities.narrativeLinkId(fmh)}">
2201
- <td>${relationship}</td>
2202
- <td>${conditionCode}</td>
2203
- <td>${status}</td>
2204
- <td>${onset}</td>
2205
- <td>${notes}</td>
2206
- </tr>`;
2207
- }
2208
- } else {
2209
- html += `
2210
- <tr id="${templateUtilities.narrativeLinkId(fmh)}">
2211
- <td>${relationship}</td>
2212
- <td>Not specified</td>
2213
- <td>${fmh.status || ""}</td>
2214
- <td></td>
2215
- <td>${templateUtilities.renderNotes(fmh.note, timezone)}</td>
2216
- </tr>`;
2217
- }
2218
- }
2219
- if (!hasFamilyHistory) {
2220
- html += `
2221
- <tr>
2222
- <td colspan="5">No family history recorded</td>
2223
- </tr>`;
2224
- }
2225
- } else {
2226
- html += `
2227
- <tr>
2228
- <td colspan="5">No family history recorded</td>
2229
- </tr>`;
2230
- }
2231
- html += `
2232
- </tbody>
2233
- </table>`;
2234
- return html;
2235
- }
2236
- };
2237
-
2238
2179
  // src/narratives/templates/typescript/TypeScriptTemplateMapper.ts
2239
2180
  var TypeScriptTemplateMapper = class {
2240
2181
  /**
@@ -2264,7 +2205,6 @@ TypeScriptTemplateMapper.sectionToTemplate = {
2264
2205
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: new MedicalDevicesTemplate(),
2265
2206
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
2266
2207
  ["HistoryOfProceduresSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
2267
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: new FamilyHistoryTemplate(),
2268
2208
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: new SocialHistoryTemplate(),
2269
2209
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
2270
2210
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: new FunctionalStatusTemplate(),
@@ -2528,7 +2468,7 @@ var ComprehensiveIPSCompositionBuilder = class {
2528
2468
  "Patient" /* PATIENT */,
2529
2469
  [this.patient],
2530
2470
  timezone,
2531
- false
2471
+ true
2532
2472
  )
2533
2473
  };
2534
2474
  const bundle = {
@@ -2569,6 +2509,13 @@ var ComprehensiveIPSCompositionBuilder = class {
2569
2509
  });
2570
2510
  return bundle;
2571
2511
  }
2512
+ /**
2513
+ * Returns the Composition sections without creating a full bundle.
2514
+ * @returns Array of TCompositionSection
2515
+ */
2516
+ getSections() {
2517
+ return this.sections;
2518
+ }
2572
2519
  };
2573
2520
 
2574
2521
  // src/index.ts
package/dist/index.d.cts CHANGED
@@ -518,6 +518,22 @@ type TPatient = {
518
518
  link?: TPatientLink[];
519
519
  };
520
520
 
521
+ type TCompositionSection = {
522
+ id?: string;
523
+ extension?: TExtension[];
524
+ modifierExtension?: TExtension[];
525
+ title?: string;
526
+ code?: TCodeableConcept;
527
+ author?: TReference[];
528
+ focus?: TReference;
529
+ text?: TNarrative;
530
+ mode?: string;
531
+ orderedBy?: TCodeableConcept;
532
+ entry?: TReference[];
533
+ emptyReason?: TCodeableConcept;
534
+ section?: TCompositionSection[];
535
+ };
536
+
521
537
  type TDomainResource = {
522
538
  resourceType?: string;
523
539
  id?: string;
@@ -545,8 +561,7 @@ declare enum IPSSections {
545
561
  CARE_PLAN = "PlanOfCareSection",
546
562
  MEDICAL_HISTORY = "HistoryOfPastIllnessSection",
547
563
  SOCIAL_HISTORY = "SocialHistorySection",
548
- VITAL_SIGNS = "VitalSignsSection",
549
- FAMILY_HISTORY = "FamilyHistorySection"
564
+ VITAL_SIGNS = "VitalSignsSection"
550
565
  }
551
566
 
552
567
  type TBundleLink = {
@@ -646,6 +661,11 @@ declare class ComprehensiveIPSCompositionBuilder {
646
661
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
647
662
  */
648
663
  buildBundleAsync(authorOrganizationId: string, authorOrganizationName: string, baseUrl: string, timezone: string | undefined): Promise<TBundle>;
664
+ /**
665
+ * Returns the Composition sections without creating a full bundle.
666
+ * @returns Array of TCompositionSection
667
+ */
668
+ getSections(): TCompositionSection[];
649
669
  }
650
670
 
651
671
  interface Narrative {
package/dist/index.d.ts CHANGED
@@ -518,6 +518,22 @@ type TPatient = {
518
518
  link?: TPatientLink[];
519
519
  };
520
520
 
521
+ type TCompositionSection = {
522
+ id?: string;
523
+ extension?: TExtension[];
524
+ modifierExtension?: TExtension[];
525
+ title?: string;
526
+ code?: TCodeableConcept;
527
+ author?: TReference[];
528
+ focus?: TReference;
529
+ text?: TNarrative;
530
+ mode?: string;
531
+ orderedBy?: TCodeableConcept;
532
+ entry?: TReference[];
533
+ emptyReason?: TCodeableConcept;
534
+ section?: TCompositionSection[];
535
+ };
536
+
521
537
  type TDomainResource = {
522
538
  resourceType?: string;
523
539
  id?: string;
@@ -545,8 +561,7 @@ declare enum IPSSections {
545
561
  CARE_PLAN = "PlanOfCareSection",
546
562
  MEDICAL_HISTORY = "HistoryOfPastIllnessSection",
547
563
  SOCIAL_HISTORY = "SocialHistorySection",
548
- VITAL_SIGNS = "VitalSignsSection",
549
- FAMILY_HISTORY = "FamilyHistorySection"
564
+ VITAL_SIGNS = "VitalSignsSection"
550
565
  }
551
566
 
552
567
  type TBundleLink = {
@@ -646,6 +661,11 @@ declare class ComprehensiveIPSCompositionBuilder {
646
661
  * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
647
662
  */
648
663
  buildBundleAsync(authorOrganizationId: string, authorOrganizationName: string, baseUrl: string, timezone: string | undefined): Promise<TBundle>;
664
+ /**
665
+ * Returns the Composition sections without creating a full bundle.
666
+ * @returns Array of TCompositionSection
667
+ */
668
+ getSections(): TCompositionSection[];
649
669
  }
650
670
 
651
671
  interface Narrative {
package/dist/index.js CHANGED
@@ -15,7 +15,6 @@ var IPSSections = /* @__PURE__ */ ((IPSSections2) => {
15
15
  IPSSections2["MEDICAL_HISTORY"] = "HistoryOfPastIllnessSection";
16
16
  IPSSections2["SOCIAL_HISTORY"] = "SocialHistorySection";
17
17
  IPSSections2["VITAL_SIGNS"] = "VitalSignsSection";
18
- IPSSections2["FAMILY_HISTORY"] = "FamilyHistorySection";
19
18
  return IPSSections2;
20
19
  })(IPSSections || {});
21
20
 
@@ -30,7 +29,6 @@ var IPS_SECTION_LOINC_CODES = {
30
29
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: "46264-8",
31
30
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: "30954-2",
32
31
  ["HistoryOfProceduresSection" /* PROCEDURES */]: "47519-4",
33
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: "10157-6",
34
32
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: "29762-2",
35
33
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "10162-6",
36
34
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: "47420-5",
@@ -53,10 +51,9 @@ var IPS_SECTION_DISPLAY_NAMES = {
53
51
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: "History of Pregnancies",
54
52
  ["PlanOfCareSection" /* CARE_PLAN */]: "Plan of Care",
55
53
  ["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: "History of Past Illness",
56
- ["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History",
57
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: "History of Family Member Diseases"
54
+ ["SocialHistorySection" /* SOCIAL_HISTORY */]: "Social History"
58
55
  };
59
- var PREGNANCY_LONIC_CODES = {
56
+ var PREGNANCY_LOINC_CODES = {
60
57
  PREGNANCY_STATUS: {
61
58
  "LA15173-0": "Pregnant",
62
59
  "LA26683-5": "Not pregnant",
@@ -74,7 +71,7 @@ var PREGNANCY_LONIC_CODES = {
74
71
  "33065-4": "Ectopic Pregnancy"
75
72
  }
76
73
  };
77
- var SOCIAL_HISTORY_LONIC_CODES = {
74
+ var SOCIAL_HISTORY_LOINC_CODES = {
78
75
  "72166-2": "Tobacco Use",
79
76
  "74013-4": "Alcohol Use"
80
77
  };
@@ -93,7 +90,6 @@ var IPSSectionResourceMap = {
93
90
  // Device resource is used for medical devices name
94
91
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: ["DiagnosticReport", "Observation"],
95
92
  ["HistoryOfProceduresSection" /* PROCEDURES */]: ["Procedure"],
96
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: ["FamilyMemberHistory"],
97
93
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: ["Observation"],
98
94
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: ["Observation"],
99
95
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: ["Condition", "ClinicalImpression"],
@@ -114,12 +110,10 @@ var IPSSectionResourceFilters = {
114
110
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
115
111
  // Only include completed procedures
116
112
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
117
- // Only include family history resources
118
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: (resource) => resource.resourceType === "FamilyMemberHistory",
119
113
  // Only include social history Observations (category.coding contains 'social-history')
120
- ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LONIC_CODES).includes(c.code)),
114
+ ["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && resource.code?.coding?.some((c) => Object.keys(SOCIAL_HISTORY_LOINC_CODES).includes(c.code)),
121
115
  // Only include pregnancy history Observations (category.coding contains 'pregnancy')
122
- ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code?.coding?.some((c) => Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) || resource.valueCodeableConcept?.coding?.some((c) => Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code))),
116
+ ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (resource.code?.coding?.some((c) => Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) || resource.valueCodeableConcept?.coding?.some((c) => Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code))),
123
117
  // Only include active functional status Conditions or ClinicalImpressions
124
118
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => typeof c.code === "string") || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
125
119
  // Only include resolved medical history Conditions
@@ -549,17 +543,17 @@ var TemplateUtilities = class {
549
543
  extractPregnancyStatus(observation) {
550
544
  let status = "";
551
545
  observation.code?.coding?.forEach((c) => {
552
- if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_STATUS).includes(c.code)) {
553
- status = PREGNANCY_LONIC_CODES.PREGNANCY_STATUS[c.code];
546
+ if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS).includes(c.code)) {
547
+ status = PREGNANCY_LOINC_CODES.PREGNANCY_STATUS[c.code];
554
548
  }
555
549
  });
556
550
  if (observation.valueCodeableConcept) {
557
551
  observation.valueCodeableConcept.coding?.forEach((c) => {
558
- if (c.code && Object.keys(PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
552
+ if (c.code && Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME).includes(c.code)) {
559
553
  if (status) {
560
554
  status += " - ";
561
555
  }
562
- status += PREGNANCY_LONIC_CODES.PREGNANCY_OUTCOME[c.code];
556
+ status += PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME[c.code];
563
557
  }
564
558
  });
565
559
  }
@@ -808,7 +802,7 @@ var PatientTemplate = class _PatientTemplate {
808
802
  <li><strong>Gender:</strong>${patient.gender ? this.capitalize(patient.gender) : ""}</li>
809
803
  <li><strong>Date of Birth:</strong>${patient.birthDate || ""}</li>
810
804
  <li><strong>Identifier(s):</strong>${this.renderIdentifiers(patient)}</li>
811
- <li><strong>Telecom:</strong>${this.renderTelecom(patient)}</li>
805
+ <li><strong>Telecom:</strong><ul>${this.renderTelecom(patient)}</ul></li>
812
806
  <li><strong>Address(es):</strong>${this.renderAddresses(patient)}</li>
813
807
  <li><strong>Marital Status:</strong> ${patient.maritalStatus?.text || ""}</li>
814
808
  <li><strong>Deceased:</strong>${this.renderDeceased(patient)}</li>
@@ -828,10 +822,14 @@ var PatientTemplate = class _PatientTemplate {
828
822
  if (!patient.name || patient.name.length === 0) {
829
823
  return "";
830
824
  }
831
- return patient.name.map((name) => {
832
- const nameText = name.text || ((name.given || []).join(" ") + " " + (name.family || "")).trim();
833
- return `<ul><li>${nameText}</li></ul>`;
834
- }).join("");
825
+ const uniqueNames = /* @__PURE__ */ new Set();
826
+ patient.name.forEach((name) => {
827
+ if (name.use !== "old") {
828
+ const nameText = name.text || ((name.given || []).join(" ") + " " + (name.family || "")).trim();
829
+ uniqueNames.add(nameText);
830
+ }
831
+ });
832
+ return Array.from(uniqueNames).map((nameText) => `<ul><li>${nameText}</li></ul>`).join("");
835
833
  }
836
834
  /**
837
835
  * Renders patient identifiers as HTML list items
@@ -849,19 +847,38 @@ var PatientTemplate = class _PatientTemplate {
849
847
  }).join("");
850
848
  }
851
849
  /**
852
- * Renders patient telecom information as HTML list items
850
+ * Renders patient telecom information grouped by system
853
851
  * @param patient - Patient resource
854
- * @returns HTML string of list items
852
+ * @returns HTML string grouped by system
855
853
  */
856
854
  static renderTelecom(patient) {
857
855
  if (!patient.telecom || patient.telecom.length === 0) {
858
856
  return "";
859
857
  }
860
- return patient.telecom.map((telecom) => {
861
- const system = telecom.system ? this.capitalize(telecom.system) : "";
862
- const value = telecom.value || "";
863
- const use = telecom.use ? ` (${telecom.use})` : "";
864
- return `<ul><li>${system}: ${value}${use}</li></ul>`;
858
+ const systemPriority = ["email", "phone", "pager", "sms", "fax", "url", "other"];
859
+ const telecomBySystem = /* @__PURE__ */ new Map();
860
+ patient.telecom.forEach((telecom) => {
861
+ if (telecom.system && telecom.value) {
862
+ const system = telecom.system.toLowerCase();
863
+ if (!telecomBySystem.has(system)) {
864
+ telecomBySystem.set(system, /* @__PURE__ */ new Set());
865
+ }
866
+ telecomBySystem.get(system).add(telecom.value);
867
+ }
868
+ });
869
+ return Array.from(telecomBySystem.entries()).sort(([systemA], [systemB]) => {
870
+ const priorityA = systemPriority.indexOf(systemA);
871
+ const priorityB = systemPriority.indexOf(systemB);
872
+ if (priorityA !== -1 && priorityB !== -1) {
873
+ return priorityA - priorityB;
874
+ }
875
+ if (priorityA !== -1) return -1;
876
+ if (priorityB !== -1) return 1;
877
+ return systemA.localeCompare(systemB);
878
+ }).map(([system, values]) => {
879
+ const systemLabel = this.capitalize(system);
880
+ const valueList = Array.from(values).map((value) => `<li>${value}</li>`).join("");
881
+ return `<li><strong>${systemLabel}:</strong><ul>${valueList}</ul></li>`;
865
882
  }).join("");
866
883
  }
867
884
  /**
@@ -873,10 +890,14 @@ var PatientTemplate = class _PatientTemplate {
873
890
  if (!patient.address || patient.address.length === 0) {
874
891
  return "";
875
892
  }
876
- return patient.address.map((address) => {
893
+ const uniqueAddresses = /* @__PURE__ */ new Set();
894
+ patient.address.forEach((address) => {
877
895
  const addressText = address.text || ((address.line || []).join(", ") + ", " + (address.city || "") + ", " + (address.country || "")).trim();
878
- return `<ul><li>${addressText}</li></ul>`;
879
- }).join("");
896
+ if (addressText) {
897
+ uniqueAddresses.add(addressText);
898
+ }
899
+ });
900
+ return Array.from(uniqueAddresses).map((addressText) => `<ul><li>${addressText}</li></ul>`).join("");
880
901
  }
881
902
  /**
882
903
  * Renders patient deceased status
@@ -1620,7 +1641,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1620
1641
  */
1621
1642
  static renderObservations(templateUtilities, observations, timezone) {
1622
1643
  let html = `
1623
- <h5>Observations</h5>
1644
+ <h3>Observations</h3>
1624
1645
  <table>
1625
1646
  <thead>
1626
1647
  <tr>
@@ -1660,7 +1681,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
1660
1681
  */
1661
1682
  static renderDiagnosticReports(templateUtilities, reports, timezone) {
1662
1683
  let html = `
1663
- <h5>Diagnostic Reports</h5>
1684
+ <h3>Diagnostic Reports</h3>
1664
1685
  <table>
1665
1686
  <thead>
1666
1687
  <tr>
@@ -1960,8 +1981,7 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
1960
1981
  </tr>`;
1961
1982
  }
1962
1983
  html += `</tbody>
1963
- </table>
1964
- </div>`;
1984
+ </table>`;
1965
1985
  }
1966
1986
  if (clinicalImpressions.length > 0) {
1967
1987
  html += `<h3>Clinical Impressions</h3>
@@ -2013,6 +2033,8 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
2013
2033
  <td>${notes}</td>
2014
2034
  </tr>`;
2015
2035
  }
2036
+ html += `</tbody>
2037
+ </table>`;
2016
2038
  }
2017
2039
  return html;
2018
2040
  }
@@ -2126,87 +2148,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
2126
2148
  }
2127
2149
  };
2128
2150
 
2129
- // src/narratives/templates/typescript/FamilyHistoryTemplate.ts
2130
- var FamilyHistoryTemplate = class {
2131
- /**
2132
- * Generate HTML narrative for Family History
2133
- * @param resource - FHIR Bundle containing FamilyMemberHistory resources
2134
- * @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
2135
- * @returns HTML string for rendering
2136
- */
2137
- generateNarrative(resource, timezone) {
2138
- const templateUtilities = new TemplateUtilities(resource);
2139
- let html = `
2140
- <h5>Family History</h5>
2141
- <table>
2142
- <thead>
2143
- <tr>
2144
- <th>Relationship</th>
2145
- <th>Condition</th>
2146
- <th>Status</th>
2147
- <th>Onset</th>
2148
- <th>Notes</th>
2149
- </tr>
2150
- </thead>
2151
- <tbody>`;
2152
- if (resource.entry && Array.isArray(resource.entry)) {
2153
- let hasFamilyHistory = false;
2154
- for (const entry of resource.entry) {
2155
- const familyHistory = entry.resource;
2156
- if (!familyHistory || familyHistory.resourceType !== "FamilyMemberHistory") {
2157
- continue;
2158
- }
2159
- hasFamilyHistory = true;
2160
- const fmh = familyHistory;
2161
- const relationship = templateUtilities.codeableConcept(fmh.relationship, "display");
2162
- if (fmh.condition && Array.isArray(fmh.condition)) {
2163
- for (const condition of fmh.condition) {
2164
- const conditionCode = templateUtilities.codeableConcept(condition.code, "display");
2165
- const status = fmh.status || "";
2166
- let onset = "";
2167
- if (condition.onsetAge) {
2168
- onset = templateUtilities.renderOnset(condition.onsetAge, timezone);
2169
- }
2170
- const notes = condition.note ? templateUtilities.renderNotes(condition.note, timezone) : templateUtilities.renderNotes(fmh.note, timezone);
2171
- html += `
2172
- <tr id="${templateUtilities.narrativeLinkId(fmh)}">
2173
- <td>${relationship}</td>
2174
- <td>${conditionCode}</td>
2175
- <td>${status}</td>
2176
- <td>${onset}</td>
2177
- <td>${notes}</td>
2178
- </tr>`;
2179
- }
2180
- } else {
2181
- html += `
2182
- <tr id="${templateUtilities.narrativeLinkId(fmh)}">
2183
- <td>${relationship}</td>
2184
- <td>Not specified</td>
2185
- <td>${fmh.status || ""}</td>
2186
- <td></td>
2187
- <td>${templateUtilities.renderNotes(fmh.note, timezone)}</td>
2188
- </tr>`;
2189
- }
2190
- }
2191
- if (!hasFamilyHistory) {
2192
- html += `
2193
- <tr>
2194
- <td colspan="5">No family history recorded</td>
2195
- </tr>`;
2196
- }
2197
- } else {
2198
- html += `
2199
- <tr>
2200
- <td colspan="5">No family history recorded</td>
2201
- </tr>`;
2202
- }
2203
- html += `
2204
- </tbody>
2205
- </table>`;
2206
- return html;
2207
- }
2208
- };
2209
-
2210
2151
  // src/narratives/templates/typescript/TypeScriptTemplateMapper.ts
2211
2152
  var TypeScriptTemplateMapper = class {
2212
2153
  /**
@@ -2236,7 +2177,6 @@ TypeScriptTemplateMapper.sectionToTemplate = {
2236
2177
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: new MedicalDevicesTemplate(),
2237
2178
  ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: new DiagnosticResultsTemplate(),
2238
2179
  ["HistoryOfProceduresSection" /* PROCEDURES */]: new HistoryOfProceduresTemplate(),
2239
- ["FamilyHistorySection" /* FAMILY_HISTORY */]: new FamilyHistoryTemplate(),
2240
2180
  ["SocialHistorySection" /* SOCIAL_HISTORY */]: new SocialHistoryTemplate(),
2241
2181
  ["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: new PregnancyTemplate(),
2242
2182
  ["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: new FunctionalStatusTemplate(),
@@ -2500,7 +2440,7 @@ var ComprehensiveIPSCompositionBuilder = class {
2500
2440
  "Patient" /* PATIENT */,
2501
2441
  [this.patient],
2502
2442
  timezone,
2503
- false
2443
+ true
2504
2444
  )
2505
2445
  };
2506
2446
  const bundle = {
@@ -2541,6 +2481,13 @@ var ComprehensiveIPSCompositionBuilder = class {
2541
2481
  });
2542
2482
  return bundle;
2543
2483
  }
2484
+ /**
2485
+ * Returns the Composition sections without creating a full bundle.
2486
+ * @returns Array of TCompositionSection
2487
+ */
2488
+ getSections() {
2489
+ return this.sections;
2490
+ }
2544
2491
  };
2545
2492
 
2546
2493
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imranq2/fhirpatientsummary",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
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",