summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--api/__init__.py0
-rw-r--r--api/apps.py5
-rw-r--r--api/serializers.py66
-rw-r--r--api/tests.py906
-rw-r--r--api/urls.py27
-rw-r--r--api/views.py374
-rw-r--r--online_test/settings.py15
-rw-r--r--online_test/urls.py2
-rw-r--r--requirements/requirements-common.txt1
-rw-r--r--yaksh/models.py1
11 files changed, 1398 insertions, 0 deletions
diff --git a/.travis.yml b/.travis.yml
index a2cf266..0dd53fb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,6 +27,7 @@ script:
- coverage run -p manage.py test -v 2 yaksh
- coverage run -p manage.py test -v 2 grades
- coverage run -p manage.py test -v 2 yaksh.live_server_tests.load_test
+ - coverage run -p manage.py test -v 2 --settings online_test.test_settings api
after_success:
- coverage combine
diff --git a/api/__init__.py b/api/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/api/__init__.py
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..df66730
--- /dev/null
+++ b/api/serializers.py
@@ -0,0 +1,66 @@
+from rest_framework import serializers
+from yaksh.models import (
+ Question, Quiz, QuestionPaper, AnswerPaper, Course,
+ LearningModule, LearningUnit, Lesson
+)
+
+
+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):
+
+ 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/tests.py b/api/tests.py
new file mode 100644
index 0000000..feffcc0
--- /dev/null
+++ b/api/tests.py
@@ -0,0 +1,906 @@
+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)
+ questionpapers = QuestionPaper.objects.filter(quiz=self.quiz2)
+ self.assertFalse(questionpapers.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(
+ 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)
+ 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.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)}
+ 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.get('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.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):
+ # 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.get('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.get('status') == 'done':
+ result = json.loads(response.data.get('result'))
+ self.assertTrue(result.get('success'))
+ else:
+ self.assertEqual(response.data.get('status'), 'running')
diff --git a/api/urls.py b/api/urls.py
new file mode 100644
index 0000000..22a9b54
--- /dev/null
+++ b/api/urls.py
@@ -0,0 +1,27 @@
+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<pk>[0-9]+)/$', views.QuestionDetail.as_view(),
+ name='question'),
+ url(r'get_courses/$', views.CourseList.as_view(), name='get_courses'),
+ url(r'start_quiz/(?P<course_id>[0-9]+)/(?P<quiz_id>[0-9]+)/$', views.StartQuiz.as_view(),
+ name='start_quiz'),
+ url(r'quizzes/$', views.QuizList.as_view(), name='quizzes'),
+ url(r'quizzes/(?P<pk>[0-9]+)/$', views.QuizDetail.as_view(), name='quiz'),
+ url(r'questionpapers/$', views.QuestionPaperList.as_view(),
+ name='questionpapers'),
+ url(r'questionpapers/(?P<pk>[0-9]+)/$',
+ views.QuestionPaperDetail.as_view(), name='questionpaper'),
+ url(r'answerpapers/$', views.AnswerPaperList.as_view(),
+ name='answerpapers'),
+ url(r'validate/(?P<answerpaper_id>[0-9]+)/(?P<question_id>[0-9]+)/$',
+ views.AnswerValidator.as_view(), name='validators'),
+ url(r'validate/(?P<uid>[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..b81e895
--- /dev/null
+++ b/api/views.py
@@ -0,0 +1,374 @@
+from yaksh.models import (
+ Question, Quiz, QuestionPaper, QuestionSet, AnswerPaper, Course, Answer
+)
+from api.serializers import (
+ QuestionSerializer, QuizSerializer, QuestionPaperSerializer,
+ AnswerPaperSerializer, CourseSerializer
+)
+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 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 """
+
+ 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)
diff --git a/online_test/settings.py b/online_test/settings.py
index 7b9a231..fa3712e 100644
--- a/online_test/settings.py
+++ b/online_test/settings.py
@@ -45,6 +45,8 @@ INSTALLED_APPS = (
'taggit',
'social_django',
'grades',
+ 'rest_framework',
+ 'api',
)
MIDDLEWARE = (
@@ -208,3 +210,16 @@ AUTH_PASSWORD_VALIDATORS = [
]
TAGGIT_CASE_INSENSITIVE = True
+
+REST_FRAMEWORK = {
+ # Use Django's standard `django.contrib.auth` permissions,
+ # or allow read-only access for unauthenticated users.
+ 'DEFAULT_AUTHENTICATION_CLASSES': [
+ 'rest_framework.authentication.BasicAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ ],
+ 'DEFAULT_PERMISSION_CLASSES': [
+ 'rest_framework.permissions.IsAuthenticated'
+ ],
+ 'TEST_REQUEST_DEFAULT_FORMAT': 'json'
+}
diff --git a/online_test/urls.py b/online_test/urls.py
index 52cbd40..bb5a04a 100644
--- a/online_test/urls.py
+++ b/online_test/urls.py
@@ -16,5 +16,7 @@ urlpatterns = [
url(r'^exam/reset/', include('django.contrib.auth.urls')),
url(r'^', include('social_django.urls', namespace='social')),
url(r'^grades/', include(('grades.urls', 'grades'))),
+ url(r'^api/', include('api.urls', namespace='api')),
+
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/requirements/requirements-common.txt b/requirements/requirements-common.txt
index d1fed93..bd38920 100644
--- a/requirements/requirements-common.txt
+++ b/requirements/requirements-common.txt
@@ -10,3 +10,4 @@ coverage
ruamel.yaml==0.15.23
markdown==2.6.9
pygments==2.2.0
+djangorestframework==3.9.0
diff --git a/yaksh/models.py b/yaksh/models.py
index 12c902b..949b87e 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -1717,6 +1717,7 @@ class QuestionPaper(models.Model):
for question_set in self.random_questions.all():
marks += question_set.marks * question_set.num_questions
self.total_marks = marks
+ self.save()
def _get_questions_for_answerpaper(self):
""" Returns fixed and random questions for the answer paper"""