initial commit

This commit is contained in:
Jp
2026-01-30 23:32:43 +08:00
commit 3df16ee995
20 changed files with 6405 additions and 0 deletions

340
utils/index.js Normal file
View File

@@ -0,0 +1,340 @@
const db = require('../models');
const { Op } = require('sequelize');
function calculateBMR(weightKg, heightCm, age, gender) {
// Calculate Basal Metabolic Rate using Mifflin-St Jeor Equation
let bmr;
if (gender.toLowerCase() === 'male') {
bmr = (10 * weightKg) + (6.25 * heightCm) - (5 * age) + 5;
} else {
// female
bmr = (10 * weightKg) + (6.25 * heightCm) - (5 * age) - 161;
}
return Math.round(bmr);
}
function calculateTDEE(bmr, activityLevel) {
// Calculate Total Daily Energy Expenditure
const multipliers = {
'sedentary': 1.2,
'light': 1.375,
'moderate': 1.55,
'active': 1.725,
'very_active': 1.9
};
const multiplier = multipliers[activityLevel] || 1.55;
return Math.round(bmr * multiplier);
}
function calculateMacroTargets(weightKg, goalType = 'recomp') {
// Calculate macro targets based on body weight and goal
let protein, carbs, fat;
if (goalType === 'muscle_gain') {
protein = weightKg * 2.4; // High protein for muscle building
carbs = weightKg * 3.5; // Higher carbs for energy
fat = weightKg * 1.0; // Moderate fat
} else if (goalType === 'weight_loss') {
protein = weightKg * 2.2; // High protein to preserve muscle
carbs = weightKg * 2.0; // Lower carbs for deficit
fat = weightKg * 0.8; // Lower fat
} else {
// recomp (body recomposition)
protein = weightKg * 2.2; // High protein
carbs = weightKg * 2.5; // Moderate carbs
fat = weightKg * 0.9; // Moderate fat
}
return {
protein_g: Math.round(protein),
carbs_g: Math.round(carbs),
fat_g: Math.round(fat)
};
}
async function calculateDailyTotals(userId, targetDate = null) {
// Calculate total nutrition consumed for a given date
if (!targetDate) {
targetDate = new Date();
}
// Ensure date is in YYYY-MM-DD format if it's a string, or create date object
const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate;
// Get all meals for the date
const meals = await db.Meal.findAll({
where: {
user_id: userId,
date: dateStr
},
include: [{
model: db.MealFood,
include: [db.FoodItem]
}]
});
const totals = {
calories: 0,
protein: 0,
carbs: 0,
fat: 0,
meals: []
};
for (const meal of meals) {
const mealTotals = {
calories: 0,
protein: 0,
carbs: 0,
fat: 0
};
const mealFoods = [];
for (const mf of meal.MealFoods) {
// Calculate nutrition for this food item based on quantity
// If calculated values exist in MealFood, use them, otherwise calculate
const quantity = mf.quantity || 1.0;
const food = mf.FoodItem;
const calories = mf.calories_consumed || (food.calories * quantity);
const protein = mf.protein_consumed || (food.protein_g * quantity);
const carbs = mf.carbs_consumed || (food.carbs_g * quantity);
const fat = mf.fat_consumed || (food.fat_g * quantity);
mealTotals.calories += calories;
mealTotals.protein += protein;
mealTotals.carbs += carbs;
mealTotals.fat += fat;
mealFoods.push({
name: food.name,
quantity: quantity,
calories: calories
});
}
totals.calories += mealTotals.calories;
totals.protein += mealTotals.protein;
totals.carbs += mealTotals.carbs;
totals.fat += mealTotals.fat;
totals.meals.push({
id: meal.id,
type: meal.meal_type,
time: meal.time ? meal.time.substring(0, 5) : null,
totals: mealTotals,
foods: mealFoods
});
}
return totals;
}
async function calculateWaterTotal(userId, targetDate = null) {
// Calculate total water intake for a given date
if (!targetDate) {
targetDate = new Date();
}
const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate;
const waterLogs = await db.WaterLog.findAll({
where: {
user_id: userId,
date: dateStr
}
});
const total = waterLogs.reduce((sum, log) => sum + log.amount_ml, 0);
return {
total_ml: total,
logs: waterLogs.map(log => ({
id: log.id,
amount_ml: log.amount_ml,
time: log.time ? log.time.substring(0, 5) : null
}))
};
}
async function getWeightTrend(userId, days = 7) {
// Get weight trend for the past N days
const endDate = new Date();
const startDate = new Date();
startDate.setDate(endDate.getDate() - (days - 1));
const startDateStr = startDate.toISOString().split('T')[0];
const endDateStr = endDate.toISOString().split('T')[0];
const weightLogs = await db.WeightLog.findAll({
where: {
user_id: userId,
date: {
[Op.between]: [startDateStr, endDateStr]
}
},
order: [['date', 'ASC']]
});
return weightLogs.map(log => ({
date: log.date,
weight_kg: log.weight_kg
}));
}
async function getCalorieTrend(userId, days = 7) {
// Get calorie intake trend for the past N days
const endDate = new Date();
const startDate = new Date();
startDate.setDate(endDate.getDate() - (days - 1));
const trend = [];
let currentDate = new Date(startDate);
while (currentDate <= endDate) {
const dateStr = currentDate.toISOString().split('T')[0];
const totals = await calculateDailyTotals(userId, dateStr);
trend.push({
date: dateStr,
calories: Math.round(totals.calories),
protein: Math.round(totals.protein),
carbs: Math.round(totals.carbs),
fat: Math.round(totals.fat)
});
currentDate.setDate(currentDate.getDate() + 1);
}
return trend;
}
async function updateDailySummary(userId, targetDate = null) {
// Update or create daily summary for a user
if (!targetDate) {
targetDate = new Date();
}
const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate;
// Calculate totals
const nutrition = await calculateDailyTotals(userId, dateStr);
const water = await calculateWaterTotal(userId, dateStr);
// Get weight for the day
const weightLog = await db.WeightLog.findOne({
where: {
user_id: userId,
date: dateStr
}
});
const weight = weightLog ? weightLog.weight_kg : null;
// Get user's calorie target
const user = await db.User.findByPk(userId);
const targetCalories = user ? user.target_daily_calories : 2000;
// Find or create summary
let summary = await db.DailySummary.findOne({
where: {
user_id: userId,
date: dateStr
}
});
if (!summary) {
summary = await db.DailySummary.create({
user_id: userId,
date: dateStr
});
}
// Update values
summary.total_calories = nutrition.calories;
summary.total_protein_g = nutrition.protein;
summary.total_carbs_g = nutrition.carbs;
summary.total_fat_g = nutrition.fat;
summary.total_water_ml = water.total_ml;
summary.calories_remaining = targetCalories - nutrition.calories;
summary.weight_kg = weight;
try {
await summary.save();
return summary;
} catch (e) {
console.error(`Error updating daily summary: ${e}`);
return null;
}
}
function getMacroPercentages(proteinG, carbsG, fatG) {
// Calculate macro distribution as percentages
const proteinCal = proteinG * 4;
const carbsCal = carbsG * 4;
const fatCal = fatG * 9;
const totalCal = proteinCal + carbsCal + fatCal;
if (totalCal === 0) {
return { protein: 0, carbs: 0, fat: 0 };
}
return {
protein: Math.round((proteinCal / totalCal) * 100),
carbs: Math.round((carbsCal / totalCal) * 100),
fat: Math.round((fatCal / totalCal) * 100)
};
}
function suggestFoodsForMacros(remainingProtein, remainingCarbs, remainingFat) {
// Suggest Filipino foods based on remaining macros
const suggestions = [];
// High protein needed
if (remainingProtein > 30) {
suggestions.push({
category: 'High Protein Ulam',
examples: ['Grilled Tilapia', 'Chicken Tinola', 'Grilled Chicken']
});
}
// High carbs needed
if (remainingCarbs > 40) {
suggestions.push({
category: 'Carbs',
examples: ['White Rice', 'Pandesal', 'Sweet Potato']
});
}
// High fat needed
if (remainingFat > 20) {
suggestions.push({
category: 'Healthy Fats',
examples: ['Sisig', 'Lechon Kawali', 'Bicol Express']
});
}
// Balanced meal needed
if (remainingProtein > 20 && remainingCarbs > 30) {
suggestions.push({
category: 'Balanced Meals',
examples: ['Tapsilog', 'Chicken Adobo with Rice', 'Sinigang']
});
}
return suggestions;
}
module.exports = {
calculateBMR,
calculateTDEE,
calculateMacroTargets,
calculateDailyTotals,
calculateWaterTotal,
getWeightTrend,
getCalorieTrend,
updateDailySummary,
getMacroPercentages,
suggestFoodsForMacros
};