From 6aeda97b5f37fc7ce50b7fd1f1e0465c42ba0969 Mon Sep 17 00:00:00 2001
From: mahesh
Date: Mon, 15 Jan 2018 17:40:06 +0530
Subject: Add shuffle testcases option to questions
---
yaksh/models.py | 51 ++++++++++++++++++++++++++++++---
yaksh/templates/yaksh/add_question.html | 1 +
yaksh/views.py | 2 +-
3 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/yaksh/models.py b/yaksh/models.py
index f065190..6ba4589 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -815,8 +815,14 @@ class Question(models.Model):
min_time = models.IntegerField("time in minutes", default=0)
+ #Solution for the question.
solution = models.TextField(blank=True)
+ # Shuffle testcase order.
+ shuffle_testcases = models.BooleanField("Shuffle testcase for each user",
+ default=False
+ )
+
def consolidate_answer_data(self, user_answer, user=None):
question_data = {}
metadata = {}
@@ -928,6 +934,17 @@ class Question(models.Model):
return test_case
+ def get_ordered_test_cases(self, answerpaper):
+ try:
+ order = TestcaseOrder.objects.get(answer_paper=answerpaper,
+ question = self
+ ).testcase_order.split(",")
+ return [self.get_test_cases(id=int(tc_id))[0]\
+ for tc_id in order
+ ]
+ except TestcaseOrder.DoesNotExist:
+ return self.get_test_cases()
+
def get_maximum_test_case_weight(self, **kwargs):
max_weight = 0.0
for test_case in self.get_test_cases():
@@ -1197,7 +1214,17 @@ class QuestionPaper(models.Model):
ans_paper.save()
questions = self._get_questions_for_answerpaper()
ans_paper.questions.add(*questions)
- question_ids = [str(que.id) for que in questions]
+ question_ids = []
+ for question in questions:
+ question_ids.append(str(question.id))
+ testcases = question.get_test_cases()
+ if question.shuffle_testcases:
+ random.shuffle(testcases)
+ testcases_ids = ",".join([str(tc.id) for tc in testcases])
+ testcases_order = TestcaseOrder.objects.create(
+ answer_paper=ans_paper,
+ question=question,
+ testcase_order=testcases_ids)
ans_paper.questions_order = ",".join(question_ids)
ans_paper.save()
ans_paper.questions_unanswered.add(*questions)
@@ -1829,7 +1856,7 @@ class AnswerPaper(models.Model):
.format(u.first_name, u.last_name, q.description)
-################################################################################
+##############################################################################
class AssignmentUploadManager(models.Manager):
def get_assignments(self, qp, que_id=None, user_id=None):
@@ -1851,7 +1878,7 @@ class AssignmentUploadManager(models.Manager):
return assignment_files, file_name
-################################################################################
+##############################################################################
class AssignmentUpload(models.Model):
user = models.ForeignKey(User)
assignmentQuestion = models.ForeignKey(Question)
@@ -1860,7 +1887,7 @@ class AssignmentUpload(models.Model):
objects = AssignmentUploadManager()
-###############################################################################
+##############################################################################
class TestCase(models.Model):
question = models.ForeignKey(Question, blank=True, null=True)
type = models.CharField(max_length=24, choices=test_case_types, null=True)
@@ -1978,3 +2005,19 @@ class FloatTestCase(TestCase):
return u'Testcase | Correct: {0} | Error Margin: +or- {1}'.format(
self.correct, self.error_margin
)
+
+
+##############################################################################
+class TestcaseOrder(models.Model):
+ """Testcase order contains a set of ordered test cases for a given question
+ for each user.
+ """
+
+ # Answerpaper of the user.
+ answer_paper= models.ForeignKey(AnswerPaper,related_name="answer_paper")
+
+ # Question in an answerpaper.
+ question = models.ForeignKey(Question)
+
+ #Order of the test case for a question.
+ testcase_order = models.TextField()
diff --git a/yaksh/templates/yaksh/add_question.html b/yaksh/templates/yaksh/add_question.html
index ed69657..b02487c 100644
--- a/yaksh/templates/yaksh/add_question.html
+++ b/yaksh/templates/yaksh/add_question.html
@@ -29,6 +29,7 @@
Snippet: | {{ qform.snippet }}
|
Minimum Time(in minutes): | {{ qform.min_time }}
|
Partial Grading: | {{ qform.partial_grading }}
+ |
Shuffle Testcases: | {{ qform.shuffle_testcases }}
|
Grade Assignment Upload: | {{ qform.grade_assignment_upload }}
|
File: | {{ fileform.file_field }}{{ fileform.file_field.errors }}
{% if uploaded_files %} Uploaded files: Check on delete to delete files,
diff --git a/yaksh/views.py b/yaksh/views.py
index 011b417..17cfb13 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -616,7 +616,7 @@ def show_question(request, question, paper, error_message=None, notification=Non
if question.type == "code" else
'You have already attempted this question'
)
- test_cases = question.get_test_cases()
+ test_cases = question.get_ordered_test_cases(paper)
files = FileUpload.objects.filter(question_id=question.id, hide=False)
course = Course.objects.get(id=course_id)
module = course.learning_module.get(id=module_id)
--
cgit
From 98775e565c99fd6486fb5908a5ca9be491db9b1c Mon Sep 17 00:00:00 2001
From: mahesh
Date: Tue, 16 Jan 2018 01:13:49 +0530
Subject: Add test case order in view_answerpaper
---
yaksh/templates/yaksh/view_answerpaper.html | 5 +++--
yaksh/templatetags/custom_filters.py | 5 +++++
yaksh/views.py | 5 ++++-
3 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/yaksh/templates/yaksh/view_answerpaper.html b/yaksh/templates/yaksh/view_answerpaper.html
index 410b578..971ef77 100644
--- a/yaksh/templates/yaksh/view_answerpaper.html
+++ b/yaksh/templates/yaksh/view_answerpaper.html
@@ -34,7 +34,7 @@
Start time: {{ paper.start_time }}
End time : {{ paper.end_time }}
Percentage obtained: {{ paper.percent }}%
- {% if paper.passed == 0 %}
+ {% if paper.passed %}
Status : Failed
{% else %}
Status : Passed
@@ -55,7 +55,8 @@
Question: {{ question.description|safe }}
{% if question.type == "mcq" or question.type == "mcc" %}
Choices:
- {% for testcase in question.get_test_cases %}
+ {% get_ordered_testcases question paper as testcases %}
+ {% for testcase in testcases %}
{% if testcase.correct %}
{{ forloop.counter }}. {{ testcase.options|safe }}
diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py
index 3c2c6fd..fa0802f 100644
--- a/yaksh/templatetags/custom_filters.py
+++ b/yaksh/templatetags/custom_filters.py
@@ -62,3 +62,8 @@ def module_completion_percent(course, module, user):
@register.simple_tag
def course_completion_percent(course, user):
return course.percent_completed(user)
+
+
+@register.simple_tag
+def get_ordered_testcases(question, answerpaper):
+ return question.get_ordered_test_cases(answerpaper)
\ No newline at end of file
diff --git a/yaksh/views.py b/yaksh/views.py
index 17cfb13..27a07d2 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -616,7 +616,10 @@ def show_question(request, question, paper, error_message=None, notification=Non
if question.type == "code" else
'You have already attempted this question'
)
- test_cases = question.get_ordered_test_cases(paper)
+ if question.type in ['mcc', 'mcq']:
+ test_cases = question.get_ordered_test_cases(paper)
+ else:
+ test_cases = question.get_test_cases()
files = FileUpload.objects.filter(question_id=question.id, hide=False)
course = Course.objects.get(id=course_id)
module = course.learning_module.get(id=module_id)
--
cgit
From 03e74a964504e98cb0f01678ce12a626581a406b Mon Sep 17 00:00:00 2001
From: mahesh
Date: Fri, 19 Jan 2018 12:44:57 +0530
Subject: Add test cases for shuffle testcase
---
.../evaluator_tests/test_simple_question_types.py | 203 ++++++++++++++++++---
yaksh/test_models.py | 3 +-
2 files changed, 178 insertions(+), 28 deletions(-)
diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py
index b86a9d8..3fe27d4 100644
--- a/yaksh/evaluator_tests/test_simple_question_types.py
+++ b/yaksh/evaluator_tests/test_simple_question_types.py
@@ -4,49 +4,56 @@ from django.utils import timezone
import pytz
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, Course, IntegerTestCase, FloatTestCase,\
- StringTestCase
+ StringTestCase, McqTestCase
def setUpModule():
- # create user profile
+ # Create user profile
+ # Create User 1
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 User 2
+ user2 = User.objects.create_user(username='demo_user_101',
+ password='demo',
+ email='demo@test.com')
- # create a course
+ Profile.objects.create(user=user2, roll_number=2,
+ 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),
+ 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,
+ time_between_attempts=0, pass_criteria=0,
+ description='demo quiz 100',
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,
- course=course
- )
-
+
def tearDownModule():
User.objects.get(username="demo_user_100").delete()
+ User.objects.get(username="demo_user_101").delete()
class IntegerQuestionTestCases(unittest.TestCase):
@classmethod
def setUpClass(self):
+ # Creating Course
+ self.course = Course.objects.get(name="Python Course 100")
# Creating Quiz
self.quiz = Quiz.objects.get(description="demo quiz 100")
# Creating Question paper
@@ -65,9 +72,16 @@ class IntegerQuestionTestCases(unittest.TestCase):
self.question1.save()
#Creating answerpaper
- self.answerpaper = AnswerPaper.objects.get(question_paper\
- =self.question_paper)
- self.answerpaper.attempt_number = 1
+
+ self.answerpaper = AnswerPaper.objects.create(user=self.user,
+ user_ip='101.0.0.1',
+ start_time=timezone.now(),
+ question_paper=self.question_paper,
+ end_time=timezone.now()
+ +timedelta(minutes=5),
+ attempt_number=1,
+ course=self.course
+ )
self.answerpaper.questions.add(self.question1)
self.answerpaper.save()
# For question
@@ -80,6 +94,7 @@ class IntegerQuestionTestCases(unittest.TestCase):
@classmethod
def tearDownClass(self):
self.question1.delete()
+ self.answerpaper.delete()
def test_validate_regrade_integer_correct_answer(self):
# Given
@@ -158,6 +173,8 @@ class IntegerQuestionTestCases(unittest.TestCase):
class StringQuestionTestCases(unittest.TestCase):
@classmethod
def setUpClass(self):
+ # Creating Course
+ self.course = Course.objects.get(name="Python Course 100")
# Creating Quiz
self.quiz = Quiz.objects.get(description="demo quiz 100")
# Creating Question paper
@@ -182,9 +199,16 @@ class StringQuestionTestCases(unittest.TestCase):
self.question2.save()
#Creating answerpaper
- self.answerpaper = AnswerPaper.objects.get(question_paper\
- =self.question_paper)
- self.answerpaper.attempt_number = 1
+
+ self.answerpaper = AnswerPaper.objects.create(user=self.user,
+ user_ip='101.0.0.1',
+ start_time=timezone.now(),
+ question_paper=self.question_paper,
+ end_time=timezone.now()
+ +timedelta(minutes=5),
+ attempt_number=1,
+ course=self.course
+ )
self.answerpaper.questions.add(*[self.question1, self.question2])
self.answerpaper.save()
@@ -207,6 +231,7 @@ class StringQuestionTestCases(unittest.TestCase):
def tearDownClass(self):
self.question1.delete()
self.question2.delete()
+ self.answerpaper.delete()
def test_validate_regrade_case_insensitive_string_correct_answer(self):
# Given
@@ -346,6 +371,8 @@ class StringQuestionTestCases(unittest.TestCase):
class FloatQuestionTestCases(unittest.TestCase):
@classmethod
def setUpClass(self):
+ # Creating Course
+ self.course = Course.objects.get(name="Python Course 100")
# Creating Quiz
self.quiz = Quiz.objects.get(description="demo quiz 100")
# Creating Question paper
@@ -362,9 +389,16 @@ class FloatQuestionTestCases(unittest.TestCase):
self.question1.save()
#Creating answerpaper
- self.answerpaper = AnswerPaper.objects.get(question_paper\
- =self.question_paper)
- self.answerpaper.attempt_number = 1
+
+ self.answerpaper = AnswerPaper.objects.create(user=self.user,
+ user_ip='101.0.0.1',
+ start_time=timezone.now(),
+ question_paper=self.question_paper,
+ end_time=timezone.now()
+ +timedelta(minutes=5),
+ attempt_number=1,
+ course=self.course
+ )
self.answerpaper.questions.add(self.question1)
self.answerpaper.save()
# For question
@@ -378,6 +412,7 @@ class FloatQuestionTestCases(unittest.TestCase):
@classmethod
def tearDownClass(self):
self.question1.delete()
+ self.answerpaper.delete()
def test_validate_regrade_float_correct_answer(self):
# Given
@@ -450,3 +485,117 @@ class FloatQuestionTestCases(unittest.TestCase):
self.assertTrue(details[0])
self.assertEqual(self.answer.marks, 1)
self.assertTrue(self.answer.correct)
+
+class MCQQuestionTestCases(unittest.TestCase):
+ @classmethod
+ def setUpClass(self):
+ #Creating User
+ self.user = User.objects.get(username='demo_user_100')
+ self.user2 = User.objects.get(username='demo_user_101')
+ self.user_ip = '127.0.0.1'
+
+ #Creating Course
+ self.course = Course.objects.get(name="Python Course 100")
+ # Creating Quiz
+ self.quiz = Quiz.objects.get(description="demo quiz 100")
+ # Creating Question paper
+ self.question_paper = QuestionPaper.objects.get(quiz=self.quiz)
+ #Creating Question
+ self.question1 = Question.objects.create(summary='mcq1', points=1,
+ type='code', user=self.user,
+ )
+ self.question1.language = 'python'
+ self.question1.type = "mcq"
+ self.question1.test_case_type = 'Mcqtestcase'
+ self.question1.description = 'Which option is Correct?'
+ self.question1.shuffle_testcases = True
+ self.question1.save()
+
+ # For questions
+ self.mcq_based_testcase_1 = McqTestCase(question=self.question1,
+ options="Correct",
+ correct=True,
+ type='mcqtestcase',
+ )
+ self.mcq_based_testcase_1.save()
+
+ self.mcq_based_testcase_2 = McqTestCase(question=self.question1,
+ options="Incorrect",
+ correct=False,
+ type='mcqtestcase',
+ )
+ self.mcq_based_testcase_2.save()
+
+ self.mcq_based_testcase_3 = McqTestCase(question=self.question1,
+ options="Incorrect",
+ correct=False,
+ type='mcqtestcase',
+ )
+ self.mcq_based_testcase_3.save()
+
+ self.mcq_based_testcase_4 = McqTestCase(question=self.question1,
+ options="Incorrect",
+ correct=False,
+ type='mcqtestcase',
+ )
+ self.mcq_based_testcase_4.save()
+
+ self.question_paper.fixed_questions.add(self.question1)
+
+ self.answerpaper = self.question_paper.make_answerpaper(
+ user=self.user, ip=self.user_ip,
+ attempt_num=1,
+ course_id=self.course.id
+ )
+
+ # Answerpaper for user 2
+ self.answerpaper2 = self.question_paper.make_answerpaper(
+ user=self.user2, ip=self.user_ip,
+ attempt_num=1,
+ course_id=self.course.id
+ )
+
+ @classmethod
+ def tearDownClass(self):
+ self.question1.delete()
+
+ def test_shuffle_test_cases(self):
+ # Given
+ # Answerpaper for user 1
+ # When
+ user_testcase = self.question1.get_ordered_test_cases(
+ self.answerpaper
+ )
+ order1 = [tc.id for tc in user_testcase]
+ user2_testcase = self.question1.get_ordered_test_cases(
+ self.answerpaper2
+ )
+ order2 = [tc.id for tc in user2_testcase]
+
+ # Then
+ self.assertNotEqual(order1, order2)
+
+ # def test_not_shuffle_test_cases(self):
+ # # Given
+ # self.question1.shuffle_testcases = False
+ # self.question1.save()
+ # answerpaper = self.question_paper.make_answerpaper(
+ # user=self.user, ip=self.user_ip,
+ # attempt_num=1,
+ # course_id=self.course.id
+ # )
+
+ # # Answerpaper for user 2
+ # answerpaper2 = self.question_paper.make_answerpaper(
+ # user=self.user2, ip=self.user_ip,
+ # attempt_num=1,
+ # course_id=self.course.id
+ # )
+
+ # user_testcase = self.question1.get_ordered_test_cases(answerpaper)
+ # order1 = [tc.id for tc in user_testcase]
+
+ # user2_testcase = self.question1.get_ordered_test_cases(answerpaper2)
+ # order2 = [tc.id for tc in user2_testcase]
+ # print(order2, order1)
+ # self.assertNotEqual(order1, order2)
\ No newline at end of file
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index cd4279b..381a2f3 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -113,6 +113,7 @@ def tearDownModule():
Lesson.objects.all().delete()
LearningUnit.objects.all().delete()
LearningModule.objects.all().delete()
+ AnswerPaper.objects.all().delete()
###############################################################################
@@ -848,7 +849,7 @@ class AnswerPaperTestCases(unittest.TestCase):
)
self.qtn_paper_with_single_question.save()
- all_questions = Question.objects.all()
+ all_questions = Question.objects.filter(user=self.user)
self.questions = all_questions[0:3]
self.start_time = timezone.now()
self.end_time = self.start_time + timedelta(minutes=20)
--
cgit
From 1f06442fba1420e03f54604bffe8029bb098169c Mon Sep 17 00:00:00 2001
From: mahesh
Date: Fri, 19 Jan 2018 13:13:25 +0530
Subject: Disable/enable shuffle testcase option based on question type
---
yaksh/static/yaksh/js/add_question.js | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/yaksh/static/yaksh/js/add_question.js b/yaksh/static/yaksh/js/add_question.js
index 346991a..579904b 100644
--- a/yaksh/static/yaksh/js/add_question.js
+++ b/yaksh/static/yaksh/js/add_question.js
@@ -126,8 +126,9 @@ function textareaformat()
document.getElementById('my').innerHTML = document.getElementById('id_description').value ;
document.getElementById('rend_solution').innerHTML = document.getElementById('id_solution').value ;
+ var question_type = document.getElementById('id_type').value
if (document.getElementById('id_grade_assignment_upload').checked ||
- document.getElementById('id_type').value == 'upload'){
+ question_type == 'upload'){
$("#id_grade_assignment_upload").prop("disabled", false);
}
else{
@@ -142,6 +143,23 @@ function textareaformat()
$("#id_grade_assignment_upload").prop("disabled", true);
}
});
+
+ if (document.getElementById('id_shuffle_testcases').checked ||
+ question_type == "mcc" || question_type == "mcq"){
+ $("#id_shuffle_testcases").prop("disabled", false);
+ }
+ else{
+ $("#id_shuffle_testcases").prop("disabled", true);
+ }
+
+ $('#id_type').change(function() {
+ if ($(this).val() == "mcc" || $(this).val() == "mcq"){
+ $("#id_shuffle_testcases").prop("disabled", false);
+ }
+ else{
+ $("#id_shuffle_testcases").prop("disabled", true);
+ }
+ });
}
function autosubmit()
--
cgit
From 51c8184a795942c9f85b14a8914d03ee788e5639 Mon Sep 17 00:00:00 2001
From: mahesh
Date: Thu, 1 Feb 2018 15:05:47 +0530
Subject: Change TestcaseOrder to TestCaseOrder
---
.../evaluator_tests/test_simple_question_types.py | 25 -----------
yaksh/models.py | 16 ++++----
yaksh/test_models.py | 48 +++++++++++++---------
3 files changed, 37 insertions(+), 52 deletions(-)
diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py
index 3fe27d4..d2996e4 100644
--- a/yaksh/evaluator_tests/test_simple_question_types.py
+++ b/yaksh/evaluator_tests/test_simple_question_types.py
@@ -574,28 +574,3 @@ class MCQQuestionTestCases(unittest.TestCase):
# Then
self.assertNotEqual(order1, order2)
-
- # def test_not_shuffle_test_cases(self):
- # # Given
- # self.question1.shuffle_testcases = False
- # self.question1.save()
- # answerpaper = self.question_paper.make_answerpaper(
- # user=self.user, ip=self.user_ip,
- # attempt_num=1,
- # course_id=self.course.id
- # )
-
- # # Answerpaper for user 2
- # answerpaper2 = self.question_paper.make_answerpaper(
- # user=self.user2, ip=self.user_ip,
- # attempt_num=1,
- # course_id=self.course.id
- # )
-
- # user_testcase = self.question1.get_ordered_test_cases(answerpaper)
- # order1 = [tc.id for tc in user_testcase]
-
- # user2_testcase = self.question1.get_ordered_test_cases(answerpaper2)
- # order2 = [tc.id for tc in user2_testcase]
- # print(order2, order1)
- # self.assertNotEqual(order1, order2)
\ No newline at end of file
diff --git a/yaksh/models.py b/yaksh/models.py
index 6ba4589..f823cda 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -936,13 +936,13 @@ class Question(models.Model):
def get_ordered_test_cases(self, answerpaper):
try:
- order = TestcaseOrder.objects.get(answer_paper=answerpaper,
+ order = TestCaseOrder.objects.get(answer_paper=answerpaper,
question = self
- ).testcase_order.split(",")
+ ).order.split(",")
return [self.get_test_cases(id=int(tc_id))[0]\
for tc_id in order
]
- except TestcaseOrder.DoesNotExist:
+ except TestCaseOrder.DoesNotExist:
return self.get_test_cases()
def get_maximum_test_case_weight(self, **kwargs):
@@ -1221,10 +1221,10 @@ class QuestionPaper(models.Model):
if question.shuffle_testcases:
random.shuffle(testcases)
testcases_ids = ",".join([str(tc.id) for tc in testcases])
- testcases_order = TestcaseOrder.objects.create(
+ testcases_order = TestCaseOrder.objects.create(
answer_paper=ans_paper,
question=question,
- testcase_order=testcases_ids)
+ order=testcases_ids)
ans_paper.questions_order = ",".join(question_ids)
ans_paper.save()
ans_paper.questions_unanswered.add(*questions)
@@ -2008,16 +2008,16 @@ class FloatTestCase(TestCase):
##############################################################################
-class TestcaseOrder(models.Model):
+class TestCaseOrder(models.Model):
"""Testcase order contains a set of ordered test cases for a given question
for each user.
"""
# Answerpaper of the user.
- answer_paper= models.ForeignKey(AnswerPaper,related_name="answer_paper")
+ answer_paper = models.ForeignKey(AnswerPaper, related_name="answer_paper")
# Question in an answerpaper.
question = models.ForeignKey(Question)
#Order of the test case for a question.
- testcase_order = models.TextField()
+ order = models.TextField()
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 381a2f3..a0ccd49 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -626,8 +626,9 @@ class QuestionPaperTestCases(unittest.TestCase):
@classmethod
def setUpClass(self):
self.course = Course.objects.get(name="Python Course")
+ self.user= User.objects.get(username='creator')
# All active questions
- self.questions = Question.objects.filter(active=True)
+ self.questions = Question.objects.filter(active=True, user=self.user)
self.quiz = Quiz.objects.get(description="demo quiz 1")
# create question paper with only fixed questions
@@ -849,7 +850,7 @@ class AnswerPaperTestCases(unittest.TestCase):
)
self.qtn_paper_with_single_question.save()
- all_questions = Question.objects.filter(user=self.user)
+ all_questions = Question.objects.filter(user=self.user).order_by("id")
self.questions = all_questions[0:3]
self.start_time = timezone.now()
self.end_time = self.start_time + timedelta(minutes=20)
@@ -875,6 +876,9 @@ class AnswerPaperTestCases(unittest.TestCase):
self.answerpaper.attempt_number = already_attempted + 1
self.answerpaper.save()
self.answerpaper.questions.add(*self.questions)
+ self.answerpaper.questions_order = ",".join(
+ [str(q.id) for q in self.questions]
+ )
self.answerpaper.questions_unanswered.add(*self.questions)
self.answerpaper.save()
# answers for the Answer Paper
@@ -929,17 +933,17 @@ class AnswerPaperTestCases(unittest.TestCase):
self.question1.language = 'python'
self.question1.test_case_type = 'standardtestcase'
- self.question1.summary = "Question1"
+ self.question1.summary = "Q1"
self.question1.save()
self.question2.language = 'python'
self.question2.type = 'mcq'
self.question2.test_case_type = 'mcqtestcase'
- self.question2.summary = "Question2"
+ self.question2.summary = "Q2"
self.question2.save()
self.question3.language = 'python'
self.question3.type = 'mcc'
self.question3.test_case_type = 'mcqtestcase'
- self.question3.summary = "Question3"
+ self.question3.summary = "Q3"
self.question3.save()
self.assertion_testcase = StandardTestCase(
question=self.question1,
@@ -1098,7 +1102,8 @@ class AnswerPaperTestCases(unittest.TestCase):
details = self.answerpaper.regrade(self.question3.id)
# Then
- self.answer = self.answerpaper.answers.filter(question=self.question3).last()
+ self.answer = self.answerpaper.answers.filter(
+ question=self.question3).last()
self.assertTrue(details[0])
self.assertEqual(self.answer.marks, 0)
self.assertFalse(self.answer.correct)
@@ -1238,9 +1243,9 @@ class AnswerPaperTestCases(unittest.TestCase):
""" Test Answer Paper"""
self.assertEqual(self.answerpaper.user.username, 'creator')
self.assertEqual(self.answerpaper.user_ip, self.ip)
- questions = self.answerpaper.get_questions()
+ questions = [q.id for q in self.answerpaper.get_questions()]
num_questions = len(questions)
- self.assertSequenceEqual(list(questions), list(self.questions))
+ self.assertEqual(set(questions), set([q.id for q in self.questions]))
self.assertEqual(num_questions, 3)
self.assertEqual(self.answerpaper.question_paper, self.question_paper)
self.assertEqual(self.answerpaper.start_time, self.start_time)
@@ -1251,7 +1256,7 @@ 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.summary, "Question1")
+ self.assertEqual(current_question.summary, "Q1")
# Test completed_question() method of Answer Paper
question = self.answerpaper.add_completed_question(self.question1.id)
@@ -1260,14 +1265,14 @@ class AnswerPaperTestCases(unittest.TestCase):
# Test next_question() method of Answer Paper
current_question = self.answerpaper.current_question()
- self.assertEqual(current_question.summary, "Question2")
+ self.assertEqual(current_question.summary, "Q2")
# 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.summary, "Question3")
+ self.assertEqual(next_question_id.summary, "Q3")
# Given, here question is already answered
current_question_id = self.question1.id
@@ -1277,7 +1282,7 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertTrue(next_question_id is not None)
- self.assertEqual(next_question_id.summary, "Question2")
+ self.assertEqual(next_question_id.summary, "Q2")
# Given, wrong question id
current_question_id = 12
@@ -1287,7 +1292,7 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertTrue(next_question_id is not None)
- self.assertEqual(next_question_id.summary, "Question1")
+ self.assertEqual(next_question_id.summary, "Q1")
# Given, last question in the list
current_question_id = self.question3.id
@@ -1298,7 +1303,7 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertTrue(next_question_id is not None)
- self.assertEqual(next_question_id.summary, "Question1")
+ self.assertEqual(next_question_id.summary, "Q1")
# Test get_questions_answered() method
# When
@@ -1313,8 +1318,11 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertEqual(questions_unanswered.count(), 2)
- self.assertSequenceEqual(questions_unanswered,
- [self.questions[1], self.questions[2]])
+ self.assertEqual(set([q.id for q in questions_unanswered]),
+ set([self.questions[1].id,
+ self.questions[2].id]
+ )
+ )
# Test completed_question and next_question
# When all questions are answered
@@ -1326,7 +1334,7 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertEqual(self.answerpaper.questions_left(), 1)
self.assertIsNotNone(current_question)
- self.assertEqual(current_question.summary, "Question3")
+ self.assertEqual(current_question.summary, "Q3")
# When
current_question = self.answerpaper.add_completed_question(
@@ -1336,7 +1344,7 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertEqual(self.answerpaper.questions_left(), 0)
self.assertIsNotNone(current_question)
- self.assertTrue(current_question == self.answerpaper.questions.all()[0])
+ self.assertTrue(current_question == self.answerpaper.get_all_ordered_questions()[0])
# When
next_question_id = self.answerpaper.next_question(current_question_id)
@@ -1427,7 +1435,9 @@ class CourseTestCases(unittest.TestCase):
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')
- self.questions = Question.objects.filter(active=True)
+ self.questions = Question.objects.filter(active=True,
+ user=self.creator
+ )
self.modules = LearningModule.objects.filter(creator=self.creator)
# create courses with disabled enrollment
--
cgit
From 337daeef8954a1be20164c5fb27050e67597b8a2 Mon Sep 17 00:00:00 2001
From: mahesh
Date: Thu, 8 Feb 2018 15:19:27 +0530
Subject: Improve test cases for TestCaseOrder model
---
yaksh/evaluator_tests/test_simple_question_types.py | 15 ++++++++++++++-
yaksh/models.py | 2 +-
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py
index d2996e4..2d991a7 100644
--- a/yaksh/evaluator_tests/test_simple_question_types.py
+++ b/yaksh/evaluator_tests/test_simple_question_types.py
@@ -554,6 +554,15 @@ class MCQQuestionTestCases(unittest.TestCase):
attempt_num=1,
course_id=self.course.id
)
+ self.answerpaper3 = AnswerPaper.objects.create(
+ user=self.user,
+ question_paper=self.question_paper,
+ course=self.course,
+ attempt_number=self.answerpaper.attempt_number+1,
+ start_time=timezone.now(),
+ end_time=timezone.now()+timedelta(minutes=5),
+ user_ip="127.0.0.1"
+ )
@classmethod
def tearDownClass(self):
@@ -571,6 +580,10 @@ class MCQQuestionTestCases(unittest.TestCase):
self.answerpaper2
)
order2 = [tc.id for tc in user2_testcase]
-
+ not_ordered_testcase = self.question1.get_ordered_test_cases(
+ self.answerpaper3
+ )
+ get_test_cases = self.question1.get_test_cases()
# Then
self.assertNotEqual(order1, order2)
+ self.assertEqual(get_test_cases, not_ordered_testcase)
diff --git a/yaksh/models.py b/yaksh/models.py
index f823cda..0bb1e66 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -939,7 +939,7 @@ class Question(models.Model):
order = TestCaseOrder.objects.get(answer_paper=answerpaper,
question = self
).order.split(",")
- return [self.get_test_cases(id=int(tc_id))[0]\
+ return [self.get_test_case(id=int(tc_id))
for tc_id in order
]
except TestCaseOrder.DoesNotExist:
--
cgit
From f531b9ca9c088263a53aff8574e93b74945b468f Mon Sep 17 00:00:00 2001
From: mahesh
Date: Fri, 16 Feb 2018 16:30:55 +0530
Subject: Add shuffle_testcases to QuestionPaper model instead of Question
---
.../evaluator_tests/test_simple_question_types.py | 38 +++++++++++-----------
yaksh/forms.py | 2 +-
yaksh/models.py | 12 ++++---
yaksh/static/yaksh/js/add_question.js | 17 ----------
yaksh/templates/yaksh/add_question.html | 1 -
yaksh/templates/yaksh/design_questionpaper.html | 8 +++--
6 files changed, 33 insertions(+), 45 deletions(-)
diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py
index 2d991a7..cbf2abd 100644
--- a/yaksh/evaluator_tests/test_simple_question_types.py
+++ b/yaksh/evaluator_tests/test_simple_question_types.py
@@ -485,7 +485,6 @@ class FloatQuestionTestCases(unittest.TestCase):
self.assertTrue(details[0])
self.assertEqual(self.answer.marks, 1)
self.assertTrue(self.answer.correct)
-
class MCQQuestionTestCases(unittest.TestCase):
@classmethod
def setUpClass(self):
@@ -493,13 +492,15 @@ class MCQQuestionTestCases(unittest.TestCase):
self.user = User.objects.get(username='demo_user_100')
self.user2 = User.objects.get(username='demo_user_101')
self.user_ip = '127.0.0.1'
-
+
#Creating Course
self.course = Course.objects.get(name="Python Course 100")
# Creating Quiz
self.quiz = Quiz.objects.get(description="demo quiz 100")
# Creating Question paper
self.question_paper = QuestionPaper.objects.get(quiz=self.quiz)
+ self.question_paper.shuffle_testcases = True
+ self.question_paper.save()
#Creating Question
self.question1 = Question.objects.create(summary='mcq1', points=1,
type='code', user=self.user,
@@ -508,7 +509,6 @@ class MCQQuestionTestCases(unittest.TestCase):
self.question1.type = "mcq"
self.question1.test_case_type = 'Mcqtestcase'
self.question1.description = 'Which option is Correct?'
- self.question1.shuffle_testcases = True
self.question1.save()
# For questions
@@ -518,21 +518,21 @@ class MCQQuestionTestCases(unittest.TestCase):
type='mcqtestcase',
)
self.mcq_based_testcase_1.save()
-
+
self.mcq_based_testcase_2 = McqTestCase(question=self.question1,
options="Incorrect",
correct=False,
type='mcqtestcase',
)
self.mcq_based_testcase_2.save()
-
+
self.mcq_based_testcase_3 = McqTestCase(question=self.question1,
options="Incorrect",
correct=False,
type='mcqtestcase',
)
self.mcq_based_testcase_3.save()
-
+
self.mcq_based_testcase_4 = McqTestCase(question=self.question1,
options="Incorrect",
correct=False,
@@ -544,7 +544,7 @@ class MCQQuestionTestCases(unittest.TestCase):
self.answerpaper = self.question_paper.make_answerpaper(
user=self.user, ip=self.user_ip,
- attempt_num=1,
+ attempt_num=1,
course_id=self.course.id
)
@@ -554,24 +554,16 @@ class MCQQuestionTestCases(unittest.TestCase):
attempt_num=1,
course_id=self.course.id
)
- self.answerpaper3 = AnswerPaper.objects.create(
- user=self.user,
- question_paper=self.question_paper,
- course=self.course,
- attempt_number=self.answerpaper.attempt_number+1,
- start_time=timezone.now(),
- end_time=timezone.now()+timedelta(minutes=5),
- user_ip="127.0.0.1"
- )
-
@classmethod
def tearDownClass(self):
self.question1.delete()
+ self.answerpaper.delete()
+ self.answerpaper2.delete()
def test_shuffle_test_cases(self):
# Given
- # Answerpaper for user 1
# When
+
user_testcase = self.question1.get_ordered_test_cases(
self.answerpaper
)
@@ -580,10 +572,18 @@ class MCQQuestionTestCases(unittest.TestCase):
self.answerpaper2
)
order2 = [tc.id for tc in user2_testcase]
+ self.question_paper.shuffle_testcases = False
+ self.question_paper.save()
+ answerpaper3 = self.question_paper.make_answerpaper(
+ user=self.user2, ip=self.user_ip,
+ attempt_num=self.answerpaper.attempt_number+1,
+ course_id=self.course.id
+ )
not_ordered_testcase = self.question1.get_ordered_test_cases(
- self.answerpaper3
+ answerpaper3
)
get_test_cases = self.question1.get_test_cases()
# Then
self.assertNotEqual(order1, order2)
self.assertEqual(get_test_cases, not_ordered_testcase)
+ answerpaper3.delete()
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 258a1ee..e53eda3 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -308,7 +308,7 @@ class UploadFileForm(forms.Form):
class QuestionPaperForm(forms.ModelForm):
class Meta:
model = QuestionPaper
- fields = ['shuffle_questions']
+ fields = ['shuffle_questions', 'shuffle_testcases']
class LessonForm(forms.ModelForm):
diff --git a/yaksh/models.py b/yaksh/models.py
index 0bb1e66..8e0bb4d 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -818,10 +818,6 @@ class Question(models.Model):
#Solution for the question.
solution = models.TextField(blank=True)
- # Shuffle testcase order.
- shuffle_testcases = models.BooleanField("Shuffle testcase for each user",
- default=False
- )
def consolidate_answer_data(self, user_answer, user=None):
question_data = {}
@@ -1151,6 +1147,11 @@ class QuestionPaper(models.Model):
# Sequence or Order of fixed questions
fixed_question_order = models.CharField(max_length=255, blank=True)
+ # Shuffle testcase order.
+ shuffle_testcases = models.BooleanField("Shuffle testcase for each user",
+ default=True
+ )
+
objects = QuestionPaperManager()
def get_question_bank(self):
@@ -1218,7 +1219,8 @@ class QuestionPaper(models.Model):
for question in questions:
question_ids.append(str(question.id))
testcases = question.get_test_cases()
- if question.shuffle_testcases:
+ if self.shuffle_testcases and \
+ question.type in ["mcq", "mcc"]:
random.shuffle(testcases)
testcases_ids = ",".join([str(tc.id) for tc in testcases])
testcases_order = TestCaseOrder.objects.create(
diff --git a/yaksh/static/yaksh/js/add_question.js b/yaksh/static/yaksh/js/add_question.js
index 579904b..0f02aab 100644
--- a/yaksh/static/yaksh/js/add_question.js
+++ b/yaksh/static/yaksh/js/add_question.js
@@ -143,23 +143,6 @@ function textareaformat()
$("#id_grade_assignment_upload").prop("disabled", true);
}
});
-
- if (document.getElementById('id_shuffle_testcases').checked ||
- question_type == "mcc" || question_type == "mcq"){
- $("#id_shuffle_testcases").prop("disabled", false);
- }
- else{
- $("#id_shuffle_testcases").prop("disabled", true);
- }
-
- $('#id_type').change(function() {
- if ($(this).val() == "mcc" || $(this).val() == "mcq"){
- $("#id_shuffle_testcases").prop("disabled", false);
- }
- else{
- $("#id_shuffle_testcases").prop("disabled", true);
- }
- });
}
function autosubmit()
diff --git a/yaksh/templates/yaksh/add_question.html b/yaksh/templates/yaksh/add_question.html
index b02487c..ed69657 100644
--- a/yaksh/templates/yaksh/add_question.html
+++ b/yaksh/templates/yaksh/add_question.html
@@ -29,7 +29,6 @@
|
Snippet: | {{ qform.snippet }}
|
Minimum Time(in minutes): | {{ qform.min_time }}
|
Partial Grading: | {{ qform.partial_grading }}
- |
Shuffle Testcases: | {{ qform.shuffle_testcases }}
|
Grade Assignment Upload: | {{ qform.grade_assignment_upload }}
|
File: | {{ fileform.file_field }}{{ fileform.file_field.errors }}
{% if uploaded_files %} Uploaded files: Check on delete to delete files,
diff --git a/yaksh/templates/yaksh/design_questionpaper.html b/yaksh/templates/yaksh/design_questionpaper.html
index 1656e2b..d982d27 100644
--- a/yaksh/templates/yaksh/design_questionpaper.html
+++ b/yaksh/templates/yaksh/design_questionpaper.html
@@ -192,10 +192,14 @@ select
- Almost finished creating your question paper
+ Almost finished creating your question paper
+
--
cgit
From 21181922ee2b4d69fa62ae9725fcda759b475b80 Mon Sep 17 00:00:00 2001
From: mahesh
Date: Tue, 20 Feb 2018 12:41:59 +0530
Subject: TestCaseOrder created only mcc/mcq questions
---
yaksh/models.py | 8 +++++---
yaksh/templates/yaksh/user_data.html | 3 +--
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/yaksh/models.py b/yaksh/models.py
index 8e0bb4d..ecc0fc4 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -1218,15 +1218,17 @@ class QuestionPaper(models.Model):
question_ids = []
for question in questions:
question_ids.append(str(question.id))
- testcases = question.get_test_cases()
if self.shuffle_testcases and \
question.type in ["mcq", "mcc"]:
+ testcases = question.get_test_cases()
random.shuffle(testcases)
- testcases_ids = ",".join([str(tc.id) for tc in testcases])
- testcases_order = TestCaseOrder.objects.create(
+ testcases_ids = ",".join([str(tc.id) for tc in testcases]
+ )
+ testcases_order = TestCaseOrder.objects.create(
answer_paper=ans_paper,
question=question,
order=testcases_ids)
+
ans_paper.questions_order = ",".join(question_ids)
ans_paper.save()
ans_paper.questions_unanswered.add(*questions)
diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html
index 45867d2..ce2533e 100644
--- a/yaksh/templates/yaksh/user_data.html
+++ b/yaksh/templates/yaksh/user_data.html
@@ -74,8 +74,7 @@ User IP address: {{ paper.user_ip }}
{% endif %}
{% endfor %}
- {% elif question.type == "integer" or question.type == "string"
- or question.type == "float" %}
+ {% elif question.type == "integer" or question.type == "string" or question.type == "float" %}
Correct Answer:
{% for testcase in question.get_test_cases %}
{{ testcase.correct|safe }}
--
cgit
From 1c8bc4aaadb307b14cb4e673485bf2405e921543 Mon Sep 17 00:00:00 2001
From: mahesh
Date: Thu, 8 Feb 2018 15:19:27 +0530
Subject: Improve test cases for TestCaseOrder model
---
yaksh/evaluator_tests/test_simple_question_types.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py
index cbf2abd..c952884 100644
--- a/yaksh/evaluator_tests/test_simple_question_types.py
+++ b/yaksh/evaluator_tests/test_simple_question_types.py
@@ -554,6 +554,7 @@ class MCQQuestionTestCases(unittest.TestCase):
attempt_num=1,
course_id=self.course.id
)
+
@classmethod
def tearDownClass(self):
self.question1.delete()
@@ -580,8 +581,7 @@ class MCQQuestionTestCases(unittest.TestCase):
course_id=self.course.id
)
not_ordered_testcase = self.question1.get_ordered_test_cases(
- answerpaper3
- )
+ answerpaper3 )
get_test_cases = self.question1.get_test_cases()
# Then
self.assertNotEqual(order1, order2)
--
cgit
From 9c9e505e79abce0cae6b341880ed9f8a4e31a8be Mon Sep 17 00:00:00 2001
From: mahesh
Date: Fri, 2 Feb 2018 15:22:25 +0530
Subject: Add jumble question type
---
yaksh/models.py | 24 ++
yaksh/static/yaksh/js/jquery-sortable.js | 693 +++++++++++++++++++++++++++++++
yaksh/static/yaksh/js/requesthandler.js | 11 +-
yaksh/templates/yaksh/add_question.html | 1 +
yaksh/templates/yaksh/question.html | 23 +-
yaksh/views.py | 3 +
6 files changed, 752 insertions(+), 3 deletions(-)
create mode 100644 yaksh/static/yaksh/js/jquery-sortable.js
diff --git a/yaksh/models.py b/yaksh/models.py
index ecc0fc4..7c5bb85 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -54,6 +54,8 @@ question_types = (
("integer", "Answer in Integer"),
("string", "Answer in String"),
("float", "Answer in Float"),
+ ("arrange", "Arrange Options in Order"),
+
)
enrollment_methods = (
@@ -69,6 +71,7 @@ test_case_types = (
("integertestcase", "Integer Testcase"),
("stringtestcase", "String Testcase"),
("floattestcase", "Float Testcase"),
+ ("arrangetestcase", "Arrange Options Testcase"),
)
string_check_type = (
@@ -1791,6 +1794,13 @@ class AnswerPaper(models.Model):
result['success'] = True
result['error'] = ['Correct answer']
+ elif question.type == 'arrange':
+ tc_list = sorted([ids.id for ids in question.get_test_cases()])
+ if user_answer == tc_list:
+ result['success'] = True
+ result['error'] = ['Correct answer']
+
+
elif question.type == 'code' or question.type == "upload":
user_dir = self.user.profile.get_user_dir()
url = '{0}:{1}'.format(SERVER_HOST_NAME, server_port)
@@ -2011,6 +2021,18 @@ class FloatTestCase(TestCase):
)
+class ArrangeTestCase(TestCase):
+
+ options = models.TextField(default=None)
+
+ def get_field_value(self):
+ return {"test_case_type": "arrangetestcase",
+ "options": self.options}
+
+ def __str__(self):
+ return u'Arrange Testcase | Option: {0}'.format(self.options)
+
+
##############################################################################
class TestCaseOrder(models.Model):
"""Testcase order contains a set of ordered test cases for a given question
@@ -2025,3 +2047,5 @@ class TestCaseOrder(models.Model):
#Order of the test case for a question.
order = models.TextField()
+
+
diff --git a/yaksh/static/yaksh/js/jquery-sortable.js b/yaksh/static/yaksh/js/jquery-sortable.js
new file mode 100644
index 0000000..376880c
--- /dev/null
+++ b/yaksh/static/yaksh/js/jquery-sortable.js
@@ -0,0 +1,693 @@
+/* ===================================================
+ * jquery-sortable.js v0.9.13
+ * http://johnny.github.com/jquery-sortable/
+ * ===================================================
+ * Copyright (c) 2012 Jonas von Andrian
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ========================================================== */
+
+
+!function ( $, window, pluginName, undefined){
+ var containerDefaults = {
+ // If true, items can be dragged from this container
+ drag: true,
+ // If true, items can be droped onto this container
+ drop: true,
+ // Exclude items from being draggable, if the
+ // selector matches the item
+ exclude: "",
+ // If true, search for nested containers within an item.If you nest containers,
+ // either the original selector with which you call the plugin must only match the top containers,
+ // or you need to specify a group (see the bootstrap nav example)
+ nested: true,
+ // If true, the items are assumed to be arranged vertically
+ vertical: true
+ }, // end container defaults
+ groupDefaults = {
+ // This is executed after the placeholder has been moved.
+ // $closestItemOrContainer contains the closest item, the placeholder
+ // has been put at or the closest empty Container, the placeholder has
+ // been appended to.
+ afterMove: function ($placeholder, container, $closestItemOrContainer) {
+ },
+ // The exact css path between the container and its items, e.g. "> tbody"
+ containerPath: "",
+ // The css selector of the containers
+ containerSelector: "ol, ul",
+ // Distance the mouse has to travel to start dragging
+ distance: 0,
+ // Time in milliseconds after mousedown until dragging should start.
+ // This option can be used to prevent unwanted drags when clicking on an element.
+ delay: 0,
+ // The css selector of the drag handle
+ handle: "",
+ // The exact css path between the item and its subcontainers.
+ // It should only match the immediate items of a container.
+ // No item of a subcontainer should be matched. E.g. for ol>div>li the itemPath is "> div"
+ itemPath: "",
+ // The css selector of the items
+ itemSelector: "li",
+ // The class given to "body" while an item is being dragged
+ bodyClass: "dragging",
+ // The class giving to an item while being dragged
+ draggedClass: "dragged",
+ // Check if the dragged item may be inside the container.
+ // Use with care, since the search for a valid container entails a depth first search
+ // and may be quite expensive.
+ isValidTarget: function ($item, container) {
+ return true
+ },
+ // Executed before onDrop if placeholder is detached.
+ // This happens if pullPlaceholder is set to false and the drop occurs outside a container.
+ onCancel: function ($item, container, _super, event) {
+ },
+ // Executed at the beginning of a mouse move event.
+ // The Placeholder has not been moved yet.
+ onDrag: function ($item, position, _super, event) {
+ $item.css(position)
+ },
+ // Called after the drag has been started,
+ // that is the mouse button is being held down and
+ // the mouse is moving.
+ // The container is the closest initialized container.
+ // Therefore it might not be the container, that actually contains the item.
+ onDragStart: function ($item, container, _super, event) {
+ $item.css({
+ height: $item.outerHeight(),
+ width: $item.outerWidth()
+ })
+ $item.addClass(container.group.options.draggedClass)
+ $("body").addClass(container.group.options.bodyClass)
+ },
+ // Called when the mouse button is being released
+ onDrop: function ($item, container, _super, event) {
+ $item.removeClass(container.group.options.draggedClass).removeAttr("style")
+ $("body").removeClass(container.group.options.bodyClass)
+ },
+ // Called on mousedown. If falsy value is returned, the dragging will not start.
+ // Ignore if element clicked is input, select or textarea
+ onMousedown: function ($item, _super, event) {
+ if (!event.target.nodeName.match(/^(input|select|textarea)$/i)) {
+ event.preventDefault()
+ return true
+ }
+ },
+ // The class of the placeholder (must match placeholder option markup)
+ placeholderClass: "placeholder",
+ // Template for the placeholder. Can be any valid jQuery input
+ // e.g. a string, a DOM element.
+ // The placeholder must have the class "placeholder"
+ placeholder: '',
+ // If true, the position of the placeholder is calculated on every mousemove.
+ // If false, it is only calculated when the mouse is above a container.
+ pullPlaceholder: true,
+ // Specifies serialization of the container group.
+ // The pair $parent/$children is either container/items or item/subcontainers.
+ serialize: function ($parent, $children, parentIsContainer) {
+ var result = $.extend({}, $parent.data())
+
+ if(parentIsContainer)
+ return [$children]
+ else if ($children[0]){
+ result.children = $children
+ }
+
+ delete result.subContainers
+ delete result.sortable
+
+ return result
+ },
+ // Set tolerance while dragging. Positive values decrease sensitivity,
+ // negative values increase it.
+ tolerance: 0
+ }, // end group defaults
+ containerGroups = {},
+ groupCounter = 0,
+ emptyBox = {
+ left: 0,
+ top: 0,
+ bottom: 0,
+ right:0
+ },
+ eventNames = {
+ start: "touchstart.sortable mousedown.sortable",
+ drop: "touchend.sortable touchcancel.sortable mouseup.sortable",
+ drag: "touchmove.sortable mousemove.sortable",
+ scroll: "scroll.sortable"
+ },
+ subContainerKey = "subContainers"
+
+ /*
+ * a is Array [left, right, top, bottom]
+ * b is array [left, top]
+ */
+ function d(a,b) {
+ var x = Math.max(0, a[0] - b[0], b[0] - a[1]),
+ y = Math.max(0, a[2] - b[1], b[1] - a[3])
+ return x+y;
+ }
+
+ function setDimensions(array, dimensions, tolerance, useOffset) {
+ var i = array.length,
+ offsetMethod = useOffset ? "offset" : "position"
+ tolerance = tolerance || 0
+
+ while(i--){
+ var el = array[i].el ? array[i].el : $(array[i]),
+ // use fitting method
+ pos = el[offsetMethod]()
+ pos.left += parseInt(el.css('margin-left'), 10)
+ pos.top += parseInt(el.css('margin-top'),10)
+ dimensions[i] = [
+ pos.left - tolerance,
+ pos.left + el.outerWidth() + tolerance,
+ pos.top - tolerance,
+ pos.top + el.outerHeight() + tolerance
+ ]
+ }
+ }
+
+ function getRelativePosition(pointer, element) {
+ var offset = element.offset()
+ return {
+ left: pointer.left - offset.left,
+ top: pointer.top - offset.top
+ }
+ }
+
+ function sortByDistanceDesc(dimensions, pointer, lastPointer) {
+ pointer = [pointer.left, pointer.top]
+ lastPointer = lastPointer && [lastPointer.left, lastPointer.top]
+
+ var dim,
+ i = dimensions.length,
+ distances = []
+
+ while(i--){
+ dim = dimensions[i]
+ distances[i] = [i,d(dim,pointer), lastPointer && d(dim, lastPointer)]
+ }
+ distances = distances.sort(function (a,b) {
+ return b[1] - a[1] || b[2] - a[2] || b[0] - a[0]
+ })
+
+ // last entry is the closest
+ return distances
+ }
+
+ function ContainerGroup(options) {
+ this.options = $.extend({}, groupDefaults, options)
+ this.containers = []
+
+ if(!this.options.rootGroup){
+ this.scrollProxy = $.proxy(this.scroll, this)
+ this.dragProxy = $.proxy(this.drag, this)
+ this.dropProxy = $.proxy(this.drop, this)
+ this.placeholder = $(this.options.placeholder)
+
+ if(!options.isValidTarget)
+ this.options.isValidTarget = undefined
+ }
+ }
+
+ ContainerGroup.get = function (options) {
+ if(!containerGroups[options.group]) {
+ if(options.group === undefined)
+ options.group = groupCounter ++
+
+ containerGroups[options.group] = new ContainerGroup(options)
+ }
+
+ return containerGroups[options.group]
+ }
+
+ ContainerGroup.prototype = {
+ dragInit: function (e, itemContainer) {
+ this.$document = $(itemContainer.el[0].ownerDocument)
+
+ // get item to drag
+ var closestItem = $(e.target).closest(this.options.itemSelector);
+ // using the length of this item, prevents the plugin from being started if there is no handle being clicked on.
+ // this may also be helpful in instantiating multidrag.
+ if (closestItem.length) {
+ this.item = closestItem;
+ this.itemContainer = itemContainer;
+ if (this.item.is(this.options.exclude) || !this.options.onMousedown(this.item, groupDefaults.onMousedown, e)) {
+ return;
+ }
+ this.setPointer(e);
+ this.toggleListeners('on');
+ this.setupDelayTimer();
+ this.dragInitDone = true;
+ }
+ },
+ drag: function (e) {
+ if(!this.dragging){
+ if(!this.distanceMet(e) || !this.delayMet)
+ return
+
+ this.options.onDragStart(this.item, this.itemContainer, groupDefaults.onDragStart, e)
+ this.item.before(this.placeholder)
+ this.dragging = true
+ }
+
+ this.setPointer(e)
+ // place item under the cursor
+ this.options.onDrag(this.item,
+ getRelativePosition(this.pointer, this.item.offsetParent()),
+ groupDefaults.onDrag,
+ e)
+
+ var p = this.getPointer(e),
+ box = this.sameResultBox,
+ t = this.options.tolerance
+
+ if(!box || box.top - t > p.top || box.bottom + t < p.top || box.left - t > p.left || box.right + t < p.left)
+ if(!this.searchValidTarget()){
+ this.placeholder.detach()
+ this.lastAppendedItem = undefined
+ }
+ },
+ drop: function (e) {
+ this.toggleListeners('off')
+
+ this.dragInitDone = false
+
+ if(this.dragging){
+ // processing Drop, check if placeholder is detached
+ if(this.placeholder.closest("html")[0]){
+ this.placeholder.before(this.item).detach()
+ } else {
+ this.options.onCancel(this.item, this.itemContainer, groupDefaults.onCancel, e)
+ }
+ this.options.onDrop(this.item, this.getContainer(this.item), groupDefaults.onDrop, e)
+
+ // cleanup
+ this.clearDimensions()
+ this.clearOffsetParent()
+ this.lastAppendedItem = this.sameResultBox = undefined
+ this.dragging = false
+ }
+ },
+ searchValidTarget: function (pointer, lastPointer) {
+ if(!pointer){
+ pointer = this.relativePointer || this.pointer
+ lastPointer = this.lastRelativePointer || this.lastPointer
+ }
+
+ var distances = sortByDistanceDesc(this.getContainerDimensions(),
+ pointer,
+ lastPointer),
+ i = distances.length
+
+ while(i--){
+ var index = distances[i][0],
+ distance = distances[i][1]
+
+ if(!distance || this.options.pullPlaceholder){
+ var container = this.containers[index]
+ if(!container.disabled){
+ if(!this.$getOffsetParent()){
+ var offsetParent = container.getItemOffsetParent()
+ pointer = getRelativePosition(pointer, offsetParent)
+ lastPointer = getRelativePosition(lastPointer, offsetParent)
+ }
+ if(container.searchValidTarget(pointer, lastPointer))
+ return true
+ }
+ }
+ }
+ if(this.sameResultBox)
+ this.sameResultBox = undefined
+ },
+ movePlaceholder: function (container, item, method, sameResultBox) {
+ var lastAppendedItem = this.lastAppendedItem
+ if(!sameResultBox && lastAppendedItem && lastAppendedItem[0] === item[0])
+ return;
+
+ item[method](this.placeholder)
+ this.lastAppendedItem = item
+ this.sameResultBox = sameResultBox
+ this.options.afterMove(this.placeholder, container, item)
+ },
+ getContainerDimensions: function () {
+ if(!this.containerDimensions)
+ setDimensions(this.containers, this.containerDimensions = [], this.options.tolerance, !this.$getOffsetParent())
+ return this.containerDimensions
+ },
+ getContainer: function (element) {
+ return element.closest(this.options.containerSelector).data(pluginName)
+ },
+ $getOffsetParent: function () {
+ if(this.offsetParent === undefined){
+ var i = this.containers.length - 1,
+ offsetParent = this.containers[i].getItemOffsetParent()
+
+ if(!this.options.rootGroup){
+ while(i--){
+ if(offsetParent[0] != this.containers[i].getItemOffsetParent()[0]){
+ // If every container has the same offset parent,
+ // use position() which is relative to this parent,
+ // otherwise use offset()
+ // compare #setDimensions
+ offsetParent = false
+ break;
+ }
+ }
+ }
+
+ this.offsetParent = offsetParent
+ }
+ return this.offsetParent
+ },
+ setPointer: function (e) {
+ var pointer = this.getPointer(e)
+
+ if(this.$getOffsetParent()){
+ var relativePointer = getRelativePosition(pointer, this.$getOffsetParent())
+ this.lastRelativePointer = this.relativePointer
+ this.relativePointer = relativePointer
+ }
+
+ this.lastPointer = this.pointer
+ this.pointer = pointer
+ },
+ distanceMet: function (e) {
+ var currentPointer = this.getPointer(e)
+ return (Math.max(
+ Math.abs(this.pointer.left - currentPointer.left),
+ Math.abs(this.pointer.top - currentPointer.top)
+ ) >= this.options.distance)
+ },
+ getPointer: function(e) {
+ var o = e.originalEvent || e.originalEvent.touches && e.originalEvent.touches[0]
+ return {
+ left: e.pageX || o.pageX,
+ top: e.pageY || o.pageY
+ }
+ },
+ setupDelayTimer: function () {
+ var that = this
+ this.delayMet = !this.options.delay
+
+ // init delay timer if needed
+ if (!this.delayMet) {
+ clearTimeout(this._mouseDelayTimer);
+ this._mouseDelayTimer = setTimeout(function() {
+ that.delayMet = true
+ }, this.options.delay)
+ }
+ },
+ scroll: function (e) {
+ this.clearDimensions()
+ this.clearOffsetParent() // TODO is this needed?
+ },
+ toggleListeners: function (method) {
+ var that = this,
+ events = ['drag','drop','scroll']
+
+ $.each(events,function (i,event) {
+ that.$document[method](eventNames[event], that[event + 'Proxy'])
+ })
+ },
+ clearOffsetParent: function () {
+ this.offsetParent = undefined
+ },
+ // Recursively clear container and item dimensions
+ clearDimensions: function () {
+ this.traverse(function(object){
+ object._clearDimensions()
+ })
+ },
+ traverse: function(callback) {
+ callback(this)
+ var i = this.containers.length
+ while(i--){
+ this.containers[i].traverse(callback)
+ }
+ },
+ _clearDimensions: function(){
+ this.containerDimensions = undefined
+ },
+ _destroy: function () {
+ containerGroups[this.options.group] = undefined
+ }
+ }
+
+ function Container(element, options) {
+ this.el = element
+ this.options = $.extend( {}, containerDefaults, options)
+
+ this.group = ContainerGroup.get(this.options)
+ this.rootGroup = this.options.rootGroup || this.group
+ this.handle = this.rootGroup.options.handle || this.rootGroup.options.itemSelector
+
+ var itemPath = this.rootGroup.options.itemPath
+ this.target = itemPath ? this.el.find(itemPath) : this.el
+
+ this.target.on(eventNames.start, this.handle, $.proxy(this.dragInit, this))
+
+ if(this.options.drop)
+ this.group.containers.push(this)
+ }
+
+ Container.prototype = {
+ dragInit: function (e) {
+ var rootGroup = this.rootGroup
+
+ if( !this.disabled &&
+ !rootGroup.dragInitDone &&
+ this.options.drag &&
+ this.isValidDrag(e)) {
+ rootGroup.dragInit(e, this)
+ }
+ },
+ isValidDrag: function(e) {
+ return e.which == 1 ||
+ e.type == "touchstart" && e.originalEvent.touches.length == 1
+ },
+ searchValidTarget: function (pointer, lastPointer) {
+ var distances = sortByDistanceDesc(this.getItemDimensions(),
+ pointer,
+ lastPointer),
+ i = distances.length,
+ rootGroup = this.rootGroup,
+ validTarget = !rootGroup.options.isValidTarget ||
+ rootGroup.options.isValidTarget(rootGroup.item, this)
+
+ if(!i && validTarget){
+ rootGroup.movePlaceholder(this, this.target, "append")
+ return true
+ } else
+ while(i--){
+ var index = distances[i][0],
+ distance = distances[i][1]
+ if(!distance && this.hasChildGroup(index)){
+ var found = this.getContainerGroup(index).searchValidTarget(pointer, lastPointer)
+ if(found)
+ return true
+ }
+ else if(validTarget){
+ this.movePlaceholder(index, pointer)
+ return true
+ }
+ }
+ },
+ movePlaceholder: function (index, pointer) {
+ var item = $(this.items[index]),
+ dim = this.itemDimensions[index],
+ method = "after",
+ width = item.outerWidth(),
+ height = item.outerHeight(),
+ offset = item.offset(),
+ sameResultBox = {
+ left: offset.left,
+ right: offset.left + width,
+ top: offset.top,
+ bottom: offset.top + height
+ }
+ if(this.options.vertical){
+ var yCenter = (dim[2] + dim[3]) / 2,
+ inUpperHalf = pointer.top <= yCenter
+ if(inUpperHalf){
+ method = "before"
+ sameResultBox.bottom -= height / 2
+ } else
+ sameResultBox.top += height / 2
+ } else {
+ var xCenter = (dim[0] + dim[1]) / 2,
+ inLeftHalf = pointer.left <= xCenter
+ if(inLeftHalf){
+ method = "before"
+ sameResultBox.right -= width / 2
+ } else
+ sameResultBox.left += width / 2
+ }
+ if(this.hasChildGroup(index))
+ sameResultBox = emptyBox
+ this.rootGroup.movePlaceholder(this, item, method, sameResultBox)
+ },
+ getItemDimensions: function () {
+ if(!this.itemDimensions){
+ this.items = this.$getChildren(this.el, "item").filter(
+ ":not(." + this.group.options.placeholderClass + ", ." + this.group.options.draggedClass + ")"
+ ).get()
+ setDimensions(this.items, this.itemDimensions = [], this.options.tolerance)
+ }
+ return this.itemDimensions
+ },
+ getItemOffsetParent: function () {
+ var offsetParent,
+ el = this.el
+ // Since el might be empty we have to check el itself and
+ // can not do something like el.children().first().offsetParent()
+ if(el.css("position") === "relative" || el.css("position") === "absolute" || el.css("position") === "fixed")
+ offsetParent = el
+ else
+ offsetParent = el.offsetParent()
+ return offsetParent
+ },
+ hasChildGroup: function (index) {
+ return this.options.nested && this.getContainerGroup(index)
+ },
+ getContainerGroup: function (index) {
+ var childGroup = $.data(this.items[index], subContainerKey)
+ if( childGroup === undefined){
+ var childContainers = this.$getChildren(this.items[index], "container")
+ childGroup = false
+
+ if(childContainers[0]){
+ var options = $.extend({}, this.options, {
+ rootGroup: this.rootGroup,
+ group: groupCounter ++
+ })
+ childGroup = childContainers[pluginName](options).data(pluginName).group
+ }
+ $.data(this.items[index], subContainerKey, childGroup)
+ }
+ return childGroup
+ },
+ $getChildren: function (parent, type) {
+ var options = this.rootGroup.options,
+ path = options[type + "Path"],
+ selector = options[type + "Selector"]
+
+ parent = $(parent)
+ if(path)
+ parent = parent.find(path)
+
+ return parent.children(selector)
+ },
+ _serialize: function (parent, isContainer) {
+ var that = this,
+ childType = isContainer ? "item" : "container",
+
+ children = this.$getChildren(parent, childType).not(this.options.exclude).map(function () {
+ return that._serialize($(this), !isContainer)
+ }).get()
+
+ return this.rootGroup.options.serialize(parent, children, isContainer)
+ },
+ traverse: function(callback) {
+ $.each(this.items || [], function(item){
+ var group = $.data(this, subContainerKey)
+ if(group)
+ group.traverse(callback)
+ });
+
+ callback(this)
+ },
+ _clearDimensions: function () {
+ this.itemDimensions = undefined
+ },
+ _destroy: function() {
+ var that = this;
+
+ this.target.off(eventNames.start, this.handle);
+ this.el.removeData(pluginName)
+
+ if(this.options.drop)
+ this.group.containers = $.grep(this.group.containers, function(val){
+ return val != that
+ })
+
+ $.each(this.items || [], function(){
+ $.removeData(this, subContainerKey)
+ })
+ }
+ }
+
+ var API = {
+ enable: function() {
+ this.traverse(function(object){
+ object.disabled = false
+ })
+ },
+ disable: function (){
+ this.traverse(function(object){
+ object.disabled = true
+ })
+ },
+ serialize: function () {
+ return this._serialize(this.el, true)
+ },
+ refresh: function() {
+ this.traverse(function(object){
+ object._clearDimensions()
+ })
+ },
+ destroy: function () {
+ this.traverse(function(object){
+ object._destroy();
+ })
+ }
+ }
+
+ $.extend(Container.prototype, API)
+
+ /**
+ * jQuery API
+ *
+ * Parameters are
+ * either options on init
+ * or a method name followed by arguments to pass to the method
+ */
+ $.fn[pluginName] = function(methodOrOptions) {
+ var args = Array.prototype.slice.call(arguments, 1)
+
+ return this.map(function(){
+ var $t = $(this),
+ object = $t.data(pluginName)
+
+ if(object && API[methodOrOptions])
+ return API[methodOrOptions].apply(object, args) || this
+ else if(!object && (methodOrOptions === undefined ||
+ typeof methodOrOptions === "object"))
+ $t.data(pluginName, new Container($t, methodOrOptions))
+
+ return this
+ });
+ };
+
+}(jQuery, window, 'sortable');
diff --git a/yaksh/static/yaksh/js/requesthandler.js b/yaksh/static/yaksh/js/requesthandler.js
index ec2391a..a215ce4 100644
--- a/yaksh/static/yaksh/js/requesthandler.js
+++ b/yaksh/static/yaksh/js/requesthandler.js
@@ -177,6 +177,13 @@ if (question_type == 'upload' || question_type == 'code') {
global_editor.editor.setValue(init_val);
global_editor.editor.clearHistory();
}
-
-
});
+function user_arranged_options(){
+ var temp_array = []
+ var add_array = document.getElementById("arrange_order");
+ var ans_array = order_array.children().get()
+ var answer_is = $.each(ans_array, function( index, value ) {
+ temp_array.push(value.id);
+ });
+ add_array.value = temp_array
+}
diff --git a/yaksh/templates/yaksh/add_question.html b/yaksh/templates/yaksh/add_question.html
index ed69657..79c132c 100644
--- a/yaksh/templates/yaksh/add_question.html
+++ b/yaksh/templates/yaksh/add_question.html
@@ -64,6 +64,7 @@
+
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 9d6ce48..ed04963 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -21,6 +21,7 @@
+
+ {% endif %}
+
+
{% if question.type == "code" %}
@@ -269,6 +287,9 @@ question_type = "{{ question.type }}"
{% elif question.type == "upload" %}
+ {% elif question.type == "arrange" %}
+
+
{% else %}
{% if question in paper.get_questions_unanswered or quiz.is_exercise %}
diff --git a/yaksh/views.py b/yaksh/views.py
index 27a07d2..5c88391 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -726,6 +726,9 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None,
elif current_question.type == 'mcc':
user_answer = request.POST.getlist('answer')
+ elif current_question.type == 'arrange':
+ user_answer_list = request.POST.get('answer').split(',')
+ user_answer = [int(ids) for ids in user_answer_list]
elif current_question.type == 'upload':
# if time-up at upload question then the form is submitted without
# validation
--
cgit
From 453a5d8f191b62af45426d5c98a99552c34ba396 Mon Sep 17 00:00:00 2001
From: maheshgudi
Date: Mon, 5 Feb 2018 17:06:09 +0530
Subject: Include Rebase changes from shuffle testcases
---
yaksh/forms.py | 3 +++
yaksh/templates/yaksh/question.html | 7 +++++--
yaksh/views.py | 4 +++-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/yaksh/forms.py b/yaksh/forms.py
index e53eda3..7eae673 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -35,6 +35,9 @@ question_types = (
("integer", "Answer in Integer"),
("string", "Answer in String"),
("float", "Answer in Float"),
+ ("arrange", "Arrange Options in Order"),
+
+
)
test_case_types = (
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index ed04963..e67a260 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -257,11 +257,14 @@ question_type = "{{ question.type }}"
{% if question.type == "arrange" %}
+
{% for test_case in test_cases %}
- - {{test_case.options| safe }}
+ - {{test_case.options| safe }}
{% endfor %}
-
+
+
+
|