Added Ranking Functionality

This commit is contained in:
2024-04-12 17:28:57 -04:00
parent 3daad2dfa9
commit 10b8c5717d
8 changed files with 183 additions and 5 deletions

View File

@@ -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),
),
]

View File

@@ -6,6 +6,7 @@ from django.db import models
class PeerUser(AbstractUser): class PeerUser(AbstractUser):
ip_address = models.GenericIPAddressField(blank=True, null=True) ip_address = models.GenericIPAddressField(blank=True, null=True)
last_poll = models.DateTimeField(auto_now_add=True) last_poll = models.DateTimeField(auto_now_add=True)
points = models.IntegerField(default=0, blank=False)
class Topic(models.Model): class Topic(models.Model):
@@ -57,6 +58,7 @@ class File(models.Model):
Course, on_delete=models.SET_NULL, null=True, related_name="files" Course, on_delete=models.SET_NULL, null=True, related_name="files"
) )
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
points = models.IntegerField(default=0, blank=False)
def __str__(self): def __str__(self):
return self.filename return self.filename

View File

@@ -10,7 +10,7 @@ class LoginSerializer(serializers.Serializer):
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = PeerUser model = PeerUser
fields = ["id", "username", "email", "ip_address"] fields = ["id", "username", "email", "ip_address", "points"]
class TopicSerializer(serializers.ModelSerializer): class TopicSerializer(serializers.ModelSerializer):

View File

@@ -54,7 +54,17 @@ urlpatterns = [
name="course-detail", name="course-detail",
), ),
path("api/files/", views.FileListCreateAPIView.as_view(), name="file-list"), path("api/files/", views.FileListCreateAPIView.as_view(), name="file-list"),
path(
"api/files/<int:file_id>/upvote/",
views.UpvoteFile.as_view(),
name="upvote-file",
),
path(
"api/files/<int:file_id>/downvote/",
views.DownvoteFile.as_view(),
name="downvote-file",
),
path("api/files/<int:pk>/", views.FileDetailAPIView.as_view(), name="file-detail"), path("api/files/<int:pk>/", 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/register/", views.RegisterFile.as_view(), name="file-register"),
path("api/files/filter/", views.FileFilterView.as_view(), name="file-filter-view"),
] ]

View File

@@ -7,6 +7,7 @@ from rest_framework.views import APIView
from rest_framework.authentication import TokenAuthentication from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from api.utils.get_client_ip import get_client_ip from api.utils.get_client_ip import get_client_ip
from django.utils import timezone
class LoginView(APIView): class LoginView(APIView):
@@ -18,6 +19,7 @@ class LoginView(APIView):
user = authenticate(username=username, password=password) user = authenticate(username=username, password=password)
if user: if user:
token, _ = Token.objects.get_or_create(user=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 = Response({"token": token.key}, status=status.HTTP_200_OK)
response.set_cookie("token", token.key) response.set_cookie("token", token.key)
return response return response
@@ -66,6 +68,7 @@ class PollOnlineView(APIView):
user = request.user user = request.user
ip_address = get_client_ip(request) ip_address = get_client_ip(request)
user.ip_address = ip_address user.ip_address = ip_address
user.last_poll = timezone.now()
user.save() user.save()
return Response( return Response(
{ {

View File

@@ -1,3 +1,6 @@
from django.utils import timezone
def get_client_ip(request): def get_client_ip(request):
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
if x_forwarded_for: if x_forwarded_for:
@@ -11,4 +14,5 @@ def update_user_ip(request):
user = request.user user = request.user
ip_address = get_client_ip(request) ip_address = get_client_ip(request)
user.ip_address = ip_address user.ip_address = ip_address
user.last_poll = timezone.now()
user.save() user.save()

View File

@@ -5,10 +5,12 @@ from api.serializers import (
ProfessorSerializer, ProfessorSerializer,
SemesterSerializer, SemesterSerializer,
TopicSerializer, TopicSerializer,
UserSerializer,
) )
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.db import transaction from django.db import transaction
from django.utils import timezone
from rest_framework import generics, status from rest_framework import generics, status
from rest_framework.authentication import TokenAuthentication from rest_framework.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
@@ -105,6 +107,52 @@ class FileDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsAuthenticated] 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): class RegisterFile(APIView):
authentication_classes = [TokenAuthentication] authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@@ -115,6 +163,7 @@ class RegisterFile(APIView):
data = request.data data = request.data
try: try:
with transaction.atomic(): with transaction.atomic():
user.last_poll = timezone.now()
required_fields = set( required_fields = set(
["filename", "topic", "semester", "professor", "course"] ["filename", "topic", "semester", "professor", "course"]
) )
@@ -166,7 +215,9 @@ class RegisterFile(APIView):
"course": file.course.name if file.course else None, "course": file.course.name if file.course else None,
"professor": file.professor.name if file.professor else None, "professor": file.professor.name if file.professor else None,
} }
user.points += 1
file.save() file.save()
user.save()
return Response( return Response(
response_data, response_data,
status=status.HTTP_201_CREATED, 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): class FileFilterView(APIView):
authentication_classes = [TokenAuthentication] authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def get(self, request, format=None): def get(self, request, format=None):
try: try:
# Extract filters from the query parameters # Extract filters from the query parameters
@@ -226,15 +287,90 @@ class FileFilterView(APIView):
# Filter files based on active peers in the past hour # Filter files based on active peers in the past hour
active_peer_ids = PeerUser.objects.filter( 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) ).values_list("id", flat=True)
queryset = queryset.filter(peer_users__in=active_peer_ids).distinct() 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) 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) return Response(serializer.data)
except Exception as e: except Exception as e:
return Response( return Response(
{"error": f"Something went wrong: {str(e)}"}, {"error": f"Something went wrong: {str(e)}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR, 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,
# )

View File

@@ -137,4 +137,4 @@ CORS_ALLOW_ALL_ORIGINS = True
# CORS_ALLOWED_ORIGINS = [ # CORS_ALLOWED_ORIGINS = [
# "http://localhost:5173" # "http://localhost:5173"
# ] # ]
CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_CREDENTIALS = True