summaryrefslogtreecommitdiff
path: root/exam
diff options
context:
space:
mode:
Diffstat (limited to 'exam')
-rw-r--r--exam/__init__.py0
-rw-r--r--exam/admin.py4
-rw-r--r--exam/forms.py15
-rw-r--r--exam/models.py102
-rw-r--r--exam/tests.py16
-rw-r--r--exam/urls.py9
-rw-r--r--exam/views.py150
7 files changed, 296 insertions, 0 deletions
diff --git a/exam/__init__.py b/exam/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/exam/__init__.py
diff --git a/exam/admin.py b/exam/admin.py
new file mode 100644
index 0000000..d40a7d6
--- /dev/null
+++ b/exam/admin.py
@@ -0,0 +1,4 @@
+from exam.models import Question
+from django.contrib import admin
+
+admin.site.register(Question) \ No newline at end of file
diff --git a/exam/forms.py b/exam/forms.py
new file mode 100644
index 0000000..bd5732e
--- /dev/null
+++ b/exam/forms.py
@@ -0,0 +1,15 @@
+from django import forms
+from exam.models import Profile
+
+class UserRegisterForm(forms.ModelForm):
+
+ first_name = forms.CharField(max_length=30)
+ last_name = forms.CharField(max_length=30)
+ roll_number = forms.CharField(max_length=30)
+ #email_address = forms.EmailField()
+ #password = forms.CharField(max_length=30, widget=forms.PasswordInput())
+
+ class Meta:
+ model = Profile
+ fields = ['first_name', 'last_name', 'roll_number']
+
diff --git a/exam/models.py b/exam/models.py
new file mode 100644
index 0000000..247b362
--- /dev/null
+++ b/exam/models.py
@@ -0,0 +1,102 @@
+from django.db import models
+from django.contrib.auth.models import User
+
+# Create your models here.
+
+class Profile(models.Model):
+ """Profile for a user to store roll number etc."""
+ 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.
+ summary = models.CharField(max_length=256)
+ # The question text.
+ question = models.TextField()
+
+ # Number of points for the question.
+ points = models.IntegerField()
+
+ # Test cases for the question in the form of code that is run.
+ # This is simple Python code.
+ test = models.TextField()
+
+ def __unicode__(self):
+ return self.summary
+
+
+class Answer(models.Model):
+ """Answers submitted by users.
+ """
+ question = models.ForeignKey(Question)
+ # The last answer submitted by the user.
+ answer = models.TextField()
+ attempts = models.IntegerField()
+
+ # Is the question correct.
+ correct = models.BooleanField()
+ # Marks obtained.
+ marks = models.IntegerField()
+
+ def __unicode__(self):
+ return self.answer
+
+class Quiz(models.Model):
+ """A quiz for a student.
+ """
+ user = models.ForeignKey(User)
+ user_ip = models.CharField(max_length=15)
+ key = models.CharField(max_length=10)
+ questions = models.CharField(max_length=128)
+ questions_answered = models.CharField(max_length=128)
+
+ def current_question(self):
+ """Returns the current active question to display."""
+ qs = self.questions.split('|')
+ if len(qs) > 0:
+ return qs[0]
+ else:
+ return ''
+
+ def questions_left(self):
+ """Returns the number of questions left."""
+ qs = self.questions
+ if len(qs) == 0:
+ return 0
+ else:
+ return qs.count('|') + 1
+
+ def answered_question(self, question_id):
+ """Removes the question from the list of questions and returns
+ the next."""
+ 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):
+ """Skip the current question and return the next available question."""
+ qs = self.questions.split('|')
+ if len(qs) == 0:
+ return ''
+ else:
+ # Put head at the end.
+ head = qs.pop(0)
+ qs.append(head)
+ self.questions = '|'.join(qs)
+ self.save()
+ return qs[0]
+
+ def __unicode__(self):
+ u = self.user
+ return u'Quiz for {0} {1}'.format(u.first_name, u.last_name)
diff --git a/exam/tests.py b/exam/tests.py
new file mode 100644
index 0000000..501deb7
--- /dev/null
+++ b/exam/tests.py
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.assertEqual(1 + 1, 2)
diff --git a/exam/urls.py b/exam/urls.py
new file mode 100644
index 0000000..7922255
--- /dev/null
+++ b/exam/urls.py
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import patterns, include, url
+
+urlpatterns = patterns('exam.views',
+ url(r'^$', 'index'),
+ url(r'^start/$', 'start'),
+ url(r'^complete/$', 'complete'),
+ url(r'^(?P<q_id>\d+)/$', 'question'),
+ url(r'^(?P<q_id>\d+)/check/$', 'check'),
+)
diff --git a/exam/views.py b/exam/views.py
new file mode 100644
index 0000000..10d6c9a
--- /dev/null
+++ b/exam/views.py
@@ -0,0 +1,150 @@
+import random
+import sys
+import traceback
+import string
+
+from django.db import IntegrityError
+from django.contrib.auth.models import User
+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, Quiz, Profile
+from exam.forms import UserRegisterForm
+
+def gen_key(no_of_chars):
+ allowed_chars = string.digits+string.uppercase
+ return ''.join([random.choice(allowed_chars) for i in range(no_of_chars)])
+
+def index_old(request):
+ """The start page.
+ """
+ question_list = Question.objects.all()
+ context = {'question_list': question_list}
+ return render_to_response('exam/index.html', context)
+
+def index(request):
+ """The start page.
+ """
+ # Largely copied from Nishanth's quiz app.
+ user = request.user
+ if user.is_authenticated():
+ return redirect("/exam/start/")
+ else:
+ try:
+ ip = request.META['REMOTE_ADDR']
+ Quiz.objects.get(user_ip=ip)
+ return redirect("/exam/complete")
+ except Quiz.DoesNotExist:
+ pass
+
+ if request.method == "POST":
+ form = UserRegisterForm(request.POST)
+ if form.is_valid():
+ data = form.cleaned_data
+
+ while True:
+ try:
+ username = gen_key(20)
+ new_user = User.objects.create_user(username, "temp@temp.com", "123")
+ break
+ except IntegrityError:
+ pass
+
+ new_user.first_name = data['first_name']
+ new_user.last_name = data['last_name']
+ new_user.save()
+
+ new_profile = Profile(user=new_user)
+ new_profile.roll_number = data['roll_number']
+ new_profile.save()
+
+ user = authenticate(username=username, password="123")
+ login(request, user)
+ return redirect("/exam/start/")
+
+ else:
+ return render_to_response('exam/register.html',{'form':form},
+ context_instance=RequestContext(request))
+ else:
+ form = UserRegisterForm()
+ return render_to_response('exam/register.html',{'form':form},
+ context_instance=RequestContext(request))
+
+def show_question(request, q_id):
+ if len(q_id) == 0:
+ return redirect("/exam/complete")
+ else:
+ return question(request, q_id)
+
+def start(request):
+ user = request.user
+ try:
+ old_quiz = Quiz.objects.get(user=user)
+ q = old_quiz.current_question()
+ return show_question(request, q)
+ except Quiz.DoesNotExist:
+ ip = request.META['REMOTE_ADDR']
+ key = gen_key(10)
+ new_quiz = Quiz(user=user, user_ip=ip, key=key)
+
+ questions = [ str(_.id) for _ in Question.objects.all() ]
+ random.shuffle(questions)
+ questions = questions[:3]
+
+ new_quiz.questions = "|".join(questions)
+ new_quiz.save()
+ q = new_quiz.current_question()
+
+ return show_question(request, q)
+
+def question(request, q_id):
+ q = get_object_or_404(Question, pk=q_id)
+ try:
+ quiz = Quiz.objects.get(user=request.user)
+ except Quiz.DoesNotExist:
+ redirect('/exam/start')
+ context = {'question': q, 'quiz': quiz}
+ ci = RequestContext(request)
+ return render_to_response('exam/question.html', context,
+ context_instance=ci)
+
+def test_answer(func_code, test_code):
+ exec func_code
+ exec test_code
+
+def check(request, q_id):
+ user = request.user
+ question = get_object_or_404(Question, pk=q_id)
+ quiz = Quiz.objects.get(user=user)
+ answer = request.POST.get('answer')
+ skip = request.POST.get('skip', None)
+
+ if skip is not None:
+ next_q = quiz.skip()
+ return show_question(request, next_q)
+
+ # Otherwise we were asked to check.
+ retry = True
+ try:
+ test_answer(answer, question.test)
+ except:
+ type, value, tb = sys.exc_info()
+ info = traceback.extract_tb(tb)
+ fname, lineno, func, text = info[-1]
+ err = "{0}: {1} In code: {2}".format(type.__name__, str(value), text)
+ else:
+ retry = False
+ err = 'Correct answer'
+
+ ci = RequestContext(request)
+ if retry:
+ context = {'question': question, 'error_message': err}
+ return render_to_response('exam/question.html', context,
+ context_instance=ci)
+ else:
+ next_q = quiz.answered_question(question.id)
+ return show_question(request, next_q)
+
+def complete(request):
+ return render_to_response('exam/complete.html')
+ \ No newline at end of file