Local-first Flutter app that identifies the single behavioral change most likely to extend lifespan using hazard-based modeling. Features: - Risk engine with hazard ratios from meta-analyses - 50 countries mapped to 4 mortality groups - 6 modifiable factors: smoking, alcohol, sleep, activity, driving, work hours - SQLite local storage (no cloud, no accounts) - Muted clinical UI theme - 23 unit tests for risk engine Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
163 lines
3.6 KiB
Dart
163 lines
3.6 KiB
Dart
import '../models/models.dart';
|
|
|
|
/// Hazard ratios based on conservative estimates from meta-analyses.
|
|
/// All HRs are relative to optimal baseline (HR = 1.0).
|
|
|
|
double getSmokingHR(SmokingStatus status, int cigarettesPerDay) {
|
|
switch (status) {
|
|
case SmokingStatus.never:
|
|
return 1.0;
|
|
case SmokingStatus.former:
|
|
return 1.3;
|
|
case SmokingStatus.current:
|
|
if (cigarettesPerDay < 10) return 1.8;
|
|
if (cigarettesPerDay <= 20) return 2.2;
|
|
return 2.8;
|
|
}
|
|
}
|
|
|
|
double getAlcoholHR(AlcoholLevel level) {
|
|
switch (level) {
|
|
case AlcoholLevel.none:
|
|
case AlcoholLevel.light:
|
|
return 1.0;
|
|
case AlcoholLevel.moderate:
|
|
return 1.1;
|
|
case AlcoholLevel.heavy:
|
|
return 1.3;
|
|
case AlcoholLevel.veryHeavy:
|
|
return 1.6;
|
|
}
|
|
}
|
|
|
|
double getSleepHR(double hours, bool consistent) {
|
|
double hr;
|
|
if (hours >= 7 && hours <= 8) {
|
|
hr = 1.0;
|
|
} else if (hours >= 6 && hours < 7) {
|
|
hr = 1.05;
|
|
} else if (hours < 6) {
|
|
hr = 1.15;
|
|
} else {
|
|
// > 8 hours
|
|
hr = 1.10;
|
|
}
|
|
|
|
// Inconsistent sleep schedule adds additional risk
|
|
if (!consistent) {
|
|
hr *= 1.05;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
double getActivityHR(ActivityLevel level) {
|
|
switch (level) {
|
|
case ActivityLevel.high:
|
|
return 1.0;
|
|
case ActivityLevel.moderate:
|
|
return 1.05;
|
|
case ActivityLevel.light:
|
|
return 1.15;
|
|
case ActivityLevel.sedentary:
|
|
return 1.4;
|
|
}
|
|
}
|
|
|
|
double getBmiHR(double bmi) {
|
|
if (bmi >= 18.5 && bmi < 25) return 1.0;
|
|
if (bmi >= 25 && bmi < 30) return 1.1;
|
|
if (bmi >= 30 && bmi < 35) return 1.2;
|
|
if (bmi >= 35 && bmi < 40) return 1.4;
|
|
if (bmi >= 40) return 1.8;
|
|
// Underweight
|
|
return 1.15;
|
|
}
|
|
|
|
double getDrivingHR(DrivingExposure level) {
|
|
switch (level) {
|
|
case DrivingExposure.low:
|
|
return 1.0;
|
|
case DrivingExposure.moderate:
|
|
return 1.02;
|
|
case DrivingExposure.high:
|
|
return 1.04;
|
|
case DrivingExposure.veryHigh:
|
|
return 1.08;
|
|
}
|
|
}
|
|
|
|
double getWorkHoursHR(WorkHoursLevel level) {
|
|
switch (level) {
|
|
case WorkHoursLevel.normal:
|
|
return 1.0;
|
|
case WorkHoursLevel.elevated:
|
|
return 1.05;
|
|
case WorkHoursLevel.high:
|
|
return 1.15;
|
|
case WorkHoursLevel.extreme:
|
|
return 1.3;
|
|
}
|
|
}
|
|
|
|
/// Existing conditions modify baseline mortality but are NOT modifiable.
|
|
double getDiagnosisHR(Set<Diagnosis> diagnoses) {
|
|
double hr = 1.0;
|
|
for (final diagnosis in diagnoses) {
|
|
switch (diagnosis) {
|
|
case Diagnosis.cardiovascular:
|
|
hr *= 1.5;
|
|
break;
|
|
case Diagnosis.diabetes:
|
|
hr *= 1.4;
|
|
break;
|
|
case Diagnosis.cancer:
|
|
hr *= 2.0;
|
|
break;
|
|
case Diagnosis.copd:
|
|
hr *= 1.6;
|
|
break;
|
|
case Diagnosis.hypertension:
|
|
hr *= 1.2;
|
|
break;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/// Confidence levels for each behavior based on evidence quality.
|
|
Confidence getConfidenceForBehavior(String behaviorKey) {
|
|
switch (behaviorKey) {
|
|
case 'smoking':
|
|
case 'alcohol':
|
|
case 'activity':
|
|
return Confidence.high;
|
|
case 'sleep':
|
|
case 'workHours':
|
|
return Confidence.moderate;
|
|
case 'driving':
|
|
return Confidence.emerging;
|
|
default:
|
|
return Confidence.moderate;
|
|
}
|
|
}
|
|
|
|
/// Display names for behaviors.
|
|
String getDisplayName(String behaviorKey) {
|
|
switch (behaviorKey) {
|
|
case 'smoking':
|
|
return 'Smoking';
|
|
case 'alcohol':
|
|
return 'Alcohol Consumption';
|
|
case 'sleep':
|
|
return 'Sleep';
|
|
case 'activity':
|
|
return 'Physical Activity';
|
|
case 'driving':
|
|
return 'Driving Exposure';
|
|
case 'workHours':
|
|
return 'Work Hours';
|
|
default:
|
|
return behaviorKey;
|
|
}
|
|
}
|