Hozifa Elgharbawy commited on
Commit
cbe2a8f
·
1 Parent(s): b499f38

feat: Update meal plan model fields to be optional

Browse files
src/common/models/meal-plan.model.ts CHANGED
@@ -24,10 +24,10 @@ const mealPlanSchema = new Schema({
24
  description: { type: String, required: true },
25
  duration: { type: Number, required: true },
26
  level: { type: String, enum: FitnessLevel, required: true },
27
- your_journey: { type: String, required: true },
28
  key_features: [{
29
- title: { type: String, required: true },
30
- description: { type: String, required: true },
31
  }],
32
  days: [
33
  {
 
24
  description: { type: String, required: true },
25
  duration: { type: Number, required: true },
26
  level: { type: String, enum: FitnessLevel, required: true },
27
+ your_journey: { type: String, required: false },
28
  key_features: [{
29
+ title: { type: String, required: false },
30
+ description: { type: String, required: false },
31
  }],
32
  days: [
33
  {
src/lib/services/crud.service.ts CHANGED
@@ -27,10 +27,12 @@ export const CrudService = <ModelDoc extends Document>(
27
 
28
  async updateMany(
29
  filter: FilterQuery<ModelDoc>,
30
- data: AnyKeys<ModelDoc>
 
31
  ): Promise<ModelDoc[]> {
32
  filter = { ...crudOptions?.defaultFilter, ...filter };
33
- await this.existsOrThrow(filter);
 
34
  await this.model.updateMany(filter, data);
35
  return this.model.find(filter);
36
  }
@@ -137,7 +139,7 @@ export const CrudService = <ModelDoc extends Document>(
137
  options?: {
138
  populateArray: any
139
  }): Promise<ModelDoc | null> {
140
- const queryInstruction = this.model.findOne();
141
  if (options?.populateArray) queryInstruction.populate(options.populateArray);
142
  const document = await queryInstruction
143
  return document;
 
27
 
28
  async updateMany(
29
  filter: FilterQuery<ModelDoc>,
30
+ data: AnyKeys<ModelDoc>,
31
+ checkExists: boolean = true
32
  ): Promise<ModelDoc[]> {
33
  filter = { ...crudOptions?.defaultFilter, ...filter };
34
+ if(checkExists)
35
+ await this.existsOrThrow(filter);
36
  await this.model.updateMany(filter, data);
37
  return this.model.find(filter);
38
  }
 
139
  options?: {
140
  populateArray: any
141
  }): Promise<ModelDoc | null> {
142
+ const queryInstruction = this.model.findOne(filter);
143
  if (options?.populateArray) queryInstruction.populate(options.populateArray);
144
  const document = await queryInstruction
145
  return document;
src/modules/users/modules/auth/services/users-auth.service.ts CHANGED
@@ -6,9 +6,11 @@ import { User } from "@common/models/user.model";
6
  import { IUserRegister } from "@common/validations/user-register.validation";
7
  import { CrudService } from "@lib/services/crud.service";
8
  import { WorkoutService } from "../../workouts/services/workouts.service";
 
9
 
10
  export class UsersAuthService extends CrudService(User) {
11
  private workoutsService = new WorkoutService();
 
12
 
13
  async register(createParams: IUserRegister) {
14
  if (createParams.password !== createParams.confirmPassword) {
@@ -16,6 +18,7 @@ export class UsersAuthService extends CrudService(User) {
16
  }
17
  const user = await this.create(createParams);
18
  await this.workoutsService.createModelWorkout(user);
 
19
  return user;
20
  }
21
 
 
6
  import { IUserRegister } from "@common/validations/user-register.validation";
7
  import { CrudService } from "@lib/services/crud.service";
8
  import { WorkoutService } from "../../workouts/services/workouts.service";
9
+ import { MealPlansService } from "../../meal-plans/services/meal-plans.service";
10
 
11
  export class UsersAuthService extends CrudService(User) {
12
  private workoutsService = new WorkoutService();
13
+ private mealPlanService = new MealPlansService();
14
 
15
  async register(createParams: IUserRegister) {
16
  if (createParams.password !== createParams.confirmPassword) {
 
18
  }
19
  const user = await this.create(createParams);
20
  await this.workoutsService.createModelWorkout(user);
21
+ await this.mealPlanService.createModelMealPlan(user);
22
  return user;
23
  }
24
 
src/modules/users/modules/meal-plans/services/meal-plans.service.ts CHANGED
@@ -4,12 +4,13 @@ import { NutritionModel, INParams, INutritionPredictionItem } from "@lib/models/
4
  import { calcAge } from "@lib/utils/age";
5
  import { MealsService } from "../../meals/services/meals.service";
6
  import { UserRegisteredMealPlansService } from "../../user-registered-meal-plans/services/user-registered-meal-plans.service";
 
7
 
8
  export class MealPlansService extends CrudService(MealPlan) {
9
  private mealsService = new MealsService();
10
  private userRegisteredMealPlansService = new UserRegisteredMealPlansService();
11
 
12
- public async createModelMealPlan(user: any) {
13
  let caloriesPerDay = 0;
14
  if (user.gender === "male") {
15
  caloriesPerDay = 10 * user.weight + 6.25 * user.height - 5 * calcAge(user.dob) + 5;
@@ -29,45 +30,36 @@ export class MealPlansService extends CrudService(MealPlan) {
29
 
30
  const mealsNames = pMealPlan.flat().map((meal) => meal.Name);
31
  const meals = await this.mealsService.listAll({ name: { $in: mealsNames } });
32
-
 
 
 
 
33
  const mealPlan = await this.create({
34
  aiGenerated: true,
35
  image: "https://placehold.co/300x400",
36
- description: `AI Generated Meal Plan (${new Date().toLocaleDateString()})`,
 
 
37
  duration: 28,
38
  level: user.fitness_level,
39
- your_journey: `Welcome to your personalized meal plan journey! As someone with a ${user.fitness_level} fitness level, this plan is tailored to meet your specific needs.`,
40
- key_features: [
41
- {
42
- title: "Balanced Nutrition",
43
- description: `Each meal provides a well-balanced mix of nutrients to support your health goals.`,
44
- },
45
- {
46
- title: "Customizable Meals",
47
- description: `Meals can be adjusted to fit your dietary preferences.`,
48
- },
49
- {
50
- title: "Easy to Prepare",
51
- description: `All meals are designed to be quick and easy to prepare.`,
52
- },
53
- {
54
- title: "Variety and Flavor",
55
- description: `Enjoy a diverse range of delicious meals.`,
56
- },
57
- ],
58
  days: pMealPlan.map((day, i) => ({
59
  day_number: i + 1,
60
  meals: day.map((m) => meals.find((meal) => meal.name === m.Name)?._id),
61
  })),
62
  });
 
 
63
 
64
  await this.userRegisteredMealPlansService.createForUser(
65
  {
66
- MealPlan: mealPlan._id,
67
  },
68
  user._id
69
  );
70
 
 
 
71
  return mealPlan;
72
  }
73
  }
 
4
  import { calcAge } from "@lib/utils/age";
5
  import { MealsService } from "../../meals/services/meals.service";
6
  import { UserRegisteredMealPlansService } from "../../user-registered-meal-plans/services/user-registered-meal-plans.service";
7
+ import { UserDocument } from "@common/models/user.model";
8
 
9
  export class MealPlansService extends CrudService(MealPlan) {
10
  private mealsService = new MealsService();
11
  private userRegisteredMealPlansService = new UserRegisteredMealPlansService();
12
 
13
+ public async createModelMealPlan(user: UserDocument) {
14
  let caloriesPerDay = 0;
15
  if (user.gender === "male") {
16
  caloriesPerDay = 10 * user.weight + 6.25 * user.height - 5 * calcAge(user.dob) + 5;
 
30
 
31
  const mealsNames = pMealPlan.flat().map((meal) => meal.Name);
32
  const meals = await this.mealsService.listAll({ name: { $in: mealsNames } });
33
+ const todayDate = new Date().toLocaleDateString('en-US', {
34
+ year: 'numeric',
35
+ month: 'long',
36
+ day: 'numeric'
37
+ });
38
  const mealPlan = await this.create({
39
  aiGenerated: true,
40
  image: "https://placehold.co/300x400",
41
+ description: `This AI-generated meal plan is designed specifically for you, considering your personal fitness goal of ${user.preferences.fitness_goal}.
42
+ Created on ${todayDate}, this plan is tailored to provide a balanced and nutritious diet that supports your workout frequency of ${user.preferences.workout_frequency} times per week.
43
+ Whether you prefer working out on ${user.preferences.preferred_days.join(", ")}, at ${user.preferences.workout_place}, or using ${user.preferences.preferred_equipment.join(", ")}, this meal plan will help you achieve your health and fitness goals. Enjoy a variety of delicious and nutritious meals selected just for you.`,
44
  duration: 28,
45
  level: user.fitness_level,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  days: pMealPlan.map((day, i) => ({
47
  day_number: i + 1,
48
  meals: day.map((m) => meals.find((meal) => meal.name === m.Name)?._id),
49
  })),
50
  });
51
+
52
+
53
 
54
  await this.userRegisteredMealPlansService.createForUser(
55
  {
56
+ mealPlan: mealPlan._id,
57
  },
58
  user._id
59
  );
60
 
61
+
62
+
63
  return mealPlan;
64
  }
65
  }
src/modules/users/modules/user-registered-meal-plans/services/user-registered-meal-plans.service.ts CHANGED
@@ -15,7 +15,7 @@ export class UserRegisteredMealPlansService extends CrudService(UserRegisteredMe
15
  isActive: true,
16
  }, {
17
  isActive: false,
18
- });
19
  }
20
 
21
  async createForUser(data: any, userId: string) {
@@ -24,7 +24,7 @@ export class UserRegisteredMealPlansService extends CrudService(UserRegisteredMe
24
  });
25
 
26
  await this.unregisterCurrentMealPlan(userId);
27
-
28
  return await this.create({
29
  ...data,
30
  user: userId,
@@ -32,25 +32,34 @@ export class UserRegisteredMealPlansService extends CrudService(UserRegisteredMe
32
  isActive: true,
33
  });
34
  }
35
- async updateForUser(mealPlanProps: {urwId: string; dayNumber: number}, data: any, userId: string) {
36
- // find workout
37
- const mealPlan = await this.findOneOrFail({
38
- _id: mealPlanProps.urwId,
39
- user: userId,
40
- });
41
 
 
 
 
 
42
 
43
- // find day
44
- const day = mealPlan.days.find(d => d.day_number === mealPlanProps.dayNumber);
45
- if(!day) throw new HttpError(404, 'Workout Day Not Found');
46
- const dayIndex = mealPlan.days.indexOf(day)
47
-
48
- // update day and week
49
- day.is_eaten = true;
50
- mealPlan.days[dayIndex] = day;
51
-
52
- // save changes
53
- mealPlan.markModified('days');
54
- return mealPlan.save()
55
- }
 
 
 
 
 
 
 
 
 
56
  }
 
15
  isActive: true,
16
  }, {
17
  isActive: false,
18
+ }, false);
19
  }
20
 
21
  async createForUser(data: any, userId: string) {
 
24
  });
25
 
26
  await this.unregisterCurrentMealPlan(userId);
27
+
28
  return await this.create({
29
  ...data,
30
  user: userId,
 
32
  isActive: true,
33
  });
34
  }
35
+ async updateForUser(mealPlanProps: { urwId: string; dayNumber: number }, data: any, userId: string) {
36
+ // find mealPlan
 
 
 
 
37
 
38
+ const mealPlan = await this.findOneOrFail({
39
+ _id: mealPlanProps.urwId,
40
+ user: userId,
41
+ });
42
 
43
+ // find day
44
+ const day = mealPlan.days.find(d => d.day_number === mealPlanProps.dayNumber);
45
+ if (!day) throw new HttpError(404, 'Workout Day Not Found');
46
+ const dayIndex = mealPlan.days.indexOf(day)
47
+
48
+ // update day
49
+ day.is_eaten = true;
50
+ mealPlan.days[dayIndex] = day;
51
+
52
+ // save changes
53
+ mealPlan.markModified('days');
54
+
55
+ const updatedMealPlan = await mealPlan.save();
56
+
57
+ // check if it's the last day
58
+ const lastDay = mealPlan.days[mealPlan.days.length - 1];
59
+ if (lastDay.day_number === mealPlanProps.dayNumber) {
60
+ console.log('This is the last day that was updated.');
61
+ }
62
+
63
+ return updatedMealPlan;
64
+ }
65
  }
src/modules/users/modules/user-registered-workouts/services/user-registered-workouts.service.ts CHANGED
@@ -18,7 +18,7 @@ export class UserRegisteredWorkoutsService extends CrudService(UserRegisteredWor
18
  is_active: true,
19
  }, {
20
  is_active: false,
21
- });
22
  }
23
 
24
  async createForUser(data: ICreateUserRegisteredWorkouts, userId: string) {
 
18
  is_active: true,
19
  }, {
20
  is_active: false,
21
+ }, false);
22
  }
23
 
24
  async createForUser(data: ICreateUserRegisteredWorkouts, userId: string) {
src/modules/users/modules/workouts/services/workouts.service.ts CHANGED
@@ -34,11 +34,15 @@ export class WorkoutService extends CrudService(Workout) {
34
 
35
  const exercisesNames = pworkout.flat().map((e) => e.name);
36
  const exercises = await this.exerciseService.listAll({ name: { $in: exercisesNames } });
37
-
 
 
 
 
38
  const workout = await this.create({
39
  aiGenerated: true,
40
- name: `AI Generated Workout for ${user.name} (${new Date().toLocaleDateString()})`,
41
- description: `AI Generated Workout for ${user.name} (${new Date().toLocaleDateString()})`,
42
  type: "AI Generated",
43
  created_by: user._id,
44
  image: "https://placehold.co/300x400",
 
34
 
35
  const exercisesNames = pworkout.flat().map((e) => e.name);
36
  const exercises = await this.exerciseService.listAll({ name: { $in: exercisesNames } });
37
+ const todayDate = new Date().toLocaleDateString('en-US', {
38
+ year: 'numeric',
39
+ month: 'long',
40
+ day: 'numeric'
41
+ });
42
  const workout = await this.create({
43
  aiGenerated: true,
44
+ name: `AI Generated Workout (${user.preferences.fitness_goal} - ${user.fitness_level}) - ${todayDate}`,
45
+ description: `This AI-generated workout plan, created on ${todayDate}, is tailored for your ${user.fitness_level.toLowerCase()} fitness level and ${user.preferences.fitness_goal.toLowerCase()} goal. It is designed to be performed ${user.preferences.workout_place === WorkoutPlace.GYM ? "at the gym" : "at home"} using your preferred equipment.`,
46
  type: "AI Generated",
47
  created_by: user._id,
48
  image: "https://placehold.co/300x400",
src/resources/meals.json CHANGED
The diff for this file is too large to render. See raw diff
 
src/seeder/seed.ts CHANGED
@@ -13,14 +13,14 @@ import { loadExercisesDataset } from "./helpers/load-exercises-dataset";
13
  import { dbStore } from "./helpers/db-store";
14
  import { loadMealsDataset } from "./helpers/load-meals-dataset";
15
 
16
- const loadDatasets = async() => {
17
  const exercisesDataset = await loadExercisesDataset();
18
  const mealsDataset = loadMealsDataset();
19
  const musclesDataset = Array.from(new Set(exercisesDataset.map((exercise) => exercise.target)));
20
  const equipmentsDataset = Array.from(new Set(exercisesDataset.map((exercise) => exercise.equipment)));
21
- const ingredientsArrays = mealsDataset.map(m=>m.RecipeIngredientParts);
22
  const ingredientsNames = Array.from(new Set(ingredientsArrays.flat()))
23
-
24
  dbStore.excerisesDataset = exercisesDataset;
25
  dbStore.musclesDataset = musclesDataset;
26
  dbStore.equipmentsDataset = equipmentsDataset;
@@ -53,6 +53,7 @@ const main = async () => {
53
  return seederNames.some(name => path.basename(file).includes(name));
54
  })
55
  .sort();
 
56
 
57
  // load datasets
58
  await loadDatasets();
 
13
  import { dbStore } from "./helpers/db-store";
14
  import { loadMealsDataset } from "./helpers/load-meals-dataset";
15
 
16
+ const loadDatasets = async () => {
17
  const exercisesDataset = await loadExercisesDataset();
18
  const mealsDataset = loadMealsDataset();
19
  const musclesDataset = Array.from(new Set(exercisesDataset.map((exercise) => exercise.target)));
20
  const equipmentsDataset = Array.from(new Set(exercisesDataset.map((exercise) => exercise.equipment)));
21
+ const ingredientsArrays = mealsDataset.map(m => m.RecipeIngredientParts);
22
  const ingredientsNames = Array.from(new Set(ingredientsArrays.flat()))
23
+
24
  dbStore.excerisesDataset = exercisesDataset;
25
  dbStore.musclesDataset = musclesDataset;
26
  dbStore.equipmentsDataset = equipmentsDataset;
 
53
  return seederNames.some(name => path.basename(file).includes(name));
54
  })
55
  .sort();
56
+ console.log(seedersFiles);
57
 
58
  // load datasets
59
  await loadDatasets();
src/seeder/seeders/2-users.seeder.ts CHANGED
@@ -1,6 +1,7 @@
1
  import { AuthenticatableType } from "@common/enums/authenticatable-type.enum";
2
  import { FitnessGoal } from "@common/enums/fitness-goal.enum";
3
  import { FitnessLevel } from "@common/enums/fitness-level.enum";
 
4
  import { Injury } from "@common/enums/injury.enum";
5
  import { PreferredDay } from "@common/enums/preferred-day.enum";
6
  import { PreferredEquipment } from "@common/enums/preferred-equipment.enum";
@@ -16,9 +17,9 @@ export default seederWrapper(User, async () => {
16
  email: `user-${i}@app.com`,
17
  password: "password",
18
  image: `https://placehold.co/300x400`,
19
- gender: (i as number % 2 === 0) ?
20
- 'male' :
21
- 'female',
22
  height: 170,
23
  weight: 70,
24
  fitness_level: [FitnessLevel.BEGINNER, FitnessLevel.INTERMEDIATE, FitnessLevel.ADVANCED][i % 3],
 
1
  import { AuthenticatableType } from "@common/enums/authenticatable-type.enum";
2
  import { FitnessGoal } from "@common/enums/fitness-goal.enum";
3
  import { FitnessLevel } from "@common/enums/fitness-level.enum";
4
+ import { Gender } from "@common/enums/gender.enum";
5
  import { Injury } from "@common/enums/injury.enum";
6
  import { PreferredDay } from "@common/enums/preferred-day.enum";
7
  import { PreferredEquipment } from "@common/enums/preferred-equipment.enum";
 
17
  email: `user-${i}@app.com`,
18
  password: "password",
19
  image: `https://placehold.co/300x400`,
20
+ gender: (i as number % 2 === 0) ?
21
+ Gender.MALE :
22
+ Gender.FEMALE,
23
  height: 170,
24
  weight: 70,
25
  fitness_level: [FitnessLevel.BEGINNER, FitnessLevel.INTERMEDIATE, FitnessLevel.ADVANCED][i % 3],
src/seeder/seeders/9-meals.seeder.ts CHANGED
@@ -22,7 +22,7 @@ export default seederWrapper(Meal, async () => {
22
  const data = await Promise.all(dbStore.mealsDataset.map(async (mealJson) => ({
23
  name: mealJson.Name,
24
  created_at: new Date(),
25
- image: `https://placehold.co/300x400`,
26
  ingredients: mealJson.RecipeIngredientParts.map(name => ingredientsIds.find(i => i.name === name)._id),
27
  calories: mealJson.Calories,
28
  carbs: mealJson.CarbohydrateContent,
 
22
  const data = await Promise.all(dbStore.mealsDataset.map(async (mealJson) => ({
23
  name: mealJson.Name,
24
  created_at: new Date(),
25
+ image: mealJson.Images[0],
26
  ingredients: mealJson.RecipeIngredientParts.map(name => ingredientsIds.find(i => i.name === name)._id),
27
  calories: mealJson.Calories,
28
  carbs: mealJson.CarbohydrateContent,