summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yaksh/documentation/images/float_testcase.jpgbin0 -> 81100 bytes
-rw-r--r--yaksh/documentation/images/integer_testcase.jpgbin0 -> 82479 bytes
-rw-r--r--yaksh/documentation/images/string_testcase.jpgbin0 -> 84962 bytes
-rw-r--r--yaksh/documentation/moderator_docs/creating_question.rst37
-rw-r--r--yaksh/evaluator_tests/test_simple_question_types.py304
-rw-r--r--yaksh/forms.py8
-rw-r--r--yaksh/migrations/0003_auto_20170321_0917.py52
-rw-r--r--yaksh/models.py83
-rw-r--r--yaksh/static/yaksh/css/question.css5
-rw-r--r--yaksh/templates/yaksh/add_question.html3
-rw-r--r--yaksh/templates/yaksh/grade_user.html10
-rw-r--r--yaksh/templates/yaksh/question.html38
-rw-r--r--yaksh/templates/yaksh/user_data.html8
-rw-r--r--yaksh/templates/yaksh/view_answerpaper.html7
-rw-r--r--yaksh/test_models.py128
-rw-r--r--yaksh/views.py32
16 files changed, 650 insertions, 65 deletions
diff --git a/yaksh/documentation/images/float_testcase.jpg b/yaksh/documentation/images/float_testcase.jpg
new file mode 100644
index 0000000..2b6827c
--- /dev/null
+++ b/yaksh/documentation/images/float_testcase.jpg
Binary files differ
diff --git a/yaksh/documentation/images/integer_testcase.jpg b/yaksh/documentation/images/integer_testcase.jpg
new file mode 100644
index 0000000..ca70a41
--- /dev/null
+++ b/yaksh/documentation/images/integer_testcase.jpg
Binary files differ
diff --git a/yaksh/documentation/images/string_testcase.jpg b/yaksh/documentation/images/string_testcase.jpg
new file mode 100644
index 0000000..7286eff
--- /dev/null
+++ b/yaksh/documentation/images/string_testcase.jpg
Binary files differ
diff --git a/yaksh/documentation/moderator_docs/creating_question.rst b/yaksh/documentation/moderator_docs/creating_question.rst
index 94bb95c..69bb635 100644
--- a/yaksh/documentation/moderator_docs/creating_question.rst
+++ b/yaksh/documentation/moderator_docs/creating_question.rst
@@ -251,6 +251,43 @@ How to write Test cases
.. image:: ../images/hook_testcase.jpg
:width: 80%
+ * **Create Integer Based Test Case**
+
+ Select **Answer in Integer** from Type field.
+
+ Select Integer from Add Test Case field.
+
+ In the Correct field, add the correct integer value for the question.
+
+ .. image:: ../images/integer_testcase.jpg
+ :width: 80%
+
+ * **Create String Based Test Case**
+
+ Select **Answer in String** from Type field.
+
+ Select **String** from Add Test Case field.
+
+ In the **Correct** field, add the exact string answer for the question.
+
+ In **String Check** field, select if the checking of the string answer
+ should be case sensitive or not.
+
+ .. image:: ../images/string_testcase.jpg
+ :width: 80%
+
+ * **Create Float Based Test Case**
+
+ Select **Answer in Float** from Type field.
+
+ Select **Float** from Add Test Case field.
+
+ In the **Correct** field, add the correct float value for the question.
+
+ In the **Error Margin** field, add the margin of error that will be allowed.
+
+ .. image:: ../images/float_testcase.jpg
+ :width: 80%
Features in Question
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..fb1c220
--- /dev/null
+++ b/yaksh/evaluator_tests/test_simple_question_types.py
@@ -0,0 +1,304 @@
+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 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.create(summary='int1', points=1,
+ type='code', user=self.user)
+ 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()
+
+ @classmethod
+ def tearDownClass(self):
+ self.question1.delete()
+
+ 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.create(summary='str1', points=1,
+ type='code', user=self.user)
+ 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.create(summary='str2', points=1,
+ type='code', user=self.user)
+ 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()
+
+ @classmethod
+ def tearDownClass(self):
+ self.question1.delete()
+ self.question2.delete()
+
+ 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.create(summary='flt1', points=1,
+ type='code', user=self.user)
+ 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()
+
+ @classmethod
+ def tearDownClass(self):
+ self.question1.delete()
+
+ 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/migrations/0003_auto_20170321_0917.py b/yaksh/migrations/0003_auto_20170321_0917.py
new file mode 100644
index 0000000..5a575c7
--- /dev/null
+++ b/yaksh/migrations/0003_auto_20170321_0917.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-03-21 09:17
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('yaksh', '0002_questionpaper_fixed_question_order'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='FloatTestCase',
+ fields=[
+ ('testcase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='yaksh.TestCase')),
+ ('correct', models.FloatField(default=None)),
+ ('error_margin', models.FloatField(blank=True, default=0.0, help_text='Margin of error', null=True)),
+ ],
+ bases=('yaksh.testcase',),
+ ),
+ migrations.CreateModel(
+ name='IntegerTestCase',
+ fields=[
+ ('testcase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='yaksh.TestCase')),
+ ('correct', models.IntegerField(default=None)),
+ ],
+ bases=('yaksh.testcase',),
+ ),
+ migrations.CreateModel(
+ name='StringTestCase',
+ fields=[
+ ('testcase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='yaksh.TestCase')),
+ ('correct', models.TextField(default=None)),
+ ('string_check', models.CharField(choices=[('lower', 'Case Insensitive'), ('exact', 'Case Sensitive')], max_length=200)),
+ ],
+ bases=('yaksh.testcase',),
+ ),
+ migrations.AlterField(
+ model_name='question',
+ name='type',
+ field=models.CharField(choices=[('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')], max_length=24),
+ ),
+ migrations.AlterField(
+ model_name='testcase',
+ name='type',
+ field=models.CharField(choices=[('standardtestcase', 'Standard Testcase'), ('stdiobasedtestcase', 'StdIO Based Testcase'), ('mcqtestcase', 'MCQ Testcase'), ('hooktestcase', 'Hook Testcase'), ('integertestcase', 'Integer Testcase'), ('stringtestcase', 'String Testcase'), ('floattestcase', 'Float Testcase')], max_length=24, null=True),
+ ),
+ ]
diff --git a/yaksh/models.py b/yaksh/models.py
index b14fcf6..9e05af0 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -38,10 +38,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 = (
@@ -54,6 +57,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)]
@@ -1171,6 +1182,7 @@ class AnswerPaper(models.Model):
if user_answer.strip() == expected_answer.strip():
result['success'] = True
result['error'] = ['Correct answer']
+
elif question.type == 'mcc':
expected_answers = []
for opt in question.get_test_cases(correct=True):
@@ -1178,6 +1190,39 @@ class AnswerPaper(models.Model):
if set(user_answer) == set(expected_answers):
result['success'] = True
result['error'] = ['Correct answer']
+
+ elif question.type == 'integer':
+ expected_answers = []
+ for tc in question.get_test_cases():
+ expected_answers.append(int(tc.correct))
+ if int(user_answer) in expected_answers:
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
+ elif question.type == 'string':
+ tc_status = []
+ for tc in question.get_test_cases():
+ if tc.string_check == "lower":
+ if tc.correct.lower().splitlines()\
+ == user_answer.lower().splitlines():
+ tc_status.append(True)
+ else:
+ if tc.correct.splitlines()\
+ == user_answer.splitlines():
+ tc_status.append(True)
+ if any(tc_status):
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
+ elif question.type == 'float':
+ tc_status = []
+ for tc in question.get_test_cases():
+ if abs(tc.correct - user_answer) <= tc.error_margin:
+ tc_status.append(True)
+ if any(tc_status):
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
elif question.type == 'code' or question.type == "upload":
user_dir = self.user.profile.get_user_dir()
json_result = code_server.run_code(
@@ -1326,3 +1371,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/yaksh/add_question.html b/yaksh/templates/yaksh/add_question.html
index 0d54ef7..a33950a 100644
--- a/yaksh/templates/yaksh/add_question.html
+++ b/yaksh/templates/yaksh/add_question.html
@@ -58,6 +58,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 d20695b..1cb1f99 100644
--- a/yaksh/templates/yaksh/grade_user.html
+++ b/yaksh/templates/yaksh/grade_user.html
@@ -144,6 +144,16 @@ 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>
+ {% if testcase.error_margin %}
+ <strong>{{ testcase.error_margin|safe }}</strong>
+ {% endif %}
+ {% 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 dc8a165..0dad59d 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -156,13 +156,25 @@ function call_skip(url)
<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" %}
+ (FILL IN THE BLANKS WITH INTEGER ANSWER)
+ {% elif question.type == "string" %}
+ (FILL IN THE BLANKS WITH STRING ANSWER)
+ {% if testcase.string_check == "lower" %}
+ <h5>(CASE INSENSITIVE)</h5>
+ {% else %}
+ <h5>(CASE SENSITIVE)</h5>
+ {% endif %}
+
+ {% elif question.type == "float" %}
+ (FILL IN THE BLANKS WITH FLOAT ANSWER)
{% endif %}
</u>
<font class=pull-right>(Marks : {{ question.points }}) </font>
@@ -181,6 +193,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:<br/>
+ <input name="answer" type="number" 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 :<br/>
+ <input name="answer" type="number" step="any" 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 }}
@@ -206,10 +237,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>&nbsp;&nbsp;
{% elif question.type == "upload" %}
<br><button class="btn btn-primary" type="submit" name="check" id="check" onClick="return validate();">Upload</button>&nbsp;&nbsp;
+
{% else %}
{% if question in paper.get_questions_unanswered %}
<button class="btn btn-primary" type="submit" name="check" id="check" onClick="submitCode();">Check Answer <span class="glyphicon glyphicon-cog"></span></button>&nbsp;&nbsp;
diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html
index 6679599..6e62b66 100644
--- a/yaksh/templates/yaksh/user_data.html
+++ b/yaksh/templates/yaksh/user_data.html
@@ -76,6 +76,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 f4edf67..4520ac3 100644
--- a/yaksh/templates/yaksh/view_answerpaper.html
+++ b/yaksh/templates/yaksh/view_answerpaper.html
@@ -55,6 +55,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 c732e58..f8f506a 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")
tmp_file1 = os.path.join(tempfile.gettempdir(), "test.txt")
@@ -66,13 +66,21 @@ 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:
+ dir_path = os.path.join(os.getcwd(), "yaksh", "data","question_{0}".format(que_id))
+ if os.path.exists(dir_path):
+ shutil.rmtree(dir_path)
###############################################################################
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"""
@@ -87,9 +95,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,
@@ -98,9 +106,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,
@@ -109,7 +116,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 = os.path.join(tempfile.gettempdir(), "test.txt")
@@ -167,7 +173,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')
@@ -209,8 +215,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')
@@ -228,10 +234,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):
@@ -242,7 +248,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)
@@ -278,7 +284,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)
@@ -293,7 +301,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)
@@ -324,7 +333,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,
@@ -371,7 +380,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,
@@ -385,7 +394,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]]
)
@@ -478,17 +487,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,
@@ -508,12 +517,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,
@@ -526,14 +535,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,
@@ -685,31 +697,32 @@ 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.add_completed_question(1)
+
+ question = self.answerpaper.add_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
+ current_question_id = self.question1.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, 2)
+ self.assertEqual(next_question_id.summary, "Question2")
# Given, wrong question id
current_question_id = 12
@@ -719,17 +732,19 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertTrue(next_question_id is not None)
- self.assertEqual(next_question_id.id, 1)
+
+ self.assertEqual(next_question_id.summary, "Question1")
# Given, last question in the list
- current_question_id = 3
+ current_question_id = self.question3.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, 1)
+
+ self.assertEqual(next_question_id.summary, "Question1")
# Test get_questions_answered() method
# When
@@ -749,14 +764,19 @@ class AnswerPaperTestCases(unittest.TestCase):
# Test completed_question and next_question
# When all questions are answered
- current_question = self.answerpaper.add_completed_question(2)
+
+ current_question = self.answerpaper.add_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.add_completed_question(3)
+ current_question = self.answerpaper.add_completed_question(
+ self.question3.id
+ )
# Then
self.assertEqual(self.answerpaper.questions_left(), 0)
@@ -816,12 +836,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):
@@ -898,21 +918,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 83749c1..bdb6f49 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
@@ -24,8 +25,9 @@ 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,\
+ FloatTestCase, StringTestCase
from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\
QuestionForm, RandomQuestionForm,\
QuestionFilterForm, CourseForm, ProfileForm, UploadFileForm,\
@@ -465,6 +467,24 @@ 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, notification=msg
+ )
+ elif current_question.type == 'float':
+ try:
+ user_answer = float(request.POST.get('answer'))
+ except ValueError:
+ msg = "Please enter a Float Value"
+ return show_question(request, current_question,
+ paper, notification=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':
@@ -502,9 +522,11 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
# 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, user) \
- if current_question.type == 'code' or \
- current_question.type == 'upload' else None
- result = paper.validate_answer(user_answer, current_question, json_data)
+ if current_question.type == 'code' or \
+ current_question.type == 'upload' else None
+ result = paper.validate_answer(user_answer, current_question,
+ json_data
+ )
if result.get('success'):
new_answer.marks = (current_question.points * result['weight'] /
current_question.get_maximum_test_case_weight()) \