From a072c6ba5ef3d0f087a268b97d3d66d814ce7416 Mon Sep 17 00:00:00 2001 From: Anthony Merlo Date: Fri, 21 Aug 2026 10:10:22 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20potassium/calcium/magnesium=20unit=20con?= =?UTF-8?q?version=20(g=20=E2=86=92=20mg)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OFF stores potassium_100g, calcium_100g, magnesium_100g in grams. Goals are in mg. sum_nutriments now multiplies these by 1000. Before: potassium showed 0.7mg (was 0.0007g displayed as mg) After: potassium shows 691.2mg (banana: 0.32g/100g × 216g × 1000) Added MG_NUTRIENTS const to db.rs. Updated test assertions. 31 tests passing. --- src/db.rs | 8 ++++++++ src/tools.rs | 6 ++++-- tests/integration_tests.rs | 9 +++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/db.rs b/src/db.rs index 3289ff9..6681505 100644 --- a/src/db.rs +++ b/src/db.rs @@ -27,6 +27,14 @@ pub const TRACKED_NUTRIENTS: &[(&str, &str)] = &[ ("cholesterol", "cholesterol_100g"), ]; +/// Nutrients that OFF stores in grams but we track in milligrams. +/// When summing, these values are multiplied by 1000 to convert g → mg. +pub const MG_NUTRIENTS: &[&str] = &[ + "potassium_100g", + "calcium_100g", + "magnesium_100g", +]; + /// Default daily goals (gout + hypertension aware). /// Values are in grams or mg as appropriate (matches OFF _100g units). pub const DEFAULT_GOALS_JSON: &str = r#"{ diff --git a/src/tools.rs b/src/tools.rs index 9a30c52..df255bf 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -25,7 +25,7 @@ use sqlx::Row; use crate::api::{OffClient, Product}; use crate::config::Config; -use crate::db::{self, TRACKED_NUTRIENTS}; +use crate::db::{self, TRACKED_NUTRIENTS, MG_NUTRIENTS}; /// The MCP server handler. Each client session gets its own instance, /// but they share the same Config (which contains the DB path — the pool @@ -228,9 +228,11 @@ pub fn sum_nutriments(entries_nutriments: &[Value]) -> Value { for (display_key, tracked_key) in TRACKED_NUTRIENTS { let mut sum = 0.0; let mut has_data = false; + let is_mg = MG_NUTRIENTS.contains(tracked_key); for entry_nutriments in entries_nutriments { if let Some(val) = entry_nutriments.get(tracked_key).and_then(|v| v.as_f64()) { - sum += val; + // OFF stores potassium/calcium/magnesium in grams; convert to mg + sum += if is_mg { val * 1000.0 } else { val }; has_data = true; } } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 48af183..d42f6de 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -238,13 +238,14 @@ mod tests { #[test] fn test_sum_nutriments_partial_data() { - // One entry has potassium, another doesn't — should sum available + // One entry has potassium (in grams, as OFF stores it), another doesn't + // 0.1g = 100mg, 0.05g = 50mg → sum should be 150mg let entries = vec![ - json!({"potassium_100g": 100.0}), - json!({"potassium_100g": 50.0, "energy-kcal_100g": 200.0}), + json!({"potassium_100g": 0.1}), + json!({"potassium_100g": 0.05, "energy-kcal_100g": 200.0}), ]; let totals = tools::sum_nutriments(&entries); - assert_eq!(totals["potassium"], json!(150.0)); + assert_eq!(totals["potassium"], json!(150.0)); // 100mg + 50mg = 150mg assert_eq!(totals["energy_kcal"], json!(200.0)); }