Risk scoring updated file
This commit is contained in:
parent
e9a97a4c07
commit
3793462363
147
.Net Capstone Project/RiskScoringServiceupdatedcode.cs
Normal file
147
.Net Capstone Project/RiskScoringServiceupdatedcode.cs
Normal file
@ -0,0 +1,147 @@
|
||||
using ClinicalInsightsPro.API.Data;
|
||||
using ClinicalInsightsPro.API.DTOs;
|
||||
using ClinicalInsightsPro.API.Models;
|
||||
using ClinicalInsightsPro.API.Services.Interfaces;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ClinicalInsightsPro.API.Services;
|
||||
|
||||
// Questionnaire-based rule engine that scores an encounter as High or Normal
|
||||
// risk, combining chart data (conditions, vitals) with clinician-entered
|
||||
// questionnaire responses. Rule weights are intentionally simple and modular
|
||||
// so they can be extended, or eventually replaced by an ML model, per scope.
|
||||
public class RiskScoringService : IRiskScoringService
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private const int HighRiskThreshold = 50;
|
||||
|
||||
public RiskScoringService(ApplicationDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<RiskAssessmentResultDto> AssessAsync(string patientId, QuestionnaireDto questionnaire)
|
||||
{
|
||||
var patient = await _db.Patients
|
||||
.Include(p => p.Conditions)
|
||||
.Include(p => p.Observations)
|
||||
.Include(p => p.Allergies)
|
||||
.Include(p => p.Medications)
|
||||
.Include(p => p.Encounters)
|
||||
.FirstOrDefaultAsync(p => p.Id == patientId);
|
||||
|
||||
if (patient == null)
|
||||
throw new KeyNotFoundException($"Patient '{patientId}' was not found.");
|
||||
|
||||
int score = 0;
|
||||
var factors = new List<string>();
|
||||
|
||||
foreach (var c in patient.Conditions.Where(c => c.ClinicalStatus == "active"))
|
||||
{
|
||||
switch (c.Code)
|
||||
{
|
||||
case "E11.9":
|
||||
score += 20; factors.Add("Active Type 2 diabetes (+20)"); break;
|
||||
case "I10":
|
||||
score += 15; factors.Add("Essential hypertension (+15)"); break;
|
||||
case "J45.909":
|
||||
score += 10; factors.Add("Asthma (+10)"); break;
|
||||
default:
|
||||
score += 5; factors.Add($"Active condition: {c.Display} (+5)"); break;
|
||||
}
|
||||
}
|
||||
|
||||
var latestSystolic = patient.Observations
|
||||
.Where(o => o.Code == "8480-6")
|
||||
.OrderByDescending(o => o.EffectiveDate)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (latestSystolic != null && latestSystolic.Value >= 160)
|
||||
{
|
||||
score += 20; factors.Add($"Systolic BP {latestSystolic.Value} mmHg — hypertensive urgency range (+20)");
|
||||
}
|
||||
else if (latestSystolic != null && latestSystolic.Value >= 140)
|
||||
{
|
||||
score += 10; factors.Add($"Systolic BP {latestSystolic.Value} mmHg — stage 2 hypertension (+10)");
|
||||
}
|
||||
|
||||
var latestGlucose = patient.Observations
|
||||
.Where(o => o.Code == "2339-0")
|
||||
.OrderByDescending(o => o.EffectiveDate)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (latestGlucose != null && latestGlucose.Value >= 200)
|
||||
{
|
||||
score += 15; factors.Add($"Glucose {latestGlucose.Value} mg/dL — poorly controlled (+15)");
|
||||
}
|
||||
|
||||
// Allergies — a known high-criticality allergy raises the risk of a
|
||||
// future adverse drug event, independent of what's happening today.
|
||||
var highCriticalityAllergies = patient.Allergies.Count(a => a.Criticality == "high");
|
||||
if (highCriticalityAllergies > 0)
|
||||
{
|
||||
var allergyPoints = Math.Min(highCriticalityAllergies * 10, 20);
|
||||
score += allergyPoints;
|
||||
factors.Add($"{highCriticalityAllergies} high-criticality allerg{(highCriticalityAllergies == 1 ? "y" : "ies")} on file (+{allergyPoints})");
|
||||
}
|
||||
|
||||
// Medications — polypharmacy (commonly defined as 5+ concurrent
|
||||
// medications) is itself an established risk factor for interactions
|
||||
// and adverse events, regardless of what the medications are for.
|
||||
var activeMedicationCount = patient.Medications.Count(m => m.Status == "active");
|
||||
if (activeMedicationCount >= 5)
|
||||
{
|
||||
score += 12; factors.Add($"Polypharmacy — {activeMedicationCount} active medications (+12)");
|
||||
}
|
||||
else if (activeMedicationCount >= 3)
|
||||
{
|
||||
score += 6; factors.Add($"{activeMedicationCount} active medications (+6)");
|
||||
}
|
||||
|
||||
// Encounters — frequent recent encounters (visits, admissions, ED
|
||||
// visits) in the last 12 months suggest an unstable or worsening
|
||||
// condition even if no single encounter looks alarming on its own.
|
||||
var recentEncounterCount = patient.Encounters.Count(e => e.EncounterDate >= DateTime.UtcNow.AddMonths(-12));
|
||||
if (recentEncounterCount >= 3)
|
||||
{
|
||||
score += 12; factors.Add($"{recentEncounterCount} encounters within the last 12 months (+12)");
|
||||
}
|
||||
else if (recentEncounterCount == 2)
|
||||
{
|
||||
score += 6; factors.Add("2 encounters within the last 12 months (+6)");
|
||||
}
|
||||
|
||||
if (questionnaire.Smoker) { score += 10; factors.Add("Current smoker (+10)"); }
|
||||
if (questionnaire.FamilyHistoryHeartDisease) { score += 8; factors.Add("Family history of heart disease (+8)"); }
|
||||
if (questionnaire.PriorHospitalization) { score += 12; factors.Add("Prior hospitalization within 12 months (+12)"); }
|
||||
if (questionnaire.Bmi is >= 30) { score += 8; factors.Add($"BMI {questionnaire.Bmi:F1} — obese range (+8)"); }
|
||||
if (questionnaire.PainScore >= 7) { score += 5; factors.Add($"Reported pain score {questionnaire.PainScore}/10 (+5)"); }
|
||||
|
||||
score = Math.Min(score, 100);
|
||||
var level = score >= HighRiskThreshold ? "High" : "Normal";
|
||||
|
||||
if (!factors.Any())
|
||||
factors.Add("No significant risk factors identified from chart or questionnaire.");
|
||||
|
||||
var assessment = new RiskAssessment
|
||||
{
|
||||
PatientId = patientId,
|
||||
Score = score,
|
||||
RiskLevel = level,
|
||||
Rationale = string.Join("; ", factors),
|
||||
AssessedDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_db.RiskAssessments.Add(assessment);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return new RiskAssessmentResultDto
|
||||
{
|
||||
PatientId = patientId,
|
||||
Score = score,
|
||||
RiskLevel = level,
|
||||
ContributingFactors = factors,
|
||||
AssessedDate = assessment.AssessedDate
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user