fix: potassium/calcium/magnesium unit conversion (g → mg)

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.
This commit is contained in:
Anthony Merlo 2026-08-21 10:10:22 +01:00
parent e8bdea78bb
commit a072c6ba5e
3 changed files with 17 additions and 6 deletions

View file

@ -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#"{

View file

@ -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;
}
}

View file

@ -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));
}