@imranq2/fhirpatientsummary 1.0.26 → 1.0.27

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.
Files changed (3) hide show
  1. package/dist/index.cjs +406 -31
  2. package/dist/index.js +406 -31
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -115,6 +115,8 @@ var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
115
115
  "Diastolic Blood Pressure": "valueRatio.denominator.value",
116
116
  "Default": "valueString"
117
117
  };
118
+ var RESULT_SUMMARY_OBSERVATION_CATEGORIES = ["laboratory", "Lab", "LAB"];
119
+ var RESULT_SUMMARY_OBSERVATION_DATE_FILTER = 2 * 365 * 24 * 60 * 60 * 1e3;
118
120
  var IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM = "https://fhir.icanbwell.com/4_0_0/CodeSystem/composition/";
119
121
 
120
122
  // src/structures/ips_section_resource_map.ts
@@ -134,7 +136,7 @@ var IPSSectionResourceFilters = {
134
136
  // Includes DeviceUseStatement. Device is needed for linked device details
135
137
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
136
138
  // Only include finalized diagnostic reports
137
- ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
139
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => RESULT_SUMMARY_OBSERVATION_CATEGORIES.includes(c.code))),
138
140
  // Only include completed procedures
139
141
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
140
142
  // Only include social history Observations
@@ -156,7 +158,7 @@ var IPSSectionSummaryCompositionFilter = {
156
158
  ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "careplan_summary_document"),
157
159
  ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "immunization_summary_document"),
