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/__init__.py | 0 api/apps.py | 5 + api/serializers.py | 25 ++ api/tests.py | 888 +++++++++++++++++++++++++++++++++++++++++++++++++++++ api/urls.py | 23 ++ api/views.py | 330 ++++++++++++++++++++ 6 files changed, 1271 insertions(+) create mode 100644 api/__init__.py create mode 100644 api/apps.py create mode 100644 api/serializers.py create mode 100644 api/tests.py create mode 100644 api/urls.py create mode 100644 api/views.py (limited to 'api') diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/apps.py b/api/apps.py new file mode 100644 index 0000000..d87006d --- /dev/null +++ b/api/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + name = 'api' diff --git a/api/serializers.py b/api/serializers.py new file mode 100644 index 0000000..919e429 --- /dev/null +++ b/api/serializers.py @@ -0,0 +1,25 @@ +from rest_framework import serializers +from yaksh.models import Question, Quiz, QuestionPaper, AnswerPaper + + +class QuestionSerializer(serializers.ModelSerializer): + class Meta: + model = Question + fields = '__all__' + + +class QuizSerializer(serializers.ModelSerializer): + class Meta: + model = Quiz + fields = '__all__' + + +class QuestionPaperSerializer(serializers.ModelSerializer): + class Meta: + model = QuestionPaper + fields = '__all__' + +class AnswerPaperSerializer(serializers.ModelSerializer): + class Meta: + model = AnswerPaper + fields = '__all__' diff --git a/api/tests.py b/api/tests.py new file mode 100644 index 0000000..0930685 --- /dev/null +++ b/api/tests.py @@ -0,0 +1,888 @@ +from django.test import TestCase +from django.core.urlresolvers import reverse +from django.contrib.auth.models import User +from rest_framework.test import APIClient +from rest_framework import status +from yaksh.models import ( + Question, Quiz, QuestionPaper, QuestionSet, + AnswerPaper, Course, LearningModule, LearningUnit, StandardTestCase, + McqTestCase, Profile +) +from api.serializers import ( + QuestionSerializer, QuizSerializer + QuestionPaperSerializer, AnswerPaperSerializer +) +from datetime import datetime +import pytz +from textwrap import dedent +from yaksh.settings import SERVER_POOL_PORT +from yaksh.code_server import ServerPool +from yaksh import settings +from threading import Thread +import time +import json + + +class QuestionListTestCase(TestCase): + """ Test get all questions and create a new question """ + + def setUp(self): + self.client = APIClient() + self.username = 'demo' + self.password = 'demo' + self.user = User.objects.create_user(username=self.username, + password=self.password) + Question.objects.create(summary='test question 1', language='python', + type='mcq', user=self.user) + Question.objects.create(summary='test question 2', language='python', + type='mcq', user=self.user) + + + def test_get_all_questions_anonymous(self): + # When + response = self.client.get(reverse('api:questions')) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_get_all_questions(self): + # Given + questions = Question.objects.filter(user=self.user) + serializer = QuestionSerializer(questions, many=True) + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:questions')) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 2) + self.assertEqual(response.data, serializer.data) + + def test_create_question_invalid_data(self): + # Given + data = {'summary': 'Add test question', 'user': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:questions'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse(Question.objects.filter( + summary='Add test question').exists()) + + def test_create_question_valid_data(self): + # Given + data = {'summary': 'Add test question', 'description': 'test', + 'language': 'python', 'type': 'mcq', 'user': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:questions'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertTrue(Question.objects.filter( + summary='Add test question').exists()) + + def tearDown(self): + self.client.logout() + User.objects.all().delete() + Question.objects.all().delete() + + +class QuestionDetailTestCase(TestCase): + """ Test get, update or delete a question """ + + def setUp(self): + self.client = APIClient() + self.username = 'demo' + self.password = 'demo' + self.otherusername = 'otheruser' + self.user = User.objects.create_user(username=self.username, + password=self.password) + self.otheruser = User.objects.create_user(username=self.otherusername, + password=self.password) + Question.objects.create(summary='test question', language='python', + type='mcq', user=self.user) + Question.objects.create(summary='delete question', language='python', + type='mcq', user=self.user) + Question.objects.create(summary='Created by other user', language='python', + type='mcq', user=self.otheruser) + + def test_get_question_anonymous(self): + # Given + question = Question.objects.get(summary='test question') + # When + response = self.client.get(reverse('api:question', + kwargs={'pk': question.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_get_question_invalid_pk(self): + # Given + invalid_pk = 3243 + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:question', + kwargs={'pk': invalid_pk})) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_get_question(self): + # Given + question = Question.objects.get(summary='test question') + serializer = QuestionSerializer(question) + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:question', + kwargs={'pk': question.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, serializer.data) + + def test_get_question_not_own(self): + # Given + question = Question.objects.get(summary='Created by other user') + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:question', + kwargs={'pk': question.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_edit_question_anonymous(self): + # Given + question = Question.objects.get(summary='test question') + data = {'summary': 'Edited test question', 'description': 'test', + 'language': 'python', 'type': 'mcq', 'user': self.user.id} + # When + response = self.client.put(reverse('api:question', + kwargs={'pk': question.id}), data) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_edit_question_invalid_data(self): + # Given + question = Question.objects.get(summary='test question') + data = {'summary': 'Edited test question', 'user': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.put(reverse('api:question', + kwargs={'pk': question.id}), data) + # Then + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_edit_question(self): + # Given + question = Question.objects.get(summary='test question') + data = {'summary': 'Edited test question', 'description': 'test', + 'language': 'python', 'type': 'mcq', 'user': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.put(reverse('api:question', + kwargs={'pk': question.id}), data) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + question = Question.objects.get(pk=question.pk) + self.assertEqual(question.summary, 'Edited test question') + + def test_delete_question_anonymous(self): + # Given + question = Question.objects.get(summary='delete question') + # When + response = self.client.delete(reverse('api:question', + kwargs={'pk':question.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_delete_question_not_own(self): + # Given + question = Question.objects.get(summary='Created by other user') + # When + self.client.login(username=self.username, password=self.password) + response = self.client.delete(reverse('api:question', + kwargs={'pk':question.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertTrue(Question.objects.filter(pk=question.id).exists()) + + def test_delete_question(self): + # Given + question = Question.objects.get(summary='delete question') + # When + self.client.login(username=self.username, password=self.password) + response = self.client.delete(reverse('api:question', + kwargs={'pk':question.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertFalse(Question.objects.filter(pk=question.id).exists()) + + def tearDown(self): + self.client.logout() + User.objects.all().delete() + Question.objects.all().delete() + + +class QuestionPaperListTestCase(TestCase): + """ Test get all question paper and create a new question paper """ + + def setUp(self): + self.client = APIClient() + self.username = 'demo' + self.otherusername = 'otheruser' + self.password = 'demo' + self.user = User.objects.create_user(username=self.username, + password=self.password) + self.otheruser = User.objects.create_user(username=self.otherusername, + password=self.password) + self.quiz1 = Quiz.objects.create(description='Quiz1', creator=self.user) + self.quiz2 = Quiz.objects.create(description='Quiz2', creator=self.user) + self.quiz3 = Quiz.objects.create(description='Quiz3', + creator=self.otheruser) + + self.quiz4 = Quiz.objects.create(description='Quiz4', creator=self.user) + self.quiz5 = Quiz.objects.create(description='Quiz5', creator=self.user) + self.quiz6 = Quiz.objects.create(description='Quiz6', + creator=self.otheruser) + self.questionpaper = QuestionPaper.objects.create(quiz=self.quiz1) + self.questionpaper2 = QuestionPaper.objects.create(quiz=self.quiz2) + QuestionPaper.objects.create(quiz=self.quiz3) + + self.question1 = Question.objects.create(summary='Q1', user=self.user, + language='python', type='mcq') + self.question2 = Question.objects.create(summary='Q2', user=self.user, + language='python', type='mcq') + self.question3 = Question.objects.create(summary='Q3', user=self.user, + language='python', type='mcq') + self.question4 = Question.objects.create(summary='Q4', user=self.user, + language='python', type='mcq') + self.question5 = Question.objects.create(summary='Q5', user=self.otheruser, + language='python', type='mcq') + self.questionset = QuestionSet.objects.create(marks=1, num_questions=1) + self.questionset.questions.add(self.question3) + self.questionset.questions.add(self.question4) + self.questionset.save() + self.questionpaper.fixed_questions.add(self.question1) + self.questionpaper.fixed_questions.add(self.question2) + self.questionpaper.random_questions.add(self.questionset) + self.questionpaper.save() + self.questionpaper.update_total_marks() + + def test_get_all_questionpapers_anonymous(self): + # When + response = self.client.get(reverse('api:questionpapers')) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_get_all_questionpaper(self): + # Given + questionpapers = QuestionPaper.objects.filter( + quiz__in=[self.quiz1, self.quiz2]) + serializer = QuestionPaperSerializer(questionpapers, many=True) + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:questionpapers')) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 2) + self.assertEqual(response.data, serializer.data) + + def test_create_questionpaper_invalid_data(self): + # Given + data = {'fixed_questions': [self.question1.id], 'user': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:questionpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_questionpaper_valid_data(self): + # Given + data = {'quiz': self.quiz4.id, + 'fixed_questions': [self.question1.id, self.question2.id], + 'random_questions': [self.questionset.id] + } + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:questionpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertTrue(QuestionPaper.objects.filter(quiz=self.quiz4).exists()) + + def test_create_questionpaper_not_own_quiz(self): + # Given + data = {'quiz': self.quiz5.id, 'fixed_questions':[self.question1.id], + 'random_questions': [self.questionset.id]} + # When + self.client.login(username=self.otherusername, password=self.password) + response = self.client.post(reverse('api:questionpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_create_questionpaper_not_own_questions(self): + # Given + data = {'quiz': self.quiz6.id, + 'fixed_questions':[self.question1.id, self.question5.id], + 'random_questions': [self.questionset.id] + } + # When + self.client.login(username=self.otherusername, password=self.password) + response = self.client.post(reverse('api:questionpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)\ + + def test_create_questionpaper_not_own_questionsets(self): + # Given + data = {'quiz': self.quiz6.id, + 'fixed_questions':[self.question5.id], + 'random_questions': [self.questionset.id] + } + # When + self.client.login(username=self.otherusername, password=self.password) + response = self.client.post(reverse('api:questionpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_create_questionpaper_already_exists(self): + # Given + data = {'quiz': self.quiz1.id, + 'fixed_questions': [self.question1.id], + 'random_questions': [self.questionset.id] + } + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:questionpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_409_CONFLICT) + self.assertEqual(QuestionPaper.objects.filter( + quiz=self.quiz1).count(), 1) + + # QuestionPaper Detail Tests + def test_get_questionpaper(self): + # Given + questionpaper = self.questionpaper + serializer = QuestionPaperSerializer(questionpaper) + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:questionpaper', + kwargs={'pk': questionpaper.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, serializer.data) + + def test_get_questionpaper_not_own(self): + # Given + questionpaper = self.questionpaper + # When + self.client.login(username=self.otherusername, password=self.password) + response = self.client.get(reverse('api:questionpaper', + kwargs={'pk': questionpaper.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + + def test_edit_questionpaper(self): + # Given + questionpaper = self.questionpaper2 + data = {'quiz': self.quiz5.id, + 'fixed_questions': [self.question1.id, self.question2.id], + 'random_questions': [self.questionset.id] + } + # When + self.client.login(username=self.username, password=self.password) + response = self.client.put(reverse('api:questionpaper', + kwargs={'pk': questionpaper.id}), data) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + questionpaper = QuestionPaper.objects.get(pk=questionpaper.id) + self.assertEqual(questionpaper.quiz.id, self.quiz5.id) + + + def test_delete_questionpaper(self): + # Given + questionpaper = self.questionpaper2 + # When + self.client.login(username=self.username, password=self.password) + response = self.client.delete(reverse('api:questionpaper', + kwargs={'pk':questionpaper.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertFalse(QuestionPaper.objects.filter(quiz=self.quiz2).exists()) + + def tearDown(self): + self.client.logout() + User.objects.all().delete() + Question.objects.all().delete() + QuestionPaper.objects.all().delete() + Quiz.objects.all().delete() + + +class QuizListTestCase(TestCase): + """ Test get all quizzes and create a new quiz """ + + def setUp(self): + self.client = APIClient() + self.username = 'demo' + self.password = 'demo' + self.user = User.objects.create_user(username=self.username, + password=self.password) + Quiz.objects.create(description='Test Quiz 1', creator=self.user) + Quiz.objects.create(description='Test Quiz 2', creator=self.user) + + def test_get_all_quizzes_anonymous(self): + # When + response = self.client.get(reverse('api:quizzes')) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + + def test_get_all_quizzes(self): + # Given + quizzes = Quiz.objects.filter(creator=self.user) + serializer = QuizSerializer(quizzes, many=True) + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:quizzes')) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 2) + self.assertEqual(response.data, serializer.data) + + def test_create_quiz_invalid_data(self): + # Given + data = {'creator': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:quizzes'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_quiz_valid_data(self): + # Given + data = {'description': 'Added quiz', 'creator': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:quizzes'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertTrue(Quiz.objects.filter(description='Added quiz').exists()) + + def tearDown(self): + self.client.logout() + User.objects.all().delete() + Quiz.objects.all().delete() + + +class QuizDetailTestCase(TestCase): + """ Test get, update or delete a quiz """ + + def setUp(self): + self.client = APIClient() + self.username = 'quizuser' + self.password = 'demo' + self.otherusername = 'quizuser2' + self.user = User.objects.create_user(username=self.username, + password=self.password) + self.otheruser = User.objects.create_user(username=self.otherusername, + password=self.password) + Quiz.objects.create(description='Quiz1', creator=self.user) + Quiz.objects.create(description='Quiz2', creator=self.user) + Quiz.objects.create(description='delete quiz', creator=self.user) + Quiz.objects.create(description='Quiz3', creator=self.otheruser) + + def test_get_quiz_anonymous(self): + # Given + quiz = Quiz.objects.get(description='Quiz1') + # When + response = self.client.get(reverse('api:quiz', kwargs={'pk': quiz.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_get_quiz_invalid_pk(self): + # Given + invalid_pk = 3242 + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:quiz', + kwargs={'pk': invalid_pk})) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_get_quiz(self): + # Given + quiz = Quiz.objects.get(description='Quiz1') + serializer = QuizSerializer(quiz) + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:quiz', + kwargs={'pk': quiz.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, serializer.data) + + def test_get_quiz_not_own(self): + # Given + quiz = Quiz.objects.get(description='Quiz3') + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:quiz', kwargs={'pk': quiz.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_edit_quiz_anonymous(self): + # Given + quiz = Quiz.objects.get(description='Quiz1') + data = {'description': 'Quiz1 Edited', 'creator': self.user.id} + # When + response = self.client.put(reverse('api:quiz', kwargs={'pk': quiz.id}), + data) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_edit_quiz_invalid_data(self): + # Given + quiz = Quiz.objects.get(description='Quiz1') + data = {'creator': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.put(reverse('api:quiz', kwargs={'pk': quiz.id}), + data) + # Then + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_edit_quiz(self): + # Given + quiz = Quiz.objects.get(description='Quiz2') + data = {'description': 'Quiz2 edited', 'creator': self.user.id} + # When + self.client.login(username=self.username, password=self.password) + response = self.client.put(reverse('api:quiz', kwargs={'pk': quiz.id}), + data) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + quiz = Quiz.objects.get(pk=quiz.id) + self.assertEqual(quiz.description, 'Quiz2 edited') + + def test_delete_quiz_anonymous(self): + # Given + quiz = Quiz.objects.get(description='delete quiz') + # When + response = self.client.delete(reverse('api:quiz', + kwargs={'pk': quiz.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_delete_quiz_not_own(self): + # Given + quiz = Quiz.objects.get(description='Quiz3') + # When + self.client.login(username=self.username, password=self.password) + response = self.client.delete(reverse('api:quiz', + kwargs={'pk': quiz.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertTrue(Quiz.objects.filter(pk=quiz.id).exists()) + + def test_delete_quiz(self): + # Given + quiz = Quiz.objects.get(description='delete quiz') + # When + self.client.login(username=self.username, password=self.password) + response = self.client.delete(reverse('api:quiz', + kwargs={'pk':quiz.id})) + # Then + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertFalse(Quiz.objects.filter(pk=quiz.id).exists()) + + def tearDown(self): + self.client.logout() + User.objects.all().delete() + Quiz.objects.all().delete() + + +class AnswerPaperListTestCase(TestCase): + + def setUp(self): + self.client = APIClient() + self.username = 'demo' + self.otherusername = 'otheruser' + self.studentusername = 'student' + self.password = 'demo' + self.user = User.objects.create_user(username=self.username, + password=self.password) + self.otheruser = User.objects.create_user(username=self.otherusername, + password=self.password) + self.student = User.objects.create_user(username=self.studentusername, + password='demo') + self.quiz1 = Quiz.objects.create(description='Quiz1', creator=self.user) + self.quiz2 = Quiz.objects.create(description='Quiz2', + creator=self.otheruser) + self.questionpaper1 = QuestionPaper.objects.create(quiz=self.quiz1) + self.questionpaper2 = QuestionPaper.objects.create(quiz=self.quiz2) + self.question1 = Question.objects.create(summary='Q1', user=self.user, + language='python', type='mcq') + self.question2 = Question.objects.create(summary='Q5', user=self.otheruser, + language='python', type='mcq') + self.questionpaper1.fixed_questions.add(self.question1) + self.questionpaper2.fixed_questions.add(self.question2) + self.questionpaper1.save() + self.questionpaper2.save() + self.questionpaper1.update_total_marks() + self.questionpaper2.update_total_marks() + self.answerpaper1 = AnswerPaper.objects.create(user=self.user, + question_paper=self.questionpaper1, attempt_number=1, + start_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_time=datetime(2015, 10, 9, 10, 28, 15, 0, tzinfo=pytz.utc)) + self.answerpaper2 = AnswerPaper.objects.create(user=self.otheruser, + question_paper=self.questionpaper2, attempt_number=1, + start_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_time=datetime(2015, 10, 9, 10, 28, 15, 0, tzinfo=pytz.utc)) + + self.course = Course.objects.create(name="Python Course", + enrollment="Enroll Request", + creator=self.user) + + # Learing module + learning_module = LearningModule.objects.create( + name='LM1', description='module one', creator=self.user + ) + learning_unit_quiz = LearningUnit.objects.create(quiz=self.quiz1, + type='quiz', order=1) + learning_module.learning_unit.add(learning_unit_quiz) + learning_module.save() + self.course.learning_module.add(learning_module) + self.course.students.add(self.student) + self.course.save() + + + def test_get_all_answerpapers(self): + # Given + answerpapers = [self.answerpaper1] + serializer = AnswerPaperSerializer(answerpapers, many=True) + # When + self.client.login(username=self.username, password=self.password) + response = self.client.get(reverse('api:answerpapers')) + # Then + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data, serializer.data) + + def test_create_answerpaper_valid_data(self): + # Given + data = {'question_paper': self.questionpaper1.id, + 'attempt_number': 1, 'course': self.course.id + } + # When + self.client.login(username=self.studentusername, password=self.password) + response = self.client.post(reverse('api:answerpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + answerpapers = AnswerPaper.objects.filter(question_paper=self.questionpaper1, + user=self.student, attempt_number=1, + course=self.course) + self.assertTrue(answerpapers.exists()) + self.assertEqual(answerpapers.count(), 1) + + def test_create_answerpaper_invalid_data(self): + # Given + data = {'question_paper': self.questionpaper1.id} + # When + self.client.login(username=self.studentusername, password=self.password) + response = self.client.post(reverse('api:answerpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_answerpaper_not_enrolled(self): + # Given + data = {'question_paper': self.questionpaper1.id, + 'attempt_number': 1, 'course': self.course.id + } + # When + self.client.login(username=self.otherusername, password=self.password) + response = self.client.post(reverse('api:answerpapers'), data) + # Then + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + answerpapers = AnswerPaper.objects.filter(question_paper=self.questionpaper1, + user=self.otheruser, attempt_number=1, + course=self.course) + self.assertFalse(answerpapers.exists()) + self.assertEqual(answerpapers.count(), 0) + + def tearDown(self): + self.client.logout() + User.objects.all().delete() + Question.objects.all().delete() + QuestionPaper.objects.all().delete() + Quiz.objects.all().delete() + AnswerPaper.objects.all().delete() + + +class AnswerValidatorTestCase(TestCase): + + @classmethod + def setUpClass(self): + self.client = APIClient() + self.username = 'demo' + self.password = 'demo' + self.user = User.objects.create_user(username=self.username, + password=self.password) + Profile.objects.create(user=self.user) + self.quiz = Quiz.objects.create(description='Quiz', creator=self.user) + self.questionpaper = QuestionPaper.objects.create(quiz=self.quiz) + self.question1 = Question.objects.create(summary='Q1', user=self.user, + points=1.0, language='python', type='code') + self.question2 = Question.objects.create(summary='Q2', user=self.user, + points=1.0, language='python', type='mcq') + self.question3 = Question.objects.create(summary='Q3', user=self.user, + points=1.0, language='python', type='mcc') + self.question4 = Question.objects.create(summary='Q4', user=self.user, + points=1.0, language='python', type='mcq') + self.question5 = Question.objects.create(summary='Q5', user=self.user, + points=1.0, language='python', type='mcq') + self.assertion_testcase = StandardTestCase( + question=self.question1, + test_case='assert add(1, 3) == 4', + type='standardtestcase' + ) + self.assertion_testcase.save() + self.mcq_based_testcase1 = McqTestCase( + options='a', + question=self.question2, + correct=True, + type='mcqtestcase' + ) + self.mcq_based_testcase1.save() + self.mcq_based_testcase2 = McqTestCase( + options='b', + question=self.question2, + correct=False, + type='mcqtestcase' + ) + self.mcq_based_testcase2.save() + self.mcc_based_testcase = McqTestCase( + question=self.question3, + options='a', + correct=True, + type='mcqtestcase' + ) + self.mcc_based_testcase.save() + self.questionset = QuestionSet.objects.create(marks=1, num_questions=1) + self.questionset.questions.add(self.question3) + self.questionset.questions.add(self.question4) + self.questionset.save() + self.questionpaper.fixed_questions.add(self.question1) + self.questionpaper.fixed_questions.add(self.question2) + self.questionpaper.random_questions.add(self.questionset) + self.questionpaper.save() + self.questionpaper.update_total_marks() + self.course = Course.objects.create(name="Python Course", + enrollment="Enroll Request", + creator=self.user) + # Learing module + learning_module = LearningModule.objects.create( + name='LM1', description='module one', creator=self.user + ) + learning_unit_quiz = LearningUnit.objects.create(quiz=self.quiz, + type='quiz', order=1) + learning_module.learning_unit.add(learning_unit_quiz) + learning_module.save() + self.course.learning_module.add(learning_module) + self.course.students.add(self.user) + self.course.save() + self.ip = '127.0.0.1' + self.answerpaper = self.questionpaper.make_answerpaper( + self.user, self.ip, 1, self.course.id + ) + + settings.code_evaluators['python']['standardtestcase'] = \ + "yaksh.python_assertion_evaluator.PythonAssertionEvaluator" + server_pool = ServerPool(n=1, pool_port=SERVER_POOL_PORT) + self.server_pool = server_pool + self.server_thread = t = Thread(target=server_pool.run) + t.start() + + @classmethod + def tearDownClass(self): + self.client.logout() + User.objects.all().delete() + Question.objects.all().delete() + QuestionPaper.objects.all().delete() + Quiz.objects.all().delete() + AnswerPaper.objects.all().delete() + self.server_pool.stop() + self.server_thread.join() + settings.code_evaluators['python']['standardtestcase'] = \ + "python_assertion_evaluator.PythonAssertionEvaluator" + + def test_correct_mcq(self): + # Given + data = {'answer': str( self.mcq_based_testcase1.id)} + answerpaper_id = self.answerpaper.id + question_id = self.question2.id + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:validators', + kwargs={'answerpaper_id': answerpaper_id, 'question_id': question_id}), data) + # Then + self.assertTrue(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['success']) + answerpaper = AnswerPaper.objects.get(user=self.user, course=self.course, + attempt_number=1, + question_paper=self.questionpaper) + self.assertTrue(answerpaper.marks_obtained > 0) + + + def test_wrong_mcq(self): + # Given + data = {'answer': str( self.mcq_based_testcase2.id)} + answerpaper_id = self.answerpaper.id + question_id = self.question2.id + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:validators', + kwargs={'answerpaper_id': answerpaper_id, 'question_id': question_id}), data) + # Then + self.assertTrue(response.status_code, status.HTTP_200_OK) + self.assertFalse(response.data['success']) + + def test_correct_mcc(self): + # Given + data = {'answer': str( self.mcc_based_testcase.id)} + answerpaper_id = self.answerpaper.id + question_id = self.question3.id + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:validators', + kwargs={'answerpaper_id': answerpaper_id, 'question_id': question_id}), data) + # Then + self.assertTrue(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['success']) + answerpaper = AnswerPaper.objects.get(user=self.user, course=self.course, + attempt_number=1, + question_paper=self.questionpaper) + self.assertTrue(answerpaper.marks_obtained > 0) + + def test_correct_code(self): + # Given + answer = dedent("""\ + def add(a,b): + return a+b + """) + data = {'answer': answer} + answerpaper_id = self.answerpaper.id + question_id = self.question1.id + # When + self.client.login(username=self.username, password=self.password) + response = self.client.post(reverse('api:validators', + kwargs={'answerpaper_id': answerpaper_id, 'question_id': question_id}), data) + # Then + self.assertTrue(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['status'], 'running') + uid = response.data['uid'] + time.sleep(2) + response = self.client.get(reverse('api:validator', kwargs={'uid': uid})) + self.assertTrue(response.status_code, status.HTTP_200_OK) + answerpaper = AnswerPaper.objects.get(user=self.user, course=self.course, + attempt_number=1, + question_paper=self.questionpaper) + if response.data['status'] == 'done': + result = json.loads(response.data['result']) + self.assertTrue(result['success']) + else: + self.assertEqual(response.data['status'], 'running') + diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 0000000..be276da --- /dev/null +++ b/api/urls.py @@ -0,0 +1,23 @@ +from django.conf.urls import url +from rest_framework.urlpatterns import format_suffix_patterns +from api import views + + +urlpatterns = [ + url(r'questions/$', views.QuestionList.as_view(), name='questions'), + url(r'questions/(?P[0-9]+)/$', views.QuestionDetail.as_view(), + name='question'), + url(r'quizzes/$', views.QuizList.as_view(), name='quizzes'), + url(r'quizzes/(?P[0-9]+)/$', views.QuizDetail.as_view(), name='quiz'), + url(r'questionpapers/$', views.QuestionPaperList.as_view(), + name='questionpapers'), + url(r'questionpapers/(?P[0-9]+)/$', views.QuestionPaperDetail.as_view(), + name='questionpaper'), + url(r'answerpapers/$', views.AnswerPaperList.as_view(), name='answerpapers'), + url(r'validate/(?P[0-9]+)/(?P[0-9]+)/$', + views.AnswerValidator.as_view(), name='validators'), + url(r'validate/(?P[0-9]+)/$', + views.AnswerValidator.as_view(), name='validator'), +] + +urlpatterns = format_suffix_patterns(urlpatterns) 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 bcff98d9fe7ac8fa50b1e8ab384a853ee6800c73 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Wed, 19 Dec 2018 13:57:57 +0530 Subject: Add rest framework in requirements --- api/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'api') diff --git a/api/tests.py b/api/tests.py index 0930685..06f45d6 100644 --- a/api/tests.py +++ b/api/tests.py @@ -9,7 +9,7 @@ from yaksh.models import ( McqTestCase, Profile ) from api.serializers import ( - QuestionSerializer, QuizSerializer + QuestionSerializer, QuizSerializer, QuestionPaperSerializer, AnswerPaperSerializer ) from datetime import datetime -- 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/serializers.py | 1 + api/tests.py | 244 ++++++++++++++++++++++++++++------------------------- api/urls.py | 7 +- api/views.py | 17 ++-- 4 files changed, 145 insertions(+), 124 deletions(-) (limited to 'api') diff --git a/api/serializers.py b/api/serializers.py index 919e429..d34f269 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -19,6 +19,7 @@ class QuestionPaperSerializer(serializers.ModelSerializer): model = QuestionPaper fields = '__all__' + class AnswerPaperSerializer(serializers.ModelSerializer): class Meta: model = AnswerPaper diff --git a/api/tests.py b/api/tests.py index 06f45d6..feffcc0 100644 --- a/api/tests.py +++ b/api/tests.py @@ -37,7 +37,6 @@ class QuestionListTestCase(TestCase): Question.objects.create(summary='test question 2', language='python', type='mcq', user=self.user) - def test_get_all_questions_anonymous(self): # When response = self.client.get(reverse('api:questions')) @@ -55,7 +54,7 @@ class QuestionListTestCase(TestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 2) self.assertEqual(response.data, serializer.data) - + def test_create_question_invalid_data(self): # Given data = {'summary': 'Add test question', 'user': self.user.id} @@ -65,7 +64,7 @@ class QuestionListTestCase(TestCase): # Then self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertFalse(Question.objects.filter( - summary='Add test question').exists()) + summary='Add test question').exists()) def test_create_question_valid_data(self): # Given @@ -96,13 +95,14 @@ class QuestionDetailTestCase(TestCase): self.user = User.objects.create_user(username=self.username, password=self.password) self.otheruser = User.objects.create_user(username=self.otherusername, - password=self.password) + password=self.password) Question.objects.create(summary='test question', language='python', type='mcq', user=self.user) Question.objects.create(summary='delete question', language='python', type='mcq', user=self.user) - Question.objects.create(summary='Created by other user', language='python', - type='mcq', user=self.otheruser) + Question.objects.create(summary='Created by other user', + language='python', type='mcq', + user=self.otheruser) def test_get_question_anonymous(self): # Given @@ -152,7 +152,7 @@ class QuestionDetailTestCase(TestCase): 'language': 'python', 'type': 'mcq', 'user': self.user.id} # When response = self.client.put(reverse('api:question', - kwargs={'pk': question.id}), data) + kwargs={'pk': question.id}), data) # Then self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) @@ -163,7 +163,7 @@ class QuestionDetailTestCase(TestCase): # When self.client.login(username=self.username, password=self.password) response = self.client.put(reverse('api:question', - kwargs={'pk': question.id}), data) + kwargs={'pk': question.id}), data) # Then self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -175,7 +175,7 @@ class QuestionDetailTestCase(TestCase): # When self.client.login(username=self.username, password=self.password) response = self.client.put(reverse('api:question', - kwargs={'pk': question.id}), data) + kwargs={'pk': question.id}), data) # Then self.assertEqual(response.status_code, status.HTTP_200_OK) question = Question.objects.get(pk=question.pk) @@ -186,28 +186,28 @@ class QuestionDetailTestCase(TestCase): question = Question.objects.get(summary='delete question') # When response = self.client.delete(reverse('api:question', - kwargs={'pk':question.id})) + kwargs={'pk': question.id})) # Then self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - + def test_delete_question_not_own(self): # Given question = Question.objects.get(summary='Created by other user') # When self.client.login(username=self.username, password=self.password) response = self.client.delete(reverse('api:question', - kwargs={'pk':question.id})) + kwargs={'pk': question.id})) # Then self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertTrue(Question.objects.filter(pk=question.id).exists()) - + def test_delete_question(self): # Given question = Question.objects.get(summary='delete question') # When self.client.login(username=self.username, password=self.password) response = self.client.delete(reverse('api:question', - kwargs={'pk':question.id})) + kwargs={'pk': question.id})) # Then self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertFalse(Question.objects.filter(pk=question.id).exists()) @@ -229,14 +229,17 @@ class QuestionPaperListTestCase(TestCase): self.user = User.objects.create_user(username=self.username, password=self.password) self.otheruser = User.objects.create_user(username=self.otherusername, - password=self.password) - self.quiz1 = Quiz.objects.create(description='Quiz1', creator=self.user) - self.quiz2 = Quiz.objects.create(description='Quiz2', creator=self.user) + password=self.password) + self.quiz1 = Quiz.objects.create(description='Quiz1', + creator=self.user) + self.quiz2 = Quiz.objects.create(description='Quiz2', + creator=self.user) self.quiz3 = Quiz.objects.create(description='Quiz3', creator=self.otheruser) - - self.quiz4 = Quiz.objects.create(description='Quiz4', creator=self.user) - self.quiz5 = Quiz.objects.create(description='Quiz5', creator=self.user) + self.quiz4 = Quiz.objects.create(description='Quiz4', + creator=self.user) + self.quiz5 = Quiz.objects.create(description='Quiz5', + creator=self.user) self.quiz6 = Quiz.objects.create(description='Quiz6', creator=self.otheruser) self.questionpaper = QuestionPaper.objects.create(quiz=self.quiz1) @@ -251,7 +254,8 @@ class QuestionPaperListTestCase(TestCase): language='python', type='mcq') self.question4 = Question.objects.create(summary='Q4', user=self.user, language='python', type='mcq') - self.question5 = Question.objects.create(summary='Q5', user=self.otheruser, + self.question5 = Question.objects.create(summary='Q5', + user=self.otheruser, language='python', type='mcq') self.questionset = QuestionSet.objects.create(marks=1, num_questions=1) self.questionset.questions.add(self.question3) @@ -268,11 +272,12 @@ class QuestionPaperListTestCase(TestCase): response = self.client.get(reverse('api:questionpapers')) # Then self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - + def test_get_all_questionpaper(self): # Given questionpapers = QuestionPaper.objects.filter( - quiz__in=[self.quiz1, self.quiz2]) + quiz__in=[self.quiz1, self.quiz2] + ) serializer = QuestionPaperSerializer(questionpapers, many=True) # When self.client.login(username=self.username, password=self.password) @@ -295,8 +300,7 @@ class QuestionPaperListTestCase(TestCase): # Given data = {'quiz': self.quiz4.id, 'fixed_questions': [self.question1.id, self.question2.id], - 'random_questions': [self.questionset.id] - } + 'random_questions': [self.questionset.id]} # When self.client.login(username=self.username, password=self.password) response = self.client.post(reverse('api:questionpapers'), data) @@ -306,7 +310,7 @@ class QuestionPaperListTestCase(TestCase): def test_create_questionpaper_not_own_quiz(self): # Given - data = {'quiz': self.quiz5.id, 'fixed_questions':[self.question1.id], + data = {'quiz': self.quiz5.id, 'fixed_questions': [self.question1.id], 'random_questions': [self.questionset.id]} # When self.client.login(username=self.otherusername, password=self.password) @@ -317,21 +321,19 @@ class QuestionPaperListTestCase(TestCase): def test_create_questionpaper_not_own_questions(self): # Given data = {'quiz': self.quiz6.id, - 'fixed_questions':[self.question1.id, self.question5.id], - 'random_questions': [self.questionset.id] - } + 'fixed_questions': [self.question1.id, self.question5.id], + 'random_questions': [self.questionset.id]} # When self.client.login(username=self.otherusername, password=self.password) response = self.client.post(reverse('api:questionpapers'), data) # Then - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)\ + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_create_questionpaper_not_own_questionsets(self): # Given data = {'quiz': self.quiz6.id, - 'fixed_questions':[self.question5.id], - 'random_questions': [self.questionset.id] - } + 'fixed_questions': [self.question5.id], + 'random_questions': [self.questionset.id]} # When self.client.login(username=self.otherusername, password=self.password) response = self.client.post(reverse('api:questionpapers'), data) @@ -342,8 +344,7 @@ class QuestionPaperListTestCase(TestCase): # Given data = {'quiz': self.quiz1.id, 'fixed_questions': [self.question1.id], - 'random_questions': [self.questionset.id] - } + 'random_questions': [self.questionset.id]} # When self.client.login(username=self.username, password=self.password) response = self.client.post(reverse('api:questionpapers'), data) @@ -375,14 +376,12 @@ class QuestionPaperListTestCase(TestCase): # Then self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - def test_edit_questionpaper(self): # Given questionpaper = self.questionpaper2 data = {'quiz': self.quiz5.id, 'fixed_questions': [self.question1.id, self.question2.id], - 'random_questions': [self.questionset.id] - } + 'random_questions': [self.questionset.id]} # When self.client.login(username=self.username, password=self.password) response = self.client.put(reverse('api:questionpaper', @@ -392,17 +391,17 @@ class QuestionPaperListTestCase(TestCase): questionpaper = QuestionPaper.objects.get(pk=questionpaper.id) self.assertEqual(questionpaper.quiz.id, self.quiz5.id) - def test_delete_questionpaper(self): # Given questionpaper = self.questionpaper2 # When self.client.login(username=self.username, password=self.password) response = self.client.delete(reverse('api:questionpaper', - kwargs={'pk':questionpaper.id})) + kwargs={'pk': questionpaper.id})) # Then self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertFalse(QuestionPaper.objects.filter(quiz=self.quiz2).exists()) + questionpapers = QuestionPaper.objects.filter(quiz=self.quiz2) + self.assertFalse(questionpapers.exists()) def tearDown(self): self.client.logout() @@ -414,7 +413,7 @@ class QuestionPaperListTestCase(TestCase): class QuizListTestCase(TestCase): """ Test get all quizzes and create a new quiz """ - + def setUp(self): self.client = APIClient() self.username = 'demo' @@ -430,7 +429,6 @@ class QuizListTestCase(TestCase): # Then self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - def test_get_all_quizzes(self): # Given quizzes = Quiz.objects.filter(creator=self.user) @@ -566,7 +564,7 @@ class QuizDetailTestCase(TestCase): kwargs={'pk': quiz.id})) # Then self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - + def test_delete_quiz_not_own(self): # Given quiz = Quiz.objects.get(description='Quiz3') @@ -577,14 +575,14 @@ class QuizDetailTestCase(TestCase): # Then self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertTrue(Quiz.objects.filter(pk=quiz.id).exists()) - + def test_delete_quiz(self): # Given quiz = Quiz.objects.get(description='delete quiz') # When self.client.login(username=self.username, password=self.password) response = self.client.delete(reverse('api:quiz', - kwargs={'pk':quiz.id})) + kwargs={'pk': quiz.id})) # Then self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertFalse(Quiz.objects.filter(pk=quiz.id).exists()) @@ -609,14 +607,16 @@ class AnswerPaperListTestCase(TestCase): password=self.password) self.student = User.objects.create_user(username=self.studentusername, password='demo') - self.quiz1 = Quiz.objects.create(description='Quiz1', creator=self.user) + self.quiz1 = Quiz.objects.create(description='Quiz1', + creator=self.user) self.quiz2 = Quiz.objects.create(description='Quiz2', creator=self.otheruser) self.questionpaper1 = QuestionPaper.objects.create(quiz=self.quiz1) self.questionpaper2 = QuestionPaper.objects.create(quiz=self.quiz2) self.question1 = Question.objects.create(summary='Q1', user=self.user, language='python', type='mcq') - self.question2 = Question.objects.create(summary='Q5', user=self.otheruser, + self.question2 = Question.objects.create(summary='Q5', + user=self.otheruser, language='python', type='mcq') self.questionpaper1.fixed_questions.add(self.question1) self.questionpaper2.fixed_questions.add(self.question2) @@ -624,23 +624,25 @@ class AnswerPaperListTestCase(TestCase): self.questionpaper2.save() self.questionpaper1.update_total_marks() self.questionpaper2.update_total_marks() - self.answerpaper1 = AnswerPaper.objects.create(user=self.user, - question_paper=self.questionpaper1, attempt_number=1, - start_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), - end_time=datetime(2015, 10, 9, 10, 28, 15, 0, tzinfo=pytz.utc)) - self.answerpaper2 = AnswerPaper.objects.create(user=self.otheruser, - question_paper=self.questionpaper2, attempt_number=1, - start_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), - end_time=datetime(2015, 10, 9, 10, 28, 15, 0, tzinfo=pytz.utc)) - + self.answerpaper1 = AnswerPaper.objects.create( + user=self.user, + question_paper=self.questionpaper1, attempt_number=1, + start_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_time=datetime(2015, 10, 9, 10, 28, 15, 0, tzinfo=pytz.utc) + ) + self.answerpaper2 = AnswerPaper.objects.create( + user=self.otheruser, + question_paper=self.questionpaper2, attempt_number=1, + start_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_time=datetime(2015, 10, 9, 10, 28, 15, 0, tzinfo=pytz.utc) + ) self.course = Course.objects.create(name="Python Course", enrollment="Enroll Request", creator=self.user) - # Learing module learning_module = LearningModule.objects.create( name='LM1', description='module one', creator=self.user - ) + ) learning_unit_quiz = LearningUnit.objects.create(quiz=self.quiz1, type='quiz', order=1) learning_module.learning_unit.add(learning_unit_quiz) @@ -649,7 +651,6 @@ class AnswerPaperListTestCase(TestCase): self.course.students.add(self.student) self.course.save() - def test_get_all_answerpapers(self): # Given answerpapers = [self.answerpaper1] @@ -665,41 +666,43 @@ class AnswerPaperListTestCase(TestCase): def test_create_answerpaper_valid_data(self): # Given data = {'question_paper': self.questionpaper1.id, - 'attempt_number': 1, 'course': self.course.id - } + 'attempt_number': 1, 'course': self.course.id} # When - self.client.login(username=self.studentusername, password=self.password) + self.client.login(username=self.studentusername, + password=self.password) response = self.client.post(reverse('api:answerpapers'), data) # Then self.assertEqual(response.status_code, status.HTTP_201_CREATED) - answerpapers = AnswerPaper.objects.filter(question_paper=self.questionpaper1, - user=self.student, attempt_number=1, - course=self.course) + answerpapers = AnswerPaper.objects.filter( + user=self.student, attempt_number=1, + question_paper=self.questionpaper1, course=self.course + ) self.assertTrue(answerpapers.exists()) self.assertEqual(answerpapers.count(), 1) - + def test_create_answerpaper_invalid_data(self): # Given data = {'question_paper': self.questionpaper1.id} # When - self.client.login(username=self.studentusername, password=self.password) + self.client.login(username=self.studentusername, + password=self.password) response = self.client.post(reverse('api:answerpapers'), data) # Then self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - + def test_create_answerpaper_not_enrolled(self): # Given data = {'question_paper': self.questionpaper1.id, - 'attempt_number': 1, 'course': self.course.id - } + 'attempt_number': 1, 'course': self.course.id} # When self.client.login(username=self.otherusername, password=self.password) response = self.client.post(reverse('api:answerpapers'), data) # Then self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - answerpapers = AnswerPaper.objects.filter(question_paper=self.questionpaper1, - user=self.otheruser, attempt_number=1, - course=self.course) + answerpapers = AnswerPaper.objects.filter( + question_paper=self.questionpaper1, user=self.otheruser, + attempt_number=1, course=self.course + ) self.assertFalse(answerpapers.exists()) self.assertEqual(answerpapers.count(), 0) @@ -713,7 +716,7 @@ class AnswerPaperListTestCase(TestCase): class AnswerValidatorTestCase(TestCase): - + @classmethod def setUpClass(self): self.client = APIClient() @@ -725,15 +728,20 @@ class AnswerValidatorTestCase(TestCase): self.quiz = Quiz.objects.create(description='Quiz', creator=self.user) self.questionpaper = QuestionPaper.objects.create(quiz=self.quiz) self.question1 = Question.objects.create(summary='Q1', user=self.user, - points=1.0, language='python', type='code') + points=1.0, language='python', + type='code') self.question2 = Question.objects.create(summary='Q2', user=self.user, - points=1.0, language='python', type='mcq') + points=1.0, language='python', + type='mcq') self.question3 = Question.objects.create(summary='Q3', user=self.user, - points=1.0, language='python', type='mcc') + points=1.0, language='python', + type='mcc') self.question4 = Question.objects.create(summary='Q4', user=self.user, - points=1.0, language='python', type='mcq') + points=1.0, language='python', + type='mcq') self.question5 = Question.objects.create(summary='Q5', user=self.user, - points=1.0, language='python', type='mcq') + points=1.0, language='python', + type='mcq') self.assertion_testcase = StandardTestCase( question=self.question1, test_case='assert add(1, 3) == 4', @@ -776,7 +784,7 @@ class AnswerValidatorTestCase(TestCase): # Learing module learning_module = LearningModule.objects.create( name='LM1', description='module one', creator=self.user - ) + ) learning_unit_quiz = LearningUnit.objects.create(quiz=self.quiz, type='quiz', order=1) learning_module.learning_unit.add(learning_unit_quiz) @@ -811,50 +819,57 @@ class AnswerValidatorTestCase(TestCase): def test_correct_mcq(self): # Given - data = {'answer': str( self.mcq_based_testcase1.id)} + data = {'answer': str(self.mcq_based_testcase1.id)} answerpaper_id = self.answerpaper.id question_id = self.question2.id # When self.client.login(username=self.username, password=self.password) - response = self.client.post(reverse('api:validators', - kwargs={'answerpaper_id': answerpaper_id, 'question_id': question_id}), data) + response = self.client.post( + reverse('api:validators', kwargs={'answerpaper_id': answerpaper_id, + 'question_id': question_id}), data + ) # Then self.assertTrue(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - answerpaper = AnswerPaper.objects.get(user=self.user, course=self.course, - attempt_number=1, - question_paper=self.questionpaper) + self.assertTrue(response.data.get('success')) + answerpaper = AnswerPaper.objects.get( + user=self.user, course=self.course, attempt_number=1, + question_paper=self.questionpaper + ) self.assertTrue(answerpaper.marks_obtained > 0) - def test_wrong_mcq(self): # Given - data = {'answer': str( self.mcq_based_testcase2.id)} + data = {'answer': str(self.mcq_based_testcase2.id)} answerpaper_id = self.answerpaper.id question_id = self.question2.id # When self.client.login(username=self.username, password=self.password) - response = self.client.post(reverse('api:validators', - kwargs={'answerpaper_id': answerpaper_id, 'question_id': question_id}), data) + response = self.client.post( + reverse('api:validators', kwargs={'answerpaper_id': answerpaper_id, + 'question_id': question_id}), data + ) # Then self.assertTrue(response.status_code, status.HTTP_200_OK) - self.assertFalse(response.data['success']) - + self.assertFalse(response.data.get('success')) + def test_correct_mcc(self): # Given - data = {'answer': str( self.mcc_based_testcase.id)} + data = {'answer': str(self.mcc_based_testcase.id)} answerpaper_id = self.answerpaper.id question_id = self.question3.id # When self.client.login(username=self.username, password=self.password) - response = self.client.post(reverse('api:validators', - kwargs={'answerpaper_id': answerpaper_id, 'question_id': question_id}), data) + response = self.client.post( + reverse('api:validators', kwargs={'answerpaper_id': answerpaper_id, + 'question_id': question_id}), data + ) # Then self.assertTrue(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - answerpaper = AnswerPaper.objects.get(user=self.user, course=self.course, - attempt_number=1, - question_paper=self.questionpaper) + self.assertTrue(response.data.get('success')) + answerpaper = AnswerPaper.objects.get( + user=self.user, course=self.course, attempt_number=1, + question_paper=self.questionpaper + ) self.assertTrue(answerpaper.marks_obtained > 0) def test_correct_code(self): @@ -868,21 +883,24 @@ class AnswerValidatorTestCase(TestCase): question_id = self.question1.id # When self.client.login(username=self.username, password=self.password) - response = self.client.post(reverse('api:validators', - kwargs={'answerpaper_id': answerpaper_id, 'question_id': question_id}), data) + response = self.client.post( + reverse('api:validators', kwargs={'answerpaper_id': answerpaper_id, + 'question_id': question_id}), data + ) # Then self.assertTrue(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['status'], 'running') + self.assertEqual(response.data.get('status'), 'running') uid = response.data['uid'] time.sleep(2) - response = self.client.get(reverse('api:validator', kwargs={'uid': uid})) + response = self.client.get(reverse('api:validator', + kwargs={'uid': uid})) self.assertTrue(response.status_code, status.HTTP_200_OK) - answerpaper = AnswerPaper.objects.get(user=self.user, course=self.course, - attempt_number=1, - question_paper=self.questionpaper) - if response.data['status'] == 'done': - result = json.loads(response.data['result']) - self.assertTrue(result['success']) + answerpaper = AnswerPaper.objects.get( + user=self.user, course=self.course, attempt_number=1, + question_paper=self.questionpaper + ) + if response.data.get('status') == 'done': + result = json.loads(response.data.get('result')) + self.assertTrue(result.get('success')) else: - self.assertEqual(response.data['status'], 'running') - + self.assertEqual(response.data.get('status'), 'running') diff --git a/api/urls.py b/api/urls.py index be276da..5dfd8c7 100644 --- a/api/urls.py +++ b/api/urls.py @@ -11,9 +11,10 @@ urlpatterns = [ url(r'quizzes/(?P[0-9]+)/$', views.QuizDetail.as_view(), name='quiz'), url(r'questionpapers/$', views.QuestionPaperList.as_view(), name='questionpapers'), - url(r'questionpapers/(?P[0-9]+)/$', views.QuestionPaperDetail.as_view(), - name='questionpaper'), - url(r'answerpapers/$', views.AnswerPaperList.as_view(), name='answerpapers'), + url(r'questionpapers/(?P[0-9]+)/$', + views.QuestionPaperDetail.as_view(), name='questionpaper'), + url(r'answerpapers/$', views.AnswerPaperList.as_view(), + name='answerpapers'), url(r'validate/(?P[0-9]+)/(?P[0-9]+)/$', views.AnswerValidator.as_view(), name='validators'), url(r'validate/(?P[0-9]+)/$', 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/serializers.py | 42 +++++++++++++++++++++++++++++++++++++++++- api/urls.py | 3 +++ api/views.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 2 deletions(-) (limited to 'api') diff --git a/api/serializers.py b/api/serializers.py index d34f269..df66730 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,5 +1,8 @@ from rest_framework import serializers -from yaksh.models import Question, Quiz, QuestionPaper, AnswerPaper +from yaksh.models import ( + Question, Quiz, QuestionPaper, AnswerPaper, Course, + LearningModule, LearningUnit, Lesson +) class QuestionSerializer(serializers.ModelSerializer): @@ -21,6 +24,43 @@ class QuestionPaperSerializer(serializers.ModelSerializer): class AnswerPaperSerializer(serializers.ModelSerializer): + + questions = QuestionSerializer(many=True) + class Meta: model = AnswerPaper fields = '__all__' + + +class LessonSerializer(serializers.ModelSerializer): + class Meta: + model = Lesson + fields = '__all__' + + +class LearningUnitSerializer(serializers.ModelSerializer): + + quiz = QuizSerializer() + lesson = LessonSerializer() + + class Meta: + model = LearningUnit + fields = '__all__' + + +class LearningModuleSerializer(serializers.ModelSerializer): + + learning_unit = LearningUnitSerializer(many=True) + + class Meta: + model = LearningModule + fields = '__all__' + + +class CourseSerializer(serializers.ModelSerializer): + + learning_module = LearningModuleSerializer(many=True) + + class Meta: + model = Course + fields = '__all__' diff --git a/api/urls.py b/api/urls.py index 5dfd8c7..22a9b54 100644 --- a/api/urls.py +++ b/api/urls.py @@ -7,6 +7,9 @@ urlpatterns = [ url(r'questions/$', views.QuestionList.as_view(), name='questions'), url(r'questions/(?P[0-9]+)/$', views.QuestionDetail.as_view(), name='question'), + url(r'get_courses/$', views.CourseList.as_view(), name='get_courses'), + url(r'start_quiz/(?P[0-9]+)/(?P[0-9]+)/$', views.StartQuiz.as_view(), + name='start_quiz'), url(r'quizzes/$', views.QuizList.as_view(), name='quizzes'), url(r'quizzes/(?P[0-9]+)/$', views.QuizDetail.as_view(), name='quiz'), url(r'questionpapers/$', views.QuestionPaperList.as_view(), 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 96539298001042e3a215278b918ea1dffef1f403 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 30 Mar 2020 18:56:50 +0530 Subject: Fix ImproperlyConfigured URL issue --- api/urls.py | 1 + 1 file changed, 1 insertion(+) (limited to 'api') diff --git a/api/urls.py b/api/urls.py index 22a9b54..e81a275 100644 --- a/api/urls.py +++ b/api/urls.py @@ -2,6 +2,7 @@ from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns from api import views +app_name = 'api' urlpatterns = [ url(r'questions/$', views.QuestionList.as_view(), name='questions'), -- cgit From 7d75a279112bce3969d2d4a2b70d74dd67570e29 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 30 Mar 2020 20:39:55 +0530 Subject: Show question test_cases, exclude unncessary fields from API --- api/serializers.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'api') diff --git a/api/serializers.py b/api/serializers.py index df66730..1c1e6a4 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -6,15 +6,21 @@ from yaksh.models import ( class QuestionSerializer(serializers.ModelSerializer): + test_cases = serializers.SerializerMethodField() + + def get_test_cases(self, obj): + test_cases = obj.get_test_cases_as_dict() + return test_cases + class Meta: model = Question - fields = '__all__' + exclude = ('partial_grading', ) class QuizSerializer(serializers.ModelSerializer): class Meta: model = Quiz - fields = '__all__' + exclude = ('view_answerpaper', ) class QuestionPaperSerializer(serializers.ModelSerializer): @@ -63,4 +69,11 @@ class CourseSerializer(serializers.ModelSerializer): class Meta: model = Course - fields = '__all__' + exclude = ( + 'teachers', + 'rejected', + 'requests', + 'students', + 'grading_system', + 'view_grade', + ) -- 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/tests.py | 2 +- api/urls.py | 5 +++++ api/views.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 62 insertions(+), 8 deletions(-) (limited to 'api') diff --git a/api/tests.py b/api/tests.py index feffcc0..4ef6fa4 100644 --- a/api/tests.py +++ b/api/tests.py @@ -1,5 +1,5 @@ from django.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from django.contrib.auth.models import User from rest_framework.test import APIClient from rest_framework import status diff --git a/api/urls.py b/api/urls.py index e81a275..3edf945 100644 --- a/api/urls.py +++ b/api/urls.py @@ -23,6 +23,11 @@ urlpatterns = [ views.AnswerValidator.as_view(), name='validators'), url(r'validate/(?P[0-9]+)/$', views.AnswerValidator.as_view(), name='validator'), + url(r'course/(?P[0-9]+)/$', + views.ICourse.as_view(), name='get_course'), + url(r'quit/(?P\d+)/$', views.QuitQuiz.as_view(), + name="quit_quiz"), + url(r'login/$', views.login, name='login') ] urlpatterns = format_suffix_patterns(urlpatterns) 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/urls.py | 2 +- api/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'api') diff --git a/api/urls.py b/api/urls.py index 3edf945..f519aea 100644 --- a/api/urls.py +++ b/api/urls.py @@ -24,7 +24,7 @@ urlpatterns = [ url(r'validate/(?P[0-9]+)/$', views.AnswerValidator.as_view(), name='validator'), url(r'course/(?P[0-9]+)/$', - views.ICourse.as_view(), name='get_course'), + views.GetCourse.as_view(), name='get_course'), url(r'quit/(?P\d+)/$', views.QuitQuiz.as_view(), name="quit_quiz"), url(r'login/$', views.login, name='login') 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') 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') 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