From c8fca412b88b49ed7ccd76108d2aa35fd4e47f44 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sun, 1 Oct 2023 01:24:58 +0800 Subject: [PATCH] Added endpoint for querying avatars of users in a study group and for individual study group details --- .../0003_alter_customuser_options_and_more.py | 21 ++++ stude/schema.yml | 106 ++++++++++++++++-- stude/study_groups/serializers.py | 25 +++++ stude/study_groups/urls.py | 7 +- stude/study_groups/views.py | 47 +++++++- stude/studygroup_messages/serializers.py | 7 +- stude/studygroup_messages/views.py | 2 +- 7 files changed, 199 insertions(+), 16 deletions(-) create mode 100644 stude/accounts/migrations/0003_alter_customuser_options_and_more.py diff --git a/stude/accounts/migrations/0003_alter_customuser_options_and_more.py b/stude/accounts/migrations/0003_alter_customuser_options_and_more.py new file mode 100644 index 0000000..0828deb --- /dev/null +++ b/stude/accounts/migrations/0003_alter_customuser_options_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.3 on 2023-09-30 15:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0002_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='customuser', + options={}, + ), + migrations.AlterUniqueTogether( + name='customuser', + unique_together={('first_name', 'last_name')}, + ), + ] diff --git a/stude/schema.yml b/stude/schema.yml index d8357cb..9d52df3 100644 --- a/stude/schema.yml +++ b/stude/schema.yml @@ -135,7 +135,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this user. + description: A unique integer value identifying this custom user. required: true tags: - api @@ -155,7 +155,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this user. + description: A unique integer value identifying this custom user. required: true tags: - api @@ -187,7 +187,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this user. + description: A unique integer value identifying this custom user. required: true tags: - api @@ -218,7 +218,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this user. + description: A unique integer value identifying this custom user. required: true tags: - api @@ -763,6 +763,26 @@ paths: items: $ref: '#/components/schemas/StudyGroup' description: '' + /api/v1/study_groups/{name}/: + get: + operationId: api_v1_study_groups_retrieve + parameters: + - in: path + name: name + schema: + type: string + required: true + tags: + - api + security: + - jwtAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/StudyGroupDetail' + description: '' /api/v1/study_groups/create/: post: operationId: api_v1_study_groups_create_create @@ -851,7 +871,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/StudyGroup' + $ref: '#/components/schemas/StudyGroupDistance' description: '' /api/v1/subjects/: get: @@ -1068,21 +1088,18 @@ components: readOnly: true user: type: string + description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ + only. study_group: type: string message_content: type: string - maxLength: 1024 timestamp: type: string format: date-time - readOnly: true required: - id - message_content - - study_group - - timestamp - - user PasswordResetConfirm: type: object properties: @@ -1348,6 +1365,75 @@ components: - name - subject - timestamp + StudyGroupDetail: + type: object + properties: + id: + type: integer + readOnly: true + name: + type: string + students: + type: array + items: + type: string + subject: + type: string + location: + type: string + landmark: + type: string + nullable: true + timestamp: + type: string + format: date + readOnly: true + required: + - id + - location + - name + - students + - subject + - timestamp + StudyGroupDistance: + type: object + properties: + id: + type: integer + readOnly: true + name: + type: string + students: + type: array + items: + type: string + subject: + type: string + location: + type: string + landmark: + type: string + nullable: true + radius: + type: number + format: double + distance: + type: string + readOnly: true + default: 0 + timestamp: + type: string + format: date + readOnly: true + required: + - distance + - id + - location + - name + - radius + - students + - subject + - timestamp SubjectInstance: type: object properties: diff --git a/stude/study_groups/serializers.py b/stude/study_groups/serializers.py index eb8ea76..c420932 100644 --- a/stude/study_groups/serializers.py +++ b/stude/study_groups/serializers.py @@ -4,6 +4,16 @@ from accounts.models import CustomUser from subjects.models import Subject from drf_extra_fields.geo_fields import PointField from landmarks.models import Landmark +from drf_extra_fields.fields import Base64ImageField +from djoser.serializers import UserSerializer as BaseUserSerializer + + +class CustomUserAvatarSerializer(BaseUserSerializer): + avatar = Base64ImageField() + + class Meta(BaseUserSerializer.Meta): + model = CustomUser + fields = ['avatar', 'username'] class CustomUserKeyRelatedField(serializers.PrimaryKeyRelatedField): @@ -13,6 +23,21 @@ class CustomUserKeyRelatedField(serializers.PrimaryKeyRelatedField): return str(value) +class StudyGroupDetailSerializer(serializers.ModelSerializer): + name = serializers.CharField() + students = serializers.StringRelatedField(many=True) + subject = serializers.SlugRelatedField( + many=False, slug_field='name', queryset=Subject.objects.all(), required=True, allow_null=False) + location = PointField() + landmark = serializers.SlugRelatedField( + queryset=Landmark.objects.all(), many=False, slug_field='name', required=False, allow_null=True) + + class Meta: + model = StudyGroup + fields = '__all__' + read_only_fields = ['landmark', 'radius', 'students', 'distance'] + + class StudyGroupSerializer(serializers.ModelSerializer): name = serializers.CharField() students = serializers.StringRelatedField(many=True) diff --git a/stude/study_groups/urls.py b/stude/study_groups/urls.py index 9bee14f..ec9625d 100644 --- a/stude/study_groups/urls.py +++ b/stude/study_groups/urls.py @@ -1,5 +1,5 @@ from django.urls import include, path -from .views import StudyGroupListView, StudyGroupListNearView, StudyGroupCreateView +from .views import StudyGroupListView, StudyGroupListNearView, StudyGroupCreateView, StudyGroupDetailView, StudyGroupAvatarsView from rest_framework import routers router = routers.DefaultRouter() @@ -7,7 +7,10 @@ router.register(r'create', StudyGroupCreateView, basename='Create Study Group') urlpatterns = [ + path('', StudyGroupListView.as_view()), - path('near/', StudyGroupListNearView.as_view()), path('', include(router.urls)), + path('near/', StudyGroupListNearView.as_view()), + path('member_avatars/', StudyGroupAvatarsView.as_view()), + path('/', StudyGroupDetailView.as_view()), ] diff --git a/stude/study_groups/views.py b/stude/study_groups/views.py index 80335da..0afd242 100644 --- a/stude/study_groups/views.py +++ b/stude/study_groups/views.py @@ -2,7 +2,7 @@ from django.shortcuts import render from rest_framework import generics, mixins from rest_framework.exceptions import PermissionDenied from rest_framework.permissions import IsAuthenticated -from .serializers import StudyGroupSerializer, StudyGroupCreateSerializer, StudyGroupDistanceSerializer +from .serializers import StudyGroupSerializer, StudyGroupCreateSerializer, StudyGroupDistanceSerializer, StudyGroupDetailSerializer, CustomUserAvatarSerializer from .models import StudyGroup from subjects.models import Subject, SubjectInstance from student_status.models import StudentStatus @@ -12,10 +12,48 @@ from django.contrib.gis.db.models.functions import Distance from rest_framework import status from rest_framework.response import Response from rest_framework import permissions +from accounts.models import CustomUser # Create your views here. +class StudyGroupAvatarsView(generics.ListAPIView): + permission_classes = [IsAuthenticated] + serializer_class = CustomUserAvatarSerializer + queryset = CustomUser.objects.all() + + def get_queryset(self): + # Get user + user = self.request.user + + # Get status of user + user_status = StudentStatus.objects.filter(user=user).first() + + # Return error if not in study group + if (not user_status.study_group): + raise exceptions.ValidationError("You are not in a study group") + + # Get study group of user + user_study_group = user_status.study_group + + # Get students in that study group + study_group_student_ids = user_study_group.students.values_list( + 'user', flat=True) + + # Then get user instances of those students + study_group_users = CustomUser.objects.filter( + id__in=study_group_student_ids) + + # Return to be parsed by serializer + return study_group_users + + +class StudyGroupMembersAvatarView(generics.ListAPIView): + permission_classes = [IsAuthenticated] + serializer_class = StudyGroupCreateSerializer + queryset = CustomUser.objects.all() + + class StudyGroupCreateView(viewsets.ModelViewSet): http_method_names = ['patch', 'post', 'delete'] permission_classes = [IsAuthenticated] @@ -23,6 +61,13 @@ class StudyGroupCreateView(viewsets.ModelViewSet): queryset = StudyGroup.objects.all() +class StudyGroupDetailView(generics.RetrieveAPIView): + permission_classes = [IsAuthenticated] + serializer_class = StudyGroupDetailSerializer + queryset = StudyGroup.objects.all() + lookup_field = 'name' + + class StudyGroupListView(generics.ListAPIView): permission_classes = [IsAuthenticated] serializer_class = StudyGroupSerializer diff --git a/stude/studygroup_messages/serializers.py b/stude/studygroup_messages/serializers.py index 89b11da..991d70c 100644 --- a/stude/studygroup_messages/serializers.py +++ b/stude/studygroup_messages/serializers.py @@ -6,10 +6,13 @@ from study_groups.models import StudyGroup class MessageSerializer(serializers.ModelSerializer): user = serializers.SlugRelatedField( - queryset=CustomUser.objects.all(), slug_field='full_name', required=True) + queryset=CustomUser.objects.all(), slug_field='username', required=False) study_group = serializers.SlugRelatedField( - queryset=StudyGroup.objects.all(), slug_field='name', required=True) + queryset=StudyGroup.objects.all(), slug_field='name', required=False) + message_content = serializers.CharField() + timestamp = serializers.DateTimeField(format="%I:%M %p", required=False) class Meta: model = Message fields = '__all__' + read_only_fields = ['user', 'study_group', 'timestamp'] diff --git a/stude/studygroup_messages/views.py b/stude/studygroup_messages/views.py index 1dec029..827f11c 100644 --- a/stude/studygroup_messages/views.py +++ b/stude/studygroup_messages/views.py @@ -48,5 +48,5 @@ class MessageViewSet(viewsets.ModelViewSet): # Now fetch the Messages matching the study group id messages = Message.objects.filter( - study_group=user_study_group).order_by('-timestamp') + study_group=user_study_group).order_by('timestamp') return messages