@imranq2/fhirpatientsummary 1.0.38 → 1.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +211 -27
- package/dist/index.js +211 -27
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -199,6 +199,24 @@ var FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES = {
|
|
|
199
199
|
"55123-4": "Resident Believes Capable of Increased Independence",
|
|
200
200
|
"45613-7": "Staff Believes Capable of Increased Independence"
|
|
201
201
|
};
|
|
202
|
+
var ADVANCED_DIRECTIVE_CATEGORY_CODES = {
|
|
203
|
+
acd: "Advance Directive",
|
|
204
|
+
dnr: "Do Not Resuscitate",
|
|
205
|
+
polst: "POLST",
|
|
206
|
+
hcd: "Health Care Directive"
|
|
207
|
+
};
|
|
208
|
+
var ADVANCED_DIRECTIVE_CATEGORY_SYSTEM = "http://terminology.hl7.org/CodeSystem/consentcategorycodes";
|
|
209
|
+
var ADVANCED_DIRECTIVE_LOINC_CODES = {
|
|
210
|
+
"45473-6": "Advance healthcare directive Completed",
|
|
211
|
+
"45474-4": "Do not resuscitate",
|
|
212
|
+
"45475-1": "Do not hospitalize",
|
|
213
|
+
"45476-9": "Organ donation",
|
|
214
|
+
"45477-7": "Autopsy request",
|
|
215
|
+
"45478-5": "Feeding restrictions",
|
|
216
|
+
"45479-3": "Medication restrictions",
|
|
217
|
+
"45480-1": "Other treatment restrictions"
|
|
218
|
+
};
|
|
219
|
+
var LOINC_SYSTEM = "http://loinc.org";
|
|
202
220
|
|
|
203
221
|
// src/structures/ips_section_constants.ts
|
|
204
222
|
var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
|
|
@@ -243,8 +261,7 @@ var IPSSectionResourceFilters = {
|
|
|
243
261
|
// Only include active care plans
|
|
244
262
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
245
263
|
// Only include active advance directives (Consent resources)
|
|
246
|
-
|
|
247
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: () => false
|
|
264
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active" && resource.scope?.coding?.some((c) => codingMatches(c, "adr", "http://terminology.hl7.org/CodeSystem/consentscope"))
|
|
248
265
|
};
|
|
249
266
|
var IPSSectionSummaryCompositionFilter = {
|
|
250
267
|
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "allergy_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
@@ -260,8 +277,10 @@ var IPSSectionSummaryCompositionFilter = {
|
|
|
260
277
|
var IPSSectionSummaryIPSCompositionFilter = {
|
|
261
278
|
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_patient_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
262
279
|
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_vital_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
280
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_advanced_directives_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
263
281
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_social_history_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
264
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, ["ips_functional_status_condition_summary_document", "ips_functional_status_clinical_impression_summary_document"], IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
|
|
282
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, ["ips_functional_status_condition_summary_document", "ips_functional_status_clinical_impression_summary_document"], IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
283
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_medical_device_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
|
|
265
284
|
};
|
|
266
285
|
var IPSSectionResourceHelper = class {
|
|
267
286
|
static getResourceFilterForSection(section) {
|
|
@@ -393,13 +412,26 @@ var TemplateUtilities = class {
|
|
|
393
412
|
return code ? `${code} (${systemDisplay})` : "";
|
|
394
413
|
}
|
|
395
414
|
/**
|
|
396
|
-
*
|
|
415
|
+
* Resolves and returns a Device resource from a Device Reference.
|
|
416
|
+
* Returns null if the reference cannot be resolved or is not a Device.
|
|
417
|
+
* @param deviceRef - Reference to a Device resource
|
|
418
|
+
* @returns The resolved Device resource or null
|
|
419
|
+
*/
|
|
420
|
+
getDeviceFromReference(deviceRef) {
|
|
421
|
+
if (!deviceRef) return null;
|
|
422
|
+
const device = this.resolveReference(deviceRef);
|
|
423
|
+
return device && device.resourceType === "Device" ? device : null;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Renders a Device name from a Device reference.
|
|
427
|
+
* Uses getDeviceFromReference to resolve the Device and returns a concatenated device.deviceName.
|
|
428
|
+
* Returns an empty string if no deviceName is available. The returned text is HTML-safe.
|
|
397
429
|
* @param deviceRef - Reference to a Device resource
|
|
398
|
-
* @returns
|
|
430
|
+
* @returns Sanitized device name or empty string
|
|
399
431
|
*/
|
|
400
432
|
renderDevice(deviceRef) {
|
|
401
|
-
const device =
|
|
402
|
-
if (device && device.
|
|
433
|
+
const device = this.getDeviceFromReference(deviceRef);
|
|
434
|
+
if (device && device.deviceName && device.deviceName.length > 0) {
|
|
403
435
|
return this.safeConcat(device.deviceName, "name");
|
|
404
436
|
}
|
|
405
437
|
return "";
|
|
@@ -2815,11 +2847,64 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
2815
2847
|
return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
|
|
2816
2848
|
}
|
|
2817
2849
|
/**
|
|
2818
|
-
*
|
|
2819
|
-
* @param resources - FHIR
|
|
2850
|
+
* Generate HTML narrative for history of medical devices using summary
|
|
2851
|
+
* @param resources - FHIR Composition resources
|
|
2820
2852
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2821
2853
|
* @returns HTML string for rendering
|
|
2822
2854
|
*/
|
|
2855
|
+
generateSummaryNarrative(resources, timezone) {
|
|
2856
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2857
|
+
let isSummaryCreated = false;
|
|
2858
|
+
let html = `<p>This list includes all information about the patient's medical devices history, sorted by recorded date (most recent first).</p>
|
|
2859
|
+
`;
|
|
2860
|
+
html += `
|
|
2861
|
+
<table>
|
|
2862
|
+
<thead>
|
|
2863
|
+
<tr>
|
|
2864
|
+
<th>Device</th>
|
|
2865
|
+
<th>Code (System)</th>
|
|
2866
|
+
<th>Status</th>
|
|
2867
|
+
<th>Comments</th>
|
|
2868
|
+
<th>Date Recorded</th>
|
|
2869
|
+
<th>Source</th>
|
|
2870
|
+
</tr>
|
|
2871
|
+
</thead>
|
|
2872
|
+
<tbody>`;
|
|
2873
|
+
for (const resourceItem of resources) {
|
|
2874
|
+
for (const rowData of resourceItem.section ?? []) {
|
|
2875
|
+
const sectionCodeableConcept = rowData.code;
|
|
2876
|
+
const data = {};
|
|
2877
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2878
|
+
for (const columnData of rowData.section ?? []) {
|
|
2879
|
+
const columnTitle = columnData.title;
|
|
2880
|
+
if (columnTitle) {
|
|
2881
|
+
data[columnTitle] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
if (data["Device Name"]?.toLowerCase() === "unknown") {
|
|
2885
|
+
continue;
|
|
2886
|
+
}
|
|
2887
|
+
isSummaryCreated = true;
|
|
2888
|
+
html += `
|
|
2889
|
+
<tr>
|
|
2890
|
+
<td>${templateUtilities.capitalizeFirstLetter(data["Device Name"] ?? "")}</td>
|
|
2891
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
2892
|
+
<td>${templateUtilities.renderTextAsHtml(data["Status"] ?? "")}</td>
|
|
2893
|
+
<td>${templateUtilities.renderTextAsHtml(data["Notes"] ?? "")}</td>
|
|
2894
|
+
<td>${templateUtilities.renderTime(data["Recorded On"] ?? "", timezone)}</td>
|
|
2895
|
+
<td>${data["Source"] ?? ""}</td>
|
|
2896
|
+
</tr>`;
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
html += `
|
|
2900
|
+
</tbody>
|
|
2901
|
+
</table>`;
|
|
2902
|
+
return isSummaryCreated ? html : void 0;
|
|
2903
|
+
}
|
|
2904
|
+
/**
|
|
2905
|
+
* Internal static implementation that generates the narrative from DeviceUseStatement resources.
|
|
2906
|
+
* The “Code (System)” column renders Device.type as "code (SystemDisplay)" using TemplateUtilities.codeableConceptCoding.
|
|
2907
|
+
*/
|
|
2823
2908
|
static generateStaticNarrative(resources, timezone) {
|
|
2824
2909
|
const templateUtilities = new TemplateUtilities(resources);
|
|
2825
2910
|
let html = `<p>This list includes all DeviceUseStatement resources, sorted by recorded date (most recent first).</p>
|
|
@@ -2827,6 +2912,7 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
2827
2912
|
<thead>
|
|
2828
2913
|
<tr>
|
|
2829
2914
|
<th>Device</th>
|
|
2915
|
+
<th>Code (System)</th>
|
|
2830
2916
|
<th>Status</th>
|
|
2831
2917
|
<th>Comments</th>
|
|
2832
2918
|
<th>Date Recorded</th>
|
|
@@ -2839,26 +2925,32 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
2839
2925
|
const dateB = b.recordedOn;
|
|
2840
2926
|
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
2841
2927
|
});
|
|
2842
|
-
|
|
2928
|
+
const devicesAdded = /* @__PURE__ */ new Set();
|
|
2843
2929
|
for (const dus of deviceStatements) {
|
|
2844
|
-
const deviceName = templateUtilities.
|
|
2845
|
-
if (deviceName?.toLowerCase() === "unknown") {
|
|
2930
|
+
const deviceName = templateUtilities.renderDevice(dus.device);
|
|
2931
|
+
if (deviceName?.toLowerCase() === "unknown" || devicesAdded.has(deviceName)) {
|
|
2932
|
+
continue;
|
|
2933
|
+
}
|
|
2934
|
+
const device = templateUtilities.getDeviceFromReference(dus.device);
|
|
2935
|
+
const codeSystem = templateUtilities.codeableConceptCoding(device?.type);
|
|
2936
|
+
if (!codeSystem) {
|
|
2846
2937
|
continue;
|
|
2847
2938
|
}
|
|
2848
|
-
|
|
2939
|
+
devicesAdded.add(deviceName);
|
|
2849
2940
|
html += `
|
|
2850
2941
|
<tr>
|
|
2851
2942
|
<td>${templateUtilities.capitalizeFirstLetter(deviceName)}</td>
|
|
2943
|
+
<td>${codeSystem}</td>
|
|
2852
2944
|
<td>${templateUtilities.renderTextAsHtml(dus.status || "")}</td>
|
|
2853
2945
|
<td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
|
|
2854
|
-
<td>${templateUtilities.
|
|
2946
|
+
<td>${templateUtilities.renderTime(dus.recordedOn, timezone)}</td>
|
|
2855
2947
|
<td>${templateUtilities.getOwnerTag(dus)}</td>
|
|
2856
2948
|
</tr>`;
|
|
2857
2949
|
}
|
|
2858
2950
|
html += `
|
|
2859
2951
|
</tbody>
|
|
2860
2952
|
</table>`;
|
|
2861
|
-
return
|
|
2953
|
+
return devicesAdded.size > 0 ? html : void 0;
|
|
2862
2954
|
}
|
|
2863
2955
|
};
|
|
2864
2956
|
|
|
@@ -4599,7 +4691,78 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
4599
4691
|
const dateB = new Date(b.dateTime || 0);
|
|
4600
4692
|
return dateB.getTime() - dateA.getTime();
|
|
4601
4693
|
});
|
|
4602
|
-
return _AdvanceDirectivesTemplate.generateStaticNarrative(
|
|
4694
|
+
return _AdvanceDirectivesTemplate.generateStaticNarrative(
|
|
4695
|
+
resources,
|
|
4696
|
+
timezone
|
|
4697
|
+
);
|
|
4698
|
+
}
|
|
4699
|
+
/**
|
|
4700
|
+
* Generate HTML narrative for advance directives using summary
|
|
4701
|
+
* @param resources - FHIR Composition resources
|
|
4702
|
+
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
4703
|
+
* @returns HTML string for rendering
|
|
4704
|
+
*/
|
|
4705
|
+
generateSummaryNarrative(resources, timezone) {
|
|
4706
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
4707
|
+
let isSummaryCreated = false;
|
|
4708
|
+
let html = `<p>This list includes all Consent resources, sorted by date (most recent first).</p>
|
|
4709
|
+
`;
|
|
4710
|
+
html += `
|
|
4711
|
+
<div>
|
|
4712
|
+
<table>
|
|
4713
|
+
<thead>
|
|
4714
|
+
<tr>
|
|
4715
|
+
<th>Directive</th>
|
|
4716
|
+
<th>Code (System)</th>
|
|
4717
|
+
<th>Action Controlled</th>
|
|
4718
|
+
<th>Policy Rule</th>
|
|
4719
|
+
<th>Date</th>
|
|
4720
|
+
<th>Source</th>
|
|
4721
|
+
</tr>
|
|
4722
|
+
</thead>
|
|
4723
|
+
<tbody>`;
|
|
4724
|
+
for (const resourceItem of resources) {
|
|
4725
|
+
for (const rowData of resourceItem.section ?? []) {
|
|
4726
|
+
const sectionCodeableConcept = rowData.code;
|
|
4727
|
+
const data = {};
|
|
4728
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(
|
|
4729
|
+
sectionCodeableConcept
|
|
4730
|
+
);
|
|
4731
|
+
for (const columnData of rowData.section ?? []) {
|
|
4732
|
+
const columnTitle = columnData.title;
|
|
4733
|
+
if (columnTitle) {
|
|
4734
|
+
if (columnTitle === "DateTime") {
|
|
4735
|
+
data[columnTitle] = templateUtilities.renderTime(
|
|
4736
|
+
columnData.text?.div ?? "",
|
|
4737
|
+
timezone
|
|
4738
|
+
);
|
|
4739
|
+
} else {
|
|
4740
|
+
data[columnTitle] = templateUtilities.renderTextAsHtml(
|
|
4741
|
+
columnData.text?.div ?? ""
|
|
4742
|
+
);
|
|
4743
|
+
}
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
if (data["Advanced Directive Name"]?.toLowerCase() === "unknown") {
|
|
4747
|
+
continue;
|
|
4748
|
+
}
|
|
4749
|
+
isSummaryCreated = true;
|
|
4750
|
+
html += `
|
|
4751
|
+
<tr>
|
|
4752
|
+
<td>${templateUtilities.capitalizeFirstLetter(data["Advanced Directive Name"] ?? "")}</td>
|
|
4753
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
4754
|
+
<td>${data["Provision Action"] ?? ""}</td>
|
|
4755
|
+
<td>${data["Policy Rule"] ?? ""}</td>
|
|
4756
|
+
<td>${data["DateTime"] ?? ""}</td>
|
|
4757
|
+
<td>${data["Source"] ?? ""}</td>
|
|
4758
|
+
</tr>`;
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
html += `
|
|
4762
|
+
</tbody>
|
|
4763
|
+
</table>
|
|
4764
|
+
</div>`;
|
|
4765
|
+
return isSummaryCreated ? html : void 0;
|
|
4603
4766
|
}
|
|
4604
4767
|
/**
|
|
4605
4768
|
* Internal static implementation that actually generates the narrative
|
|
@@ -4607,7 +4770,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
4607
4770
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
4608
4771
|
* @returns HTML string for rendering
|
|
4609
4772
|
*/
|
|
4610
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
4611
4773
|
static generateStaticNarrative(resources, timezone) {
|
|
4612
4774
|
const templateUtilities = new TemplateUtilities(resources);
|
|
4613
4775
|
let html = `<p>This list includes all Consent resources, sorted by date (most recent first).</p>
|
|
@@ -4615,35 +4777,57 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
4615
4777
|
html += `<table>
|
|
4616
4778
|
<thead>
|
|
4617
4779
|
<tr>
|
|
4618
|
-
<th>
|
|
4619
|
-
<th>
|
|
4780
|
+
<th>Directive</th>
|
|
4781
|
+
<th>Code (System)</th>
|
|
4620
4782
|
<th>Action Controlled</th>
|
|
4783
|
+
<th>Policy Rule</th>
|
|
4621
4784
|
<th>Date</th>
|
|
4622
4785
|
<th>Source</th>
|
|
4623
4786
|
</tr>
|
|
4624
4787
|
</thead>
|
|
4625
4788
|
<tbody>`;
|
|
4626
|
-
|
|
4789
|
+
const consentAdded = /* @__PURE__ */ new Set();
|
|
4627
4790
|
for (const resourceItem of resources) {
|
|
4628
4791
|
const consent = resourceItem;
|
|
4629
|
-
|
|
4630
|
-
|
|
4792
|
+
let consentDirective = {};
|
|
4793
|
+
for (const category of consent.category || []) {
|
|
4794
|
+
for (const coding of category.coding || []) {
|
|
4795
|
+
if (coding.system === ADVANCED_DIRECTIVE_CATEGORY_SYSTEM && coding.code && coding.code in ADVANCED_DIRECTIVE_CATEGORY_CODES) {
|
|
4796
|
+
consentDirective = {
|
|
4797
|
+
name: ADVANCED_DIRECTIVE_CATEGORY_CODES[coding.code],
|
|
4798
|
+
system: coding.system,
|
|
4799
|
+
code: coding.code
|
|
4800
|
+
};
|
|
4801
|
+
break;
|
|
4802
|
+
} else if (coding.system === LOINC_SYSTEM && coding.code && coding.code in ADVANCED_DIRECTIVE_LOINC_CODES) {
|
|
4803
|
+
consentDirective = {
|
|
4804
|
+
name: ADVANCED_DIRECTIVE_LOINC_CODES[coding.code],
|
|
4805
|
+
system: coding.system,
|
|
4806
|
+
code: coding.code
|
|
4807
|
+
};
|
|
4808
|
+
break;
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
if (!consentDirective.name || !consentDirective.code || !consentDirective.system || consentDirective.name.toLowerCase() === "unknown" || consentAdded.has(consentDirective.name)) {
|
|
4631
4813
|
continue;
|
|
4632
4814
|
}
|
|
4633
|
-
|
|
4815
|
+
consentAdded.add(consentDirective.name);
|
|
4816
|
+
const consentCodeSystem = `${consentDirective.code} (${codingSystemDisplayNames_default[consentDirective.system] || consentDirective.system})`;
|
|
4634
4817
|
html += `
|
|
4635
4818
|
<tr>
|
|
4636
|
-
<td>${
|
|
4637
|
-
<td>${
|
|
4819
|
+
<td>${consentDirective.name}</td>
|
|
4820
|
+
<td>${consentCodeSystem || ""}</td>
|
|
4638
4821
|
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
4639
|
-
<td>${consent.
|
|
4822
|
+
<td>${consent.policyRule ? templateUtilities.formatCodeableConceptValue(consent.policyRule) : ""}</td>
|
|
4823
|
+
<td>${templateUtilities.renderTime(consent.dateTime, timezone) || ""}</td>
|
|
4640
4824
|
<td>${templateUtilities.getOwnerTag(consent)}</td>
|
|
4641
4825
|
</tr>`;
|
|
4642
4826
|
}
|
|
4643
4827
|
html += `
|
|
4644
4828
|
</tbody>
|
|
4645
4829
|
</table>`;
|
|
4646
|
-
return
|
|
4830
|
+
return consentAdded.size > 0 ? html : void 0;
|
|
4647
4831
|
}
|
|
4648
4832
|
};
|
|
4649
4833
|
|
package/dist/index.js
CHANGED
|
@@ -171,6 +171,24 @@ var FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES = {
|
|
|
171
171
|
"55123-4": "Resident Believes Capable of Increased Independence",
|
|
172
172
|
"45613-7": "Staff Believes Capable of Increased Independence"
|
|
173
173
|
};
|
|
174
|
+
var ADVANCED_DIRECTIVE_CATEGORY_CODES = {
|
|
175
|
+
acd: "Advance Directive",
|
|
176
|
+
dnr: "Do Not Resuscitate",
|
|
177
|
+
polst: "POLST",
|
|
178
|
+
hcd: "Health Care Directive"
|
|
179
|
+
};
|
|
180
|
+
var ADVANCED_DIRECTIVE_CATEGORY_SYSTEM = "http://terminology.hl7.org/CodeSystem/consentcategorycodes";
|
|
181
|
+
var ADVANCED_DIRECTIVE_LOINC_CODES = {
|
|
182
|
+
"45473-6": "Advance healthcare directive Completed",
|
|
183
|
+
"45474-4": "Do not resuscitate",
|
|
184
|
+
"45475-1": "Do not hospitalize",
|
|
185
|
+
"45476-9": "Organ donation",
|
|
186
|
+
"45477-7": "Autopsy request",
|
|
187
|
+
"45478-5": "Feeding restrictions",
|
|
188
|
+
"45479-3": "Medication restrictions",
|
|
189
|
+
"45480-1": "Other treatment restrictions"
|
|
190
|
+
};
|
|
191
|
+
var LOINC_SYSTEM = "http://loinc.org";
|
|
174
192
|
|
|
175
193
|
// src/structures/ips_section_constants.ts
|
|
176
194
|
var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
|
|
@@ -215,8 +233,7 @@ var IPSSectionResourceFilters = {
|
|
|
215
233
|
// Only include active care plans
|
|
216
234
|
["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "CarePlan" && resource.status === "active",
|
|
217
235
|
// Only include active advance directives (Consent resources)
|
|
218
|
-
|
|
219
|
-
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: () => false
|
|
236
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Consent" && resource.status === "active" && resource.scope?.coding?.some((c) => codingMatches(c, "adr", "http://terminology.hl7.org/CodeSystem/consentscope"))
|
|
220
237
|
};
|
|
221
238
|
var IPSSectionSummaryCompositionFilter = {
|
|
222
239
|
["AllergyIntoleranceSection" /* ALLERGIES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "allergy_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
@@ -232,8 +249,10 @@ var IPSSectionSummaryCompositionFilter = {
|
|
|
232
249
|
var IPSSectionSummaryIPSCompositionFilter = {
|
|
233
250
|
["Patient" /* PATIENT */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_patient_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
234
251
|
["VitalSignsSection" /* VITAL_SIGNS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_vital_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
252
|
+
["AdvanceDirectivesSection" /* ADVANCE_DIRECTIVES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_advanced_directives_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
235
253
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_social_history_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
236
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, ["ips_functional_status_condition_summary_document", "ips_functional_status_clinical_impression_summary_document"], IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
|
|
254
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, ["ips_functional_status_condition_summary_document", "ips_functional_status_clinical_impression_summary_document"], IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM)),
|
|
255
|
+
["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => codingMatches(c, "ips_medical_device_summary_document", IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM))
|
|
237
256
|
};
|
|
238
257
|
var IPSSectionResourceHelper = class {
|
|
239
258
|
static getResourceFilterForSection(section) {
|
|
@@ -365,13 +384,26 @@ var TemplateUtilities = class {
|
|
|
365
384
|
return code ? `${code} (${systemDisplay})` : "";
|
|
366
385
|
}
|
|
367
386
|
/**
|
|
368
|
-
*
|
|
387
|
+
* Resolves and returns a Device resource from a Device Reference.
|
|
388
|
+
* Returns null if the reference cannot be resolved or is not a Device.
|
|
389
|
+
* @param deviceRef - Reference to a Device resource
|
|
390
|
+
* @returns The resolved Device resource or null
|
|
391
|
+
*/
|
|
392
|
+
getDeviceFromReference(deviceRef) {
|
|
393
|
+
if (!deviceRef) return null;
|
|
394
|
+
const device = this.resolveReference(deviceRef);
|
|
395
|
+
return device && device.resourceType === "Device" ? device : null;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Renders a Device name from a Device reference.
|
|
399
|
+
* Uses getDeviceFromReference to resolve the Device and returns a concatenated device.deviceName.
|
|
400
|
+
* Returns an empty string if no deviceName is available. The returned text is HTML-safe.
|
|
369
401
|
* @param deviceRef - Reference to a Device resource
|
|
370
|
-
* @returns
|
|
402
|
+
* @returns Sanitized device name or empty string
|
|
371
403
|
*/
|
|
372
404
|
renderDevice(deviceRef) {
|
|
373
|
-
const device =
|
|
374
|
-
if (device && device.
|
|
405
|
+
const device = this.getDeviceFromReference(deviceRef);
|
|
406
|
+
if (device && device.deviceName && device.deviceName.length > 0) {
|
|
375
407
|
return this.safeConcat(device.deviceName, "name");
|
|
376
408
|
}
|
|
377
409
|
return "";
|
|
@@ -2787,11 +2819,64 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
2787
2819
|
return _MedicalDevicesTemplate.generateStaticNarrative(resources, timezone);
|
|
2788
2820
|
}
|
|
2789
2821
|
/**
|
|
2790
|
-
*
|
|
2791
|
-
* @param resources - FHIR
|
|
2822
|
+
* Generate HTML narrative for history of medical devices using summary
|
|
2823
|
+
* @param resources - FHIR Composition resources
|
|
2792
2824
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
2793
2825
|
* @returns HTML string for rendering
|
|
2794
2826
|
*/
|
|
2827
|
+
generateSummaryNarrative(resources, timezone) {
|
|
2828
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
2829
|
+
let isSummaryCreated = false;
|
|
2830
|
+
let html = `<p>This list includes all information about the patient's medical devices history, sorted by recorded date (most recent first).</p>
|
|
2831
|
+
`;
|
|
2832
|
+
html += `
|
|
2833
|
+
<table>
|
|
2834
|
+
<thead>
|
|
2835
|
+
<tr>
|
|
2836
|
+
<th>Device</th>
|
|
2837
|
+
<th>Code (System)</th>
|
|
2838
|
+
<th>Status</th>
|
|
2839
|
+
<th>Comments</th>
|
|
2840
|
+
<th>Date Recorded</th>
|
|
2841
|
+
<th>Source</th>
|
|
2842
|
+
</tr>
|
|
2843
|
+
</thead>
|
|
2844
|
+
<tbody>`;
|
|
2845
|
+
for (const resourceItem of resources) {
|
|
2846
|
+
for (const rowData of resourceItem.section ?? []) {
|
|
2847
|
+
const sectionCodeableConcept = rowData.code;
|
|
2848
|
+
const data = {};
|
|
2849
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(sectionCodeableConcept);
|
|
2850
|
+
for (const columnData of rowData.section ?? []) {
|
|
2851
|
+
const columnTitle = columnData.title;
|
|
2852
|
+
if (columnTitle) {
|
|
2853
|
+
data[columnTitle] = templateUtilities.renderTextAsHtml(columnData.text?.div ?? "");
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
if (data["Device Name"]?.toLowerCase() === "unknown") {
|
|
2857
|
+
continue;
|
|
2858
|
+
}
|
|
2859
|
+
isSummaryCreated = true;
|
|
2860
|
+
html += `
|
|
2861
|
+
<tr>
|
|
2862
|
+
<td>${templateUtilities.capitalizeFirstLetter(data["Device Name"] ?? "")}</td>
|
|
2863
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
2864
|
+
<td>${templateUtilities.renderTextAsHtml(data["Status"] ?? "")}</td>
|
|
2865
|
+
<td>${templateUtilities.renderTextAsHtml(data["Notes"] ?? "")}</td>
|
|
2866
|
+
<td>${templateUtilities.renderTime(data["Recorded On"] ?? "", timezone)}</td>
|
|
2867
|
+
<td>${data["Source"] ?? ""}</td>
|
|
2868
|
+
</tr>`;
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
html += `
|
|
2872
|
+
</tbody>
|
|
2873
|
+
</table>`;
|
|
2874
|
+
return isSummaryCreated ? html : void 0;
|
|
2875
|
+
}
|
|
2876
|
+
/**
|
|
2877
|
+
* Internal static implementation that generates the narrative from DeviceUseStatement resources.
|
|
2878
|
+
* The “Code (System)” column renders Device.type as "code (SystemDisplay)" using TemplateUtilities.codeableConceptCoding.
|
|
2879
|
+
*/
|
|
2795
2880
|
static generateStaticNarrative(resources, timezone) {
|
|
2796
2881
|
const templateUtilities = new TemplateUtilities(resources);
|
|
2797
2882
|
let html = `<p>This list includes all DeviceUseStatement resources, sorted by recorded date (most recent first).</p>
|
|
@@ -2799,6 +2884,7 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
2799
2884
|
<thead>
|
|
2800
2885
|
<tr>
|
|
2801
2886
|
<th>Device</th>
|
|
2887
|
+
<th>Code (System)</th>
|
|
2802
2888
|
<th>Status</th>
|
|
2803
2889
|
<th>Comments</th>
|
|
2804
2890
|
<th>Date Recorded</th>
|
|
@@ -2811,26 +2897,32 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
|
|
|
2811
2897
|
const dateB = b.recordedOn;
|
|
2812
2898
|
return typeof dateA === "string" && typeof dateB === "string" ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
|
|
2813
2899
|
});
|
|
2814
|
-
|
|
2900
|
+
const devicesAdded = /* @__PURE__ */ new Set();
|
|
2815
2901
|
for (const dus of deviceStatements) {
|
|
2816
|
-
const deviceName = templateUtilities.
|
|
2817
|
-
if (deviceName?.toLowerCase() === "unknown") {
|
|
2902
|
+
const deviceName = templateUtilities.renderDevice(dus.device);
|
|
2903
|
+
if (deviceName?.toLowerCase() === "unknown" || devicesAdded.has(deviceName)) {
|
|
2904
|
+
continue;
|
|
2905
|
+
}
|
|
2906
|
+
const device = templateUtilities.getDeviceFromReference(dus.device);
|
|
2907
|
+
const codeSystem = templateUtilities.codeableConceptCoding(device?.type);
|
|
2908
|
+
if (!codeSystem) {
|
|
2818
2909
|
continue;
|
|
2819
2910
|
}
|
|
2820
|
-
|
|
2911
|
+
devicesAdded.add(deviceName);
|
|
2821
2912
|
html += `
|
|
2822
2913
|
<tr>
|
|
2823
2914
|
<td>${templateUtilities.capitalizeFirstLetter(deviceName)}</td>
|
|
2915
|
+
<td>${codeSystem}</td>
|
|
2824
2916
|
<td>${templateUtilities.renderTextAsHtml(dus.status || "")}</td>
|
|
2825
2917
|
<td>${templateUtilities.renderNotes(dus.note, timezone)}</td>
|
|
2826
|
-
<td>${templateUtilities.
|
|
2918
|
+
<td>${templateUtilities.renderTime(dus.recordedOn, timezone)}</td>
|
|
2827
2919
|
<td>${templateUtilities.getOwnerTag(dus)}</td>
|
|
2828
2920
|
</tr>`;
|
|
2829
2921
|
}
|
|
2830
2922
|
html += `
|
|
2831
2923
|
</tbody>
|
|
2832
2924
|
</table>`;
|
|
2833
|
-
return
|
|
2925
|
+
return devicesAdded.size > 0 ? html : void 0;
|
|
2834
2926
|
}
|
|
2835
2927
|
};
|
|
2836
2928
|
|
|
@@ -4571,7 +4663,78 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
4571
4663
|
const dateB = new Date(b.dateTime || 0);
|
|
4572
4664
|
return dateB.getTime() - dateA.getTime();
|
|
4573
4665
|
});
|
|
4574
|
-
return _AdvanceDirectivesTemplate.generateStaticNarrative(
|
|
4666
|
+
return _AdvanceDirectivesTemplate.generateStaticNarrative(
|
|
4667
|
+
resources,
|
|
4668
|
+
timezone
|
|
4669
|
+
);
|
|
4670
|
+
}
|
|
4671
|
+
/**
|
|
4672
|
+
* Generate HTML narrative for advance directives using summary
|
|
4673
|
+
* @param resources - FHIR Composition resources
|
|
4674
|
+
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
4675
|
+
* @returns HTML string for rendering
|
|
4676
|
+
*/
|
|
4677
|
+
generateSummaryNarrative(resources, timezone) {
|
|
4678
|
+
const templateUtilities = new TemplateUtilities(resources);
|
|
4679
|
+
let isSummaryCreated = false;
|
|
4680
|
+
let html = `<p>This list includes all Consent resources, sorted by date (most recent first).</p>
|
|
4681
|
+
`;
|
|
4682
|
+
html += `
|
|
4683
|
+
<div>
|
|
4684
|
+
<table>
|
|
4685
|
+
<thead>
|
|
4686
|
+
<tr>
|
|
4687
|
+
<th>Directive</th>
|
|
4688
|
+
<th>Code (System)</th>
|
|
4689
|
+
<th>Action Controlled</th>
|
|
4690
|
+
<th>Policy Rule</th>
|
|
4691
|
+
<th>Date</th>
|
|
4692
|
+
<th>Source</th>
|
|
4693
|
+
</tr>
|
|
4694
|
+
</thead>
|
|
4695
|
+
<tbody>`;
|
|
4696
|
+
for (const resourceItem of resources) {
|
|
4697
|
+
for (const rowData of resourceItem.section ?? []) {
|
|
4698
|
+
const sectionCodeableConcept = rowData.code;
|
|
4699
|
+
const data = {};
|
|
4700
|
+
data["codeSystem"] = templateUtilities.codeableConceptCoding(
|
|
4701
|
+
sectionCodeableConcept
|
|
4702
|
+
);
|
|
4703
|
+
for (const columnData of rowData.section ?? []) {
|
|
4704
|
+
const columnTitle = columnData.title;
|
|
4705
|
+
if (columnTitle) {
|
|
4706
|
+
if (columnTitle === "DateTime") {
|
|
4707
|
+
data[columnTitle] = templateUtilities.renderTime(
|
|
4708
|
+
columnData.text?.div ?? "",
|
|
4709
|
+
timezone
|
|
4710
|
+
);
|
|
4711
|
+
} else {
|
|
4712
|
+
data[columnTitle] = templateUtilities.renderTextAsHtml(
|
|
4713
|
+
columnData.text?.div ?? ""
|
|
4714
|
+
);
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
if (data["Advanced Directive Name"]?.toLowerCase() === "unknown") {
|
|
4719
|
+
continue;
|
|
4720
|
+
}
|
|
4721
|
+
isSummaryCreated = true;
|
|
4722
|
+
html += `
|
|
4723
|
+
<tr>
|
|
4724
|
+
<td>${templateUtilities.capitalizeFirstLetter(data["Advanced Directive Name"] ?? "")}</td>
|
|
4725
|
+
<td>${data["codeSystem"] ?? ""}</td>
|
|
4726
|
+
<td>${data["Provision Action"] ?? ""}</td>
|
|
4727
|
+
<td>${data["Policy Rule"] ?? ""}</td>
|
|
4728
|
+
<td>${data["DateTime"] ?? ""}</td>
|
|
4729
|
+
<td>${data["Source"] ?? ""}</td>
|
|
4730
|
+
</tr>`;
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
html += `
|
|
4734
|
+
</tbody>
|
|
4735
|
+
</table>
|
|
4736
|
+
</div>`;
|
|
4737
|
+
return isSummaryCreated ? html : void 0;
|
|
4575
4738
|
}
|
|
4576
4739
|
/**
|
|
4577
4740
|
* Internal static implementation that actually generates the narrative
|
|
@@ -4579,7 +4742,6 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
4579
4742
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
4580
4743
|
* @returns HTML string for rendering
|
|
4581
4744
|
*/
|
|
4582
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
4583
4745
|
static generateStaticNarrative(resources, timezone) {
|
|
4584
4746
|
const templateUtilities = new TemplateUtilities(resources);
|
|
4585
4747
|
let html = `<p>This list includes all Consent resources, sorted by date (most recent first).</p>
|
|
@@ -4587,35 +4749,57 @@ var AdvanceDirectivesTemplate = class _AdvanceDirectivesTemplate {
|
|
|
4587
4749
|
html += `<table>
|
|
4588
4750
|
<thead>
|
|
4589
4751
|
<tr>
|
|
4590
|
-
<th>
|
|
4591
|
-
<th>
|
|
4752
|
+
<th>Directive</th>
|
|
4753
|
+
<th>Code (System)</th>
|
|
4592
4754
|
<th>Action Controlled</th>
|
|
4755
|
+
<th>Policy Rule</th>
|
|
4593
4756
|
<th>Date</th>
|
|
4594
4757
|
<th>Source</th>
|
|
4595
4758
|
</tr>
|
|
4596
4759
|
</thead>
|
|
4597
4760
|
<tbody>`;
|
|
4598
|
-
|
|
4761
|
+
const consentAdded = /* @__PURE__ */ new Set();
|
|
4599
4762
|
for (const resourceItem of resources) {
|
|
4600
4763
|
const consent = resourceItem;
|
|
4601
|
-
|
|
4602
|
-
|
|
4764
|
+
let consentDirective = {};
|
|
4765
|
+
for (const category of consent.category || []) {
|
|
4766
|
+
for (const coding of category.coding || []) {
|
|
4767
|
+
if (coding.system === ADVANCED_DIRECTIVE_CATEGORY_SYSTEM && coding.code && coding.code in ADVANCED_DIRECTIVE_CATEGORY_CODES) {
|
|
4768
|
+
consentDirective = {
|
|
4769
|
+
name: ADVANCED_DIRECTIVE_CATEGORY_CODES[coding.code],
|
|
4770
|
+
system: coding.system,
|
|
4771
|
+
code: coding.code
|
|
4772
|
+
};
|
|
4773
|
+
break;
|
|
4774
|
+
} else if (coding.system === LOINC_SYSTEM && coding.code && coding.code in ADVANCED_DIRECTIVE_LOINC_CODES) {
|
|
4775
|
+
consentDirective = {
|
|
4776
|
+
name: ADVANCED_DIRECTIVE_LOINC_CODES[coding.code],
|
|
4777
|
+
system: coding.system,
|
|
4778
|
+
code: coding.code
|
|
4779
|
+
};
|
|
4780
|
+
break;
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
}
|
|
4784
|
+
if (!consentDirective.name || !consentDirective.code || !consentDirective.system || consentDirective.name.toLowerCase() === "unknown" || consentAdded.has(consentDirective.name)) {
|
|
4603
4785
|
continue;
|
|
4604
4786
|
}
|
|
4605
|
-
|
|
4787
|
+
consentAdded.add(consentDirective.name);
|
|
4788
|
+
const consentCodeSystem = `${consentDirective.code} (${codingSystemDisplayNames_default[consentDirective.system] || consentDirective.system})`;
|
|
4606
4789
|
html += `
|
|
4607
4790
|
<tr>
|
|
4608
|
-
<td>${
|
|
4609
|
-
<td>${
|
|
4791
|
+
<td>${consentDirective.name}</td>
|
|
4792
|
+
<td>${consentCodeSystem || ""}</td>
|
|
4610
4793
|
<td>${consent.provision?.action ? templateUtilities.concatCodeableConcept(consent.provision.action) : ""}</td>
|
|
4611
|
-
<td>${consent.
|
|
4794
|
+
<td>${consent.policyRule ? templateUtilities.formatCodeableConceptValue(consent.policyRule) : ""}</td>
|
|
4795
|
+
<td>${templateUtilities.renderTime(consent.dateTime, timezone) || ""}</td>
|
|
4612
4796
|
<td>${templateUtilities.getOwnerTag(consent)}</td>
|
|
4613
4797
|
</tr>`;
|
|
4614
4798
|
}
|
|
4615
4799
|
html += `
|
|
4616
4800
|
</tbody>
|
|
4617
4801
|
</table>`;
|
|
4618
|
-
return
|
|
4802
|
+
return consentAdded.size > 0 ? html : void 0;
|
|
4619
4803
|
}
|
|
4620
4804
|
};
|
|
4621
4805
|
|