From: Tom Walker Date: Mon, 30 Jun 2014 16:45:33 +0000 (+0100) Subject: added the option to allow a quiz to be only sat once by registered users, not at... X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=3c24e4f5658165a20ee738e5a8d6267a56fc32e7;p=django_quiz.git added the option to allow a quiz to be only sat once by registered users, not at all by anonymous users. --- diff --git a/quiz/models.py b/quiz/models.py index 8d83359..517cc23 100644 --- a/quiz/models.py +++ b/quiz/models.py @@ -14,7 +14,6 @@ from model_utils.managers import InheritanceManager If you want to prepopulate the category choices then here is an example. Uncomment 'choices' in the category model. """ - CATEGORY_CHOICES = ( ('Endocrinology', 'Endocrinology'), ('Dermatology', 'Dermatology'), ('Cellular Biology', 'Cellular Biology'), @@ -73,12 +72,12 @@ class Quiz(models.Model): blank = False,) description = models.TextField(blank = True, - help_text = "a description of the quiz",) + help_text = ("a description of the quiz"),) url = models.CharField(max_length = 60, blank = False, - help_text = "an SEO friendly url", - verbose_name = 'SEO friendly url',) + help_text = ("a user friendly url"), + verbose_name = ('user friendly url'),) category = models.ForeignKey(Category, null = True, @@ -86,19 +85,30 @@ class Quiz(models.Model): random_order = models.BooleanField(blank = False, default = False, - help_text = "Display the questions in a \ - random order or as they are set?",) + help_text = ("Display the questions in \ + a random order or as they \ + are set?"),) answers_at_end = models.BooleanField(blank = False, default = False, - help_text = "Correct answer is NOT \ - shown after question. Answers \ - displayed at end",) + help_text = ("Correct answer is NOT \ + shown after question. \ + Answers displayed at \ + the end"),) exam_paper = models.BooleanField(blank = False, default = False, - help_text = "If yes, the result of each \ - attempt by a user will be stored",) + help_text = ("If yes, the result of each \ + attempt by a user will be \ + stored"),) + + single_attempt = models.BooleanField(blank = False, + default = False, + help_text = ("If yes, only one \ + attempt by a user will \ + be permitted. Non \ + users cannot sit \ + this exam."),) def save(self, force_insert = False, force_update = False, *args, **kwargs): @@ -107,6 +117,9 @@ class Quiz(models.Model): self.url = ''.join(letter for letter in self.url if letter.isalnum() or letter == '-') + if self.single_attempt == True: + self.exam_paper = True + super(Quiz, self).save(force_insert, force_update, *args, **kwargs) @@ -140,13 +153,10 @@ class Progress(models.Model): Data stored in csv using the format: category, score, possible, category, score, possible, ... """ - user = models.OneToOneField('auth.User') # one user per progress class - # The god awful csv. Always end with a comma score = models.CommaSeparatedIntegerField(max_length = 1024) - objects = ProgressManager() class Meta: @@ -164,7 +174,6 @@ class Progress(models.Model): The dict will have one key for every category that you have defined """ - categories = Category.objects.all() score_before = self.score output = {} diff --git a/quiz/templates/result.html b/quiz/templates/result.html index c24c6d5..0da8bde 100644 --- a/quiz/templates/result.html +++ b/quiz/templates/result.html @@ -23,7 +23,7 @@ {% endif %} -{% if score %} +{% if score or max_score %}

Exam results

diff --git a/quiz/templates/single_complete.html b/quiz/templates/single_complete.html new file mode 100644 index 0000000..2c5e50c --- /dev/null +++ b/quiz/templates/single_complete.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% load quiz_tags %} + +{% block title %} {{ quiz.title }} {% endblock %} +{% block description %} {{ quiz.title }} - {{ quiz.description }} {% endblock %} + +{% block content %} + +
+ +{% if user.is_authenticated %} +

You have already sat this exam and only one sitting is permitted.

