@imranq2/fhirpatientsummary 1.0.37 → 1.0.38
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 +199 -76
- package/dist/index.js +199 -76
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -138,6 +138,67 @@ var ESSENTIAL_LAB_PANELS = {
|
|
|
138
138
|
"80235-8": "Cardiac Markers Panel",
|
|
139
139
|
"69738-3": "CBC with Auto Differential"
|
|
140
140
|
};
|
|
141
|
+
var FUNCTIONAL_STATUS_SNOMED_CODES = {
|
|
142
|
+
"118233009": "Core Functional Status",
|
|
143
|
+
"364644000": "Core Functional Status",
|
|
144
|
+
"160245001": "Core Functional Status",
|
|
145
|
+
"21134002": "Disability & Impairment",
|
|
146
|
+
"2655002": "Disability & Impairment",
|
|
147
|
+
"2219003": "Disability & Impairment",
|
|
148
|
+
"962000": "Disability & Impairment",
|
|
149
|
+
"3089009": "Disability & Impairment",
|
|
150
|
+
"4197006": "Disability & Impairment",
|
|
151
|
+
"284545001": "Activities of Daily Living",
|
|
152
|
+
"719930002": "Activities of Daily Living",
|
|
153
|
+
"301438001": "Activities of Daily Living",
|
|
154
|
+
"282145008": "Mobility & Movement",
|
|
155
|
+
"165245003": "Mobility & Movement",
|
|
156
|
+
"713458007": "Mobility & Movement"
|
|
157
|
+
};
|
|
158
|
+
var FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES = {
|
|
159
|
+
"54522-8": "Functional Status Panel",
|
|
160
|
+
"54523-6": "ADL Assistance",
|
|
161
|
+
"45588-1": "Bed Mobility",
|
|
162
|
+
"45589-9": "Bed Mobility",
|
|
163
|
+
"45590-7": "Transfer",
|
|
164
|
+
"45591-5": "Transfer",
|
|
165
|
+
"45592-3": "Walk in room",
|
|
166
|
+
"45593-1": "Walk in room",
|
|
167
|
+
"45594-9": "Walk in corridor",
|
|
168
|
+
"45595-6": "Walk in corridor",
|
|
169
|
+
"45596-4": "Locomotion on unit",
|
|
170
|
+
"45597-2": "Locomotion on unit",
|
|
171
|
+
"45598-0": "Locomotion off unit",
|
|
172
|
+
"45599-8": "Locomotion off unit",
|
|
173
|
+
"45600-4": "Dressing",
|
|
174
|
+
"45601-2": "Dressing",
|
|
175
|
+
"45602-0": "Eating",
|
|
176
|
+
"45603-8": "Eating",
|
|
177
|
+
"45604-6": "Toilet use",
|
|
178
|
+
"45605-3": "Toilet use",
|
|
179
|
+
"45606-1": "Personal hygiene",
|
|
180
|
+
"45607-9": "Personal hygiene",
|
|
181
|
+
"46008-9": "Bathing",
|
|
182
|
+
"45608-7": "Bathing",
|
|
183
|
+
"45609-5": "Bathing",
|
|
184
|
+
"54524-4": "Balance during Transitions and Walking",
|
|
185
|
+
"54749-7": "Moving from Seated to Standing",
|
|
186
|
+
"54750-5": "Walking with Assistive Device",
|
|
187
|
+
"54751-3": "Turning Around While Walking",
|
|
188
|
+
"54753-9": "Surface-to-Surface Transfer",
|
|
189
|
+
"54525-1": "Functional Limitation in Range of Motion",
|
|
190
|
+
"54754-7": "Upper Extremity ROM",
|
|
191
|
+
"54755-4": "Lower Extremity ROM",
|
|
192
|
+
"54526-9": "Mobility Devices",
|
|
193
|
+
"54756-2": "Cane/Crutch",
|
|
194
|
+
"54757-0": "Walker",
|
|
195
|
+
"54758-8": "Wheelchair",
|
|
196
|
+
"54759-6": "Limb Prosthesis",
|
|
197
|
+
"54760-4": "No Mobility Devices",
|
|
198
|
+
"54527-7": "Functional Rehabilitation Potential",
|
|
199
|
+
"55123-4": "Resident Believes Capable of Increased Independence",
|
|
200
|
+
"45613-7": "Staff Believes Capable of Increased Independence"
|
|
201
|
+
};
|
|
141
202
|
|
|
142
203
|
// src/structures/ips_section_constants.ts
|
|
143
204
|
var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
|
|
@@ -173,11 +234,9 @@ var IPSSectionResourceFilters = {
|
|
|
173
234
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && codeableConceptMatches(resource.code, Object.keys(SOCIAL_HISTORY_LOINC_CODES), "http://loinc.org"),
|
|
174
235
|
// Only include pregnancy history Observations or relevant Conditions
|
|
175
236
|
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS), "http://loinc.org") || codeableConceptMatches(resource.valueCodeableConcept, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME), "http://loinc.org") || codingMatches(resource.code?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct") || codingMatches(resource.valueCodeableConcept?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct")) || resource.resourceType === "Condition" && (codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS), "http://loinc.org") || codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME), "http://loinc.org") || codingMatches(resource.code?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct")),
|
|
176
|
-
// Only include
|
|
177
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "
|
|
178
|
-
(cat) => cat.coding?.some(
|
|
179
|
-
(c) => c.code === "functional-status" && c.system === "http://terminology.hl7.org/CodeSystem/observation-category" || typeof c.display === "string" && c.display.toLowerCase().includes("functional")
|
|
180
|
-
)
|
|
237
|
+
// Only include Condition with Functional Status LOINC and SNOMED codes, category code 'problem-list-item', and completed ClinicalImpressions
|
|
238
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && ((codeableConceptMatches(resource.code, Object.keys(FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES), "http://loinc.org") || codeableConceptMatches(resource.code, Object.keys(FUNCTIONAL_STATUS_SNOMED_CODES), "http://snomed.info/sct")) && resource.clinicalStatus?.coding?.some((c) => c.code === "active") && resource.category?.some(
|
|
239
|
+
(cat) => cat.coding?.some((c) => c.code === "problem-list-item")
|
|
181
240
|
)) || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
|
|
182
241
|
// Only include resolved medical history Conditions
|
|
183
242
|
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
|
|
@@ -4103,7 +4162,7 @@ var PlanOfCareTemplate = class {
|
|
|
4103
4162
|
var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
4104
4163
|
/**
|
|
4105
4164
|
* Generate HTML narrative for Functional Status
|
|
4106
|
-
* @param resources - FHIR resources array containing
|
|
4165
|
+
* @param resources - FHIR resources array containing Condition and ClinicalImpression resources
|
|
4107
4166
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
4108
4167
|
* @returns HTML string for rendering
|
|
4109
4168
|
*/
|
|
@@ -4143,8 +4202,8 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
4143
4202
|
<thead>
|
|
4144
4203
|
<tr>
|
|
4145
4204
|
<th>Name</th>
|
|
4146
|
-
<th>Date</th>
|
|
4147
4205
|
<th>Code (System)</th>
|
|
4206
|
+
<th>Date</th>
|
|
4148
4207
|
<th>Description</th>
|
|
4149
4208
|
<th>Summary</th>
|
|
4150
4209
|
<th>Source</th>
|
|
@@ -4210,8 +4269,8 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
4210
4269
|
clinicalImpressionsHtml += `
|
|
4211
4270
|
<tr>
|
|
4212
4271
|
<td>${templateUtilities.capitalizeFirstLetter(name)}</td>
|
|
4213
|
-
<td>${date ?? ""}</td>
|
|
4214
4272
|
<td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
|
|
4273
|
+
<td>${date ?? ""}</td>
|
|
4215
4274
|
<td>${data["Description"] ?? ""}</td>
|
|
4216
4275
|
<td>${data["Summary"] ?? ""}</td>
|
|
4217
4276
|
<td>${data["Source"] ?? ""}</td>
|
|
@@ -4240,92 +4299,156 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
4240
4299
|
}
|
|
4241
4300
|
/**
|
|
4242
4301
|
* Internal static implementation that actually generates the narrative
|
|
4243
|
-
* @param resources - FHIR resources array containing
|
|
4302
|
+
* @param resources - FHIR resources array containing Condition and ClinicalImpression resources
|
|
4244
4303
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
4245
4304
|
* @returns HTML string for rendering
|
|
4246
4305
|
*/
|
|
4247
4306
|
static generateStaticNarrative(resources, timezone) {
|
|
4248
4307
|
const templateUtilities = new TemplateUtilities(resources);
|
|
4249
|
-
let html = `<
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
let
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4308
|
+
let html = `<div>
|
|
4309
|
+
<p>This section summarizes key conditions and assessments related to the person's functional status and ability to perform daily activities.</p>`;
|
|
4310
|
+
let conditionHtml = `
|
|
4311
|
+
<div>
|
|
4312
|
+
<h3>Conditions</h3>
|
|
4313
|
+
<table>
|
|
4314
|
+
<thead>
|
|
4315
|
+
<tr>
|
|
4316
|
+
<th>Problem</th>
|
|
4317
|
+
<th>Code (System)</th>
|
|
4318
|
+
<th>Onset Date</th>
|
|
4319
|
+
<th>Recorded Date</th>
|
|
4320
|
+
<th>Source</th>
|
|
4321
|
+
</tr>
|
|
4322
|
+
</thead>
|
|
4323
|
+
<tbody>`;
|
|
4324
|
+
let clinicalImpressionsHtml = `
|
|
4325
|
+
<div>
|
|
4326
|
+
<h3>Clinical Impressions</h3>
|
|
4327
|
+
<table>
|
|
4328
|
+
<thead>
|
|
4329
|
+
<tr>
|
|
4330
|
+
<th>Name</th>
|
|
4331
|
+
<th>Code (System)</th>
|
|
4332
|
+
<th>Date</th>
|
|
4333
|
+
<th>Description</th>
|
|
4334
|
+
<th>Summary</th>
|
|
4335
|
+
<th>Source</th>
|
|
4336
|
+
</tr>
|
|
4337
|
+
</thead>
|
|
4338
|
+
<tbody>`;
|
|
4339
|
+
const conditions = resources.filter((entry) => entry.resourceType === "Condition").map((entry) => entry);
|
|
4340
|
+
conditions.sort((a, b) => {
|
|
4341
|
+
const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
|
|
4342
|
+
const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
|
|
4343
|
+
return dateB - dateA;
|
|
4269
4344
|
});
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4345
|
+
const addedConditions = /* @__PURE__ */ new Set();
|
|
4346
|
+
for (const cond of conditions) {
|
|
4347
|
+
const functionalStatusName = this.getFunctionalStatusNameFromCode(cond.code);
|
|
4348
|
+
const problem = templateUtilities.codeableConceptDisplay(cond.code) || functionalStatusName;
|
|
4349
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(cond.code);
|
|
4350
|
+
if (!codeAndSystem || !problem || !functionalStatusName || addedConditions.has(functionalStatusName)) {
|
|
4351
|
+
continue;
|
|
4352
|
+
}
|
|
4353
|
+
if (problem?.toLowerCase() === "unknown") {
|
|
4354
|
+
continue;
|
|
4355
|
+
}
|
|
4356
|
+
addedConditions.add(functionalStatusName);
|
|
4357
|
+
let date = cond.onsetDateTime ? templateUtilities.renderTime(cond.onsetDateTime, timezone) : "";
|
|
4358
|
+
if (!date && cond.onsetPeriod?.start) {
|
|
4359
|
+
date = templateUtilities.renderTime(
|
|
4360
|
+
cond.onsetPeriod?.start,
|
|
4361
|
+
timezone
|
|
4362
|
+
);
|
|
4363
|
+
if (cond.onsetPeriod?.end) {
|
|
4364
|
+
date += " - " + templateUtilities.renderTime(cond.onsetPeriod?.end, timezone);
|
|
4277
4365
|
}
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
html += `<tr>
|
|
4283
|
-
<td>${templateUtilities.capitalizeFirstLetter(obsName)}</td>
|
|
4284
|
-
<td>${value ?? ""}</td>
|
|
4366
|
+
}
|
|
4367
|
+
conditionHtml += `<tr>
|
|
4368
|
+
<td>${templateUtilities.capitalizeFirstLetter(problem)}</td>
|
|
4369
|
+
<td>${codeAndSystem}</td>
|
|
4285
4370
|
<td>${date}</td>
|
|
4286
|
-
<td>${
|
|
4287
|
-
<td>${
|
|
4288
|
-
<td>${templateUtilities.getOwnerTag(observation)}</td>
|
|
4371
|
+
<td>${templateUtilities.renderTime(cond.recordedDate, timezone)}</td>
|
|
4372
|
+
<td>${templateUtilities.getOwnerTag(cond)}</td>
|
|
4289
4373
|
</tr>`;
|
|
4374
|
+
}
|
|
4375
|
+
const clinicalImpressions = resources.filter((entry) => entry.resourceType === "ClinicalImpression").map((entry) => entry);
|
|
4376
|
+
clinicalImpressions.sort((a, b) => {
|
|
4377
|
+
const dateA = this.getClinicalImpressionEffectiveDate(a);
|
|
4378
|
+
const dateB = this.getClinicalImpressionEffectiveDate(b);
|
|
4379
|
+
return dateB && dateA ? dateB.getTime() - dateA.getTime() : 0;
|
|
4380
|
+
});
|
|
4381
|
+
const addedClinicalImpressions = /* @__PURE__ */ new Set();
|
|
4382
|
+
for (const impression of clinicalImpressions) {
|
|
4383
|
+
const name = templateUtilities.codeableConceptDisplay(impression.code);
|
|
4384
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(impression.code);
|
|
4385
|
+
if (!codeAndSystem || addedClinicalImpressions.has(name)) {
|
|
4386
|
+
continue;
|
|
4290
4387
|
}
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
}
|
|
4304
|
-
let findingsHtml = "";
|
|
4305
|
-
if (impression.finding && impression.finding.length > 0) {
|
|
4306
|
-
findingsHtml = "<ul>";
|
|
4307
|
-
for (const finding of impression.finding) {
|
|
4308
|
-
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConceptDisplay(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
4309
|
-
const cause = finding.basis || "";
|
|
4310
|
-
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
4311
|
-
}
|
|
4312
|
-
findingsHtml += "</ul>";
|
|
4388
|
+
if (!name || name?.toLowerCase() === "unknown") {
|
|
4389
|
+
continue;
|
|
4390
|
+
}
|
|
4391
|
+
addedClinicalImpressions.add(name);
|
|
4392
|
+
let date = impression.effectiveDateTime ? templateUtilities.renderTime(impression.effectiveDateTime, timezone) : "";
|
|
4393
|
+
if (!date && impression.effectivePeriod?.start) {
|
|
4394
|
+
date = templateUtilities.renderTime(
|
|
4395
|
+
impression.effectivePeriod?.start,
|
|
4396
|
+
timezone
|
|
4397
|
+
);
|
|
4398
|
+
if (impression.effectivePeriod?.end) {
|
|
4399
|
+
date += " - " + templateUtilities.renderTime(impression.effectivePeriod?.end, timezone);
|
|
4313
4400
|
}
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
<td>${
|
|
4401
|
+
}
|
|
4402
|
+
clinicalImpressionsHtml += `<tr>
|
|
4403
|
+
<td>${templateUtilities.capitalizeFirstLetter(name)}</td>
|
|
4404
|
+
<td>${codeAndSystem}</td>
|
|
4405
|
+
<td>${date}</td>
|
|
4317
4406
|
<td>${impression.description || ""}</td>
|
|
4318
4407
|
<td>${impression.summary || ""}</td>
|
|
4319
|
-
<td>${findingsHtml}</td>
|
|
4320
4408
|
<td>${templateUtilities.getOwnerTag(impression)}</td>
|
|
4321
4409
|
</tr>`;
|
|
4410
|
+
}
|
|
4411
|
+
if (addedConditions.size > 0) {
|
|
4412
|
+
html += conditionHtml;
|
|
4413
|
+
html += `
|
|
4414
|
+
</tbody>
|
|
4415
|
+
</table>
|
|
4416
|
+
</div>`;
|
|
4417
|
+
}
|
|
4418
|
+
if (addedClinicalImpressions.size > 0) {
|
|
4419
|
+
html += clinicalImpressionsHtml;
|
|
4420
|
+
html += `
|
|
4421
|
+
</tbody>
|
|
4422
|
+
</table>
|
|
4423
|
+
</div>`;
|
|
4424
|
+
}
|
|
4425
|
+
html += `
|
|
4426
|
+
</div>`;
|
|
4427
|
+
return addedConditions.size > 0 || addedClinicalImpressions.size > 0 ? html : void 0;
|
|
4428
|
+
}
|
|
4429
|
+
static getFunctionalStatusNameFromCode(cc) {
|
|
4430
|
+
if (!cc) return "";
|
|
4431
|
+
for (const coding of cc.coding || []) {
|
|
4432
|
+
let functionalStatusName = FUNCTIONAL_STATUS_SNOMED_CODES[coding.code];
|
|
4433
|
+
if (functionalStatusName) {
|
|
4434
|
+
return functionalStatusName;
|
|
4435
|
+
}
|
|
4436
|
+
functionalStatusName = FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES[coding.code];
|
|
4437
|
+
if (functionalStatusName) {
|
|
4438
|
+
return functionalStatusName;
|
|
4322
4439
|
}
|
|
4323
|
-
html += `</tbody></table>`;
|
|
4324
4440
|
}
|
|
4325
|
-
|
|
4326
|
-
|
|
4441
|
+
}
|
|
4442
|
+
static getClinicalImpressionEffectiveDate(impression) {
|
|
4443
|
+
if (impression.effectiveDateTime) {
|
|
4444
|
+
return new Date(impression.effectiveDateTime);
|
|
4445
|
+
} else if (impression.effectivePeriod) {
|
|
4446
|
+
if (impression.effectivePeriod.start) {
|
|
4447
|
+
return new Date(impression.effectivePeriod.start);
|
|
4448
|
+
} else if (impression.effectivePeriod.end) {
|
|
4449
|
+
return new Date(impression.effectivePeriod.end);
|
|
4450
|
+
}
|
|
4327
4451
|
}
|
|
4328
|
-
return html;
|
|
4329
4452
|
}
|
|
4330
4453
|
};
|
|
4331
4454
|
|
package/dist/index.js
CHANGED
|
@@ -110,6 +110,67 @@ var ESSENTIAL_LAB_PANELS = {
|
|
|
110
110
|
"80235-8": "Cardiac Markers Panel",
|
|
111
111
|
"69738-3": "CBC with Auto Differential"
|
|
112
112
|
};
|
|
113
|
+
var FUNCTIONAL_STATUS_SNOMED_CODES = {
|
|
114
|
+
"118233009": "Core Functional Status",
|
|
115
|
+
"364644000": "Core Functional Status",
|
|
116
|
+
"160245001": "Core Functional Status",
|
|
117
|
+
"21134002": "Disability & Impairment",
|
|
118
|
+
"2655002": "Disability & Impairment",
|
|
119
|
+
"2219003": "Disability & Impairment",
|
|
120
|
+
"962000": "Disability & Impairment",
|
|
121
|
+
"3089009": "Disability & Impairment",
|
|
122
|
+
"4197006": "Disability & Impairment",
|
|
123
|
+
"284545001": "Activities of Daily Living",
|
|
124
|
+
"719930002": "Activities of Daily Living",
|
|
125
|
+
"301438001": "Activities of Daily Living",
|
|
126
|
+
"282145008": "Mobility & Movement",
|
|
127
|
+
"165245003": "Mobility & Movement",
|
|
128
|
+
"713458007": "Mobility & Movement"
|
|
129
|
+
};
|
|
130
|
+
var FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES = {
|
|
131
|
+
"54522-8": "Functional Status Panel",
|
|
132
|
+
"54523-6": "ADL Assistance",
|
|
133
|
+
"45588-1": "Bed Mobility",
|
|
134
|
+
"45589-9": "Bed Mobility",
|
|
135
|
+
"45590-7": "Transfer",
|
|
136
|
+
"45591-5": "Transfer",
|
|
137
|
+
"45592-3": "Walk in room",
|
|
138
|
+
"45593-1": "Walk in room",
|
|
139
|
+
"45594-9": "Walk in corridor",
|
|
140
|
+
"45595-6": "Walk in corridor",
|
|
141
|
+
"45596-4": "Locomotion on unit",
|
|
142
|
+
"45597-2": "Locomotion on unit",
|
|
143
|
+
"45598-0": "Locomotion off unit",
|
|
144
|
+
"45599-8": "Locomotion off unit",
|
|
145
|
+
"45600-4": "Dressing",
|
|
146
|
+
"45601-2": "Dressing",
|
|
147
|
+
"45602-0": "Eating",
|
|
148
|
+
"45603-8": "Eating",
|
|
149
|
+
"45604-6": "Toilet use",
|
|
150
|
+
"45605-3": "Toilet use",
|
|
151
|
+
"45606-1": "Personal hygiene",
|
|
152
|
+
"45607-9": "Personal hygiene",
|
|
153
|
+
"46008-9": "Bathing",
|
|
154
|
+
"45608-7": "Bathing",
|
|
155
|
+
"45609-5": "Bathing",
|
|
156
|
+
"54524-4": "Balance during Transitions and Walking",
|
|
157
|
+
"54749-7": "Moving from Seated to Standing",
|
|
158
|
+
"54750-5": "Walking with Assistive Device",
|
|
159
|
+
"54751-3": "Turning Around While Walking",
|
|
160
|
+
"54753-9": "Surface-to-Surface Transfer",
|
|
161
|
+
"54525-1": "Functional Limitation in Range of Motion",
|
|
162
|
+
"54754-7": "Upper Extremity ROM",
|
|
163
|
+
"54755-4": "Lower Extremity ROM",
|
|
164
|
+
"54526-9": "Mobility Devices",
|
|
165
|
+
"54756-2": "Cane/Crutch",
|
|
166
|
+
"54757-0": "Walker",
|
|
167
|
+
"54758-8": "Wheelchair",
|
|
168
|
+
"54759-6": "Limb Prosthesis",
|
|
169
|
+
"54760-4": "No Mobility Devices",
|
|
170
|
+
"54527-7": "Functional Rehabilitation Potential",
|
|
171
|
+
"55123-4": "Resident Believes Capable of Increased Independence",
|
|
172
|
+
"45613-7": "Staff Believes Capable of Increased Independence"
|
|
173
|
+
};
|
|
113
174
|
|
|
114
175
|
// src/structures/ips_section_constants.ts
|
|
115
176
|
var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
|
|
@@ -145,11 +206,9 @@ var IPSSectionResourceFilters = {
|
|
|
145
206
|
["SocialHistorySection" /* SOCIAL_HISTORY */]: (resource) => resource.resourceType === "Observation" && codeableConceptMatches(resource.code, Object.keys(SOCIAL_HISTORY_LOINC_CODES), "http://loinc.org"),
|
|
146
207
|
// Only include pregnancy history Observations or relevant Conditions
|
|
147
208
|
["HistoryOfPregnancySection" /* PREGNANCY_HISTORY */]: (resource) => resource.resourceType === "Observation" && (codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS), "http://loinc.org") || codeableConceptMatches(resource.valueCodeableConcept, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME), "http://loinc.org") || codingMatches(resource.code?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct") || codingMatches(resource.valueCodeableConcept?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct")) || resource.resourceType === "Condition" && (codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_STATUS), "http://loinc.org") || codeableConceptMatches(resource.code, Object.keys(PREGNANCY_LOINC_CODES.PREGNANCY_OUTCOME), "http://loinc.org") || codingMatches(resource.code?.coding?.[0], PREGNANCY_SNOMED_CODES, "http://snomed.info/sct")),
|
|
148
|
-
// Only include
|
|
149
|
-
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "
|
|
150
|
-
(cat) => cat.coding?.some(
|
|
151
|
-
(c) => c.code === "functional-status" && c.system === "http://terminology.hl7.org/CodeSystem/observation-category" || typeof c.display === "string" && c.display.toLowerCase().includes("functional")
|
|
152
|
-
)
|
|
209
|
+
// Only include Condition with Functional Status LOINC and SNOMED codes, category code 'problem-list-item', and completed ClinicalImpressions
|
|
210
|
+
["FunctionalStatusSection" /* FUNCTIONAL_STATUS */]: (resource) => resource.resourceType === "Condition" && ((codeableConceptMatches(resource.code, Object.keys(FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES), "http://loinc.org") || codeableConceptMatches(resource.code, Object.keys(FUNCTIONAL_STATUS_SNOMED_CODES), "http://snomed.info/sct")) && resource.clinicalStatus?.coding?.some((c) => c.code === "active") && resource.category?.some(
|
|
211
|
+
(cat) => cat.coding?.some((c) => c.code === "problem-list-item")
|
|
153
212
|
)) || resource.resourceType === "ClinicalImpression" && resource.status === "completed",
|
|
154
213
|
// Only include resolved medical history Conditions
|
|
155
214
|
["HistoryOfPastIllnessSection" /* MEDICAL_HISTORY */]: (resource) => resource.resourceType === "Condition" && resource.clinicalStatus?.coding?.some((c) => ["inactive", "resolved"].includes(c.code)),
|
|
@@ -4075,7 +4134,7 @@ var PlanOfCareTemplate = class {
|
|
|
4075
4134
|
var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
4076
4135
|
/**
|
|
4077
4136
|
* Generate HTML narrative for Functional Status
|
|
4078
|
-
* @param resources - FHIR resources array containing
|
|
4137
|
+
* @param resources - FHIR resources array containing Condition and ClinicalImpression resources
|
|
4079
4138
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
4080
4139
|
* @returns HTML string for rendering
|
|
4081
4140
|
*/
|
|
@@ -4115,8 +4174,8 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
4115
4174
|
<thead>
|
|
4116
4175
|
<tr>
|
|
4117
4176
|
<th>Name</th>
|
|
4118
|
-
<th>Date</th>
|
|
4119
4177
|
<th>Code (System)</th>
|
|
4178
|
+
<th>Date</th>
|
|
4120
4179
|
<th>Description</th>
|
|
4121
4180
|
<th>Summary</th>
|
|
4122
4181
|
<th>Source</th>
|
|
@@ -4182,8 +4241,8 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
4182
4241
|
clinicalImpressionsHtml += `
|
|
4183
4242
|
<tr>
|
|
4184
4243
|
<td>${templateUtilities.capitalizeFirstLetter(name)}</td>
|
|
4185
|
-
<td>${date ?? ""}</td>
|
|
4186
4244
|
<td>${templateUtilities.codeableConceptCoding(sectionCodeableConcept)}</td>
|
|
4245
|
+
<td>${date ?? ""}</td>
|
|
4187
4246
|
<td>${data["Description"] ?? ""}</td>
|
|
4188
4247
|
<td>${data["Summary"] ?? ""}</td>
|
|
4189
4248
|
<td>${data["Source"] ?? ""}</td>
|
|
@@ -4212,92 +4271,156 @@ var FunctionalStatusTemplate = class _FunctionalStatusTemplate {
|
|
|
4212
4271
|
}
|
|
4213
4272
|
/**
|
|
4214
4273
|
* Internal static implementation that actually generates the narrative
|
|
4215
|
-
* @param resources - FHIR resources array containing
|
|
4274
|
+
* @param resources - FHIR resources array containing Condition and ClinicalImpression resources
|
|
4216
4275
|
* @param timezone - Optional timezone to use for date formatting (e.g., 'America/New_York', 'Europe/London')
|
|
4217
4276
|
* @returns HTML string for rendering
|
|
4218
4277
|
*/
|
|
4219
4278
|
static generateStaticNarrative(resources, timezone) {
|
|
4220
4279
|
const templateUtilities = new TemplateUtilities(resources);
|
|
4221
|
-
let html = `<
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
let
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4280
|
+
let html = `<div>
|
|
4281
|
+
<p>This section summarizes key conditions and assessments related to the person's functional status and ability to perform daily activities.</p>`;
|
|
4282
|
+
let conditionHtml = `
|
|
4283
|
+
<div>
|
|
4284
|
+
<h3>Conditions</h3>
|
|
4285
|
+
<table>
|
|
4286
|
+
<thead>
|
|
4287
|
+
<tr>
|
|
4288
|
+
<th>Problem</th>
|
|
4289
|
+
<th>Code (System)</th>
|
|
4290
|
+
<th>Onset Date</th>
|
|
4291
|
+
<th>Recorded Date</th>
|
|
4292
|
+
<th>Source</th>
|
|
4293
|
+
</tr>
|
|
4294
|
+
</thead>
|
|
4295
|
+
<tbody>`;
|
|
4296
|
+
let clinicalImpressionsHtml = `
|
|
4297
|
+
<div>
|
|
4298
|
+
<h3>Clinical Impressions</h3>
|
|
4299
|
+
<table>
|
|
4300
|
+
<thead>
|
|
4301
|
+
<tr>
|
|
4302
|
+
<th>Name</th>
|
|
4303
|
+
<th>Code (System)</th>
|
|
4304
|
+
<th>Date</th>
|
|
4305
|
+
<th>Description</th>
|
|
4306
|
+
<th>Summary</th>
|
|
4307
|
+
<th>Source</th>
|
|
4308
|
+
</tr>
|
|
4309
|
+
</thead>
|
|
4310
|
+
<tbody>`;
|
|
4311
|
+
const conditions = resources.filter((entry) => entry.resourceType === "Condition").map((entry) => entry);
|
|
4312
|
+
conditions.sort((a, b) => {
|
|
4313
|
+
const dateA = a.recordedDate ? new Date(a.recordedDate).getTime() : 0;
|
|
4314
|
+
const dateB = b.recordedDate ? new Date(b.recordedDate).getTime() : 0;
|
|
4315
|
+
return dateB - dateA;
|
|
4241
4316
|
});
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4317
|
+
const addedConditions = /* @__PURE__ */ new Set();
|
|
4318
|
+
for (const cond of conditions) {
|
|
4319
|
+
const functionalStatusName = this.getFunctionalStatusNameFromCode(cond.code);
|
|
4320
|
+
const problem = templateUtilities.codeableConceptDisplay(cond.code) || functionalStatusName;
|
|
4321
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(cond.code);
|
|
4322
|
+
if (!codeAndSystem || !problem || !functionalStatusName || addedConditions.has(functionalStatusName)) {
|
|
4323
|
+
continue;
|
|
4324
|
+
}
|
|
4325
|
+
if (problem?.toLowerCase() === "unknown") {
|
|
4326
|
+
continue;
|
|
4327
|
+
}
|
|
4328
|
+
addedConditions.add(functionalStatusName);
|
|
4329
|
+
let date = cond.onsetDateTime ? templateUtilities.renderTime(cond.onsetDateTime, timezone) : "";
|
|
4330
|
+
if (!date && cond.onsetPeriod?.start) {
|
|
4331
|
+
date = templateUtilities.renderTime(
|
|
4332
|
+
cond.onsetPeriod?.start,
|
|
4333
|
+
timezone
|
|
4334
|
+
);
|
|
4335
|
+
if (cond.onsetPeriod?.end) {
|
|
4336
|
+
date += " - " + templateUtilities.renderTime(cond.onsetPeriod?.end, timezone);
|
|
4249
4337
|
}
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
html += `<tr>
|
|
4255
|
-
<td>${templateUtilities.capitalizeFirstLetter(obsName)}</td>
|
|
4256
|
-
<td>${value ?? ""}</td>
|
|
4338
|
+
}
|
|
4339
|
+
conditionHtml += `<tr>
|
|
4340
|
+
<td>${templateUtilities.capitalizeFirstLetter(problem)}</td>
|
|
4341
|
+
<td>${codeAndSystem}</td>
|
|
4257
4342
|
<td>${date}</td>
|
|
4258
|
-
<td>${
|
|
4259
|
-
<td>${
|
|
4260
|
-
<td>${templateUtilities.getOwnerTag(observation)}</td>
|
|
4343
|
+
<td>${templateUtilities.renderTime(cond.recordedDate, timezone)}</td>
|
|
4344
|
+
<td>${templateUtilities.getOwnerTag(cond)}</td>
|
|
4261
4345
|
</tr>`;
|
|
4346
|
+
}
|
|
4347
|
+
const clinicalImpressions = resources.filter((entry) => entry.resourceType === "ClinicalImpression").map((entry) => entry);
|
|
4348
|
+
clinicalImpressions.sort((a, b) => {
|
|
4349
|
+
const dateA = this.getClinicalImpressionEffectiveDate(a);
|
|
4350
|
+
const dateB = this.getClinicalImpressionEffectiveDate(b);
|
|
4351
|
+
return dateB && dateA ? dateB.getTime() - dateA.getTime() : 0;
|
|
4352
|
+
});
|
|
4353
|
+
const addedClinicalImpressions = /* @__PURE__ */ new Set();
|
|
4354
|
+
for (const impression of clinicalImpressions) {
|
|
4355
|
+
const name = templateUtilities.codeableConceptDisplay(impression.code);
|
|
4356
|
+
const codeAndSystem = templateUtilities.codeableConceptCoding(impression.code);
|
|
4357
|
+
if (!codeAndSystem || addedClinicalImpressions.has(name)) {
|
|
4358
|
+
continue;
|
|
4262
4359
|
}
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
}
|
|
4276
|
-
let findingsHtml = "";
|
|
4277
|
-
if (impression.finding && impression.finding.length > 0) {
|
|
4278
|
-
findingsHtml = "<ul>";
|
|
4279
|
-
for (const finding of impression.finding) {
|
|
4280
|
-
const findingText = finding.itemCodeableConcept ? templateUtilities.codeableConceptDisplay(finding.itemCodeableConcept) : finding.itemReference ? templateUtilities.renderReference(finding.itemReference) : "";
|
|
4281
|
-
const cause = finding.basis || "";
|
|
4282
|
-
findingsHtml += `<li>${findingText}${cause ? ` - ${cause}` : ""}</li>`;
|
|
4283
|
-
}
|
|
4284
|
-
findingsHtml += "</ul>";
|
|
4360
|
+
if (!name || name?.toLowerCase() === "unknown") {
|
|
4361
|
+
continue;
|
|
4362
|
+
}
|
|
4363
|
+
addedClinicalImpressions.add(name);
|
|
4364
|
+
let date = impression.effectiveDateTime ? templateUtilities.renderTime(impression.effectiveDateTime, timezone) : "";
|
|
4365
|
+
if (!date && impression.effectivePeriod?.start) {
|
|
4366
|
+
date = templateUtilities.renderTime(
|
|
4367
|
+
impression.effectivePeriod?.start,
|
|
4368
|
+
timezone
|
|
4369
|
+
);
|
|
4370
|
+
if (impression.effectivePeriod?.end) {
|
|
4371
|
+
date += " - " + templateUtilities.renderTime(impression.effectivePeriod?.end, timezone);
|
|
4285
4372
|
}
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
<td>${
|
|
4373
|
+
}
|
|
4374
|
+
clinicalImpressionsHtml += `<tr>
|
|
4375
|
+
<td>${templateUtilities.capitalizeFirstLetter(name)}</td>
|
|
4376
|
+
<td>${codeAndSystem}</td>
|
|
4377
|
+
<td>${date}</td>
|
|
4289
4378
|
<td>${impression.description || ""}</td>
|
|
4290
4379
|
<td>${impression.summary || ""}</td>
|
|
4291
|
-
<td>${findingsHtml}</td>
|
|
4292
4380
|
<td>${templateUtilities.getOwnerTag(impression)}</td>
|
|
4293
4381
|
</tr>`;
|
|
4382
|
+
}
|
|
4383
|
+
if (addedConditions.size > 0) {
|
|
4384
|
+
html += conditionHtml;
|
|
4385
|
+
html += `
|
|
4386
|
+
</tbody>
|
|
4387
|
+
</table>
|
|
4388
|
+
</div>`;
|
|
4389
|
+
}
|
|
4390
|
+
if (addedClinicalImpressions.size > 0) {
|
|
4391
|
+
html += clinicalImpressionsHtml;
|
|
4392
|
+
html += `
|
|
4393
|
+
</tbody>
|
|
4394
|
+
</table>
|
|
4395
|
+
</div>`;
|
|
4396
|
+
}
|
|
4397
|
+
html += `
|
|
4398
|
+
</div>`;
|
|
4399
|
+
return addedConditions.size > 0 || addedClinicalImpressions.size > 0 ? html : void 0;
|
|
4400
|
+
}
|
|
4401
|
+
static getFunctionalStatusNameFromCode(cc) {
|
|
4402
|
+
if (!cc) return "";
|
|
4403
|
+
for (const coding of cc.coding || []) {
|
|
4404
|
+
let functionalStatusName = FUNCTIONAL_STATUS_SNOMED_CODES[coding.code];
|
|
4405
|
+
if (functionalStatusName) {
|
|
4406
|
+
return functionalStatusName;
|
|
4407
|
+
}
|
|
4408
|
+
functionalStatusName = FUNCTIONAL_STATUS_ASSESSMENT_LOINC_CODES[coding.code];
|
|
4409
|
+
if (functionalStatusName) {
|
|
4410
|
+
return functionalStatusName;
|
|
4294
4411
|
}
|
|
4295
|
-
html += `</tbody></table>`;
|
|
4296
4412
|
}
|
|
4297
|
-
|
|
4298
|
-
|
|
4413
|
+
}
|
|
4414
|
+
static getClinicalImpressionEffectiveDate(impression) {
|
|
4415
|
+
if (impression.effectiveDateTime) {
|
|
4416
|
+
return new Date(impression.effectiveDateTime);
|
|
4417
|
+
} else if (impression.effectivePeriod) {
|
|
4418
|
+
if (impression.effectivePeriod.start) {
|
|
4419
|
+
return new Date(impression.effectivePeriod.start);
|
|
4420
|
+
} else if (impression.effectivePeriod.end) {
|
|
4421
|
+
return new Date(impression.effectivePeriod.end);
|
|
4422
|
+
}
|
|
4299
4423
|
}
|
|
4300
|
-
return html;
|
|
4301
4424
|
}
|
|
4302
4425
|
};
|
|
4303
4426
|
|