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
This commit is contained in:
385
dist/utils/utils.service.js
vendored
Normal file
385
dist/utils/utils.service.js
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
"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
|
||||
Reference in New Issue
Block a user