summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yaksh/models.py23
-rw-r--r--yaksh/templates/yaksh/question.html21
-rw-r--r--yaksh/templates/yaksh/user_data.html10
-rw-r--r--yaksh/test_models.py93
-rw-r--r--yaksh/views.py33
5 files changed, 125 insertions, 55 deletions
diff --git a/yaksh/models.py b/yaksh/models.py
index 898662c..acb46f2 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -786,21 +786,28 @@ class AnswerPaper(models.Model):
Adds the completed question to the list of answered
questions and returns the next question.
"""
+ next_question = self.next_question(question_id)
self.questions_answered.add(question_id)
self.questions_unanswered.remove(question_id)
+ if next_question.id == int(question_id):
+ return None
+ return next_question
- return self.current_question()
-
- def skip(self, question_id):
+ def next_question(self, question_id):
"""
Skips the current question and returns the next sequentially
available question.
"""
- questions = self.questions_unanswered.all()
- question_cycle = cycle(questions)
- for question in question_cycle:
- if question.id==int(question_id):
- return question_cycle.next()
+ unanswered_questions = self.questions_unanswered.all()
+ questions = list(unanswered_questions.values_list('id', flat=True))
+ if len(questions) == 0:
+ return None
+ try:
+ index = questions.index(int(question_id))
+ next_id = questions[index+1]
+ except (ValueError, IndexError):
+ next_id = questions[0]
+ return unanswered_questions.get(id=next_id)
def time_left(self):
"""Return the time remaining for the user in seconds."""
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 73d851a..9a0f899 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -85,19 +85,6 @@ function call_skip(url)
form.action = url
form.submit();
}
- {% if error_message == 'Correct Output'%}
- {% if paper.questions_left %}
- window.setTimeout(function()
- {
- location.href="{{ URL_ROOT }}/exam/{{ paper.current_question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/"
- }, 2000);
- {% else %}
- window.setTimeout(function()
- {
- location.href="{{ URL_ROOT }}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/"
- }, 2000);
- {% endif %}
- {% endif %}
</script>
{% endblock script %}
@@ -165,11 +152,17 @@ function call_skip(url)
<input type=hidden name="question_id" id="question_id" value={{ question.id }}></input>
{% if question.type == "mcq" %}
+ {% if error_message %}
+ <p>{{ error_message }}</p>
+ {% endif %}
{% for test_case in test_cases %}
<input name="answer" type="radio" value="{{ test_case.options }}" />{{ test_case.options }} <br/>
{% endfor %}
{% endif %}
{% if question.type == "mcc" %}
+ {% if error_message %}
+ <p>{{ error_message }}</p>
+ {% endif %}
{% for test_case in test_cases %}
<input name="answer" type="checkbox" value="{{ test_case.options }}"> {{ test_case.options }}
<br>
@@ -188,7 +181,7 @@ function call_skip(url)
{% endif %}
- {% if question.type == "mcq" or question.type == "mcc "%}
+ {% if question.type == "mcq" or question.type == "mcc"%}
<br><button class="btn" type="submit" name="check" id="check">Submit Answer</button>&nbsp;&nbsp;
{% elif question.type == "upload" %}
<br><button class="btn" type="submit" name="check" id="check" onClick="return validate();">Upload</button>&nbsp;&nbsp;
diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html
index 04544f9..2e7db50 100644
--- a/yaksh/templates/yaksh/user_data.html
+++ b/yaksh/templates/yaksh/user_data.html
@@ -56,11 +56,15 @@ User IP address: {{ paper.user_ip }}
</p>
<p>Student answer: {{ answers.0 }}</p>
{% else %}{# non-mcq questions #}
-<pre>
-{% for answer in answers %}################################################################################
+{% for answer in answers %}
+{% if not answer.skipped %}
+<pre>
+###############################################################################
{{ answer.answer.strip }}
# Autocheck: {{ answer.error }}
-{% endfor %}</pre>
+</pre>
+{% endif %}
+{% endfor %}
{% endif %}
{% with answers|last as answer %}
<p><em>Marks: {{answer.marks}} </em> </p>
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 8bd2dda..c0721f3 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -424,7 +424,7 @@ class AnswerPaperTestCases(unittest.TestCase):
self.answerpaper.save()
# answers for the Answer Paper
self.answer_right = Answer(question=Question.objects.get(id=1),
- answer="Demo answer",
+ answer="Demo answer",
correct=True, marks=1
)
self.answer_wrong = Answer(question=Question.objects.get(id=2),
@@ -458,20 +458,85 @@ class AnswerPaperTestCases(unittest.TestCase):
# Test completed_question() method of Answer Paper
question = self.answerpaper.completed_question(1)
self.assertEqual(self.answerpaper.questions_left(), 2)
- # Test skip() method of Answer Paper
+
+ # Test next_question() method of Answer Paper
current_question = self.answerpaper.current_question()
self.assertEqual(current_question.id, 2)
- next_question_id = self.answerpaper.skip(current_question.id)
+
+ # When
+ next_question_id = self.answerpaper.next_question(current_question.id)
+
+ # Then
self.assertTrue(next_question_id is not None)
self.assertEqual(next_question_id.id, 3)
+
+ # Given, here question is already answered
+ current_question_id = 1
+
+ # When
+ next_question_id = self.answerpaper.next_question(current_question_id)
+
+ # Then
+ self.assertTrue(next_question_id is not None)
+ self.assertEqual(next_question_id.id, 2)
+
+ # Given, wrong question id
+ current_question_id = 12
+
+ # When
+ next_question_id = self.answerpaper.next_question(current_question_id)
+
+ # Then
+ self.assertTrue(next_question_id is not None)
+ self.assertEqual(next_question_id.id, 2)
+
+ # Given, last question in the list
+ current_question_id = 3
+
+ # When
+ next_question_id = self.answerpaper.next_question(current_question_id)
+
+ # Then
+ self.assertTrue(next_question_id is not None)
+ self.assertEqual(next_question_id.id, 2)
+
+ # Test get_questions_answered() method
+ # When
questions_answered = self.answerpaper.get_questions_answered()
+
+ # Then
self.assertEqual(questions_answered.count(), 1)
self.assertSequenceEqual(questions_answered, [self.questions[0]])
+
+ # When
questions_unanswered = self.answerpaper.get_questions_unanswered()
+
+ # Then
self.assertEqual(questions_unanswered.count(), 2)
self.assertSequenceEqual(questions_unanswered,
[self.questions[1], self.questions[2]])
+ # Test completed_question and next_question
+ # When all questions are answered
+ current_question = self.answerpaper.completed_question(2)
+
+ # Then
+ self.assertEqual(self.answerpaper.questions_left(), 1)
+ self.assertEqual(current_question.id, 3)
+
+ # When
+ current_question = self.answerpaper.completed_question(3)
+
+ # Then
+ self.assertEqual(self.answerpaper.questions_left(), 0)
+ self.assertTrue(current_question is None)
+
+ # When
+ next_question_id = self.answerpaper.next_question(current_question_id)
+
+ # Then
+ self.assertTrue(next_question_id is None)
+
def test_update_marks(self):
""" Test update_marks method of AnswerPaper"""
self.answerpaper.update_marks('inprogress')
@@ -584,7 +649,7 @@ class CourseTestCases(unittest.TestCase):
def test_add_teachers(self):
""" Test to add teachers to a course"""
self.course.add_teachers(self.student1, self.student2)
- self.assertSequenceEqual(self.course.get_teachers(),
+ self.assertSequenceEqual(self.course.get_teachers(),
[self.student1, self.student2])
def test_remove_teachers(self):
@@ -616,23 +681,23 @@ class CourseTestCases(unittest.TestCase):
class TestCaseTestCases(unittest.TestCase):
def setUp(self):
self.user = User.objects.get(pk=1)
- self.question1 = Question(summary='Demo question 1',
+ self.question1 = Question(summary='Demo question 1',
language='Python',
- type='Code',
+ type='Code',
active=True,
- description='Write a function',
+ description='Write a function',
points=1.0,
- test_case_type="standardtestcase",
+ test_case_type="standardtestcase",
user=self.user,
snippet='def myfunc()'
)
- self.question2 = Question(summary='Demo question 2',
+ self.question2 = Question(summary='Demo question 2',
language='Python',
- type='Code',
+ type='Code',
active=True,
- description='Write to standard output',
+ description='Write to standard output',
points=1.0,
- test_case_type="stdoutbasedtestcase",
+ test_case_type="stdoutbasedtestcase",
user=self.user,
snippet='def myfunc()'
)
@@ -658,13 +723,13 @@ class TestCaseTestCases(unittest.TestCase):
def test_assertion_testcase(self):
""" Test question """
self.assertEqual(self.assertion_testcase.question, self.question1)
- self.assertEqual(self.assertion_testcase.test_case,
+ self.assertEqual(self.assertion_testcase.test_case,
'assert myfunc(12, 13) == 15')
def test_stdout_based_testcase(self):
""" Test question """
self.assertEqual(self.stdout_based_testcase.question, self.question2)
- self.assertEqual(self.stdout_based_testcase.expected_output,
+ self.assertEqual(self.stdout_based_testcase.expected_output,
'Hello World'
)
diff --git a/yaksh/views.py b/yaksh/views.py
index 16454b2..923b3c2 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -454,16 +454,22 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None):
paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num,
question_paper=questionpaper_id)
question = get_object_or_404(Question, pk=q_id)
+ if question in paper.questions_answered.all():
+ next_q = paper.next_question(q_id)
+ return show_question(request, next_q, paper)
+
if request.method == 'POST' and question.type == 'code':
user_code = request.POST.get('answer')
new_answer = Answer(question=question, answer=user_code,
correct=False, skipped=True)
new_answer.save()
paper.answers.add(new_answer)
- if next_q is None:
- next_q = paper.skip(q_id) if paper.skip(q_id) else question
- else:
+ if next_q is not None:
next_q = get_object_or_404(Question, pk=next_q)
+ if next_q not in paper.questions_unanswered.all():
+ return show_question(request, question, paper)
+ else:
+ next_q = paper.next_question(q_id)
return show_question(request, next_q, paper)
@@ -475,7 +481,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
question_paper=questionpaper_id)
question = get_object_or_404(Question, pk=q_id)
if question in paper.questions_answered.all():
- next_q = paper.skip(q_id)
+ next_q = paper.next_question(q_id)
return show_question(request, next_q, paper)
if request.method == 'POST':
@@ -504,7 +510,9 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
correct=False)
new_answer.save()
paper.answers.add(new_answer)
-
+ if not user_answer:
+ msg = "Please submit a valid option or code"
+ return show_question(request, question, paper, msg)
# If we were not skipped, we were asked to check. For any non-mcq
# questions, we obtain the results via XML-RPC with the code executed
# safely in a separate process (the code_server.py) running as nobody.
@@ -525,17 +533,8 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
new_answer.save()
return show_question(request, question, paper, result.get('error'))
else:
- # Display the same question if user_answer is None
- if not user_answer:
- msg = "Please submit a valid option or code"
- return show_question(request, question, paper, msg)
- elif question.type == 'code' and user_answer:
- msg = "Correct Output"
- paper.completed_question(question.id)
- return show_question(request, question, paper, msg)
- else:
- next_q = paper.completed_question(question.id)
- return show_question(request, next_q, paper)
+ next_q = paper.completed_question(question.id)
+ return show_question(request, next_q, paper)
else:
return show_question(request, question, paper)
@@ -558,11 +557,13 @@ def validate_answer(user, user_answer, question, json_data=None):
expected_answer = question.get_test_case(correct=True).options
if user_answer.strip() == expected_answer.strip():
correct = True
+ result['error'] = 'Correct answer'
elif question.type == 'mcc':
expected_answers = []
for opt in question.get_test_cases(correct=True):
expected_answers.append(opt.options)
if set(user_answer) == set(expected_answers):
+ result['error'] = 'Correct answer'
correct = True
elif question.type == 'code':
user_dir = get_user_dir(user)