158
160
  ["MedicationSummarySection" /* MEDICATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "medication_summary_document"),
159
- ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && ["lab_summary_document", "diagnosticreportlab_summary_document"].includes(c.code)),
161
+ // [IPSSections.DIAGNOSTIC_REPORTS]: (resource) => resource.resourceType === 'Composition' && resource.type?.coding?.some((c: any) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && ["lab_summary_document", "diagnosticreportlab_summary_document"].includes(c.code)),
160
162
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "procedure_summary_document")
161
163
  };
162
164
  var IPSSectionResourceHelper = class {
@@ -620,9 +622,18 @@ var TemplateUtilities = class {
620
622
  }
621
623
  return status;
622
624
  }
625
+ formatFloatValue(value) {
626
+ if (typeof value === "number") {
627
+ return value.toFixed(2).replace(/\.?0+$/, "");
628
+ } else if (typeof value === "string" && !isNaN(Number(value))) {
629
+ return parseFloat(value).toFixed(2).replace(/\.?0+$/, "");
630
+ }
631
+ return value;
632
+ }
623
633
  extractObservationSummaryValue(data, timezone) {
624
634
  if (data["valueQuantity.value"] !== void 0) {
625
- const value = data["valueQuantity.value"];
635
+ let value = data["valueQuantity.value"];
636
+ value = this.formatFloatValue(value);
626
637
  const unit = data["valueQuantity.unit"];
627
638
  return unit ? `${value} ${unit}` : `${value}`;
628
639
  }
@@ -639,7 +650,9 @@ var TemplateUtilities = class {
639
650
  return String(data["valueBoolean"]);
640
651
  }
641
652
  if (data["valueInteger"] !== void 0) {
642
- return String(data["valueInteger"]);
653
+ let value = String(data["valueInteger"]);
654
+ value = this.formatFloatValue(value);
655
+ return value;
643
656
  }
644
657
  if (data["valueDateTime"] !== void 0) {
645
658
  return this.renderTime(data["valueDateTime"], timezone);
@@ -659,7 +672,8 @@ var TemplateUtilities = class {
659
672
  return this.renderTime(data["valueTime"], timezone);
660
673
  }
661
674
  if (data["valueSampledData.origin.value"] !== void 0 || data["valueSampledData.origin.unit"] !== void 0) {
662
- const originValue = data["valueSampledData.origin.value"];
675
+ let originValue = data["valueSampledData.origin.value"];
676
+ originValue = this.formatFloatValue(originValue);
663
677
  const originUnit = data["valueSampledData.origin.unit"];
664
678
  let result = "";
665
679
  if (originValue !== void 0 && originUnit !== void 0) {
@@ -669,10 +683,10 @@ var TemplateUtilities = class {
669
683
  } else if (originUnit !== void 0) {
670
684
  result = `${originUnit}`;
671
685
  }
672
- const period = data["valueSampledData.period"];
673
- const factor = data["valueSampledData.factor"];
674
- const lowerLimit = data["valueSampledData.lowerLimit"];
675
- const upperLimit = data["valueSampledData.upperLimit"];
686
+ const period = this.formatFloatValue(data["valueSampledData.period"]);
687
+ const factor = this.formatFloatValue(data["valueSampledData.factor"]);
688
+ const lowerLimit = this.formatFloatValue(data["valueSampledData.lowerLimit"]);
689
+ const upperLimit = this.formatFloatValue(data["valueSampledData.upperLimit"]);
676
690
  const sampledData = data["valueSampledData.data"];
677
691
  const extras = [];
678
692
  if (period !== void 0) extras.push(`period: ${period}`);
@@ -708,14 +722,14 @@ var TemplateUtilities = class {
708
722
  if (data["valueRatio.numerator.value"] !== void 0 || data["valueRatio.denominator.value"] !== void 0) {
709
723
  let ratio = "";
710
724
  if (data["valueRatio.numerator.value"] !== void 0) {
711
- ratio += `${data["valueRatio.numerator.value"]}`;
725
+ ratio += `${this.formatFloatValue(data["valueRatio.numerator.value"])}`;
712
726
  if (data["valueRatio.numerator.unit"] !== void 0) {
713
727
  ratio += ` ${data["valueRatio.numerator.unit"]}`;
714
728
  }
715
729
  }
716
730
  if (data["valueRatio.denominator.value"] !== void 0) {
717
731
  ratio += " / ";
718
- ratio += `${data["valueRatio.denominator.value"]}`;
732
+ ratio += `${this.formatFloatValue(data["valueRatio.denominator.value"])}`;
719
733
  if (data["valueRatio.denominator.unit"] !== void 0) {
720
734
  ratio += ` ${data["valueRatio.denominator.unit"]}`;
721
735
  }
@@ -969,6 +983,313 @@ var TemplateUtilities = class {
969
983
 
970
984
  // src/constants.ts
971
985
  var ADDRESS_SIMILARITY_THRESHOLD = 70;
986
+ var LAB_LOINC_MAP = {
987
+ // METABOLIC PANELS (VERY COMMONLY ORDERED)
988
+ "Basic Metabolic Panel": [
989
+ "24321-2",
990
+ // Basic metabolic 2000 panel - Serum or Plasma
991
+ "51990-0"
992
+ // Basic metabolic 2000 panel - Blood
993
+ ],
994
+ "Comprehensive Metabolic Panel": [
995
+ "24323-8"
996
+ // Comprehensive metabolic 2000 panel - Serum or Plasma
997
+ ],
998
+ // CBC COMPONENTS
999
+ Hemoglobin: [
1000
+ "718-7"
1001
+ // Hemoglobin [Mass/volume] in Blood
1002
+ ],
1003
+ Hematocrit: [
1004
+ "4544-3"
1005
+ // Hematocrit [Volume Fraction] of Blood by Automated count
1006
+ ],
1007
+ "White Blood Cell Count": [
1008
+ "6690-2"
1009
+ // Leukocytes [///volume] in Blood by Automated count
1010
+ ],
1011
+ "Platelet Count": [
1012
+ "777-3"
1013
+ // Platelets [///volume] in Blood by Automated count
1014
+ ],
1015
+ "Complete Blood Count": [
1016
+ "58410-2",
1017
+ // CBC panel - Blood by Automated count
1018
+ "57021-8",
1019
+ // CBC W Auto Differential panel - Blood
1020
+ "69738-3"
1021
+ // CBC W Auto Differential panel - Blood by Automated count
1022
+ ],
1023
+ // CHEMISTRY - GLUCOSE
1024
+ Glucose: [
1025
+ "2345-7",
1026
+ // Glucose [Mass/volume] in Serum or Plasma
1027
+ "1558-6",
1028
+ // Fasting glucose [Mass/volume] in Serum or Plasma
1029
+ "2339-0"
1030
+ // Glucose [Mass/volume] in Blood
1031
+ ],
1032
+ // RENAL FUNCTION
1033
+ Creatinine: [
1034
+ "2160-0"
1035
+ // Creatinine [Mass/volume] in Serum or Plasma
1036
+ ],
1037
+ "Blood Urea Nitrogen": [
1038
+ "3094-0",
1039
+ // Urea nitrogen [Mass/volume] in Serum or Plasma
1040
+ "6299-2"
1041
+ // Urea nitrogen [Mass/volume] in Blood
1042
+ ],
1043
+ // ELECTROLYTES
1044
+ Sodium: [
1045
+ "2951-2"
1046
+ // Sodium [Moles/volume] in Serum or Plasma
1047
+ ],
1048
+ Potassium: [
1049
+ "2823-3"
1050
+ // Potassium [Moles/volume] in Serum or Plasma
1051
+ ],
1052
+ Chloride: [
1053
+ "2075-0"
1054
+ // Chloride [Moles/volume] in Serum or Plasma
1055
+ ],
1056
+ Calcium: [
1057
+ "17861-6",
1058
+ // Calcium [Mass/volume] in Serum or Plasma
1059
+ "1994-3"
1060
+ // Calcium.ionized [Moles/volume] in Serum or Plasma
1061
+ ],
1062
+ Magnesium: [
1063
+ "19123-9"
1064
+ // Magnesium [Mass/volume] in Serum or Plasma
1065
+ ],
1066
+ Phosphate: [
1067
+ "14879-1"
1068
+ // Phosphate [Mass/volume] in Serum or Plasma
1069
+ ],
1070
+ // PROTEINS
1071
+ Albumin: [
1072
+ "1751-7"
1073
+ // Albumin [Mass/volume] in Serum or Plasma
1074
+ ],
1075
+ "Total Protein": [
1076
+ "2885-2"
1077
+ // Protein [Mass/volume] in Serum or Plasma
1078
+ ],
1079
+ // LIVER FUNCTION
1080
+ Bilirubin: [
1081
+ "1975-2",
1082
+ // Bilirubin.total [Mass/volume] in Serum or Plasma
1083
+ "1968-7",
1084
+ // Bilirubin.direct [Mass/volume] in Serum or Plasma
1085
+ "1971-1"
1086
+ // Bilirubin.indirect [Mass/volume] in Serum or Plasma
1087
+ ],
1088
+ "Alkaline Phosphatase": [
1089
+ "6768-6"
1090
+ // Alkaline phosphatase [Enzymatic activity/volume] in Serum or Plasma
1091
+ ],
1092
+ AST: [
1093
+ "1920-8"
1094
+ // Aspartate aminotransferase [Enzymatic activity/volume] in Serum or Plasma
1095
+ ],
1096
+ ALT: [
1097
+ "1742-6"
1098
+ // Alanine aminotransferase [Enzymatic activity/volume] in Serum or Plasma
1099
+ ],
1100
+ GGT: [
1101
+ "2324-2"
1102
+ // Gamma glutamyl transferase [Enzymatic activity/volume] in Serum or Plasma
1103
+ ],
1104
+ // ENDOCRINE
1105
+ TSH: [
1106
+ "3016-3"
1107
+ // Thyrotropin [Units/volume] in Serum or Plasma
1108
+ ],
1109
+ "Free T4": [
1110
+ "3024-7"
1111
+ // Thyroxine (T4) free [Mass/volume] in Serum or Plasma
1112
+ ],
1113
+ "Total T4": [
1114
+ "3026-2"
1115
+ // Thyroxine (T4) [Mass/volume] in Serum or Plasma
1116
+ ],
1117
+ "Free T3": [
1118
+ "3051-0"
1119
+ // Triiodothyronine (T3) free [Mass/volume] in Serum or Plasma
1120
+ ],
1121
+ "Total T3": [
1122
+ "3053-6"
1123
+ // Triiodothyronine (T3) [Mass/volume] in Serum or Plasma
1124
+ ],
1125
+ HbA1c: [
1126
+ "4548-4",
1127
+ // Hemoglobin A1c/Hemoglobin.total in Blood
1128
+ "17856-6"
1129
+ // Hemoglobin A1c/Hemoglobin.total in Blood by HPLC
1130
+ ],
1131
+ // LIPID PANEL
1132
+ "Lipid Panel": [
1133
+ "24331-1",
1134
+ // Lipid 1996 panel - Serum or Plasma
1135
+ "57698-3"
1136
+ // Lipid panel with direct LDL - Serum or Plasma
1137
+ ],
1138
+ "Cholesterol Total": [
1139
+ "2093-3"
1140
+ // Cholesterol [Mass/volume] in Serum or Plasma
1141
+ ],
1142
+ "HDL Cholesterol": [
1143
+ "2085-9"
1144
+ // Cholesterol in HDL [Mass/volume] in Serum or Plasma
1145
+ ],
1146
+ "LDL Cholesterol": [
1147
+ "13457-7",
1148
+ // Cholesterol in LDL [Mass/volume] in Serum or Plasma by calculation
1149
+ "18262-6"
1150
+ // Cholesterol in LDL [Mass/volume] in Serum or Plasma by Direct assay
1151
+ ],
1152
+ Triglycerides: [
1153
+ "2571-8"
1154
+ // Triglyceride [Mass/volume] in Serum or Plasma
1155
+ ],
1156
+ // COAGULATION STUDIES (COMMONLY MISSING!)
1157
+ PT: [
1158
+ "5902-2"
1159
+ // Prothrombin time (PT)
1160
+ ],
1161
+ INR: [
1162
+ "6301-6"
1163
+ // INR in Platelet poor plasma by Coagulation assay
1164
+ ],
1165
+ PTT: [
1166
+ "3173-2",
1167
+ // aPTT in Blood by Coagulation assay
1168
+ "14979-9"
1169
+ // aPTT in Platelet poor plasma by Coagulation assay
1170
+ ],
1171
+ Fibrinogen: [
1172
+ "3255-7"
1173
+ // Fibrinogen [Mass/volume] in Platelet poor plasma by Coagulation assay
1174
+ ],
1175
+ "D-Dimer": [
1176
+ "48065-7",
1177
+ // D-dimer FEU [Mass/volume] in Platelet poor plasma
1178
+ "48066-5"
1179
+ // D-dimer DDU [Mass/volume] in Platelet poor plasma
1180
+ ],
1181
+ // CARDIAC MARKERS (CRITICAL FOR ER!)
1182
+ "Troponin I": [
1183
+ "10839-9",
1184
+ // Troponin I.cardiac [Mass/volume] in Serum or Plasma
1185
+ "42757-5",
1186
+ // Troponin I.cardiac [Mass/volume] in Blood
1187
+ "89579-7"
1188
+ // Troponin I.cardiac [Mass/volume] in Serum or Plasma by High sensitivity method
1189
+ ],
1190
+ "Troponin T": [
1191
+ "6598-7",
1192
+ // Troponin T.cardiac [Mass/volume] in Serum or Plasma
1193
+ "48425-3"
1194
+ // Troponin T.cardiac [Mass/volume] in Serum or Plasma by High sensitivity method
1195
+ ],
1196
+ BNP: [
1197
+ "30934-4"
1198
+ // BNP [Mass/volume] in Serum or Plasma
1199
+ ],
1200
+ "NT-proBNP": [
1201
+ "33762-6"
1202
+ // NT-proBNP [Mass/volume] in Serum or Plasma
1203
+ ],
1204
+ "CK-MB": [
1205
+ "13969-1"
1206
+ // Creatine kinase.MB [Mass/volume] in Serum or Plasma
1207
+ ],
1208
+ // INFLAMMATORY MARKERS
1209
+ CRP: [
1210
+ "1988-5",
1211
+ // C reactive protein [Mass/volume] in Serum or Plasma
1212
+ "30522-7"
1213
+ // C reactive protein [Mass/volume] in Serum or Plasma by High sensitivity method
1214
+ ],
1215
+ ESR: [
1216
+ "30341-2",
1217
+ // Erythrocyte sedimentation rate by Westergren method
1218
+ "4537-7"
1219
+ // Erythrocyte sedimentation rate
1220
+ ],
1221
+ // VITAMINS & MINERALS (VERY HIGH VOLUME!)
1222
+ "Vitamin D": [
1223
+ "1990-1",
1224
+ // Vitamin D [Mass/volume] in Serum or Plasma (obsolete, but still used)
1225
+ "14635-7",
1226
+ // 25-Hydroxyvitamin D3 [Mass/volume] in Serum or Plasma
1227
+ "62292-8"
1228
+ // 25-Hydroxyvitamin D2+D3 [Mass/volume] in Serum or Plasma
1229
+ ],
1230
+ "Vitamin B12": [
1231
+ "2132-9"
1232
+ // Cobalamin (Vitamin B12) [Mass/volume] in Serum or Plasma
1233
+ ],
1234
+ Folate: [
1235
+ "2284-8",
1236
+ // Folate [Mass/volume] in Serum or Plasma
1237
+ "15152-2"
1238
+ // Folate [Mass/volume] in Red Blood Cells
1239
+ ],
1240
+ Iron: [
1241
+ "2498-4"
1242
+ // Iron [Mass/volume] in Serum or Plasma
1243
+ ],
1244
+ Ferritin: [
1245
+ "2276-4"
1246
+ // Ferritin [Mass/volume] in Serum or Plasma
1247
+ ],
1248
+ TIBC: [
1249
+ "2500-7"
1250
+ // Iron binding capacity [Mass/volume] in Serum or Plasma
1251
+ ],
1252
+ // OTHER COMMON TESTS
1253
+ PSA: [
1254
+ "2857-1",
1255
+ // Prostate specific Ag [Mass/volume] in Serum or Plasma
1256
+ "10886-0"
1257
+ // Prostate specific Ag Free [Mass/volume] in Serum or Plasma
1258
+ ],
1259
+ "Uric Acid": [
1260
+ "3084-1"
1261
+ // Urate [Mass/volume] in Serum or Plasma
1262
+ ],
1263
+ LDH: [
1264
+ "2532-0",
1265
+ // Lactate dehydrogenase [Enzymatic activity/volume] in Serum or Plasma
1266
+ "14804-9"
1267
+ // Lactate dehydrogenase [Enzymatic activity/volume] in Serum or Plasma by Lactate to pyruvate reaction
1268
+ ],
1269
+ Amylase: [
1270
+ "1798-8"
1271
+ // Amylase [Enzymatic activity/volume] in Serum or Plasma
1272
+ ],
1273
+ Lipase: [
1274
+ "3040-3"
1275
+ // Lipase [Enzymatic activity/volume] in Serum or Plasma
1276
+ ],
1277
+ hCG: [
1278
+ "21198-7",
1279
+ // Choriogonadotropin.beta subunit [Units/volume] in Serum or Plasma
1280
+ "2118-8",
1281
+ // Choriogonadotropin (pregnancy test) [Presence] in Serum or Plasma
1282
+ "2106-3"
1283
+ // Choriogonadotropin (pregnancy test) [Presence] in Urine
1284
+ ],
1285
+ // URINALYSIS
1286
+ Urinalysis: [
1287
+ "24357-6",
1288
+ // Urinalysis macro (dipstick) panel - Urine
1289
+ "24356-8"
1290
+ // Urinalysis complete panel - Urine
1291
+ ]
1292
+ };
972
1293
 
973
1294
  // src/narratives/templates/typescript/PatientTemplate.ts
974
1295
  var PatientTemplate = class _PatientTemplate {
@@ -2093,6 +2414,12 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
2093
2414
  };
2094
2415
 
2095
2416
  // src/narratives/templates/typescript/DiagnosticResultsTemplate.ts
2417
+ var loincToLabName = {};
2418
+ for (const [labName, loincCodes] of Object.entries(LAB_LOINC_MAP)) {
2419
+ for (const code of loincCodes) {
2420
+ loincToLabName[code] = labName;
2421
+ }
2422
+ }
2096
2423
  var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2097
2424
  /**
2098
2425
  * Generate HTML narrative for Diagnostic Results
@@ -2519,30 +2846,80 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2519
2846
  const observations = this.getObservations(resources);
2520
2847
  if (observations.length > 0) {
2521
2848
  observations.sort((a, b) => {
2522
- const dateA = a.effectiveDateTime || a.effectivePeriod?.start || a.effectivePeriod?.end;
2523
- const dateB = b.effectiveDateTime || b.effectivePeriod?.start || b.effectivePeriod?.end;
2524
- return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2849
+ const dateA = this.getObservationDate(a);
2850
+ const dateB = this.getObservationDate(b);
2851
+ return dateA && dateB ? dateB.getTime() - dateA.getTime() : 0;
2525
2852
  });
2853
+ this.filterObservationForLoincCodes(observations);
2526
2854
  html += this.renderObservations(templateUtilities, observations, timezone);
2527
2855
  }
2528
- const diagnosticReports = this.getDiagnosticReports(resources);
2529
- if (diagnosticReports.length > 0) {
2530
- diagnosticReports.sort((a, b) => {
2531
- const dateA = a.issued;
2532
- const dateB = b.issued;
2533
- return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2534
- });
2535
- html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
2856
+ if (process.env.ENABLE_DIAGNOSTIC_REPORTS_IN_SUMMARY === "true") {
2857
+ const diagnosticReports = this.getDiagnosticReports(resources);
2858
+ if (diagnosticReports.length > 0) {
2859
+ diagnosticReports.sort((a, b) => {
2860
+ const dateA = a.issued;
2861
+ const dateB = b.issued;
2862
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2863
+ });
2864
+ html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
2865
+ }
2536
2866
  }
2537
2867
  return html;
2538
2868
  }
2869
+ static filterObservationForLoincCodes(observations) {
2870
+ const labsAdded = /* @__PURE__ */ new Set();
2871
+ const filteredObservations = [];
2872
+ for (const obs of observations) {
2873
+ const loincCode = this.getObservationLoincCode(obs);
2874
+ if (loincCode && loincToLabName[loincCode]) {
2875
+ const labName = loincToLabName[loincCode];
2876
+ if (!labsAdded.has(labName)) {
2877
+ labsAdded.add(labName);
2878
+ filteredObservations.push(obs);
2879
+ }
2880
+ }
2881
+ }
2882
+ observations.length = 0;
2883
+ observations.push(...filteredObservations);
2884
+ }
2885
+ static getObservationLoincCode(obs) {
2886
+ if (obs.code && obs.code.coding) {
2887
+ for (const coding of obs.code.coding) {
2888
+ if (coding.system && coding.system.toLowerCase().includes("loinc") && coding.code) {
2889
+ return coding.code;
2890
+ }
2891
+ }
2892
+ }
2893
+ return void 0;
2894
+ }
2895
+ static getObservationDate(obs) {
2896
+ let obsDate = void 0;
2897
+ if (obs.effectiveDateTime) {
2898
+ obsDate = new Date(obs.effectiveDateTime);
2899
+ } else if (obs.effectivePeriod) {
2900
+ if (obs.effectivePeriod.start) {
2901
+ obsDate = new Date(obs.effectivePeriod.start);
2902
+ } else if (obs.effectivePeriod.end) {
2903
+ obsDate = new Date(obs.effectivePeriod.end);
2904
+ }
2905
+ }
2906
+ return obsDate;
2907
+ }
2539
2908
  /**
2540
2909
  * Get all Observation resources from the resource array
2541
2910
  * @param resources - FHIR resources array
2542
2911
  * @returns Array of Observation resources
2543
2912
  */
