diff options
-rw-r--r-- | yaksh/evaluator_tests/test_simple_question_types.py | 292 | ||||
-rw-r--r-- | yaksh/forms.py | 8 | ||||
-rw-r--r-- | yaksh/models.py | 74 | ||||
-rw-r--r-- | yaksh/static/yaksh/css/question.css | 5 | ||||
-rw-r--r-- | yaksh/templates/exam.html | 8 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_question.html | 3 | ||||
-rw-r--r-- | yaksh/templates/yaksh/grade_user.html | 7 | ||||
-rw-r--r-- | yaksh/templates/yaksh/question.html | 38 | ||||
-rw-r--r-- | yaksh/templates/yaksh/user_data.html | 8 | ||||
-rw-r--r-- | yaksh/templates/yaksh/view_answerpaper.html | 7 | ||||
-rw-r--r-- | yaksh/test_models.py | 110 | ||||
-rw-r--r-- | yaksh/views.py | 29 |
12 files changed, 524 insertions, 65 deletions
diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py new file mode 100644 index 0000000..1d0a1e2 --- /dev/null +++ b/yaksh/evaluator_tests/test_simple_question_types.py @@ -0,0 +1,292 @@ +import unittest +from datetime import datetime, timedelta +from django.utils import timezone +import pytz +from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ + QuestionSet, AnswerPaper, Answer, Course, IntegerTestCase, FloatTestCase,\ + StringTestCase + + +def setUpModule(): + # create user profile + user = User.objects.create_user(username='demo_user_100', + password='demo', + email='demo@test.com') + Profile.objects.create(user=user, roll_number=1, + institute='IIT', department='Aerospace', + position='Student') + # create 2 questions + for i in range(101, 103): + Question.objects.create(summary='Q%d' % (i), points=1, + type='code', user=user) + + # create a course + course = Course.objects.create(name="Python Course 100", + enrollment="Enroll Request", creator=user) + + quiz = Quiz.objects.create(start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), + end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), + duration=30, active=True, attempts_allowed=1, + time_between_attempts=0, description='demo quiz 100', + pass_criteria=0,language='Python', + prerequisite=None,course=course, + instructions="Demo Instructions" + ) + question_paper = QuestionPaper.objects.create(quiz=quiz, + total_marks=1.0) + + answerpaper = AnswerPaper.objects.create(user=user, user_ip='101.0.0.1', + start_time=timezone.now(), + question_paper=question_paper, + end_time=timezone.now() + +timedelta(minutes=5), + attempt_number=1 + ) + +def tearDownModule(): + User.objects.get(username="demo_user_100").delete() + + +class IntegerQuestionTestCases(unittest.TestCase): + @classmethod + def setUpClass(self): + # Creating Quiz + self.quiz = Quiz.objects.get(description="demo quiz 100") + # Creating Question paper + self.question_paper = QuestionPaper.objects.get(quiz=self.quiz) + + #Creating User + self.user = User.objects.get(username='demo_user_100') + + #Creating Question + self.question1 = Question.objects.get(summary='Q101') + self.question1.language = 'python' + self.question1.type = "integer" + self.question1.test_case_type = 'integertestcase' + self.question1.description = 'sum of 12+13?' + self.question1.save() + + #Creating answerpaper + self.answerpaper = AnswerPaper.objects.get(question_paper\ + =self.question_paper) + self.answerpaper.attempt_number = 1 + self.answerpaper.save() + # For question + self.integer_based_testcase = IntegerTestCase(question=self.question1, + correct=25, + type = 'integertestcase', + ) + self.integer_based_testcase.save() + + def test_integer_correct_answer(self): + # Given + integer_answer = 25 + self.answer = Answer(question=self.question1, + answer=integer_answer, + ) + self.answer.save() + self.answerpaper.answers.add(self.answer) + + # When + json_data = None + result = self.answerpaper.validate_answer(integer_answer, + self.question1, + json_data, + ) + # Then + self.assertTrue(result['success']) + + def test_integer_incorrect_answer(self): + # Given + integer_answer = 26 + self.answer = Answer(question=self.question1, + answer=integer_answer, + ) + self.answer.save() + self.answerpaper.answers.add(self.answer) + + # When + json_data = None + result = self.answerpaper.validate_answer(integer_answer, + self.question1, json_data + ) + + # Then + self.assertFalse(result['success']) + + +class StringQuestionTestCases(unittest.TestCase): + @classmethod + def setUpClass(self): + # Creating Quiz + self.quiz = Quiz.objects.get(description="demo quiz 100") + # Creating Question paper + self.question_paper = QuestionPaper.objects.get(quiz=self.quiz) + #Creating User + self.user = User.objects.get(username='demo_user_100') + #Creating Question + self.question1 = Question.objects.get(summary='Q101') + self.question1.language = 'python' + self.question1.type = "string" + self.question1.test_case_type = 'stringtestcase' + self.question1.description = 'Write Hello, EARTH!' + self.question1.save() + + self.question2 = Question.objects.get(summary='Q102') + self.question2.language = 'python' + self.question2.type = "string" + self.question2.test_case_type = 'stringtestcase' + self.question2.description = 'Write Hello, EARTH!' + self.question2.save() + + #Creating answerpaper + self.answerpaper = AnswerPaper.objects.get(question_paper\ + =self.question_paper) + self.answerpaper.attempt_number = 1 + self.answerpaper.save() + + # For question + self.lower_string_testcase = StringTestCase(question=self.question1, + correct="Hello, EARTH!", + string_check="lower", + type = 'stringtestcase', + ) + self.lower_string_testcase.save() + + self.exact_string_testcase = StringTestCase(question=self.question2, + correct="Hello, EARTH!", + string_check="exact", + type = 'stringtestcase', + ) + self.exact_string_testcase.save() + + def test_case_insensitive_string_correct_answer(self): + # Given + string_answer = "hello, earth!" + answer = Answer(question=self.question1,answer=string_answer) + answer.save() + self.answerpaper.answers.add(answer) + + # When + json_data = None + result = self.answerpaper.validate_answer(string_answer, + self.question1, json_data + ) + # Then + self.assertTrue(result['success']) + + def test_case_insensitive_string_incorrect_answer(self): + # Given + string_answer = "hello, mars!" + answer = Answer(question=self.question1,answer=string_answer) + answer.save() + self.answerpaper.answers.add(answer) + + # When + json_data = None + result = self.answerpaper.validate_answer(string_answer, + self.question1, json_data + ) + + # Then + self.assertFalse(result['success']) + + def test_case_sensitive_string_correct_answer(self): + # Given + string_answer = "Hello, EARTH!" + answer = Answer(question=self.question2,answer=string_answer) + answer.save() + self.answerpaper.answers.add(answer) + + # When + json_data = None + result = self.answerpaper.validate_answer(string_answer, + self.question2, json_data + ) + # Then + self.assertTrue(result['success']) + + def test_case_sensitive_string_incorrect_answer(self): + # Given + string_answer = "hello, earth!" + answer = Answer(question=self.question2,answer=string_answer) + answer.save() + self.answerpaper.answers.add(answer) + + # When + json_data = None + result = self.answerpaper.validate_answer(string_answer, + self.question2, json_data + ) + + # Then + self.assertFalse(result['success']) + + +class FloatQuestionTestCases(unittest.TestCase): + @classmethod + def setUpClass(self): + # Creating Quiz + self.quiz = Quiz.objects.get(description="demo quiz 100") + # Creating Question paper + self.question_paper = QuestionPaper.objects.get(quiz=self.quiz) + + #Creating User + self.user = User.objects.get(username='demo_user_100') + #Creating Question + self.question1 = Question.objects.get(summary='Q101') + self.question1.language = 'python' + self.question1.type = "float" + self.question1.test_case_type = 'floattestcase' + self.question1.save() + + #Creating answerpaper + self.answerpaper = AnswerPaper.objects.get(question_paper\ + =self.question_paper) + self.answerpaper.attempt_number = 1 + self.answerpaper.save() + # For question + self.float_based_testcase = FloatTestCase(question=self.question1, + correct=100, + error_margin=0.1, + type = 'floattestcase', + ) + self.float_based_testcase.save() + + def test_float_correct_answer(self): + # Given + float_answer = 99.9 + self.answer = Answer(question=self.question1, + answer=float_answer, + ) + self.answer.save() + self.answerpaper.answers.add(self.answer) + + # When + json_data = None + result = self.answerpaper.validate_answer(float_answer, + self.question1, + json_data, + ) + # Then + self.assertTrue(result['success']) + + def test_integer_incorrect_answer(self): + # Given + float_answer = 99.8 + self.answer = Answer(question=self.question1, + answer=float_answer, + ) + self.answer.save() + self.answerpaper.answers.add(self.answer) + + # When + json_data = None + result = self.answerpaper.validate_answer(float_answer, + self.question1, json_data + ) + + # Then + self.assertFalse(result['success']) diff --git a/yaksh/forms.py b/yaksh/forms.py index 6ec031c..c6283c8 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -1,7 +1,7 @@ from django import forms from yaksh.models import get_model_class, Profile, Quiz, Question, TestCase, Course,\ QuestionPaper, StandardTestCase, StdIOBasedTestCase, \ - HookTestCase + HookTestCase, IntegerTestCase, StringTestCase from django.contrib.auth import authenticate from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType @@ -35,6 +35,9 @@ question_types = ( ("mcc", "Multiple Correct Choices"), ("code", "Code"), ("upload", "Assignment Upload"), + ("integer", "Answer in Integer"), + ("string", "Answer in String"), + ("float", "Answer in Float"), ) test_case_types = ( @@ -42,6 +45,9 @@ test_case_types = ( ("stdiobasedtestcase", "StdIO Based Testcase"), ("mcqtestcase", "MCQ Testcase"), ("hooktestcase", "Hook Testcase"), + ("integertestcase", "Integer Testcase"), + ("stringtestcase", "String Testcase"), + ("floattestcase", "Float Testcase"), ) UNAME_CHARS = letters + "._" + digits diff --git a/yaksh/models.py b/yaksh/models.py index 2d84622..d60a377 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -36,10 +36,13 @@ languages = ( ) question_types = ( - ("mcq", "Multiple Choice"), + ("mcq", "Single Correct Choice"), ("mcc", "Multiple Correct Choices"), ("code", "Code"), ("upload", "Assignment Upload"), + ("integer", "Answer in Integer"), + ("string", "Answer in String"), + ("float", "Answer in Float"), ) enrollment_methods = ( @@ -52,6 +55,14 @@ test_case_types = ( ("stdiobasedtestcase", "StdIO Based Testcase"), ("mcqtestcase", "MCQ Testcase"), ("hooktestcase", "Hook Testcase"), + ("integertestcase", "Integer Testcase"), + ("stringtestcase", "String Testcase"), + ("floattestcase", "Float Testcase"), + ) + +string_check_type = ( + ("lower", "Case Insensitive"), + ("exact", "Case Sensitive"), ) attempts = [(i, i) for i in range(1, 6)] @@ -672,7 +683,6 @@ class QuestionPaper(models.Model): # Total marks for the question paper. total_marks = models.FloatField(default=0.0, blank=True) - objects = QuestionPaperManager() def update_total_marks(self): @@ -1133,6 +1143,30 @@ class AnswerPaper(models.Model): if set(user_answer) == set(expected_answers): result['success'] = True result['error'] = ['Correct answer'] + + elif question.type == 'integer': + expected_answer = question.get_test_case().correct + if expected_answer == int(user_answer): + result['success'] = True + result['error'] = ['Correct answer'] + + elif question.type == 'string': + testcase = question.get_test_case() + if testcase.string_check == "lower": + if testcase.correct.lower().splitlines() == user_answer.lower().splitlines(): + result['success'] = True + result['error'] = ['Correct answer'] + else: + if testcase.correct.splitlines() == user_answer.splitlines(): + result['success'] = True + result['error'] = ['Correct answer'] + + elif question.type == 'float': + testcase = question.get_test_case() + if abs(testcase.correct-user_answer) <= testcase.error_margin: + result['success'] = True + result['error'] = ['Correct answer'] + elif question.type == 'code': user_dir = self.user.profile.get_user_dir() json_result = code_server.run_code( @@ -1280,3 +1314,39 @@ class HookTestCase(TestCase): def __str__(self): return u'Hook Testcase | Correct: {0}'.format(self.hook_code) + +class IntegerTestCase(TestCase): + correct = models.IntegerField(default=None) + + def get_field_value(self): + return {"test_case_type": "integertestcase", "correct": self.correct} + + def __str__(self): + return u'Integer Testcase | Correct: {0}'.format(self.correct) + + +class StringTestCase(TestCase): + correct = models.TextField(default=None) + string_check = models.CharField(max_length=200,choices=string_check_type) + + def get_field_value(self): + return {"test_case_type": "stringtestcase", "correct": self.correct, + "string_check":self.string_check} + + def __str__(self): + return u'String Testcase | Correct: {0}'.format(self.correct) + + +class FloatTestCase(TestCase): + correct = models.FloatField(default=None) + error_margin = models.FloatField(default=0.0, null=True, blank=True, + help_text="Margin of error") + + def get_field_value(self): + return {"test_case_type": "floattestcase", "correct": self.correct, + "error_margin":self.error_margin} + + def __str__(self): + return u'Testcase | Correct: {0} | Error Margin: +or- {1}'.format(self.correct, + self.error_margin + ) diff --git a/yaksh/static/yaksh/css/question.css b/yaksh/static/yaksh/css/question.css index 9fb2e1a..fdbe5f2 100644 --- a/yaksh/static/yaksh/css/question.css +++ b/yaksh/static/yaksh/css/question.css @@ -36,3 +36,8 @@ .lineObj{ color: grey; } + +#string{ + width: 60em; + height: 10em; +} diff --git a/yaksh/templates/exam.html b/yaksh/templates/exam.html index b497cc0..ae1a620 100644 --- a/yaksh/templates/exam.html +++ b/yaksh/templates/exam.html @@ -84,9 +84,13 @@ <div class="panel-body"><pre><code>{{ error }}</code></pre></div> </div> {% endfor %} - + {% if error != "Incorrect answer" %} + <div class="panel-heading">Testcase No. {{ forloop.counter }}</div> + <div class="panel-body"><pre><code>{{ error }}</code></pre></div> + {% endif %} </div> - {% endif %} + {% endfor %} + </div> {% endif %} </div> </div> diff --git a/yaksh/templates/yaksh/add_question.html b/yaksh/templates/yaksh/add_question.html index 75802b4..6bd96d3 100644 --- a/yaksh/templates/yaksh/add_question.html +++ b/yaksh/templates/yaksh/add_question.html @@ -56,6 +56,9 @@ <option value="stdiobasedtestcase">StdIO </option> <option value="mcqtestcase">MCQ/MCC </option> <option value="hooktestcase">Hook </option> + <option value="integertestcase">Integer </option> + <option value="stringtestcase"> String </option> + <option value="floattestcase"> Float </option> </select></p> <center> <button class="btn" type="submit" name="save_question">Save</button> diff --git a/yaksh/templates/yaksh/grade_user.html b/yaksh/templates/yaksh/grade_user.html index 63ff5eb..8653dc7 100644 --- a/yaksh/templates/yaksh/grade_user.html +++ b/yaksh/templates/yaksh/grade_user.html @@ -139,6 +139,13 @@ Status : <b style="color: green;"> Passed </b><br/> {{ forloop.counter }}. {{ testcase.options|safe }}</strong> {% endif %} {% endfor %} + + {% elif question.type == "integer" or "string" or "float" %} + <h5> <u>Correct Answer:</u></h5> + {% for testcase in question.get_test_cases %} + <strong>{{ testcase.correct|safe }}</strong> + {% endfor %} + {% else %} <h5> <u>Test cases: </u></h5> {% for testcase in question.get_test_cases %} diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index 1f6e67d..0106bac 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -143,13 +143,25 @@ function call_skip(url) <div class="panel-heading"> <h4><u> {{ question.summary }} {% if question.type == "mcq" %} - (Single Correct Choice Questions) + (SINGLE CORRECT CHOICE) {% elif question.type == "mcc" %} - (Multiple Correct Choices) + (MULTIPLE CORRECT CHOICES) {% elif question.type == "code" %} (PROGRAMMING) {% elif question.type == "upload" %} (ASSIGNMENT UPLOAD) + {% elif question.type == "integer" %} + (ANSWER IN INTEGER) + {% elif question.type == "string" %} + (ANSWER IN STRING) + {% if testcase.string_check == "lower" %} + <h5>(CASE INSENSITIVE)</h5> + {% else %} + <h5>(CASE SENSITIVE)</h5> + {% endif %} + + {% elif question.type == "float" %} + (ANSWER IN FLOAT) {% endif %} </u> <font class=pull-right>(Marks : {{ question.points }}) </font> @@ -168,6 +180,25 @@ function call_skip(url) <input name="answer" type="radio" value="{{ test_case.options }}" />{{ test_case.options|safe }} <br/> {% endfor %} {% endif %} + + {% if question.type == "integer" %} + Enter Integer: + <input name="answer" type="textbox" id="integer" /> + <br/><br/> + {% endif %} + + {% if question.type == "string" %} + Enter Text:<br/> + <textarea name="answer" id="string"></textarea> + <br/><br/> + {% endif %} + + {% if question.type == "float" %} + Enter Decimal Value : + <input name="answer" type="textbox" id="float" /> + <br/><br/> + {% endif %} + {% if question.type == "mcc" %} {% for test_case in test_cases %} <input name="answer" type="checkbox" value="{{ test_case.options }}"> {{ test_case.options|safe }} @@ -193,10 +224,11 @@ function call_skip(url) {% endif %} <div class="from-group"> - {% if question.type == "mcq" or question.type == "mcc"%} + {% if question.type == "mcq" or "mcc" or "integer" or "float" or "string" %} <br><button class="btn btn-primary" type="submit" name="check" id="check">Submit Answer</button> {% elif question.type == "upload" %} <br><button class="btn btn-primary" type="submit" name="check" id="check" onClick="return validate();">Upload</button> + {% else %} <button class="btn btn-primary" type="submit" name="check" id="check" onClick="submitCode();">Check Answer <span class="glyphicon glyphicon-cog"></span></button> {% endif %} diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html index 9be28fb..832e343 100644 --- a/yaksh/templates/yaksh/user_data.html +++ b/yaksh/templates/yaksh/user_data.html @@ -75,6 +75,14 @@ User IP address: {{ paper.user_ip }} {{ forloop.counter }}. {{ testcase.options|safe }}</strong> {% endif %} {% endfor %} + + {{% elif question.type == "integer" or "string" or "float" %} + <h5> <u>Correct Answer:</u></h5> + {% for testcase in question.get_test_cases %} + <strong>{{ testcase.correct|safe }}</strong> + {% endfor %} + + {% else %} <h5> <u>Test cases: </u></h5> {% for testcase in question.get_test_cases %} diff --git a/yaksh/templates/yaksh/view_answerpaper.html b/yaksh/templates/yaksh/view_answerpaper.html index 5eb55df..633be9e 100644 --- a/yaksh/templates/yaksh/view_answerpaper.html +++ b/yaksh/templates/yaksh/view_answerpaper.html @@ -51,6 +51,13 @@ {{ forloop.counter }}. {{ testcase.options|safe }}</strong> {% endif %} {% endfor %} + + {% elif question.type == "integer" or "string" or "float" %} + <h5> <u>Correct Answer:</u></h5> + {% for testcase in question.get_test_cases %} + <strong>{{ testcase.correct|safe }}</strong> + {% endfor %} + {% else %} <h5> <u>Test cases: </u></h5> {% for testcase in question.get_test_cases %} diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 91d8806..ada5a65 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -44,7 +44,7 @@ def setUpModule(): tzinfo=pytz.utc), duration=30, active=True, attempts_allowed=1, time_between_attempts=0, - description='demo quiz', pass_criteria=0, + description='demo quiz 1', pass_criteria=0, language='Python', prerequisite=None, course=course, instructions="Demo Instructions") @@ -54,7 +54,7 @@ def setUpModule(): tzinfo=pytz.utc), duration=30, active=False, attempts_allowed=-1, time_between_attempts=0, - description='demo quiz', pass_criteria=40, + description='demo quiz 2', pass_criteria=40, language='Python', prerequisite=quiz, course=course, instructions="Demo Instructions") @@ -65,6 +65,8 @@ def tearDownModule(): User.objects.all().delete() Question.objects.all().delete() Quiz.objects.all().delete() + Course.objects.all().delete() + QuestionPaper.objects.all().delete() que_id_list = ["25", "22", "24", "27"] for que_id in que_id_list: @@ -75,9 +77,9 @@ def tearDownModule(): ############################################################################### class ProfileTestCases(unittest.TestCase): def setUp(self): - self.user1 = User.objects.get(pk=1) - self.profile = Profile.objects.get(pk=1) - self.user2 = User.objects.get(pk=3) + self.user1 = User.objects.get(username="demo_user") + self.profile = Profile.objects.get(user=self.user1) + self.user2 = User.objects.get(username='demo_user3') def test_user_profile(self): """ Test user profile""" @@ -92,9 +94,9 @@ class ProfileTestCases(unittest.TestCase): class QuestionTestCases(unittest.TestCase): def setUp(self): # Single question details - self.user1 = User.objects.get(pk=1) - self.user2 = User.objects.get(pk=2) - self.question1 = Question(summary='Demo question', + self.user1 = User.objects.get(username="demo_user") + self.user2 = User.objects.get(username="demo_user2") + self.question1 = Question.objects.create(summary='Demo Python 1', language='Python', type='Code', active=True, @@ -103,9 +105,8 @@ class QuestionTestCases(unittest.TestCase): snippet='def myfunc()', user=self.user1 ) - self.question1.save() - self.question2 = Question(summary='Demo Json', + self.question2 = Question.objects.create(summary='Demo Json', language='python', type='code', active=True, @@ -114,7 +115,6 @@ class QuestionTestCases(unittest.TestCase): snippet='def fact()', user=self.user2 ) - self.question2.save() # create a temp directory and add files for loading questions test file_path = "/tmp/test.txt" @@ -161,7 +161,7 @@ class QuestionTestCases(unittest.TestCase): def test_question(self): """ Test question """ - self.assertEqual(self.question1.summary, 'Demo question') + self.assertEqual(self.question1.summary, 'Demo Python 1') self.assertEqual(self.question1.language, 'Python') self.assertEqual(self.question1.type, 'Code') self.assertEqual(self.question1.description, 'Write a function') @@ -203,8 +203,8 @@ class QuestionTestCases(unittest.TestCase): """ Test load questions into database from json """ question = Question() result = question.load_questions(self.json_questions_data, self.user1) - question_data = Question.objects.get(pk=25) - file = FileUpload.objects.get(question=25) + question_data = Question.objects.get(summary="Json Demo") + file = FileUpload.objects.get(question=question_data) test_case = question_data.get_test_cases() self.assertEqual(question_data.summary, 'Json Demo') self.assertEqual(question_data.language, 'Python') @@ -220,10 +220,10 @@ class QuestionTestCases(unittest.TestCase): ############################################################################### class QuizTestCases(unittest.TestCase): def setUp(self): - self.creator = User.objects.get(pk=1) - self.teacher = User.objects.get(pk=2) - self.quiz1 = Quiz.objects.get(pk=1) - self.quiz2 = Quiz.objects.get(pk=2) + self.creator = User.objects.get(username="demo_user") + self.teacher = User.objects.get(username="demo_user2") + self.quiz1 = Quiz.objects.get(description='demo quiz 1') + self.quiz2 = Quiz.objects.get(description='demo quiz 2') self.trial_course = Course.objects.create_trial_course(self.creator) def test_quiz(self): @@ -234,7 +234,7 @@ class QuizTestCases(unittest.TestCase): '10:08:15') self.assertEqual(self.quiz1.duration, 30) self.assertTrue(self.quiz1.active) - self.assertEqual(self.quiz1.description, 'demo quiz') + self.assertEqual(self.quiz1.description, 'demo quiz 1') self.assertEqual(self.quiz1.language, 'Python') self.assertEqual(self.quiz1.pass_criteria, 0) self.assertEqual(self.quiz1.prerequisite, None) @@ -270,7 +270,9 @@ class QuizTestCases(unittest.TestCase): self.creator, True ) - self.assertEqual(trial_quiz.description, "Trial_orig_id_1_godmode") + self.assertEqual(trial_quiz.description, + "Trial_orig_id_{}_godmode".format(self.quiz1.id) + ) self.assertTrue(trial_quiz.is_trial) self.assertEqual(trial_quiz.duration, 1000) self.assertTrue(trial_quiz.active) @@ -285,7 +287,8 @@ class QuizTestCases(unittest.TestCase): self.creator, False ) - self.assertEqual(trial_quiz.description, "Trial_orig_id_2_usermode") + self.assertEqual(trial_quiz.description, + "Trial_orig_id_{}_usermode".format(self.quiz2.id)) self.assertTrue(trial_quiz.is_trial) self.assertEqual(trial_quiz.duration, self.quiz2.duration) self.assertEqual(trial_quiz.active, self.quiz2.active) @@ -316,7 +319,7 @@ class QuestionPaperTestCases(unittest.TestCase): def setUpClass(self): # All active questions self.questions = Question.objects.filter(active=True) - self.quiz = Quiz.objects.get(id=1) + self.quiz = Quiz.objects.get(description="demo quiz 1") # create question paper self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, @@ -359,7 +362,7 @@ class QuestionPaperTestCases(unittest.TestCase): # ip address for AnswerPaper self.ip = '127.0.0.1' - self.user = User.objects.get(pk=1) + self.user = User.objects.get(username="demo_user") self.attempted_papers = AnswerPaper.objects.filter( question_paper=self.question_paper, @@ -373,7 +376,7 @@ class QuestionPaperTestCases(unittest.TestCase): def test_questionpaper(self): """ Test question paper""" - self.assertEqual(self.question_paper.quiz.description, 'demo quiz') + self.assertEqual(self.question_paper.quiz.description, 'demo quiz 1') self.assertSequenceEqual(self.question_paper.fixed_questions.all(), [self.questions[3], self.questions[5]]) self.assertTrue(self.question_paper.shuffle_questions) @@ -453,17 +456,17 @@ class AnswerPaperTestCases(unittest.TestCase): @classmethod def setUpClass(self): self.ip = '101.0.0.1' - self.user = User.objects.get(id=1) + self.user = User.objects.get(username='demo_user') self.profile = self.user.profile - self.quiz = Quiz.objects.get(pk=1) + self.quiz = Quiz.objects.get(description='demo quiz 1') self.question_paper = QuestionPaper(quiz=self.quiz, total_marks=3) self.question_paper.save() - self.questions = Question.objects.filter(id__in=[1,2,3]) + self.questions = Question.objects.all()[0:3] self.start_time = timezone.now() self.end_time = self.start_time + timedelta(minutes=20) - self.question1 = self.questions.get(id=1) - self.question2 = self.questions.get(id=2) - self.question3 = self.questions.get(id=3) + self.question1 = self.questions[0] + self.question2 = self.questions[1] + self.question3 = self.questions[2] # create answerpaper self.answerpaper = AnswerPaper(user=self.user, @@ -483,12 +486,12 @@ class AnswerPaperTestCases(unittest.TestCase): self.answerpaper.questions_unanswered.add(*self.questions) self.answerpaper.save() # answers for the Answer Paper - self.answer_right = Answer(question=Question.objects.get(id=1), + self.answer_right = Answer(question=self.question1, answer="Demo answer", correct=True, marks=1, error=json.dumps([]) ) - self.answer_wrong = Answer(question=Question.objects.get(id=2), + self.answer_wrong = Answer(question=self.question2, answer="My answer", correct=False, marks=0, @@ -501,14 +504,17 @@ class AnswerPaperTestCases(unittest.TestCase): self.question1.language = 'python' self.question1.test_case_type = 'standardtestcase' + self.question1.summary = "Question1" self.question1.save() self.question2.language = 'python' self.question2.type = 'mcq' self.question2.test_case_type = 'mcqtestcase' + self.question2.summary = "Question2" self.question2.save() self.question3.language = 'python' self.question3.type = 'mcc' self.question3.test_case_type = 'mcqtestcase' + self.question3.summary = "Question3" self.question3.save() self.assertion_testcase = StandardTestCase( question=self.question1, @@ -660,21 +666,21 @@ class AnswerPaperTestCases(unittest.TestCase): self.assertEqual(self.answerpaper.questions_left(), 3) # Test current_question() method of Answer Paper current_question = self.answerpaper.current_question() - self.assertEqual(current_question.id, 1) + self.assertEqual(current_question.summary, "Question1") # Test completed_question() method of Answer Paper - question = self.answerpaper.completed_question(1) + question = self.answerpaper.completed_question(self.question1.id) self.assertEqual(self.answerpaper.questions_left(), 2) # Test next_question() method of Answer Paper current_question = self.answerpaper.current_question() - self.assertEqual(current_question.id, 2) + self.assertEqual(current_question.summary, "Question2") # 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) + self.assertEqual(next_question_id.summary, "Question3") # Given, here question is already answered current_question_id = 1 @@ -684,7 +690,7 @@ class AnswerPaperTestCases(unittest.TestCase): # Then self.assertTrue(next_question_id is not None) - self.assertEqual(next_question_id.id, 2) + self.assertEqual(next_question_id.summary, "Question2") # Given, wrong question id current_question_id = 12 @@ -694,7 +700,7 @@ class AnswerPaperTestCases(unittest.TestCase): # Then self.assertTrue(next_question_id is not None) - self.assertEqual(next_question_id.id, 2) + self.assertEqual(next_question_id.summary, "Question2") # Given, last question in the list current_question_id = 3 @@ -704,7 +710,7 @@ class AnswerPaperTestCases(unittest.TestCase): # Then self.assertTrue(next_question_id is not None) - self.assertEqual(next_question_id.id, 2) + self.assertEqual(next_question_id.summary, "Question2") # Test get_questions_answered() method # When @@ -724,14 +730,14 @@ class AnswerPaperTestCases(unittest.TestCase): # Test completed_question and next_question # When all questions are answered - current_question = self.answerpaper.completed_question(2) + current_question = self.answerpaper.completed_question(self.question2.id) # Then self.assertEqual(self.answerpaper.questions_left(), 1) - self.assertEqual(current_question.id, 3) + self.assertEqual(current_question.summary, "Question3") # When - current_question = self.answerpaper.completed_question(3) + current_question = self.answerpaper.completed_question(self.question3.id) # Then self.assertEqual(self.answerpaper.questions_left(), 0) @@ -791,12 +797,12 @@ class AnswerPaperTestCases(unittest.TestCase): ############################################################################### class CourseTestCases(unittest.TestCase): def setUp(self): - self.course = Course.objects.get(pk=1) - self.creator = User.objects.get(pk=1) - self.student1 = User.objects.get(pk=2) - self.student2 = User.objects.get(pk=3) - self.quiz1 = Quiz.objects.get(pk=1) - self.quiz2 = Quiz.objects.get(pk=2) + self.course = Course.objects.get(name="Python Course") + self.creator = User.objects.get(username="demo_user") + self.student1 = User.objects.get(username="demo_user2") + self.student2 = User.objects.get(username="demo_user3") + self.quiz1 = Quiz.objects.get(description='demo quiz 1') + self.quiz2 = Quiz.objects.get(description='demo quiz 2') def test_is_creator(self): @@ -873,21 +879,19 @@ class CourseTestCases(unittest.TestCase): def test_create_trial_course(self): """Test to check if trial course is created""" - # Test for manager method create_trial_course trial_course = Course.objects.create_trial_course(self.creator) self.assertEqual(trial_course.name, "trial_course") self.assertEqual(trial_course.enrollment, "open") self.assertTrue(trial_course.active) - self.assertEqual(trial_course.students.get(user=self.creator.id), - self.creator - ) + self.assertEqual(self.creator, trial_course.creator) + self.assertIn(self.creator, trial_course.students.all()) self.assertTrue(trial_course.is_trial) ############################################################################### class TestCaseTestCases(unittest.TestCase): def setUp(self): - self.user = User.objects.get(pk=1) + self.user = User.objects.get(username="demo_user") self.question1 = Question(summary='Demo question 1', language='Python', type='Code', diff --git a/yaksh/views.py b/yaksh/views.py index 74c409c..d067393 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -16,6 +16,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.models import Group from django.forms.models import inlineformset_factory from django.utils import timezone +from django.core.exceptions import MultipleObjectsReturned import pytz from taggit.models import Tag from itertools import chain @@ -25,7 +26,8 @@ import six # Local imports. from yaksh.models import get_model_class, Quiz, Question, QuestionPaper, QuestionSet, Course from yaksh.models import Profile, Answer, AnswerPaper, User, TestCase, FileUpload,\ - has_profile, StandardTestCase, McqTestCase, StdIOBasedTestCase, HookTestCase + has_profile, StandardTestCase, McqTestCase,\ + StdIOBasedTestCase, HookTestCase, IntegerTestCase from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\ QuestionForm, RandomQuestionForm,\ QuestionFilterForm, CourseForm, ProfileForm, UploadFileForm,\ @@ -468,6 +470,21 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): # Add the answer submitted, regardless of it being correct or not. if current_question.type == 'mcq': user_answer = request.POST.get('answer') + elif current_question.type == 'integer': + try: + user_answer = int(request.POST.get('answer')) + except ValueError: + msg = ["Please enter an Integer Value"] + return show_question(request, current_question, paper, msg) + elif current_question.type == 'float': + try: + user_answer = float(request.POST.get('answer')) + except ValueError: + msg = ["Please enter a Decimal Value"] + return show_question(request, current_question, paper, msg) + elif current_question.type == 'string': + user_answer = str(request.POST.get('answer')) + elif current_question.type == 'mcc': user_answer = request.POST.getlist('answer') elif current_question.type == 'upload': @@ -495,9 +512,13 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): # 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. - json_data = current_question.consolidate_answer_data(user_answer) \ - if current_question.type == 'code' else None - result = paper.validate_answer(user_answer, current_question, json_data) + try: + json_data = current_question.consolidate_answer_data(user_answer) \ + if current_question.type == 'code' else None + result = paper.validate_answer(user_answer, current_question, json_data) + except MultipleObjectsReturned: + msg = ["Multiple objects returned. Contact admin."] + return show_question(request, current_question, paper, msg) if result.get('success'): new_answer.marks = (current_question.points * result['weight'] / current_question.get_maximum_test_case_weight()) \ |