From 960a798f0581e44d9f6fc12c3ec8dd2cb13353f4 Mon Sep 17 00:00:00 2001 From: Dogapinarr <147092474+Dogapinarr@users.noreply.github.com> Date: Wed, 22 May 2024 14:55:45 +0300 Subject: [PATCH] ai,meal planner,profile upgrade --- recommendSystem/recommend.py | 170 +++++++++++++++++++++++++++++--- scripts/userEditProfile.js | 49 +++------ scripts/userRecipeDetail.js | 89 ++++++++++++++++- templates/meal_planner.html | 45 +++++---- templates/userEditProfile.html | 60 +++++------ templates/userRecipeDetail.html | 49 +++++++++ 6 files changed, 360 insertions(+), 102 deletions(-) diff --git a/recommendSystem/recommend.py b/recommendSystem/recommend.py index 3155779..1ac0a4f 100644 --- a/recommendSystem/recommend.py +++ b/recommendSystem/recommend.py @@ -2,11 +2,27 @@ import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import linear_kernel +from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient +import requests +import json +from recipeRecDTO import RecipeRecommendationObject + app = Flask(__name__) +sas_token = "sp=racwdyti&st=2024-05-14T12:06:34Z&se=2024-06-04T20:06:34Z&sv=2022-11-02&sr=b&sig=%2FYZk57JEkcpWlOKhm9rl5y2roVMQbwl%2Fa%2FgyPS5uL5A%3D" +blob_url = "https://blobrecipeimages.blob.core.windows.net/data-set-kaggle/recipes_with_no_tags_and_cuisine.csv?" + sas_token +#Blob Service Client oluştur +blob_service_client = BlobServiceClient(account_url="https://blobrecipeimages.blob.core.windows.net", credential=sas_token) + +# Container Client oluştur +container_client = blob_service_client.get_container_client("data-set-kaggle") + +#blob_url="https://blobrecipeimages.blob.core.windows.net/data-set-kaggle/recipes_with_no_tags_and_cuisine.csv?" + + # Load recipe data -recipe_data = pd.read_csv('https://blobrecipeimages.blob.core.windows.net/data-set-kaggle/recipes_with_no_tags_and_cuisine.csv') +recipe_data = pd.read_csv(blob_url) recipe_data = recipe_data.dropna(subset=['ingredients', 'cuisine_path']) recipe_data['total_time'] = recipe_data['total_time'].astype(str) recipe_data['combined_features'] = recipe_data['total_time'] + ' ' + recipe_data['ingredients'] + ' ' + recipe_data['cuisine_path'] @@ -15,25 +31,149 @@ tfidf_vectorizer = TfidfVectorizer(stop_words='english') tfidf_matrix = tfidf_vectorizer.fit_transform(recipe_data['combined_features']) cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix) +def get_recommendations(title=None, ingredients=None): + matching_recipes = recipe_data + + if title: + matching_recipes = matching_recipes[matching_recipes['recipe_name'].str.contains(title, case=False)] + + if ingredients: + ingredient_query = '|'.join(ingredients) + matching_recipes = matching_recipes[matching_recipes['ingredients'].str.contains(ingredient_query, case=False)] -def get_recommendations(query): - matching_recipes = recipe_data[recipe_data['recipe_name'].str.contains(query, case=False)] if matching_recipes.empty: return [] - selected_recipe = matching_recipes.iloc[0]['recipe_name'] - idx = recipe_data[recipe_data['recipe_name'] == selected_recipe].index[0] - sim_scores = list(enumerate(cosine_sim[idx])) + + combined_features_with_ingredients = matching_recipes['total_time'] + ' ' + matching_recipes['ingredients'] + ' ' + matching_recipes['cuisine_path'] + tfidf_matrix_with_ingredients = tfidf_vectorizer.fit_transform(combined_features_with_ingredients) + cosine_sim_with_ingredients = linear_kernel(tfidf_matrix_with_ingredients, tfidf_matrix_with_ingredients) + + # Calculate similarity scores for matched recipes + sim_scores = [] + for i in range(len(matching_recipes)): + sim_scores.append((i, cosine_sim_with_ingredients[i,-1])) # similarity score for the last recipe + sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True) - sim_recipes = sim_scores[1:11] - return recipe_data['recipe_name'].iloc[[i[0] for i in sim_recipes]].tolist() + sim_recipes = sim_scores[:5] # Take the top 5 similar recipes + recipe_indices = [i[0] for i in sim_recipes] + recommended_recipes = [matching_recipes.iloc[idx] for idx in recipe_indices] + + return recommended_recipes -@app.route('/recommend', methods=['GET']) + + + +@app.route('/hello', methods=['GET']) +def hello(): + return "hellooooo" + + +@app.route('/get-recommendations', methods=['GET']) def recommend(): - query = request.args.get('query') - if not query: - return jsonify({"error": "No query parameter provided"}), 400 - recommendations = get_recommendations(query) - return jsonify({"recommendations": recommendations}) + title = request.args.get('title') + ingredients = request.args.get('ingredients') + + if not title and not ingredients: + return jsonify({'error': 'Please provide at least one parameter.'}), 400 + + if ingredients and ":" in ingredients: + ingredients = format_data_ingredients(ingredients) + + recommendations = get_recommendations(title, ingredients) + + if not recommendations: + return jsonify({'message': 'No recipes found matching the criteria.'}), 404 + + # Convert recommendations to RecipeRecommendationObject + recommendation_objects = [create_recommendation_object(recipe) for recipe in recommendations] + + return jsonify({'recommendations': [vars(obj) for obj in recommendation_objects]}) + +def format_data_ingredients(ingredients): + formatted_ingredients = [] + for ingredient in ingredients.split(','): + parts = ingredient.strip().split(':') + formatted_ingredient = parts[0].strip() if len(parts) > 1 else parts[0].strip() + formatted_ingredients.append(formatted_ingredient) + return formatted_ingredients + + +def create_recommendation_object(recipe_row): + return RecipeRecommendationObject ( + title=recipe_row['recipe_name'], + ingredients=recipe_row['ingredients'], + description=recipe_row['directions'], + cuisine=recipe_row['cuisine_path'], + timing=recipe_row['total_time'], + photoPathURL=recipe_row['img_src'] + ) + +@app.route('/add-recipe-to-dataset', methods=['POST']) +def add_recipe_to_dataset(): + + global recipe_data # Declare recipe_data as global + + data = request.json + + # Extract data from request body + recipe_name = data.get('title', '') + prep_time = format_time(data.get('preparationTime', None)) + cook_time = None + total_time = None + servings = None + yield_value = None + ingredients = data.get('ingredients', '') + directions = data.get('description', '') + rating = None + url = None + cuisine_path = data.get('cuisine', '') + nutrition = None + timing = None + img_src = data.get('photoPathURL', '') + + # Process the data and add it to the dataset + # You can perform any necessary processing here, such as data validation and formatting + # Then add the recipe to the dataset + new_recipe = pd.DataFrame.from_dict({ + 'recipe_name': [recipe_name], + 'prep_time': [prep_time], + 'cook_time': [cook_time], + 'total_time': [total_time], + 'servings': [servings], + 'yield': [yield_value], + 'ingredients': [ingredients], + 'directions': [directions], + 'rating': [rating], + 'url': [url], + 'cuisine_path': [cuisine_path], + 'nutrition': [nutrition], + 'timing': [timing], + 'img_src': [img_src] + }) + + recipe_data = pd.concat([recipe_data, new_recipe], ignore_index=True) + + # Dosyayı bloba yükle + data = recipe_data.to_csv(index=False) + blob_client = container_client.get_blob_client(blob="recipes_with_no_tags_and_cuisine.csv") + blob_client.upload_blob(data, overwrite=True) + + print("Veri başarıyla bloba yüklendi.") + + return jsonify({"message": "Recipe added to dataset successfully"}) + +def format_time(minutes): + if not minutes: + return '' + + hours, mins = divmod(minutes, 60) + + if hours > 0 and mins > 0: + return f"{hours} hrs {mins} mins" + elif hours > 0: + return f"{hours} hrs" + else: + return f"{mins} mins" if __name__ == '__main__': - app.run(debug=True) + app.run() \ No newline at end of file diff --git a/scripts/userEditProfile.js b/scripts/userEditProfile.js index ea2d218..23c6c06 100644 --- a/scripts/userEditProfile.js +++ b/scripts/userEditProfile.js @@ -5,9 +5,7 @@ document.addEventListener('DOMContentLoaded', function () { ]; var allergicFoodSection = document.querySelector('.allergic-food-section'); - var otherFoodContainer = document.getElementById('other-food-container'); - // Checkboxları oluştur allergicFoods.forEach(function(food) { var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; @@ -22,13 +20,6 @@ document.addEventListener('DOMContentLoaded', function () { allergicFoodSection.appendChild(document.createElement('br')); }); - // Diğer seçeneği için text kutusu oluştur - var otherInput = document.createElement('input'); - otherInput.type = 'text'; - otherInput.placeholder = 'Type: Allergy 1, Allergy 2'; - otherInput.id = 'other-food-input'; - otherFoodContainer.appendChild(otherInput); - var photoUpload = document.getElementById('photo-upload'); var photoFrame = document.getElementById('photo-frame'); @@ -47,23 +38,19 @@ document.addEventListener('DOMContentLoaded', function () { }); }); - const saveButton = document.getElementById("save-button"); saveButton.addEventListener("click", async function (event) { - event.preventDefault(); // Sayfanın yeniden yüklenmesini önle + event.preventDefault(); try { - // Kullanıcının girdiği bilgileri al const bio = document.getElementById('bio').value; - // Seçilen alerjenleri al const selectedFoods = []; document.querySelectorAll('input[name="allergic-food"]:checked').forEach(function(checkbox) { selectedFoods.push(checkbox.value); }); - // Diğer alerjeni al - let otherFoodInput = document.getElementById('other-food-input'); + const otherFoodInput = document.getElementById('other-food-input'); if (otherFoodInput) { const otherFoodValue = otherFoodInput.value.trim(); if (otherFoodValue !== '') { @@ -71,39 +58,32 @@ saveButton.addEventListener("click", async function (event) { } } - // Boşlukları virgülle değiştir const finalAllergiesString = selectedFoods.map(food => food.trim()).join(','); - // Kullanıcının girdiği diğer alanları al const newPassword = document.getElementById('new-password').value; const confirmPassword = document.getElementById('confirm-password').value; - // Kullanıcı fotoğrafını al const photoUpload = document.getElementById('photo-upload'); const photoFile = photoUpload.files[0]; - // Profil verilerini oluştur - const profileData = { - password: newPassword, - bio: bio, - allergicFoods: finalAllergiesString, - profilePhoto: photoFile - }; + const formData = new FormData(); + formData.append('password', newPassword); + formData.append('bio', bio); + formData.append('allergicFoods', finalAllergiesString); + if (photoFile) { + formData.append('profilePhoto', photoFile); + } - console.log(selectedFoods); + console.log('Selected Allergies:', selectedFoods); console.log('Final Allergic Foods String:', finalAllergiesString); - // Profil verilerini kaydetme fonksiyonunu çağır - await saveProfile(profileData); + await saveProfile(formData); } catch (error) { console.error('Error saving profile:', error); - // Hata durumunda kullanıcıya bildirim gönderilebilir - // Örn: alert('Error saving profile: ' + error.message); } }); - -async function saveProfile(profileData) { +async function saveProfile(formData) { const apiUrl = 'https://recipiebeckend.azurewebsites.net/user/save-user-profile'; const JWTAccessToken = sessionStorage.getItem('accessToken'); @@ -111,10 +91,9 @@ async function saveProfile(profileData) { const response = await fetch(apiUrl, { method: 'PUT', headers: { - 'Authorization': JWTAccessToken, - 'Content-Type': 'application/json' + 'Authorization': JWTAccessToken }, - body: JSON.stringify(profileData), + body: formData }); if (!response.ok) { diff --git a/scripts/userRecipeDetail.js b/scripts/userRecipeDetail.js index 5f9730a..97948ef 100644 --- a/scripts/userRecipeDetail.js +++ b/scripts/userRecipeDetail.js @@ -119,9 +119,37 @@ class RecipeDetail { }); } - displayRecommendations(recommendations) { - // Önerileri kullanıcı arayüzüne göster - } + // Önerileri slider içeriğine ekle + displayRecommendations(recommendations) { + const slidesContainer = document.getElementById('slides'); + slidesContainer.innerHTML = ''; // Slider içeriğini temizle + + recommendations.recommendations.forEach((recommendation, index) => { + if (index % 3 === 0) { + // Her üç öneriden sonra yeni bir slide oluştur + const slide = document.createElement('div'); + slide.className = 'slide'; + slidesContainer.appendChild(slide); + } + + // Resmi oluştur + const img = document.createElement('img'); + img.src = recommendation.photoPathURL; + img.alt = recommendation.title; + + // Başlığı oluştur + const title = document.createElement('div'); + title.className = 'recommendation-title'; + title.textContent = recommendation.title; + + // Resmi ve başlığı slide içeriğine ekle + const currentSlide = slidesContainer.lastElementChild; + const slideContent = document.createElement('div'); + slideContent.appendChild(img); + slideContent.appendChild(title); + currentSlide.appendChild(slideContent); + }); +} setupBackButton() { const backButton = document.getElementById('back-arrow-button'); @@ -132,5 +160,60 @@ class RecipeDetail { }); } } +let slideIndex = 0; +const slidesContainer = document.getElementById('slides'); + +// AI'dan gelen verileri burada tanımlayın +const aiData = [ + { img: "image1.jpg", alt: "AI verisi 1" }, + { img: "image2.jpg", alt: "AI verisi 2" }, + { img: "image3.jpg", alt: "AI verisi 3" }, + { img: "image4.jpg", alt: "AI verisi 4" }, + { img: "image5.jpg", alt: "AI verisi 5" }, + { img: "image6.jpg", alt: "AI verisi 6" }, + { img: "image7.jpg", alt: "AI verisi 7" }, + { img: "image8.jpg", alt: "AI verisi 8" }, + { img: "image9.jpg", alt: "AI verisi 9" } +]; + +// Verileri 3'erli gruplar halinde slider'a eklemek +for (let i = 0; i < aiData.length; i += 3) { + const slide = document.createElement('div'); + slide.className = 'slide'; + + for (let j = 0; j < 3; j++) { + if (aiData[i + j]) { + const img = document.createElement('img'); + img.src = aiData[i + j].img; + img.alt = aiData[i + j].alt; + slide.appendChild(img); + } + } + + slidesContainer.appendChild(slide); +} + +function showSlide(index) { + const slides = document.querySelectorAll('.slide'); + if (index >= slides.length) { + slideIndex = 0; + } else if (index < 0) { + slideIndex = slides.length - 1; + } else { + slideIndex = index; + } + slidesContainer.style.transform = `translateX(${-slideIndex * 100 / 3}%)`; // 3 slide olduğundan dolayı her biri container'ın 1/3'ü genişliğinde +} + +function nextSlide() { + showSlide(slideIndex + 1); +} + +function prevSlide() { + showSlide(slideIndex - 1); +} + +// İlk slide'ı göster +showSlide(slideIndex); const recipeDetailPage = new RecipeDetail(); diff --git a/templates/meal_planner.html b/templates/meal_planner.html index 457b65b..44d65ef 100644 --- a/templates/meal_planner.html +++ b/templates/meal_planner.html @@ -85,9 +85,11 @@