from .models import Information from django.contrib.auth.models import User from rest_framework import generics, status from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from django.db.models import Count, Max from .models import Account, Pantry, History, Favorite from .serializers import ( AccountSerializer, FavoriteSerializer, HistorySerializer, PantrySerializer, ProfilePictureSerializer ) from recipes.models import Information, RawIngredient from recipes.serializers import RecipeInformationSerializer, RawIngredientSerializer from .utils import calculate_top_recipes_by_ingredients, get_hybrid_recommendation, get_recommendations_for_user class AccountView(generics.GenericAPIView): permission_classes = [IsAuthenticated] serializer_class = AccountSerializer def get(self, request): account = Account.objects.get(user_id=request.user.id) serializer = self.get_serializer(account) return Response(serializer.data) def post(self, request): account = User.objects.get(id=request.user.id) account_serializer = self.get_serializer( instance=account, data=request.data) profile_picture_serializer = ProfilePictureSerializer( instance=account.account, data=request.data) if profile_picture_serializer.is_valid(raise_exception=True): profile_picture_serializer.save() if account_serializer.is_valid(raise_exception=True): account_serializer.save() return Response(account_serializer.data) class FavoriteView(generics.GenericAPIView): permission_classes = [IsAuthenticated] def get(self, request): user = request.user recipes = Information.objects.filter( favorite__user=user).order_by("-favorite__created_at") recipes_serializer = RecipeInformationSerializer( recipes, context={'request': request}, many=True) return Response(recipes_serializer.data) def post(self, request, recipe_id): user = request.user try: recipe = Information.objects.get(id=recipe_id) except Information.DoesNotExist: return Response({'message': 'Resep tidak ditemukan'}, status=status.HTTP_404_NOT_FOUND) source = request.data.get('source', '') favorite, created = Favorite.objects.get_or_create( user=user, recipe=recipe, source=source) if created: favorite_serializer = FavoriteSerializer( favorite, context={'request': request}) return Response(favorite_serializer.data, status=status.HTTP_201_CREATED) else: return Response({'message': 'Anda sudah menambahkan resep ini ke favorit'}, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, recipe_id): favorite = Favorite.objects.filter( recipe_id=recipe_id, user=request.user).first() if favorite: favorite.delete() return Response({'message': 'Berhasil menghapus resep dari daftar favorit'}, status=status.HTTP_200_OK) else: return Response({'message': 'Data tidak ditemukan'}, status=status.HTTP_404_NOT_FOUND) class HistoryView(generics.GenericAPIView): def get(self, request): user = request.user recipes = Information.objects.filter(history__user=user).annotate( newest_created_at=Max('history__created_at')).order_by('-newest_created_at') recipes_serializer = RecipeInformationSerializer( recipes, context={'request': request}, many=True) return Response(recipes_serializer.data) def post(self, request, recipe_id): if request.user.is_authenticated: user = request.user else: user = User.objects.get(id=4) try: recipe = Information.objects.get(id=recipe_id) except Information.DoesNotExist: return Response({'message': 'Resep tidak ditemukan'}, status=status.HTTP_404_NOT_FOUND) source = request.data.get('source', '') history = History( user=user, recipe=recipe, source=source) history.save() history_serializer = HistorySerializer(history) return Response(history_serializer.data, status=status.HTTP_201_CREATED) def delete(self, request, recipe_id): history = History.objects.filter( recipe_id=recipe_id, user=request.user).first() if history: history.delete() return Response({'message': 'History berhasil dihapus'}, status=status.HTTP_200_OK) else: return Response({'message': 'History tidak ditemukan'}, status=status.HTTP_404_NOT_FOUND) class PantryView(generics.GenericAPIView): permission_classes = [IsAuthenticated] serializer_class = PantrySerializer def get(self, request): pantry = Pantry.objects.filter(user=request.user) if pantry.exists(): serializer = PantrySerializer(pantry, many=True) return Response(serializer.data, status=status.HTTP_200_OK) else: return Response({"message": "Penyimpanan kosong."}, status=status.HTTP_204_NO_CONTENT) def post(self, request): user = request.user raw_ingredient_id = request.data.get('id') amount = request.data.get('amount') unit_id = request.data.get('unit_id') raw_ingredient_ids = request.data.get('raw_ingredient_id', []) if (len(raw_ingredient_ids) != 0): Pantry.objects.filter(user=user).delete() raw_ingredients = RawIngredient.objects.filter( pk__in=raw_ingredient_ids) for raw_ingredient in raw_ingredients: pantry = Pantry(user=user, raw_ingredient=raw_ingredient) pantry.save() user = request.user top_recipes = calculate_top_recipes_by_ingredients(user)[:12] top_recipe_ids = [recipe['recipe_id'] for recipe in top_recipes] recipes = Information.objects.filter(id__in=top_recipe_ids).annotate() recipes_list = RecipeInformationSerializer( recipes, context={'request': request, 'top_recipes': top_recipes}, many=True).data sorted_recipes = sorted( recipes_list, key=lambda x: (0.8 * next( (item['similarity'] for item in top_recipes if item['recipe_id'] == x['id']), 0)) + (0.2 * x['completeness_percentage']), reverse=True ) return Response(sorted_recipes, status=status.HTTP_200_OK) else: try: pantry = Pantry.objects.get( user=user, raw_ingredient_id=raw_ingredient_id) pantry.amount = amount pantry.unit_id = unit_id pantry.save() pantry_serializer = PantrySerializer(pantry).data return Response(pantry_serializer, status=status.HTTP_200_OK) except Pantry.DoesNotExist: pantry = Pantry.objects.create( user=user, raw_ingredient_id=raw_ingredient_id, amount=amount, unit_id=unit_id) pantry_serializer = PantrySerializer(pantry).data return Response(pantry_serializer, status=status.HTTP_200_OK) def delete(self, request, pantry_id): pantry = Pantry.objects.filter( user=request.user, id=pantry_id) pantry.delete() return Response('Berhasil menghapus data', status=status.HTTP_204_NO_CONTENT) class UpdatePasswordView(generics.GenericAPIView): permission_classes = [IsAuthenticated] def post(self, request): user = request.user current_password = request.data.get('current_password') new_password = request.data.get('new_password') if user.check_password(current_password): user.set_password(new_password) user.save() return Response("Password berhasil diperbarui", status=status.HTTP_200_OK) else: return Response("Password saat ini salah", status=status.HTTP_400_BAD_REQUEST) class PantryRecommendationView(generics.GenericAPIView): permission_classes = [IsAuthenticated] def get(self, request): user = request.user top_recipes = calculate_top_recipes_by_ingredients(user)[:20] top_recipe_ids = [recipe['recipe_id'] for recipe in top_recipes] recipes = Information.objects.filter(id__in=top_recipe_ids).annotate() recipes_list = RecipeInformationSerializer( recipes, context={'request': request, 'top_recipes': top_recipes}, many=True).data filtered_recipes = [ recipe for recipe in recipes_list if recipe['completeness_percentage'] > 0] sorted_recipes = sorted( filtered_recipes, key=lambda x: (0.8 * next( (item['similarity'] for item in top_recipes if item['recipe_id'] == x['id']), 0)) + (0.2 * x['completeness_percentage']), reverse=True ) sorted_recipes = sorted_recipes[:12] return Response(sorted_recipes, status=status.HTTP_200_OK) class CFRecommendationView(generics.GenericAPIView): permission_classes = [IsAuthenticated] serializer_class = RecipeInformationSerializer def get(self, request): user_id = self.request.user.id top_recipes = get_recommendations_for_user(user_id)[:20] top_recipes_ids = [recipe['recipe_id'] for recipe in top_recipes] recipes = Information.objects.filter( id__in=top_recipes_ids) recipes_list = RecipeInformationSerializer( recipes, context={'request': request, 'top_recipes': top_recipes}, many=True).data sorted_recipes = sorted( recipes_list, key=lambda x: x['similarity'], reverse=True) return Response(sorted_recipes, status=status.HTTP_200_OK) class MostViewedRecommendationView(generics.GenericAPIView): def get(self, request): recipe_counts = History.objects.values('recipe_id').annotate( count=Count('recipe_id')).order_by('-count')[:9] recipe_ids = [count['recipe_id'] for count in recipe_counts] recipes = Information.objects.filter( id__in=recipe_ids).annotate(history_count=Count('history')).order_by('-history_count') recipes_list = RecipeInformationSerializer( recipes, context={'request': request}, many=True).data return Response(recipes_list, status=status.HTTP_200_OK) class FavoriteRecommendationView(generics.GenericAPIView): def get(self, request): recipe_counts = Favorite.objects.values('recipe_id').annotate( count=Count('recipe_id')).order_by('-count')[:9] recipe_ids = [count['recipe_id'] for count in recipe_counts] recipes = Information.objects.filter( id__in=recipe_ids).annotate(favorite_count=Count('favorite')).order_by('-favorite_count') recipes_list = RecipeInformationSerializer( recipes, context={'request': request}, many=True).data return Response(recipes_list, status=status.HTTP_200_OK) # Unused class HybridRecommendationView(generics.GenericAPIView): permission_classes = [IsAuthenticated] serializer_class = RecipeInformationSerializer def get(self, request): user_id = self.request.user.id recommended_recipe_ids = get_hybrid_recommendation(user_id)[:9] recipes = Information.objects.filter(id__in=recommended_recipe_ids) recipes_list = RecipeInformationSerializer( recipes, context={'request': request}, many=True).data sorted_recipes = sorted( recipes_list, key=lambda x: x['completeness_percentage'], reverse=True)[:9] return Response(sorted_recipes, status=status.HTTP_200_OK)