summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--exam/admin.py5
-rw-r--r--exam/models.py48
-rw-r--r--exam/views.py60
-rw-r--r--templates/exam/complete.html3
-rw-r--r--templates/exam/question.html38
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