summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/live_server_tests/selenium_test.py4
-rw-r--r--yaksh/models.py17
-rw-r--r--yaksh/templates/exam.html4
-rw-r--r--yaksh/templates/user.html2
-rw-r--r--yaksh/templates/yaksh/course_detail.html6
-rw-r--r--yaksh/templates/yaksh/question.html37
-rw-r--r--yaksh/templates/yaksh/quizzes_user.html2
-rw-r--r--yaksh/templates/yaksh/user_data.html4
-rw-r--r--yaksh/test_models.py4
-rw-r--r--yaksh/views.py34
10 files changed, 74 insertions, 40 deletions
diff --git a/yaksh/live_server_tests/selenium_test.py b/yaksh/live_server_tests/selenium_test.py
index d91f3ec..277f08e 100644
--- a/yaksh/live_server_tests/selenium_test.py
+++ b/yaksh/live_server_tests/selenium_test.py
@@ -29,8 +29,8 @@ class SeleniumTest():
except Exception as e:
self.driver.close()
msg = ("An Error occurred while running the Selenium load"
- " test on Yaksh!"
- "Error:\n ".format(e))
+ " test on Yaksh!\n"
+ "Error:\n{0}".format(e))
raise SeleniumTestError(msg)
diff --git a/yaksh/models.py b/yaksh/models.py
index fdf10bd..a323994 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -992,15 +992,17 @@ class AnswerPaper(models.Model):
"""Returns the number of questions left."""
return self.questions_unanswered.count()
- def completed_question(self, question_id):
+ def add_completed_question(self, question_id):
"""
Adds the completed question to the list of answered
questions and returns the next question.
"""
- next_question = self.next_question(question_id)
- self.questions_answered.add(question_id)
+ if question_id not in self.questions_answered.all():
+ self.questions_answered.add(question_id)
self.questions_unanswered.remove(question_id)
- if next_question.id == int(question_id):
+
+ next_question = self.next_question(question_id)
+ if next_question and next_question.id == int(question_id):
return None
return next_question
@@ -1009,16 +1011,19 @@ class AnswerPaper(models.Model):
Skips the current question and returns the next sequentially
available question.
"""
+ all_questions = self.questions.all()
unanswered_questions = self.questions_unanswered.all()
- questions = list(unanswered_questions.values_list('id', flat=True))
+ questions = list(all_questions.values_list('id', flat=True))
if len(questions) == 0:
return None
+ if unanswered_questions.count() == 0:
+ return None
try:
index = questions.index(int(question_id))
next_id = questions[index+1]
except (ValueError, IndexError):
next_id = questions[0]
- return unanswered_questions.get(id=next_id)
+ return all_questions.get(id=next_id)
def time_left(self):
"""Return the time remaining for the user in seconds."""
diff --git a/yaksh/templates/exam.html b/yaksh/templates/exam.html
index ae1a620..4818717 100644
--- a/yaksh/templates/exam.html
+++ b/yaksh/templates/exam.html
@@ -52,7 +52,9 @@
{% endif %}
{% endif %}
{% if qid in paper.get_questions_answered %}
- <li class="disabled"><a style="background-color:#B4B8BA; width:25%" href="#" data-toggle="tooltip" title="{{ qid.description }}" >{{ forloop.counter }}</a></li>
+ <li><a style="background-color:#B4B8BA; width:25%" href="#" data-toggle="tooltip"
+ onclick="call_skip('{{ URL_ROOT }}/exam/{{ question.id }}/skip/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')"
+ title="{{ qid.description }}">{{ forloop.counter }}</a></li>
{% endif %}
{% else %}
{% if qid.id == question.id %}
diff --git a/yaksh/templates/user.html b/yaksh/templates/user.html
index 0d1f980..b068fae 100644
--- a/yaksh/templates/user.html
+++ b/yaksh/templates/user.html
@@ -16,7 +16,7 @@
<div class= "collapse navbar-collapse" id="navbar">
<ul class="nav navbar-nav navbar-right">
<li><a href="{{ URL_ROOT }}/exam/viewprofile"> {{ user.get_full_name.title }} </a></li>
- <li><a style='cursor:pointer' id='logout' onClick='location.replace("{{URL_ROOT}}/exam/complete/");'> <span class="glyphicon glyphicon-log-out">Logout </span></a></li>
+ <li><a style='cursor:pointer' id='logout' onClick='location.replace("{{URL_ROOT}}/exam/logout/");'> <span class="glyphicon glyphicon-log-out">Logout </span></a></li>
</ul>
</div><!-- /.navbar -->
</div><!-- /.container -->
diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html
index dc85080..4b7efaf 100644
--- a/yaksh/templates/yaksh/course_detail.html
+++ b/yaksh/templates/yaksh/course_detail.html
@@ -32,6 +32,7 @@
<div id="enroll-all">
<table class="table table-striped">
<th></th>
+ <th></th>
<th>Full Name</th>
<th>Email</th>
<th>Roll Number</th>
@@ -43,6 +44,7 @@
{% for request in course.get_requests %}
<tr>
<td><input type="checkbox" name="check" value="{{ request.id }}"></td>
+ <td>{{ forloop.counter }}.</td>
<td>{{request.get_full_name}}</td>
<td> {{request.email}}</td>
<td> {{request.profile.roll_number}}</td>
@@ -72,6 +74,7 @@
<div id="reject">
<table class="table table-striped">
<th></th>
+ <th></th>
<th>Full Name</th>
<th>Email</th>
<th>Roll Number</th>
@@ -83,6 +86,7 @@
{% csrf_token %}
<tr>
<td><input type="checkbox" name="check" value="{{ enrolled.id }}"></td>
+ <td>{{ forloop.counter }}.</td>
<td> {{ enrolled.get_full_name|title }} </td>
<td> {{enrolled.email}}</td>
<td> {{enrolled.profile.roll_number}}</td>
@@ -108,6 +112,7 @@
<div id="enroll">
<table class="table table-striped">
<th></th>
+ <th></th>
<th>Full Name</th>
<th>Email</th>
<th>Roll Number</th>
@@ -119,6 +124,7 @@
{% csrf_token %}
<tr>
<td><input type="checkbox" name="check" value="{{ rejected.id }}"></td>
+ <td>{{ forloop.counter }}.</td>
<td>{{rejected.get_full_name|title}}</td>
<td> {{rejected.email}}</td>
<td> {{rejected.profile.roll_number}}</td>
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 0106bac..93ed531 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -135,13 +135,25 @@ function call_skip(url)
{% block onload %} onload="updateTime();" {% endblock %}
{% block main %}
- <p id="status"></p>
- <form id="code" action="{{URL_ROOT}}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post" enctype="multipart/form-data">
- {% csrf_token %}
- <input type=hidden name="question_id" id="question_id" value={{ question.id }}></input>
- <div class="panel panel-default">
- <div class="panel-heading">
- <h4><u> {{ question.summary }}
+ <p id="status"></p>
+ {% if notification %}
+ {% if question.type == "code" %}
+ <div class="alert alert-success" role="alert">
+ <strong>Note:</strong> {{ notification }}
+ </div>
+ {% else %}
+ <div class="alert alert-warning" role="alert">
+ <strong>Note:</strong> {{ notification }}
+ </div>
+ {% endif %}
+ {% endif %}
+ <form id="code" action="{{URL_ROOT}}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post" enctype="multipart/form-data">
+ {% csrf_token %}
+ <input type=hidden name="question_id" id="question_id" value={{ question.id }}></input>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h4>
+ <u> {{ question.summary }}
{% if question.type == "mcq" %}
(SINGLE CORRECT CHOICE)
{% elif question.type == "mcc" %}
@@ -163,8 +175,8 @@ function call_skip(url)
{% elif question.type == "float" %}
(ANSWER IN FLOAT)
{% endif %}
- </u>
- <font class=pull-right>(Marks : {{ question.points }}) </font>
+ </u>
+ <font class=pull-right>(Marks : {{ question.points }}) </font>
</h4>
<font size=3 face=arial> {{ question.description|safe }} </font>
{% if files %}
@@ -230,10 +242,15 @@ function call_skip(url)
<br><button class="btn btn-primary" type="submit" name="check" id="check" onClick="return validate();">Upload</button>&nbsp;&nbsp;
{% else %}
- <button class="btn btn-primary" type="submit" name="check" id="check" onClick="submitCode();">Check Answer <span class="glyphicon glyphicon-cog"></span></button>&nbsp;&nbsp;
+ {% 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;
+ {% endif %}
{% endif %}
+
{% if paper.question_paper.quiz.allow_skip and not paper.get_questions_unanswered|length_is:"1" %}
+ {% if question in paper.get_questions_unanswered %}
<button class="btn btn-primary" onclick="call_skip('{{ URL_ROOT }}/exam/{{ question.id }}/skip/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')" name="skip" id="skip">Attempt Later <span class="glyphicon glyphicon-arrow-right"></span></button>
+ {% endif %}
</div>
</div>
</div>
diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html
index 63f22a1..72fce30 100644
--- a/yaksh/templates/yaksh/quizzes_user.html
+++ b/yaksh/templates/yaksh/quizzes_user.html
@@ -6,7 +6,7 @@
<div class="col-md-12">
<div class="row">
<div class="col-md-4">
- <h4><b><u> {{ course.name }} by {{ course.creator }}</u></b></h4>
+ <h4><b><u> {{ course.name }} by {{ course.creator.get_full_name }}</u></b></h4>
</div>
<div class="col-md-4">
{% if user in course.requests.all %} <span class="label label-warning">Request Pending </span>
diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html
index 0bfd00e..aa37703 100644
--- a/yaksh/templates/yaksh/user_data.html
+++ b/yaksh/templates/yaksh/user_data.html
@@ -108,7 +108,7 @@ User IP address: {{ paper.user_ip }}
{% else %}
<h5>Student answer: </h5>
{% for answer in answers %}
- {% if not answer.skipped %}
+
{% if answer.answer.correct %}
<div class="panel panel-success">
<div class="panel-heading">Correct answer
@@ -124,7 +124,7 @@ User IP address: {{ paper.user_ip }}
</div>
<div class="panel-body"><pre><code>{{ answer.answer.answer.strip }}</code></pre></div>
</div>
- {% endif %}
+
{% endfor %}
{% endif %}
<hr>
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index ada5a65..f0bb310 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -668,6 +668,7 @@ class AnswerPaperTestCases(unittest.TestCase):
current_question = self.answerpaper.current_question()
self.assertEqual(current_question.summary, "Question1")
# Test completed_question() method of Answer Paper
+
question = self.answerpaper.completed_question(self.question1.id)
self.assertEqual(self.answerpaper.questions_left(), 2)
@@ -700,6 +701,7 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertTrue(next_question_id is not None)
+
self.assertEqual(next_question_id.summary, "Question2")
# Given, last question in the list
@@ -710,6 +712,7 @@ class AnswerPaperTestCases(unittest.TestCase):
# Then
self.assertTrue(next_question_id is not None)
+
self.assertEqual(next_question_id.summary, "Question2")
# Test get_questions_answered() method
@@ -730,6 +733,7 @@ class AnswerPaperTestCases(unittest.TestCase):
# Test completed_question and next_question
# When all questions are answered
+
current_question = self.answerpaper.completed_question(self.question2.id)
# Then
diff --git a/yaksh/views.py b/yaksh/views.py
index d067393..6929ced 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -402,7 +402,7 @@ def start(request, questionpaper_id=None, attempt_num=None):
@login_required
-def show_question(request, question, paper, error_message=None):
+def show_question(request, question, paper, error_message=None, notification=None):
"""Show a question if possible."""
user = request.user
if not question:
@@ -414,10 +414,14 @@ def show_question(request, question, paper, error_message=None):
if paper.time_left() <= 0:
reason='Your time is up!'
return complete(request, reason, paper.attempt_number, paper.question_paper.id)
+ if question in paper.questions_answered.all():
+ notification = 'You have already attempted this question successfully' \
+ if question.type == "code" else \
+ 'You have already attempted this question'
test_cases = question.get_test_cases()
files = FileUpload.objects.filter(question_id=question.id, hide=False)
context = {'question': question, 'paper': paper, 'error_message': error_message,
- 'test_cases': test_cases, 'files': files,
+ 'test_cases': test_cases, 'files': files, 'notification': notification,
'last_attempt': question.snippet.encode('unicode-escape')}
answers = paper.get_previous_answers(question)
if answers:
@@ -434,9 +438,6 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None):
paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num,
question_paper=questionpaper_id)
question = get_object_or_404(Question, pk=q_id)
- if question in paper.questions_answered.all():
- next_q = paper.next_question(q_id)
- return show_question(request, next_q, paper)
if request.method == 'POST' and question.type == 'code':
user_code = request.POST.get('answer')
@@ -447,8 +448,6 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None):
paper.answers.add(new_answer)
if next_q is not None:
next_q = get_object_or_404(Question, pk=next_q)
- if next_q not in paper.questions_unanswered.all():
- return show_question(request, question, paper)
else:
next_q = paper.next_question(q_id)
return show_question(request, next_q, paper)
@@ -461,9 +460,6 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num,
question_paper=questionpaper_id)
current_question = get_object_or_404(Question, pk=q_id)
- if current_question in paper.questions_answered.all():
- next_q = paper.next_question(q_id)
- return show_question(request, next_q, paper)
if request.method == 'POST':
snippet_code = request.POST.get('snippet')
@@ -497,14 +493,14 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
assign.assignmentFile = request.FILES['assignment']
assign.save()
user_answer = 'ASSIGNMENT UPLOADED'
- next_q = paper.completed_question(current_question.id)
+ next_q = paper.add_completed_question(current_question.id)
return show_question(request, next_q, paper)
else:
user_code = request.POST.get('answer')
user_answer = snippet_code + "\n" + user_code if snippet_code else user_code
if not user_answer:
msg = ["Please submit a valid option or code"]
- return show_question(request, current_question, paper, msg)
+ return show_question(request, current_question, paper, notification=msg)
new_answer = Answer(question=current_question, answer=user_answer,
correct=False, error=json.dumps([]))
new_answer.save()
@@ -526,15 +522,16 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
new_answer.correct = result.get('success')
error_message = None
new_answer.error = json.dumps(result.get('error'))
- next_question = paper.completed_question(current_question.id)
+ next_question = paper.add_completed_question(current_question.id)
else:
new_answer.marks = (current_question.points * result['weight'] /
current_question.get_maximum_test_case_weight()) \
if current_question.partial_grading and current_question.type == 'code' else 0
- error_message = result.get('error')
+ error_message = result.get('error') if current_question.type == 'code' \
+ else None
new_answer.error = json.dumps(result.get('error'))
next_question = current_question if current_question.type == 'code' \
- else paper.completed_question(current_question.id)
+ else paper.add_completed_question(current_question.id)
new_answer.save()
paper.update_marks('inprogress')
paper.set_end_time(timezone.now())
@@ -792,7 +789,7 @@ def ajax_questions_filter(request):
"""Ajax call made when filtering displayed questions."""
user = request.user
- filter_dict = {"user_id": user.id}
+ filter_dict = {"user_id": user.id, "active": True}
question_type = request.POST.get('question_type')
marks = request.POST.get('marks')
language = request.POST.get('language')
@@ -816,7 +813,10 @@ def _get_questions(user, question_type, marks):
if question_type is None and marks is None:
return None
if question_type:
- questions = Question.objects.filter(type=question_type, user=user)
+ questions = Question.objects.filter(type=question_type,
+ user=user,
+ active=True
+ )
if marks:
questions = questions.filter(points=marks)
return questions