2544
2913
  static getObservations(resources) {
2545
- return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
2914
+ return resources.filter((resourceItem) => {
2915
+ if (resourceItem.resourceType === "Observation") {
2916
+ const obsDate = this.getObservationDate(resourceItem);
2917
+ if (obsDate && obsDate >= new Date(Date.now() - RESULT_SUMMARY_OBSERVATION_DATE_FILTER)) {
2918
+ return true;
2919
+ }
2920
+ }
2921
+ return false;
2922
+ }).map((resourceItem) => resourceItem);
2546
2923
  }
2547
2924
  /**
2548
2925
  * Get all DiagnosticReport resources from the resource array
@@ -2560,17 +2937,18 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2560
2937
  * @returns HTML string for rendering
2561
2938
  */
2562
2939
  static renderObservations(templateUtilities, observations, timezone) {
2563
- let html = `
2564
- <h3>Observations</h3>
2940
+ let html = "";
2941
+ if (process.env.ENABLE_DIAGNOSTIC_REPORTS_IN_SUMMARY === "true") {
2942
+ html += `
2943
+ <h3>Observations</h3>`;
2944
+ }
2945
+ html += `
2565
2946
  <table>
2566
2947
  <thead>
2567
2948
  <tr>
2568
2949
  <th>Code</th>
2569
2950
  <th>Result</th>
2570
- <th>Unit</th>
2571
- <th>Interpretation</th>
2572
2951
  <th>Reference Range</th>
2573
- <th>Comments</th>
2574
2952
  <th>Date</th>
2575
2953
  </tr>
2576
2954
  </thead>
@@ -2584,10 +2962,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2584
2962
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
2585
2963
  <td>${obsCode}</td>
2586
2964
  <td>${templateUtilities.extractObservationValue(obs)}</td>
2587
- <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
2588
- <td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
2589
2965
  <td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
2590
- <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
2591
2966
  <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
2592
2967
  </tr>`;
2593
2968
  }
package/dist/index.js CHANGED
@@ -87,6 +87,8 @@ var VITAL_SIGNS_SUMMARY_COMPONENT_MAP = {
87
87
  "Diastolic Blood Pressure": "valueRatio.denominator.value",
88
88
  "Default": "valueString"
89
89
  };
90
+ var RESULT_SUMMARY_OBSERVATION_CATEGORIES = ["laboratory", "Lab", "LAB"];
91
+ var RESULT_SUMMARY_OBSERVATION_DATE_FILTER = 2 * 365 * 24 * 60 * 60 * 1e3;
90
92
  var IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM = "https://fhir.icanbwell.com/4_0_0/CodeSystem/composition/";
91
93
 
92
94
  // src/structures/ips_section_resource_map.ts
@@ -106,7 +108,7 @@ var IPSSectionResourceFilters = {
106
108
  // Includes DeviceUseStatement. Device is needed for linked device details
107
109
  ["MedicalDeviceSection" /* MEDICAL_DEVICES */]: (resource) => ["DeviceUseStatement", "Device"].includes(resource.resourceType),
108
110
  // Only include finalized diagnostic reports
109
- ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => ["DiagnosticReport", "Observation"].includes(resource.resourceType) && resource.status === "final",
111
+ ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "Observation" && resource.category?.some((cat) => cat.coding?.some((c) => RESULT_SUMMARY_OBSERVATION_CATEGORIES.includes(c.code))),
110
112
  // Only include completed procedures
111
113
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Procedure" && resource.status === "completed",
112
114
  // Only include social history Observations
@@ -128,7 +130,7 @@ var IPSSectionSummaryCompositionFilter = {
128
130
  ["PlanOfCareSection" /* CARE_PLAN */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "careplan_summary_document"),
129
131
  ["ImmunizationSection" /* IMMUNIZATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "immunization_summary_document"),
130
132
  ["MedicationSummarySection" /* MEDICATIONS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "medication_summary_document"),
131
- ["ResultsSection" /* DIAGNOSTIC_REPORTS */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && ["lab_summary_document", "diagnosticreportlab_summary_document"].includes(c.code)),
133
+ // [IPSSections.DIAGNOSTIC_REPORTS]: (resource) => resource.resourceType === 'Composition' && resource.type?.coding?.some((c: any) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && ["lab_summary_document", "diagnosticreportlab_summary_document"].includes(c.code)),
132
134
  ["HistoryOfProceduresSection" /* PROCEDURES */]: (resource) => resource.resourceType === "Composition" && resource.type?.coding?.some((c) => c.system === IPS_SUMMARY_COMPOSITION_TYPE_SYSTEM && c.code === "procedure_summary_document")
133
135
  };
134
136
  var IPSSectionResourceHelper = class {
@@ -592,9 +594,18 @@ var TemplateUtilities = class {
592
594
  }
593
595
  return status;
594
596
  }
597
+ formatFloatValue(value) {
598
+ if (typeof value === "number") {
599
+ return value.toFixed(2).replace(/\.?0+$/, "");
600
+ } else if (typeof value === "string" && !isNaN(Number(value))) {
601
+ return parseFloat(value).toFixed(2).replace(/\.?0+$/, "");
602
+ }
603
+ return value;
604
+ }
595
605
  extractObservationSummaryValue(data, timezone) {
596
606
  if (data["valueQuantity.value"] !== void 0) {
597
- const value = data["valueQuantity.value"];
607
+ let value = data["valueQuantity.value"];
608
+ value = this.formatFloatValue(value);
598
609
  const unit = data["valueQuantity.unit"];
599
610
  return unit ? `${value} ${unit}` : `${value}`;
600
611
  }
@@ -611,7 +622,9 @@ var TemplateUtilities = class {
611
622
  return String(data["valueBoolean"]);
612
623
  }
613
624
  if (data["valueInteger"] !== void 0) {
614
- return String(data["valueInteger"]);
625
+ let value = String(data["valueInteger"]);
626
+ value = this.formatFloatValue(value);
627
+ return value;
615
628
  }
616
629
  if (data["valueDateTime"] !== void 0) {
617
630
  return this.renderTime(data["valueDateTime"], timezone);
@@ -631,7 +644,8 @@ var TemplateUtilities = class {
631
644
  return this.renderTime(data["valueTime"], timezone);
632
645
  }
633
646
  if (data["valueSampledData.origin.value"] !== void 0 || data["valueSampledData.origin.unit"] !== void 0) {
634
- const originValue = data["valueSampledData.origin.value"];
647
+ let originValue = data["valueSampledData.origin.value"];
648
+ originValue = this.formatFloatValue(originValue);
635
649
  const originUnit = data["valueSampledData.origin.unit"];
636
650
  let result = "";
637
651
  if (originValue !== void 0 && originUnit !== void 0) {
@@ -641,10 +655,10 @@ var TemplateUtilities = class {
641
655
  } else if (originUnit !== void 0) {
642
656
  result = `${originUnit}`;
643
657
  }
644
- const period = data["valueSampledData.period"];
645
- const factor = data["valueSampledData.factor"];
646
- const lowerLimit = data["valueSampledData.lowerLimit"];
647
- const upperLimit = data["valueSampledData.upperLimit"];
658
+ const period = this.formatFloatValue(data["valueSampledData.period"]);
659
+ const factor = this.formatFloatValue(data["valueSampledData.factor"]);
660
+ const lowerLimit = this.formatFloatValue(data["valueSampledData.lowerLimit"]);
661
+ const upperLimit = this.formatFloatValue(data["valueSampledData.upperLimit"]);
648
662
  const sampledData = data["valueSampledData.data"];
649
663
  const extras = [];
650
664
  if (period !== void 0) extras.push(`period: ${period}`);
@@ -680,14 +694,14 @@ var TemplateUtilities = class {
680
694
  if (data["valueRatio.numerator.value"] !== void 0 || data["valueRatio.denominator.value"] !== void 0) {
681
695
  let ratio = "";
682
696
  if (data["valueRatio.numerator.value"] !== void 0) {
683
- ratio += `${data["valueRatio.numerator.value"]}`;
697
+ ratio += `${this.formatFloatValue(data["valueRatio.numerator.value"])}`;
684
698
  if (data["valueRatio.numerator.unit"] !== void 0) {
685
699
  ratio += ` ${data["valueRatio.numerator.unit"]}`;
686
700
  }
687
701
  }
688
702
  if (data["valueRatio.denominator.value"] !== void 0) {
689
703
  ratio += " / ";
690
- ratio += `${data["valueRatio.denominator.value"]}`;
704
+ ratio += `${this.formatFloatValue(data["valueRatio.denominator.value"])}`;
691
705
  if (data["valueRatio.denominator.unit"] !== void 0) {
692
706
  ratio += ` ${data["valueRatio.denominator.unit"]}`;
693
707
  }
@@ -941,6 +955,313 @@ var TemplateUtilities = class {
941
955
 
942
956
  // src/constants.ts
943
957
  var ADDRESS_SIMILARITY_THRESHOLD = 70;
958
+ var LAB_LOINC_MAP = {
959
+ // METABOLIC PANELS (VERY COMMONLY ORDERED)
960
+ "Basic Metabolic Panel": [
961
+ "24321-2",
962
+ // Basic metabolic 2000 panel - Serum or Plasma
963
+ "51990-0"
964
+ // Basic metabolic 2000 panel - Blood
965
+ ],
966
+ "Comprehensive Metabolic Panel": [
967
+ "24323-8"
968
+ // Comprehensive metabolic 2000 panel - Serum or Plasma
969
+ ],
970
+ // CBC COMPONENTS
971
+ Hemoglobin: [
972
+ "718-7"
973
+ // Hemoglobin [Mass/volume] in Blood
974
+ ],
975
+ Hematocrit: [
976
+ "4544-3"
977
+ // Hematocrit [Volume Fraction] of Blood by Automated count
978
+ ],
979
+ "White Blood Cell Count": [
980
+ "6690-2"
981
+ // Leukocytes [///volume] in Blood by Automated count
982
+ ],
983
+ "Platelet Count": [
984
+ "777-3"
985
+ // Platelets [///volume] in Blood by Automated count
986
+ ],
987
+ "Complete Blood Count": [
988
+ "58410-2",
989
+ // CBC panel - Blood by Automated count
990
+ "57021-8",
991
+ // CBC W Auto Differential panel - Blood
992
+ "69738-3"
993
+ // CBC W Auto Differential panel - Blood by Automated count
994
+ ],
995
+ // CHEMISTRY - GLUCOSE
996
+ Glucose: [
997
+ "2345-7",
998
+ // Glucose [Mass/volume] in Serum or Plasma
999
+ "1558-6",
1000
+ // Fasting glucose [Mass/volume] in Serum or Plasma
1001
+ "2339-0"
1002
+ // Glucose [Mass/volume] in Blood
1003
+ ],
1004
+ // RENAL FUNCTION
1005
+ Creatinine: [
1006
+ "2160-0"
1007
+ // Creatinine [Mass/volume] in Serum or Plasma
1008
+ ],
1009
+ "Blood Urea Nitrogen": [
1010
+ "3094-0",
1011
+ // Urea nitrogen [Mass/volume] in Serum or Plasma
1012
+ "6299-2"
1013
+ // Urea nitrogen [Mass/volume] in Blood
1014
+ ],
1015
+ // ELECTROLYTES
1016
+ Sodium: [
1017
+ "2951-2"
1018
+ // Sodium [Moles/volume] in Serum or Plasma
1019
+ ],
1020
+ Potassium: [
1021
+ "2823-3"
1022
+ // Potassium [Moles/volume] in Serum or Plasma
1023
+ ],
1024
+ Chloride: [
1025
+ "2075-0"
1026
+ // Chloride [Moles/volume] in Serum or Plasma
1027
+ ],
1028
+ Calcium: [
1029
+ "17861-6",
1030
+ // Calcium [Mass/volume] in Serum or Plasma
1031
+ "1994-3"
1032
+ // Calcium.ionized [Moles/volume] in Serum or Plasma
1033
+ ],
1034
+ Magnesium: [
1035
+ "19123-9"
1036
+ // Magnesium [Mass/volume] in Serum or Plasma
1037
+ ],
1038
+ Phosphate: [
1039
+ "14879-1"
1040
+ // Phosphate [Mass/volume] in Serum or Plasma
1041
+ ],
1042
+ // PROTEINS
1043
+ Albumin: [
1044
+ "1751-7"
1045
+ // Albumin [Mass/volume] in Serum or Plasma
1046
+ ],
1047
+ "Total Protein": [
1048
+ "2885-2"
1049
+ // Protein [Mass/volume] in Serum or Plasma
1050
+ ],
1051
+ // LIVER FUNCTION
1052
+ Bilirubin: [
1053
+ "1975-2",
1054
+ // Bilirubin.total [Mass/volume] in Serum or Plasma
1055
+ "1968-7",
1056
+ // Bilirubin.direct [Mass/volume] in Serum or Plasma
1057
+ "1971-1"
1058
+ // Bilirubin.indirect [Mass/volume] in Serum or Plasma
1059
+ ],
1060
+ "Alkaline Phosphatase": [
1061
+ "6768-6"
1062
+ // Alkaline phosphatase [Enzymatic activity/volume] in Serum or Plasma
1063
+ ],
1064
+ AST: [
1065
+ "1920-8"
1066
+ // Aspartate aminotransferase [Enzymatic activity/volume] in Serum or Plasma
1067
+ ],
1068
+ ALT: [
1069
+ "1742-6"
1070
+ // Alanine aminotransferase [Enzymatic activity/volume] in Serum or Plasma
1071
+ ],
1072
+ GGT: [
1073
+ "2324-2"
1074
+ // Gamma glutamyl transferase [Enzymatic activity/volume] in Serum or Plasma
1075
+ ],
1076
+ // ENDOCRINE
1077
+ TSH: [
1078
+ "3016-3"
1079
+ // Thyrotropin [Units/volume] in Serum or Plasma
1080
+ ],
1081
+ "Free T4": [
1082
+ "3024-7"
1083
+ // Thyroxine (T4) free [Mass/volume] in Serum or Plasma
1084
+ ],
1085
+ "Total T4": [
1086
+ "3026-2"
1087
+ // Thyroxine (T4) [Mass/volume] in Serum or Plasma
1088
+ ],
1089
+ "Free T3": [
1090
+ "3051-0"
1091
+ // Triiodothyronine (T3) free [Mass/volume] in Serum or Plasma
1092
+ ],
1093
+ "Total T3": [
1094
+ "3053-6"
1095
+ // Triiodothyronine (T3) [Mass/volume] in Serum or Plasma
1096
+ ],
1097
+ HbA1c: [
1098
+ "4548-4",
1099
+ // Hemoglobin A1c/Hemoglobin.total in Blood
1100
+ "17856-6"
1101
+ // Hemoglobin A1c/Hemoglobin.total in Blood by HPLC
1102
+ ],
1103
+ // LIPID PANEL
1104
+ "Lipid Panel": [
1105
+ "24331-1",
1106
+ // Lipid 1996 panel - Serum or Plasma
1107
+ "57698-3"
1108
+ // Lipid panel with direct LDL - Serum or Plasma
1109
+ ],
1110
+ "Cholesterol Total": [
1111
+ "2093-3"
1112
+ // Cholesterol [Mass/volume] in Serum or Plasma
1113
+ ],
1114
+ "HDL Cholesterol": [
1115
+ "2085-9"
1116
+ // Cholesterol in HDL [Mass/volume] in Serum or Plasma
1117
+ ],
1118
+ "LDL Cholesterol": [
1119
+ "13457-7",
1120
+ // Cholesterol in LDL [Mass/volume] in Serum or Plasma by calculation
1121
+ "18262-6"
1122
+ // Cholesterol in LDL [Mass/volume] in Serum or Plasma by Direct assay
1123
+ ],
1124
+ Triglycerides: [
1125
+ "2571-8"
1126
+ // Triglyceride [Mass/volume] in Serum or Plasma
1127
+ ],
1128
+ // COAGULATION STUDIES (COMMONLY MISSING!)
1129
+ PT: [
1130
+ "5902-2"
1131
+ // Prothrombin time (PT)
1132
+ ],
1133
+ INR: [
1134
+ "6301-6"
1135
+ // INR in Platelet poor plasma by Coagulation assay
1136
+ ],
1137
+ PTT: [
1138
+ "3173-2",
1139
+ // aPTT in Blood by Coagulation assay
1140
+ "14979-9"
1141
+ // aPTT in Platelet poor plasma by Coagulation assay
1142
+ ],
1143
+ Fibrinogen: [
1144
+ "3255-7"
1145
+ // Fibrinogen [Mass/volume] in Platelet poor plasma by Coagulation assay
1146
+ ],
1147
+ "D-Dimer": [
1148
+ "48065-7",
1149
+ // D-dimer FEU [Mass/volume] in Platelet poor plasma
1150
+ "48066-5"
1151
+ // D-dimer DDU [Mass/volume] in Platelet poor plasma
1152
+ ],
1153
+ // CARDIAC MARKERS (CRITICAL FOR ER!)
1154
+ "Troponin I": [
1155
+ "10839-9",
1156
+ // Troponin I.cardiac [Mass/volume] in Serum or Plasma
1157
+ "42757-5",
1158
+ // Troponin I.cardiac [Mass/volume] in Blood
1159
+ "89579-7"
1160
+ // Troponin I.cardiac [Mass/volume] in Serum or Plasma by High sensitivity method
1161
+ ],
1162
+ "Troponin T": [
1163
+ "6598-7",
1164
+ // Troponin T.cardiac [Mass/volume] in Serum or Plasma
1165
+ "48425-3"
1166
+ // Troponin T.cardiac [Mass/volume] in Serum or Plasma by High sensitivity method
1167
+ ],
1168
+ BNP: [
1169
+ "30934-4"
1170
+ // BNP [Mass/volume] in Serum or Plasma
1171
+ ],
1172
+ "NT-proBNP": [
1173
+ "33762-6"
1174
+ // NT-proBNP [Mass/volume] in Serum or Plasma
1175
+ ],
1176
+ "CK-MB": [
1177
+ "13969-1"
1178
+ // Creatine kinase.MB [Mass/volume] in Serum or Plasma
1179
+ ],
1180
+ // INFLAMMATORY MARKERS
1181
+ CRP: [
1182
+ "1988-5",
1183
+ // C reactive protein [Mass/volume] in Serum or Plasma
1184
+ "30522-7"
1185
+ // C reactive protein [Mass/volume] in Serum or Plasma by High sensitivity method
1186
+ ],
1187
+ ESR: [
1188
+ "30341-2",
1189
+ // Erythrocyte sedimentation rate by Westergren method
1190
+ "4537-7"
1191
+ // Erythrocyte sedimentation rate
1192
+ ],
1193
+ // VITAMINS & MINERALS (VERY HIGH VOLUME!)
1194
+ "Vitamin D": [
1195
+ "1990-1",
1196
+ // Vitamin D [Mass/volume] in Serum or Plasma (obsolete, but still used)
1197
+ "14635-7",
1198
+ // 25-Hydroxyvitamin D3 [Mass/volume] in Serum or Plasma
1199
+ "62292-8"
1200
+ // 25-Hydroxyvitamin D2+D3 [Mass/volume] in Serum or Plasma
1201
+ ],
1202
+ "Vitamin B12": [
1203
+ "2132-9"
1204
+ // Cobalamin (Vitamin B12) [Mass/volume] in Serum or Plasma
1205
+ ],
1206
+ Folate: [
1207
+ "2284-8",
1208
+ // Folate [Mass/volume] in Serum or Plasma
1209
+ "15152-2"
1210
+ // Folate [Mass/volume] in Red Blood Cells
1211
+ ],
1212
+ Iron: [
1213
+ "2498-4"
1214
+ // Iron [Mass/volume] in Serum or Plasma
1215
+ ],
1216
+ Ferritin: [
1217
+ "2276-4"
1218
+ // Ferritin [Mass/volume] in Serum or Plasma
1219
+ ],
1220
+ TIBC: [
1221
+ "2500-7"
1222
+ // Iron binding capacity [Mass/volume] in Serum or Plasma
1223
+ ],
1224
+ // OTHER COMMON TESTS
1225
+ PSA: [
1226
+ "2857-1",
1227
+ // Prostate specific Ag [Mass/volume] in Serum or Plasma
1228
+ "10886-0"
1229
+ // Prostate specific Ag Free [Mass/volume] in Serum or Plasma
1230
+ ],
1231
+ "Uric Acid": [
1232
+ "3084-1"
1233
+ // Urate [Mass/volume] in Serum or Plasma
1234
+ ],
1235
+ LDH: [
1236
+ "2532-0",
1237
+ // Lactate dehydrogenase [Enzymatic activity/volume] in Serum or Plasma
1238
+ "14804-9"
1239
+ // Lactate dehydrogenase [Enzymatic activity/volume] in Serum or Plasma by Lactate to pyruvate reaction
1240
+ ],
1241
+ Amylase: [
1242
+ "1798-8"
1243
+ // Amylase [Enzymatic activity/volume] in Serum or Plasma
1244
+ ],
1245
+ Lipase: [
1246
+ "3040-3"
1247
+ // Lipase [Enzymatic activity/volume] in Serum or Plasma
1248
+ ],
1249
+ hCG: [
1250
+ "21198-7",
1251
+ // Choriogonadotropin.beta subunit [Units/volume] in Serum or Plasma
1252
+ "2118-8",
1253
+ // Choriogonadotropin (pregnancy test) [Presence] in Serum or Plasma
1254
+ "2106-3"
1255
+ // Choriogonadotropin (pregnancy test) [Presence] in Urine
1256
+ ],
1257
+ // URINALYSIS
1258
+ Urinalysis: [
1259
+ "24357-6",
1260
+ // Urinalysis macro (dipstick) panel - Urine
1261
+ "24356-8"
1262
+ // Urinalysis complete panel - Urine
1263
+ ]
1264
+ };
944
1265
 
945
1266
  // src/narratives/templates/typescript/PatientTemplate.ts
946
1267
  var PatientTemplate = class _PatientTemplate {
@@ -2065,6 +2386,12 @@ var MedicalDevicesTemplate = class _MedicalDevicesTemplate {
2065
2386
  };
2066
2387
 
2067
2388
  // src/narratives/templates/typescript/DiagnosticResultsTemplate.ts
2389
+ var loincToLabName = {};
2390
+ for (const [labName, loincCodes] of Object.entries(LAB_LOINC_MAP)) {
2391
+ for (const code of loincCodes) {
2392
+ loincToLabName[code] = labName;
2393
+ }
2394
+ }
2068
2395
  var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2069
2396
  /**
2070
2397
  * Generate HTML narrative for Diagnostic Results
@@ -2491,30 +2818,80 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2491
2818
  const observations = this.getObservations(resources);
2492
2819
  if (observations.length > 0) {
2493
2820
  observations.sort((a, b) => {
2494
- const dateA = a.effectiveDateTime || a.effectivePeriod?.start || a.effectivePeriod?.end;
2495
- const dateB = b.effectiveDateTime || b.effectivePeriod?.start || b.effectivePeriod?.end;
2496
- return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2821
+ const dateA = this.getObservationDate(a);
2822
+ const dateB = this.getObservationDate(b);
2823
+ return dateA && dateB ? dateB.getTime() - dateA.getTime() : 0;
2497
2824
  });
2825
+ this.filterObservationForLoincCodes(observations);
2498
2826
  html += this.renderObservations(templateUtilities, observations, timezone);
2499
2827
  }
2500
- const diagnosticReports = this.getDiagnosticReports(resources);
2501
- if (diagnosticReports.length > 0) {
2502
- diagnosticReports.sort((a, b) => {
2503
- const dateA = a.issued;
2504
- const dateB = b.issued;
2505
- return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2506
- });
2507
- html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
2828
+ if (process.env.ENABLE_DIAGNOSTIC_REPORTS_IN_SUMMARY === "true") {
2829
+ const diagnosticReports = this.getDiagnosticReports(resources);
2830
+ if (diagnosticReports.length > 0) {
2831
+ diagnosticReports.sort((a, b) => {
2832
+ const dateA = a.issued;
2833
+ const dateB = b.issued;
2834
+ return dateA && dateB ? new Date(dateB).getTime() - new Date(dateA).getTime() : 0;
2835
+ });
2836
+ html += this.renderDiagnosticReports(templateUtilities, diagnosticReports, timezone);
2837
+ }
2508
2838
  }
2509
2839
  return html;
2510
2840
  }
2841
+ static filterObservationForLoincCodes(observations) {
2842
+ const labsAdded = /* @__PURE__ */ new Set();
2843
+ const filteredObservations = [];
2844
+ for (const obs of observations) {
2845
+ const loincCode = this.getObservationLoincCode(obs);
2846
+ if (loincCode && loincToLabName[loincCode]) {
2847
+ const labName = loincToLabName[loincCode];
2848
+ if (!labsAdded.has(labName)) {
2849
+ labsAdded.add(labName);
2850
+ filteredObservations.push(obs);
2851
+ }
2852
+ }
2853
+ }
2854
+ observations.length = 0;
2855
+ observations.push(...filteredObservations);
2856
+ }
2857
+ static getObservationLoincCode(obs) {
2858
+ if (obs.code && obs.code.coding) {
2859
+ for (const coding of obs.code.coding) {
2860
+ if (coding.system && coding.system.toLowerCase().includes("loinc") && coding.code) {
2861
+ return coding.code;
2862
+ }
2863
+ }
2864
+ }
2865
+ return void 0;
2866
+ }
2867
+ static getObservationDate(obs) {
2868
+ let obsDate = void 0;
2869
+ if (obs.effectiveDateTime) {
2870
+ obsDate = new Date(obs.effectiveDateTime);
2871
+ } else if (obs.effectivePeriod) {
2872
+ if (obs.effectivePeriod.start) {
2873
+ obsDate = new Date(obs.effectivePeriod.start);
2874
+ } else if (obs.effectivePeriod.end) {
2875
+ obsDate = new Date(obs.effectivePeriod.end);
2876
+ }
2877
+ }
2878
+ return obsDate;
2879
+ }
2511
2880
  /**
2512
2881
  * Get all Observation resources from the resource array
2513
2882
  * @param resources - FHIR resources array
2514
2883
  * @returns Array of Observation resources
2515
2884
  */
2516
2885
  static getObservations(resources) {
2517
- return resources.filter((resourceItem) => resourceItem.resourceType === "Observation").map((resourceItem) => resourceItem);
2886
+ return resources.filter((resourceItem) => {
2887
+ if (resourceItem.resourceType === "Observation") {
2888
+ const obsDate = this.getObservationDate(resourceItem);
2889
+ if (obsDate && obsDate >= new Date(Date.now() - RESULT_SUMMARY_OBSERVATION_DATE_FILTER)) {
2890
+ return true;
2891
+ }
2892
+ }
2893
+ return false;
2894
+ }).map((resourceItem) => resourceItem);
2518
2895
  }
2519
2896
  /**
2520
2897
  * Get all DiagnosticReport resources from the resource array
@@ -2532,17 +2909,18 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2532
2909
  * @returns HTML string for rendering
2533
2910
  */
2534
2911
  static renderObservations(templateUtilities, observations, timezone) {
2535
- let html = `
2536
- <h3>Observations</h3>
2912
+ let html = "";
2913
+ if (process.env.ENABLE_DIAGNOSTIC_REPORTS_IN_SUMMARY === "true") {
2914
+ html += `
2915
+ <h3>Observations</h3>`;
2916
+ }
2917
+ html += `
2537
2918
  <table>
2538
2919
  <thead>
2539
2920
  <tr>
2540
2921
  <th>Code</th>
2541
2922
  <th>Result</th>
2542
- <th>Unit</th>
2543
- <th>Interpretation</th>
2544
2923
  <th>Reference Range</th>
2545
- <th>Comments</th>
2546
2924
  <th>Date</th>
2547
2925
  </tr>
2548
2926
  </thead>
@@ -2556,10 +2934,7 @@ var DiagnosticResultsTemplate = class _DiagnosticResultsTemplate {
2556
2934
  <tr id="${templateUtilities.narrativeLinkId(obs)}">
2557
2935
  <td>${obsCode}</td>
2558
2936
  <td>${templateUtilities.extractObservationValue(obs)}</td>
2559
- <td>${templateUtilities.extractObservationValueUnit(obs)}</td>
2560
- <td>${templateUtilities.firstFromCodeableConceptList(obs.interpretation)}</td>
2561
2937
  <td>${templateUtilities.concatReferenceRange(obs.referenceRange)}</td>
2562
- <td>${templateUtilities.renderNotes(obs.note, timezone)}</td>
2563
2938
  <td>${obs.effectiveDateTime ? templateUtilities.renderTime(obs.effectiveDateTime, timezone) : obs.effectivePeriod ? templateUtilities.renderPeriod(obs.effectivePeriod, timezone) : ""}</td>
2564
2939
  </tr>`;
2565
2940
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imranq2/fhirpatientsummary",
3
- "version": "1.0.26",
3
+ "version": "1.0.27",
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",