-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathseedMealDB.js
320 lines (277 loc) · 11.4 KB
/
seedMealDB.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
// This is untested and may never end
/*
import fetch from 'node-fetch';
import { Recipe, User, Category, RecipeCategory } from './models/TableCreation.js';
import sequelize from './config/db.js';
import { fileURLToPath } from 'url';
// Helper to fetch meals based on a given URL and page number
async function fetchMealsPage(page = 1) {
try {
const response = await fetch(`https://www.themealdb.com/api/json/v1/1/search.php?s=&page=${page}`);
const data = await response.json();
return data.meals || [];
} catch (error) {
console.error(`Error fetching meals on page ${page}:`, error);
return [];
}
}
// Function to fetch all meals across pages
async function fetchAllMeals() {
let meals = [];
let page = 1;
let hasMoreMeals = true;
while (hasMoreMeals) {
console.log(`Fetching page ${page}...`);
const mealsOnPage = await fetchMealsPage(page);
if (mealsOnPage.length > 0) {
meals = meals.concat(mealsOnPage);
page++;
} else {
hasMoreMeals = false;
}
}
return meals;
}
async function seedMealDB() {
try {
// Create a system user for MealDB recipes if it doesn't exist
const [systemUser] = await User.findOrCreate({
where: { username: 'themealdb' },
defaults: {
email: 'api@themealdb.com',
password: 'randompassword123',
bio: 'Official TheMealDB recipes collection',
profileImage: 'https://www.themealdb.com/images/logo-small.png'
}
});
console.log('Fetching all meals...');
const meals = await fetchAllMeals();
let totalRecipes = 0;
for (const meal of meals) {
// Format ingredients and measurements into a list
const ingredients = [];
for (let i = 1; i <= 20; i++) {
const ingredient = meal[`strIngredient${i}`];
const measure = meal[`strMeasure${i}`];
if (ingredient && ingredient.trim()) {
ingredients.push(`${measure ? measure.trim() + ' ' : ''}${ingredient.trim()}`);
}
}
// Estimate cooking time from instructions length
const instructionsLength = meal.strInstructions?.length || 0;
const estimatedTime = Math.max(30, Math.min(180, Math.floor(instructionsLength / 50)));
// Estimate calories (random between 200-800 for demo)
const estimatedCalories = Math.floor(Math.random() * (800 - 200) + 200);
// Create or update recipe
const [recipe, created] = await Recipe.findOrCreate({
where: { title: meal.strMeal },
defaults: {
userId: systemUser.id,
description: `${meal.strMeal} - A delicious ${meal.strCategory} recipe from ${meal.strArea} cuisine.`,
ingredients: ingredients.join('\n'),
instructions: meal.strInstructions,
imageUrl: meal.strMealThumb,
cookingTime: estimatedTime,
servings: 4,
difficulty: instructionsLength > 1000 ? 'hard' : instructionsLength > 500 ? 'medium' : 'easy',
calories: estimatedCalories,
youtubeLink: meal.strYoutube // Add YouTube link
}
});
// Handle categories
if (meal.strCategory) {
const [category] = await Category.findOrCreate({
where: { name: meal.strCategory },
defaults: {
type: 'meal',
imageUrl: `https://www.themealdb.com/images/category/${meal.strCategory.toLowerCase()}.png`
}
});
// Link recipe to category
await RecipeCategory.findOrCreate({
where: {
recipeId: recipe.id,
categoryId: category.id
}
});
}
// Add area as a category if it exists
if (meal.strArea) {
const [areaCategory] = await Category.findOrCreate({
where: { name: meal.strArea },
defaults: {
type: 'cuisine',
imageUrl: `https://www.themealdb.com/images/category/miscellaneous.png`
}
});
await RecipeCategory.findOrCreate({
where: {
recipeId: recipe.id,
categoryId: areaCategory.id
}
});
}
totalRecipes++;
console.log(`${created ? 'Created' : 'Updated'} recipe: ${recipe.title}`);
}
console.log(`MealDB seed completed successfully. Total recipes: ${totalRecipes}`);
} catch (error) {
console.error('Error seeding MealDB data:', error);
throw error;
}
}
// Run the seeder if this file is executed directly
if (process.argv[1] === fileURLToPath(import.meta.url)) {
try {
await sequelize.authenticate();
console.log('Database connection established');
await seedMealDB();
console.log('Seeding completed');
process.exit(0);
} catch (error) {
console.error('Seeding failed:', error);
process.exit(1);
}
}
export default seedMealDB;
*/
// expected outcome "MealDB seed completed successfully. Total recipes: 301" message.
import fetch from 'node-fetch';
import { Recipe, User, Category, RecipeCategory } from './models/TableCreation.js';
import sequelize from './config/db.js';
import { fileURLToPath } from 'url';
// Helper to fetch meals by the first letter
async function fetchMealsByLetter(letter) {
try {
const response = await fetch(`https://www.themealdb.com/api/json/v1/1/search.php?f=${letter}`);
const data = await response.json();
return data.meals || [];
} catch (error) {
console.error(`Error fetching meals starting with ${letter}:`, error);
return [];
}
}
// Function to fetch meals for all letters of the alphabet
async function fetchAllMeals() {
const meals = [];
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
for (const letter of alphabet) {
console.log(`Fetching meals starting with letter: ${letter}`);
const mealsOnLetter = await fetchMealsByLetter(letter);
meals.push(...mealsOnLetter);
console.log(`Found ${mealsOnLetter.length} meals starting with ${letter}`);
}
// Limit to 1000 recipes (if needed)
return meals.slice(0, 1000);
}
async function seedMealDB() {
try {
// Create a system user for MealDB recipes if it doesn't exist
const [systemUser] = await User.findOrCreate({
where: { username: 'themealdb' },
defaults: {
id: '1',
email: 'api@themealdb.com',
password: 'randompassword123',
bio: 'Official TheMealDB recipes collection',
profileImage: 'https://www.themealdb.com/images/logo-small.png'
}
});
console.log('Fetching all meals...');
const meals = await fetchAllMeals();
let totalRecipes = 0;
for (const meal of meals) {
// Check if the recipe already exists to avoid duplicates
const existingRecipe = await Recipe.findOne({
where: { title: meal.strMeal }
});
if (existingRecipe) {
console.log(`Skipping duplicate recipe: ${meal.strMeal}`);
continue; // Skip this meal as it already exists in the database
}
// Format ingredients and measurements into a list
const ingredients = [];
for (let i = 1; i <= 20; i++) {
const ingredient = meal[`strIngredient${i}`];
const measure = meal[`strMeasure${i}`];
if (ingredient && ingredient.trim()) {
ingredients.push(`${measure ? measure.trim() + ' ' : ''}${ingredient.trim()}`);
}
}
// Estimate cooking time from instructions length
const instructionsLength = meal.strInstructions?.length || 0;
const estimatedTime = Math.max(30, Math.min(180, Math.floor(instructionsLength / 50)));
// Estimate calories (random between 200-800 for demo)
const estimatedCalories = Math.floor(Math.random() * (800 - 200) + 200);
// Create or update recipe
const [recipe, created] = await Recipe.findOrCreate({
where: { title: meal.strMeal },
defaults: {
userId: systemUser.id,
description: `${meal.strMeal} - A delicious ${meal.strCategory} recipe from ${meal.strArea} cuisine.`,
ingredients: ingredients.join('\n'),
instructions: meal.strInstructions,
imageUrl: meal.strMealThumb,
cookingTime: estimatedTime,
servings: 4,
difficulty: instructionsLength > 1000 ? 'hard' : instructionsLength > 500 ? 'medium' : 'easy',
calories: estimatedCalories,
youtubeLink: meal.strYoutube && meal.strYoutube.trim() ? meal.strYoutube : null // Ensure youtubeLink is null if empty
}
});
// Handle categories
if (meal.strCategory) {
const [category] = await Category.findOrCreate({
where: { name: meal.strCategory },
defaults: {
type: 'meal',
imageUrl: `https://www.themealdb.com/images/category/${meal.strCategory.toLowerCase()}.png`
}
});
// Link recipe to category
await RecipeCategory.findOrCreate({
where: {
recipeId: recipe.id,
categoryId: category.id
}
});
}
// Add area as a category if it exists
if (meal.strArea) {
const [areaCategory] = await Category.findOrCreate({
where: { name: meal.strArea },
defaults: {
type: 'cuisine',
imageUrl: `https://www.themealdb.com/images/category/miscellaneous.png`
}
});
await RecipeCategory.findOrCreate({
where: {
recipeId: recipe.id,
categoryId: areaCategory.id
}
});
}
totalRecipes++;
console.log(`${created ? 'Created' : 'Updated'} recipe: ${recipe.title}`);
}
console.log(`MealDB seed completed successfully. Total recipes: ${totalRecipes}`);
} catch (error) {
console.error('Error seeding MealDB data:', error);
throw error;
}
}
// Run the seeder if this file is executed directly
if (process.argv[1] === fileURLToPath(import.meta.url)) {
try {
await sequelize.authenticate();
console.log('Database connection established');
await seedMealDB();
console.log('Seeding completed');
process.exit(0);
} catch (error) {
console.error('Seeding failed:', error);
process.exit(1);
}
}
export default seedMealDB;