summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yaksh/models.py79
-rw-r--r--yaksh/templates/exam.html2
-rw-r--r--yaksh/test_models.py25
-rw-r--r--yaksh/test_views.py6
-rw-r--r--yaksh/views.py3
5 files changed, 90 insertions, 25 deletions
diff --git a/yaksh/models.py b/yaksh/models.py
index 4859d3e..eb14e06 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
from datetime import datetime, timedelta
import json
+import random
import ruamel.yaml
from ruamel.yaml.scalarstring import PreservedScalarString
from ruamel.yaml.comments import CommentedMap
@@ -851,7 +852,11 @@ class QuestionPaper(models.Model):
questions = self.get_ordered_questions()
for question_set in self.random_questions.all():
questions += question_set.get_random_questions()
- return questions
+ if self.shuffle_questions:
+ all_questions = self.get_shuffled_questions(questions)
+ else:
+ all_questions = questions
+ return all_questions
def make_answerpaper(self, user, ip, attempt_num):
"""Creates an answer paper for the user to attempt the quiz"""
@@ -872,10 +877,11 @@ class QuestionPaper(models.Model):
ans_paper.question_paper = self
ans_paper.save()
questions = self._get_questions_for_answerpaper()
- for question in questions:
- ans_paper.questions.add(question)
- for question in questions:
- ans_paper.questions_unanswered.add(question)
+ ans_paper.questions.add(*questions)
+ question_ids = [str(que.id) for que in questions]
+ ans_paper.questions_order = ",".join(question_ids)
+ ans_paper.save()
+ ans_paper.questions_unanswered.add(*questions)
except AnswerPaper.MultipleObjectsReturned:
ans_paper = AnswerPaper.objects.get(user=user,
attempt_number=attempt_num,
@@ -918,7 +924,7 @@ class QuestionPaper(models.Model):
def create_demo_quiz_ppr(self, demo_quiz, user):
question_paper = QuestionPaper.objects.create(quiz=demo_quiz,
total_marks=6.0,
- shuffle_questions=True
+ shuffle_questions=False
)
summaries = ['Roots of quadratic equation', 'Print Output',
'Adding decimals', 'For Loop over String',
@@ -941,9 +947,19 @@ class QuestionPaper(models.Model):
for que_id in que_order:
ques.append(self.fixed_questions.get(id=que_id))
else:
- ques = self.fixed_questions.all()
+ ques = list(self.fixed_questions.all())
return ques
+ def get_shuffled_questions(self, questions):
+ """Get shuffled questions if auto suffle is enabled"""
+ random.shuffle(questions)
+ return questions
+
+ def has_questions(self):
+ questions = self.get_ordered_questions() + \
+ list(self.random_questions.all())
+ return len(questions) > 0
+
def __str__(self):
return "Question Paper for " + self.quiz.description
@@ -1164,13 +1180,27 @@ class AnswerPaper(models.Model):
default='inprogress'
)
+ # set question order
+ questions_order = models.TextField(blank=True, default='')
+
objects = AnswerPaperManager()
def current_question(self):
"""Returns the current active question to display."""
- if self.questions_unanswered.all():
- return self.questions_unanswered.all()[0]
- return self.questions.all()[0]
+ questions = self.questions_unanswered.all()
+ if questions.exists():
+ cur_question = self.get_current_question(questions)
+ else:
+ cur_question = self.get_current_question(self.questions.all())
+ return cur_question
+
+ def get_current_question(self, questions):
+ if self.questions_order:
+ question_id = int(self.questions_order.split(',')[0])
+ question = self.questions_unanswered.get(id=question_id)
+ else:
+ question = questions.first()
+ return question
def questions_left(self):
"""Returns the number of questions left."""
@@ -1195,17 +1225,30 @@ class AnswerPaper(models.Model):
Skips the current question and returns the next sequentially
available question.
"""
- all_questions = self.questions.all()
- unanswered_questions = self.questions_unanswered.all()
- questions = list(all_questions.values_list('id', flat=True))
- if len(questions) == 0:
+ if self.questions_order:
+ all_questions = [int(q_id)
+ for q_id in self.questions_order.split(',')]
+ else:
+ all_questions = list(self.questions.all().values_list(
+ 'id', flat=True))
+ if len(all_questions) == 0:
return None
try:
- index = questions.index(int(question_id))
- next_id = questions[index+1]
+ index = all_questions.index(int(question_id))
+ next_id = all_questions[index+1]
except (ValueError, IndexError):
- next_id = questions[0]
- return all_questions.get(id=next_id)
+ next_id = all_questions[0]
+ return self.questions.get(id=next_id)
+
+ def get_all_ordered_questions(self):
+ """Get all questions in a specific order for answerpaper"""
+ if self.questions_order:
+ que_ids = [int(q_id) for q_id in self.questions_order.split(',')]
+ questions = [self.questions.get(id=que_id)
+ for que_id in que_ids]
+ else:
+ questions = list(self.questions.all())
+ return questions
def time_left(self):
"""Return the time remaining for the user in seconds."""
diff --git a/yaksh/templates/exam.html b/yaksh/templates/exam.html
index 4d211d2..9596c1c 100644
--- a/yaksh/templates/exam.html
+++ b/yaksh/templates/exam.html
@@ -46,7 +46,7 @@
<div class="col-sm-3 col-md-2 sidebar">
<p> Question Navigator </p>
<ul class="pagination pagination-sm">
- {% for qid in paper.questions.all %}
+ {% for qid in paper.get_all_ordered_questions %}
{%if paper.question_paper.quiz.allow_skip %}
{% if qid in paper.get_questions_unanswered %}
{% if qid.id == question.id %}
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 2c83024..03af521 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -502,6 +502,7 @@ class AnswerPaperTestCases(unittest.TestCase):
def setUpClass(self):
self.ip = '101.0.0.1'
self.user = User.objects.get(username='demo_user')
+ self.user2 = User.objects.get(username='demo_user2')
self.profile = self.user.profile
self.quiz = Quiz.objects.get(description='demo quiz 1')
self.question_paper = QuestionPaper(quiz=self.quiz, total_marks=3)
@@ -590,6 +591,24 @@ class AnswerPaperTestCases(unittest.TestCase):
)
self.mcc_based_testcase.save()
+ # Setup quiz where questions are shuffled
+ # Create Quiz and Question Paper
+ self.quiz2 = Quiz.objects.get(description="demo quiz 2")
+ self.question_paper2 = QuestionPaper(
+ quiz=self.quiz2, total_marks=3, shuffle_questions=True)
+ self.question_paper2.save()
+ summary_list = ['Q%d' % (i) for i in range(1, 21)]
+ que_list = Question.objects.filter(summary__in=summary_list)
+ self.question_paper2.fixed_questions.add(*que_list)
+
+ # Create AnswerPaper for user1 and user2
+ self.user1_answerpaper = self.question_paper2.make_answerpaper(
+ self.user, self.ip, 1
+ )
+ self.user2_answerpaper = self.question_paper2.make_answerpaper(
+ self.user2, self.ip, 1
+ )
+
def test_validate_and_regrade_mcc_correct_answer(self):
# Given
mcc_answer = [str(self.mcc_based_testcase.id)]
@@ -663,7 +682,6 @@ class AnswerPaperTestCases(unittest.TestCase):
self.assertEqual(self.answer.marks, 0)
self.assertFalse(self.answer.correct)
-
def test_mcq_incorrect_answer(self):
# Given
mcq_answer = 'b'
@@ -858,6 +876,11 @@ class AnswerPaperTestCases(unittest.TestCase):
self.assertEqual(latest_answer.id, self.answer1.id)
self.assertEqual(latest_answer.answer, "answer1")
+ def test_shuffle_questions(self):
+ ques_set_1 = self.user1_answerpaper.get_all_ordered_questions()
+ ques_set_2 = self.user2_answerpaper.get_all_ordered_questions()
+ self.assertFalse(ques_set_1 == ques_set_2)
+
###############################################################################
class CourseTestCases(unittest.TestCase):
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 2d86b77..dc06126 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -2092,13 +2092,13 @@ class TestViewAnswerPaper(TestCase):
self.quiz = Quiz.objects.create(time_between_attempts=0, course=self.course,
description='demo quiz', language='Python')
-
+ self.user3 = User.objects.get(username="demo_user3")
self.question_paper = QuestionPaper.objects.create(quiz=self.quiz,
total_marks=1.0)
self.question_paper.fixed_questions.add(self.question)
self.question_paper.save()
- self.ans_paper = AnswerPaper.objects.create(user_id=3,
+ self.ans_paper = AnswerPaper.objects.create(user=self.user3,
attempt_number=1, question_paper=self.question_paper,
start_time=timezone.now(), user_ip='101.0.0.1',
end_time=timezone.now()+timezone.timedelta(minutes=20))
@@ -2382,7 +2382,7 @@ class TestGrader(TestCase):
self.question_paper.fixed_questions.add(self.question)
self.question_paper.save()
- self.answerpaper = AnswerPaper.objects.create(user_id=3,
+ self.answerpaper = AnswerPaper.objects.create(user=self.user2,
attempt_number=1, question_paper=self.question_paper,
start_time=timezone.now(), user_ip='101.0.0.1',
end_time=timezone.now()+timezone.timedelta(minutes=20))
diff --git a/yaksh/views.py b/yaksh/views.py
index 6abfa2b..b4cb844 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -392,8 +392,7 @@ def start(request, questionpaper_id=None, attempt_num=None):
msg = 'Quiz not found, please contact your '\
'instructor/administrator.'
return complete(request, msg, attempt_num, questionpaper_id=None)
- if not quest_paper.get_ordered_questions() and not \
- quest_paper.random_questions.all():
+ if not quest_paper.has_questions():
msg = 'Quiz does not have Questions, please contact your '\
'instructor/administrator.'
return complete(request, msg, attempt_num, questionpaper_id=None)