Changes between Version 1 and Version 2 of AdvancedReports
- Timestamp:
- 09/23/26 17:18:11 (11 hours ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
AdvancedReports
v1 v2 6 6 == Doctor performance analytics == 7 7 8 This report identifies which doctors are performing best over a given date range (`report_start`–`report_end` ), combining several independent activity streams into one weighted score:9 10 * Appointment volume and completion rate.11 * Procedure volume, revenue, and how often a performed procedure has a documented result on file.12 * Lab test volume, revenue, and how often an ordered test type has a result available.13 * Referral volume and whether referred patients actually followed up with the receiving doctor.8 This report identifies which doctors are performing best over a given date range (`report_start`–`report_end`, set in the `params` CTE). Every doctor with at least one appointment, performed procedure, ordered lab test, or referral in the range is included; doctors with no activity in the range are left out of the ranking. For each doctor the report shows: 9 10 * Appointment volume, split into completed, cancelled, and open (`SCHEDULED` or `IN_PROGRESS`) appointments, and the completion rate. The completion rate only considers resolved appointments, `COMPLETED` / (`COMPLETED` + `CANCELLED`), so open appointments, including future ones, do not lower it. 11 * Procedure volume, revenue, and the procedure documentation rate: the share of the doctor's performed procedures that have a result recorded in `procedure_results`. 12 * Lab test volume, revenue, and the lab result availability rate: the share of ordered test types that have at least one result in `lab_results`. 13 * Referral volume and the referral follow-up rate is the share of referrals where the patient had a non-cancelled appointment with the receiving doctor after the referral date. 14 14 * Breadth of care, measured as the number of distinct patients touched across procedures, lab tests, and appointments combined. 15 15 16 Six factors feed into one weighted performance_score: appointment completion, 17 procedure documentation, lab result availability, and referral follow-up each 18 contribute a percentage-based rate, while procedure volume and patient breadth 19 contribute capped counts (so one very busy doctor can't dominate the score). Doctors 20 are then ranked with DENSE_RANK(), so tied scores share a rank instead of skipping 21 numbers. 16 Revenue is calculated at list price, as the number of performed procedures or ordered tests multiplied by the `cost` in `procedures` and `lab_tests`; it does not depend on billing or payment status. 17 18 Six factors feed into one weighted performance_score: appointment completion (20%), 19 procedure documentation (20%), lab result availability (15%), and referral follow-up 20 (15%) each contribute a percentage-based rate, while procedure volume (0.30 points 21 per procedure, capped at 50) and patient breadth (0.20 points per patient, capped at 22 100) contribute capped counts, so one very busy doctor can't dominate the score. 23 Revenue, lab test volume, and referral volume are reported for context but do not 24 affect the score. A rate that cannot be calculated (for example, a doctor with no lab 25 tests, or with only open appointments) counts as 0. Doctors are then ranked with 26 DENSE_RANK(), so tied scores share a rank instead of skipping numbers. 27 28 Because `procedure_results` and `lab_results` reference a procedure or test type rather 29 than a specific performed procedure or test, a result counts as documentation for every 30 performance of that procedure or test type. 22 31 23 32 … … 27 36 WITH params AS ( 28 37 SELECT 29 CAST('2026-05-16' AS DATE) AS report_start, 30 CAST('2026-08-26' AS DATE) AS report_end 31 ), 32 33 38 CAST('2025-01-01' AS DATE) AS report_start, 39 CAST('2026-12-31' AS DATE) AS report_end 40 ), 34 41 documented_procedure_ids AS ( 35 42 SELECT DISTINCT procedure_id FROM procedure_results … … 38 45 SELECT DISTINCT test_id FROM lab_results 39 46 ), 40 41 47 doctor_appointments AS ( 42 48 SELECT 43 49 d.doctor_id, d.first_name, d.last_name, d.email_address, 44 50 ds.specialization_name, dept.department_name, 45 COUNT(*) AS total_appointments, 46 COUNT(*) FILTER (WHERE a.status = 'COMPLETED') AS completed_appointments, 47 COUNT(*) FILTER (WHERE a.status = 'CANCELLED') AS cancelled_appointments, 48 ROUND(100.0 * COUNT(*) FILTER (WHERE a.status = 'COMPLETED') / NULLIF(COUNT(*), 0), 2) AS appointment_completion_rate 51 COUNT(a.appointment_id) AS total_appointments, 52 COUNT(a.appointment_id) FILTER (WHERE a.status = 'COMPLETED') AS completed_appointments, 53 COUNT(a.appointment_id) FILTER (WHERE a.status = 'CANCELLED') AS cancelled_appointments, 54 COUNT(a.appointment_id) FILTER (WHERE a.status IN ('SCHEDULED', 'IN_PROGRESS')) AS open_appointments, 55 ROUND(100.0 * COUNT(a.appointment_id) FILTER (WHERE a.status = 'COMPLETED') 56 / NULLIF(COUNT(a.appointment_id) FILTER (WHERE a.status IN ('COMPLETED', 'CANCELLED')), 0), 2) 57 AS appointment_completion_rate 49 58 FROM doctors d 50 JOIN appointments a ON d.doctor_id = a.doctor_id51 59 JOIN doctor_specialization ds ON d.specialization_id = ds.specialization_id 52 60 JOIN departments dept ON d.department_id = dept.department_id 53 JOIN params p ON a.appointment_date >= p.report_start AND a.appointment_date <= p.report_end 61 CROSS JOIN params p 62 LEFT JOIN appointments a 63 ON a.doctor_id = d.doctor_id 64 AND a.appointment_date >= p.report_start 65 AND a.appointment_date <= p.report_end 54 66 GROUP BY d.doctor_id, d.first_name, d.last_name, d.email_address, ds.specialization_name, dept.department_name 55 67 ), 56 57 68 doctor_procedures AS ( 58 69 SELECT … … 61 72 ROUND(AVG(proc.cost)::numeric, 2) AS avg_procedure_cost, 62 73 SUM(proc.cost) AS total_procedure_revenue, 63 ROUND(100.0 * COUNT( DISTINCT dpi.procedure_id) / NULLIF(COUNT(DISTINCT pp.procedure_id), 0), 2) AS procedure_documentation_rate74 ROUND(100.0 * COUNT(dpi.procedure_id) / NULLIF(COUNT(*), 0), 2) AS procedure_documentation_rate 64 75 FROM doctors d 65 76 JOIN performed_procedures pp ON d.doctor_id = pp.doctor_id … … 69 80 GROUP BY d.doctor_id 70 81 ), 71 72 82 doctor_lab_tests AS ( 73 83 SELECT … … 84 94 GROUP BY d.doctor_id 85 95 ), 86 87 96 doctor_referrals AS ( 88 97 SELECT … … 94 103 AND fa.patient_id = mr.patient_id 95 104 AND fa.appointment_date > ref.referral_date 105 AND fa.status <> 'CANCELLED' 96 106 ) THEN ref.referral_id END) / NULLIF(COUNT(DISTINCT ref.referral_id), 0), 2) AS referral_followup_rate 97 107 FROM doctors d … … 101 111 GROUP BY d.doctor_id 102 112 ), 103 104 113 doctor_patient_touchpoints AS ( 105 114 SELECT pp.doctor_id, pp.patient_id FROM performed_procedures pp … … 112 121 JOIN params p ON a.appointment_date >= p.report_start AND a.appointment_date <= p.report_end 113 122 ), 114 115 123 doctor_unique_patients AS ( 116 124 SELECT doctor_id, COUNT(DISTINCT patient_id) AS total_unique_patients … … 118 126 GROUP BY doctor_id 119 127 ), 120 121 128 doctor_scores AS ( 122 129 SELECT 123 130 da.doctor_id, da.first_name, da.last_name, da.email_address, 124 131 da.specialization_name, da.department_name, 125 da.total_appointments, da.completed_appointments, da.cancelled_appointments, da.appointment_completion_rate, 132 da.total_appointments, da.completed_appointments, da.cancelled_appointments, da.open_appointments, 133 COALESCE(da.appointment_completion_rate, 0) AS appointment_completion_rate, 126 134 COALESCE(dp.procedures_performed, 0) AS procedures_performed, 127 135 COALESCE(dp.avg_procedure_cost, 0) AS avg_procedure_cost, … … 149 157 LEFT JOIN doctor_unique_patients dup ON da.doctor_id = dup.doctor_id 150 158 ) 151 152 159 SELECT *, 153 160 DENSE_RANK() OVER (ORDER BY performance_score DESC) AS performance_rank 154 161 FROM doctor_scores 162 WHERE total_appointments > 0 163 OR procedures_performed > 0 164 OR lab_tests_ordered > 0 165 OR referrals_made > 0 155 166 ORDER BY performance_rank, last_name, first_name; 156 167 }}} … … 169 180 email_address := d.email_address; specialization_name := ds.specialization_name; 170 181 department_name := dept.department_name; 171 total_appointments := COUNT(*); 172 completed_appointments := COUNT(*) FILTER (a.status = 'COMPLETED'); 173 cancelled_appointments := COUNT(*) FILTER (a.status = 'CANCELLED'); 174 appointment_completion_rate := ROUND(100.0 * COUNT(*) FILTER (a.status = 'COMPLETED') / COUNT(*), 2) 175 ( 176 σ (a.appointment_date ≥ p.report_start ∧ a.appointment_date ≤ p.report_end) 182 total_appointments := COUNT(a.appointment_id); 183 completed_appointments := COUNT(a.appointment_id) FILTER (a.status = 'COMPLETED'); 184 cancelled_appointments := COUNT(a.appointment_id) FILTER (a.status = 'CANCELLED'); 185 open_appointments := COUNT(a.appointment_id) FILTER (a.status ∈ {'SCHEDULED', 'IN_PROGRESS'}); 186 appointment_completion_rate := ROUND(100.0 * COUNT(a.appointment_id) FILTER (a.status = 'COMPLETED') 187 / COUNT(a.appointment_id) FILTER (a.status ∈ {'COMPLETED', 'CANCELLED'}), 2) 188 ( 177 189 ( 178 190 ( 179 (doctors d ⨝ (d. doctor_id = a.doctor_id) appointments a)180 ⨝ (d. specialization_id = ds.specialization_id) doctor_specialization ds191 (doctors d ⨝ (d.specialization_id = ds.specialization_id) doctor_specialization ds) 192 ⨝ (d.department_id = dept.department_id) departments dept 181 193 ) 182 ⨝ (d.department_id = dept.department_id) departments dept183 194 × Params p 184 195 ) 196 ⟕ (d.doctor_id = a.doctor_id ∧ a.appointment_date ≥ p.report_start ∧ a.appointment_date ≤ p.report_end) appointments a 185 197 ) 186 198 … … 191 203 avg_procedure_cost := ROUND(AVG(proc.cost), 2); 192 204 total_procedure_revenue := SUM(proc.cost); 193 procedure_documentation_rate := ROUND(100.0 * COUNT _DISTINCT(dpi.procedure_id) / COUNT_DISTINCT(pp.procedure_id), 2)205 procedure_documentation_rate := ROUND(100.0 * COUNT(dpi.procedure_id) / COUNT(*), 2) 194 206 ( 195 207 σ (pp.procedure_date ≥ p.report_start ∧ pp.procedure_date ≤ p.report_end) 196 208 ( 197 209 ( 198 (doctors d ⨝ (d.doctor_id = pp.doctor_id) performed_procedures pp) 199 ⨝ (pp.procedure_id = proc.procedure_id) procedures proc 210 ( 211 (doctors d ⨝ (d.doctor_id = pp.doctor_id) performed_procedures pp) 212 ⨝ (pp.procedure_id = proc.procedure_id) procedures proc 213 ) 214 ⟕ (pp.procedure_id = dpi.procedure_id) DocumentedProcedureIds dpi 200 215 ) 201 ⟕ (pp.procedure_id = dpi.procedure_id) DocumentedProcedureIds dpi202 216 × Params p 203 217 ) … … 215 229 ( 216 230 ( 217 (doctors d ⨝ (d.doctor_id = plt.doctor_id) performed_lab_tests plt) 218 ⨝ (plt.test_id = lt.test_id) lab_tests lt 231 ( 232 (doctors d ⨝ (d.doctor_id = plt.doctor_id) performed_lab_tests plt) 233 ⨝ (plt.test_id = lt.test_id) lab_tests lt 234 ) 235 ⟕ (plt.test_id = dti.test_id) DocumentedTestIds dti 219 236 ) 220 ⟕ (plt.test_id = dti.test_id) DocumentedTestIds dti221 237 × Params p 222 238 ) … … 226 242 π_{referral_id} 227 243 ( 228 σ (fa.doctor_id = ref.to_doctor_id ∧ fa.patient_id = mr.patient_id ∧ fa.appointment_date > ref.referral_date) 244 σ (fa.doctor_id = ref.to_doctor_id ∧ fa.patient_id = mr.patient_id 245 ∧ fa.appointment_date > ref.referral_date ∧ fa.status ≠ 'CANCELLED') 229 246 ( 230 247 (referrals ref ⨝ (ref.record_id = mr.record_id) medical_records mr) … … 242 259 ( 243 260 ( 244 (doctors d ⨝ (d.doctor_id = ref.from_doctor_id) referrals ref) 245 ⨝ (ref.record_id = mr.record_id) medical_records mr 261 ( 262 (doctors d ⨝ (d.doctor_id = ref.from_doctor_id) referrals ref) 263 ⨝ (ref.record_id = mr.record_id) medical_records mr 264 ) 265 ⟕ (ref.referral_id = fur.referral_id) FollowedUpReferrals fur 246 266 ) 247 ⟕ (ref.referral_id = fur.referral_id) FollowedUpReferrals fur248 267 × Params p 249 268 ) … … 273 292 π 274 293 doctor_id, first_name, last_name, email_address, specialization_name, department_name, 275 total_appointments, completed_appointments, cancelled_appointments, appointment_completion_rate, 294 total_appointments, completed_appointments, cancelled_appointments, open_appointments, 295 appointment_completion_rate := COALESCE(da.appointment_completion_rate, 0), 276 296 procedures_performed := COALESCE(dp.procedures_performed, 0), 277 297 avg_procedure_cost := COALESCE(dp.avg_procedure_cost, 0), … … 287 307 performance_score := 288 308 ROUND( 289 COALESCE( appointment_completion_rate, 0) * 0.20309 COALESCE(da.appointment_completion_rate, 0) * 0.20 290 310 + COALESCE(dp.procedure_documentation_rate, 0) * 0.20 291 311 + COALESCE(dl.lab_result_availability_rate, 0) * 0.15 … … 306 326 ) 307 327 328 ActiveDoctors ← 329 σ (total_appointments > 0 ∨ procedures_performed > 0 ∨ lab_tests_ordered > 0 ∨ referrals_made > 0) 330 (DoctorScores) 331 308 332 RankedDoctors ← 309 333 rank_dense 310 334 performance_rank := ORDER BY performance_score DESC 311 ( DoctorScores)335 (ActiveDoctors) 312 336 313 337 Result ← … … 324 348 325 349 * Chronic condition burden is the number of distinct diagnoses on file, capped at 10 in scoring so one outlier patient can't dominate the whole composite score, plus the count of severe symptoms. 326 * Allergy severity reflects the worst allergy severity level on file for the patient .327 * A direct medication safety flag counts how many of the patient's currently prescribed medications are linked, via `allergy_prescription_restrictions`, to one of their documented allergies. This is the strongest single signal in the score, worth 10 points per conflict.328 * Polypharmacy tier reflects how many distinct medications the patient currently has on record, bucketed into `LOW_POLYPHARMACY`, `MODERATE_POLYPHARMACY`, or `HIGH_POLYPHARMACY`.329 * Appointment adherence covers the completion rate and days since the last appointment, within the lookback window set by the `:lookback_months` parameter.350 * Allergy severity reflects the worst allergy severity level on file for the patient (`NONE` if the patient has no documented allergies). 351 * A direct medication safety flag counts how many of the patient's prescribed medications are linked, via `allergy_prescription_restrictions`, to one of their documented allergies. It is one of the most heavily weighted signals in the score, worth 10 points per conflicting medication, and is not capped. 352 * Polypharmacy tier reflects how many distinct medications the patient has on record, bucketed into `LOW_POLYPHARMACY`, `MODERATE_POLYPHARMACY`, or `HIGH_POLYPHARMACY`. Prescription records carry no dates or active status, so every medication ever prescribed to the patient is treated as current. 353 * Appointment adherence covers the completion rate of past appointments within the lookback window set by the `:lookback_months` parameter (future scheduled appointments are excluded), and the number of days since the patient's last completed appointment across their full history. Patients with no completed appointment, or none in the last 180 days, receive additional points. 330 354 * Referral burden is the number of referrals in the last 6 months. 331 355 * Age acts as a standard clinical risk modifier, with patients over 65 receiving additional points. 332 356 357 Only the diagnosis count is capped; severe symptoms, medication conflicts, and referrals add points without an upper limit. 333 358 These combine into a `risk_score`, bucketed into `CRITICAL`/`HIGH`/`MODERATE`/`LOW`, and ranked with DENSE_RANK(). 334 359 … … 341 366 CURRENT_DATE - (CAST(:lookback_months AS INTEGER) * INTERVAL '1 month') AS risk_assessment_start 342 367 ), 343 344 368 patient_records AS ( 345 369 SELECT p.patient_id, mr.record_id … … 347 371 LEFT JOIN medical_records mr ON mr.patient_id = p.patient_id 348 372 ), 349 350 351 373 patient_chronic_conditions AS ( 352 374 SELECT … … 361 383 GROUP BY p.patient_id, p.first_name, p.last_name, p.embg, p.date_of_birth 362 384 ), 363 364 365 385 patient_medication_profile AS ( 366 386 SELECT … … 376 396 WHEN COUNT(DISTINCT CASE WHEN a.allergy_severity = 'CRITICAL' THEN pa.allergy_id END) > 0 THEN 'CRITICAL' 377 397 WHEN COUNT(DISTINCT CASE WHEN a.allergy_severity = 'HIGH' THEN pa.allergy_id END) > 0 THEN 'HIGH' 378 WHEN COUNT(DISTINCT pa.allergy_id) > 0 THEN 'MEDIUM' 379 ELSE 'LOW' 398 WHEN COUNT(DISTINCT CASE WHEN a.allergy_severity = 'MEDIUM' THEN pa.allergy_id END) > 0 THEN 'MEDIUM' 399 WHEN COUNT(DISTINCT pa.allergy_id) > 0 THEN 'LOW' 400 ELSE 'NONE' 380 401 END AS max_allergy_severity, 381 COUNT(DISTINCT restr.restriction_id) FILTER (WHERE pmr2.prescription_id IS NOT NULL) 382 AS conflicting_restrictions_on_current_meds 402 COUNT(DISTINCT pmr2.prescription_id) AS conflicting_medications 383 403 FROM patients p 384 404 LEFT JOIN patient_records pr ON pr.patient_id = p.patient_id … … 392 412 GROUP BY p.patient_id 393 413 ), 394 395 414 patient_activity AS ( 396 415 SELECT 397 416 p.patient_id, 398 417 ROUND(100.0 * COUNT(a.appointment_id) FILTER (WHERE a.status = 'COMPLETED') 399 / NULLIF(COUNT(a.appointment_id), 0), 2) AS appointment_completion_rate, 400 CURRENT_DATE - MAX(a.appointment_date) AS days_since_last_appointment 418 / NULLIF(COUNT(a.appointment_id), 0), 2) AS appointment_completion_rate 401 419 FROM patients p 420 CROSS JOIN params prm 402 421 LEFT JOIN appointments a 403 422 ON a.patient_id = p.patient_id 404 AND a.appointment_date >= (SELECT risk_assessment_start FROM params) 423 AND a.appointment_date >= prm.risk_assessment_start 424 AND a.appointment_date <= CURRENT_DATE 405 425 GROUP BY p.patient_id 406 426 ), 407 427 patient_last_visit AS ( 428 SELECT 429 patient_id, 430 CURRENT_DATE - MAX(appointment_date) AS days_since_last_appointment 431 FROM appointments 432 WHERE status = 'COMPLETED' 433 AND appointment_date <= CURRENT_DATE 434 GROUP BY patient_id 435 ), 408 436 patient_referrals AS ( 409 437 SELECT … … 416 444 GROUP BY p.patient_id 417 445 ), 418 419 446 patient_risk_scores AS ( 420 447 SELECT … … 422 449 pcc.chronic_diagnoses_count, pcc.severe_symptoms_count, 423 450 COALESCE(pmp.allergy_count, 0) AS allergy_count, 424 COALESCE(pmp.max_allergy_severity, ' LOW') AS max_allergy_severity,425 COALESCE(pmp.conflicting_ restrictions_on_current_meds, 0) AS conflicting_restrictions_on_current_meds,451 COALESCE(pmp.max_allergy_severity, 'NONE') AS max_allergy_severity, 452 COALESCE(pmp.conflicting_medications, 0) AS conflicting_medications, 426 453 COALESCE(pmp.current_medications, 0) AS current_medications, 427 454 COALESCE(pmp.polypharmacy_status, 'LOW_POLYPHARMACY') AS polypharmacy_status, 428 455 COALESCE(pa.appointment_completion_rate, 100) AS appointment_completion_rate, 429 COALESCE(p a.days_since_last_appointment, 9999) AS days_since_last_appointment,456 COALESCE(plv.days_since_last_appointment, 9999) AS days_since_last_appointment, 430 457 COALESCE(pr.referrals_last_6_months, 0) AS referrals_last_6_months, 431 458 ROUND( 432 459 LEAST(COALESCE(pcc.chronic_diagnoses_count, 0), 10) * 3 433 460 + COALESCE(pcc.severe_symptoms_count, 0) * 4 434 + CASE COALESCE(pmp.max_allergy_severity, ' LOW')461 + CASE COALESCE(pmp.max_allergy_severity, 'NONE') 435 462 WHEN 'CRITICAL' THEN 20 WHEN 'HIGH' THEN 10 WHEN 'MEDIUM' THEN 5 ELSE 0 END 436 463 + CASE COALESCE(pmp.polypharmacy_status, 'LOW_POLYPHARMACY') 437 464 WHEN 'HIGH_POLYPHARMACY' THEN 15 WHEN 'MODERATE_POLYPHARMACY' THEN 8 ELSE 0 END 438 + COALESCE(pmp.conflicting_ restrictions_on_current_meds, 0) * 10465 + COALESCE(pmp.conflicting_medications, 0) * 10 439 466 + (100 - COALESCE(pa.appointment_completion_rate, 100)) * 0.2 440 + CASE WHEN COALESCE(p a.days_since_last_appointment, 9999) > 180 THEN 10 ELSE 0 END467 + CASE WHEN COALESCE(plv.days_since_last_appointment, 9999) > 180 THEN 10 ELSE 0 END 441 468 + COALESCE(pr.referrals_last_6_months, 0) * 2 442 469 + CASE WHEN pcc.age > 65 THEN 10 ELSE 0 END … … 445 472 LEFT JOIN patient_medication_profile pmp ON pmp.patient_id = pcc.patient_id 446 473 LEFT JOIN patient_activity pa ON pa.patient_id = pcc.patient_id 474 LEFT JOIN patient_last_visit plv ON plv.patient_id = pcc.patient_id 447 475 LEFT JOIN patient_referrals pr ON pr.patient_id = pcc.patient_id 448 476 ) 449 450 477 SELECT 451 478 patient_id, first_name, last_name, embg, age, 452 479 chronic_diagnoses_count, severe_symptoms_count, 453 allergy_count, max_allergy_severity, conflicting_ restrictions_on_current_meds,480 allergy_count, max_allergy_severity, conflicting_medications, 454 481 current_medications, polypharmacy_status, 455 482 appointment_completion_rate, days_since_last_appointment, … … 503 530 CASE WHEN COUNT_DISTINCT(CASE WHEN a.allergy_severity = 'CRITICAL' THEN pa.allergy_id END) > 0 THEN 'CRITICAL' 504 531 WHEN COUNT_DISTINCT(CASE WHEN a.allergy_severity = 'HIGH' THEN pa.allergy_id END) > 0 THEN 'HIGH' 505 WHEN COUNT_DISTINCT(pa.allergy_id) > 0 THEN 'MEDIUM' ELSE 'LOW' END; 506 conflicting_restrictions_on_current_meds := 507 COUNT_DISTINCT(restr.restriction_id) FILTER (pmr2.prescription_id IS NOT NULL) 532 WHEN COUNT_DISTINCT(CASE WHEN a.allergy_severity = 'MEDIUM' THEN pa.allergy_id END) > 0 THEN 'MEDIUM' 533 WHEN COUNT_DISTINCT(pa.allergy_id) > 0 THEN 'LOW' 534 ELSE 'NONE' END; 535 conflicting_medications := COUNT_DISTINCT(pmr2.prescription_id) 508 536 ( 509 537 ( … … 511 539 ( 512 540 ( 513 (patients p ⟕ (p.patient_id = pr.patient_id) PatientRecords pr) 514 ⟕ (pr.record_id = pmr.record_id) prescription_medical_records pmr 541 ( 542 (patients p ⟕ (p.patient_id = pr.patient_id) PatientRecords pr) 543 ⟕ (pr.record_id = pmr.record_id) prescription_medical_records pmr 544 ) 545 ⟕ (p.patient_id = pa.patient_id) patient_allergies pa 515 546 ) 516 ⟕ (p .patient_id = pa.patient_id) patient_allergies pa547 ⟕ (pa.allergy_id = a.allergy_id) allergies a 517 548 ) 518 ⟕ ( pa.allergy_id = a.allergy_id) allergies a549 ⟕ (a.allergy_id = apr.allergy_id) allergy_prescription_restrictions apr 519 550 ) 520 ⟕ (a.allergy_id = apr.allergy_id) allergy_prescription_restrictions apr 521 ) 522 ⟕ (apr.restriction_id = restr.restriction_id) prescription_restriction restr 551 ⟕ (apr.restriction_id = restr.restriction_id) prescription_restriction restr 552 ) 523 553 ⟕ (pr.record_id = pmr2.record_id ∧ pmr2.prescription_id = restr.prescription_id) prescription_medical_records pmr2 524 554 ) 525 555 526 FilteredAppointments ← σ (a.appointment_date ≥ p.risk_assessment_start) (appointments a × Params p) 556 FilteredAppointments ← 557 π_{a.appointment_id, a.patient_id, a.status} 558 ( 559 σ (a.appointment_date ≥ p.risk_assessment_start ∧ a.appointment_date ≤ CURRENT_DATE) 560 (appointments a × Params p) 561 ) 527 562 528 563 PatientActivity ← 529 564 γ 530 565 patient_id := p.patient_id; 531 appointment_completion_rate := ROUND(100.0 * COUNT(fa.appointment_id) FILTER (fa.status = 'COMPLETED') / COUNT(fa.appointment_id), 2); 532 days_since_last_appointment := CURRENT_DATE - MAX(fa.appointment_date) 566 appointment_completion_rate := ROUND(100.0 * COUNT(fa.appointment_id) FILTER (fa.status = 'COMPLETED') / COUNT(fa.appointment_id), 2) 533 567 ( 534 568 patients p ⟕ (p.patient_id = fa.patient_id) FilteredAppointments fa 569 ) 570 571 PatientLastVisit ← 572 γ 573 patient_id; 574 days_since_last_appointment := CURRENT_DATE - MAX(appointment_date) 575 ( 576 σ (status = 'COMPLETED' ∧ appointment_date ≤ CURRENT_DATE) (appointments) 535 577 ) 536 578 … … 548 590 patient_id, first_name, last_name, embg, age, chronic_diagnoses_count, severe_symptoms_count, 549 591 allergy_count := COALESCE(pmp.allergy_count, 0), 550 max_allergy_severity := COALESCE(pmp.max_allergy_severity, ' LOW'),551 conflicting_ restrictions_on_current_meds := COALESCE(pmp.conflicting_restrictions_on_current_meds, 0),592 max_allergy_severity := COALESCE(pmp.max_allergy_severity, 'NONE'), 593 conflicting_medications := COALESCE(pmp.conflicting_medications, 0), 552 594 current_medications := COALESCE(pmp.current_medications, 0), 553 595 polypharmacy_status := COALESCE(pmp.polypharmacy_status, 'LOW_POLYPHARMACY'), 554 596 appointment_completion_rate := COALESCE(pa.appointment_completion_rate, 100), 555 days_since_last_appointment := COALESCE(p a.days_since_last_appointment, 9999),597 days_since_last_appointment := COALESCE(plv.days_since_last_appointment, 9999), 556 598 referrals_last_6_months := COALESCE(pr.referrals_last_6_months, 0), 557 599 risk_score := … … 559 601 LEAST(COALESCE(chronic_diagnoses_count, 0), 10) * 3 560 602 + COALESCE(severe_symptoms_count, 0) * 4 561 + CASE COALESCE(pmp.max_allergy_severity, ' LOW')603 + CASE COALESCE(pmp.max_allergy_severity, 'NONE') 562 604 WHEN 'CRITICAL' THEN 20 WHEN 'HIGH' THEN 10 WHEN 'MEDIUM' THEN 5 ELSE 0 END 563 605 + CASE COALESCE(pmp.polypharmacy_status, 'LOW_POLYPHARMACY') 564 606 WHEN 'HIGH_POLYPHARMACY' THEN 15 WHEN 'MODERATE_POLYPHARMACY' THEN 8 ELSE 0 END 565 + COALESCE(pmp.conflicting_ restrictions_on_current_meds, 0) * 10607 + COALESCE(pmp.conflicting_medications, 0) * 10 566 608 + (100 - COALESCE(pa.appointment_completion_rate, 100)) * 0.2 567 + CASE WHEN COALESCE(p a.days_since_last_appointment, 9999) > 180 THEN 10 ELSE 0 END609 + CASE WHEN COALESCE(plv.days_since_last_appointment, 9999) > 180 THEN 10 ELSE 0 END 568 610 + COALESCE(pr.referrals_last_6_months, 0) * 2 569 611 + CASE WHEN age > 65 THEN 10 ELSE 0 END … … 571 613 ( 572 614 ( 573 (PatientChronicConditions pcc 574 ⟕ (pcc.patient_id = pmp.patient_id) PatientMedicationProfile pmp) 575 ⟕ (pcc.patient_id = pa.patient_id) PatientActivity pa 615 ( 616 (PatientChronicConditions pcc 617 ⟕ (pcc.patient_id = pmp.patient_id) PatientMedicationProfile pmp) 618 ⟕ (pcc.patient_id = pa.patient_id) PatientActivity pa 619 ) 620 ⟕ (pcc.patient_id = plv.patient_id) PatientLastVisit plv 576 621 ) 577 622 ⟕ (pcc.patient_id = pr.patient_id) PatientReferrals pr
