From 50fdebf222afbcfae6b6be1182cd9c36f394d271 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sun, 24 Sep 2023 21:03:32 +0800 Subject: [PATCH] Offloaded study group radius calculation to backend and fixed study groups serializer --- stude/study_groups/serializers.py | 4 ++- stude/study_groups/views.py | 53 +++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/stude/study_groups/serializers.py b/stude/study_groups/serializers.py index 2362576..70707ee 100644 --- a/stude/study_groups/serializers.py +++ b/stude/study_groups/serializers.py @@ -14,6 +14,7 @@ class CustomUserKeyRelatedField(serializers.PrimaryKeyRelatedField): class StudyGroupSerializer(serializers.ModelSerializer): + name = serializers.CharField() users = CustomUserKeyRelatedField( queryset=CustomUser.objects.all(), many=True) subject = serializers.SlugRelatedField( @@ -21,6 +22,7 @@ class StudyGroupSerializer(serializers.ModelSerializer): location = PointField() landmark = serializers.SlugRelatedField( queryset=Landmark.objects.all(), many=False, slug_field='name', required=False, allow_null=True) + radius = serializers.FloatField() def create(self, validated_data): user = self.context['request'].user @@ -40,7 +42,7 @@ class StudyGroupSerializer(serializers.ModelSerializer): class Meta: model = StudyGroup fields = '__all__' - read_only_fields = ['landmark'] + read_only_fields = ['landmark', 'radius'] class StudyGroupMembershipSerializer(serializers.ModelSerializer): diff --git a/stude/study_groups/views.py b/stude/study_groups/views.py index 9894aee..7483daf 100644 --- a/stude/study_groups/views.py +++ b/stude/study_groups/views.py @@ -4,11 +4,12 @@ from rest_framework.exceptions import PermissionDenied from rest_framework.permissions import IsAuthenticated from .serializers import StudyGroupSerializer from .models import StudyGroup -from subjects.models import Subject +from subjects.models import Subject, SubjectInstance from student_status.models import StudentStatus from rest_framework import generics, viewsets, exceptions from django.contrib.gis.geos import fromstr from django.contrib.gis.db.models.functions import Distance + # Create your views here. @@ -20,6 +21,11 @@ class StudyGroupListView(generics.ListAPIView): def get_queryset(self): user = self.request.user + if not user.is_student: + raise PermissionDenied( + "You must be a student to view study groups" + ) + if not user.is_student: raise PermissionDenied( "You must be a student to view study groups" @@ -29,15 +35,14 @@ class StudyGroupListView(generics.ListAPIView): user_course = user.course print(user_course) - # Get subject names related to the user's course - subject_names = Subject.objects.filter( - course=user_course - ).values_list('subject', flat=True) + # Get the subject name of the student's subjects from SubjectInstance + user_subject_names = user.subjects.values_list('subject', flat=True) + print('user subjects:', user_subject_names) + # Get the corresponding Subject models + user_subjects = Subject.objects.filter(name__in=user_subject_names) + # Now fetch the StudyGroups with the matching Subject models + studygroups = StudyGroup.objects.filter(subject__in=user_subjects) - print(subject_names) - - # Now fetch the StudyGroups with the matching subject names - studygroups = StudyGroup.objects.filter(subject_name__in=subject_names) return studygroups @@ -64,16 +69,30 @@ class StudyGroupListNearView(generics.ListAPIView): user_course = user.course print(user_course) - # Get subject names related to the user's course - subject_names = Subject.objects.filter( - course=user_course - ).values_list('subject', flat=True) + # Get the subject name of the student's subjects from SubjectInstance + user_subject_names = user.subjects.values_list('subject', flat=True) + # Get the corresponding Subject models + user_subjects = Subject.objects.filter(name__in=user_subject_names) - print(subject_names) + # Now fetch the StudyGroups with the matching Subject models that are within 100m + studygroups = StudyGroup.objects.filter(subject__in=user_subjects).annotate( + distance=Distance('location', user_location)).filter(distance__lte=100) + + for group in studygroups: + # Get all StudentStatus locations of the group + group_locations = group.users.values_list('location', flat=True) + # Convert string locations to GEOSGeometry objects + point_locations = [fromstr(loc, srid=4326) + for loc in group_locations] + # Calculate distances between every pair of locations + distances = [(loc1.distance( + loc2)*100000)for loc1 in point_locations for loc2 in point_locations] + # Get the maximum distance + group_radius = max(distances) if distances else 0 + group_radius = max(group_radius, 15) + # Annotate the group with the radius + group.radius = group_radius - # Now fetch the StudyGroups with the matching subject names that are within 50m - studygroups = StudyGroup.objects.filter(subject_name__in=subject_names).annotate( - distance=Distance('location', user_location)).filter(distance__lte=50) return studygroups