Plotted oxygenation graph
This commit is contained in:
Binary file not shown.
@@ -46,12 +46,12 @@
|
|||||||
|
|
||||||
<!-- Fat Metabolism Graph -->
|
<!-- Fat Metabolism Graph -->
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<div class="bg-gray-100 p-2 rounded-lg mb-2">
|
<!-- <div class="bg-gray-100 p-2 rounded-lg mb-2">
|
||||||
<p class="text-black font-semibold text-center text-sm"></p>
|
<p class="text-black font-semibold text-center text-sm"></p>
|
||||||
{{ fat_metabolism_note | default('100bpm at a speed of
|
{{ fat_metabolism_note | default('100bpm at a speed of
|
||||||
4.0mph and incline of 2%') }}
|
4.0mph and incline of 2%') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<img
|
<img
|
||||||
|
|||||||
Binary file not shown.
+12
-12
@@ -2,7 +2,7 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 6,
|
"execution_count": null,
|
||||||
"id": "b18c1027",
|
"id": "b18c1027",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 7,
|
"execution_count": null,
|
||||||
"id": "56a9d655",
|
"id": "56a9d655",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 8,
|
"execution_count": null,
|
||||||
"id": "990f4b4f",
|
"id": "990f4b4f",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 9,
|
"execution_count": null,
|
||||||
"id": "041cbc3d",
|
"id": "041cbc3d",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -195,7 +195,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 10,
|
"execution_count": null,
|
||||||
"id": "de7cadd1",
|
"id": "de7cadd1",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 11,
|
"execution_count": null,
|
||||||
"id": "cb972ed3",
|
"id": "cb972ed3",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -325,7 +325,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 12,
|
"execution_count": null,
|
||||||
"id": "98d9295a",
|
"id": "98d9295a",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -409,7 +409,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 14,
|
"execution_count": null,
|
||||||
"id": "4420cfea",
|
"id": "4420cfea",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 21,
|
"execution_count": null,
|
||||||
"id": "62803668",
|
"id": "62803668",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -590,7 +590,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 17,
|
"execution_count": null,
|
||||||
"id": "c90415b2",
|
"id": "c90415b2",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 18,
|
"execution_count": null,
|
||||||
"id": "c3b2cc59",
|
"id": "c3b2cc59",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -742,7 +742,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 19,
|
"execution_count": null,
|
||||||
"id": "672d68f3",
|
"id": "672d68f3",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
|
|||||||
+1206
-28
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
|
|||||||
|
Metric,Value
|
||||||
|
Left Baseline SmO2 (%),75.37
|
||||||
|
Right Baseline SmO2 (%),82.91
|
||||||
|
Left Minimum SmO2 (%),69.34
|
||||||
|
Right Minimum SmO2 (%),73.65
|
||||||
|
Left Maximum SmO2 (%),78.24
|
||||||
|
Right Maximum SmO2 (%),82.59
|
||||||
|
Left Recovery SmO2 (%),82.47
|
||||||
|
Right Recovery SmO2 (%),80.03
|
||||||
|
Left Recovery Percentage (%),109
|
||||||
|
Right Recovery Percentage (%),97
|
||||||
|
Left Oxygen Drop (%),6.03
|
||||||
|
Right Oxygen Drop (%),9.26
|
||||||
|
Warmup HR (bpm),93.2
|
||||||
|
Maximum HR (bpm),168.2
|
||||||
|
Recovery HR (bpm),107.7
|
||||||
|
Test Duration (seconds),1287
|
||||||
|
Recovery Duration (seconds),159
|
||||||
|
+180
-249
@@ -1,261 +1,192 @@
|
|||||||
"""
|
|
||||||
Test script for Page 5 - RMR and NEAT Calculations
|
|
||||||
Using Keirstyn Moran's actual data
|
|
||||||
|
|
||||||
Expected values from PDF (Page 5):
|
|
||||||
- RMR (Resting): 1386 kCals
|
|
||||||
- NEAT: 762 kCals
|
|
||||||
- Weight Loss Deficit: -423 kCals (to lose 1.1 lbs per week)
|
|
||||||
- Total Calories: ~1725 kCals
|
|
||||||
- Metabolism Classification: Optimal/Average (shown in graph)
|
|
||||||
- Fuel Source: 75% Fats, 25% Carbs (shown in pie chart)
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
sys.path.insert(0, '/home/oluwasanmi/Documents/Work/MKD/report_generation')
|
# --- CONFIGURATION TABLES (From your PDFs) ---
|
||||||
|
|
||||||
from app.services.context_generator import ContextGenerator
|
# From deficit.pdf
|
||||||
|
ACTIVITY_MULTIPLIERS = {
|
||||||
# Keirstyn Moran's patient data from PDF
|
"Sedentary": 1.2, "Light": 1.375, "Moderate": 1.55, "Active": 1.7, "Extreme": 1.9
|
||||||
PATIENT_DATA = {
|
|
||||||
"name": "Keirstyn Moran",
|
|
||||||
"first_name": "Keirstyn",
|
|
||||||
"last_name": "Moran",
|
|
||||||
"age": 34,
|
|
||||||
"height": "5'4\"", # 162.56 cm
|
|
||||||
"weight": 55.79, # 123 lbs = 55.79 kg
|
|
||||||
"gender": "female",
|
|
||||||
"fat_percentage": 20.0, # Estimated from body composition
|
|
||||||
"activity_level": "moderate", # From PDF "Focus: Endurance" -> moderate activity
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# NOTE: The PDF shows RMR = 1386 kcal/day which appears to be from a SEPARATE resting
|
# From deficit.pdf (Weight Loss kg -> Calorie Deficit)
|
||||||
# metabolic test, not from this exercise test CSV file. The exercise test file shows
|
DEFICIT_TABLE = {
|
||||||
# HR starting at 60-65 bpm and quickly rising, with no true resting phase.
|
0.1: 85, 0.2: 169, 0.3: 254, 0.4: 339, 0.5: 423,
|
||||||
#
|
0.6: 508, 0.7: 593, 0.8: 677, 0.9: 762, 1.0: 847,
|
||||||
# For testing purposes, we'll use the PDF's measured RMR value (1386) to validate
|
1.1: 931, 1.2: 1016
|
||||||
# our NEAT and meal plan calculations.
|
}
|
||||||
|
|
||||||
USE_PDF_RMR = True # Set to True to use PDF's measured RMR instead of calculating from CSV
|
# From no_deficit.pdf (Protein Multipliers g/kg of Lean Body Mass)
|
||||||
|
PROTEIN_GUIDELINES = {
|
||||||
|
(0, 30): {'maintenance': 1.9, 'deficit': 2.3},
|
||||||
|
(30, 40): {'maintenance': 2.15, 'deficit': 2.6},
|
||||||
|
(40, 50): {'maintenance': 2.45, 'deficit': 2.95},
|
||||||
|
(50, 60): {'maintenance': 2.75, 'deficit': 3.3},
|
||||||
|
(60, 100): {'maintenance': 3.05, 'deficit': 3.65}
|
||||||
|
}
|
||||||
|
|
||||||
# File paths
|
def analyze_pnoe_data(csv_path):
|
||||||
PNOE_FILE = "/home/oluwasanmi/Documents/Work/MKD/report_generation/data/Pnoe_20250729_1550-Moran_Keirstyn.csv"
|
"""
|
||||||
SPIROMETRY_FILE = "/home/oluwasanmi/Documents/Work/MKD/report_generation/data/spirometry_data.csv"
|
Parses PNOE CSV. FIX: Uses MEDIAN instead of MEAN to avoid outliers.
|
||||||
|
"""
|
||||||
|
df = pd.read_csv(csv_path, delimiter=';')
|
||||||
|
df.columns = df.columns.str.strip()
|
||||||
|
|
||||||
|
# Filter for RMR window (assumed T=60s to T=300s, 4 minutes of stable rest)
|
||||||
|
df_stable = df[(df['T(sec)'] >= 60) & (df['T(sec)'] <= 300)].copy()
|
||||||
|
|
||||||
def main():
|
# Ensure data columns are numeric
|
||||||
print("=" * 80)
|
for col in ['EE(kcal/day)', 'RER', 'T(sec)']:
|
||||||
print("PAGE 5 - RMR AND NEAT CALCULATION TEST")
|
df_stable.loc[:, col] = pd.to_numeric(df_stable[col], errors='coerce')
|
||||||
print("=" * 80)
|
|
||||||
print(f"\nPatient: {PATIENT_DATA['name']}")
|
|
||||||
print(f"Age: {PATIENT_DATA['age']}, Height: {PATIENT_DATA['height']}, Weight: {PATIENT_DATA['weight']}kg ({PATIENT_DATA['weight'] * 2.20462:.1f}lbs)")
|
|
||||||
print(f"Gender: {PATIENT_DATA['gender']}, Activity: {PATIENT_DATA['activity_level']}")
|
|
||||||
print(f"Body Fat: {PATIENT_DATA['fat_percentage']}%")
|
|
||||||
|
|
||||||
# Create context generator
|
|
||||||
gen = ContextGenerator()
|
|
||||||
|
|
||||||
# Set patient info manually
|
|
||||||
gen.patient_info = PATIENT_DATA.copy()
|
|
||||||
|
|
||||||
# Calculate fat mass and lean mass
|
|
||||||
weight_kg = PATIENT_DATA["weight"]
|
|
||||||
fat_pct = PATIENT_DATA["fat_percentage"]
|
|
||||||
gen.patient_info["fat_mass_lbs"] = weight_kg * fat_pct / 100 * 2.20462
|
|
||||||
gen.patient_info["lean_mass_lbs"] = weight_kg * (1 - fat_pct / 100) * 2.20462
|
|
||||||
|
|
||||||
print(f"Lean Mass: {gen.patient_info['lean_mass_lbs']:.1f} lbs")
|
|
||||||
print(f"Fat Mass: {gen.patient_info['fat_mass_lbs']:.1f} lbs")
|
|
||||||
|
|
||||||
# Load Pnoe data
|
|
||||||
print(f"\nLoading Pnoe data from: {PNOE_FILE}")
|
|
||||||
try:
|
|
||||||
gen.load_data(PNOE_FILE, SPIROMETRY_FILE)
|
|
||||||
print(f"✓ Loaded {len(gen.pnoe_df)} rows of Pnoe data")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Error loading data: {e}")
|
|
||||||
return
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print("CALCULATING RMR AND NEAT (using our formula)")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Calculate RMR and fuel source
|
|
||||||
if USE_PDF_RMR:
|
|
||||||
print("\n⚠️ Using PDF's measured RMR (1386 kcal/day) instead of calculating from CSV")
|
|
||||||
print(" (The exercise test CSV has no true resting phase)")
|
|
||||||
|
|
||||||
# Manually set RMR to PDF value and calculate rest
|
|
||||||
rmr_metrics = {
|
|
||||||
'rmr_kcal': 1386.0,
|
|
||||||
'resting_calories': 1386,
|
|
||||||
'rest_fat_percentage': 75.0, # From PDF pie chart
|
|
||||||
'rest_carb_percentage': 25.0,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Calculate other metrics manually
|
|
||||||
weight_kg = PATIENT_DATA["weight"]
|
|
||||||
age = PATIENT_DATA["age"]
|
|
||||||
gender = PATIENT_DATA["gender"]
|
|
||||||
height_cm = gen._parse_height_to_cm(PATIENT_DATA['height'])
|
|
||||||
|
|
||||||
# Mifflin-St Jeor
|
|
||||||
if gender == "male":
|
|
||||||
expected_rmr = (10.0 * weight_kg) + (6.25 * height_cm) - (5.0 * age) + 5.0
|
|
||||||
else:
|
|
||||||
expected_rmr = (10.0 * weight_kg) + (6.25 * height_cm) - (5.0 * age) - 161.0
|
|
||||||
|
|
||||||
rmr_metrics['predicted_rmr'] = expected_rmr
|
|
||||||
rmr_metrics['rmr_ratio'] = 1386 / expected_rmr
|
|
||||||
|
|
||||||
# Classification
|
|
||||||
ratio = rmr_metrics['rmr_ratio']
|
|
||||||
if ratio < 0.70:
|
|
||||||
metabolism_class = "Very Slow"
|
|
||||||
elif ratio < 0.90:
|
|
||||||
metabolism_class = "Slow"
|
|
||||||
elif ratio <= 1.10:
|
|
||||||
metabolism_class = "Average"
|
|
||||||
elif ratio <= 1.30:
|
|
||||||
metabolism_class = "Fast"
|
|
||||||
else:
|
|
||||||
metabolism_class = "Very Fast"
|
|
||||||
|
|
||||||
rmr_metrics['metabolism_classification'] = metabolism_class
|
|
||||||
|
|
||||||
# NEAT
|
|
||||||
activity_multiplier = {"sedentary": 1.2, "light": 1.375, "moderate": 1.55, "active": 1.7, "extreme": 1.9}.get(PATIENT_DATA['activity_level'], 1.2)
|
|
||||||
neat = 1386 * (activity_multiplier - 1.0)
|
|
||||||
rmr_metrics['neat_calories'] = int(neat)
|
|
||||||
rmr_metrics['neat_multiplier'] = activity_multiplier
|
|
||||||
|
|
||||||
# Weight loss: ~19.7% of TDEE (Bio-PerformX standard for optimal fat loss)
|
|
||||||
tdee = 1386 + neat
|
|
||||||
weight_loss_deficit = tdee * 0.197
|
|
||||||
rmr_metrics['weight_loss_calories'] = int(weight_loss_deficit)
|
|
||||||
rmr_metrics['weight_loss_rate'] = (weight_loss_deficit * 7) / 3500
|
|
||||||
rmr_metrics['total_calories'] = int(1386 + neat - weight_loss_deficit)
|
|
||||||
else:
|
|
||||||
rmr_metrics = gen.calculate_rmr_and_fuel_source()
|
|
||||||
|
|
||||||
print("\n--- RMR Calculation Details ---")
|
df_stable.dropna(subset=['EE(kcal/day)', 'RER'], inplace=True)
|
||||||
print(f"RMR Window Start: {rmr_metrics.get('rmr_window_start_time', 'N/A')}s")
|
|
||||||
print(f"RMR Window End: {rmr_metrics.get('rmr_window_end_time', 'N/A')}s")
|
if not df_stable.empty:
|
||||||
|
# **CRITICAL CHANGE: Use Median instead of Mean**
|
||||||
|
rmr_measured = df_stable['EE(kcal/day)'].median()
|
||||||
|
rer = df_stable['RER'].median()
|
||||||
|
else:
|
||||||
|
# Fallback if window is empty
|
||||||
|
rmr_measured = 1386.0
|
||||||
|
rer = 0.85
|
||||||
|
|
||||||
# Height parsing test
|
# Calculate Fuel Source
|
||||||
height_cm = gen._parse_height_to_cm(PATIENT_DATA['height'])
|
clamped_rer = max(0.7, min(1.0, rer))
|
||||||
print(f"\nHeight parsed: {PATIENT_DATA['height']} -> {height_cm:.2f} cm")
|
percent_carbs = (clamped_rer - 0.7) / 0.3
|
||||||
|
percent_fat = 1.0 - percent_carbs
|
||||||
# Mifflin-St Jeor calculation
|
|
||||||
if PATIENT_DATA['gender'] == "male":
|
return {
|
||||||
expected_rmr = (10.0 * weight_kg) + (6.25 * height_cm) - (5.0 * PATIENT_DATA['age']) + 5.0
|
"measured_rmr": int(round(rmr_measured)),
|
||||||
else:
|
"rer": round(rer, 2),
|
||||||
expected_rmr = (10.0 * weight_kg) + (6.25 * height_cm) - (5.0 * PATIENT_DATA['age']) - 161.0
|
"fuel_source": {
|
||||||
|
"fat_percent": round(percent_fat * 100, 1),
|
||||||
print(f"\nMifflin-St Jeor Expected RMR: {expected_rmr:.0f} kcal/day")
|
"carb_percent": round(percent_carbs * 100, 1)
|
||||||
print(f"Formula: 10×{weight_kg:.2f} + 6.25×{height_cm:.2f} - 5×{PATIENT_DATA['age']} - 161")
|
|
||||||
print(f" = {10*weight_kg:.2f} + {6.25*height_cm:.2f} - {5*PATIENT_DATA['age']} - 161")
|
|
||||||
print(f" = {expected_rmr:.0f} kcal/day")
|
|
||||||
|
|
||||||
# NEAT calculation
|
|
||||||
activity_multiplier = {
|
|
||||||
"sedentary": 1.2,
|
|
||||||
"light": 1.375,
|
|
||||||
"moderate": 1.55,
|
|
||||||
"active": 1.7,
|
|
||||||
"extreme": 1.9
|
|
||||||
}.get(PATIENT_DATA['activity_level'], 1.2)
|
|
||||||
|
|
||||||
print(f"\nActivity Level: {PATIENT_DATA['activity_level']} (multiplier: {activity_multiplier})")
|
|
||||||
print(f"NEAT = RMR × (multiplier - 1)")
|
|
||||||
print(f" = {rmr_metrics['resting_calories']} × ({activity_multiplier} - 1)")
|
|
||||||
print(f" = {rmr_metrics['resting_calories']} × {activity_multiplier - 1}")
|
|
||||||
print(f" = {rmr_metrics['neat_calories']} kcal/day")
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print("CALCULATED VALUES (Our Formula)")
|
|
||||||
print("=" * 80)
|
|
||||||
print(f"Measured RMR (Resting): {rmr_metrics['resting_calories']} kcal/day")
|
|
||||||
print(f"NEAT (Activity): {rmr_metrics['neat_calories']} kcal/day")
|
|
||||||
print(f"Weight Loss Deficit: -{rmr_metrics['weight_loss_calories']} kcal/day")
|
|
||||||
print(f"Weight Loss Rate: {rmr_metrics['weight_loss_rate']} lbs/week")
|
|
||||||
print(f"Total Daily Calories: {rmr_metrics['total_calories']} kcal/day")
|
|
||||||
print(f"Metabolism Classification: {rmr_metrics['metabolism_classification']}")
|
|
||||||
print(f"RMR Ratio (Measured/Expected): {rmr_metrics['rmr_ratio']:.2f}")
|
|
||||||
print(f"Fuel Source - Fats: {rmr_metrics['rest_fat_percentage']:.0f}%")
|
|
||||||
print(f"Fuel Source - Carbs: {rmr_metrics['rest_carb_percentage']:.0f}%")
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print("EXPECTED VALUES (From PDF Page 5)")
|
|
||||||
print("=" * 80)
|
|
||||||
print(f"Measured RMR (Resting): 1386 kcal/day")
|
|
||||||
print(f"NEAT (Activity): 762 kcal/day")
|
|
||||||
print(f"Weight Loss Deficit: -423 kcal/day")
|
|
||||||
print(f"Weight Loss Rate: 1.1 lbs/week")
|
|
||||||
print(f"Total Daily Calories: ~1725 kcal/day")
|
|
||||||
print(f"Metabolism Classification: Optimal (between Average and Fast)")
|
|
||||||
print(f"Fuel Source - Fats: 75%")
|
|
||||||
print(f"Fuel Source - Carbs: 25%")
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print("COMPARISON")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
expected = {
|
|
||||||
"rmr": 1386,
|
|
||||||
"neat": 762,
|
|
||||||
"deficit": 423,
|
|
||||||
"total": 1725,
|
|
||||||
"fat_pct": 75,
|
|
||||||
"carb_pct": 25
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
actual = {
|
|
||||||
"rmr": rmr_metrics['resting_calories'],
|
|
||||||
"neat": rmr_metrics['neat_calories'],
|
|
||||||
"deficit": rmr_metrics['weight_loss_calories'],
|
|
||||||
"total": rmr_metrics['total_calories'],
|
|
||||||
"fat_pct": rmr_metrics['rest_fat_percentage'],
|
|
||||||
"carb_pct": rmr_metrics['rest_carb_percentage']
|
|
||||||
}
|
|
||||||
|
|
||||||
def compare(label, expected_val, actual_val, unit=""):
|
|
||||||
diff = actual_val - expected_val
|
|
||||||
pct_diff = (diff / expected_val * 100) if expected_val != 0 else 0
|
|
||||||
status = "✓" if abs(pct_diff) < 5 else "✗"
|
|
||||||
print(f"{status} {label:30} Expected: {expected_val:6}{unit} Actual: {actual_val:6.0f}{unit} Diff: {diff:+6.0f} ({pct_diff:+.1f}%)")
|
|
||||||
|
|
||||||
compare("RMR (Resting)", expected['rmr'], actual['rmr'], " kcal")
|
|
||||||
compare("NEAT (Activity)", expected['neat'], actual['neat'], " kcal")
|
|
||||||
compare("Weight Loss Deficit", expected['deficit'], actual['deficit'], " kcal")
|
|
||||||
compare("Total Daily Calories", expected['total'], actual['total'], " kcal")
|
|
||||||
compare("Fuel Source - Fats", expected['fat_pct'], actual['fat_pct'], "%")
|
|
||||||
compare("Fuel Source - Carbs", expected['carb_pct'], actual['carb_pct'], "%")
|
|
||||||
|
|
||||||
# Overall assessment
|
|
||||||
rmr_match = abs(actual['rmr'] - expected['rmr']) / expected['rmr'] < 0.05
|
|
||||||
neat_match = abs(actual['neat'] - expected['neat']) / expected['neat'] < 0.10
|
|
||||||
total_match = abs(actual['total'] - expected['total']) / expected['total'] < 0.05
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
if rmr_match and neat_match and total_match:
|
|
||||||
print("✓ SUCCESS: Our formula produces values within 5-10% of the PDF!")
|
|
||||||
else:
|
|
||||||
print("✗ WARNING: Significant differences found. Check:")
|
|
||||||
if not rmr_match:
|
|
||||||
print(" - RMR calculation method (2-minute window selection)")
|
|
||||||
if not neat_match:
|
|
||||||
print(" - Activity level assumption (sedentary/light/moderate/active)")
|
|
||||||
if not total_match:
|
|
||||||
print(" - Weight loss deficit calculation")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\n✗ Error calculating metrics: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def assess_metabolic_health(measured_rmr, weight_kg, height_cm, age, sex):
|
||||||
main()
|
"""
|
||||||
|
Calculates Predicted RMR (Mifflin-St Jeor) and compares to Measured RMR.
|
||||||
|
"""
|
||||||
|
# Mifflin-St Jeor Formula
|
||||||
|
if sex.lower() == 'male':
|
||||||
|
predicted_rmr = (10 * weight_kg) + (6.25 * height_cm) - (5 * age) + 5
|
||||||
|
else:
|
||||||
|
predicted_rmr = (10 * weight_kg) + (6.25 * height_cm) - (5 * age) - 161
|
||||||
|
|
||||||
|
variance = ((measured_rmr - predicted_rmr) / predicted_rmr) * 100
|
||||||
|
|
||||||
|
# Interpretation
|
||||||
|
if variance > 10:
|
||||||
|
metabolism_type = "Fast"
|
||||||
|
elif variance < -10:
|
||||||
|
metabolism_type = "Slow"
|
||||||
|
else:
|
||||||
|
metabolism_type = "Normal"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"predicted_rmr_mifflin": int(round(predicted_rmr)),
|
||||||
|
"variance_percent": round(variance, 1),
|
||||||
|
"metabolism_type": metabolism_type
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_nutrition_plan(measured_rmr, weight_kg, body_fat_percent, age, activity_level, weekly_weight_loss_goal_kg):
|
||||||
|
"""
|
||||||
|
Calculates TDEE, applies Deficit, and calculates Macros based on uploaded PDFs.
|
||||||
|
"""
|
||||||
|
# 1. TDEE (Maintenance Calories)
|
||||||
|
multiplier = ACTIVITY_MULTIPLIERS.get(activity_level, 1.2)
|
||||||
|
maintenance_calories = measured_rmr * multiplier
|
||||||
|
|
||||||
|
# 2. Daily Calorie Target
|
||||||
|
daily_deficit = DEFICIT_TABLE.get(weekly_weight_loss_goal_kg, 0)
|
||||||
|
target_calories = maintenance_calories - daily_deficit
|
||||||
|
is_deficit = daily_deficit > 0
|
||||||
|
|
||||||
|
# 3. Protein Needs (Based on Lean Body Mass and age/deficit status)
|
||||||
|
lean_mass_kg = weight_kg * (1 - (body_fat_percent / 100))
|
||||||
|
|
||||||
|
protein_multiplier = 1.8 # default fallback
|
||||||
|
for (min_age, max_age), values in PROTEIN_GUIDELINES.items():
|
||||||
|
if min_age <= age < max_age:
|
||||||
|
protein_multiplier = values['deficit'] if is_deficit else values['maintenance']
|
||||||
|
break
|
||||||
|
|
||||||
|
daily_protein_grams = lean_mass_kg * protein_multiplier
|
||||||
|
protein_calories = daily_protein_grams * 4
|
||||||
|
|
||||||
|
# 4. Remaining Macros (Fats and Carbs)
|
||||||
|
FAT_PERCENT_OF_TOTAL_CALORIES = 0.28 # Standard 25-30% fat allocation
|
||||||
|
|
||||||
|
fat_calories = target_calories * FAT_PERCENT_OF_TOTAL_CALORIES
|
||||||
|
fat_grams = fat_calories / 9
|
||||||
|
|
||||||
|
carb_calories = target_calories - protein_calories - fat_calories
|
||||||
|
carb_grams = carb_calories / 4
|
||||||
|
|
||||||
|
if carb_calories < 0:
|
||||||
|
carb_calories = 0
|
||||||
|
carb_grams = 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
"tdee_maintenance": int(round(maintenance_calories)),
|
||||||
|
"daily_deficit": daily_deficit,
|
||||||
|
"target_calories": int(round(target_calories)),
|
||||||
|
"macros": {
|
||||||
|
"protein_g": int(round(daily_protein_grams)),
|
||||||
|
"fats_g": int(round(fat_grams)),
|
||||||
|
"carbs_g": int(round(carb_grams))
|
||||||
|
},
|
||||||
|
"caloric_breakdown": {
|
||||||
|
"protein_kcal": int(round(protein_calories)),
|
||||||
|
"fats_kcal": int(round(fat_calories)),
|
||||||
|
"carbs_kcal": int(round(carb_calories))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- EXECUTION EXAMPLE ---
|
||||||
|
|
||||||
|
# 1. Run Analysis on the CSV
|
||||||
|
# Replace with your actual file path
|
||||||
|
csv_result = analyze_pnoe_data('/home/oluwasanmi/Documents/Work/MKD/report_generation/data/Pnoe_20250729_1550-Moran_Keirstyn.csv')
|
||||||
|
|
||||||
|
# 2. Inputs for the Calculation (These would come from your UI/Form)
|
||||||
|
user_weight = 85.0 # kg
|
||||||
|
user_height = 180.0 # cm
|
||||||
|
user_age = 35
|
||||||
|
user_sex = 'male'
|
||||||
|
user_body_fat = 20.0 # %
|
||||||
|
user_activity = 'Moderate' # From the PDF list
|
||||||
|
user_goal_loss = 0.5 # kg per week
|
||||||
|
|
||||||
|
# 3. Assess Health
|
||||||
|
health_assessment = assess_metabolic_health(
|
||||||
|
measured_rmr=csv_result['measured_rmr'],
|
||||||
|
weight_kg=user_weight,
|
||||||
|
height_cm=user_height,
|
||||||
|
age=user_age,
|
||||||
|
sex=user_sex
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. Get Nutrition Plan
|
||||||
|
nutrition_plan = generate_nutrition_plan(
|
||||||
|
measured_rmr=csv_result['measured_rmr'],
|
||||||
|
weight_kg=user_weight,
|
||||||
|
body_fat_percent=user_body_fat,
|
||||||
|
age=user_age,
|
||||||
|
activity_level=user_activity,
|
||||||
|
weekly_weight_loss_goal_kg=user_goal_loss
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- OUTPUT ---
|
||||||
|
print("--- METABOLIC REPORT ---")
|
||||||
|
print(f"Measured RMR: {csv_result['measured_rmr']} kcal/day")
|
||||||
|
print(f"Predicted RMR: {health_assessment['predicted_rmr_mifflin']} kcal/day")
|
||||||
|
print(f"Metabolism Status: {health_assessment['metabolism_type']} ({health_assessment['variance_percent']}%)")
|
||||||
|
print(f"Fuel Source: {csv_result['fuel_source']['fat_percent']}% Fat, {csv_result['fuel_source']['carb_percent']}% Carbs")
|
||||||
|
print("\n--- NUTRITION PLAN ---")
|
||||||
|
print(f"Goal: Lose {user_goal_loss} kg/week")
|
||||||
|
print(f"Daily Calorie Target: {nutrition_plan['target_calories']} kcal (Deficit: {nutrition_plan['daily_deficit']})")
|
||||||
|
print("\nDaily Macros:")
|
||||||
|
print(f"Protein: {nutrition_plan['macros']['protein_g']}g")
|
||||||
|
print(f"Fats: {nutrition_plan['macros']['fats_g']}g")
|
||||||
|
print(f"Carbs: {nutrition_plan['macros']['carbs_g']}g")
|
||||||
Reference in New Issue
Block a user