diff options
-rw-r--r-- | yaksh/models.py | 23 | ||||
-rw-r--r-- | yaksh/templates/yaksh/question.html | 21 | ||||
-rw-r--r-- | yaksh/templates/yaksh/user_data.html | 10 | ||||
-rw-r--r-- | yaksh/test_models.py | 93 | ||||
-rw-r--r-- | yaksh/views.py | 33 |
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> {% elif question.type == "upload" %} <br><button class="btn" type="submit" name="check" id="check" onClick="return validate();">Upload</button> 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) |