* Questions can be given a category
* Success rate for each category can be monitored on a progress page
* Explanation for each question result can be given
+* Pass marks can be set
* Multiple choice question type
* True/False question type
+* Custom message displayed for those that pass or fail a quiz
+

self.incorrect_questions += str(question.id) + ","
self.save()
+ @property
def get_incorrect_questions(self):
"""
- Returns a list of non empty strings
+ Returns a list of non empty integers, representing the pk of
+ questions
"""
- return filter(None, self.incorrect_questions.split(','))
+ return [int(q) for q in self.incorrect_questions.split(',') if q]
@property
def check_if_passed(self):
--- /dev/null
+{% extends 'base.html' %}
+{% block title %}
+Result of {{ sitting.quiz.title }} for {{ sitting.user }}
+{% endblock %}
+
+{% block content %}
+<h2>Quiz title: {{ sitting.quiz.title }}</h2>
+<h3>Category: {{ sitting.quiz.category }}</h3>
+<p>{{ sitting.quiz.description }}</p>
+<hr>
+<p>User: {{ sitting.user }}</p>
+<p>Score: {{ sitting.get_percent_correct }}%</p>
+
+<table class="table table-bordered table-striped">
+
+ <thead>
+ <tr>
+ <th>Question</th>
+ <th></th>
+ </tr>
+ </thead>
+
+ <tbody>
+{% for question in questions %}
+
+ <tr>
+ <td>{{ question.content }}</td>
+ <td>
+ {% if question.id in incorrect %}
+ <p>incorrect</p>
+ {% else %}
+ <p>correct</p>
+ {% endif %}
+ </td>
+ </tr>
+
+{% endfor %}
+
+ </tbody>
+
+</table>
+{% endblock %}
--- /dev/null
+{% extends 'base.html' %}
+{% block title %}All Quizzes{% endblock %}
+
+{% block content %}
+<h2>List of complete exams</h2>
+ {% if sitting_list %}
+
+ <table class="table table-bordered table-striped">
+
+ <thead>
+ <tr>
+ <th>User</th>
+ <th>Quiz</th>
+ <th>Score (%)</th>
+ <th></th>
+ </tr>
+ </thead>
+
+ <tbody>
+
+ {% for sitting in sitting_list %}
+
+ <tr>
+ <td>{{ sitting.user }}</td>
+ <td>{{ sitting.quiz }}</td>
+ <td>{{ sitting.get_current_score }}</td>
+ <td>
+ <a href="{% url 'quiz_marking_detail' pk=sitting.id %}">
+ View details
+ </a>
+ </tr>
+
+ {% endfor %}
+
+ </tbody>
+
+ </table>
+ {% else %}
+ <p>There are no complete quizzes.</p>
+ {% endif %}
+{% endblock %}
if the answer is incorrect, informs the user
"""
answers = question.get_answers()
- incorrect_list = context.get('incorrect_questions', '')
- if str(question.id) in incorrect_list:
+ incorrect_list = context.get('incorrect_questions', [])
+ if question.id in incorrect_list:
user_was_incorrect = True
else:
user_was_incorrect = False
self.assertEqual(self.sitting.result_message, 'Well done')
def test_incorrect_and_complete(self):
- self.assertEqual(self.sitting.get_incorrect_questions(), [])
+ self.assertEqual(self.sitting.get_incorrect_questions, [])
self.sitting.add_incorrect_question(self.question1)
- self.assertIn('1', self.sitting.get_incorrect_questions())
+ self.assertIn(1, self.sitting.get_incorrect_questions)
question3 = TF_Question.objects.create(id=3,
content='oink')
self.sitting.add_incorrect_question(question3)
- self.assertIn('3', self.sitting.get_incorrect_questions())
+ self.assertIn(3, self.sitting.get_incorrect_questions)
f_test = self.sitting.add_incorrect_question(self.quiz1)
self.assertEqual(f_test, False)
- self.assertNotIn('test', self.sitting.get_incorrect_questions())
+ self.assertNotIn('test', self.sitting.get_incorrect_questions)
self.assertEqual(self.sitting.complete, False)
self.sitting.mark_quiz_complete()
score, possible = anon_session_score(request.session)
self.assertEqual((score, possible), (0.5, 2))
+ def test_paper_marking_view(self):
+ student = User.objects.create_user(username='luke',
+ email='luke@rebels.com',
+ password='top_secret')
+ teacher = User.objects.create_user(username='yoda',
+ email='yoda@jedis.com',
+ password='use_d@_force')
+ question1 = MCQuestion.objects.create(id=1, content='squawk')
+ question1.quiz.add(self.quiz1)
+ sitting1 = Sitting.objects.new_sitting(student, self.quiz1)
+ sitting2 = Sitting.objects.new_sitting(student, self.quiz2)
+ sitting1.complete = True
+ sitting1.incorrect_questions = '1'
+ sitting1.save()
+ sitting2.complete = True
+ sitting2.save()
+
+ response = self.client.get('/q/marking/')
+ self.assertRedirects(response, 'accounts/login/?next=/q/marking/',
+ status_code=302, target_status_code=404 or 200)
+
+ self.client.login(username='yoda', password='use_d@_force')
+ response = self.client.get('/q/marking/')
+ self.assertContains(response, 'test quiz 1')
+
+ response = self.client.get('/q/marking/1/')
+ self.assertContains(response, 'test quiz 1')
+ self.assertContains(response, 'squawk')
+ self.assertContains(response, 'incorrect')
+
class TestQuestionViewsAnon(TestCase):
'{% correct_answer_for_all question %}')
context = Context({'question': self.question1,
- 'incorrect_questions': '1,'})
+ 'incorrect_questions': [1]})
self.assertTemplateUsed('correct_answer.html')
self.assertIn('bing', template.render(context))
from django.conf.urls import patterns, url
from .views import QuizListView, CategoriesListView,\
- ViewQuizListByCategory, QuizUserProgressView, QuizDetailView, QuizTake
+ ViewQuizListByCategory, QuizUserProgressView, QuizMarkingList,\
+ QuizMarkingDetail, QuizDetailView, QuizTake
urlpatterns = patterns('',
view=QuizUserProgressView.as_view(),
name='quiz_progress'),
+ url(regex=r'^marking/$',
+ view=QuizMarkingList.as_view(),
+ name='quiz_marking'),
+
+ url(regex=r'^marking/(?P<pk>[\d.]+)/$',
+ view=QuizMarkingDetail.as_view(),
+ name='quiz_marking_detail'),
+
# passes variable 'quiz_name' to quiz_take view
url(regex=r'^(?P<slug>[\w-]+)/$',
view=QuizDetailView.as_view(),
return context
+class QuizMarkingList(ListView):
+ model = Sitting
+
+ @method_decorator(login_required)
+ def dispatch(self, request, *args, **kwargs):
+ return super(QuizMarkingList, self).dispatch(request, *args, **kwargs)
+
+ def get_queryset(self):
+ queryset = super(QuizMarkingList, self).get_queryset()
+ return queryset.filter(complete=True)
+
+
+class QuizMarkingDetail(DetailView):
+ model = Sitting
+
+ @method_decorator(login_required)
+ def dispatch(self, request, *args, **kwargs):
+ return super(QuizMarkingDetail, self)\
+ .dispatch(request, *args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ context = super(QuizMarkingDetail, self).get_context_data(**kwargs)
+ context['questions'] = context['object'].quiz.question_set.all()
+ context['incorrect'] = context['object'].get_incorrect_questions
+ return context
+
+
class QuizTake(FormView):
form_class = QuestionForm
template_name = 'question.html'
def final_result_user(request, sitting, quiz, previous):
score = sitting.get_current_score
- incorrect = sitting.get_incorrect_questions()
+ incorrect = sitting.get_incorrect_questions
max_score = quiz.get_max_score
percent = sitting.get_percent_correct