"""
import csv
+from ntclient import BUFFER_WD
+from ntclient.persistence.sql.usda.funcs import (
+ sql_analyze_foods,
+ sql_nutrients_overview,
+)
+from ntclient.services.analyze import day_format
+from ntclient.services.calculate import calculate_nutrient_totals
from ntclient.utils import CLI_CONFIG
def print_analysis(self, scale: float = 0, scale_mode: str = "kcal") -> None:
"""Run analysis on a single recipe"""
- from ntclient import BUFFER_WD
- from ntclient.persistence.sql.usda.funcs import (
- sql_analyze_foods,
- sql_nutrients_overview,
- )
- from ntclient.services.analyze import day_format
# Get nutrient overview (RDAs, units, etc.)
nutrients_rows = sql_nutrients_overview()
foods_analysis[food_id].append(anl)
# Compute totals
- nutrient_totals = {}
- total_weight = 0.0
- for food_id, grams in self.food_data.items():
- total_weight += grams
- if food_id not in foods_analysis:
- continue
- for _nutrient in foods_analysis[food_id]:
- nutr_id = _nutrient[0]
- nutr_per_100g = _nutrient[1]
- nutr_val = grams / 100 * nutr_per_100g
- if nutr_id not in nutrient_totals:
- nutrient_totals[nutr_id] = nutr_val
- else:
- nutrient_totals[nutr_id] += nutr_val
+ nutrient_totals, total_weight = calculate_nutrient_totals(
+ self.food_data, foods_analysis
+ )
# Print results using day_format for consistency
buffer = BUFFER_WD - 4 if BUFFER_WD > 4 else BUFFER_WD
"""Main SQL persistence module, shared between USDA and NT databases"""
import sqlite3
-from collections.abc import Sequence
+from collections.abc import Sequence # pylint: disable=import-error
from ntclient.utils import CLI_CONFIG
import os
import sqlite3
-from collections.abc import Sequence
+from collections.abc import Sequence # pylint: disable=import-error
from ntclient import (
NT_DB_NAME,
import sqlite3
import tarfile
import urllib.request
-from collections.abc import Sequence
+from collections.abc import Sequence # pylint: disable=import-error
from ntclient import NUTRA_HOME, USDA_DB_NAME, __db_target_usda__
from ntclient.persistence.sql import _sql, version
# Compute totals
nutrients_totals = []
total_grams_list = []
+ from ntclient.services.calculate import calculate_nutrient_totals
+
for log in logs:
- nutrient_totals = OrderedDict() # NOTE: dict()/{} is NOT ORDERED before 3.6/3.7
- daily_grams = 0.0
+ # Aggregate duplicates in log if any
+ food_data: OrderedDict[int, float] = OrderedDict()
for entry in log:
if entry["id"]:
- food_id = int(entry["id"])
- grams = float(entry["grams"])
- daily_grams += grams
- for _nutrient2 in foods_analysis[food_id]:
- nutr_id = _nutrient2[0]
- nutr_per_100g = _nutrient2[1]
- nutr_val = grams / 100 * nutr_per_100g
- if nutr_id not in nutrient_totals:
- nutrient_totals[nutr_id] = nutr_val
- else:
- nutrient_totals[nutr_id] += nutr_val
+ f_id = int(entry["id"])
+ f_grams = float(entry["grams"])
+ if f_id in food_data:
+ food_data[f_id] += f_grams
+ else:
+ food_data[f_id] = f_grams
+
+ nutrient_totals, daily_grams = calculate_nutrient_totals(
+ food_data, foods_analysis
+ )
nutrients_totals.append(nutrient_totals)
total_grams_list.append(daily_grams)
) -> None:
"""Formats day analysis for printing to console"""
- multiplier = 1.0
- if scale:
- if scale_mode == "kcal":
- current_val = analysis.get(NUTR_ID_KCAL, 0)
- multiplier = scale / current_val if current_val else 0
- elif scale_mode == "weight":
- multiplier = scale / total_weight if total_weight else 0
- else:
- # Try to interpret scale_mode as nutrient ID or Name
- target_id = None
- # 1. Check if int
- try:
- target_id = int(scale_mode)
- except ValueError:
- # 2. Check names
- for n_id, n_data in nutrients.items():
- # n_data usually: (id, rda, unit, tag, name, ...)
- # Check tag or desc
- if scale_mode.lower() in str(n_data[3]).lower():
- target_id = n_id
- break
- if scale_mode.lower() in str(n_data[4]).lower():
- target_id = n_id
- break
-
- if target_id and target_id in analysis:
- current_val = analysis[target_id]
- multiplier = scale / current_val if current_val else 0
- else:
- print(f"WARN: Could not scale by '{scale_mode}', nutrient not found.")
+ from ntclient.services.calculate import calculate_scaling_multiplier
+
+ multiplier = calculate_scaling_multiplier(
+ scale, scale_mode, analysis, nutrients, total_weight
+ )
- # Apply multiplier
- if multiplier != 1.0:
- analysis = {k: v * multiplier for k, v in analysis.items()}
+ # Apply multiplier
+ if multiplier != 1.0:
+ analysis = {k: v * multiplier for k, v in analysis.items()}
# Actual values
kcals = round(analysis.get(NUTR_ID_KCAL, 0))
"""
import argparse
import math
+from collections import OrderedDict
+from typing import Mapping
from ntclient.utils import Gender
# calf
round(0.9812 * ankle + 0.1250 * height, 2),
)
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Nutrient Aggregation
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def calculate_nutrient_totals(
+ food_data: Mapping[int, float], foods_analysis: Mapping[int, list]
+) -> tuple[OrderedDict, float]:
+ """
+ Common logic to aggregate nutrient data for a list of foods.
+
+ @param food_data: dict of {food_id: grams, ...}
+ @param foods_analysis: dict of {food_id: [(nutr_id, val_per_100g), ...], ...}
+ @return: (nutrient_totals, total_grams)
+ """
+ nutrient_totals = OrderedDict()
+ total_grams = 0.0
+
+ for food_id, grams in food_data.items():
+ total_grams += grams
+ if food_id not in foods_analysis:
+ continue
+ for _nutrient in foods_analysis[food_id]:
+ nutr_id = _nutrient[0]
+ nutr_per_100g = _nutrient[1]
+ nutr_val = grams / 100 * nutr_per_100g
+ if nutr_id not in nutrient_totals:
+ nutrient_totals[nutr_id] = nutr_val
+ else:
+ nutrient_totals[nutr_id] += nutr_val
+
+ return nutrient_totals, total_grams
+
+
+def calculate_scaling_multiplier(
+ scale: float,
+ scale_mode: str,
+ analysis: Mapping,
+ nutrients: Mapping,
+ total_weight: float,
+) -> float:
+ """
+ Determine the multiplier needed to scale the analysis values.
+ """
+ multiplier = 1.0
+ from ntclient import NUTR_ID_KCAL
+
+ if not scale:
+ return multiplier
+
+ if scale_mode == "kcal":
+ current_val = analysis.get(NUTR_ID_KCAL, 0)
+ multiplier = scale / current_val if current_val else 0
+ elif scale_mode == "weight":
+ multiplier = scale / total_weight if total_weight else 0
+ else:
+ # Try to interpret scale_mode as nutrient ID or Name
+ target_id = None
+ # 1. Check if int
+ try:
+ target_id = int(scale_mode)
+ except ValueError:
+ # 2. Check names
+ for n_id, n_data in nutrients.items():
+ # n_data usually: (id, rda, unit, tag, name, ...)
+ if scale_mode.lower() in str(n_data[3]).lower():
+ target_id = n_id
+ break
+ if scale_mode.lower() in str(n_data[4]).lower():
+ target_id = n_id
+ break
+
+ if target_id and target_id in analysis:
+ current_val = analysis[target_id]
+ multiplier = scale / current_val if current_val else 0
+ else:
+ print(f"WARN: Could not scale by '{scale_mode}', nutrient not found.")
+
+ return multiplier
pytest.xfail("PermissionError, are you using Microsoft Windows?")
# mocks input, could also pass `-y` flag or set yes=True
- usda.input = lambda x: "y" # pylint: disable=redefined-builtin
+ # pylint: disable=redefined-builtin
+ usda.input = lambda x: "y"
code, successful = init()
assert code == 0