From 10b8c5717d3671dcf5c7ca6c79934f7856a61050 Mon Sep 17 00:00:00 2001 From: Aman Tahiliani Date: Fri, 12 Apr 2024 17:28:57 -0400 Subject: [PATCH] Added Ranking Functionality --- .../0006_file_points_peeruser_points.py | 23 +++ backend/api/models.py | 2 + backend/api/serializers.py | 2 +- backend/api/urls.py | 12 +- backend/api/user_login.py | 3 + backend/api/utils/get_client_ip.py | 4 + backend/api/views.py | 140 +++++++++++++++++- backend/peer_notes/settings.py | 2 +- 8 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 backend/api/migrations/0006_file_points_peeruser_points.py diff --git a/backend/api/migrations/0006_file_points_peeruser_points.py b/backend/api/migrations/0006_file_points_peeruser_points.py new file mode 100644 index 0000000..27daea7 --- /dev/null +++ b/backend/api/migrations/0006_file_points_peeruser_points.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.11 on 2024-04-12 20:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0005_file_created_at"), + ] + + operations = [ + migrations.AddField( + model_name="file", + name="points", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="peeruser", + name="points", + field=models.IntegerField(default=0), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index ce8f50a..5480732 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -6,6 +6,7 @@ from django.db import models class PeerUser(AbstractUser): ip_address = models.GenericIPAddressField(blank=True, null=True) last_poll = models.DateTimeField(auto_now_add=True) + points = models.IntegerField(default=0, blank=False) class Topic(models.Model): @@ -57,6 +58,7 @@ class File(models.Model): Course, on_delete=models.SET_NULL, null=True, related_name="files" ) created_at = models.DateTimeField(auto_now_add=True) + points = models.IntegerField(default=0, blank=False) def __str__(self): return self.filename diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 8b582fa..b95a237 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -10,7 +10,7 @@ class LoginSerializer(serializers.Serializer): class UserSerializer(serializers.ModelSerializer): class Meta: model = PeerUser - fields = ["id", "username", "email", "ip_address"] + fields = ["id", "username", "email", "ip_address", "points"] class TopicSerializer(serializers.ModelSerializer): diff --git a/backend/api/urls.py b/backend/api/urls.py index 9409a0b..5f57f26 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -54,7 +54,17 @@ urlpatterns = [ name="course-detail", ), path("api/files/", views.FileListCreateAPIView.as_view(), name="file-list"), + path( + "api/files//upvote/", + views.UpvoteFile.as_view(), + name="upvote-file", + ), + path( + "api/files//downvote/", + views.DownvoteFile.as_view(), + name="downvote-file", + ), path("api/files//", views.FileDetailAPIView.as_view(), name="file-detail"), - path("api/files/filter/", views.FileFilterView.as_view(), name="file-filter-view"), path("api/register/", views.RegisterFile.as_view(), name="file-register"), + path("api/files/filter/", views.FileFilterView.as_view(), name="file-filter-view"), ] diff --git a/backend/api/user_login.py b/backend/api/user_login.py index 4cd8ff5..f5da98f 100644 --- a/backend/api/user_login.py +++ b/backend/api/user_login.py @@ -7,6 +7,7 @@ from rest_framework.views import APIView from rest_framework.authentication import TokenAuthentication from rest_framework.permissions import IsAuthenticated from api.utils.get_client_ip import get_client_ip +from django.utils import timezone class LoginView(APIView): @@ -18,6 +19,7 @@ class LoginView(APIView): user = authenticate(username=username, password=password) if user: token, _ = Token.objects.get_or_create(user=user) + user.last_poll = timezone.now() response = Response({"token": token.key}, status=status.HTTP_200_OK) response.set_cookie("token", token.key) return response @@ -66,6 +68,7 @@ class PollOnlineView(APIView): user = request.user ip_address = get_client_ip(request) user.ip_address = ip_address + user.last_poll = timezone.now() user.save() return Response( { diff --git a/backend/api/utils/get_client_ip.py b/backend/api/utils/get_client_ip.py index 821928c..6d65d6a 100644 --- a/backend/api/utils/get_client_ip.py +++ b/backend/api/utils/get_client_ip.py @@ -1,3 +1,6 @@ +from django.utils import timezone + + def get_client_ip(request): x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") if x_forwarded_for: @@ -11,4 +14,5 @@ def update_user_ip(request): user = request.user ip_address = get_client_ip(request) user.ip_address = ip_address + user.last_poll = timezone.now() user.save() diff --git a/backend/api/views.py b/backend/api/views.py index 5ef87b6..07f9ce4 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -5,10 +5,12 @@ from api.serializers import ( ProfessorSerializer, SemesterSerializer, TopicSerializer, + UserSerializer, ) from datetime import datetime, timedelta from django.contrib.auth import authenticate from django.db import transaction +from django.utils import timezone from rest_framework import generics, status from rest_framework.authentication import TokenAuthentication from rest_framework.authtoken.models import Token @@ -105,6 +107,52 @@ class FileDetailAPIView(generics.RetrieveUpdateDestroyAPIView): permission_classes = [IsAuthenticated] +class UpvoteFile(APIView): + authentication_classes = [TokenAuthentication] + permission_classes = [IsAuthenticated] + + def post(self, request, file_id): + update_user_ip(request) + + try: + file = File.objects.get(id=file_id) + file.points += 1 + file.save() + return Response( + {"msg": f"Upvoted {file.filename}", "file_points": file.points}, + status=status.HTTP_200_OK, + ) + except Exception as e: + print(f"Error: {str(e)}") + return Response( + {"error": "Please enter valid file id"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + +class DownvoteFile(APIView): + authentication_classes = [TokenAuthentication] + permission_classes = [IsAuthenticated] + + def post(self, request, file_id): + update_user_ip(request) + + try: + file = File.objects.get(id=file_id) + file.points -= 1 + file.save() + return Response( + {"msg": f"Downvoted {file.filename}", "file_points": file.points}, + status=status.HTTP_200_OK, + ) + except Exception as e: + print(f"Error: {str(e)}") + return Response( + {"error": "Please enter valid file id"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + class RegisterFile(APIView): authentication_classes = [TokenAuthentication] permission_classes = [IsAuthenticated] @@ -115,6 +163,7 @@ class RegisterFile(APIView): data = request.data try: with transaction.atomic(): + user.last_poll = timezone.now() required_fields = set( ["filename", "topic", "semester", "professor", "course"] ) @@ -166,7 +215,9 @@ class RegisterFile(APIView): "course": file.course.name if file.course else None, "professor": file.professor.name if file.professor else None, } + user.points += 1 file.save() + user.save() return Response( response_data, status=status.HTTP_201_CREATED, @@ -178,9 +229,19 @@ class RegisterFile(APIView): ) +from django.utils import timezone +from datetime import timedelta +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from .models import File, PeerUser +from .serializers import FileSerializer + + class FileFilterView(APIView): authentication_classes = [TokenAuthentication] permission_classes = [IsAuthenticated] + def get(self, request, format=None): try: # Extract filters from the query parameters @@ -226,15 +287,90 @@ class FileFilterView(APIView): # Filter files based on active peers in the past hour active_peer_ids = PeerUser.objects.filter( - last_poll__gte=datetime.now() - timedelta(hours=1) + last_poll__gte=timezone.now() - timedelta(hours=1) ).values_list("id", flat=True) - queryset = queryset.filter(peer_users__in=active_peer_ids).distinct() + # Sort files based on points column in descending order + queryset = queryset.order_by("-points") + + # Serialize files serializer = FileSerializer(queryset, many=True) + # Loop through each serialized file to sort its peer_users + for file_data in serializer.data: + file_obj = File.objects.get(id=file_data["id"]) + sorted_peer_users = sorted( + file_obj.peer_users.all(), key=lambda x: x.points, reverse=True + ) + file_data["peer_users"] = UserSerializer( + sorted_peer_users, many=True + ).data + return Response(serializer.data) except Exception as e: return Response( {"error": f"Something went wrong: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) + + +# class FileFilterView(APIView): +# authentication_classes = [TokenAuthentication] +# permission_classes = [IsAuthenticated] +# def get(self, request, format=None): +# try: +# # Extract filters from the query parameters +# topic_id = request.query_params.get("topic") +# professor_id = request.query_params.get("professor") +# course_id = request.query_params.get("course") +# semester_id = request.query_params.get("semester") + +# # Check if provided filter IDs exist +# if topic_id and not Topic.objects.filter(id=topic_id).exists(): +# return Response( +# {"error": f"Topic with id {topic_id} does not exist"}, +# status=status.HTTP_400_BAD_REQUEST, +# ) +# if professor_id and not Professor.objects.filter(id=professor_id).exists(): +# return Response( +# {"error": f"Professor with id {professor_id} does not exist"}, +# status=status.HTTP_400_BAD_REQUEST, +# ) +# if course_id and not Course.objects.filter(id=course_id).exists(): +# return Response( +# {"error": f"Course with id {course_id} does not exist"}, +# status=status.HTTP_400_BAD_REQUEST, +# ) +# if semester_id and not Semester.objects.filter(id=semester_id).exists(): +# return Response( +# {"error": f"Semester with id {semester_id} does not exist"}, +# status=status.HTTP_400_BAD_REQUEST, +# ) + +# # Start with the base queryset +# queryset = File.objects.all() + +# # Apply filters if they are provided +# if topic_id: +# queryset = queryset.filter(topic__id=topic_id) +# if professor_id: +# queryset = queryset.filter(professor__id=professor_id) +# if course_id: +# queryset = queryset.filter(course__id=course_id) +# if semester_id: +# queryset = queryset.filter(semester__id=semester_id) + +# # Filter files based on active peers in the past hour +# active_peer_ids = PeerUser.objects.filter( +# last_poll__gte=timezone.now() - timedelta(hours=1) +# ).values_list("id", flat=True) + +# queryset = queryset.filter(peer_users__in=active_peer_ids).distinct() + +# serializer = FileSerializer(queryset, many=True) +# return Response(serializer.data) +# except Exception as e: +# return Response( +# {"error": f"Something went wrong: {str(e)}"}, +# status=status.HTTP_500_INTERNAL_SERVER_ERROR, +# ) diff --git a/backend/peer_notes/settings.py b/backend/peer_notes/settings.py index 2418f69..bef179d 100644 --- a/backend/peer_notes/settings.py +++ b/backend/peer_notes/settings.py @@ -137,4 +137,4 @@ CORS_ALLOW_ALL_ORIGINS = True # CORS_ALLOWED_ORIGINS = [ # "http://localhost:5173" # ] -CORS_ALLOW_CREDENTIALS = True \ No newline at end of file +CORS_ALLOW_CREDENTIALS = True