summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--testapp/exam/models.py65
-rw-r--r--testapp/exam/templates/exam/complete.html27
-rw-r--r--testapp/exam/templates/exam/question.html5
-rw-r--r--testapp/exam/templates/exam/quit.html29
-rw-r--r--testapp/exam/views.py85
-rw-r--r--testapp/exam/xmlrpc_clients.py1
6 files changed, 154 insertions, 58 deletions
diff --git a/testapp/exam/models.py b/testapp/exam/models.py
index c5043dc..e5a51af 100644
--- a/testapp/exam/models.py
+++ b/testapp/exam/models.py
@@ -1,6 +1,7 @@
import datetime
import json
from random import sample, shuffle
+from itertools import islice, cycle
from django.db import models
from django.contrib.auth.models import User
from taggit_autocomplete_modified.managers import TaggableManagerAutocomplete\
@@ -24,7 +25,7 @@ languages = (
("cpp", "C++ Language"),
("java", "Java Language"),
("scilab", "Scilab"),
- )
+ )
question_types = (
@@ -32,7 +33,7 @@ question_types = (
("mcc", "Multiple Correct Choices"),
("code", "Code"),
("upload", "Assignment Upload"),
- )
+ )
attempts = [(i, i) for i in range(1, 6)]
attempts.append((-1, 'Infinite'))
days_between_attempts = ((j, j) for j in range(401))
@@ -319,54 +320,66 @@ class AnswerPaper(models.Model):
def current_question(self):
"""Returns the current active question to display."""
- qs = self.questions.split('|')
- if len(qs) > 0:
- return qs[0]
+ qu = self.get_unanswered_questions()
+ if len(qu) > 0:
+ return qu[0]
else:
return ''
def questions_left(self):
"""Returns the number of questions left."""
- qs = self.questions
- if len(qs) == 0:
+ qu = self.get_unanswered_questions()
+ if len(qu) == 0:
return 0
else:
- return qs.count('|') + 1
+ return qu.count('|') + 1
+
+ def get_unanswered_questions(self):
+ """Returns the list of unanswered questions."""
+ qa = self.questions_answered.split('|')
+ qs = self.questions.split('|')
+ qu = [q for q in qs if q not in qa]
+ return qu
def completed_question(self, question_id):
"""
- Removes the completed question from the list of questions and
- returns the next question.
+ Adds the completed question to the list of answered
+ questions and returns the next question.
"""
qa = self.questions_answered
if len(qa) > 0:
self.questions_answered = '|'.join([qa, str(question_id)])
else:
self.questions_answered = str(question_id)
- qs = self.questions.split('|')
- qs.remove(unicode(question_id))
- self.questions = '|'.join(qs)
self.save()
- if len(qs) == 0:
- return ''
- else:
- return qs[0]
- def skip(self):
+ return self.skip(question_id)
+
+ def skip(self, question_id):
"""
- Skips the current question and returns the next available question.
+ Skips the current question and returns the next sequentially
+ available question.
"""
+ qu = self.get_unanswered_questions()
qs = self.questions.split('|')
- if len(qs) == 0:
+
+ if len(qu) == 0:
return ''
- else:
- # Put head at the end.
- head = qs.pop(0)
- qs.append(head)
- self.questions = '|'.join(qs)
- self.save()
+
+ try:
+ q_index = qs.index(unicode(question_id))
+ except ValueError:
return qs[0]
+ start = q_index + 1
+ stop = q_index + 1 + len(qs)
+ q_list = islice(cycle(qs), start, stop)
+ for next_q in q_list:
+ if next_q in qu:
+ return next_q
+
+ return qs[0]
+
def time_left(self):
"""Return the time remaining for the user in seconds."""
dt = datetime.datetime.now() - self.start_time.replace(tzinfo=None)
diff --git a/testapp/exam/templates/exam/complete.html b/testapp/exam/templates/exam/complete.html
index 1d5df3c..08abe76 100644
--- a/testapp/exam/templates/exam/complete.html
+++ b/testapp/exam/templates/exam/complete.html
@@ -5,8 +5,29 @@
{% block pagetitle %}Online Test{% endblock %}
{% block content %}
{% csrf_token %}
- <center><h2> Good bye! </h2></center>
- <center><h4> {{message}} </h4></center>
- <center><h4>You may now close the browser.</h4></center><br>
+ {% if submitted or unattempted %}
+ <br><center><table class="bordered-table zebra-striped span8"
+ style="text-align:left;">
+ <tr><td><b>Submitted Questions</b></td>
+ <td>
+ {% if submitted %}
+ {{ submitted|join:", " }}
+ {% else %}
+ <p><b>No Questions have been Submitted</b></p>
+ {% endif %}
+ </td></tr>
+ <tr><td><b>Unattempted Questions</b></td>
+ <td>
+ {% if unattempted %}
+ {{ unattempted|join:", " }}
+ {% else %}
+ <p><b>All Questions have been Submitted</b></p>
+ {% endif %}
+ </td></tr>
+ </table></center>
+ {% endif %}
+ <center><h2> Good bye! </h2></center>
+ <center><h4> {{message}} </h4></center>
+ <br><center><h4>You may now close the browser.</h4></center><br>
<center><a href="{{URL_ROOT}}/exam/"> Login Again </a></center>
{% endblock content %}
diff --git a/testapp/exam/templates/exam/question.html b/testapp/exam/templates/exam/question.html
index eac6792..b7a7053 100644
--- a/testapp/exam/templates/exam/question.html
+++ b/testapp/exam/templates/exam/question.html
@@ -104,7 +104,6 @@ function call_skip(url)
</div>
</div>
</div>
-
<div class = container>
<div class="sidebar">
<p>Question Navigator </p>
@@ -179,7 +178,9 @@ function call_skip(url)
{% else %}
<button class="btn" type="submit" name="check" id="check" onClick="submitCode();">Check Answer</button>&nbsp;&nbsp;
{% endif %}
- <button class="btn" type="submit" name="skip" id="skip">Skip Question</button>
+ {% if to_attempt|length != 1 %}
+ <button class="btn" type="submit" name="skip" id="skip">Attempt Later</button>
+ {% endif %}
</form>
diff --git a/testapp/exam/templates/exam/quit.html b/testapp/exam/templates/exam/quit.html
index f49b62f..91bce64 100644
--- a/testapp/exam/templates/exam/quit.html
+++ b/testapp/exam/templates/exam/quit.html
@@ -3,12 +3,33 @@
{% block title %}Quit exam {% endblock %}
{% block pagetitle %}Online Test {% endblock %}
{% block content %}
+ {% if submitted or unattempted %}
+ <br><center><table class="bordered-table zebra-striped span8"
+ style="text-align:left;">
+ <tr><td><b>Submitted Questions</b></td>
+ <td>
+ {% if submitted %}
+ {{ submitted|join:", " }}
+ {% else %}
+ <p><b>No Questions have been Submitted</b></p>
+ {% endif %}
+ </td></tr>
+ <tr><td><b>Unattempted Questions</b></td>
+ <td>
+ {% if unattempted %}
+ {{ unattempted|join:", " }}
+ {% else %}
+ <p><b>All Questions have been Submitted</b></p>
+ {% endif %}
+ </td></tr>
+ </table></center>
+ {% endif %}
- <center><h4>Your current answers are saved.</h4></center>
+ <center><h4>Your current answers are saved.</h4></center>
<center><h4> Are you sure you wish to quit the exam?</h4></center>
- <center><h4> Be sure, as you won't be able to restart this exam.</h4></center>
- <form action="{{URL_ROOT}}/exam/complete/{{ attempt_num }}/{{ id }}/" method="post">
+ <center><h4> Be sure, as you won't be able to restart this exam.</h4></center>
+ <form action="{{URL_ROOT}}/exam/complete/{{ attempt_num }}/{{ id }}/" method="post">
{% csrf_token %}
- <center><button class="btn" type="submit" name="yes">Yes!</button>&nbsp;<button class="btn" type="button" name="no" onClick="window.location='{{ URL_ROOT }}/exam/start/{{ attempt_num }}/{{ id }}/'">No!</button></center>
+ <center><button class="btn" type="submit" name="yes">Yes!</button>&nbsp;<button class="btn" type="button" name="no" onClick="window.location='{{ URL_ROOT }}/exam/start/{{ attempt_num }}/{{ id }}/'">No!</button></center>
</form>
{% endblock content %}
diff --git a/testapp/exam/views.py b/testapp/exam/views.py
index 0ca404d..69d16d7 100644
--- a/testapp/exam/views.py
+++ b/testapp/exam/views.py
@@ -803,22 +803,18 @@ def get_questions(paper):
all_questions = []
questions = {}
if paper.questions:
- to_attempt = (paper.questions).split('|')
+ all_questions = (paper.questions).split('|')
if paper.questions_answered:
- submitted = (paper.questions_answered).split('|')
- if not to_attempt:
- submitted.sort()
- all_questions = submitted
- if not submitted:
- to_attempt.sort()
- all_questions = to_attempt
- if to_attempt and submitted:
- q_append = to_attempt + submitted
- q_append.sort()
- all_questions = q_append
- for num, value in enumerate(all_questions, 1):
- questions[value] = num
- questions = collections.OrderedDict(sorted(questions.items()))
+ q_answered = (paper.questions_answered).split('|')
+ q_answered.sort()
+ submitted = q_answered
+ if paper.get_unanswered_questions():
+ q_unanswered = paper.get_unanswered_questions()
+ q_unanswered.sort()
+ to_attempt = q_unanswered
+ for index, value in enumerate(all_questions, 1):
+ questions[value] = index
+ questions = collections.OrderedDict(sorted(questions.items(), key=lambda x:x[1]))
return questions, to_attempt, submitted
@@ -909,7 +905,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
paper = AnswerPaper.objects.get(user=request.user, attempt_number=attempt_num,
question_paper=q_paper)
if q_id in paper.questions_answered:
- next_q = paper.skip()
+ next_q = paper.skip(q_id)
return show_question(request, next_q, attempt_num, questionpaper_id)
if not user.is_authenticated() or paper.end_time < datetime.datetime.now():
@@ -927,7 +923,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
if question.type == 'code':
old_skipped = paper.answers.filter(question=question, skipped=True)
_save_skipped_answer(old_skipped, user_code, paper, question)
- next_q = paper.skip()
+ next_q = paper.skip(q_id)
return show_question(request, next_q, attempt_num, questionpaper_id)
# Add the answer submitted, regardless of it being correct or not.
@@ -989,8 +985,8 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
context = {'question': question, 'error_message': result.get('error'),
'paper': paper, 'last_attempt': user_code,
'quiz_name': paper.question_paper.quiz.description,
- 'time_left': time_left, 'to_attempt': to_attempt,
- 'submitted': submitted}
+ 'time_left': time_left, 'questions': questions,
+ 'to_attempt': to_attempt, 'submitted': submitted}
ci = RequestContext(request)
return my_render_to_response('exam/question.html', context,
@@ -999,6 +995,20 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
if time_left <= 0:
reason = 'Your time is up!'
return complete(request, reason, attempt_num, questionpaper_id)
+
+ # Display the same question if user_answer is None
+ elif not user_answer:
+ msg = "Please submit a valid option or code"
+ time_left = paper.time_left()
+ questions, to_attempt, submitted = get_questions(paper)
+ context = {'question': question, 'error_message': msg,
+ 'paper': paper, 'quiz_name': paper.question_paper.quiz.description,
+ 'time_left': time_left, 'questions': questions,
+ 'to_attempt': to_attempt, 'submitted': submitted}
+ ci = RequestContext(request)
+
+ return my_render_to_response('exam/question.html', context,
+ context_instance=ci)
else:
next_q = paper.completed_question(question.id)
return show_question(request, next_q, attempt_num,
@@ -1037,11 +1047,34 @@ def validate_answer(user, user_answer, question, json_data=None):
return correct, result
+def get_question_labels(request, attempt_num=None, questionpaper_id=None):
+ """Get the question number show in template for corresponding
+ question id."""
+ unattempted_questions = []
+ submitted_questions = []
+ try:
+ q_paper = QuestionPaper.objects.get(id=questionpaper_id)
+ paper = AnswerPaper.objects.get(
+ user=request.user, attempt_number=attempt_num, question_paper=q_paper)
+ except AnswerPaper.DoesNotExist:
+ return my_redirect('/exam/start/')
+ questions, to_attempt, submitted = get_questions(paper)
+ for q_id, question_label in questions.items():
+ if q_id in to_attempt:
+ unattempted_questions.append(question_label)
+ else:
+ submitted_questions.append(question_label)
+ unattempted_questions.sort()
+ submitted_questions.sort()
+ return unattempted_questions, submitted_questions
def quit(request, attempt_num=None, questionpaper_id=None):
"""Show the quit page when the user logs out."""
- context = {'id': questionpaper_id,
- 'attempt_num': attempt_num}
+ unattempted_questions, submitted_questions = get_question_labels(request,
+ attempt_num, questionpaper_id)
+ context = {'id': questionpaper_id, 'attempt_num': attempt_num,
+ 'unattempted': unattempted_questions,
+ 'submitted': submitted_questions}
return my_render_to_response('exam/quit.html', context,
context_instance=RequestContext(request))
@@ -1056,6 +1089,8 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None):
context = {'message': message}
return my_render_to_response('exam/complete.html', context)
else:
+ unattempted_questions, submitted_questions = get_question_labels(request,
+ attempt_num, questionpaper_id)
q_paper = QuestionPaper.objects.get(id=questionpaper_id)
paper = AnswerPaper.objects.get(user=user, question_paper=q_paper,
attempt_number=attempt_num)
@@ -1071,11 +1106,15 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None):
context = {'message': "Hurray ! You did an excellent job.\
you answered all the questions correctly.\
You have been logged out successfully,\
- Thank You !"}
+ Thank You !",
+ 'unattempted': unattempted_questions,
+ 'submitted': submitted_questions}
return my_render_to_response('exam/complete.html', context)
else:
message = reason or "You are successfully logged out"
- context = {'message': message}
+ context = {'message': message,
+ 'unattempted': unattempted_questions,
+ 'submitted': submitted_questions}
return my_render_to_response('exam/complete.html', context)
no = False
message = reason or 'The quiz has been completed. Thank you.'
diff --git a/testapp/exam/xmlrpc_clients.py b/testapp/exam/xmlrpc_clients.py
index 8f5642e..3a3c0c6 100644
--- a/testapp/exam/xmlrpc_clients.py
+++ b/testapp/exam/xmlrpc_clients.py
@@ -2,6 +2,7 @@ from xmlrpclib import ServerProxy
import time
import random
import socket
+import json
from settings import SERVER_PORTS, SERVER_POOL_PORT