diff options
-rw-r--r-- | exam/admin.py | 5 | ||||
-rw-r--r-- | exam/models.py | 48 | ||||
-rw-r--r-- | exam/views.py | 60 | ||||
-rw-r--r-- | templates/exam/complete.html | 3 | ||||
-rw-r--r-- | templates/exam/question.html | 38 |
5 files changed, 126 insertions, 28 deletions
diff --git a/exam/admin.py b/exam/admin.py index d40a7d6..8482ef9 100644 --- a/exam/admin.py +++ b/exam/admin.py @@ -1,4 +1,5 @@ -from exam.models import Question +from exam.models import Question, Quiz from django.contrib import admin -admin.site.register(Question)
\ No newline at end of file +admin.site.register(Question) +admin.site.register(Quiz) diff --git a/exam/models.py b/exam/models.py index 1e8ba69..ea60f17 100644 --- a/exam/models.py +++ b/exam/models.py @@ -1,13 +1,14 @@ +import datetime from django.db import models from django.contrib.auth.models import User - +################################################################################ class Profile(models.Model): """Profile for a user to store roll number and other details.""" user = models.ForeignKey(User) roll_number = models.CharField(max_length=20) - +################################################################################ class Question(models.Model): """A question in the database.""" # An optional one-line summary of the question. @@ -26,6 +27,7 @@ class Question(models.Model): return self.summary +################################################################################ class Answer(models.Model): """Answers submitted by users. """ @@ -41,19 +43,50 @@ class Answer(models.Model): def __unicode__(self): return self.answer +################################################################################ +class Quiz(models.Model): + """A quiz that students will participate in. One can think of this + as the "examination" event. + """ + + # The starting/ending date of the quiz. + start_date = models.DateField("Date of the quiz") + # This is always in minutes. + duration = models.IntegerField("Duration of quiz in minutes", default=20) + + # Is the quiz active. The admin should deactivate the quiz once it is + # complete. + active = models.BooleanField(default=True) + + # Description of quiz. + description = models.CharField(max_length=256) + + def __unicode__(self): + desc = self.description or 'Quiz' + return '%s: on %s for %d minutes'%(desc, self.start_date, self.duration) + + +################################################################################ class QuestionPaper(models.Model): """A question paper for a student -- one per student typically. """ # The user taking this question paper. user = models.ForeignKey(User) + + # The Quiz to which this question paper is attached to. + quiz = models.ForeignKey(Quiz) + + # The time when this paper was started by the user. + start_time = models.DateTimeField() + # User's IP which is logged. user_ip = models.CharField(max_length=15) # Unused currently. key = models.CharField(max_length=10) # used to allow/stop a user from retaking the question paper. - is_active = models.BooleanField(default = True) + active = models.BooleanField(default = True) # The questions (a list of ids separated by '|') questions = models.CharField(max_length=128) @@ -108,7 +141,16 @@ class QuestionPaper(models.Model): self.questions = '|'.join(qs) self.save() return qs[0] + + def time_left(self): + """Return the time remaining for the user in seconds.""" + dt = datetime.datetime.now() - self.start_time + secs = dt.total_seconds() + total = self.quiz.duration*60.0 + remain = max(total - secs, 0) + return int(remain) def __unicode__(self): u = self.user return u'Question paper for {0} {1}'.format(u.first_name, u.last_name) + diff --git a/exam/views.py b/exam/views.py index 0c48f08..ad8a04f 100644 --- a/exam/views.py +++ b/exam/views.py @@ -3,11 +3,12 @@ import string import os import stat from os.path import dirname, pardir, abspath, join, exists +import datetime from django.contrib.auth import login, logout, authenticate from django.shortcuts import render_to_response, get_object_or_404, redirect from django.template import RequestContext -from exam.models import Question, QuestionPaper, Profile, Answer +from exam.models import Quiz, Question, QuestionPaper, Profile, Answer from exam.forms import UserRegisterForm, UserLoginForm from exam.xmlrpc_clients import python_server @@ -26,7 +27,6 @@ def get_user_dir(user): def index(request): """The start page. """ - # Largely copied from Nishanth's QuestionPaper app. user = request.user if user.is_authenticated(): return redirect("/exam/start/") @@ -82,25 +82,25 @@ def user_login(request): return render_to_response('exam/login.html', context, context_instance=RequestContext(request)) -def show_question(request, q_id): - """Show a question if possible.""" - if len(q_id) == 0: - return redirect("/exam/complete") - else: - return question(request, q_id) - def start(request): user = request.user try: - old_paper = QuestionPaper.objects.get(user=user) - if not old_paper.is_active: - return redirect("/exam/complete/") + # Right now the app is designed so there is only one active quiz + # at a particular time. + quiz = Quiz.objects.get(active=True) + except Quiz.DoesNotExist: + msg = 'No active quiz found, please contact your '\ + 'instructor/administrator. Please login again thereafter.' + return complete(request, reason=msg) + try: + old_paper = QuestionPaper.objects.get(user=user, quiz=quiz) p = old_paper.current_question() return redirect('/exam/%s'%p) except QuestionPaper.DoesNotExist: ip = request.META['REMOTE_ADDR'] key = gen_key(10) - new_paper = QuestionPaper(user=user, user_ip=ip, key=key) + new_paper = QuestionPaper(user=user, user_ip=ip, key=key, quiz=quiz) + new_paper.start_time = datetime.datetime.now() # Make user directory. user_dir = get_user_dir(user) @@ -130,11 +130,25 @@ def question(request, q_id): paper = QuestionPaper.objects.get(user=request.user) except QuestionPaper.DoesNotExist: redirect('/exam/start') - context = {'question': q, 'paper': paper, 'user': user} + time_left = paper.time_left() + if time_left == 0: + return complete(request, reason='Your time is up!') + quiz_name = paper.quiz.description + context = {'question': q, 'paper': paper, 'user': user, + 'quiz_name': quiz_name, + 'time_left': time_left} ci = RequestContext(request) return render_to_response('exam/question.html', context, context_instance=ci) +def show_question(request, q_id): + """Show a question if possible.""" + if len(q_id) == 0: + msg = 'Congratulations! You have successfully completed the quiz.' + return complete(request, msg) + else: + return question(request, q_id) + def check(request, q_id): user = request.user question = get_object_or_404(Question, pk=q_id) @@ -164,8 +178,14 @@ def check(request, q_id): ci = RequestContext(request) if not success: + time_left = paper.time_left() + if time_left == 0: + return complete(request, reason='Your time is up!') + context = {'question': question, 'error_message': err_msg, - 'paper': paper, 'last_attempt': answer} + 'paper': paper, 'last_attempt': answer, + 'time_left': time_left} + return render_to_response('exam/question.html', context, context_instance=ci) else: @@ -176,17 +196,17 @@ def quit(request): return render_to_response('exam/quit.html', context_instance=RequestContext(request)) -def complete(request): +def complete(request, reason=None): user = request.user yes = True + message = reason or 'The quiz has been completed. Thank you.' if request.method == 'POST': yes = request.POST.get('yes', None) if yes: - paper = QuestionPaper.objects.get(user=user) - paper.is_active = False - paper.save() + # Logout the user and quit with the message given. logout(request) - return render_to_response('exam/complete.html') + context = {'message': message} + return render_to_response('exam/complete.html', context) else: return redirect('/exam/') diff --git a/templates/exam/complete.html b/templates/exam/complete.html index ded38f7..bd90e6d 100644 --- a/templates/exam/complete.html +++ b/templates/exam/complete.html @@ -1,3 +1,4 @@ -<p>Exam is complete. Thank you. </p> +<h2> Good bye! </h2> +<p> {{message}} </p> <br /> <p>You may now close the browser.</p> diff --git a/templates/exam/question.html b/templates/exam/question.html index 75e5a62..2267efc 100644 --- a/templates/exam/question.html +++ b/templates/exam/question.html @@ -2,6 +2,8 @@ <script type="text/javascript"> <!-- +var time_left = {{ time_left }}; + function submitCode() { document.forms["code"].submit(); @@ -12,9 +14,36 @@ function submitCode() x.value = "Checking Answer ..."; document.getElementById("skip").disabled = true; } + +function secs_to_time(secs) +{ + var h = Math.floor(secs/3600); + var h_s = (h > 0) ? h+'h:' : ''; + var m = Math.floor((secs%3600)/60); + var m_s = (m > 0) ? m+'m:' : ''; + var s_s = Math.floor(secs%60) + 's'; + return h_s + m_s + s_s; +} + +function dec_time() +{ + time_left -= 1; + if (time_left) { + var elem = document.getElementById("time_left"); + var t_str = secs_to_time(time_left); + elem.innerHTML = "<strong> Time left: " + t_str + "</strong>"; + setTimeout("dec_time()", 1000); + } + else { + document.forms["logout"].submit(); + } +} + //--> </script> +<body onload="dec_time()"> + <p>{{ question.description }} </p> {% if error_message %}<p><strong>ERROR:</strong></p><pre>{{ error_message }}</pre>{% endif %} @@ -35,10 +64,15 @@ onclick="submitCode();"/> <input id="skip" type="submit" name="skip" value="Skip question" /> </form> -<p> {{ user.first_name.title }} {{ user.last_name.title }}, you have {{ paper.questions_left }} question(s) left. </p> +<p> {{ user.first_name.title }} {{ user.last_name.title }}, +you have {{ paper.questions_left }} question(s) left in {{ quiz_name }}.</p> + +<p id="time_left"> <strong> Time left: </strong> </p> <hr/> -<form action="/exam/quit/" method="post"> +<form id="logout" action="/exam/quit/" method="post"> {% csrf_token %} <input type="submit" name="quit" value="Quit exam and logout" /> </form> + +</body>
\ No newline at end of file |