macro_nutrients_calculator/MacroPlanner.js

118 lines
4.2 KiB
JavaScript
Raw Permalink Normal View History

2025-06-30 20:03:47 +02:00
// MacroPlanner.js
// Node.js script skeleton for meal planning + nutritional analysis pipeline using USDA FoodData Central
const fs = require('fs');
const xlsx = require('xlsx');
const axios = require('axios');
require('dotenv').config();
// === CONFIG ===
const EXCEL_FILE = 'plantilla_plan_comidas.xlsx';
const SHEET_NAME = 'Semana 1';
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const USDA_API_KEY = process.env.USDA_API_KEY;
// === STEP 1: Load Excel and parse meals ===
function parseMealsFromExcel(filePath, sheetName) {
const workbook = xlsx.readFile(filePath);
const sheet = workbook.Sheets[sheetName];
const json = xlsx.utils.sheet_to_json(sheet, { defval: '' });
return json.map(row => ({
dia: row['Día'],
desayuno: row['Desayuno'],
comida: row['Comida'],
cena: row['Cena']
}));
}
// === STEP 2: Call ChatGPT to extract ingredients ===
async function getIngredientsFromMeal(description) {
const prompt = `Extrae los ingredientes clave con cantidades aproximadas de esta comida: "${description}". Devuélvelo como lista en JSON con nombre y cantidad.`;
const response = await axios.post('https://api.openai.com/v1/chat/completions', {
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
temperature: 0.2
}, {
headers: { 'Authorization': `Bearer ${OPENAI_API_KEY}` }
});
const text = response.data.choices[0].message.content;
return JSON.parse(text);
}
// === STEP 3: Use USDA API to get nutrient info ===
async function getNutritionFromIngredients(ingredients) {
let totals = { protein: 0, carbs: 0, fat: 0 };
for (const item of ingredients) {
try {
const searchRes = await axios.get(`https://api.nal.usda.gov/fdc/v1/foods/search`, {
params: {
api_key: USDA_API_KEY,
query: item.nombre,
pageSize: 1
}
});
const food = searchRes.data.foods[0];
if (!food || !food.fdcId) continue;
const foodRes = await axios.get(`https://api.nal.usda.gov/fdc/v1/food/${food.fdcId}`, {
params: { api_key: USDA_API_KEY }
});
for (const nutrient of foodRes.data.foodNutrients) {
if (nutrient.nutrientName === 'Protein') totals.protein += nutrient.value;
if (nutrient.nutrientName === 'Carbohydrate, by difference') totals.carbs += nutrient.value;
if (nutrient.nutrientName === 'Total lipid (fat)') totals.fat += nutrient.value;
}
} catch (error) {
console.warn(`Error retrieving data for ingredient: ${item.nombre}`, error.message);
}
}
return totals;
}
// === STEP 4: Combine and write back to Excel ===
async function processAndWriteBack() {
const meals = parseMealsFromExcel(EXCEL_FILE, SHEET_NAME);
const updatedMeals = [];
for (const entry of meals) {
const daySummary = { ...entry, protein: 0, carbs: 0, fat: 0 };
for (const meal of ['desayuno', 'comida', 'cena']) {
if (!entry[meal]) continue;
try {
const ingredients = await getIngredientsFromMeal(entry[meal]);
const macros = await getNutritionFromIngredients(ingredients);
daySummary.protein += macros.protein;
daySummary.carbs += macros.carbs;
daySummary.fat += macros.fat;
} catch (err) {
console.error(`Error processing ${entry.dia} - ${meal}:`, err);
}
}
updatedMeals.push({
'Día': daySummary.dia,
'Desayuno': entry.desayuno,
'Comida': entry.comida,
'Cena': entry.cena,
'Proteínas (g)': daySummary.protein.toFixed(1),
'Carbohidratos (g)': daySummary.carbs.toFixed(1),
'Grasas (g)': daySummary.fat.toFixed(1)
});
}
const newSheet = xlsx.utils.json_to_sheet(updatedMeals);
const newBook = xlsx.utils.book_new();
xlsx.utils.book_append_sheet(newBook, newSheet, SHEET_NAME);
xlsx.writeFile(newBook, 'resultado_macros.xlsx');
console.log('Macros calculados y archivo actualizado: resultado_macros.xlsx');
}
processAndWriteBack();