Skip to content

Commit a57202b

Browse files
authored
Merge pull request #17 from DevOps-Cloud-Team5/SCRUM-83-Manipulate-lectures
add support for lecture manipulation
2 parents 829ce1d + 155acb9 commit a57202b

File tree

3 files changed

+131
-23
lines changed

3 files changed

+131
-23
lines changed

api/serializers.py

+34-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
77
from rest_framework.validators import UniqueValidator
88

9-
from .models import AccountRoles, Course
9+
from .models import AccountRoles, Course, CourseLecture, LectureTypes
1010

1111
User = get_user_model()
1212

@@ -29,6 +29,11 @@ class CourseSerializer(serializers.ModelSerializer):
2929
class Meta:
3030
model = Course
3131
fields = '__all__'
32+
33+
class LectureSerializer(serializers.ModelSerializer):
34+
class Meta:
35+
model = CourseLecture
36+
fields = '__all__'
3237

3338
# Defines the rules for registering a new user. Required fields, validation rules etc.
3439
class CreateUserSerializer(serializers.ModelSerializer):
@@ -73,8 +78,6 @@ def create(self, validated_data):
7378
c.save()
7479
return c
7580

76-
77-
7881
class MassEnrollSerializer(serializers.Serializer):
7982
usernames = serializers.ListField(required=True, allow_empty=False, child=serializers.CharField(max_length=150))
8083

@@ -88,4 +91,32 @@ def validate_enroll(self, username):
8891

8992
def validate(self, attrs):
9093
for username in attrs["usernames"]: self.validate_enroll(username)
94+
return attrs
95+
96+
class AddLectureSerializer(serializers.Serializer):
97+
MINIMUM_LECTURE_LENGTH = 10 # Minimum lecture length of 10 minutes
98+
99+
start_time = serializers.DateTimeField(required=True)
100+
end_time = serializers.DateTimeField(required=True)
101+
lecture_type = serializers.CharField(required=True)
102+
103+
def validate(self, attrs):
104+
start_time, end_time = attrs["start_time"], attrs["end_time"]
105+
if start_time > end_time:
106+
raise serializers.ValidationError({"error": f"end_time has to be after start_time"})
107+
108+
lecture_length = (end_time - start_time).seconds / 60
109+
if lecture_length < self.MINIMUM_LECTURE_LENGTH:
110+
raise serializers.ValidationError({"error": f"lecture has to be at least {self.MINIMUM_LECTURE_LENGTH} minutes"})
111+
112+
lecture_type = attrs["lecture_type"]
113+
if lecture_type not in LectureTypes.values:
114+
raise serializers.ValidationError({"error": f"invalid lecture type: '{lecture_type}'"})
115+
116+
course : Course = self.context.get("course")
117+
for lecture in course.get_lectures():
118+
for timestamp in [start_time, end_time]:
119+
if lecture.start_time < timestamp and timestamp < lecture.end_time:
120+
raise serializers.ValidationError({"error": "there is already an active lecture during this time range"})
121+
91122
return attrs

