From 0e3353d489fd54a2c84c712ae04e44a9f8d8cd90 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Wed, 19 Dec 2018 13:46:40 +0530 Subject: Provide API version 1 for Yaksh - Serializers for Question, Quiz, QuestionPaper, AnswerPaper - Can create questions, quizzes, question and answer papers - Can check mcq, mcc and code questions - Tests for the API --- api/views.py | 330 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 api/views.py (limited to 'api/views.py') diff --git a/api/views.py b/api/views.py new file mode 100644 index 0000000..14e04f0 --- /dev/null +++ b/api/views.py @@ -0,0 +1,330 @@ +from yaksh.models import ( + Question, Quiz, QuestionPaper, QuestionSet, AnswerPaper, Course, Answer +) +from api.serializers import ( + QuestionSerializer, QuizSerializer, QuestionPaperSerializer, + AnswerPaperSerializer +) +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from rest_framework import permissions +from django.http import Http404 +from yaksh.code_server import get_result as get_result_from_code_server +from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME +import json + + +class QuestionList(APIView): + """ List all questions or create a new question. """ + + def get(self, request, format=None): + questions = Question.objects.filter(user=request.user) + serializer = QuestionSerializer(questions, many=True) + return Response(serializer.data) + + + def post(self, request, format=None): + serializer = QuestionSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class QuestionDetail(APIView): + """ Retrieve, update or delete a question """ + + def get_question(self, pk, user): + try: + return Question.objects.get(pk=pk, user=user) + except Question.DoesNotExist: + raise Http404 + + def get(self, request, pk, format=None): + question = self.get_question(pk, request.user) + serializer = QuestionSerializer(question) + return Response(serializer.data) + + def put(self, request, pk, format=None): + question = self.get_question(pk, request.user) + serializer = QuestionSerializer(question, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk, format=None): + question = self.get_question(pk, request.user) + question.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + + +class AnswerPaperList(APIView): + + def get_questionpaper(self, pk): + try: + return QuestionPaper.objects.get(pk=pk) + except QuestionPaper.DoesNotExist: + raise Http404 + + def get_course(self, pk): + try: + return Course.objects.get(pk=pk) + except Course.DoesNotExist: + raise Http404 + + def get_answerpapers(self, user): + return AnswerPaper.objects.filter(question_paper__quiz__creator=user) + + def get(self, request, format=None): + user = request.user + answerpapers = self.get_answerpapers(user) + serializer = AnswerPaperSerializer(answerpapers, many=True) + return Response(serializer.data) + + def is_user_allowed(self, user, course): + ''' if user is student or teacher or creator then allow ''' + return user in course.students.all() or user in course.teachers.all() or user == course.creator + + def post(self, request, format=None): + try: + questionpaperid = request.data['question_paper'] + attempt_number = request.data['attempt_number'] + course_id = request.data['course'] + except KeyError: + return Response(status=status.HTTP_400_BAD_REQUEST) + user = request.user + ip = request.META['REMOTE_ADDR'] + questionpaper = self.get_questionpaper(questionpaperid) + course = self.get_course(course_id) + if not self.is_user_allowed(user, course): + return Response(status=status.HTTP_400_BAD_REQUEST) + answerpaper = questionpaper.make_answerpaper(user, ip, attempt_number, course_id) + serializer = AnswerPaperSerializer(answerpaper) + return Response(serializer.data, status=status.HTTP_201_CREATED) + + +class AnswerValidator(APIView): + + def get_answerpaper(self, pk, user): + try: + return AnswerPaper.objects.get(pk=pk, user=user) + except AnswerPaper.DoesNotExist: + raise Http404 + + def get_question(self, pk, answerpaper): + try: + question = Question.objects.get(pk=pk) + if question in answerpaper.questions.all(): + return question + else: + raise Http404 + except AnswerPaper.DoesNotExist: + raise Http404 + + def get_answer(self, pk): + try: + return Answer.objects.get(pk=pk) + except Answer.DoesNotExist: + raise Http404 + + def post(self, request, answerpaper_id, question_id, format=None): + try: + user_answer = request.data['answer'] + except KeyError: + return Response(status=status.HTTP_400_BAD_REQUEST) + user = request.user + answerpaper = self.get_answerpaper(answerpaper_id, user) + question = self.get_question(question_id, answerpaper) + # save answer uid + answer = Answer.objects.create(question=question, answer=user_answer) + answerpaper.answers.add(answer) + answerpaper.save() + json_data = None + if question.type in ['code', 'upload']: + json_data = question.consolidate_answer_data(user_answer, user) + result = answerpaper.validate_answer(user_answer, question, json_data, answer.id) + + # updaTE RESult + if question.type not in ['code', 'upload']: + if result.get('success'): + answer.correct = True + answer.marks = question.points + answer.error = json.dumps(result.get('error')) + answer.save() + answerpaper.update_marks(state='inprogress') + return Response(result) + + def get(self, request, uid): + answer = self.get_answer(uid) + url = '{0}:{1}'.format(SERVER_HOST_NAME, SERVER_POOL_PORT) + result = get_result_from_code_server(url, uid) + # update result + if result['status'] == 'done': + final_result = json.loads(result.get('result')) + answer.error = json.dumps(result.get('error')) + if final_result.get('success'): + answer.correct = True + answer.marks = answer.question.points + answer.save() + answerpaper = answer.answerpaper_set.get() + answerpaper.update_marks(state='inprogress') + return Response(result) + + +class QuizList(APIView): + """ List all quizzes or create a new quiz """ + + def get(self, request, format=None): + quizzes = Quiz.objects.filter(creator=request.user) + serializer = QuizSerializer(quizzes, many=True) + return Response(serializer.data) + + def post(self, request, format=None): + serializer = QuizSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class QuizDetail(APIView): + """ Retrieve, update or delete a quiz """ + + def get_quiz(self, pk, user): + try: + return Quiz.objects.get(pk=pk, creator=user) + except Quiz.DoesNotExist: + raise Http404 + + def get(self, request, pk, format=None): + quiz = self.get_quiz(pk, request.user) + serializer = QuizSerializer(quiz) + return Response(serializer.data) + + def put(self, request, pk, format=None): + quiz = self.get_quiz(pk, request.user) + serializer = QuizSerializer(quiz, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk, format=None): + quiz = self.get_quiz(pk, request.user) + quiz.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + + +class QuestionPaperList(APIView): + """ List all question papers or create a new question paper """ + + def get_questionpapers(self, user): + return QuestionPaper.objects.filter(quiz__creator=user) + + def questionpaper_exists(self, quiz_id): + return QuestionPaper.objects.filter(quiz=quiz_id).exists() + + def check_quiz_creator(self, user, quiz_id): + try: + Quiz.objects.get(pk=quiz_id, creator=user) + except Quiz.DoesNotExist: + raise Http404 + + def check_questions_creator(self, user, question_ids): + for question_id in question_ids: + try: + Question.objects.get(pk=question_id, user=user) + except Question.DoesNotExist: + raise Http404 + + def check_questionsets_creator(self, user, questionset_ids): + for question_id in questionset_ids: + try: + questionset = QuestionSet.objects.get(pk=question_id) + for question in questionset.questions.all(): + Question.objects.get(pk=question.id, user=user) + except (QuestionSet.DoesNotExist, Question.DoesNotExist): + raise Http404 + + def get(self, request, format=None): + questionpapers = self.get_questionpapers(request.user) + serializer = QuestionPaperSerializer(questionpapers, many=True) + return Response(serializer.data) + + def post(self, request, format=None): + serializer = QuestionPaperSerializer(data=request.data) + if serializer.is_valid(): + user = request.user + quiz_id = request.data.get('quiz') + question_ids = request.data.get('fixed_questions', []) + questionset_ids = request.data.get('random_questions', []) + if self.questionpaper_exists(quiz_id): + return Response({'error': 'Already exists'}, + status=status.HTTP_409_CONFLICT) + self.check_quiz_creator(user, quiz_id) + self.check_questions_creator(user, question_ids) + self.check_questionsets_creator(user, questionset_ids) + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status.HTTP_400_BAD_REQUEST) + + +class QuestionPaperDetail(APIView): + """ Retrieve, update or delete a question paper""" + + def get_questionpaper(self, pk, user): + try: + return QuestionPaper.objects.get(pk=pk, quiz__creator=user) + except QuestionPaper.DoesNotExist: + raise Http404 + + def get(self, request, pk, format=None): + questionpaper = self.get_questionpaper(pk, request.user) + serializer = QuestionPaperSerializer(questionpaper) + return Response(serializer.data) + + def check_quiz_creator(self, user, quiz_id): + try: + Quiz.objects.get(pk=quiz_id, creator=user) + except Quiz.DoesNotExist: + raise Http404 + + def check_questions_creator(self, user, question_ids): + for question_id in question_ids: + try: + Question.objects.get(pk=question_id, user=user) + except Question.DoesNotExist: + raise Http404 + + def check_questionsets_creator(self, user, questionset_ids): + for question_id in questionset_ids: + try: + questionset = QuestionSet.objects.get(pk=question_id) + for question in questionset.questions.all(): + Question.objects.get(pk=question.id, user=user) + except (QuestionSet.DoesNotExist, Question.DoesNotExist): + raise Http404 + + def put(self, request, pk, format=None): + user = request.user + questionpaper = self.get_questionpaper(pk, user) + serializer = QuestionPaperSerializer(questionpaper, data=request.data) + if serializer.is_valid(): + user = request.user + quiz_id = request.data.get('quiz') + question_ids = request.data.get('fixed_questions', []) + questionset_ids = request.data.get('random_questions', []) + self.check_quiz_creator(user, quiz_id) + self.check_questions_creator(user, question_ids) + self.check_questionsets_creator(user, questionset_ids) + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk, format=None): + questionpaper = self.get_questionpaper(pk, request.user) + questionpaper.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + -- cgit From 81d1e4e4ff4406f7f3823c40a82cf21096f2ccec Mon Sep 17 00:00:00 2001 From: prathamesh Date: Thu, 20 Dec 2018 10:53:23 +0530 Subject: Resolve PEP8 issues --- api/views.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'api/views.py') diff --git a/api/views.py b/api/views.py index 14e04f0..ff21828 100644 --- a/api/views.py +++ b/api/views.py @@ -17,13 +17,12 @@ import json class QuestionList(APIView): """ List all questions or create a new question. """ - + def get(self, request, format=None): questions = Question.objects.filter(user=request.user) serializer = QuestionSerializer(questions, many=True) return Response(serializer.data) - def post(self, request, format=None): serializer = QuestionSerializer(data=request.data) if serializer.is_valid(): @@ -85,7 +84,8 @@ class AnswerPaperList(APIView): def is_user_allowed(self, user, course): ''' if user is student or teacher or creator then allow ''' - return user in course.students.all() or user in course.teachers.all() or user == course.creator + return user in course.students.all() or user in course.teachers.all() \ + or user == course.creator def post(self, request, format=None): try: @@ -100,13 +100,14 @@ class AnswerPaperList(APIView): course = self.get_course(course_id) if not self.is_user_allowed(user, course): return Response(status=status.HTTP_400_BAD_REQUEST) - answerpaper = questionpaper.make_answerpaper(user, ip, attempt_number, course_id) + answerpaper = questionpaper.make_answerpaper(user, ip, attempt_number, + course_id) serializer = AnswerPaperSerializer(answerpaper) return Response(serializer.data, status=status.HTTP_201_CREATED) class AnswerValidator(APIView): - + def get_answerpaper(self, pk, user): try: return AnswerPaper.objects.get(pk=pk, user=user) @@ -144,7 +145,8 @@ class AnswerValidator(APIView): json_data = None if question.type in ['code', 'upload']: json_data = question.consolidate_answer_data(user_answer, user) - result = answerpaper.validate_answer(user_answer, question, json_data, answer.id) + result = answerpaper.validate_answer(user_answer, question, json_data, + answer.id) # updaTE RESult if question.type not in ['code', 'upload']: @@ -273,7 +275,7 @@ class QuestionPaperList(APIView): class QuestionPaperDetail(APIView): """ Retrieve, update or delete a question paper""" - + def get_questionpaper(self, pk, user): try: return QuestionPaper.objects.get(pk=pk, quiz__creator=user) @@ -327,4 +329,3 @@ class QuestionPaperDetail(APIView): questionpaper = self.get_questionpaper(pk, request.user) questionpaper.delete() return Response(status=status.HTTP_204_NO_CONTENT) - -- cgit From a7b4b9e0c6699987d1dee4007e9c62da15559432 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Fri, 21 Jun 2019 03:23:45 +0530 Subject: Add API to get complete course details and AnswerPaper for the quiz Courses are retrieved in which the user is a student. Complete course info is made available. Answerpaper is created if does not exists on start quiz. AnswerPaper detail info is made available. If attempts are not allowed then the message is retrieved. --- api/views.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'api/views.py') diff --git a/api/views.py b/api/views.py index ff21828..b81e895 100644 --- a/api/views.py +++ b/api/views.py @@ -3,7 +3,7 @@ from yaksh.models import ( ) from api.serializers import ( QuestionSerializer, QuizSerializer, QuestionPaperSerializer, - AnswerPaperSerializer + AnswerPaperSerializer, CourseSerializer ) from rest_framework.views import APIView from rest_framework.response import Response @@ -31,6 +31,49 @@ class QuestionList(APIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) +class CourseList(APIView): + """ List all courses """ + + def get(self, request, format=None): + courses = Course.objects.filter(students=request.user) + serializer = CourseSerializer(courses, many=True) + return Response(serializer.data) + + +class StartQuiz(APIView): + """ Retrieve Answerpaper. If does not exists then create one """ + + def get_quiz(self, pk, user): + try: + return Quiz.objects.get(pk=pk) + except Quiz.DoesNotExist: + raise Http404 + + def get(self, request, course_id, quiz_id, format=None): + user = request.user + quiz = self.get_quiz(quiz_id, user) + questionpaper = quiz.questionpaper_set.first() + + last_attempt = AnswerPaper.objects.get_user_last_attempt( + questionpaper, user, course_id) + if last_attempt and last_attempt.is_attempt_inprogress(): + serializer = AnswerPaperSerializer(last_attempt) + return Response(serializer.data) + + can_attempt, msg = questionpaper.can_attempt_now(user, course_id) + if not can_attempt: + return Response({'message': msg}) + if not last_attempt: + attempt_number = 1 + else: + attempt_number = last_attempt.attempt_number + 1 + ip = request.META['REMOTE_ADDR'] + answerpaper = questionpaper.make_answerpaper(user, ip, attempt_number, + course_id) + serializer = AnswerPaperSerializer(answerpaper) + return Response(serializer.data, status=status.HTTP_201_CREATED) + + class QuestionDetail(APIView): """ Retrieve, update or delete a question """ -- cgit From ec8e7485a8306dd9851d134578d3721abc7b9e8a Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 30 Mar 2020 22:11:05 +0530 Subject: Add Login endpoint, quit quiz endpoint --- api/views.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 7 deletions(-) (limited to 'api/views.py') diff --git a/api/views.py b/api/views.py index b81e895..5bbc077 100644 --- a/api/views.py +++ b/api/views.py @@ -9,7 +9,12 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from rest_framework import permissions +from rest_framework.authtoken.models import Token +from rest_framework.decorators import ( + api_view, authentication_classes, permission_classes +) from django.http import Http404 +from django.contrib.auth import authenticate from yaksh.code_server import get_result as get_result_from_code_server from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME import json @@ -50,6 +55,7 @@ class StartQuiz(APIView): raise Http404 def get(self, request, course_id, quiz_id, format=None): + context = {} user = request.user quiz = self.get_quiz(quiz_id, user) questionpaper = quiz.questionpaper_set.first() @@ -58,7 +64,9 @@ class StartQuiz(APIView): questionpaper, user, course_id) if last_attempt and last_attempt.is_attempt_inprogress(): serializer = AnswerPaperSerializer(last_attempt) - return Response(serializer.data) + context["time_left"] = last_attempt.time_left() + context["answerpaper"] = serializer.data + return Response(context) can_attempt, msg = questionpaper.can_attempt_now(user, course_id) if not can_attempt: @@ -71,7 +79,9 @@ class StartQuiz(APIView): answerpaper = questionpaper.make_answerpaper(user, ip, attempt_number, course_id) serializer = AnswerPaperSerializer(answerpaper) - return Response(serializer.data, status=status.HTTP_201_CREATED) + context["time_left"] = answerpaper.time_left() + context["answerpaper"] = serializer.data + return Response(context, status=status.HTTP_201_CREATED) class QuestionDetail(APIView): @@ -175,7 +185,7 @@ class AnswerValidator(APIView): def post(self, request, answerpaper_id, question_id, format=None): try: - user_answer = request.data['answer'] + user_answer = str(request.data['answer']) except KeyError: return Response(status=status.HTTP_400_BAD_REQUEST) user = request.user @@ -196,8 +206,8 @@ class AnswerValidator(APIView): if result.get('success'): answer.correct = True answer.marks = question.points - answer.error = json.dumps(result.get('error')) - answer.save() + answer.error = json.dumps(result.get('error')) + answer.save() answerpaper.update_marks(state='inprogress') return Response(result) @@ -208,11 +218,11 @@ class AnswerValidator(APIView): # update result if result['status'] == 'done': final_result = json.loads(result.get('result')) - answer.error = json.dumps(result.get('error')) + answer.error = json.dumps(final_result.get('error')) if final_result.get('success'): answer.correct = True answer.marks = answer.question.points - answer.save() + answer.save() answerpaper = answer.answerpaper_set.get() answerpaper.update_marks(state='inprogress') return Response(result) @@ -372,3 +382,42 @@ class QuestionPaperDetail(APIView): questionpaper = self.get_questionpaper(pk, request.user) questionpaper.delete() return Response(status=status.HTTP_204_NO_CONTENT) + + +class ICourse(APIView): + def get(self, request, pk, format=None): + course = Course.objects.get(id=pk) + serializer = CourseSerializer(course) + return Response(serializer.data) + + +@api_view(['POST']) +@authentication_classes(()) +@permission_classes(()) +def login(request): + data = {} + if request.method == "POST": + username = request.data.get('username') + password = request.data.get('password') + user = authenticate(username=username, password=password) + if user is not None and user.is_authenticated: + token, created = Token.objects.get_or_create(user=user) + data = { + 'token': token.key + } + return Response(data, status=status.HTTP_201_CREATED) + + +class QuitQuiz(APIView): + def get_answerpaper(self, answerpaper_id): + try: + return AnswerPaper.objects.get(id=answerpaper_id) + except AnswerPaper.DoesNotExist: + raise Http404 + + def get(self, request, answerpaper_id, format=None): + answerpaper = self.get_answerpaper(answerpaper_id) + answerpaper.status = 'completed' + answerpaper.save() + serializer = AnswerPaperSerializer(answerpaper) + return Response(serializer.data) \ No newline at end of file -- cgit From edf774b171444e2a671a838bf030931a1800ae12 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Wed, 1 Apr 2020 15:59:17 +0530 Subject: Update ruamel version, change view name - Update ruamel version to 0.16.10. - Change view name from ICourse to GetCourse. --- api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'api/views.py') diff --git a/api/views.py b/api/views.py index 5bbc077..550ee6d 100644 --- a/api/views.py +++ b/api/views.py @@ -384,7 +384,7 @@ class QuestionPaperDetail(APIView): return Response(status=status.HTTP_204_NO_CONTENT) -class ICourse(APIView): +class GetCourse(APIView): def get(self, request, pk, format=None): course = Course.objects.get(id=pk) serializer = CourseSerializer(course) -- cgit From 1d3d121831100d2b33d63899eeaa408cd16fb060 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Fri, 3 Apr 2020 17:59:20 +0530 Subject: Get user_answer based on question type --- api/views.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'api/views.py') diff --git a/api/views.py b/api/views.py index 550ee6d..cd2965c 100644 --- a/api/views.py +++ b/api/views.py @@ -184,13 +184,23 @@ class AnswerValidator(APIView): raise Http404 def post(self, request, answerpaper_id, question_id, format=None): - try: - user_answer = str(request.data['answer']) - except KeyError: - return Response(status=status.HTTP_400_BAD_REQUEST) user = request.user answerpaper = self.get_answerpaper(answerpaper_id, user) question = self.get_question(question_id, answerpaper) + try: + if question.type == 'mcq' or question.type == 'mcc': + user_answer = request.data['answer'] + elif question.type == 'integer': + user_answer = int(request.data['answer'][0]) + elif question.type == 'float': + user_answer = float(request.data['answer'][0]) + elif question.type == 'string': + user_answer = request.data['answer'] + print(user_answer) + else: + user_answer = request.data['answer'] + except KeyError: + return Response(status=status.HTTP_400_BAD_REQUEST) # save answer uid answer = Answer.objects.create(question=question, answer=user_answer) answerpaper.answers.add(answer) -- cgit From bb266a85116a1d3e92fe181305d49e861c3e4ea8 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 6 Apr 2020 17:55:21 +0530 Subject: Remove print statement --- api/views.py | 1 - 1 file changed, 1 deletion(-) (limited to 'api/views.py') diff --git a/api/views.py b/api/views.py index cd2965c..8d2da83 100644 --- a/api/views.py +++ b/api/views.py @@ -196,7 +196,6 @@ class AnswerValidator(APIView): user_answer = float(request.data['answer'][0]) elif question.type == 'string': user_answer = request.data['answer'] - print(user_answer) else: user_answer = request.data['answer'] except KeyError: -- cgit