mirror of
https://github.com/kuhyx/diet-guard.git
synced 2026-11-11 22:13:53 +01:00
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to log meals away from the PC, sharing the exact on-disk JSON shape diet_guard already uses (same field names, no translation layer). - lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition, FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/ amount-eaten portion scaling that matches _resolve.resolve_nutrition's semantics exactly. - lib/services/log_storage_service.dart: plain-JSON persistence to food_log.json's exact shape (no sqflite -- the canonical format already is this JSON). - lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy search logic for autocomplete. - lib/screens/: log_meal_screen.dart (single-item logging) and meal_builder_screen.dart (composite multi-item meals, logging full per-component macros via the new components field). Verified end-to-end on a physical device (BL9000): built, installed, logged a real meal through the UI. 77 Flutter tests passing, `flutter analyze` clean against very_good_analysis. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
99 lines
3.1 KiB
Dart
99 lines
3.1 KiB
Dart
/// Per-portion macro estimate, mirroring diet_guard's Python `Nutrition`.
|
|
library;
|
|
|
|
/// Estimated calories and macros for a logged or in-progress portion.
|
|
///
|
|
/// Field names match diet_guard's `_estimator.Nutrition` dataclass exactly
|
|
/// (`kcal`, `proteinG`, `carbsG`, `fatG`, `grams`, `source`) so JSON written
|
|
/// by this app round-trips through the PC app's schema with no translation.
|
|
class Nutrition {
|
|
/// Creates a [Nutrition] from its macro fields and provenance label.
|
|
const Nutrition({
|
|
required this.kcal,
|
|
required this.proteinG,
|
|
required this.carbsG,
|
|
required this.fatG,
|
|
required this.grams,
|
|
required this.source,
|
|
});
|
|
|
|
/// Calories for the portion.
|
|
final double kcal;
|
|
|
|
/// Protein in grams.
|
|
final double proteinG;
|
|
|
|
/// Carbohydrate in grams.
|
|
final double carbsG;
|
|
|
|
/// Fat in grams.
|
|
final double fatG;
|
|
|
|
/// Portion weight in grams (0 when unknown).
|
|
final double grams;
|
|
|
|
/// Where these macros came from (e.g. `"manual"`, `"food bank"`).
|
|
final String source;
|
|
}
|
|
|
|
/// Rescales [nutrition] to a new portion weight in [grams] (pure).
|
|
///
|
|
/// Mirrors `_estimator.scale_nutrition`: a stored or typed macro set
|
|
/// describes *some* basis portion (`nutrition.grams`), and eating a
|
|
/// different amount scales every macro proportionally. When the basis
|
|
/// weight or [grams] is unknown (`<= 0`), there is nothing to scale from,
|
|
/// so the macros are kept and only the recorded weight is updated.
|
|
Nutrition scaleNutrition(Nutrition nutrition, double grams) {
|
|
if (nutrition.grams <= 0 || grams <= 0) {
|
|
return Nutrition(
|
|
kcal: nutrition.kcal,
|
|
proteinG: nutrition.proteinG,
|
|
carbsG: nutrition.carbsG,
|
|
fatG: nutrition.fatG,
|
|
grams: grams > 0 ? grams : nutrition.grams,
|
|
source: nutrition.source,
|
|
);
|
|
}
|
|
final factor = grams / nutrition.grams;
|
|
double scale(double value) =>
|
|
double.parse((value * factor).toStringAsFixed(1));
|
|
return Nutrition(
|
|
kcal: scale(nutrition.kcal),
|
|
proteinG: scale(nutrition.proteinG),
|
|
carbsG: scale(nutrition.carbsG),
|
|
fatG: scale(nutrition.fatG),
|
|
grams: grams,
|
|
source: nutrition.source,
|
|
);
|
|
}
|
|
|
|
/// Builds the eaten-portion [Nutrition] from typed macros that may describe
|
|
/// a different reference weight than what was actually eaten -- e.g. "250
|
|
/// kcal per 100 g, I ate 150 g" -- mirroring `_resolve.resolve_nutrition`'s
|
|
/// manual-macro branch so both apps compute portions identically.
|
|
///
|
|
/// Leaving [perGrams] at 0 means the typed macros already describe the
|
|
/// full eaten portion (the original, pre-scaling behaviour); leaving
|
|
/// [ateGrams] at 0 assumes the eaten amount equals [perGrams].
|
|
Nutrition nutritionForPortion({
|
|
required double kcal,
|
|
required double proteinG,
|
|
required double carbsG,
|
|
required double fatG,
|
|
required double perGrams,
|
|
required double ateGrams,
|
|
required String source,
|
|
}) {
|
|
final referenceGrams = perGrams > 0 ? perGrams : ateGrams;
|
|
final eatenGrams = ateGrams > 0 ? ateGrams : referenceGrams;
|
|
final reference = Nutrition(
|
|
kcal: kcal,
|
|
proteinG: proteinG,
|
|
carbsG: carbsG,
|
|
fatG: fatG,
|
|
grams: referenceGrams,
|
|
source: source,
|
|
);
|
|
return scaleNutrition(reference, eatenGrams);
|
|
}
|