api/urls.py

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
from rest_framework_simplejwt.views import TokenRefreshView, TokenVerifyView, TokenBlacklistView
1818
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
1919
from .views import (
20+
AddLectureView,
21+
GetCourseLecturesView,
22+
GetLectureView,
2023
MassEnrollCourseView,
2124
test,
2225
genAdmin,
@@ -62,6 +65,16 @@
6265
path('course/mass_enroll/<pk>', MassEnrollCourseView.as_view(), name='course_mass_enroll'),
6366
path('course/get/<pk>', GetCourseByName.as_view(), name='course_get'),
6467
path('course/getall/', GetCoursesAll.as_view(), name='course_getall'),
68+
69+
path('course/lecture/<pk>/get', GetCourseLecturesView.as_view(), name='course_get_lecture'),
70+
path('course/lecture/<pk>/add', AddLectureView.as_view(), name='course_add_lecture'),
71+
# path('course/lecture/<pk>/update', GetCourseByName.as_view(), name='lecture_add'), # TODO
72+
# path('course/lecture/<pk>/delete', GetCourseByName.as_view(), name='lecture_add'),
73+
74+
path('lecture/<pk>/get', GetLectureView.as_view(), name='lecture_get'),
75+
path('lecture/<pk>/student_att', GetLectureView.as_view(), name='lecture_get'),
76+
path('lecture/<pk>/teacher_att', GetLectureView.as_view(), name='lecture_get'),
77+
6578

6679
# Documentation
6780
path('schema/', SpectacularAPIView.as_view(), name='schema'),

api/views.py

+84-20
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
from drf_spectacular.utils import extend_schema, OpenApiResponse
1414

1515
from .permissions import IsTeacher, IsAdmin, IsStudent
16-
from .models import Course, AccountRoles
17-
from .serializers import CustomTokenSerializer, CreateUserSerializer, MassEnrollSerializer, UserSerializer, CourseCreateSerializer, CourseSerializer
16+
from .models import Course, AccountRoles, CourseLecture
17+
from .serializers import AddLectureSerializer, CustomTokenSerializer, CreateUserSerializer, LectureSerializer, MassEnrollSerializer, UserSerializer, CourseCreateSerializer, CourseSerializer
1818

1919
import pdb
2020

@@ -110,11 +110,10 @@ class GetUserByUsername(generics.RetrieveAPIView):
110110

111111
def get(self, _, username):
112112
queryset = self.get_queryset().filter(username=username)
113-
serializer = self.serializer_class(queryset, many=True)
114-
if len(serializer.data) == 0:
113+
if not queryset:
115114
return Response({"error": f"user '{username}' not found"}, status=status.HTTP_404_NOT_FOUND)
116-
else:
117-
return Response(serializer.data, status=status.HTTP_200_OK)
115+
serializer = self.serializer_class(queryset[0])
116+
return Response(serializer.data, status=status.HTTP_200_OK)
118117

119118
class GetUsersByRole(generics.ListAPIView):
120119
authentication_classes = [JWTAuthentication]
@@ -175,6 +174,35 @@ class DestroyCourseView(generics.DestroyAPIView):
175174
queryset = Course.objects.all()
176175
serializer_class = CourseSerializer
177176

177+
class GetCourseByName(generics.RetrieveAPIView):
178+
authentication_classes = [JWTAuthentication]
179+
permission_classes = [IsStudent]
180+
181+
queryset = Course.objects.all()
182+
serializer_class = CourseSerializer
183+
184+
def get(self, _, pk):
185+
queryset = self.get_queryset().filter(pk=pk)
186+
if not queryset:
187+
return Response({"error": f"course id '{pk}' not found"}, status=status.HTTP_404_NOT_FOUND)
188+
serializer = self.serializer_class(queryset[0])
189+
return Response(serializer.data, status=status.HTTP_200_OK)
190+
191+
class GetCoursesAll(generics.ListAPIView):
192+
authentication_classes = [JWTAuthentication]
193+
permission_classes = [IsStudent]
194+
195+
queryset = Course.objects.all()
196+
serializer_class = CourseSerializer
197+
198+
def get(self, _):
199+
queryset = self.get_queryset()
200+
serializer = self.serializer_class(queryset, many=True)
201+
if len(serializer.data) == 0:
202+
return Response({"error": "no courses found"}, status=status.HTTP_404_NOT_FOUND)
203+
else:
204+
return Response(serializer.data, status=status.HTTP_200_OK)
205+
178206
class EnrollCourseView(generics.GenericAPIView):
179207
authentication_classes = [JWTAuthentication]
180208
permission_classes = [IsStudent]
@@ -223,32 +251,68 @@ def post(self, request, *args, **kwargs):
223251

224252
return Response({"ok": f"succesfully enrolled {len(usernames)} students"}, status=status.HTTP_200_OK)
225253

226-
class GetCourseByName(generics.RetrieveAPIView):
254+
class GetCourseLecturesView(generics.ListAPIView):
227255
authentication_classes = [JWTAuthentication]
228256
permission_classes = [IsStudent]
257+
lookup_field = 'pk'
229258

230259
queryset = Course.objects.all()
231260
serializer_class = CourseSerializer
232261

233-
def get(self, _, pk):
234-
queryset = self.get_queryset().filter(pk=pk)
235-
serializer = self.serializer_class(queryset, many=True)
262+
def get(self, _, *args, **kwargs):
263+
course : Course = self.get_object()
264+
lectures = course.get_lectures()
265+
serializer = LectureSerializer(lectures, many=True)
236266
if len(serializer.data) == 0:
237-
return Response({"error": f"course id '{pk}' not found"}, status=status.HTTP_404_NOT_FOUND)
267+
return Response({"error": "no lectures found"}, status=status.HTTP_404_NOT_FOUND)
238268
else:
239269
return Response(serializer.data, status=status.HTTP_200_OK)
240270

241-
class GetCoursesAll(generics.ListAPIView):
271+
class AddLectureView(generics.GenericAPIView):
242272
authentication_classes = [JWTAuthentication]
243-
permission_classes = [IsStudent]
273+
permission_classes = [IsTeacher]
274+
lookup_field = 'pk'
244275

245276
queryset = Course.objects.all()
246277
serializer_class = CourseSerializer
278+
279+
def post(self, request, *args, **kwargs):
280+
course : Course = self.get_object()
281+
result = AddLectureSerializer(data=request.data, context={ "course": course })
282+
result.is_valid(raise_exception=True)
283+
284+
data = result.data
285+
course.add_lecture_to_course(data["start_time"], data["end_time"], data["lecture_type"])
286+
287+
return Response({"ok": f"successfully created lecture"}, status=status.HTTP_200_OK)
288+
289+
class GetLectureView(generics.RetrieveAPIView):
290+
authentication_classes = [JWTAuthentication]
291+
permission_classes = [IsStudent]
292+
293+
queryset = CourseLecture.objects.all()
294+
serializer_class = LectureSerializer
247295

248-
def get(self, _):
249-
queryset = self.get_queryset()
250-
serializer = self.serializer_class(queryset, many=True)
251-
if len(serializer.data) == 0:
252-
return Response({"error": "no courses found"}, status=status.HTTP_404_NOT_FOUND)
253-
else:
254-
return Response(serializer.data, status=status.HTTP_200_OK)
296+
def get(self, _, pk):
297+
queryset = self.get_queryset().filter(pk=pk)
298+
if not queryset:
299+
return Response({"error": f"course id '{pk}' not found"}, status=status.HTTP_404_NOT_FOUND)
300+
serializer = self.serializer_class(queryset[0])
301+
return Response(serializer.data, status=status.HTTP_200_OK)
302+
303+
class SetStudentAttView(generics.GenericAPIView):
304+
authentication_classes = [JWTAuthentication]
305+
permission_classes = [IsStudent]
306+
lookup_field = 'pk'
307+
308+
queryset = CourseLecture.objects.all()
309+
serializer_class = CourseLecture
310+
311+
def post(self, request, *args, **kwargs):
312+
if request.user.role != AccountRoles.STUDENT:
313+
return Response({"error": f"only a student can set their attendence to a lecture"}, status=status.HTTP_200_OK)
314+
315+
course : CourseLecture = self.get_object()
316+
317+
318+
return Response({"ok": f"successfully set attendence"}, status=status.HTTP_200_OK)

0 commit comments

Comments
 (0)