MaternAlert / src /risk /detector.ts
Auspicious14's picture
feat(ocr, risk): add patient OCR and risk assessment
27e706d
Raw
History Blame Contribute Delete
9.44 kB
import { PatientDataForRiskAssessment, RiskDetectionResult, RiskFactor } from "./types";
import { RISK_FACTORS, RISK_THRESHOLDS } from "./config";
function checkRedFlags(data: PatientDataForRiskAssessment): { isRedFlag: boolean; reasons: string[] } {
const reasons: string[] = [];
if (data.bloodPressureSystolic && data.bloodPressureDiastolic) {
if (data.bloodPressureSystolic >= 160 || data.bloodPressureDiastolic >= 110) {
reasons.push("Severely elevated blood pressure (≥160/110 mmHg)");
}
} else if (data.bloodPressureSystolic && data.bloodPressureSystolic >= 160) {
reasons.push("Severely elevated systolic blood pressure (≥160 mmHg)");
} else if (data.bloodPressureDiastolic && data.bloodPressureDiastolic >= 110) {
reasons.push("Severely elevated diastolic blood pressure (≥110 mmHg)");
}
if (data.hasConvulsions) {
reasons.push("Convulsions present");
}
if (data.hasLossOfConsciousness) {
reasons.push("Loss of consciousness");
}
if (data.hasHeavyVaginalBleeding) {
reasons.push("Heavy vaginal bleeding");
}
if (data.hasAbsentFetalMovement) {
reasons.push("Absent fetal movement");
}
return { isRedFlag: reasons.length > 0, reasons };
}
function evaluateRiskFactors(data: PatientDataForRiskAssessment): {
contributingFactors: RiskFactor[];
totalScore: number;
reasons: string[];
} {
const contributingFactors: RiskFactor[] = [];
const reasons: string[] = [];
let totalScore = 0;
if (data.age !== undefined && data.age >= 35) {
const factor = RISK_FACTORS.find((f) => f.id === "advanced-maternal-age");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
} else if (data.age !== undefined && data.age < 18) {
const factor = RISK_FACTORS.find((f) => f.id === "teen-pregnancy");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.bmi !== undefined && data.bmi >= 35) {
const factor = RISK_FACTORS.find((f) => f.id === "bmi-over-35");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.parity !== undefined && data.parity >= 5) {
const factor = RISK_FACTORS.find((f) => f.id === "grand-multiparity");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousCaesareans && data.previousCaesareans > 0) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-caesarean");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousStillbirths && data.previousStillbirths > 0) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-stillbirth");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousIufd) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-iufd");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousPreeclampsia) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-preeclampsia");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousEclampsia) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-eclampsia");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.gestationalHypertension) {
const factor = RISK_FACTORS.find((f) => f.id === "gestational-hypertension");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.chronicHypertension || data.chronicHypertensionBeforePregnancy) {
const factor = RISK_FACTORS.find((f) => f.id === "chronic-hypertension");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.diabetesMellitus) {
const factor = RISK_FACTORS.find((f) => f.id === "diabetes-mellitus");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.kidneyDisease) {
const factor = RISK_FACTORS.find((f) => f.id === "kidney-disease");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.heartDisease) {
const factor = RISK_FACTORS.find((f) => f.id === "heart-disease");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousPlacentalAbruption) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-placental-abruption");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousPlacentaPrevia) {
const factor = RISK_FACTORS.find((f) => f.id === "placenta-previa");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousMultiplePregnancy) {
const factor = RISK_FACTORS.find((f) => f.id === "twin-pregnancy");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousGestationalDiabetes) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-gestational-diabetes");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.familyPreeclampsia) {
const factor = RISK_FACTORS.find((f) => f.id === "family-preeclampsia");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.familyHypertension) {
const factor = RISK_FACTORS.find((f) => f.id === "family-hypertension");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousPretermBirth) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-preterm-birth");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousLowBirthWeight) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-low-birth-weight");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.previousPostpartumHemorrhage) {
const factor = RISK_FACTORS.find((f) => f.id === "previous-postpartum-hemorrhage");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.smokingStatus === "CURRENT") {
const factor = RISK_FACTORS.find((f) => f.id === "current-smoking");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
if (data.alcoholUse === "REGULAR") {
const factor = RISK_FACTORS.find((f) => f.id === "current-alcohol");
if (factor) {
contributingFactors.push(factor);
reasons.push(factor.description);
totalScore += factor.points;
}
}
return { contributingFactors, totalScore, reasons };
}
function calculateRiskLevel(score: number): "LOW" | "MODERATE" | "HIGH" | "EMERGENCY" {
for (const threshold of RISK_THRESHOLDS) {
if (threshold.maxPoints === null) {
if (score >= threshold.minPoints) return threshold.level;
} else {
if (score >= threshold.minPoints && score <= threshold.maxPoints) {
return threshold.level;
}
}
}
return "LOW";
}
function collectRecommendations(factors: RiskFactor[]): string[] {
const recommendations = new Set<string>();
factors.forEach((factor) => {
factor.recommendations.forEach((rec) => recommendations.add(rec));
});
return Array.from(recommendations);
}
export function detectRisks(data: PatientDataForRiskAssessment): RiskDetectionResult {
const { isRedFlag, reasons: redFlagReasons } = checkRedFlags(data);
const { contributingFactors, totalScore, reasons: riskFactorReasons } = evaluateRiskFactors(data);
let riskLevel = calculateRiskLevel(totalScore);
const recommendations = collectRecommendations(contributingFactors);
if (isRedFlag) {
riskLevel = "EMERGENCY";
}
return {
riskLevel,
score: totalScore,
reasons: isRedFlag ? redFlagReasons : riskFactorReasons,
contributingFactors,
recommendations,
isRedFlag,
redFlagReasons: isRedFlag ? redFlagReasons : undefined,
};
}