MIF_E31231313/Android/flutter_app/lib/services/calculation_service.dart

158 lines
3.9 KiB
Dart

import 'dart:developer' as developer;
import 'dart:math';
import '../models/child_model.dart';
import 'data_service.dart';
class CalculationResult {
const CalculationResult({
required this.bbU,
required this.tbU,
required this.bbTb,
required this.adjustedHeightCm,
required this.weightForHeightIndicator,
required this.debugLog,
});
final double bbU;
final double tbU;
final double bbTb;
final double adjustedHeightCm;
final String weightForHeightIndicator;
final String debugLog;
}
class CalculationService {
CalculationService({DataService? dataService})
: _dataService = dataService ?? DataService.instance;
final DataService _dataService;
Future<CalculationResult> calculate(ChildModel child) async {
final gender = child.normalizedGender;
final position = child.normalizedPosition;
final adjustedHeight = child.adjustedHeightCm;
final indicator = child.weightForHeightIndicator;
final wfaLms = await _dataService.getAgeBasedLms(
gender: gender,
indicator: child.weightForAgeIndicator,
ageInMonths: child.ageInMonths,
);
final hfaLms = await _dataService.getAgeBasedLms(
gender: gender,
indicator: child.heightForAgeIndicator,
ageInMonths: child.ageInMonths,
);
final wfhLms = await _dataService.getHeightBasedLms(
gender: gender,
indicator: indicator,
heightCm: adjustedHeight,
);
final bbU = _calculateZScore(
l: wfaLms.l,
m: wfaLms.m,
s: wfaLms.s,
x: child.weightKg,
);
final tbU = _calculateZScore(
l: hfaLms.l,
m: hfaLms.m,
s: hfaLms.s,
x: child.heightCm,
);
final bbTb = _calculateZScore(
l: wfhLms.l,
m: wfhLms.m,
s: wfhLms.s,
x: child.weightKg,
);
final debugLog = _buildDebugLog(
child: child,
gender: gender,
position: position,
indicator: indicator,
adjustedHeight: adjustedHeight,
wfaLms: wfaLms,
hfaLms: hfaLms,
wfhLms: wfhLms,
bbU: bbU,
tbU: tbU,
bbTb: bbTb,
);
developer.log(debugLog, name: 'nutrition.zscore');
return CalculationResult(
bbU: bbU,
tbU: tbU,
bbTb: bbTb,
adjustedHeightCm: adjustedHeight,
weightForHeightIndicator: indicator,
debugLog: debugLog,
);
}
double _calculateZScore({
required double l,
required double m,
required double s,
required double x,
}) {
if (x <= 0 || m <= 0 || s <= 0) {
throw Exception('Nilai LMS atau data pengukuran tidak valid.');
}
final zScore = l.abs() < 1e-7
? log(x / m) / s
: (pow(x / m, l) - 1) / (l * s);
if (zScore.isNaN || zScore.isInfinite) {
throw Exception('Z-score tidak valid.');
}
return zScore.toDouble();
}
String _buildDebugLog({
required ChildModel child,
required String gender,
required String position,
required String indicator,
required double adjustedHeight,
required LmsRecord wfaLms,
required LmsRecord hfaLms,
required LmsRecord wfhLms,
required double bbU,
required double tbU,
required double bbTb,
}) {
String lmsLine(String label, LmsRecord record) {
return '$label LMS: L=${record.l}, M=${record.m}, S=${record.s}';
}
return [
'=== WHO Z-SCORE DEBUG ===',
'Umur (bulan): ${child.ageInMonths}',
'Berat (kg): ${child.weightKg}',
'Tinggi input (cm): ${child.heightCm}',
'Tinggi adjusted (cm): $adjustedHeight',
'Gender input: ${child.gender}',
'Gender normalized: $gender',
'Posisi input: ${child.measurementPosition}',
'Posisi normalized: $position',
'Indikator BB/TB: ${indicator.toUpperCase()}',
lmsLine('WFA', wfaLms),
lmsLine('HFA', hfaLms),
lmsLine(indicator.toUpperCase(), wfhLms),
'Z-score BB/U: $bbU',
'Z-score TB/U: $tbU',
'Z-score BB/TB: $bbTb',
'==========================',
].join('\n');
}
}