Files
calorie_tracker_2/dist/utils/utils.service.js
Jp f521970a65 build: add TypeScript configuration and generate declaration files
- Add tsconfig.json for TypeScript compilation with declaration and source map generation
- Generate .d.ts declaration files for all modules, services, controllers, and models
- Update package.json with NestJS dependencies and TypeScript development tools
- Include database files in the distribution output for persistence
2026-01-31 09:00:26 +08:00

385 lines
16 KiB
JavaScript

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UtilsService = void 0;
const common_1 = require("@nestjs/common");
const sequelize_1 = require("@nestjs/sequelize");
const sequelize_2 = require("sequelize");
const user_model_1 = require("../models/user.model");
const food_item_model_1 = require("../models/food-item.model");
const meal_model_1 = require("../models/meal.model");
const meal_food_model_1 = require("../models/meal-food.model");
const water_log_model_1 = require("../models/water-log.model");
const weight_log_model_1 = require("../models/weight-log.model");
const meal_plan_model_1 = require("../models/meal-plan.model");
const planned_food_model_1 = require("../models/planned-food.model");
const daily_summary_model_1 = require("../models/daily-summary.model");
let UtilsService = class UtilsService {
constructor(userModel, foodItemModel, mealModel, waterLogModel, weightLogModel, mealPlanModel, plannedFoodModel, dailySummaryModel) {
this.userModel = userModel;
this.foodItemModel = foodItemModel;
this.mealModel = mealModel;
this.waterLogModel = waterLogModel;
this.weightLogModel = weightLogModel;
this.mealPlanModel = mealPlanModel;
this.plannedFoodModel = plannedFoodModel;
this.dailySummaryModel = dailySummaryModel;
}
calculateBMR(weightKg, heightCm, age, gender) {
let bmr;
if (gender.toLowerCase() === 'male') {
bmr = 10 * weightKg + 6.25 * heightCm - 5 * age + 5;
}
else {
bmr = 10 * weightKg + 6.25 * heightCm - 5 * age - 161;
}
return Math.round(bmr);
}
calculateTDEE(bmr, activityLevel) {
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);
}
calculateMacroTargets(weightKg, goalType = 'recomp') {
let protein, carbs, fat;
if (goalType === 'muscle_gain') {
protein = weightKg * 2.4;
carbs = weightKg * 3.5;
fat = weightKg * 1.0;
}
else if (goalType === 'weight_loss') {
protein = weightKg * 2.2;
carbs = weightKg * 2.0;
fat = weightKg * 0.8;
}
else {
protein = weightKg * 2.2;
carbs = weightKg * 2.5;
fat = weightKg * 0.9;
}
return {
protein_g: Math.round(protein),
carbs_g: Math.round(carbs),
fat_g: Math.round(fat),
};
}
async calculateDailyTotals(userId, targetDate = new Date()) {
const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate;
const meals = await this.mealModel.findAll({
where: {
UserId: userId,
date: dateStr,
},
include: [
{
model: meal_food_model_1.MealFood,
include: [food_item_model_1.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) {
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 calculateWaterTotal(userId, targetDate = new Date()) {
const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate;
const waterLogs = await this.waterLogModel.findAll({
where: {
UserId: 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 getWeightTrend(userId, days = 7) {
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 this.weightLogModel.findAll({
where: {
UserId: userId,
date: {
[sequelize_2.Op.between]: [startDateStr, endDateStr],
},
},
order: [['date', 'ASC']],
});
return weightLogs.map((log) => ({
date: log.date,
weight_kg: log.weight_kg,
}));
}
async getCalorieTrend(userId, days = 7) {
const endDate = new Date();
const startDate = new Date();
startDate.setDate(endDate.getDate() - (days - 1));
const trend = [];
const currentDate = new Date(startDate);
while (currentDate <= endDate) {
const dateStr = currentDate.toISOString().split('T')[0];
const totals = await this.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 updateDailySummary(userId, targetDate = new Date()) {
const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate;
const nutrition = await this.calculateDailyTotals(userId, dateStr);
const water = await this.calculateWaterTotal(userId, dateStr);
const weightLog = await this.weightLogModel.findOne({
where: {
UserId: userId,
date: dateStr,
},
});
const weight = weightLog ? weightLog.weight_kg : null;
const user = await this.userModel.findByPk(userId);
const targetCalories = user ? user.target_daily_calories : 2000;
let summary = await this.dailySummaryModel.findOne({
where: {
UserId: userId,
date: dateStr,
},
});
if (!summary) {
summary = await this.dailySummaryModel.create({
UserId: userId,
date: dateStr,
});
}
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;
}
}
getMacroPercentages(proteinG, carbsG, fatG) {
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),
};
}
suggestFoodsForMacros(remainingProtein, remainingCarbs, remainingFat) {
const suggestions = [];
if (remainingProtein > 30) {
suggestions.push({
category: 'High Protein Ulam',
examples: ['Grilled Tilapia', 'Chicken Tinola', 'Grilled Chicken'],
});
}
if (remainingCarbs > 40) {
suggestions.push({
category: 'Carbs',
examples: ['White Rice', 'Pandesal', 'Sweet Potato'],
});
}
if (remainingFat > 20) {
suggestions.push({
category: 'Healthy Fats',
examples: ['Sisig', 'Lechon Kawali', 'Bicol Express'],
});
}
if (remainingProtein > 20 && remainingCarbs > 30) {
suggestions.push({
category: 'Balanced Meals',
examples: ['Tapsilog', 'Chicken Adobo with Rice', 'Sinigang'],
});
}
return suggestions;
}
async generateDailyMealPlan(userId, dateStr) {
try {
const user = await this.userModel.findByPk(userId);
const targetCalories = user ? user.target_daily_calories : 2000;
const targets = {
breakfast: Math.round(targetCalories * 0.25),
lunch: Math.round(targetCalories * 0.35),
dinner: Math.round(targetCalories * 0.3),
snack: Math.round(targetCalories * 0.1),
};
const foods = await this.foodItemModel.findAll();
const foodByCat = {
almusal: foods.filter((f) => f.category === 'almusal'),
ulam: foods.filter((f) => ['ulam', 'sabaw'].includes(f.category)),
kanin: foods.filter((f) => f.category === 'kanin'),
gulay: foods.filter((f) => f.category === 'gulay'),
meryenda: foods.filter((f) => f.category === 'meryenda'),
};
const allFoods = foods;
const pickRandom = (arr) => (arr.length > 0 ? arr[Math.floor(Math.random() * arr.length)] : null);
const generateMeal = async (mealType, targetCal) => {
let currentCal = 0;
const selectedFoods = [];
if (mealType === 'breakfast') {
const main = pickRandom(foodByCat.almusal) || pickRandom(allFoods);
if (main) {
selectedFoods.push({ food: main, qty: 1 });
currentCal += main.calories;
}
}
else if (['lunch', 'dinner'].includes(mealType)) {
const rice = pickRandom(foodByCat.kanin);
const ulam = pickRandom(foodByCat.ulam) || pickRandom(allFoods);
const gulay = pickRandom(foodByCat.gulay);
if (rice) {
selectedFoods.push({ food: rice, qty: 1 });
currentCal += rice.calories;
}
if (ulam) {
selectedFoods.push({ food: ulam, qty: 1 });
currentCal += ulam.calories;
}
if (gulay && currentCal < targetCal) {
selectedFoods.push({ food: gulay, qty: 1 });
currentCal += gulay.calories;
}
}
else if (mealType === 'snack') {
const snack = pickRandom(foodByCat.meryenda) || pickRandom(allFoods);
if (snack) {
selectedFoods.push({ food: snack, qty: 1 });
currentCal += snack.calories;
}
}
if (currentCal > 0 && selectedFoods.length > 0) {
const mealPlan = await this.mealPlanModel.create({
UserId: userId,
date: dateStr,
meal_type: mealType,
is_completed: false,
});
for (const item of selectedFoods) {
await this.plannedFoodModel.create({
MealPlanId: mealPlan.id,
FoodItemId: item.food.id,
quantity: item.qty,
});
}
}
};
await this.mealPlanModel.destroy({
where: {
UserId: userId,
date: dateStr,
},
});
await generateMeal('breakfast', targets.breakfast);
await generateMeal('lunch', targets.lunch);
await generateMeal('dinner', targets.dinner);
await generateMeal('snack', targets.snack);
return true;
}
catch (error) {
console.error('Error generating meal plan:', error);
throw error;
}
}
};
exports.UtilsService = UtilsService;
exports.UtilsService = UtilsService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, sequelize_1.InjectModel)(user_model_1.User)),
__param(1, (0, sequelize_1.InjectModel)(food_item_model_1.FoodItem)),
__param(2, (0, sequelize_1.InjectModel)(meal_model_1.Meal)),
__param(3, (0, sequelize_1.InjectModel)(water_log_model_1.WaterLog)),
__param(4, (0, sequelize_1.InjectModel)(weight_log_model_1.WeightLog)),
__param(5, (0, sequelize_1.InjectModel)(meal_plan_model_1.MealPlan)),
__param(6, (0, sequelize_1.InjectModel)(planned_food_model_1.PlannedFood)),
__param(7, (0, sequelize_1.InjectModel)(daily_summary_model_1.DailySummary)),
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object, Object, Object])
], UtilsService);
//# sourceMappingURL=utils.service.js.map