from rest_framework import serializers from recipes.models import Information, Review, Ingredient, RawIngredient, Unit from profiles.models import Account, Favorite, Pantry from django.db.models import Avg, Count from django.template.defaultfilters import date from decimal import Decimal import ast import math from profiles.utils import get_owned_raw_ingredients, get_owned_recipe_ingredients class RawIngredientSerializer(serializers.ModelSerializer): class Meta: model = RawIngredient fields = '__all__' class RecipeIngredientsSerializer(serializers.ModelSerializer): unit = serializers.CharField(source='unit.code') class Meta: model = Ingredient fields = ('id', 'name', 'amount', 'state', 'recipe', 'unit') class ReviewSummarySerializer(serializers.ModelSerializer): score = serializers.SerializerMethodField() amount = serializers.SerializerMethodField() class Meta: model = Review fields = ('score', 'amount') def get_score(self, instance): ratings = Review.objects.filter(recipe_id=instance.id).aggregate( avg_rating=Avg('rating'), review_count=Count('id') ) avg_rating = ratings['avg_rating'] or 0 review_count = ratings['review_count'] or 0 total_rating = avg_rating * review_count total_review_count = review_count return Decimal(total_rating / total_review_count).quantize(Decimal('0.00')) if total_review_count != 0 else 0.0 def get_amount(self, instance): review_counts = Review.objects.filter(recipe_id=instance.id).aggregate( user_review_count=Count('id') ) user_review_count = review_counts['user_review_count'] or 0 total_review_count = user_review_count return total_review_count class RecipeInformationSerializer(serializers.ModelSerializer): completeness_percentage = serializers.SerializerMethodField() is_favorited = serializers.SerializerMethodField() similarity = serializers.SerializerMethodField() rating = ReviewSummarySerializer(source='*') def get_is_favorited(self, instance): user = self.context['request'].user if user.is_authenticated: return Favorite.objects.filter(recipe_id=instance.pk, user=user).exists() return False def get_completeness_percentage(self, instance): user = self.context['request'].user if user.is_authenticated: owned_ingredient_ids = len(get_owned_recipe_ingredients( user, instance.pk)) total_ingredient_count = instance.ingredients.count() # print(instance.title + " " + str(owned_ingredient_ids) + " " + str(total_ingredient_count)) if total_ingredient_count > 0: completeness_percentage = ( owned_ingredient_ids / total_ingredient_count * 100 ) completeness_percentage = round( completeness_percentage, 2) return completeness_percentage return 0 def get_similarity(self, instance): # Add this method recipe_id = instance.pk top_recipes = self.context.get('top_recipes') similarity = 0 if top_recipes: similarity = next( (item['similarity'] for item in top_recipes if item['recipe_id'] == recipe_id), 0 ) similarity = round(Decimal(similarity), 2) return similarity class Meta: model = Information fields = ('id', 'title', 'image_url', 'difficulty', 'source_url', 'calories', 'completeness_percentage', 'is_favorited', 'rating', 'similarity') class RecipeDetailSerializer(serializers.ModelSerializer): ingredients = serializers.SerializerMethodField() owned_ingredients = serializers.SerializerMethodField() not_owned_ingredients = serializers.SerializerMethodField() instructions = serializers.CharField() rating = ReviewSummarySerializer(source='*') is_favorited = serializers.SerializerMethodField() def get_ingredients(self, instance): user = self.context['request'].user all_ingredients = Ingredient.objects.filter(recipe=instance) serialized_data = RecipeIngredientsSerializer( all_ingredients, many=True).data if user.is_authenticated: owned_ingredient_ids = get_owned_recipe_ingredients( user, instance.pk) owned_ingredients = Ingredient.objects.filter( pk__in=owned_ingredient_ids).distinct() owned_ids = [ingredient.id for ingredient in owned_ingredients] for ingredient in serialized_data: if ingredient['id'] in owned_ids: ingredient['is_owned'] = True else: ingredient['is_owned'] = False else: for ingredient in serialized_data: ingredient['is_owned'] = False return serialized_data def get_owned_ingredients(self, instance): user = self.context['request'].user if not user.is_authenticated: return [] owned_ingredient_ids = get_owned_recipe_ingredients( user, instance.pk) owned_ingredients = Ingredient.objects.filter( id__in=owned_ingredient_ids).distinct() return RecipeIngredientsSerializer(owned_ingredients, many=True).data def get_not_owned_ingredients(self, instance): user = self.context['request'].user if not user.is_authenticated: all_ingredients = Ingredient.objects.filter(recipe=instance) return RecipeIngredientsSerializer(all_ingredients, many=True).data owned_raw_ingredient_ids = Pantry.objects.filter( user=user).values_list('raw_ingredient_id', flat=True) not_owned_ingredients = Ingredient.objects.filter( recipe=instance).exclude(raw_ingredient__in=owned_raw_ingredient_ids) return RecipeIngredientsSerializer(not_owned_ingredients, many=True).data def get_is_favorited(self, instance): user = self.context['request'].user if user.is_authenticated: return Favorite.objects.filter(recipe_id=instance.pk, user=user).exists() return False class Meta: model = Information fields = ('id', 'title', 'image_url', 'time', 'servings', 'difficulty', 'source_url', 'rating', 'is_favorited', 'ingredients', 'instructions', 'owned_ingredients', 'not_owned_ingredients') def to_representation(self, instance): representation = super().to_representation(instance) string_array = representation['instructions'] array = ast.literal_eval(string_array) representation['instructions'] = array return representation class ReviewSerializer(serializers.ModelSerializer): user = serializers.SerializerMethodField() modified_date = serializers.SerializerMethodField() class Meta: model = Review fields = ('id', 'user', 'rating', 'comment', 'modified_date') def get_user(self, instance): account = Account.objects.get(user=instance.user) user_data = { 'id': account.user.id, 'profile_picture': account.profile_picture.url, 'name': f"{account.user.first_name} {account.user.last_name}" } return user_data def get_modified_date(self, instance): modified_date = date(instance.updated_at, "j F Y") return modified_date class UnitSerializer(serializers.ModelSerializer): class Meta: model = Unit fields = '__all__'