+{% else %} +

This exam is only accessible to signed in users.

+{% endif %} + +
+ +{% endblock %} diff --git a/quiz/tests.py b/quiz/tests.py index 41211b4..8bad8a5 100644 --- a/quiz/tests.py +++ b/quiz/tests.py @@ -68,6 +68,12 @@ class TestQuiz(TestCase): self.assertEqual(q5.answers_at_end, False) self.assertEqual(q5.exam_paper, True) + def test_quiz_single_attempt(self): + self.quiz1.single_attempt = True + self.quiz1.save() + + self.assertEqual(self.quiz1.exam_paper, True) + class TestProgress(TestCase): def setUp(self): @@ -203,6 +209,7 @@ class TestSitting(TestCase): self.sitting.mark_quiz_complete() self.assertEqual(self.sitting.complete, True) + ''' Tests relating to views ''' @@ -394,6 +401,16 @@ class TestQuestionViewsAnon(TestCase): self.assertEqual(self.client.session['session_score'], 1) self.assertEqual(self.client.session['session_score_possible'], 3) + def test_anon_cannot_sit_single_attempt(self): + self.quiz1.single_attempt = True + self.quiz1.save() + response = self.client.get('/q/tq1/') + + self.assertContains(response, 'accessible') + self.assertTemplateUsed('single_complete.html') + + + class TestQuestionViewsUser(TestCase): def setUp(self): @@ -526,8 +543,8 @@ class TestQuestionViewsUser(TestCase): self.assertEqual(response.context['previous'], {}) response = self.client.get('/q/tq2/', - {'guess': 456, - 'question_id': 2}) + {'guess': 'T', + 'question_id': 3}) self.assertEqual(response.context['score'], 1) self.assertEqual(response.context['max_score'], 2) @@ -547,6 +564,23 @@ class TestQuestionViewsUser(TestCase): # test that exam result can be recalled later self.assertIn(sitting, progress.show_exams()) + def test_user_cannot_sit_single_attempt(self): + self.quiz2.single_attempt = True + self.quiz2.save() + self.client.login(username='jacob', password='top_secret') + response = self.client.get('/q/tq2/', + {'guess': '123', + 'question_id': 1}) + response = self.client.get('/q/tq2/', + {'guess': 'T', + 'question_id': 3}) + + # quiz complete, trying it again + response = self.client.get('/q/tq2/') + + self.assertContains(response, 'only one sitting is permitted.') + self.assertTemplateUsed('single_complete.html') + class TestTemplateTags(TestCase): def setUp(self): @@ -621,8 +655,8 @@ class TestTemplateTags(TestCase): def test_correct_answer_all_anon(self): template = Template( '{% load quiz_tags %}' + - '{% correct_answer_for_all_with_users_incorrect ' + - 'question incorrect_questions %}') + '{% correct_answer_for_all_with_users_incorrect' + + ' question incorrect_questions %}') context = Context({'question': self.question1,}) diff --git a/quiz/views.py b/quiz/views.py index c288f02..45a98f6 100644 --- a/quiz/views.py +++ b/quiz/views.py @@ -33,6 +33,19 @@ def quiz_take(request, quiz_name): quiz = Quiz.objects.get(url = quiz_name.lower()) if request.user.is_authenticated() == True: + + if quiz.single_attempt == True: + try: + single = Sitting.objects.get(user = request.user, + quiz = quiz, + complete = True) + except Sitting.DoesNotExist: + pass + except Sitting.MultipleObjectsReturned: + return render(request, 'single_complete.html') + else: + return render(request, 'single_complete.html') + try: previous_sitting = Sitting.objects.get(user = request.user, quiz = quiz, @@ -54,6 +67,8 @@ def quiz_take(request, quiz_name): else: # anon user + if quiz.single_attempt == True: + return render(request, 'single_complete.html') quiz_id = str(quiz.id) q_list = quiz_id + "_q_list"