app/lib/models/result.dart
John Mizerek 151106aa8e Initial commit: Add Months MVP
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>
2026-02-20 21:25:00 -08:00

90 lines
2.5 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'enums.dart';
class LifespanDelta {
final int lowMonths;
final int highMonths;
final Confidence confidence;
const LifespanDelta({
required this.lowMonths,
required this.highMonths,
required this.confidence,
});
int get midpointMonths => ((lowMonths + highMonths) / 2).round();
String get rangeDisplay {
if (lowMonths <= 0 && highMonths <= 0) return '0';
if (lowMonths == highMonths) return '$lowMonths';
return '$lowMonths$highMonths';
}
Map<String, dynamic> toJson() => {
'lowMonths': lowMonths,
'highMonths': highMonths,
'confidence': confidence.name,
};
factory LifespanDelta.fromJson(Map<String, dynamic> json) => LifespanDelta(
lowMonths: json['lowMonths'] as int,
highMonths: json['highMonths'] as int,
confidence: Confidence.values.byName(json['confidence'] as String),
);
}
class RankedFactor {
final String behaviorKey;
final String displayName;
final LifespanDelta delta;
const RankedFactor({
required this.behaviorKey,
required this.displayName,
required this.delta,
});
Map<String, dynamic> toJson() => {
'behaviorKey': behaviorKey,
'displayName': displayName,
'delta': delta.toJson(),
};
factory RankedFactor.fromJson(Map<String, dynamic> json) => RankedFactor(
behaviorKey: json['behaviorKey'] as String,
displayName: json['displayName'] as String,
delta: LifespanDelta.fromJson(json['delta'] as Map<String, dynamic>),
);
}
class CalculationResult {
final List<RankedFactor> rankedFactors;
final String modelVersion;
final DateTime calculatedAt;
const CalculationResult({
required this.rankedFactors,
required this.modelVersion,
required this.calculatedAt,
});
RankedFactor? get dominantFactor =>
rankedFactors.isNotEmpty ? rankedFactors.first : null;
RankedFactor? get secondaryFactor =>
rankedFactors.length > 1 ? rankedFactors[1] : null;
Map<String, dynamic> toJson() => {
'rankedFactors': rankedFactors.map((f) => f.toJson()).toList(),
'modelVersion': modelVersion,
'calculatedAt': calculatedAt.toIso8601String(),
};
factory CalculationResult.fromJson(Map<String, dynamic> json) =>
CalculationResult(
rankedFactors: (json['rankedFactors'] as List<dynamic>)
.map((f) => RankedFactor.fromJson(f as Map<String, dynamic>))
.toList(),
modelVersion: json['modelVersion'] as String,
calculatedAt: DateTime.parse(json['calculatedAt'] as String),
);
}