summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/models.py6
-rw-r--r--yaksh/templates/yaksh/grade_user.html22
-rw-r--r--yaksh/templates/yaksh/question.html32
-rw-r--r--yaksh/test_models.py17
-rw-r--r--yaksh/test_views.py387
-rw-r--r--yaksh/urls.py4
-rw-r--r--yaksh/views.py25
7 files changed, 461 insertions, 32 deletions
diff --git a/yaksh/models.py b/yaksh/models.py
index d9e07fd..68bde48 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -1284,6 +1284,9 @@ class AnswerPaper(models.Model):
}]
return q_a
+ def get_latest_answer(self, question_id):
+ return self.answers.filter(question=question_id).order_by("id").last()
+
def get_questions(self):
return self.questions.filter(active=True)
@@ -1303,8 +1306,7 @@ class AnswerPaper(models.Model):
return self.time_left() > 0
def get_previous_answers(self, question):
- if question.type == 'code':
- return self.answers.filter(question=question).order_by('-id')
+ return self.answers.filter(question=question).order_by('-id')
def validate_answer(self, user_answer, question, json_data=None, uid=None):
"""
diff --git a/yaksh/templates/yaksh/grade_user.html b/yaksh/templates/yaksh/grade_user.html
index 9cdfb1a..2038210 100644
--- a/yaksh/templates/yaksh/grade_user.html
+++ b/yaksh/templates/yaksh/grade_user.html
@@ -9,6 +9,14 @@
{% block script %}
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML"></script>
+<script src="{{ URL_ROOT }}/static/yaksh/js/jquery.tablesorter.min.js"></script>
+<script type="text/javascript">
+$(document).ready(function()
+{
+ $("#marks_table").tablesorter({sortList: [[2,1]]});
+});
+
+</script>
{% endblock script %}
{% if course_details %}
@@ -108,17 +116,25 @@ Status : <b style="color: green;"> Passed </b><br/>
{% if paper.answers.count %}
<h4> Report </h4><br>
-<table class="table table-bordered">
+<table class="tablesorter table table-striped table-bordered" id ='marks_table'>
+ <thead>
+ <tr>
+ <th>Question Id</th>
<th>Questions</th>
<th>Marks Obtained</th>
+ </tr>
+ </thead>
+ <tbody>
{% for question, answers in paper.get_question_answers.items %}
{% with answers|last as answer %}
<tr>
- <td>{{ question.id }}</td>
+ <td>{{question.id}}</td>
+ <td><a href="#question_{{question.id}}">{{ question.summary }}</a></td>
<td>{{ answer.answer.marks }}</td>
</tr>
{% endwith %}
{% endfor %}
+ </tbody>
</table>
@@ -135,7 +151,7 @@ Status : <b style="color: green;"> Passed </b><br/>
{% for question, answers in paper.get_question_answers.items %}
<div class = "well well-sm">
<div class="panel panel-info">
- <div class="panel-heading">
+ <div class="panel-heading" id="question_{{question.id}}">
<strong> Details: {{forloop.counter}}. {{ question.summary }}
<span class="marks pull-right"> Mark(s): {{ question.points }} </span>
</strong>
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 3a3066c..eb6eb3d 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -53,7 +53,7 @@ function updateClock(){
var ss = ('0' + t.seconds).slice(-2);
if(t.total<0){
-
+
document.forms["code"].submit();
clearInterval(timeinterval);
return null;
@@ -77,10 +77,7 @@ function validate(){
$("#upload_alert").modal("show");
return false;
}
- else
- {
- send_request();
- }
+ return true;
}
function call_skip(url)
@@ -155,32 +152,44 @@ lang = "{{ question.language }}"
<div class="panel-body">
{% if question.type == "mcq" %}
{% for test_case in test_cases %}
- <input name="answer" type="radio" value="{{ test_case.options }}" />{{ test_case.options|safe }} <br/>
+ {% if last_attempt and last_attempt|safe == test_case.options|safe %}
+ <input name="answer" type="radio" value="{{ test_case.options }}" checked />
+ {{ test_case.options|safe }} <br/>
+ {% else %}
+ <input name="answer" type="radio" value="{{ test_case.options }}" />
+ {{ test_case.options|safe }} <br/>
+ {% endif %}
{% endfor %}
{% endif %}
{% if question.type == "integer" %}
Enter Integer:<br/>
- <input name="answer" type="number" id="integer" />
+ <input name="answer" type="number" id="integer" value={{ last_attempt|safe }} />
<br/><br/>
{% endif %}
{% if question.type == "string" %}
Enter Text:<br/>
- <textarea name="answer" id="string"></textarea>
+ <textarea name="answer" id="string">{{ last_attempt|safe }}</textarea>
<br/><br/>
{% endif %}
{% if question.type == "float" %}
Enter Decimal Value :<br/>
- <input name="answer" type="number" step="any" id="float" />
+ <input name="answer" type="number" step="any" id="float" value={{ last_attempt|safe }} />
<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 }}
+ {% if last_attempt and test_case.options|safe in last_attempt|safe %}
+ <input name="answer" type="checkbox" value="{{ test_case.options }}" checked/> {{ test_case.options }}
<br>
+ {% else %}
+ <input name="answer" type="checkbox" value="{{ test_case.options }}">
+ {{ test_case.options}}
+ <br/>
+ {% endif %}
{% endfor %}
{% endif %}
{% if question.type == "upload" %}
@@ -241,6 +250,3 @@ lang = "{{ question.language }}"
</div>
</div>
{% endblock main %}
-
-
-
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index bc7f114..fd31ca2 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -547,6 +547,12 @@ class AnswerPaperTestCases(unittest.TestCase):
self.answerpaper.answers.add(self.answer_right)
self.answerpaper.answers.add(self.answer_wrong)
+ self.answer1 = Answer.objects.create(
+ question=self.question1,
+ answer="answer1", correct=False, error=json.dumps([])
+ )
+ self.answerpaper.answers.add(self.answer1)
+
self.question1.language = 'python'
self.question1.test_case_type = 'standardtestcase'
self.question1.summary = "Question1"
@@ -834,17 +840,22 @@ class AnswerPaperTestCases(unittest.TestCase):
def test_get_previous_answers(self):
answers = self.answerpaper.get_previous_answers(self.questions[0])
- self.assertEqual(answers.count(), 1)
+ self.assertEqual(answers.count(), 2)
self.assertTrue(answers[0], self.answer_right)
answers = self.answerpaper.get_previous_answers(self.questions[1])
self.assertEqual(answers.count(), 1)
self.assertTrue(answers[0], self.answer_wrong)
- def test_set_marks (self):
+ def test_set_marks(self):
self.answer_wrong.set_marks(0.5)
self.assertEqual(self.answer_wrong.marks, 0.5)
self.answer_wrong.set_marks(10.0)
- self.assertEqual(self.answer_wrong.marks,1.0)
+ self.assertEqual(self.answer_wrong.marks, 1.0)
+
+ def test_get_latest_answer(self):
+ latest_answer = self.answerpaper.get_latest_answer(self.question1.id)
+ self.assertEqual(latest_answer.id, self.answer1.id)
+ self.assertEqual(latest_answer.answer, "answer1")
###############################################################################
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 064c39d..5ef97d6 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -22,7 +22,8 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
- AssignmentUpload, FileUpload
+ AssignmentUpload, FileUpload, McqTestCase, IntegerTestCase, StringTestCase,\
+ FloatTestCase
from yaksh.decorators import user_has_profile
@@ -3378,3 +3379,387 @@ class TestShowStatistics(TestCase):
[1, 1])
self.assertEqual(response.context['attempts'][0], 1)
self.assertEqual(response.context['total'], 1)
+
+
+class TestQuestionPaper(TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ self.mod_group = Group.objects.create(name='moderator')
+ tzone = pytz.timezone('UTC')
+ # Create Moderator with profile
+ self.user_plaintext_pass = 'demo'
+ self.user = User.objects.create_user(
+ username='demo_user',
+ password=self.user_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@test.com'
+ )
+
+ Profile.objects.create(
+ user=self.user,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC'
+ )
+
+ # Add to moderator group
+ self.mod_group.user_set.add(self.user)
+
+ self.course = Course.objects.create(
+ name="Python Course",
+ enrollment="Open Enrollment", creator=self.user)
+
+ self.quiz = Quiz.objects.create(
+ start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
+ end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
+ duration=30, active=True, instructions="Demo Instructions",
+ attempts_allowed=-1, time_between_attempts=0,
+ description='demo quiz', pass_criteria=40,
+ language='Python', course=self.course
+ )
+
+ # Mcq Question
+ self.question_mcq = Question.objects.create(
+ summary="Test_mcq_question", description="Test MCQ",
+ points=1.0, language="python", type="mcq", user=self.user
+ )
+ self.mcq_based_testcase = McqTestCase(
+ options="a",
+ question=self.question_mcq,
+ correct=True,
+ type='mcqtestcase'
+ )
+ self.mcq_based_testcase.save()
+
+ ordered_questions = str(self.question_mcq.id)
+
+ # Mcc Question
+ self.question_mcc = Question.objects.create(
+ summary="Test_mcc_question", description="Test MCC",
+ points=1.0, language="python", type="mcq", user=self.user
+ )
+ self.mcc_based_testcase = McqTestCase(
+ options="a",
+ question=self.question_mcc,
+ correct=True,
+ type='mcqtestcase'
+ )
+ self.mcc_based_testcase.save()
+
+ ordered_questions = ordered_questions + str(self.question_mcc.id)
+
+ # Integer Question
+ self.question_int = Question.objects.create(
+ summary="Test_mcc_question", description="Test MCC",
+ points=1.0, language="python", type="integer", user=self.user
+ )
+ self.int_based_testcase = IntegerTestCase(
+ correct=1,
+ question=self.question_int,
+ type='integertestcase'
+ )
+ self.int_based_testcase.save()
+
+ ordered_questions = ordered_questions + str(self.question_int.id)
+
+ # String Question
+ self.question_str = Question.objects.create(
+ summary="Test_mcc_question", description="Test MCC",
+ points=1.0, language="python", type="string", user=self.user
+ )
+ self.str_based_testcase = StringTestCase(
+ correct="abc",
+ string_check="lower",
+ question=self.question_str,
+ type='stringtestcase'
+ )
+ self.str_based_testcase.save()
+
+ # Float Question
+ self.question_float = Question.objects.create(
+ summary="Test_mcc_question", description="Test MCC",
+ points=1.0, language="python", type="float", user=self.user
+ )
+ self.float_based_testcase = FloatTestCase(
+ correct=2.0,
+ error_margin=0,
+ question=self.question_float,
+ type='floattestcase'
+ )
+ self.float_based_testcase.save()
+
+ ordered_questions = ordered_questions + str(self.question_float.id)
+
+ questions_list = [self.question_mcq, self.question_mcc,
+ self.question_int, self.question_str,
+ self.question_float]
+
+ self.question_paper = QuestionPaper.objects.create(
+ quiz=self.quiz,
+ total_marks=5.0, fixed_question_order=ordered_questions
+ )
+ self.question_paper.fixed_questions.add(*questions_list)
+ self.answerpaper = AnswerPaper.objects.create(
+ user=self.user, question_paper=self.question_paper,
+ attempt_number=1,
+ start_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
+ end_time=datetime(2014, 10, 9, 10, 15, 15, 0, tzone),
+ user_ip="127.0.0.1", status="inprogress", passed=False,
+ percent=0, marks_obtained=0
+ )
+ self.answerpaper.questions.add(*questions_list)
+
+ def tearDown(self):
+ self.client.logout()
+ self.user.delete()
+ self.quiz.delete()
+ self.course.delete()
+ self.answerpaper.delete()
+ self.question_mcq.delete()
+ self.question_mcc.delete()
+ self.question_int.delete()
+ self.question_paper.delete()
+
+ def test_mcq_attempt_right_after_wrong(self):
+ """ Case:- Check if answerpaper and answer marks are updated after
+ attempting same mcq question with wrong answer and then right
+ answer
+ """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ # Given Wrong Answer
+ wrong_user_answer = "b"
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_mcq.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": wrong_user_answer}
+ )
+
+ # Then
+ wrong_answer_paper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(wrong_answer_paper.marks_obtained, 0)
+
+ # Given Right Answer
+ right_user_answer = "a"
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_mcq.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": right_user_answer}
+ )
+
+ # Then
+ updated_answerpaper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(updated_answerpaper.marks_obtained, 1)
+
+ def test_mcq_question_attempt_wrong_after_right(self):
+ """ Case:- Check if answerpaper and answer marks are updated after
+ attempting same mcq question with right answer and then wrong
+ answer
+ """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ # Given Right Answer
+ right_user_answer = "a"
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_mcq.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": right_user_answer}
+ )
+
+ # Then
+ updated_answerpaper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(updated_answerpaper.marks_obtained, 1)
+
+ # Given Wrong Answer
+ wrong_user_answer = "b"
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_mcq.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": wrong_user_answer}
+ )
+
+ # Then
+ wrong_answer_paper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(wrong_answer_paper.marks_obtained, 0)
+
+ def test_mcc_question_attempt_wrong_after_right(self):
+ """ Case:- Check if answerpaper and answer marks are updated after
+ attempting same mcc question with right answer and then wrong
+ answer
+ """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ # Given Right Answer
+ right_user_answer = "a"
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_mcc.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": right_user_answer}
+ )
+
+ # Then
+ updated_answerpaper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(updated_answerpaper.marks_obtained, 1)
+
+ # Given Wrong Answer
+ wrong_user_answer = "b"
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_mcc.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": wrong_user_answer}
+ )
+
+ # Then
+ wrong_answer_paper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(wrong_answer_paper.marks_obtained, 0)
+
+ def test_integer_question_attempt_wrong_after_right(self):
+ """ Case:- Check if answerpaper and answer marks are updated after
+ attempting same integer question with right answer and then wrong
+ answer
+ """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ # Given Right Answer
+ right_user_answer = 1
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_int.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": right_user_answer}
+ )
+
+ # Then
+ updated_answerpaper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(updated_answerpaper.marks_obtained, 1)
+
+ # Given Wrong Answer
+ wrong_user_answer = -1
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_int.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": wrong_user_answer}
+ )
+
+ # Then
+ wrong_answer_paper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(wrong_answer_paper.marks_obtained, 0)
+
+ def test_string_question_attempt_wrong_after_right(self):
+ """ Case:- Check if answerpaper and answer marks are updated after
+ attempting same string question with right answer and then wrong
+ answer
+ """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ # Given Right Answer
+ right_user_answer = "abc"
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_str.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": right_user_answer}
+ )
+
+ # Then
+ updated_answerpaper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(updated_answerpaper.marks_obtained, 1)
+
+ # Given Wrong Answer
+ wrong_user_answer = "c"
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_str.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": wrong_user_answer}
+ )
+
+ # Then
+ wrong_answer_paper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(wrong_answer_paper.marks_obtained, 0)
+
+ def test_float_question_attempt_wrong_after_right(self):
+ """ Case:- Check if answerpaper and answer marks are updated after
+ attempting same float question with right answer and then wrong
+ answer
+ """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ # Given Right Answer
+ right_user_answer = 2.0
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_float.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": right_user_answer}
+ )
+
+ # Then
+ updated_answerpaper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(updated_answerpaper.marks_obtained, 1)
+
+ # Given Wrong Answer
+ wrong_user_answer = -1
+
+ # When
+ self.client.post(
+ reverse('yaksh:check',
+ kwargs={"q_id": self.question_float.id, "attempt_num": 1,
+ "questionpaper_id": self.question_paper.id}),
+ data={"answer": wrong_user_answer}
+ )
+
+ # Then
+ wrong_answer_paper = AnswerPaper.objects.get(id=self.answerpaper.id)
+ self.assertEqual(wrong_answer_paper.marks_obtained, 0)
diff --git a/yaksh/urls.py b/yaksh/urls.py
index c236640..2e25bee 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -19,10 +19,10 @@ urlpatterns = [
url(r'^complete/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',\
views.complete),
url(r'^register/$', views.user_register, name="register"),
- url(r'^(?P<q_id>\d+)/check/$', views.check),
+ url(r'^(?P<q_id>\d+)/check/$', views.check, name="check"),
url(r'^get_result/(?P<uid>\d+)/$', views.get_result),
url(r'^(?P<q_id>\d+)/check/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',\
- views.check),
+ views.check, name="check"),
url(r'^(?P<q_id>\d+)/skip/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',
views.skip),
url(r'^(?P<q_id>\d+)/skip/(?P<next_q>\d+)/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',
diff --git a/yaksh/views.py b/yaksh/views.py
index e6da2fc..6abfa2b 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -506,12 +506,15 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None):
question = get_object_or_404(Question, pk=q_id)
if request.method == 'POST' and question.type == 'code':
- user_code = request.POST.get('answer')
- new_answer = Answer(question=question, answer=user_code,
- correct=False, skipped=True,
- error=json.dumps([]))
- new_answer.save()
- paper.answers.add(new_answer)
+ if not paper.answers.filter(question=question, correct=True).exists():
+ user_code = request.POST.get('answer')
+ new_answer = Answer(
+ question=question, answer=user_code,
+ correct=False, skipped=True,
+ error=json.dumps([])
+ )
+ new_answer.save()
+ paper.answers.add(new_answer)
if next_q is not None:
next_q = get_object_or_404(Question, pk=next_q)
else:
@@ -594,11 +597,17 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
else:
user_answer = request.POST.get('answer')
if not user_answer:
- msg = ["Please submit a valid option or code"]
+ msg = "Please submit a valid answer."
return show_question(
request, current_question, paper, notification=msg
)
- new_answer = Answer(
+ if current_question in paper.get_questions_answered()\
+ and current_question.type not in ['code', 'upload']:
+ new_answer = paper.get_latest_answer(current_question.id)
+ new_answer.answer = user_answer
+ new_answer.correct = False
+ else:
+ new_answer = Answer(
question=current_question, answer=user_answer,
correct=False, error=json.dumps([])
)