├── leonardo ├── essay ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── it │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es_CO │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── zh_CN │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── models.py └── tests.py ├── quiz ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── templatetags │ ├── __init__.py │ └── quiz_tags.py ├── tests.py ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── it │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es_CO │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── zh_CN │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── templates │ ├── quiz │ │ ├── category_list.html │ │ ├── quiz_detail.html │ │ ├── quiz_list.html │ │ ├── sitting_list.html │ │ └── sitting_detail.html │ ├── single_complete.html │ ├── view_quiz_category.html │ ├── base.html │ ├── correct_answer.html │ ├── progress.html │ ├── question.html │ └── result.html ├── forms.py ├── urls.py ├── admin.py ├── views.py └── models.py ├── multichoice ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── it │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es_CO │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── zh_CN │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── tests.py └── models.py ├── true_false ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── it │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es_CO │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── zh_CN │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── models.py └── tests.py ├── requirements.txt ├── .gitignore ├── MANIFEST.in ├── tox.ini ├── runtests.py ├── setup.py ├── .travis.yml ├── README.rst ├── README.md └── test_settings.py /leonardo: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /essay/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quiz/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /multichoice/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /true_false/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /essay/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quiz/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quiz/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /true_false/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /multichoice/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quiz/tests.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/quiz/tests.py -------------------------------------------------------------------------------- /essay/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/essay/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /essay/locale/it/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/essay/locale/it/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /essay/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/essay/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /quiz/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/quiz/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /quiz/locale/it/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/quiz/locale/it/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /quiz/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/quiz/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django>=1.8.19 2 | django-model-utils>=3.1.1 3 | Pillow>=4.0.0 4 | 5 | # Only needed for running tests. 6 | tox 7 | -------------------------------------------------------------------------------- /essay/locale/es_CO/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/essay/locale/es_CO/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /essay/locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/essay/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /quiz/locale/es_CO/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/quiz/locale/es_CO/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /quiz/locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/quiz/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /multichoice/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/multichoice/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /multichoice/locale/it/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/multichoice/locale/it/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /multichoice/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/multichoice/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /true_false/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/true_false/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /true_false/locale/it/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/true_false/locale/it/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /true_false/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/true_false/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /true_false/locale/es_CO/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/true_false/locale/es_CO/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /true_false/locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/true_false/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /multichoice/locale/es_CO/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/multichoice/locale/es_CO/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /multichoice/locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomwalker/django_quiz/HEAD/multichoice/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.*~ 2 | *~ 3 | *.pyc 4 | #*# 5 | /db.sqlite3 6 | sqlite.db 7 | /manage.py 8 | /mysite* 9 | /plan.txt 10 | /dist* 11 | *.egg-info* 12 | build* 13 | /.coverage 14 | /.coveragerc 15 | /coverage_html_report/ 16 | /htmlcov/ 17 | /uploads/ 18 | .tox 19 | *.egg 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include README.rst 3 | recursive-include quiz/templates * 4 | recursive-include multichoice/templates * 5 | recursive-include true_false/templates * 6 | recursive-include quiz/locale * 7 | recursive-include multichoice/locale * 8 | recursive-include true_false/locale * 9 | recursive-include essay/locale * 10 | -------------------------------------------------------------------------------- /quiz/templates/quiz/category_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load i18n %} 3 | {% block title %}{% trans "All Quizzes" %}{% endblock %} 4 | 5 | {% block content %} 6 |

{% trans "Category list" %}

7 | 8 | 17 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /quiz/templates/single_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% load quiz_tags %} 5 | 6 | {% block title %} {{ quiz.title }} {% endblock %} 7 | {% block description %} {{ quiz.title }} - {{ quiz.description }} {% endblock %} 8 | 9 | {% block content %} 10 | 11 | 12 | {% if user.is_authenticated %} 13 |

{% trans "You have already sat this exam and only one sitting is permitted" %}.

14 | {% else %} 15 |

{% trans "This exam is only accessible to signed in users" %}.

16 | {% endif %} 17 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /quiz/templates/quiz/quiz_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load i18n %} 3 | {% block title %} 4 | {{ quiz.title }} 5 | {% endblock %} 6 | 7 | {% block content %} 8 |

{{ quiz.title }}

9 |

{% trans "Category" %}: {{ quiz.category }}

10 | {% if quiz.single_attempt %} 11 |

{% trans "You will only get one attempt at this quiz" %}.

12 | {% endif %} 13 |

{{ quiz.description }}

14 |

15 | 16 | {% trans "Start quiz" %} 17 | 18 |

19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | {py27,py32,py33,py34,py35}-django18, 4 | {py27,py34,py35}-django19, 5 | {py27,py34,py35}-django110, 6 | {py27,py34,py35,py36}-django111, 7 | {py34,py35,py36,py37}-django20 8 | {py35,py36,py37}-django21 9 | 10 | [testenv] 11 | commands = python setup.py test 12 | deps = 13 | django18: Django==1.8.19 14 | django19: Django==1.9.13 15 | django110: Django==1.10.8 16 | django111: Django==1.11.15 17 | django20: Django==2.0.8 18 | django21: Django==2.1 19 | django-model-utils 20 | Pillow 21 | 22 | setenv = 23 | PYTHONPATH = {toxinidir} 24 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings' 6 | test_dir = os.path.dirname(__file__) 7 | sys.path.insert(0, test_dir) 8 | 9 | import django 10 | from django.test.utils import get_runner 11 | from django.conf import settings 12 | 13 | 14 | def runtests(): 15 | django.setup() 16 | TestRunner = get_runner(settings) 17 | test_runner = TestRunner(verbosity=1, interactive=True) 18 | failures = test_runner.run_tests( 19 | ['quiz', 'essay', 'multichoice', 'true_false'] 20 | ) 21 | sys.exit(bool(failures)) 22 | 23 | if __name__ == '__main__': 24 | runtests() 25 | -------------------------------------------------------------------------------- /quiz/templates/view_quiz_category.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load i18n %} 3 | {% block title %}{% trans "Quizzes related to" %} {{ category.category }}{% endblock %} 4 | 5 | {% block content %} 6 |

{% trans "Quizzes in the" %} {{ category.category }} {% trans "category" %}

7 | 8 | {% with object_list as quizzes %} 9 | {% if quizzes %} 10 | 19 | {% else %} 20 |

{% trans "There are no quizzes" %}

21 | {% endif %} 22 | {% endwith %} 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /quiz/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.forms.widgets import RadioSelect, Textarea 3 | 4 | 5 | class QuestionForm(forms.Form): 6 | def __init__(self, question, *args, **kwargs): 7 | super(QuestionForm, self).__init__(*args, **kwargs) 8 | choice_list = [x for x in question.get_answers_list()] 9 | self.fields["answers"] = forms.ChoiceField(choices=choice_list, 10 | widget=RadioSelect) 11 | 12 | 13 | class EssayForm(forms.Form): 14 | def __init__(self, question, *args, **kwargs): 15 | super(EssayForm, self).__init__(*args, **kwargs) 16 | self.fields["answers"] = forms.CharField( 17 | widget=Textarea(attrs={'style': 'width:100%'})) 18 | -------------------------------------------------------------------------------- /essay/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.utils.encoding import python_2_unicode_compatible 3 | from django.utils.translation import ugettext_lazy as _ 4 | from quiz.models import Question 5 | 6 | 7 | @python_2_unicode_compatible 8 | class Essay_Question(Question): 9 | 10 | def check_if_correct(self, guess): 11 | return False 12 | 13 | def get_answers(self): 14 | return False 15 | 16 | def get_answers_list(self): 17 | return False 18 | 19 | def answer_choice_to_string(self, guess): 20 | return str(guess) 21 | 22 | def __str__(self): 23 | return self.content 24 | 25 | class Meta: 26 | verbose_name = _("Essay style question") 27 | verbose_name_plural = _("Essay style questions") 28 | -------------------------------------------------------------------------------- /quiz/templatetags/quiz_tags.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | 6 | @register.inclusion_tag('correct_answer.html', takes_context=True) 7 | def correct_answer_for_all(context, question): 8 | """ 9 | processes the correct answer based on a given question object 10 | if the answer is incorrect, informs the user 11 | """ 12 | answers = question.get_answers() 13 | incorrect_list = context.get('incorrect_questions', []) 14 | if question.id in incorrect_list: 15 | user_was_incorrect = True 16 | else: 17 | user_was_incorrect = False 18 | 19 | return {'previous': {'answers': answers}, 20 | 'user_was_incorrect': user_was_incorrect} 21 | 22 | 23 | @register.filter 24 | def answer_choice_to_string(question, answer): 25 | return question.answer_choice_to_string(answer) 26 | -------------------------------------------------------------------------------- /essay/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | from .models import Essay_Question 4 | 5 | 6 | class TestEssayQuestionModel(TestCase): 7 | def setUp(self): 8 | self.essay = Essay_Question.objects.create(content="Tell me stuff", 9 | explanation="Wow!") 10 | 11 | def test_always_false(self): 12 | self.assertEqual(self.essay.check_if_correct('spam'), False) 13 | self.assertEqual(self.essay.get_answers(), False) 14 | self.assertEqual(self.essay.get_answers_list(), False) 15 | 16 | def test_returns_guess(self): 17 | guess = "To be or not to be" 18 | self.assertEqual(self.essay.answer_choice_to_string(guess), guess) 19 | 20 | def test_answer_to_string(self): 21 | self.assertEqual('To be...', 22 | self.essay.answer_choice_to_string('To be...')) 23 | -------------------------------------------------------------------------------- /essay/locale/es_CO/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 2 | # This file is distributed under the same license as the PACKAGE package. 3 | # 4 | # , 2014. 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: django-quiz-essay\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2014-10-02 16:32+0000\n" 10 | "PO-Revision-Date: 2016-03-24 15:57-0500\n" 11 | "Language-Team: American English \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 16 | "X-Generator: Poedit 1.8.4\n" 17 | "Last-Translator: \n" 18 | "Language: es_CO\n" 19 | 20 | #: models.py:23 21 | msgid "Essay style question" 22 | msgstr "Pregunta estilo ensayo" 23 | 24 | #: models.py:24 25 | msgid "Essay style questions" 26 | msgstr "Preguntas estilo ensayo" 27 | -------------------------------------------------------------------------------- /essay/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 2 | # This file is distributed under the same license as the PACKAGE package. 3 | # 4 | # , 2014. 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: django-quiz-essay\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2014-10-02 16:32+0000\n" 10 | "PO-Revision-Date: 2015-08-21 19:40+0500\n" 11 | "Last-Translator: \n" 12 | "Language-Team: American English \n" 13 | "Language: ru\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 18 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 19 | "X-Generator: Poedit 1.5.4\n" 20 | 21 | #: models.py:23 22 | msgid "Essay style question" 23 | msgstr "Эссе" 24 | 25 | #: models.py:24 26 | msgid "Essay style questions" 27 | msgstr "Эссе" 28 | -------------------------------------------------------------------------------- /essay/locale/zh_CN/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 12 | "PO-Revision-Date: 2015-10-31 22:15+0000\n" 13 | "Last-Translator: b' <>'\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | "X-Translated-Using: django-rosetta 0.7.6\n" 21 | 22 | #: apps/essay/models.py:26 23 | msgid "Essay style question" 24 | msgstr "论述题" 25 | 26 | #: apps/essay/models.py:27 27 | msgid "Essay style questions" 28 | msgstr "论述题" 29 | -------------------------------------------------------------------------------- /quiz/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | {% trans "Example Quiz Website" %} | {% block title %}{% endblock %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | {% block content %} 28 | 29 | {% endblock %} 30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /essay/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 11 | "PO-Revision-Date: 2019-03-07 21:13+0100\n" 12 | "Last-Translator: Reiner Mayers\n" 13 | "Language: de\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | "X-Translated-Using: django-rosetta 0.7.6\n" 19 | "Language-Team: \n" 20 | "X-Generator: Poedit 2.0.6\n" 21 | 22 | #: apps/essay/models.py:26 23 | msgid "Essay style question" 24 | msgstr "Aufsatzartige Frage" 25 | 26 | #: apps/essay/models.py:27 27 | msgid "Essay style questions" 28 | msgstr "Aufsatzartige Frage" 29 | -------------------------------------------------------------------------------- /essay/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.3 on 2017-06-22 11:20 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('quiz', '__first__'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Essay_Question', 20 | fields=[ 21 | ('question_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='quiz.Question')), 22 | ], 23 | options={ 24 | 'verbose_name': 'Essay style question', 25 | 'verbose_name_plural': 'Essay style questions', 26 | }, 27 | bases=('quiz.question',), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /essay/locale/it/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 12 | "PO-Revision-Date: 2015-10-31 22:15+0000\n" 13 | "Last-Translator: b' <>'\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | "X-Translated-Using: django-rosetta 0.7.6\n" 21 | 22 | #: apps/essay/models.py:26 23 | msgid "Essay style question" 24 | msgstr "Tipo di domanda per la prova " 25 | 26 | #: apps/essay/models.py:27 27 | msgid "Essay style questions" 28 | msgstr "Tipo di domande per la prova " 29 | -------------------------------------------------------------------------------- /quiz/templates/correct_answer.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% if previous.answers %} 3 | 4 | {% if user_was_incorrect %} 5 |
6 | {% trans "You answered the above question incorrectly" %} 7 |
8 | {% endif %} 9 | 10 | 11 | 12 | {% for answer in previous.answers %} 13 | {% if answer.correct %} 14 | 15 | 16 | 17 | {% else %} 18 | 19 | 20 | 27 | {% endif %} 28 | 29 | {% endfor %} 30 | 31 |
{{ answer.content }}{% trans "This is the correct answer" %}
{{ answer.content }} 21 | {% if previous.question_type.MCQuestion %} 22 | {% if answer.id|add:"0" == previous.previous_answer|add:"0" %} 23 | {% trans "This was your answer." %} 24 | {% endif %} 25 | {% endif %} 26 |
32 | {% endif %} 33 | -------------------------------------------------------------------------------- /quiz/templates/quiz/quiz_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load i18n %} 3 | {% block title %}{% trans "All Quizzes" %}{% endblock %} 4 | 5 | {% block content %} 6 |

{% trans "List of quizzes" %}

7 | {% if quiz_list %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% for quiz in quiz_list %} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | {% endfor %} 36 | 37 | 38 |
{% trans "Title" %}{% trans "Category" %}{% trans "Exam" %}{% trans "Single attempt" %}
{{ quiz.title }}{{ quiz.category }}{{ quiz.exam_paper }}{{ quiz.single_attempt }} 30 | 31 | {% trans "View details" %} 32 | 33 |
39 | 40 | {% else %} 41 |

{% trans "There are no available quizzes" %}.

42 | {% endif %} 43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /true_false/locale/es_CO/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2016 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , 2016. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-quiz-true-false\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-10-01 17:56+0000\n" 11 | "PO-Revision-Date: 2016-03-24 16:14-0500\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 16 | "X-Generator: Poedit 1.8.4\n" 17 | "Language-Team: \n" 18 | "Last-Translator: \n" 19 | "Language: es_CO\n" 20 | 21 | #: models.py:9 22 | msgid "Tick this if the question is true. Leave it blank for false." 23 | msgstr "Marque esto si la pregunta es verdadera. Déjelo en blanco si es falsa." 24 | 25 | #: models.py:12 26 | msgid "Correct" 27 | msgstr "Correcto" 28 | 29 | #: models.py:40 30 | msgid "True/False Question" 31 | msgstr "Pregunta de Verdadero/Falso" 32 | 33 | #: models.py:41 34 | msgid "True/False Questions" 35 | msgstr "Preguntas de Verdadero/Falso" 36 | -------------------------------------------------------------------------------- /true_false/locale/zh_CN/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 12 | "PO-Revision-Date: 2015-10-31 22:12+0000\n" 13 | "Last-Translator: b' <>'\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | "X-Translated-Using: django-rosetta 0.7.6\n" 21 | 22 | #: apps/true_false/models.py:11 23 | msgid "Tick this if the question is true. Leave it blank for false." 24 | msgstr "如果题目为真,勾选此处。不选为假。" 25 | 26 | #: apps/true_false/models.py:14 27 | msgid "Correct" 28 | msgstr "正确" 29 | 30 | #: apps/true_false/models.py:42 31 | msgid "True/False Question" 32 | msgstr "判断题" 33 | 34 | #: apps/true_false/models.py:43 35 | msgid "True/False Questions" 36 | msgstr "判断题" 37 | -------------------------------------------------------------------------------- /true_false/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.3 on 2017-06-22 11:20 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('quiz', '__first__'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='TF_Question', 20 | fields=[ 21 | ('question_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='quiz.Question')), 22 | ('correct', models.BooleanField(default=False, help_text='Tick this if the question is true. Leave it blank for false.', verbose_name='Correct')), 23 | ], 24 | options={ 25 | 'verbose_name': 'True/False Question', 26 | 'verbose_name_plural': 'True/False Questions', 27 | 'ordering': ['category'], 28 | }, 29 | bases=('quiz.question',), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /true_false/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 11 | "PO-Revision-Date: 2019-03-07 21:12+0100\n" 12 | "Last-Translator: Reiner Mayers\n" 13 | "Language: de\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | "X-Translated-Using: django-rosetta 0.7.6\n" 19 | "Language-Team: \n" 20 | "X-Generator: Poedit 2.0.6\n" 21 | 22 | #: apps/true_false/models.py:11 23 | msgid "Tick this if the question is true. Leave it blank for false." 24 | msgstr "Markieren Sie, wenn die Frage stimmt. Freilassen wenn falsch." 25 | 26 | #: apps/true_false/models.py:14 27 | msgid "Correct" 28 | msgstr "Korrekt" 29 | 30 | #: apps/true_false/models.py:42 31 | msgid "True/False Question" 32 | msgstr "Wahr/Falsch Frage" 33 | 34 | #: apps/true_false/models.py:43 35 | msgid "True/False Questions" 36 | msgstr "Wahr/Falsch Fragen" 37 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | from io import open 3 | 4 | readme = open('README.rst', encoding='utf-8').read() 5 | 6 | setup( 7 | name='django-quiz-app', 8 | version='0.5.1', 9 | packages=find_packages(), 10 | include_package_data=True, 11 | license='MIT License', 12 | description='A configurable quiz app for Django.', 13 | long_description=readme, 14 | url='https://github.com/tomwalker/django_quiz', 15 | author='Tom Walker', 16 | author_email='tomwalker0472@gmail.com', 17 | zip_safe=False, 18 | classifiers=[ 19 | 'Environment :: Web Environment', 20 | 'Framework :: Django', 21 | 'Intended Audience :: Developers', 22 | 'License :: OSI Approved :: MIT License', 23 | 'Operating System :: OS Independent', 24 | 'Programming Language :: Python', 25 | 'Programming Language :: Python :: 2.6', 26 | 'Programming Language :: Python :: 2.7', 27 | 'Topic :: Internet :: WWW/HTTP', 28 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 29 | ], 30 | install_requires=[ 31 | 'django-model-utils>=3.1.1', 32 | 'Django>=1.8.19', 33 | 'Pillow>=4.0.0' 34 | ], 35 | test_suite='runtests.runtests' 36 | ) 37 | -------------------------------------------------------------------------------- /true_false/locale/it/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 12 | "PO-Revision-Date: 2015-10-31 22:12+0000\n" 13 | "Last-Translator: b' <>'\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | "X-Translated-Using: django-rosetta 0.7.6\n" 21 | 22 | #: apps/true_false/models.py:11 23 | msgid "Tick this if the question is true. Leave it blank for false." 24 | msgstr "" 25 | "Seleziona se la risposta è corretta. Lascia bianco se la risposta è falsa." 26 | 27 | #: apps/true_false/models.py:14 28 | msgid "Correct" 29 | msgstr "Corretto" 30 | 31 | #: apps/true_false/models.py:42 32 | msgid "True/False Question" 33 | msgstr "Domanda Vero/Falso" 34 | 35 | #: apps/true_false/models.py:43 36 | msgid "True/False Questions" 37 | msgstr "DOmande Vero/Falso" 38 | -------------------------------------------------------------------------------- /true_false/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-quiz-true-false\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-10-01 17:56+0000\n" 11 | "PO-Revision-Date: 2015-08-21 19:40+0500\n" 12 | "Last-Translator: Eugena Mihailikova \n" 13 | "Language-Team: LANGUAGE \n" 14 | "Language: ru\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 19 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 20 | "X-Generator: Poedit 1.5.4\n" 21 | 22 | #: models.py:9 23 | msgid "Tick this if the question is true. Leave it blank for false." 24 | msgstr "Отметьте, если ответ на вопрос 'Да'. Оставьте пустым, если ответ 'Нет'" 25 | 26 | #: models.py:12 27 | msgid "Correct" 28 | msgstr "Правильно" 29 | 30 | #: models.py:40 31 | msgid "True/False Question" 32 | msgstr "Вопрос с ответом Да/Нет" 33 | 34 | #: models.py:41 35 | msgid "True/False Questions" 36 | msgstr "Вопросы с ответом Да/Нет" 37 | -------------------------------------------------------------------------------- /quiz/urls.py: -------------------------------------------------------------------------------- 1 | try: 2 | from django.conf.urls import url 3 | except ImportError: 4 | from django.urls import re_path as url 5 | 6 | from .views import QuizListView, CategoriesListView, \ 7 | ViewQuizListByCategory, QuizUserProgressView, QuizMarkingList, \ 8 | QuizMarkingDetail, QuizDetailView, QuizTake 9 | 10 | urlpatterns = [ 11 | 12 | url(r'^$', 13 | view=QuizListView.as_view(), 14 | name='quiz_index'), 15 | 16 | url(r'^category/$', 17 | view=CategoriesListView.as_view(), 18 | name='quiz_category_list_all'), 19 | 20 | url(r'^category/(?P[\w|\W-]+)/$', 21 | view=ViewQuizListByCategory.as_view(), 22 | name='quiz_category_list_matching'), 23 | 24 | url(r'^progress/$', 25 | view=QuizUserProgressView.as_view(), 26 | name='quiz_progress'), 27 | 28 | url(r'^marking/$', 29 | view=QuizMarkingList.as_view(), 30 | name='quiz_marking'), 31 | 32 | url(r'^marking/(?P[\d.]+)/$', 33 | view=QuizMarkingDetail.as_view(), 34 | name='quiz_marking_detail'), 35 | 36 | # passes variable 'quiz_name' to quiz_take view 37 | url(r'^(?P[\w-]+)/$', 38 | view=QuizDetailView.as_view(), 39 | name='quiz_start_page'), 40 | 41 | url(r'^(?P[\w-]+)/take/$', 42 | view=QuizTake.as_view(), 43 | name='quiz_question'), 44 | ] 45 | -------------------------------------------------------------------------------- /quiz/templates/quiz/sitting_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load i18n %} 3 | {% block title %}{% trans "All Quizzes" %}{% endblock %} 4 | 5 | {% block content %} 6 |

{% trans "List of complete exams" %}

7 | {% if sitting_list %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {% for sitting in sitting_list %} 34 | 35 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | {% endfor %} 47 | 48 | 49 | 50 |
{% trans "User" %}{% trans "Quiz" %}{% trans "Completed" %}{% trans "Score" %}(%)
{{ sitting.user }}{{ sitting.quiz }}{{ sitting.end|date }}{{ sitting.get_percent_correct }} 41 | 42 | {% trans "View details" %} 43 | 44 |
51 | {% else %} 52 |

{% trans "There are no matching quizzes" %}.

53 | {% endif %} 54 | {% endblock %} 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | matrix: 3 | include: 4 | - python: "2.7" 5 | env: TOX_ENV=py27-django18 6 | - python: "3.2" 7 | env: TOX_ENV=py32-django18 8 | - python: "3.3" 9 | env: TOX_ENV=py33-django18 10 | - python: "3.4" 11 | env: TOX_ENV=py34-django18 12 | - python: "3.5" 13 | env: TOX_ENV=py35-django18 14 | 15 | - python: "2.7" 16 | env: TOX_ENV=py27-django19 17 | - python: "3.4" 18 | env: TOX_ENV=py34-django19 19 | - python: "3.5" 20 | env: TOX_ENV=py35-django19 21 | 22 | - python: "2.7" 23 | env: TOX_ENV=py27-django110 24 | - python: "3.4" 25 | env: TOX_ENV=py34-django110 26 | - python: "3.5" 27 | env: TOX_ENV=py35-django110 28 | 29 | - python: "2.7" 30 | env: TOX_ENV=py27-django111 31 | - python: "3.4" 32 | env: TOX_ENV=py34-django111 33 | - python: "3.5" 34 | env: TOX_ENV=py35-django111 35 | - python: "3.6" 36 | env: TOX_ENV=py36-django111 37 | 38 | - python: "3.4" 39 | env: TOX_ENV=py34-django20 40 | - python: "3.5" 41 | env: TOX_ENV=py35-django20 42 | - python: "3.6" 43 | env: TOX_ENV=py36-django20 44 | - python: "3.7-dev" 45 | env: TOX_ENV=py37-django20 46 | 47 | - python: "3.5" 48 | env: TOX_ENV=py35-django21 49 | - python: "3.6" 50 | env: TOX_ENV=py36-django21 51 | - python: "3.7-dev" 52 | env: TOX_ENV=py37-django21 53 | install: 54 | - pip install tox 55 | - if [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then pip install virtualenv==15.2.0; fi 56 | - if [[ $TRAVIS_PYTHON_VERSION == 3.2 ]]; then pip install virtualenv==13.1.2; fi 57 | script: 58 | - tox -e $TOX_ENV 59 | -------------------------------------------------------------------------------- /true_false/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.utils.encoding import python_2_unicode_compatible 3 | from django.utils.translation import ugettext_lazy as _ 4 | from django.db import models 5 | from quiz.models import Question 6 | 7 | 8 | class TF_Question(Question): 9 | correct = models.BooleanField(blank=False, 10 | default=False, 11 | help_text=_("Tick this if the question " 12 | "is true. Leave it blank for" 13 | " false."), 14 | verbose_name=_("Correct")) 15 | 16 | def check_if_correct(self, guess): 17 | if guess == "True": 18 | guess_bool = True 19 | elif guess == "False": 20 | guess_bool = False 21 | else: 22 | return False 23 | 24 | if guess_bool == self.correct: 25 | return True 26 | else: 27 | return False 28 | 29 | def get_answers(self): 30 | return [{'correct': self.check_if_correct("True"), 31 | 'content': 'True'}, 32 | {'correct': self.check_if_correct("False"), 33 | 'content': 'False'}] 34 | 35 | def get_answers_list(self): 36 | return [(True, True), (False, False)] 37 | 38 | def answer_choice_to_string(self, guess): 39 | return str(guess) 40 | 41 | class Meta: 42 | verbose_name = _("True/False Question") 43 | verbose_name_plural = _("True/False Questions") 44 | ordering = ['category'] 45 | -------------------------------------------------------------------------------- /true_false/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | from .models import TF_Question 4 | 5 | 6 | class TestTrueFalseQuestionModel(TestCase): 7 | def setUp(self): 8 | self.red = TF_Question.objects.create(content="Is red the best?", 9 | explanation="it is", 10 | correct=True,) 11 | self.blue = TF_Question.objects.create(content="Is blue the best?", 12 | explanation="it is not", 13 | correct=False,) 14 | 15 | def test_true_q(self): 16 | self.assertEqual(self.red.correct, True) 17 | self.assertEqual(self.red.check_if_correct("True"), True) 18 | self.assertEqual(self.red.check_if_correct("False"), False) 19 | self.assertEqual(self.red.check_if_correct("4"), False) 20 | 21 | def test_false_q(self): 22 | self.assertEqual(self.blue.correct, False) 23 | self.assertEqual(self.blue.check_if_correct("True"), False) 24 | self.assertEqual(self.blue.check_if_correct("False"), True) 25 | 26 | def test_get_answers(self): 27 | self.assertEqual(self.red.get_answers(), 28 | [{'correct': True, 29 | 'content': 'True'}, 30 | {'correct': False, 31 | 'content': 'False'}]) 32 | self.assertEqual(self.red.answer_choice_to_string('True'), 'True') 33 | 34 | def test_answer_to_string(self): 35 | self.assertEqual('True', self.red.answer_choice_to_string(True)) 36 | -------------------------------------------------------------------------------- /quiz/templates/progress.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% load quiz_tags %} 5 | 6 | {% block title %} {% trans "Progress Page" %} {% endblock %} 7 | {% block description %} {% trans "User Progress Page" %} {% endblock %} 8 | 9 | {% block content %} 10 | 11 | {% if cat_scores %} 12 | 13 |

{% trans "Question Category Scores" %}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% for cat, value in cat_scores.items %} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {% endfor %} 38 | 39 | 40 | 41 |
{% trans "Category" %}{% trans "Correctly answererd" %}{% trans "Incorrect" %}%
{{ cat }}{{ value.0 }}{{ value.1 }}{{ value.2 }}
42 | 43 | 44 | {% endif %} 45 | 46 | {% if exams %} 47 | 48 |
49 | 50 |

{% trans "Previous exam papers" %}

51 |

52 | {% trans "Below are the results of exams that you have sat." %} 53 |

54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {% for exam in exams %} 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | {% endfor %} 78 | 79 | 80 | 81 |
{% trans "Quiz Title" %}{% trans "Score" %}{% trans "Possible Score" %}%
{{ exam.quiz.title }}{{ exam.current_score }}{{ exam.get_max_score }}{{ exam.get_percent_correct }}
82 | 83 | {% endif %} 84 | 85 | 86 | {% endblock %} 87 | -------------------------------------------------------------------------------- /quiz/templates/quiz/sitting_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load i18n %} 3 | {% load quiz_tags %} 4 | {% block title %} 5 | {% trans "Result of" %} {{ sitting.quiz.title }} {% trans "for" %} {{ sitting.user }} 6 | {% endblock %} 7 | 8 | {% block content %} 9 |

{% trans "Quiz title" %}: {{ sitting.quiz.title }}

10 |

{% trans "Category" %}: {{ sitting.quiz.category }}

11 |

{{ sitting.quiz.description }}

12 |
13 |

{% trans "User" %}: {{ sitting.user }}

14 |

{% trans "Completed" %}: {{ sitting.end|date }}

15 |

{% trans "Score" %}: {{ sitting.get_percent_correct }}%

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% for question in questions %} 30 | 31 | 32 | 38 | 39 | 46 | 52 | 53 | 54 | {% endfor %} 55 | 56 | 57 | 58 |
{% trans "Question" %}{% trans "User answer" %}
33 | {{ question.content }} 34 | {% if question.figure %} 35 |
{{ question.figure }}
36 | {% endif %} 37 |
{{ question|answer_choice_to_string:question.user_answer }} 40 | {% if question.id in sitting.get_incorrect_questions %} 41 |

{% trans "incorrect" %}

42 | {% else %} 43 |

{% trans "Correct" %}

44 | {% endif %} 45 |
47 |
{% csrf_token %} 48 | 49 | 50 |
51 |
59 | {% endblock %} 60 | -------------------------------------------------------------------------------- /multichoice/locale/es_CO/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 2 | # This file is distributed under the same license as the PACKAGE package. 3 | # 4 | # , 2014. 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: django-quiz-multichoice\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2014-10-02 16:35+0000\n" 10 | "PO-Revision-Date: 2016-03-24 16:00-0500\n" 11 | "Language-Team: American English \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 16 | "X-Generator: Poedit 1.8.4\n" 17 | "Last-Translator: \n" 18 | "Language: es_CO\n" 19 | 20 | #: models.py:7 models.py:61 21 | msgid "Content" 22 | msgstr "Contenido" 23 | 24 | #: models.py:8 25 | msgid "Random" 26 | msgstr "Aleatorio" 27 | 28 | #: models.py:9 29 | msgid "None" 30 | msgstr "Ninguna" 31 | 32 | #: models.py:17 33 | msgid "The order in which multichoice answer options are displayed to the user" 34 | msgstr "El orden en que las opciones de respuesta de elección múltiple se muestran al usuario" 35 | 36 | #: models.py:20 37 | msgid "Answer Order" 38 | msgstr "Orden de las Respuestas" 39 | 40 | #: models.py:50 41 | msgid "Multiple Choice Question" 42 | msgstr "Pregunta de opción múltiple" 43 | 44 | #: models.py:51 45 | msgid "Multiple Choice Questions" 46 | msgstr "Preguntas de opción múltiple" 47 | 48 | #: models.py:55 49 | msgid "Question" 50 | msgstr "Pregunta" 51 | 52 | #: models.py:59 53 | msgid "Enter the answer text that you want displayed" 54 | msgstr "Ingrese el texto de la respuesta que desea mostrar" 55 | 56 | #: models.py:65 57 | msgid "Is this a correct answer?" 58 | msgstr "¿Es esta una respuesta correcta?" 59 | 60 | #: models.py:66 61 | msgid "Correct" 62 | msgstr "Correcto" 63 | 64 | #: models.py:72 65 | msgid "Answer" 66 | msgstr "Respuesta" 67 | 68 | #: models.py:73 69 | msgid "Answers" 70 | msgstr "Respuestas" 71 | -------------------------------------------------------------------------------- /multichoice/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 2 | # This file is distributed under the same license as the PACKAGE package. 3 | # 4 | # , 2014. 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: django-quiz-multichoice\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2014-10-02 16:35+0000\n" 10 | "PO-Revision-Date: 2015-08-21 19:39+0500\n" 11 | "Last-Translator: Eugena Mihailikova \n" 12 | "Language-Team: American English \n" 13 | "Language: ru\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 18 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 19 | "X-Generator: Poedit 1.5.4\n" 20 | 21 | #: models.py:7 models.py:61 22 | msgid "Content" 23 | msgstr "Содержание" 24 | 25 | #: models.py:8 26 | msgid "Random" 27 | msgstr "Случайно" 28 | 29 | #: models.py:9 30 | msgid "None" 31 | msgstr "Ничего" 32 | 33 | #: models.py:17 34 | msgid "The order in which multichoice answer options are displayed to the user" 35 | msgstr "Порядок отображения вопросов" 36 | 37 | #: models.py:20 38 | msgid "Answer Order" 39 | msgstr "Порядок вопросов" 40 | 41 | #: models.py:50 42 | msgid "Multiple Choice Question" 43 | msgstr "Вопрос с несколькими вариантами ответов" 44 | 45 | #: models.py:51 46 | msgid "Multiple Choice Questions" 47 | msgstr "Вопросы с несколькими вариантами ответов" 48 | 49 | #: models.py:55 50 | msgid "Question" 51 | msgstr "Вопрос" 52 | 53 | #: models.py:59 54 | msgid "Enter the answer text that you want displayed" 55 | msgstr "Введите текст ответа" 56 | 57 | #: models.py:65 58 | msgid "Is this a correct answer?" 59 | msgstr "Это правильный ответ?" 60 | 61 | #: models.py:66 62 | msgid "Correct" 63 | msgstr "Правильно" 64 | 65 | #: models.py:72 66 | msgid "Answer" 67 | msgstr "Вопрос" 68 | 69 | #: models.py:73 70 | msgid "Answers" 71 | msgstr "Вопросы" 72 | -------------------------------------------------------------------------------- /multichoice/locale/zh_CN/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 12 | "PO-Revision-Date: 2015-10-31 22:19+0000\n" 13 | "Last-Translator: b' <>'\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | "X-Translated-Using: django-rosetta 0.7.6\n" 21 | 22 | #: apps/multichoice/models.py:9 apps/multichoice/models.py:65 23 | msgid "Content" 24 | msgstr "内容" 25 | 26 | #: apps/multichoice/models.py:10 27 | msgid "Random" 28 | msgstr "随机" 29 | 30 | #: apps/multichoice/models.py:11 31 | msgid "None" 32 | msgstr "无" 33 | 34 | #: apps/multichoice/models.py:20 35 | msgid "The order in which multichoice answer options are displayed to the user" 36 | msgstr "向用户显示多选答案选项时的次序" 37 | 38 | #: apps/multichoice/models.py:23 39 | msgid "Answer Order" 40 | msgstr "答案次序" 41 | 42 | #: apps/multichoice/models.py:53 43 | msgid "Multiple Choice Question" 44 | msgstr "多选题" 45 | 46 | #: apps/multichoice/models.py:54 47 | msgid "Multiple Choice Questions" 48 | msgstr "多选题" 49 | 50 | #: apps/multichoice/models.py:59 51 | msgid "Question" 52 | msgstr "问题" 53 | 54 | #: apps/multichoice/models.py:63 55 | msgid "Enter the answer text that you want displayed" 56 | msgstr "输入你想显示的答案文本" 57 | 58 | #: apps/multichoice/models.py:69 59 | msgid "Is this a correct answer?" 60 | msgstr "这个答案正确吗?" 61 | 62 | #: apps/multichoice/models.py:70 63 | msgid "Correct" 64 | msgstr "正确" 65 | 66 | #: apps/multichoice/models.py:76 67 | msgid "Answer" 68 | msgstr "答案" 69 | 70 | #: apps/multichoice/models.py:77 71 | msgid "Answers" 72 | msgstr "答案" 73 | -------------------------------------------------------------------------------- /quiz/templates/question.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n%} 3 | 4 | {% load quiz_tags %} 5 | 6 | {% block title %} {{ quiz.title }} {% endblock %} 7 | {% block description %} {{ quiz.title }} - {{ quiz.description }} {% endblock %} 8 | 9 | {% block content %} 10 | 11 | {% if previous.answers %} 12 | 13 |

{% trans "The previous question" %}:

14 |

{{ previous.previous_question }}

15 | 16 | {% if previous.previous_outcome %} 17 |
18 | {% else %} 19 |
20 | {% endif %} 21 |

22 | {% trans "Your answer was" %} 23 | 24 | {{ previous.previous_outcome|yesno:"correct,incorrect" }} 25 | 26 |

27 | 28 |
29 | 30 | {% include 'correct_answer.html' %} 31 | 32 |

{% trans "Explanation" %}:

33 |
34 |

{{ previous.previous_question.explanation }}

35 |
36 | 37 |
38 | 39 | {% endif %} 40 | 41 |
42 | 43 | {% if question %} 44 | 45 | {% if progress %} 46 |
47 | {% trans "Question" %} {{ progress.0|add:1 }} {% trans "of" %} {{ progress.1 }} 48 |
49 | {% endif %} 50 | 51 |

52 | {% trans "Question category" %}: 53 | {{ question.category }} 54 |

55 | 56 |

{{ question.content }}

57 | 58 | {% if question.figure %} 59 | {{ question.content }} 60 | {% endif %} 61 | 62 |
{% csrf_token %} 63 | 64 | 65 |
    66 | 67 | {% for answer in form.answers %} 68 |
  • 69 | {{ answer }} 70 |
  • 71 | {% endfor %} 72 | 73 |
74 | 75 |
76 | 77 | {% endif %} 78 | 79 |
80 | 81 | 82 | {% endblock %} 83 | -------------------------------------------------------------------------------- /multichoice/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.3 on 2017-06-22 11:20 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('quiz', '__first__'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Answer', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('content', models.CharField(help_text='Enter the answer text that you want displayed', max_length=1000, verbose_name='Content')), 23 | ('correct', models.BooleanField(default=False, help_text='Is this a correct answer?', verbose_name='Correct')), 24 | ], 25 | options={ 26 | 'verbose_name': 'Answer', 27 | 'verbose_name_plural': 'Answers', 28 | }, 29 | ), 30 | migrations.CreateModel( 31 | name='MCQuestion', 32 | fields=[ 33 | ('question_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='quiz.Question')), 34 | ('answer_order', models.CharField(blank=True, choices=[('content', 'Content'), ('random', 'Random'), ('none', 'None')], help_text='The order in which multichoice answer options are displayed to the user', max_length=30, null=True, verbose_name='Answer Order')), 35 | ], 36 | options={ 37 | 'verbose_name': 'Multiple Choice Question', 38 | 'verbose_name_plural': 'Multiple Choice Questions', 39 | }, 40 | bases=('quiz.question',), 41 | ), 42 | migrations.AddField( 43 | model_name='answer', 44 | name='question', 45 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='multichoice.MCQuestion', verbose_name='Question'), 46 | ), 47 | ] 48 | -------------------------------------------------------------------------------- /multichoice/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 11 | "PO-Revision-Date: 2019-03-07 21:13+0100\n" 12 | "Last-Translator: Reiner Mayers\n" 13 | "Language: de\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | "X-Translated-Using: django-rosetta 0.7.6\n" 19 | "Language-Team: \n" 20 | "X-Generator: Poedit 2.0.6\n" 21 | 22 | #: apps/multichoice/models.py:9 apps/multichoice/models.py:65 23 | msgid "Content" 24 | msgstr "Inhalt" 25 | 26 | #: apps/multichoice/models.py:10 27 | msgid "Random" 28 | msgstr "Zufällig" 29 | 30 | #: apps/multichoice/models.py:11 31 | msgid "None" 32 | msgstr "Kein" 33 | 34 | #: apps/multichoice/models.py:20 35 | msgid "The order in which multichoice answer options are displayed to the user" 36 | msgstr "Reihenfolge in der Multichoice Antworten dem Benutzer angezeigt werden" 37 | 38 | #: apps/multichoice/models.py:23 39 | msgid "Answer Order" 40 | msgstr "Antwort Reihenfolge" 41 | 42 | #: apps/multichoice/models.py:53 43 | msgid "Multiple Choice Question" 44 | msgstr "Multiple Choice Frage" 45 | 46 | #: apps/multichoice/models.py:54 47 | msgid "Multiple Choice Questions" 48 | msgstr "Multiple Choice Frage" 49 | 50 | #: apps/multichoice/models.py:59 51 | msgid "Question" 52 | msgstr "Frage" 53 | 54 | #: apps/multichoice/models.py:63 55 | msgid "Enter the answer text that you want displayed" 56 | msgstr "Geben Sie den Antworttext ein der angezeigt werden soll" 57 | 58 | #: apps/multichoice/models.py:69 59 | msgid "Is this a correct answer?" 60 | msgstr "Ist diese Antwort richtig?" 61 | 62 | #: apps/multichoice/models.py:70 63 | msgid "Correct" 64 | msgstr "Korrekt" 65 | 66 | #: apps/multichoice/models.py:76 67 | msgid "Answer" 68 | msgstr "Antwort" 69 | 70 | #: apps/multichoice/models.py:77 71 | msgid "Answers" 72 | msgstr "Antworten" 73 | -------------------------------------------------------------------------------- /multichoice/locale/it/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 12 | "PO-Revision-Date: 2015-10-31 22:19+0000\n" 13 | "Last-Translator: b' <>'\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | "X-Translated-Using: django-rosetta 0.7.6\n" 21 | 22 | #: apps/multichoice/models.py:9 apps/multichoice/models.py:65 23 | msgid "Content" 24 | msgstr "Contenuto" 25 | 26 | #: apps/multichoice/models.py:10 27 | msgid "Random" 28 | msgstr "Casuale" 29 | 30 | #: apps/multichoice/models.py:11 31 | msgid "None" 32 | msgstr "Nessuno" 33 | 34 | #: apps/multichoice/models.py:20 35 | msgid "The order in which multichoice answer options are displayed to the user" 36 | msgstr "" 37 | "Ordine in cui le risposte multiple disponibili vengono visualizzate " 38 | "all'utente" 39 | 40 | #: apps/multichoice/models.py:23 41 | msgid "Answer Order" 42 | msgstr "Ordine Risposte" 43 | 44 | #: apps/multichoice/models.py:53 45 | msgid "Multiple Choice Question" 46 | msgstr "Domanda a risposta multipla" 47 | 48 | #: apps/multichoice/models.py:54 49 | msgid "Multiple Choice Questions" 50 | msgstr "Domande a risposta multipla" 51 | 52 | #: apps/multichoice/models.py:59 53 | msgid "Question" 54 | msgstr "Domanda" 55 | 56 | #: apps/multichoice/models.py:63 57 | msgid "Enter the answer text that you want displayed" 58 | msgstr "Inserisci il testo della risposta che vuoi visualizzare" 59 | 60 | #: apps/multichoice/models.py:69 61 | msgid "Is this a correct answer?" 62 | msgstr "Questa è la risposta corretta?" 63 | 64 | #: apps/multichoice/models.py:70 65 | msgid "Correct" 66 | msgstr "Corretto" 67 | 68 | #: apps/multichoice/models.py:76 69 | msgid "Answer" 70 | msgstr "Risposta" 71 | 72 | #: apps/multichoice/models.py:77 73 | msgid "Answers" 74 | msgstr "Risposte" 75 | -------------------------------------------------------------------------------- /multichoice/tests.py: -------------------------------------------------------------------------------- 1 | from django.core.files.base import ContentFile 2 | from django.db.models.fields.files import ImageFieldFile 3 | from django.test import TestCase 4 | from django.utils.six import StringIO 5 | 6 | from .models import MCQuestion, Answer 7 | 8 | 9 | class TestMCQuestionModel(TestCase): 10 | def setUp(self): 11 | self.q = MCQuestion.objects.create(id=1, 12 | content=("WHAT is the airspeed" + 13 | "velocity of an unladen" + 14 | "swallow?"), 15 | explanation="I, I don't know that!") 16 | 17 | self.answer1 = Answer.objects.create(id=123, 18 | question=self.q, 19 | content="African", 20 | correct=False) 21 | 22 | self.answer2 = Answer.objects.create(id=456, 23 | question=self.q, 24 | content="European", 25 | correct=True) 26 | 27 | def test_answers(self): 28 | answers = Answer.objects.filter(question=self.q) 29 | correct_a = Answer.objects.get(question=self.q, 30 | correct=True) 31 | answers_by_method = self.q.get_answers() 32 | 33 | self.assertEqual(answers.count(), 2) 34 | self.assertEqual(correct_a.content, "European") 35 | self.assertEqual(self.q.check_if_correct(123), False) 36 | self.assertEqual(self.q.check_if_correct(456), True) 37 | self.assertEqual(answers_by_method.count(), 2) 38 | self.assertEqual(self.q.answer_choice_to_string(123), 39 | self.answer1.content) 40 | 41 | def test_figure(self): 42 | # http://stackoverflow.com/a/2473445/1694979 43 | imgfile = StringIO( 44 | 'GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,' 45 | '\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;') 46 | imgfile.name = 'test_img_file.gif' 47 | 48 | self.q.figure.save('image', ContentFile(imgfile.read())) 49 | self.assertIsInstance(self.q.figure, ImageFieldFile) 50 | 51 | def test_answer_to_string(self): 52 | self.assertEqual('African', self.q.answer_choice_to_string(123)) 53 | -------------------------------------------------------------------------------- /quiz/templates/result.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% load quiz_tags %} 5 | 6 | {% block title %} {{ quiz.title}} {% endblock %} 7 | {% block description %} {% trans "Exam Results for" %} {{ quiz.title }} {% endblock %} 8 | 9 | {% block content %} 10 | 11 | {% if previous.answers %} 12 | 13 |

{% trans "The previous question" %}:

14 |

{{ previous.previous_question }}

15 |

Your answer was 16 | 17 | {{ previous.previous_outcome|yesno:"correct,incorrect" }} 18 | 19 |

20 | {% include 'correct_answer.html' %} 21 |

{% trans "Explanation" %}:

22 |
23 |

{{ previous.previous_question.explanation }}

24 |
25 |
26 | 27 | {% endif %} 28 | 29 | {% if max_score %} 30 | 31 |
32 |

{% trans "Exam results" %}

33 |

34 | {% trans "Exam title" %}: 35 | {{ quiz.title }}

36 | 37 |

38 | {% trans "You answered" %} {{ score }} {% trans "questions correctly out of" %} {{ max_score }}, {% trans "giving you" %} {{ percent }} {% trans "percent correct" %} 39 |

40 | 41 | {% if quiz.pass_mark %} 42 |
43 |

{{ sitting.result_message }}

44 |
45 | 46 | {% endif %} 47 | 48 |

{% trans "Review the questions below and try the exam again in the future"%}.

49 | 50 | {% if user.is_authenticated %} 51 | 52 |

{% trans "The result of this exam will be stored in your progress section so you can review and monitor your progression" %}.

53 | 54 | {% endif %} 55 |
56 | 57 | 58 | {% endif %} 59 | 60 | 61 |
62 | 63 | {% if possible %} 64 | 65 |

66 | {% trans "Your session score is" %} {{ session }} {% trans "out of a possible" %} {{ possible }} 67 |

68 | 69 |
70 | 71 | {% endif %} 72 | 73 | {% if questions %} 74 | 75 | {% for question in questions %} 76 | 77 |

78 | {{ question.content }} 79 |

80 | 81 | {% correct_answer_for_all question %} 82 | 83 | {% if question.user_answer %} 84 |

{% trans "Your answer" %}: {{ question|answer_choice_to_string:question.user_answer }}

85 | {% endif %} 86 | 87 |

{% trans "Explanation" %}:

88 |
89 |

{{ question.explanation|safe }}

90 |
91 | 92 |
93 | 94 | {% endfor %} 95 | 96 | {% endif %} 97 | 98 | 99 | {% endblock %} 100 | -------------------------------------------------------------------------------- /multichoice/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.utils.encoding import python_2_unicode_compatible 3 | from django.utils.translation import ugettext_lazy as _ 4 | from django.db import models 5 | from quiz.models import Question 6 | 7 | 8 | ANSWER_ORDER_OPTIONS = ( 9 | ('content', _('Content')), 10 | ('random', _('Random')), 11 | ('none', _('None')) 12 | ) 13 | 14 | 15 | class MCQuestion(Question): 16 | 17 | answer_order = models.CharField( 18 | max_length=30, null=True, blank=True, 19 | choices=ANSWER_ORDER_OPTIONS, 20 | help_text=_("The order in which multichoice " 21 | "answer options are displayed " 22 | "to the user"), 23 | verbose_name=_("Answer Order")) 24 | 25 | def check_if_correct(self, guess): 26 | answer = Answer.objects.get(id=guess) 27 | 28 | if answer.correct is True: 29 | return True 30 | else: 31 | return False 32 | 33 | def order_answers(self, queryset): 34 | if self.answer_order == 'content': 35 | return queryset.order_by('content') 36 | if self.answer_order == 'random': 37 | return queryset.order_by('?') 38 | if self.answer_order == 'none': 39 | return queryset.order_by() 40 | return queryset 41 | 42 | def get_answers(self): 43 | return self.order_answers(Answer.objects.filter(question=self)) 44 | 45 | def get_answers_list(self): 46 | return [(answer.id, answer.content) for answer in 47 | self.order_answers(Answer.objects.filter(question=self))] 48 | 49 | def answer_choice_to_string(self, guess): 50 | return Answer.objects.get(id=guess).content 51 | 52 | class Meta: 53 | verbose_name = _("Multiple Choice Question") 54 | verbose_name_plural = _("Multiple Choice Questions") 55 | 56 | 57 | @python_2_unicode_compatible 58 | class Answer(models.Model): 59 | question = models.ForeignKey(MCQuestion, verbose_name=_("Question"), on_delete=models.CASCADE) 60 | 61 | content = models.CharField(max_length=1000, 62 | blank=False, 63 | help_text=_("Enter the answer text that " 64 | "you want displayed"), 65 | verbose_name=_("Content")) 66 | 67 | correct = models.BooleanField(blank=False, 68 | default=False, 69 | help_text=_("Is this a correct answer?"), 70 | verbose_name=_("Correct")) 71 | 72 | def __str__(self): 73 | return self.content 74 | 75 | class Meta: 76 | verbose_name = _("Answer") 77 | verbose_name_plural = _("Answers") 78 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Django quiz app 3 | =============== 4 | 5 | This is a configurable quiz app for Django. 6 | 7 | Current features 8 | ---------------- 9 | * Question order randomisation 10 | * Storing of quiz results under each user 11 | * Previous quiz scores can be viewed on category page 12 | * Correct answers can be shown after each question or all at once at the end 13 | * Logged in users can return to an incomplete quiz to finish it and non-logged in users can complete a quiz if their session persists 14 | * The quiz can be limited to one attempt per user 15 | * Questions can be given a category 16 | * Success rate for each category can be monitored on a progress page 17 | * Explanation for each question result can be given 18 | * Pass marks can be set 19 | * Multiple choice question type 20 | * True/False question type 21 | * Essay question type 22 | * Custom message displayed for those that pass or fail a quiz 23 | * Custom permission (view_sittings) added, allowing users with that permission to view quiz results from users 24 | * A marking page which lists completed quizzes, can be filtered by quiz or user, and is used to mark essay questions 25 | 26 | 27 | Requirements 28 | ------------ 29 | 30 | django-model-utils 31 | 32 | 33 | Installation 34 | ------------ 35 | 36 | git clone https://github.com/tomwalker/django_quiz.git 37 | 38 | pip install -r requirements.txt 39 | 40 | Add 'quiz', 'multichoice', 'true_false', and 'essay' to your 'INSTALLED_APPS' setting. 41 | 42 | INSTALLED_APPS = ( 43 | ... 44 | 'quiz', 45 | 'multichoice', 46 | 'true_false', 47 | ... 48 | ) 49 | 50 | Add the following to your projects 'urls.py' file, substituting 'q' 51 | for whatever you want the quiz base url to be. 52 | 53 | urlpatterns = patterns('', 54 | ... 55 | url(r'^q/', include('quiz.urls')), 56 | ... 57 | ) 58 | 59 | MIT License (MIT) Copyright (c) 2012 - 2014 Dr Tom Walker 60 | 61 | Permission is hereby granted, free of charge, to any person obtaining a 62 | copy of this software and associated documentation files (the 63 | “Software”), to deal in the Software without restriction, including 64 | without limitation the rights to use, copy, modify, merge, publish, 65 | distribute, sublicense, and/or sell copies of the Software, and to 66 | permit persons to whom the Software is furnished to do so, subject to 67 | the following conditions: 68 | 69 | The above copyright notice and this permission notice shall be included 70 | in all copies or substantial portions of the Software. 71 | 72 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 73 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 74 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 75 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 76 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 77 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 78 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 79 | -------------------------------------------------------------------------------- /quiz/admin.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib import admin 3 | from django.contrib.admin.widgets import FilteredSelectMultiple 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | from .models import Quiz, Category, SubCategory, Progress, Question 7 | from multichoice.models import MCQuestion, Answer 8 | from true_false.models import TF_Question 9 | from essay.models import Essay_Question 10 | 11 | 12 | class AnswerInline(admin.TabularInline): 13 | model = Answer 14 | 15 | 16 | class QuizAdminForm(forms.ModelForm): 17 | """ 18 | below is from 19 | http://stackoverflow.com/questions/11657682/ 20 | django-admin-interface-using-horizontal-filter-with- 21 | inline-manytomany-field 22 | """ 23 | 24 | class Meta: 25 | model = Quiz 26 | exclude = [] 27 | 28 | questions = forms.ModelMultipleChoiceField( 29 | queryset=Question.objects.all().select_subclasses(), 30 | required=False, 31 | label=_("Questions"), 32 | widget=FilteredSelectMultiple( 33 | verbose_name=_("Questions"), 34 | is_stacked=False)) 35 | 36 | def __init__(self, *args, **kwargs): 37 | super(QuizAdminForm, self).__init__(*args, **kwargs) 38 | if self.instance.pk: 39 | self.fields['questions'].initial =\ 40 | self.instance.question_set.all().select_subclasses() 41 | 42 | def save(self, commit=True): 43 | quiz = super(QuizAdminForm, self).save(commit=False) 44 | quiz.save() 45 | quiz.question_set.set(self.cleaned_data['questions']) 46 | self.save_m2m() 47 | return quiz 48 | 49 | 50 | class QuizAdmin(admin.ModelAdmin): 51 | form = QuizAdminForm 52 | 53 | list_display = ('title', 'category', ) 54 | list_filter = ('category',) 55 | search_fields = ('description', 'category', ) 56 | 57 | 58 | class CategoryAdmin(admin.ModelAdmin): 59 | search_fields = ('category', ) 60 | 61 | 62 | class SubCategoryAdmin(admin.ModelAdmin): 63 | search_fields = ('sub_category', ) 64 | list_display = ('sub_category', 'category',) 65 | list_filter = ('category',) 66 | 67 | 68 | class MCQuestionAdmin(admin.ModelAdmin): 69 | list_display = ('content', 'category', ) 70 | list_filter = ('category',) 71 | fields = ('content', 'category', 'sub_category', 72 | 'figure', 'quiz', 'explanation', 'answer_order') 73 | 74 | search_fields = ('content', 'explanation') 75 | filter_horizontal = ('quiz',) 76 | 77 | inlines = [AnswerInline] 78 | 79 | 80 | class ProgressAdmin(admin.ModelAdmin): 81 | """ 82 | to do: 83 | create a user section 84 | """ 85 | search_fields = ('user', 'score', ) 86 | 87 | 88 | class TFQuestionAdmin(admin.ModelAdmin): 89 | list_display = ('content', 'category', ) 90 | list_filter = ('category',) 91 | fields = ('content', 'category', 'sub_category', 92 | 'figure', 'quiz', 'explanation', 'correct',) 93 | 94 | search_fields = ('content', 'explanation') 95 | filter_horizontal = ('quiz',) 96 | 97 | 98 | class EssayQuestionAdmin(admin.ModelAdmin): 99 | list_display = ('content', 'category', ) 100 | list_filter = ('category',) 101 | fields = ('content', 'category', 'sub_category', 'quiz', 'explanation', ) 102 | search_fields = ('content', 'explanation') 103 | filter_horizontal = ('quiz',) 104 | 105 | admin.site.register(Quiz, QuizAdmin) 106 | admin.site.register(Category, CategoryAdmin) 107 | admin.site.register(SubCategory, SubCategoryAdmin) 108 | admin.site.register(MCQuestion, MCQuestionAdmin) 109 | admin.site.register(Progress, ProgressAdmin) 110 | admin.site.register(TF_Question, TFQuestionAdmin) 111 | admin.site.register(Essay_Question, EssayQuestionAdmin) 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Django quiz app 2 | =============== 3 | [![Build Status](https://travis-ci.org/tomwalker/django_quiz.svg?branch=master)](https://travis-ci.org/tomwalker/django_quiz) 4 | 5 | This is a configurable quiz app for Django. 6 | 7 | I use it to run a few medical revision websites. Here is an [example website](http://www.revisemrcp.com/) 8 | 9 | My websites have used twitter bootstrap for the front end and I have tried to strip out anything from 10 | the template files that are dependant on bootstrap. 11 | 12 | ![Questions](http://i.imgur.com/VRYx3OV.png "Question picture hosted by Imgur") 13 | 14 | 15 | Current features 16 | ---------------- 17 | Features of each quiz: 18 | * Question order randomisation 19 | * Storing of quiz results under each user 20 | * Previous quiz scores can be viewed on category page 21 | * Correct answers can be shown after each question or all at once at the end 22 | * Logged in users can return to an incomplete quiz to finish it and non-logged in users can complete a quiz if their session persists 23 | * The quiz can be limited to one attempt per user 24 | * Questions can be given a category and subcategory 25 | * Success rate for each category can be monitored on a progress page 26 | * Explanation for each question result can be given 27 | * Pass marks can be set 28 | * Multiple choice question type 29 | * True/False question type 30 | * Essay question type 31 | * Display an image alongside the question 32 | * Custom message displayed for those that pass or fail a quiz 33 | * Custom permission (view_sittings) added, allowing users with that permission to view quiz results from users 34 | * A marking page which lists completed quizzes, can be filtered by quiz or user, and is used to mark essay questions 35 | * After selecting a larger pool of questions, a quiz can be set to show a random subset rather than all within the pool 36 | * Start and end times for sitting exams are recorded 37 | * i18n support 38 | * Russian and Italian language translation 39 | 40 | 41 | 42 | 43 | ![Result page](http://i.imgur.com/UJtRZxo.png "Result picture hosted by Imgur") 44 | 45 | Requirements 46 | ------------ 47 | django-model-utils 48 | 49 | Pillow 50 | 51 | Tests are included and pass for Django versions 1.5, 1.6, 1.7 and 1.8, running with Python 2.7, 3.3 and 3.4 52 | 53 | Installation 54 | ------------ 55 | Clone the repo with `git clone https://github.com/tomwalker/django_quiz.git`. 56 | 57 | Run `pip install -r requirements.txt`. 58 | Run `python setup.py install` 59 | 60 | Add `'quiz', 'multichoice', 'true_false', 'essay'` to your `INSTALLED_APPS` setting. 61 | 62 | INSTALLED_APPS = ( 63 | ... 64 | 'quiz', 65 | 'multichoice', 66 | 'true_false', 67 | 'essay', 68 | ... 69 | ) 70 | 71 | Add the following to your projects `urls.py` file, substituting `q` for whatever you want the quiz base url to be. 72 | 73 | urlpatterns = patterns('', 74 | ... 75 | url(r'^q/', include('quiz.urls')), 76 | ... 77 | ) 78 | 79 | 80 | Contributors 81 | ------------ 82 | * [https://github.com/certifiedloud](https://github.com/certifiedloud) 83 | * [https://github.com/crackjack](https://github.com/crackjack) 84 | * [https://github.com/richardmansfield](https://github.com/richardmansfield) 85 | * [https://github.com/rkashapov](https://github.com/rkashapov) 86 | * [https://github.com/zamphatta](https://github.com/zamphatta) 87 | * [https://github.com/d0ugal](https://github.com/d0ugal) 88 | * [https://github.com/swfiua](https://github.com/swfiua) 89 | 90 | 91 | 92 | MIT License (MIT) 93 | Copyright (c) 2012 - 2015 Dr Tom Walker 94 | 95 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 96 | 97 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 98 | 99 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 100 | -------------------------------------------------------------------------------- /test_settings.py: -------------------------------------------------------------------------------- 1 | import django 2 | 3 | DEBUG = True 4 | DEBUG_PROPAGATE_EXCEPTIONS = True 5 | 6 | ALLOWED_HOSTS = ['*'] 7 | 8 | ADMINS = ( 9 | # ('Your Name', 'your_email@domain.com'), 10 | ) 11 | 12 | MANAGERS = ADMINS 13 | 14 | DATABASES = { 15 | 'default': { 16 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 17 | 'NAME': 'sqlite.db', # Or path to database file if using sqlite3. 18 | 'USER': '', # Not used with sqlite3. 19 | 'PASSWORD': '', # Not used with sqlite3. 20 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 21 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. 22 | } 23 | } 24 | 25 | CACHES = { 26 | 'default': { 27 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 28 | } 29 | } 30 | 31 | # Local time zone for this installation. Choices can be found here: 32 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 33 | # although not all choices may be available on all operating systems. 34 | # On Unix systems, a value of None will cause Django to use the same 35 | # timezone as the operating system. 36 | # If running in a Windows environment this must be set to the same as your 37 | # system time zone. 38 | TIME_ZONE = 'Europe/London' 39 | 40 | # Language code for this installation. All choices can be found here: 41 | # http://www.i18nguy.com/unicode/language-identifiers.html 42 | LANGUAGE_CODE = 'en-uk' 43 | 44 | SITE_ID = 1 45 | 46 | # If you set this to False, Django will make some optimizations so as not 47 | # to load the internationalization machinery. 48 | USE_I18N = True 49 | 50 | # If you set this to False, Django will not format dates, numbers and 51 | # calendars according to the current locale 52 | USE_L10N = True 53 | 54 | # Absolute filesystem path to the directory that will hold user-uploaded files. 55 | # Example: "/home/media/media.lawrence.com/" 56 | MEDIA_ROOT = '' 57 | 58 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 59 | # trailing slash if there is a path component (optional in other cases). 60 | # Examples: "http://media.lawrence.com", "http://example.com/media/" 61 | MEDIA_URL = '' 62 | 63 | # Make this unique, and don't share it with anybody. 64 | SECRET_KEY = 'boing!' 65 | 66 | TEMPLATES = [ 67 | { 68 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 69 | 'DIRS': [], 70 | 'APP_DIRS': True, 71 | 'OPTIONS': { 72 | 'context_processors': [ 73 | 'django.contrib.auth.context_processors.auth', 74 | 'django.template.context_processors.debug', 75 | 'django.template.context_processors.i18n', 76 | 'django.template.context_processors.media', 77 | 'django.template.context_processors.static', 78 | 'django.template.context_processors.tz', 79 | 'django.contrib.messages.context_processors.messages', 80 | ], 81 | }, 82 | }, 83 | ] 84 | 85 | if django.VERSION >= (1, 10): 86 | MIDDLEWARE = ( 87 | 'django.middleware.common.CommonMiddleware', 88 | 'django.contrib.sessions.middleware.SessionMiddleware', 89 | 'django.middleware.csrf.CsrfViewMiddleware', 90 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 91 | 'django.contrib.messages.middleware.MessageMiddleware', 92 | ) 93 | else: 94 | MIDDLEWARE_CLASSES = ( 95 | 'django.middleware.common.CommonMiddleware', 96 | 'django.contrib.sessions.middleware.SessionMiddleware', 97 | 'django.middleware.csrf.CsrfViewMiddleware', 98 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 99 | 'django.contrib.messages.middleware.MessageMiddleware', 100 | ) 101 | 102 | ROOT_URLCONF = 'quiz.urls' 103 | 104 | TEMPLATE_DIRS = ( 105 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 106 | # Always use forward slashes, even on Windows. 107 | # Don't forget to use absolute paths, not relative paths. 108 | ) 109 | 110 | INSTALLED_APPS = ( 111 | 'django.contrib.auth', 112 | 'django.contrib.contenttypes', 113 | 'django.contrib.sessions', 114 | 'django.contrib.sites', 115 | 'django.contrib.messages', 116 | # Uncomment the next line to enable the admin: 117 | # 'django.contrib.admin', 118 | # Uncomment the next line to enable admin documentation: 119 | # 'django.contrib.admindocs', 120 | 'quiz', 121 | 'multichoice', 122 | 'true_false', 123 | 'essay', 124 | ) 125 | 126 | 127 | STATIC_URL = '/static/' 128 | 129 | PASSWORD_HASHERS = ( 130 | 'django.contrib.auth.hashers.SHA1PasswordHasher', 131 | 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 132 | 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 133 | 'django.contrib.auth.hashers.BCryptPasswordHasher', 134 | 'django.contrib.auth.hashers.MD5PasswordHasher', 135 | 'django.contrib.auth.hashers.CryptPasswordHasher', 136 | ) 137 | 138 | AUTH_USER_MODEL = 'auth.User' 139 | -------------------------------------------------------------------------------- /quiz/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.3 on 2017-06-22 11:20 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | import django.core.validators 7 | from django.db import migrations, models 8 | import django.db.models.deletion 9 | import re 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | initial = True 15 | 16 | dependencies = [ 17 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 18 | ] 19 | 20 | operations = [ 21 | migrations.CreateModel( 22 | name='Category', 23 | fields=[ 24 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 25 | ('category', models.CharField(blank=True, max_length=250, null=True, unique=True, verbose_name='Category')), 26 | ], 27 | options={ 28 | 'verbose_name': 'Category', 29 | 'verbose_name_plural': 'Categories', 30 | }, 31 | ), 32 | migrations.CreateModel( 33 | name='Progress', 34 | fields=[ 35 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 36 | ('score', models.CharField(max_length=1024, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:\\,\\d+)*\\Z', 32), code='invalid', message='Enter only digits separated by commas.')], verbose_name='Score')), 37 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')), 38 | ], 39 | options={ 40 | 'verbose_name': 'User Progress', 41 | 'verbose_name_plural': 'User progress records', 42 | }, 43 | ), 44 | migrations.CreateModel( 45 | name='Question', 46 | fields=[ 47 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 48 | ('figure', models.ImageField(blank=True, null=True, upload_to='uploads/%Y/%m/%d', verbose_name='Figure')), 49 | ('content', models.CharField(help_text='Enter the question text that you want displayed', max_length=1000, verbose_name='Question')), 50 | ('explanation', models.TextField(blank=True, help_text='Explanation to be shown after the question has been answered.', max_length=2000, verbose_name='Explanation')), 51 | ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='quiz.Category', verbose_name='Category')), 52 | ], 53 | options={ 54 | 'verbose_name': 'Question', 55 | 'verbose_name_plural': 'Questions', 56 | 'ordering': ['category'], 57 | }, 58 | ), 59 | migrations.CreateModel( 60 | name='Quiz', 61 | fields=[ 62 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 63 | ('title', models.CharField(max_length=60, verbose_name='Title')), 64 | ('description', models.TextField(blank=True, help_text='a description of the quiz', verbose_name='Description')), 65 | ('url', models.SlugField(help_text='a user friendly url', max_length=60, verbose_name='user friendly url')), 66 | ('random_order', models.BooleanField(default=False, help_text='Display the questions in a random order or as they are set?', verbose_name='Random Order')), 67 | ('max_questions', models.PositiveIntegerField(blank=True, help_text='Number of questions to be answered on each attempt.', null=True, verbose_name='Max Questions')), 68 | ('answers_at_end', models.BooleanField(default=False, help_text='Correct answer is NOT shown after question. Answers displayed at the end.', verbose_name='Answers at end')), 69 | ('exam_paper', models.BooleanField(default=False, help_text='If yes, the result of each attempt by a user will be stored. Necessary for marking.', verbose_name='Exam Paper')), 70 | ('single_attempt', models.BooleanField(default=False, help_text='If yes, only one attempt by a user will be permitted. Non users cannot sit this exam.', verbose_name='Single Attempt')), 71 | ('pass_mark', models.SmallIntegerField(blank=True, default=0, help_text='Percentage required to pass exam.', validators=[django.core.validators.MaxValueValidator(100)], verbose_name='Pass Mark')), 72 | ('success_text', models.TextField(blank=True, help_text='Displayed if user passes.', verbose_name='Success Text')), 73 | ('fail_text', models.TextField(blank=True, help_text='Displayed if user fails.', verbose_name='Fail Text')), 74 | ('draft', models.BooleanField(default=False, help_text='If yes, the quiz is not displayed in the quiz list and can only be taken by users who can edit quizzes.', verbose_name='Draft')), 75 | ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='quiz.Category', verbose_name='Category')), 76 | ], 77 | options={ 78 | 'verbose_name': 'Quiz', 79 | 'verbose_name_plural': 'Quizzes', 80 | }, 81 | ), 82 | migrations.CreateModel( 83 | name='Sitting', 84 | fields=[ 85 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 86 | ('question_order', models.CharField(max_length=1024, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:\\,\\d+)*\\Z', 32), code='invalid', message='Enter only digits separated by commas.')], verbose_name='Question Order')), 87 | ('question_list', models.CharField(max_length=1024, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:\\,\\d+)*\\Z', 32), code='invalid', message='Enter only digits separated by commas.')], verbose_name='Question List')), 88 | ('incorrect_questions', models.CharField(blank=True, max_length=1024, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:\\,\\d+)*\\Z', 32), code='invalid', message='Enter only digits separated by commas.')], verbose_name='Incorrect questions')), 89 | ('current_score', models.IntegerField(verbose_name='Current Score')), 90 | ('complete', models.BooleanField(default=False, verbose_name='Complete')), 91 | ('user_answers', models.TextField(blank=True, default='{}', verbose_name='User Answers')), 92 | ('start', models.DateTimeField(auto_now_add=True, verbose_name='Start')), 93 | ('end', models.DateTimeField(blank=True, null=True, verbose_name='End')), 94 | ('quiz', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='quiz.Quiz', verbose_name='Quiz')), 95 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')), 96 | ], 97 | options={ 98 | 'permissions': (('view_sittings', 'Can see completed exams.'),), 99 | }, 100 | ), 101 | migrations.CreateModel( 102 | name='SubCategory', 103 | fields=[ 104 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 105 | ('sub_category', models.CharField(blank=True, max_length=250, null=True, verbose_name='Sub-Category')), 106 | ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='quiz.Category', verbose_name='Category')), 107 | ], 108 | options={ 109 | 'verbose_name': 'Sub-Category', 110 | 'verbose_name_plural': 'Sub-Categories', 111 | }, 112 | ), 113 | migrations.AddField( 114 | model_name='question', 115 | name='quiz', 116 | field=models.ManyToManyField(blank=True, to='quiz.Quiz', verbose_name='Quiz'), 117 | ), 118 | migrations.AddField( 119 | model_name='question', 120 | name='sub_category', 121 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='quiz.SubCategory', verbose_name='Sub-Category'), 122 | ), 123 | ] 124 | -------------------------------------------------------------------------------- /quiz/locale/zh_CN/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 12 | "PO-Revision-Date: 2015-10-31 22:43+0000\n" 13 | "Last-Translator: b' <>'\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | "X-Translated-Using: django-rosetta 0.7.6\n" 21 | 22 | #: apps/quiz/models.py:30 apps/quiz/models.py:37 apps/quiz/models.py:53 23 | #: apps/quiz/models.py:83 apps/quiz/models.py:545 24 | #: apps/quiz/templates/progress.html:19 25 | #: apps/quiz/templates/quiz/quiz_detail.html:9 26 | #: apps/quiz/templates/quiz/quiz_list.html:13 27 | #: apps/quiz/templates/quiz/sitting_detail.html:10 28 | msgid "Category" 29 | msgstr "类别" 30 | 31 | #: apps/quiz/models.py:38 32 | msgid "Categories" 33 | msgstr "类别" 34 | 35 | #: apps/quiz/models.py:48 apps/quiz/models.py:58 apps/quiz/models.py:550 36 | msgid "Sub-Category" 37 | msgstr "子类" 38 | 39 | #: apps/quiz/models.py:59 40 | msgid "Sub-Categories" 41 | msgstr "子类" 42 | 43 | #: apps/quiz/models.py:69 apps/quiz/templates/quiz/quiz_list.html:12 44 | msgid "Title" 45 | msgstr "标题" 46 | 47 | #: apps/quiz/models.py:73 48 | msgid "Description" 49 | msgstr "描述" 50 | 51 | #: apps/quiz/models.py:74 52 | msgid "a description of the quiz" 53 | msgstr "测试的描述" 54 | 55 | #: apps/quiz/models.py:78 56 | msgid "a user friendly url" 57 | msgstr "一个用户友好的URL" 58 | 59 | #: apps/quiz/models.py:79 60 | msgid "user friendly url" 61 | msgstr "用户友好的URL" 62 | 63 | #: apps/quiz/models.py:87 64 | msgid "Random Order" 65 | msgstr "随机次序" 66 | 67 | #: apps/quiz/models.py:88 68 | msgid "Display the questions in a random order or as they are set?" 69 | msgstr "按设定的次序还是随机次序显示问题?" 70 | 71 | #: apps/quiz/models.py:93 72 | msgid "Max Questions" 73 | msgstr "最大问题数量" 74 | 75 | #: apps/quiz/models.py:94 76 | msgid "Number of questions to be answered on each attempt." 77 | msgstr "每次尝试可回答的问题数量。" 78 | 79 | #: apps/quiz/models.py:98 80 | msgid "" 81 | "Correct answer is NOT shown after question. Answers displayed at the end." 82 | msgstr "" 83 | "正确答案不会在问题后显示。回答显示在最后。" 84 | 85 | #: apps/quiz/models.py:100 86 | msgid "Answers at end" 87 | msgstr "最后给出答案" 88 | 89 | #: apps/quiz/models.py:104 90 | msgid "" 91 | "If yes, the result of each attempt by a user will be stored. Necessary for " 92 | "marking." 93 | msgstr "" 94 | "如果是,用户每次尝试的结果都将记录。如需打分此项必选。" 95 | 96 | #: apps/quiz/models.py:107 97 | msgid "Exam Paper" 98 | msgstr "试卷" 99 | 100 | #: apps/quiz/models.py:111 101 | msgid "" 102 | "If yes, only one attempt by a user will be permitted. Non users cannot sit " 103 | "this exam." 104 | msgstr "" 105 | "如果是,一个用户只允许尝试一次。非注册用户不能参加本考试。" 106 | 107 | #: apps/quiz/models.py:114 108 | msgid "Single Attempt" 109 | msgstr "单次尝试" 110 | 111 | #: apps/quiz/models.py:116 112 | msgid "Pass Mark" 113 | msgstr "通过分数" 114 | 115 | #: apps/quiz/models.py:118 116 | msgid "Percentage required to pass exam." 117 | msgstr "通过考试需要的百分数。" 118 | 119 | #: apps/quiz/models.py:122 120 | msgid "Displayed if user passes." 121 | msgstr "如果用户通过则显示。" 122 | 123 | #: apps/quiz/models.py:123 124 | msgid "Success Text" 125 | msgstr "成功文本" 126 | 127 | #: apps/quiz/models.py:126 128 | msgid "Fail Text" 129 | msgstr "失败文本" 130 | 131 | #: apps/quiz/models.py:127 132 | msgid "Displayed if user fails." 133 | msgstr "如果用户未通过则显示" 134 | 135 | #: apps/quiz/models.py:131 136 | msgid "Draft" 137 | msgstr "草稿" 138 | 139 | #: apps/quiz/models.py:132 140 | msgid "" 141 | "If yes, the quiz is not displayed in the quiz list and can only be taken by " 142 | "users who can edit quizzes." 143 | msgstr "" 144 | "如果是,测试不会显示在测试列表里,只能被可以编辑测试的用户操作。" 145 | 146 | #: apps/quiz/models.py:152 apps/quiz/models.py:372 apps/quiz/models.py:541 147 | #: apps/quiz/templates/quiz/sitting_list.html:14 148 | msgid "Quiz" 149 | msgstr "测试" 150 | 151 | #: apps/quiz/models.py:153 152 | msgid "Quizzes" 153 | msgstr "测试" 154 | 155 | #: apps/quiz/models.py:192 apps/quiz/models.py:370 156 | #: apps/quiz/templates/quiz/sitting_detail.html:13 157 | #: apps/quiz/templates/quiz/sitting_list.html:13 158 | msgid "User" 159 | msgstr "用户" 160 | 161 | #: apps/quiz/models.py:195 apps/quiz/templates/progress.html:60 162 | #: apps/quiz/templates/quiz/sitting_detail.html:15 163 | #: apps/quiz/templates/quiz/sitting_list.html:16 164 | msgid "Score" 165 | msgstr "得分" 166 | 167 | #: apps/quiz/models.py:200 168 | msgid "User Progress" 169 | msgstr "用户进度" 170 | 171 | #: apps/quiz/models.py:201 172 | msgid "User progress records" 173 | msgstr "用户进度记录" 174 | 175 | #: apps/quiz/models.py:261 176 | msgid "error" 177 | msgstr "错误" 178 | 179 | #: apps/quiz/models.py:261 180 | msgid "category does not exist or invalid score" 181 | msgstr "类别不存在或得分无效" 182 | 183 | #: apps/quiz/models.py:375 184 | msgid "Question Order" 185 | msgstr "问题次序" 186 | 187 | #: apps/quiz/models.py:378 188 | msgid "Question List" 189 | msgstr "问题列表" 190 | 191 | #: apps/quiz/models.py:381 192 | msgid "Incorrect questions" 193 | msgstr "不正确的答案" 194 | 195 | #: apps/quiz/models.py:383 196 | msgid "Current Score" 197 | msgstr "当前得分" 198 | 199 | #: apps/quiz/models.py:386 200 | msgid "Complete" 201 | msgstr "完成" 202 | 203 | #: apps/quiz/models.py:389 204 | msgid "User Answers" 205 | msgstr "用户回答" 206 | 207 | #: apps/quiz/models.py:392 208 | msgid "Start" 209 | msgstr "开始" 210 | 211 | #: apps/quiz/models.py:394 212 | msgid "End" 213 | msgstr "结束" 214 | 215 | #: apps/quiz/models.py:399 216 | msgid "Can see completed exams." 217 | msgstr "可以看到已完成的考试" 218 | 219 | #: apps/quiz/models.py:557 220 | msgid "Figure" 221 | msgstr "图像" 222 | 223 | #: apps/quiz/models.py:561 224 | msgid "Enter the question text that you want displayed" 225 | msgstr "输入你想显示的问题文本" 226 | 227 | #: apps/quiz/models.py:563 apps/quiz/models.py:575 228 | #: apps/quiz/templates/question.html:47 229 | #: apps/quiz/templates/quiz/sitting_detail.html:21 230 | msgid "Question" 231 | msgstr "问题" 232 | 233 | #: apps/quiz/models.py:567 234 | msgid "Explanation to be shown after the question has been answered." 235 | msgstr "回答问题后将展示题解。" 236 | 237 | #: apps/quiz/models.py:570 apps/quiz/templates/question.html:32 238 | #: apps/quiz/templates/result.html:21 apps/quiz/templates/result.html.py:87 239 | msgid "Explanation" 240 | msgstr "题解" 241 | 242 | #: apps/quiz/models.py:576 243 | msgid "Questions" 244 | msgstr "问题" 245 | 246 | #: apps/quiz/templates/base.html:7 247 | msgid "Example Quiz Website" 248 | msgstr "测试样例网站" 249 | 250 | #: apps/quiz/templates/correct_answer.html:6 251 | msgid "You answered the above question incorrectly" 252 | msgstr "你错误回答了以上问题" 253 | 254 | #: apps/quiz/templates/correct_answer.html:16 255 | msgid "This is the correct answer" 256 | msgstr "这是正确答案" 257 | 258 | #: apps/quiz/templates/correct_answer.html:23 259 | msgid "This was your answer." 260 | msgstr "这是你的回答。" 261 | 262 | #: apps/quiz/templates/progress.html:6 263 | msgid "Progress Page" 264 | msgstr "进度页面" 265 | 266 | #: apps/quiz/templates/progress.html:7 267 | msgid "User Progress Page" 268 | msgstr "用户进度页面" 269 | 270 | #: apps/quiz/templates/progress.html:13 271 | msgid "Question Category Scores" 272 | msgstr "问题类别得分" 273 | 274 | #: apps/quiz/templates/progress.html:20 275 | msgid "Correctly answererd" 276 | msgstr "正确回答了" 277 | 278 | #: apps/quiz/templates/progress.html:21 279 | msgid "Incorrect" 280 | msgstr "不正确" 281 | 282 | #: apps/quiz/templates/progress.html:50 283 | msgid "Previous exam papers" 284 | msgstr "前一试卷" 285 | 286 | #: apps/quiz/templates/progress.html:52 287 | msgid "Below are the results of exams that you have sat." 288 | msgstr "以下是你已经参加的考试的结果。" 289 | 290 | #: apps/quiz/templates/progress.html:59 291 | msgid "Quiz Title" 292 | msgstr "测试标题" 293 | 294 | #: apps/quiz/templates/progress.html:61 295 | msgid "Possible Score" 296 | msgstr "可能得分" 297 | 298 | #: apps/quiz/templates/question.html:13 apps/quiz/templates/result.html:13 299 | msgid "The previous question" 300 | msgstr "前一问题" 301 | 302 | #: apps/quiz/templates/question.html:22 303 | msgid "Your answer was" 304 | msgstr "你的回答是" 305 | 306 | #: apps/quiz/templates/question.html:47 307 | msgid "of" 308 | msgstr "属于" 309 | 310 | #: apps/quiz/templates/question.html:52 311 | msgid "Question category" 312 | msgstr "问题类别" 313 | 314 | #: apps/quiz/templates/question.html:74 315 | msgid "Check" 316 | msgstr "检查" 317 | 318 | #: apps/quiz/templates/quiz/category_list.html:3 319 | #: apps/quiz/templates/quiz/quiz_list.html:3 320 | #: apps/quiz/templates/quiz/sitting_list.html:3 321 | msgid "All Quizzes" 322 | msgstr "全部测试" 323 | 324 | #: apps/quiz/templates/quiz/category_list.html:6 325 | msgid "Category list" 326 | msgstr "类别列表" 327 | 328 | #: apps/quiz/templates/quiz/quiz_detail.html:11 329 | msgid "You will only get one attempt at this quiz" 330 | msgstr "本测试你只有一次尝试的机会" 331 | 332 | #: apps/quiz/templates/quiz/quiz_detail.html:16 333 | msgid "Start quiz" 334 | msgstr "开始测试" 335 | 336 | #: apps/quiz/templates/quiz/quiz_list.html:6 337 | msgid "List of quizzes" 338 | msgstr "测试列表" 339 | 340 | #: apps/quiz/templates/quiz/quiz_list.html:14 341 | msgid "Exam" 342 | msgstr "考试" 343 | 344 | #: apps/quiz/templates/quiz/quiz_list.html:15 345 | msgid "Single attempt" 346 | msgstr "单一尝试" 347 | 348 | #: apps/quiz/templates/quiz/quiz_list.html:31 349 | #: apps/quiz/templates/quiz/sitting_list.html:42 350 | msgid "View details" 351 | msgstr "显示细节" 352 | 353 | #: apps/quiz/templates/quiz/quiz_list.html:41 354 | msgid "There are no available quizzes" 355 | msgstr "没有可做的测试" 356 | 357 | #: apps/quiz/templates/quiz/sitting_detail.html:5 358 | msgid "Result of" 359 | msgstr "结果属于" 360 | 361 | #: apps/quiz/templates/quiz/sitting_detail.html:5 362 | msgid "for" 363 | msgstr "为了" 364 | 365 | #: apps/quiz/templates/quiz/sitting_detail.html:9 366 | msgid "Quiz title" 367 | msgstr "测试标题" 368 | 369 | #: apps/quiz/templates/quiz/sitting_detail.html:14 370 | #: apps/quiz/templates/quiz/sitting_list.html:15 371 | msgid "Completed" 372 | msgstr "已完成" 373 | 374 | #: apps/quiz/templates/quiz/sitting_detail.html:22 375 | msgid "User answer" 376 | msgstr "用户的回答" 377 | 378 | #: apps/quiz/templates/quiz/sitting_detail.html:41 379 | msgid "incorrect" 380 | msgstr "不正确" 381 | 382 | #: apps/quiz/templates/quiz/sitting_detail.html:43 383 | msgid "Correct" 384 | msgstr "正确" 385 | 386 | #: apps/quiz/templates/quiz/sitting_detail.html:49 387 | msgid "Toggle whether correct" 388 | msgstr "切换是否正确" 389 | 390 | #: apps/quiz/templates/quiz/sitting_list.html:6 391 | msgid "List of complete exams" 392 | msgstr "已完成考试列表" 393 | 394 | #: apps/quiz/templates/quiz/sitting_list.html:28 395 | msgid "Filter" 396 | msgstr "过滤" 397 | 398 | #: apps/quiz/templates/quiz/sitting_list.html:52 399 | msgid "There are no matching quizzes" 400 | msgstr "没有符合条件的测试" 401 | 402 | #: apps/quiz/templates/result.html:7 403 | msgid "Exam Results for" 404 | msgstr "考试结果" 405 | 406 | #: apps/quiz/templates/result.html:32 407 | msgid "Exam results" 408 | msgstr "考试结果" 409 | 410 | #: apps/quiz/templates/result.html:34 411 | msgid "Exam title" 412 | msgstr "考试标题" 413 | 414 | #: apps/quiz/templates/result.html:38 415 | msgid "You answered" 416 | msgstr "你正确回答了" 417 | 418 | #: apps/quiz/templates/result.html:38 419 | msgid "questions correctly out of" 420 | msgstr "个问题,总共" 421 | 422 | #: apps/quiz/templates/result.html:38 423 | msgid "giving you" 424 | msgstr "个问题" 425 | 426 | #: apps/quiz/templates/result.html:38 427 | msgid "percent correct" 428 | msgstr "%正确率" 429 | 430 | #: apps/quiz/templates/result.html:48 431 | msgid "Review the questions below and try the exam again in the future" 432 | msgstr "回顾以下问题,将来可以再尝试本考试" 433 | 434 | #: apps/quiz/templates/result.html:52 435 | msgid "" 436 | "The result of this exam will be stored in your progress section so you can " 437 | "review and monitor your progression" 438 | msgstr "本考试的结果将储存于你的进度区域,你可以查看和监控你的进度。" 439 | 440 | #: apps/quiz/templates/result.html:66 441 | msgid "Your session score is" 442 | msgstr "你目前得分为" 443 | 444 | #: apps/quiz/templates/result.html:66 445 | msgid "out of a possible" 446 | msgstr "而最多可能得分为" 447 | 448 | #: apps/quiz/templates/result.html:84 449 | msgid "Your answer" 450 | msgstr "你的答案" 451 | 452 | #: apps/quiz/templates/single_complete.html:13 453 | msgid "You have already sat this exam and only one sitting is permitted" 454 | msgstr "你已经参加本考试,只能参加一次" 455 | 456 | #: apps/quiz/templates/single_complete.html:15 457 | msgid "This exam is only accessible to signed in users" 458 | msgstr "用户需登录方可访问本考试" 459 | 460 | #: apps/quiz/templates/view_quiz_category.html:3 461 | msgid "Quizzes related to" 462 | msgstr "测试有关于" 463 | 464 | #: apps/quiz/templates/view_quiz_category.html:6 465 | msgid "Quizzes in the" 466 | msgstr "测试属于" 467 | 468 | #: apps/quiz/templates/view_quiz_category.html:6 469 | msgid "category" 470 | msgstr "类别" 471 | 472 | #: apps/quiz/templates/view_quiz_category.html:20 473 | msgid "There are no quizzes" 474 | msgstr "没有测试" 475 | -------------------------------------------------------------------------------- /quiz/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-quiz\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2016-01-01 11:37+0400\n" 11 | "PO-Revision-Date: 2015-08-21 19:40+0500\n" 12 | "Last-Translator: Eugena Mihailikova \n" 13 | "Language-Team: LANGUAGE \n" 14 | "Language: ru\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 19 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 20 | "X-Generator: Poedit 1.5.4\n" 21 | 22 | #: admin.py:31 admin.py:33 models.py:576 23 | msgid "Questions" 24 | msgstr "Вопросы" 25 | 26 | #: models.py:30 models.py:37 models.py:53 models.py:83 models.py:545 27 | #: templates/progress.html:19 templates/quiz/quiz_detail.html:9 28 | #: templates/quiz/quiz_list.html:13 templates/quiz/sitting_detail.html:10 29 | msgid "Category" 30 | msgstr "Категория" 31 | 32 | #: models.py:38 33 | msgid "Categories" 34 | msgstr "Категории" 35 | 36 | #: models.py:48 models.py:58 models.py:550 37 | msgid "Sub-Category" 38 | msgstr "Подкатегория" 39 | 40 | #: models.py:59 41 | msgid "Sub-Categories" 42 | msgstr "Подкатегории" 43 | 44 | #: models.py:69 templates/quiz/quiz_list.html:12 45 | msgid "Title" 46 | msgstr "Название" 47 | 48 | #: models.py:73 49 | msgid "Description" 50 | msgstr "Описание" 51 | 52 | #: models.py:74 53 | msgid "a description of the quiz" 54 | msgstr "описание теста" 55 | 56 | #: models.py:78 57 | msgid "a user friendly url" 58 | msgstr "url теста" 59 | 60 | #: models.py:79 61 | msgid "user friendly url" 62 | msgstr "url теста" 63 | 64 | #: models.py:87 65 | msgid "Random Order" 66 | msgstr "Случайный порядок" 67 | 68 | #: models.py:88 69 | msgid "Display the questions in a random order or as they are set?" 70 | msgstr "Отображать вопросы в случайном порядке или в порядке добавления?" 71 | 72 | #: models.py:93 73 | msgid "Max Questions" 74 | msgstr "Максимальное количество вопросов" 75 | 76 | #: models.py:94 77 | msgid "Number of questions to be answered on each attempt." 78 | msgstr "" 79 | "Количество вопросов, на которые должны быть даны ответы при каждой попытке" 80 | 81 | #: models.py:98 82 | msgid "" 83 | "Correct answer is NOT shown after question. Answers displayed at the end." 84 | msgstr "" 85 | "Правильный ответ НЕ показан после вопроса. Ответы отображаются после " 86 | "прохождения теста" 87 | 88 | #: models.py:100 89 | msgid "Answers at end" 90 | msgstr "Ответы в конце" 91 | 92 | #: models.py:104 93 | msgid "" 94 | "If yes, the result of each attempt by a user will be stored. Necessary for " 95 | "marking." 96 | msgstr "Если отмечено, результаты каждой попытки пользователя будет сохранен" 97 | 98 | #: models.py:107 99 | msgid "Exam Paper" 100 | msgstr "Экзаменационный лист" 101 | 102 | #: models.py:111 103 | msgid "" 104 | "If yes, only one attempt by a user will be permitted. Non users cannot sit " 105 | "this exam." 106 | msgstr "Если отмечено, пользователю будет разрешена только одна попытка" 107 | 108 | #: models.py:114 109 | msgid "Single Attempt" 110 | msgstr "Единственная попытка" 111 | 112 | #: models.py:118 113 | msgid "Percentage required to pass exam." 114 | msgstr "Процент правильных ответов для прохождения теста" 115 | 116 | #: models.py:122 117 | msgid "Displayed if user passes." 118 | msgstr "Отображается, если пользователь успешно прошел тест" 119 | 120 | #: models.py:123 121 | msgid "Success Text" 122 | msgstr "Текст в случае успеха" 123 | 124 | #: models.py:126 125 | msgid "Fail Text" 126 | msgstr "Текст в случае неудачи" 127 | 128 | #: models.py:127 129 | msgid "Displayed if user fails." 130 | msgstr "Отображается, если пользователь провалил тест" 131 | 132 | #: models.py:131 133 | msgid "Draft" 134 | msgstr "Черновик" 135 | 136 | #: models.py:132 137 | msgid "" 138 | "If yes, the quiz is not displayed in the quiz list and can only be taken by " 139 | "users who can edit quizzes." 140 | msgstr "Если отмечено, то не отображается в публичном списке и может быть " 141 | "взято только пользователями с соответствующим правом" 142 | 143 | #: models.py:152 models.py:372 models.py:541 144 | #: templates/quiz/sitting_list.html:14 145 | msgid "Quiz" 146 | msgstr "Тест" 147 | 148 | #: models.py:153 149 | msgid "Quizzes" 150 | msgstr "Тесты" 151 | 152 | #: models.py:192 models.py:370 templates/quiz/sitting_detail.html:13 153 | #: templates/quiz/sitting_list.html:13 154 | msgid "User" 155 | msgstr "Пользователь" 156 | 157 | #: models.py:195 templates/progress.html:60 158 | #: templates/quiz/sitting_detail.html:15 templates/quiz/sitting_list.html:16 159 | msgid "Score" 160 | msgstr "Баллы" 161 | 162 | #: models.py:200 163 | msgid "User Progress" 164 | msgstr "Прогресс пользователя" 165 | 166 | #: models.py:201 167 | msgid "User progress records" 168 | msgstr "Прогресс пользователя" 169 | 170 | #: models.py:261 171 | msgid "error" 172 | msgstr "ошибка" 173 | 174 | #: models.py:261 175 | msgid "category does not exist or invalid score" 176 | msgstr "категории не существует или недопустимый балл" 177 | 178 | #: models.py:375 179 | msgid "Question Order" 180 | msgstr "Порядок вопросов" 181 | 182 | #: models.py:378 183 | msgid "Question List" 184 | msgstr "Список вопросов" 185 | 186 | #: models.py:381 187 | msgid "Incorrect questions" 188 | msgstr "Вопросы, на которые дан неверный ответ" 189 | 190 | #: models.py:383 191 | msgid "Current Score" 192 | msgstr "Текущий балл" 193 | 194 | #: models.py:386 195 | msgid "Complete" 196 | msgstr "Завершен" 197 | 198 | #: models.py:389 199 | msgid "User Answers" 200 | msgstr "Ответы пользователя" 201 | 202 | #: models.py:392 203 | msgid "Start" 204 | msgstr "Начало" 205 | 206 | #: models.py:394 207 | msgid "End" 208 | msgstr "Окончание" 209 | 210 | #: models.py:399 211 | msgid "Can see completed exams." 212 | msgstr "Может просматривать оконченные тесты" 213 | 214 | #: models.py:557 215 | msgid "Figure" 216 | msgstr "Рисунок" 217 | 218 | #: models.py:561 219 | msgid "Enter the question text that you want displayed" 220 | msgstr "Введите текст вопроса, который должен отобразиться" 221 | 222 | #: models.py:563 models.py:575 templates/question.html:47 223 | #: templates/quiz/sitting_detail.html:21 224 | msgid "Question" 225 | msgstr "Вопрос" 226 | 227 | #: models.py:567 228 | msgid "Explanation to be shown after the question has been answered." 229 | msgstr "Объяснение показывается после того, как дан ответ на вопрос" 230 | 231 | #: models.py:570 templates/question.html:32 templates/result.html:21 232 | #: templates/result.html.py:87 233 | msgid "Explanation" 234 | msgstr "Объяснение" 235 | 236 | #: templates/base.html:7 237 | msgid "Example Quiz Website" 238 | msgstr "Тесты" 239 | 240 | #: templates/correct_answer.html:6 241 | msgid "You answered the above question incorrectly" 242 | msgstr "Вы дали неверный ответ" 243 | 244 | #: templates/correct_answer.html:16 245 | msgid "This is the correct answer" 246 | msgstr "Это правильный ответ" 247 | 248 | #: templates/correct_answer.html:23 249 | msgid "This was your answer." 250 | msgstr "Это был ваш ответ" 251 | 252 | #: templates/progress.html:6 253 | msgid "Progress Page" 254 | msgstr "Страница прогесса" 255 | 256 | #: templates/progress.html:7 257 | msgid "User Progress Page" 258 | msgstr "Страница прогресса пользователя" 259 | 260 | #: templates/progress.html:13 261 | msgid "Question Category Scores" 262 | msgstr "Баллы по категориям вопросов" 263 | 264 | #: templates/progress.html:20 265 | msgid "Correctly answererd" 266 | msgstr "Верных ответов" 267 | 268 | #: templates/progress.html:21 269 | msgid "Incorrect" 270 | msgstr "Неверных ответов" 271 | 272 | #: templates/progress.html:50 273 | msgid "Previous exam papers" 274 | msgstr "Список предыдущих экзаменов" 275 | 276 | #: templates/progress.html:52 277 | msgid "Below are the results of exams that you have sat." 278 | msgstr "Ниже представлены результаты пройденных Вами тестов" 279 | 280 | #: templates/progress.html:59 281 | msgid "Quiz Title" 282 | msgstr "Название теста" 283 | 284 | #: templates/progress.html:61 285 | msgid "Possible Score" 286 | msgstr "Возможный балл" 287 | 288 | #: templates/question.html:13 templates/result.html:13 289 | msgid "The previous question" 290 | msgstr "Предыдущий вопрос" 291 | 292 | #: templates/question.html:22 293 | msgid "Your answer was" 294 | msgstr "Ваш ответ был" 295 | 296 | #: templates/question.html:47 297 | msgid "of" 298 | msgstr "из" 299 | 300 | #: templates/question.html:52 301 | msgid "Question category" 302 | msgstr "Категория вопроса" 303 | 304 | #: templates/question.html:74 305 | msgid "Check" 306 | msgstr "Ответить" 307 | 308 | #: templates/quiz/category_list.html:3 templates/quiz/quiz_list.html:3 309 | #: templates/quiz/sitting_list.html:3 310 | msgid "All Quizzes" 311 | msgstr "Все тесты" 312 | 313 | #: templates/quiz/category_list.html:6 314 | msgid "Category list" 315 | msgstr "Список категорий" 316 | 317 | #: templates/quiz/quiz_detail.html:11 318 | msgid "You will only get one attempt at this quiz" 319 | msgstr "У вас есть одна попытка для прохождения данного теста" 320 | 321 | #: templates/quiz/quiz_detail.html:16 322 | msgid "Start quiz" 323 | msgstr "Начать тест" 324 | 325 | #: templates/quiz/quiz_list.html:6 326 | msgid "List of quizzes" 327 | msgstr "Список тестов" 328 | 329 | #: templates/quiz/quiz_list.html:14 330 | msgid "Exam" 331 | msgstr "Тестирование" 332 | 333 | #: templates/quiz/quiz_list.html:15 334 | msgid "Single attempt" 335 | msgstr "Единственная попытка" 336 | 337 | #: templates/quiz/quiz_list.html:31 templates/quiz/sitting_list.html:42 338 | msgid "View details" 339 | msgstr "Подробнее" 340 | 341 | #: templates/quiz/quiz_list.html:41 342 | msgid "There are no available quizzes" 343 | msgstr "Доступных тестов нет" 344 | 345 | #: templates/quiz/sitting_detail.html:5 346 | msgid "Result of" 347 | msgstr "Результаты" 348 | 349 | #: templates/quiz/sitting_detail.html:5 350 | msgid "for" 351 | msgstr "для" 352 | 353 | #: templates/quiz/sitting_detail.html:9 354 | msgid "Quiz title" 355 | msgstr "Назвние теста" 356 | 357 | #: templates/quiz/sitting_detail.html:14 templates/quiz/sitting_list.html:15 358 | msgid "Completed" 359 | msgstr "Завершено" 360 | 361 | #: templates/quiz/sitting_detail.html:22 362 | msgid "User answer" 363 | msgstr "Ответ пользователя" 364 | 365 | #: templates/quiz/sitting_detail.html:41 366 | msgid "incorrect" 367 | msgstr "Неверно" 368 | 369 | #: templates/quiz/sitting_detail.html:43 370 | msgid "Correct" 371 | msgstr "Верно" 372 | 373 | #: templates/quiz/sitting_detail.html:49 374 | msgid "Toggle whether correct" 375 | msgstr "Изменить результат" 376 | 377 | #: templates/quiz/sitting_list.html:6 378 | msgid "List of complete exams" 379 | msgstr "Список завершенных тестов" 380 | 381 | #: templates/quiz/sitting_list.html:28 382 | msgid "Filter" 383 | msgstr "Фильтр" 384 | 385 | #: templates/quiz/sitting_list.html:52 386 | msgid "There are no matching quizzes" 387 | msgstr "Подходящих тестов нет" 388 | 389 | #: templates/result.html:7 390 | msgid "Exam Results for" 391 | msgstr "Результат теста для" 392 | 393 | #: templates/result.html:32 394 | msgid "Exam results" 395 | msgstr "Результаты тестирования" 396 | 397 | #: templates/result.html:34 398 | msgid "Exam title" 399 | msgstr "Название теста" 400 | 401 | #: templates/result.html:38 402 | msgid "You answered" 403 | msgstr "Ваш результат" 404 | 405 | #: templates/result.html:38 406 | msgid "questions correctly out of" 407 | msgstr "правильных ответов из" 408 | 409 | #: templates/result.html:38 410 | msgid "giving you" 411 | msgstr "вы дали" 412 | 413 | #: templates/result.html:38 414 | msgid "percent correct" 415 | msgstr "процент правильных ответов" 416 | 417 | #: templates/result.html:48 418 | msgid "Review the questions below and try the exam again in the future" 419 | msgstr "" 420 | "Просмотрите вопросы, представленные ниже и попробуйте пройти тест еще раз" 421 | 422 | #: templates/result.html:52 423 | msgid "" 424 | "The result of this exam will be stored in your progress section so you can " 425 | "review and monitor your progression" 426 | msgstr "" 427 | "Результаты данного экзамена будут сохранены. Вы сможете просматривать ваш " 428 | "прогресс" 429 | 430 | #: templates/result.html:66 431 | msgid "Your session score is" 432 | msgstr "Балл вашей сессии" 433 | 434 | #: templates/result.html:66 435 | msgid "out of a possible" 436 | msgstr "из возможных" 437 | 438 | #: templates/result.html:84 439 | msgid "Your answer" 440 | msgstr "Ваш ответ" 441 | 442 | #: templates/single_complete.html:13 443 | msgid "You have already sat this exam and only one sitting is permitted" 444 | msgstr "Вы уже прошли данный тест. Разрешена только одна попытка" 445 | 446 | #: templates/single_complete.html:15 447 | msgid "This exam is only accessible to signed in users" 448 | msgstr "Этот тест доступен только зарегистрированным пользователям" 449 | 450 | #: templates/view_quiz_category.html:3 451 | msgid "Quizzes related to" 452 | msgstr "Тесты относятся к" 453 | 454 | #: templates/view_quiz_category.html:6 455 | msgid "Quizzes in the" 456 | msgstr "Тесты в" 457 | 458 | #: templates/view_quiz_category.html:6 459 | msgid "category" 460 | msgstr "категория" 461 | 462 | #: templates/view_quiz_category.html:20 463 | msgid "There are no quizzes" 464 | msgstr "Тестов нет" 465 | -------------------------------------------------------------------------------- /quiz/locale/es_CO/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-quiz\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2016-01-01 11:37+0400\n" 11 | "PO-Revision-Date: 2016-03-24 16:26-0500\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 16 | "X-Generator: Poedit 1.8.4\n" 17 | "Language-Team: \n" 18 | "Last-Translator: \n" 19 | "Language: es_CO\n" 20 | 21 | #: admin.py:31 admin.py:33 models.py:576 22 | msgid "Questions" 23 | msgstr "Preguntas" 24 | 25 | #: models.py:30 models.py:37 models.py:53 models.py:83 models.py:545 26 | #: templates/progress.html:19 templates/quiz/quiz_detail.html:9 27 | #: templates/quiz/quiz_list.html:13 templates/quiz/sitting_detail.html:10 28 | msgid "Category" 29 | msgstr "Categoría" 30 | 31 | #: models.py:38 32 | msgid "Categories" 33 | msgstr "Categorías" 34 | 35 | #: models.py:48 models.py:58 models.py:550 36 | msgid "Sub-Category" 37 | msgstr "Sub-Categoría" 38 | 39 | #: models.py:59 40 | msgid "Sub-Categories" 41 | msgstr "Sub-Categorías" 42 | 43 | #: models.py:69 templates/quiz/quiz_list.html:12 44 | msgid "Title" 45 | msgstr "Título" 46 | 47 | #: models.py:73 48 | msgid "Description" 49 | msgstr "Descripción" 50 | 51 | #: models.py:74 52 | msgid "a description of the quiz" 53 | msgstr "una descripción del quiz" 54 | 55 | #: models.py:78 56 | msgid "a user friendly url" 57 | msgstr "una url amigable para el usuario" 58 | 59 | #: models.py:79 60 | msgid "user friendly url" 61 | msgstr "url amigable para el usuario" 62 | 63 | #: models.py:87 64 | msgid "Random Order" 65 | msgstr "Orden Aleatorio" 66 | 67 | #: models.py:88 68 | msgid "Display the questions in a random order or as they are set?" 69 | msgstr "¿Muestra las preguntas en un orden aleatorio o como se crearon?" 70 | 71 | #: models.py:93 72 | msgid "Max Questions" 73 | msgstr "Número Máximo de Preguntas" 74 | 75 | #: models.py:94 76 | msgid "Number of questions to be answered on each attempt." 77 | msgstr "Número de preguntas a ser resueltas por cada intento" 78 | 79 | #: models.py:98 80 | msgid "" 81 | "Correct answer is NOT shown after question. Answers displayed at the end." 82 | msgstr "" 83 | "Respuestas correctas NO serán mostradas después de la pregunta. Las " 84 | "respuestas se mostrarán al final" 85 | 86 | #: models.py:100 87 | msgid "Answers at end" 88 | msgstr "Respuestas al final" 89 | 90 | #: models.py:104 91 | msgid "" 92 | "If yes, the result of each attempt by a user will be stored. Necessary for " 93 | "marking." 94 | msgstr "" 95 | "Si escoge si, el resultado de cada intento por usuario será guardado. " 96 | "Necesario para marcar" 97 | 98 | #: models.py:107 99 | msgid "Exam Paper" 100 | msgstr "Papel del Exámen" 101 | 102 | #: models.py:111 103 | msgid "" 104 | "If yes, only one attempt by a user will be permitted. Non users cannot sit " 105 | "this exam." 106 | msgstr "" 107 | "Si escoge si, solo un intento será permitido por usuario. Los que no sean " 108 | "usuarios no tomarán este exámen" 109 | 110 | #: models.py:114 111 | msgid "Single Attempt" 112 | msgstr "Un Intento" 113 | 114 | #: apps/quiz/models.py:116 115 | msgid "Pass Mark" 116 | msgstr "Mínimo exigido para aprobar" 117 | 118 | #: models.py:118 119 | msgid "Percentage required to pass exam." 120 | msgstr "Porcentaje requerido para aprobar el examen" 121 | 122 | #: models.py:122 123 | msgid "Displayed if user passes." 124 | msgstr "Mostrar si el usuario pasa" 125 | 126 | #: models.py:123 127 | msgid "Success Text" 128 | msgstr "Texto de Éxito" 129 | 130 | #: models.py:126 131 | msgid "Fail Text" 132 | msgstr "Texto de Fracaso" 133 | 134 | #: models.py:127 135 | msgid "Displayed if user fails." 136 | msgstr "Mostrar si el usuario fracasa" 137 | 138 | #: models.py:131 139 | msgid "Draft" 140 | msgstr "Borrador" 141 | 142 | #: models.py:132 143 | msgid "" 144 | "If yes, the quiz is not displayed in the quiz list and can only be taken by " 145 | "users who can edit quizzes." 146 | msgstr "" 147 | "Si escoge si, este quiz no será mostrado en la lista de quizes y solo podrá " 148 | "ser tomado por usuarios que puedan editar quizes" 149 | 150 | #: models.py:152 models.py:372 models.py:541 151 | #: templates/quiz/sitting_list.html:14 152 | msgid "Quiz" 153 | msgstr "Quiz" 154 | 155 | #: models.py:153 156 | msgid "Quizzes" 157 | msgstr "Quizes" 158 | 159 | #: models.py:192 models.py:370 templates/quiz/sitting_detail.html:13 160 | #: templates/quiz/sitting_list.html:13 161 | msgid "User" 162 | msgstr "Usuario" 163 | 164 | #: models.py:195 templates/progress.html:60 165 | #: templates/quiz/sitting_detail.html:15 templates/quiz/sitting_list.html:16 166 | msgid "Score" 167 | msgstr "Puntaje" 168 | 169 | #: models.py:200 170 | msgid "User Progress" 171 | msgstr "Progreso de Usuario" 172 | 173 | #: models.py:201 174 | msgid "User progress records" 175 | msgstr "Registros de progreso del usuario" 176 | 177 | #: models.py:261 178 | msgid "error" 179 | msgstr "error" 180 | 181 | #: models.py:261 182 | msgid "category does not exist or invalid score" 183 | msgstr "categoría no existe o puntaje inválido" 184 | 185 | #: models.py:375 186 | msgid "Question Order" 187 | msgstr "Orden de las Preguntas" 188 | 189 | #: models.py:378 190 | msgid "Question List" 191 | msgstr "Lista de Preguntas" 192 | 193 | #: models.py:381 194 | msgid "Incorrect questions" 195 | msgstr "Preguntas Incorrectas" 196 | 197 | #: models.py:383 198 | msgid "Current Score" 199 | msgstr "Puntaje Actual" 200 | 201 | #: models.py:386 202 | msgid "Complete" 203 | msgstr "Completo" 204 | 205 | #: models.py:389 206 | msgid "User Answers" 207 | msgstr "Respuestas de los Usuarios" 208 | 209 | #: models.py:392 210 | msgid "Start" 211 | msgstr "Inicio" 212 | 213 | #: models.py:394 214 | msgid "End" 215 | msgstr "Fin" 216 | 217 | #: models.py:399 218 | msgid "Can see completed exams." 219 | msgstr "Puede ver exámenes completados" 220 | 221 | #: models.py:557 222 | msgid "Figure" 223 | msgstr "Figura" 224 | 225 | #: models.py:561 226 | msgid "Enter the question text that you want displayed" 227 | msgstr "Ingresa el texto de la pregunta que quiere que aparezca" 228 | 229 | #: models.py:563 models.py:575 templates/question.html:47 230 | #: templates/quiz/sitting_detail.html:21 231 | msgid "Question" 232 | msgstr "Pregunta" 233 | 234 | #: models.py:567 235 | msgid "Explanation to be shown after the question has been answered." 236 | msgstr "" 237 | "Explicación que debe ser mostrada después de que la pregunta fue respondida" 238 | 239 | #: models.py:570 templates/question.html:32 templates/result.html:21 240 | #: templates/result.html.py:87 241 | msgid "Explanation" 242 | msgstr "Explicación" 243 | 244 | #: templates/base.html:7 245 | msgid "Example Quiz Website" 246 | msgstr "Sitio Web Ejemplo Quiz" 247 | 248 | #: templates/correct_answer.html:6 249 | msgid "You answered the above question incorrectly" 250 | msgstr "Ha respondido a la pregunta anterior de forma incorrecta" 251 | 252 | #: templates/correct_answer.html:16 253 | msgid "This is the correct answer" 254 | msgstr "Esta es la respuesta correcta" 255 | 256 | #: templates/correct_answer.html:23 257 | msgid "This was your answer." 258 | msgstr "Esta fue tu respuesta" 259 | 260 | #: templates/progress.html:6 261 | msgid "Progress Page" 262 | msgstr "Página de Progreso" 263 | 264 | #: templates/progress.html:7 265 | msgid "User Progress Page" 266 | msgstr "Página Progreso de Usuario" 267 | 268 | #: templates/progress.html:13 269 | msgid "Question Category Scores" 270 | msgstr "Pregunta Puntajes de las Categorías" 271 | 272 | #: templates/progress.html:20 273 | msgid "Correctly answererd" 274 | msgstr "Correctamente contestada" 275 | 276 | #: templates/progress.html:21 277 | msgid "Incorrect" 278 | msgstr "Incorrecta" 279 | 280 | #: templates/progress.html:50 281 | msgid "Previous exam papers" 282 | msgstr "Exámenes anteriores" 283 | 284 | #: templates/progress.html:52 285 | msgid "Below are the results of exams that you have sat." 286 | msgstr "Abajo están los resultados de los exámenes que se ha realizado." 287 | 288 | #: templates/progress.html:59 289 | msgid "Quiz Title" 290 | msgstr "Título del Quiz" 291 | 292 | #: templates/progress.html:61 293 | msgid "Possible Score" 294 | msgstr "Puntos posibles" 295 | 296 | #: templates/question.html:13 templates/result.html:13 297 | msgid "The previous question" 298 | msgstr "La pregunta anterior" 299 | 300 | #: templates/question.html:22 301 | msgid "Your answer was" 302 | msgstr "Tu respuesta fue" 303 | 304 | #: templates/question.html:47 305 | msgid "of" 306 | msgstr "de" 307 | 308 | #: templates/question.html:52 309 | msgid "Question category" 310 | msgstr "Categoría de la pregunta" 311 | 312 | #: templates/question.html:74 313 | msgid "Check" 314 | msgstr "Verificar" 315 | 316 | #: templates/quiz/category_list.html:3 templates/quiz/quiz_list.html:3 317 | #: templates/quiz/sitting_list.html:3 318 | msgid "All Quizzes" 319 | msgstr "Todos los Quizes" 320 | 321 | #: templates/quiz/category_list.html:6 322 | msgid "Category list" 323 | msgstr "Lista de Categorías" 324 | 325 | #: templates/quiz/quiz_detail.html:11 326 | msgid "You will only get one attempt at this quiz" 327 | msgstr "Solo se tendrá un intento para este quiz" 328 | 329 | #: templates/quiz/quiz_detail.html:16 330 | msgid "Start quiz" 331 | msgstr "Empezar quiz" 332 | 333 | #: templates/quiz/quiz_list.html:6 334 | msgid "List of quizzes" 335 | msgstr "Lista de quizes" 336 | 337 | #: templates/quiz/quiz_list.html:14 338 | msgid "Exam" 339 | msgstr "Examen" 340 | 341 | #: templates/quiz/quiz_list.html:15 342 | msgid "Single attempt" 343 | msgstr "Un solo intento" 344 | 345 | #: templates/quiz/quiz_list.html:31 templates/quiz/sitting_list.html:42 346 | msgid "View details" 347 | msgstr "Ver detalles" 348 | 349 | #: templates/quiz/quiz_list.html:41 350 | msgid "There are no available quizzes" 351 | msgstr "No hay quizes disponibles" 352 | 353 | #: templates/quiz/sitting_detail.html:5 354 | msgid "Result of" 355 | msgstr "Resultado de" 356 | 357 | #: templates/quiz/sitting_detail.html:5 358 | msgid "for" 359 | msgstr "para" 360 | 361 | #: templates/quiz/sitting_detail.html:9 362 | msgid "Quiz title" 363 | msgstr "Título de Quiz" 364 | 365 | #: templates/quiz/sitting_detail.html:14 templates/quiz/sitting_list.html:15 366 | msgid "Completed" 367 | msgstr "Completado" 368 | 369 | #: templates/quiz/sitting_detail.html:22 370 | msgid "User answer" 371 | msgstr "Respuesta de usuario" 372 | 373 | #: templates/quiz/sitting_detail.html:41 374 | msgid "incorrect" 375 | msgstr "Incorrecta" 376 | 377 | #: templates/quiz/sitting_detail.html:43 378 | msgid "Correct" 379 | msgstr "Correcto" 380 | 381 | #: templates/quiz/sitting_detail.html:49 382 | msgid "Toggle whether correct" 383 | msgstr "Alternar si es correcta" 384 | 385 | #: templates/quiz/sitting_list.html:6 386 | msgid "List of complete exams" 387 | msgstr "Lista de examenes completos" 388 | 389 | #: templates/quiz/sitting_list.html:28 390 | msgid "Filter" 391 | msgstr "Filtro" 392 | 393 | #: templates/quiz/sitting_list.html:52 394 | msgid "There are no matching quizzes" 395 | msgstr "No hay quizes que coincidan" 396 | 397 | #: templates/result.html:7 398 | msgid "Exam Results for" 399 | msgstr "Resultados del exámen de" 400 | 401 | #: templates/result.html:32 402 | msgid "Exam results" 403 | msgstr "Resultados del exámen" 404 | 405 | #: templates/result.html:34 406 | msgid "Exam title" 407 | msgstr "Título del exámen" 408 | 409 | #: templates/result.html:38 410 | msgid "You answered" 411 | msgstr "Tu respondiste" 412 | 413 | #: templates/result.html:38 414 | msgid "questions correctly out of" 415 | msgstr "preguntas correctas de cada" 416 | 417 | #: templates/result.html:38 418 | msgid "giving you" 419 | msgstr "darle" 420 | 421 | #: templates/result.html:38 422 | msgid "percent correct" 423 | msgstr "porcentaje de respuestas correctas" 424 | 425 | #: templates/result.html:48 426 | msgid "Review the questions below and try the exam again in the future" 427 | msgstr "" 428 | "Revisa las preguntas a continuación e intente el examen de nuevo en el " 429 | "futuro" 430 | 431 | #: templates/result.html:52 432 | msgid "" 433 | "The result of this exam will be stored in your progress section so you can " 434 | "review and monitor your progression" 435 | msgstr "" 436 | "El resultado de este examen se almacenará en su sección de progreso para " 437 | "que pueda revisar y supervisar su progreso" 438 | 439 | #: templates/result.html:66 440 | msgid "Your session score is" 441 | msgstr "Su puntuación de sesión es" 442 | 443 | #: templates/result.html:66 444 | msgid "out of a possible" 445 | msgstr "de un total posible" 446 | 447 | #: templates/result.html:84 448 | msgid "Your answer" 449 | msgstr "Tu respuesta" 450 | 451 | #: templates/single_complete.html:13 452 | msgid "You have already sat this exam and only one sitting is permitted" 453 | msgstr "Usted ya ha enviado este examen, y sólo se permite una sola sesión" 454 | 455 | #: templates/single_complete.html:15 456 | msgid "This exam is only accessible to signed in users" 457 | msgstr "Este examen es sólo accesible para los usuarios inscritos" 458 | 459 | #: templates/view_quiz_category.html:3 460 | msgid "Quizzes related to" 461 | msgstr "Quizes relacionados con" 462 | 463 | #: templates/view_quiz_category.html:6 464 | msgid "Quizzes in the" 465 | msgstr "Quizes en el" 466 | 467 | #: templates/view_quiz_category.html:6 468 | msgid "category" 469 | msgstr "categoría" 470 | 471 | #: templates/view_quiz_category.html:20 472 | msgid "There are no quizzes" 473 | msgstr "No hay quizes" 474 | -------------------------------------------------------------------------------- /quiz/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 11 | "PO-Revision-Date: 2019-03-07 21:14+0100\n" 12 | "Last-Translator: Reiner Mayers\n" 13 | "Language: de\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | "X-Translated-Using: django-rosetta 0.7.6\n" 19 | "Language-Team: \n" 20 | "X-Generator: Poedit 2.0.6\n" 21 | 22 | #: apps/quiz/models.py:30 apps/quiz/models.py:37 apps/quiz/models.py:53 23 | #: apps/quiz/models.py:83 apps/quiz/models.py:545 24 | #: apps/quiz/templates/progress.html:19 25 | #: apps/quiz/templates/quiz/quiz_detail.html:9 26 | #: apps/quiz/templates/quiz/quiz_list.html:13 27 | #: apps/quiz/templates/quiz/sitting_detail.html:10 28 | msgid "Category" 29 | msgstr "Kategorie" 30 | 31 | #: apps/quiz/models.py:38 32 | msgid "Categories" 33 | msgstr "Kategorien" 34 | 35 | #: apps/quiz/models.py:48 apps/quiz/models.py:58 apps/quiz/models.py:550 36 | msgid "Sub-Category" 37 | msgstr "Unterkategorie" 38 | 39 | #: apps/quiz/models.py:59 40 | msgid "Sub-Categories" 41 | msgstr "Unterkategorien" 42 | 43 | #: apps/quiz/models.py:69 apps/quiz/templates/quiz/quiz_list.html:12 44 | msgid "Title" 45 | msgstr "Titel" 46 | 47 | #: apps/quiz/models.py:73 48 | msgid "Description" 49 | msgstr "Beschreibung" 50 | 51 | #: apps/quiz/models.py:74 52 | msgid "a description of the quiz" 53 | msgstr "Eine Beschreibung des Quiz" 54 | 55 | #: apps/quiz/models.py:78 56 | msgid "a user friendly url" 57 | msgstr "Eine benutzerfreundliche url" 58 | 59 | #: apps/quiz/models.py:79 60 | msgid "user friendly url" 61 | msgstr "benutzerfreundliche url" 62 | 63 | #: apps/quiz/models.py:87 64 | msgid "Random Order" 65 | msgstr "Zufällige Reihenfolge" 66 | 67 | #: apps/quiz/models.py:88 68 | msgid "Display the questions in a random order or as they are set?" 69 | msgstr "Fragen zufällig anzeigen oder in der Eingabereihenfolge?" 70 | 71 | #: apps/quiz/models.py:93 72 | msgid "Max Questions" 73 | msgstr "Maximale Anzahl der Fragen" 74 | 75 | #: apps/quiz/models.py:94 76 | msgid "Number of questions to be answered on each attempt." 77 | msgstr "Anzahl der Fragen die bei jedem Versuch gestellt werden." 78 | 79 | #: apps/quiz/models.py:98 80 | msgid "" 81 | "Correct answer is NOT shown after question. Answers displayed at the end." 82 | msgstr "" 83 | "Die Richtige Antwort wird nicht nach der Frage angezeigt sondern am Ende." 84 | 85 | #: apps/quiz/models.py:100 86 | msgid "Answers at end" 87 | msgstr "Antworten am Ende" 88 | 89 | #: apps/quiz/models.py:104 90 | msgid "" 91 | "If yes, the result of each attempt by a user will be stored. Necessary for " 92 | "marking." 93 | msgstr "" 94 | "Wenn ja, wird das Ergebnis jeden Versuchs gespeichert. Zum markieren ist die " 95 | "notwendig." 96 | 97 | #: apps/quiz/models.py:107 98 | msgid "Exam Paper" 99 | msgstr "Prüfungsarbeit" 100 | 101 | #: apps/quiz/models.py:111 102 | msgid "" 103 | "If yes, only one attempt by a user will be permitted. Non users cannot sit " 104 | "this exam." 105 | msgstr "" 106 | "Wenn ja wird nur ein Versuch pro Anwender zugelassen. Nur angemeldete " 107 | "Anwender können diese Prüfung machen." 108 | 109 | #: apps/quiz/models.py:114 110 | msgid "Single Attempt" 111 | msgstr "Nur ein Versuch" 112 | 113 | #: apps/quiz/models.py:118 114 | msgid "Percentage required to pass exam." 115 | msgstr "Prozent zum bestehen der Prüfung." 116 | 117 | #: apps/quiz/models.py:122 118 | msgid "Displayed if user passes." 119 | msgstr "Angezeigt wenn der Anwender besteht." 120 | 121 | #: apps/quiz/models.py:123 122 | msgid "Success Text" 123 | msgstr "Text für den Erfolg" 124 | 125 | #: apps/quiz/models.py:126 126 | msgid "Fail Text" 127 | msgstr "Text für den Misserfolg" 128 | 129 | #: apps/quiz/models.py:127 130 | msgid "Displayed if user fails." 131 | msgstr "Wird angezeigt wenn der Anwender durchfällt." 132 | 133 | #: apps/quiz/models.py:131 134 | msgid "Draft" 135 | msgstr "Entwurf" 136 | 137 | #: apps/quiz/models.py:132 138 | msgid "" 139 | "If yes, the quiz is not displayed in the quiz list and can only be taken by " 140 | "users who can edit quizzes." 141 | msgstr "" 142 | "Wenn ja wird die Prüfung nicht in der Prüfungsliste angezeigt und kann nur " 143 | "von Anwendern ausgeführt werden welche Prüfungen bearbeiten können." 144 | 145 | #: apps/quiz/models.py:152 apps/quiz/models.py:372 apps/quiz/models.py:541 146 | #: apps/quiz/templates/quiz/sitting_list.html:14 147 | msgid "Quiz" 148 | msgstr "Quiz" 149 | 150 | #: apps/quiz/models.py:153 151 | msgid "Quizzes" 152 | msgstr "Quizze" 153 | 154 | #: apps/quiz/models.py:192 apps/quiz/models.py:370 155 | #: apps/quiz/templates/quiz/sitting_detail.html:13 156 | #: apps/quiz/templates/quiz/sitting_list.html:13 157 | msgid "User" 158 | msgstr "Anwender" 159 | 160 | #: apps/quiz/models.py:195 apps/quiz/templates/progress.html:60 161 | #: apps/quiz/templates/quiz/sitting_detail.html:15 162 | #: apps/quiz/templates/quiz/sitting_list.html:16 163 | msgid "Score" 164 | msgstr "Punkte" 165 | 166 | #: apps/quiz/models.py:200 167 | msgid "User Progress" 168 | msgstr "Anwender Fortschritt" 169 | 170 | #: apps/quiz/models.py:201 171 | msgid "User progress records" 172 | msgstr "Fortschrittsaufzeichnung des Anwenders" 173 | 174 | #: apps/quiz/models.py:261 175 | msgid "error" 176 | msgstr "Fehler" 177 | 178 | #: apps/quiz/models.py:261 179 | msgid "category does not exist or invalid score" 180 | msgstr "Diese Kategorie existiert nicht oder das Ergebnis ist ungültig" 181 | 182 | #: apps/quiz/models.py:375 183 | msgid "Question Order" 184 | msgstr "Reihenfolge der Fragen" 185 | 186 | #: apps/quiz/models.py:378 187 | msgid "Question List" 188 | msgstr "Liste der Fragen" 189 | 190 | #: apps/quiz/models.py:381 191 | msgid "Incorrect questions" 192 | msgstr "Falsche Antworten" 193 | 194 | #: apps/quiz/models.py:383 195 | msgid "Current Score" 196 | msgstr "Aktueller Punktestand" 197 | 198 | #: apps/quiz/models.py:386 199 | msgid "Complete" 200 | msgstr "Vollständig" 201 | 202 | #: apps/quiz/models.py:389 203 | msgid "User Answers" 204 | msgstr "Antworten des Anwenders" 205 | 206 | #: apps/quiz/models.py:392 207 | msgid "Start" 208 | msgstr "Anfang" 209 | 210 | #: apps/quiz/models.py:394 211 | msgid "End" 212 | msgstr "Ende" 213 | 214 | #: apps/quiz/models.py:399 215 | msgid "Can see completed exams." 216 | msgstr "Kann abgelegte Prüfungen sehen." 217 | 218 | #: apps/quiz/models.py:557 219 | msgid "Figure" 220 | msgstr "Abbildung" 221 | 222 | #: apps/quiz/models.py:561 223 | msgid "Enter the question text that you want displayed" 224 | msgstr "Geben Sie die Frage ein welche angezeigt werden soll" 225 | 226 | #: apps/quiz/models.py:563 apps/quiz/models.py:575 227 | #: apps/quiz/templates/question.html:47 228 | #: apps/quiz/templates/quiz/sitting_detail.html:21 229 | msgid "Question" 230 | msgstr "Frage" 231 | 232 | #: apps/quiz/models.py:567 233 | msgid "Explanation to be shown after the question has been answered." 234 | msgstr "Erklärung welche nach der Antwort angezeigt wird." 235 | 236 | #: apps/quiz/models.py:570 apps/quiz/templates/question.html:32 237 | #: apps/quiz/templates/result.html:21 apps/quiz/templates/result.html.py:87 238 | msgid "Explanation" 239 | msgstr "Erklärung" 240 | 241 | #: apps/quiz/models.py:576 242 | msgid "Questions" 243 | msgstr "Fragen" 244 | 245 | #: apps/quiz/templates/base.html:7 246 | msgid "Example Quiz Website" 247 | msgstr "Beispiel Quiz Webseite" 248 | 249 | #: apps/quiz/templates/correct_answer.html:6 250 | msgid "You answered the above question incorrectly" 251 | msgstr "Sie haben die obenstehende Frage falsch beantwortet" 252 | 253 | #: apps/quiz/templates/correct_answer.html:16 254 | msgid "This is the correct answer" 255 | msgstr "Dies ist die richtige Antwort" 256 | 257 | #: apps/quiz/templates/correct_answer.html:23 258 | msgid "This was your answer." 259 | msgstr "Dies war Ihre Antwort." 260 | 261 | #: apps/quiz/templates/progress.html:6 262 | msgid "Progress Page" 263 | msgstr "Fortschrittsseite" 264 | 265 | #: apps/quiz/templates/progress.html:7 266 | msgid "User Progress Page" 267 | msgstr "Fortschrittsseite des Anwenders" 268 | 269 | #: apps/quiz/templates/progress.html:13 270 | msgid "Question Category Scores" 271 | msgstr "Punkte in dieser Fragenkategorie" 272 | 273 | #: apps/quiz/templates/progress.html:20 274 | msgid "Correctly answererd" 275 | msgstr "Die Antwort ist richtig" 276 | 277 | #: apps/quiz/templates/progress.html:21 278 | msgid "Incorrect" 279 | msgstr "Falsch" 280 | 281 | #: apps/quiz/templates/progress.html:50 282 | msgid "Previous exam papers" 283 | msgstr "Vorhergehende Prüfung" 284 | 285 | #: apps/quiz/templates/progress.html:52 286 | msgid "Below are the results of exams that you have sat." 287 | msgstr "Im Folgenden sehen Sie die Ergebnisse Ihrer Prüfungen." 288 | 289 | #: apps/quiz/templates/progress.html:59 290 | msgid "Quiz Title" 291 | msgstr "Titel des Quiz" 292 | 293 | #: apps/quiz/templates/progress.html:61 294 | msgid "Possible Score" 295 | msgstr "Mögliche Punkte" 296 | 297 | #: apps/quiz/templates/question.html:13 apps/quiz/templates/result.html:13 298 | msgid "The previous question" 299 | msgstr "Die vorherige Frage" 300 | 301 | #: apps/quiz/templates/question.html:22 302 | msgid "Your answer was" 303 | msgstr "Ihre Antwort war" 304 | 305 | #: apps/quiz/templates/question.html:47 306 | msgid "of" 307 | msgstr "von" 308 | 309 | #: apps/quiz/templates/question.html:52 310 | msgid "Question category" 311 | msgstr "Fragenkategorie" 312 | 313 | #: apps/quiz/templates/question.html:74 314 | msgid "Check" 315 | msgstr "Überprüfung" 316 | 317 | #: apps/quiz/templates/quiz/category_list.html:3 318 | #: apps/quiz/templates/quiz/quiz_list.html:3 319 | #: apps/quiz/templates/quiz/sitting_list.html:3 320 | msgid "All Quizzes" 321 | msgstr "Alle Quizze" 322 | 323 | #: apps/quiz/templates/quiz/category_list.html:6 324 | msgid "Category list" 325 | msgstr "Liste der Kategorien" 326 | 327 | #: apps/quiz/templates/quiz/quiz_detail.html:11 328 | msgid "You will only get one attempt at this quiz" 329 | msgstr "Sie haben nur einen Versuch bei dieser Prüfung" 330 | 331 | #: apps/quiz/templates/quiz/quiz_detail.html:16 332 | msgid "Start quiz" 333 | msgstr "Quiz beginnen" 334 | 335 | #: apps/quiz/templates/quiz/quiz_list.html:6 336 | msgid "List of quizzes" 337 | msgstr "Liste der Quizze" 338 | 339 | #: apps/quiz/templates/quiz/quiz_list.html:14 340 | msgid "Exam" 341 | msgstr "Prüfung" 342 | 343 | #: apps/quiz/templates/quiz/quiz_list.html:15 344 | msgid "Single attempt" 345 | msgstr "Einzelversuch" 346 | 347 | #: apps/quiz/templates/quiz/quiz_list.html:31 348 | #: apps/quiz/templates/quiz/sitting_list.html:42 349 | msgid "View details" 350 | msgstr "Details ansehen" 351 | 352 | #: apps/quiz/templates/quiz/quiz_list.html:41 353 | msgid "There are no available quizzes" 354 | msgstr "Es sind keine Prüfungen verfügbar" 355 | 356 | #: apps/quiz/templates/quiz/sitting_detail.html:5 357 | msgid "Result of" 358 | msgstr "Resultat von" 359 | 360 | #: apps/quiz/templates/quiz/sitting_detail.html:5 361 | msgid "for" 362 | msgstr "für" 363 | 364 | #: apps/quiz/templates/quiz/sitting_detail.html:9 365 | msgid "Quiz title" 366 | msgstr "Quiztitel" 367 | 368 | #: apps/quiz/templates/quiz/sitting_detail.html:14 369 | #: apps/quiz/templates/quiz/sitting_list.html:15 370 | msgid "Completed" 371 | msgstr "Vollständig" 372 | 373 | #: apps/quiz/templates/quiz/sitting_detail.html:22 374 | msgid "User answer" 375 | msgstr "Anwender Antwort" 376 | 377 | #: apps/quiz/templates/quiz/sitting_detail.html:41 378 | msgid "incorrect" 379 | msgstr "falsch" 380 | 381 | #: apps/quiz/templates/quiz/sitting_detail.html:43 382 | msgid "Correct" 383 | msgstr "Richtig" 384 | 385 | #: apps/quiz/templates/quiz/sitting_detail.html:49 386 | msgid "Toggle whether correct" 387 | msgstr "Markieren wenn richtig" 388 | 389 | #: apps/quiz/templates/quiz/sitting_list.html:6 390 | msgid "List of complete exams" 391 | msgstr "Liste der abgelegten Prüfungen" 392 | 393 | #: apps/quiz/templates/quiz/sitting_list.html:28 394 | msgid "Filter" 395 | msgstr "Filter" 396 | 397 | #: apps/quiz/templates/quiz/sitting_list.html:52 398 | msgid "There are no matching quizzes" 399 | msgstr "Es gibt keine übereinstimmenden Prüfungen" 400 | 401 | #: apps/quiz/templates/result.html:7 402 | msgid "Exam Results for" 403 | msgstr "Prüfungsergebnisse für" 404 | 405 | #: apps/quiz/templates/result.html:32 406 | msgid "Exam results" 407 | msgstr "Prüfungsergebnisse" 408 | 409 | #: apps/quiz/templates/result.html:34 410 | msgid "Exam title" 411 | msgstr "Prüfungstitel" 412 | 413 | #: apps/quiz/templates/result.html:38 414 | msgid "You answered" 415 | msgstr "Ihre Antwort" 416 | 417 | #: apps/quiz/templates/result.html:38 418 | msgid "questions correctly out of" 419 | msgstr "Fragen richtig von " 420 | 421 | #: apps/quiz/templates/result.html:38 422 | msgid "giving you" 423 | msgstr "Sie erhalten" 424 | 425 | #: apps/quiz/templates/result.html:38 426 | msgid "percent correct" 427 | msgstr "Prozent korrekt" 428 | 429 | #: apps/quiz/templates/result.html:48 430 | msgid "Review the questions below and try the exam again in the future" 431 | msgstr "Sehen Sie sich die folgenden Fragen an und wiederholen Sie die Prüfung" 432 | 433 | #: apps/quiz/templates/result.html:52 434 | msgid "" 435 | "The result of this exam will be stored in your progress section so you can " 436 | "review and monitor your progression" 437 | msgstr "" 438 | "Das Resultat dieser Prüfung wird gespeichert und Sie können sich Ihren " 439 | "Fortschritt ansehen" 440 | 441 | #: apps/quiz/templates/result.html:66 442 | msgid "Your session score is" 443 | msgstr "Der Punktestand dieser Sitzung ist" 444 | 445 | #: apps/quiz/templates/result.html:66 446 | msgid "out of a possible" 447 | msgstr "von möglichen" 448 | 449 | #: apps/quiz/templates/result.html:84 450 | msgid "Your answer" 451 | msgstr "Ihre Antwort" 452 | 453 | #: apps/quiz/templates/single_complete.html:13 454 | msgid "You have already sat this exam and only one sitting is permitted" 455 | msgstr "" 456 | "Sie haben diese Prüfung schon abgelegt und dies kann nur einmal erfolgen" 457 | 458 | #: apps/quiz/templates/single_complete.html:15 459 | msgid "This exam is only accessible to signed in users" 460 | msgstr "Diese Prüfung ist angemeldeten Anwendern vorbehalten" 461 | 462 | #: apps/quiz/templates/view_quiz_category.html:3 463 | msgid "Quizzes related to" 464 | msgstr "Quizze bezogen auf" 465 | 466 | #: apps/quiz/templates/view_quiz_category.html:6 467 | msgid "Quizzes in the" 468 | msgstr "Quizze in der" 469 | 470 | #: apps/quiz/templates/view_quiz_category.html:6 471 | msgid "category" 472 | msgstr "Kategorie" 473 | 474 | #: apps/quiz/templates/view_quiz_category.html:20 475 | msgid "There are no quizzes" 476 | msgstr "Es gibt keine Quizze" 477 | -------------------------------------------------------------------------------- /quiz/locale/it/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-31 22:44+0000\n" 12 | "PO-Revision-Date: 2015-10-31 22:43+0000\n" 13 | "Last-Translator: b' <>'\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | "X-Translated-Using: django-rosetta 0.7.6\n" 21 | 22 | #: apps/quiz/models.py:30 apps/quiz/models.py:37 apps/quiz/models.py:53 23 | #: apps/quiz/models.py:83 apps/quiz/models.py:545 24 | #: apps/quiz/templates/progress.html:19 25 | #: apps/quiz/templates/quiz/quiz_detail.html:9 26 | #: apps/quiz/templates/quiz/quiz_list.html:13 27 | #: apps/quiz/templates/quiz/sitting_detail.html:10 28 | msgid "Category" 29 | msgstr "Categoria" 30 | 31 | #: apps/quiz/models.py:38 32 | msgid "Categories" 33 | msgstr "Categorie" 34 | 35 | #: apps/quiz/models.py:48 apps/quiz/models.py:58 apps/quiz/models.py:550 36 | msgid "Sub-Category" 37 | msgstr "Sotto-Categoria" 38 | 39 | #: apps/quiz/models.py:59 40 | msgid "Sub-Categories" 41 | msgstr "Sotto-categorie" 42 | 43 | #: apps/quiz/models.py:69 apps/quiz/templates/quiz/quiz_list.html:12 44 | msgid "Title" 45 | msgstr "Titolo" 46 | 47 | #: apps/quiz/models.py:73 48 | msgid "Description" 49 | msgstr "Descrizione" 50 | 51 | #: apps/quiz/models.py:74 52 | msgid "a description of the quiz" 53 | msgstr "una descrizione del quiz" 54 | 55 | #: apps/quiz/models.py:78 56 | msgid "a user friendly url" 57 | msgstr "un url amichevole" 58 | 59 | #: apps/quiz/models.py:79 60 | msgid "user friendly url" 61 | msgstr "url amichevole" 62 | 63 | #: apps/quiz/models.py:87 64 | msgid "Random Order" 65 | msgstr "Ordine Casuale" 66 | 67 | #: apps/quiz/models.py:88 68 | msgid "Display the questions in a random order or as they are set?" 69 | msgstr "" 70 | "Visualizzare le domande in un ordine casuale o nell'ordine in cui sono state " 71 | "inserite?" 72 | 73 | #: apps/quiz/models.py:93 74 | msgid "Max Questions" 75 | msgstr "Numero massimo di domande" 76 | 77 | #: apps/quiz/models.py:94 78 | msgid "Number of questions to be answered on each attempt." 79 | msgstr "Numero di domande cui rispondere in ogni tentativo." 80 | 81 | #: apps/quiz/models.py:98 82 | msgid "" 83 | "Correct answer is NOT shown after question. Answers displayed at the end." 84 | msgstr "" 85 | "La risposta corretta NON è visualizzata dopo la domanda. Le risposte vengono " 86 | "visualizzate alla fine." 87 | 88 | #: apps/quiz/models.py:100 89 | msgid "Answers at end" 90 | msgstr "Risposte alla fine" 91 | 92 | #: apps/quiz/models.py:104 93 | msgid "" 94 | "If yes, the result of each attempt by a user will be stored. Necessary for " 95 | "marking." 96 | msgstr "" 97 | "Se si, il risultato di ogni tentativo da parte di un utente verrà salvato. " 98 | "Necessario per segnare." 99 | 100 | #: apps/quiz/models.py:107 101 | msgid "Exam Paper" 102 | msgstr "Foglio dell'esame" 103 | 104 | #: apps/quiz/models.py:111 105 | msgid "" 106 | "If yes, only one attempt by a user will be permitted. Non users cannot sit " 107 | "this exam." 108 | msgstr "" 109 | "Se si, per ogni utente sarà permesso un solo tentativo. I non tutenti non " 110 | "potranno sostenere l'esame." 111 | 112 | #: apps/quiz/models.py:114 113 | msgid "Single Attempt" 114 | msgstr "Songolo tentativo" 115 | 116 | #: apps/quiz/models.py:118 117 | msgid "Percentage required to pass exam." 118 | msgstr "Percentuale richiesta per superare l'esame." 119 | 120 | #: apps/quiz/models.py:122 121 | msgid "Displayed if user passes." 122 | msgstr "Visualizzato se l'utente passa." 123 | 124 | #: apps/quiz/models.py:123 125 | msgid "Success Text" 126 | msgstr "Testo Risposta Corretta" 127 | 128 | #: apps/quiz/models.py:126 129 | msgid "Fail Text" 130 | msgstr "Test risposta Sbagliata" 131 | 132 | #: apps/quiz/models.py:127 133 | msgid "Displayed if user fails." 134 | msgstr "Visualizzato se l'utente fallisce." 135 | 136 | #: apps/quiz/models.py:131 137 | msgid "Draft" 138 | msgstr "Bozza" 139 | 140 | #: apps/quiz/models.py:132 141 | msgid "" 142 | "If yes, the quiz is not displayed in the quiz list and can only be taken by " 143 | "users who can edit quizzes." 144 | msgstr "" 145 | "Se sì, il quiz non viene visualizzato nell'elenco quiz e può essere " 146 | "selezionato solo dagli utenti che possono modificare i quiz." 147 | 148 | #: apps/quiz/models.py:152 apps/quiz/models.py:372 apps/quiz/models.py:541 149 | #: apps/quiz/templates/quiz/sitting_list.html:14 150 | msgid "Quiz" 151 | msgstr "Quiz" 152 | 153 | #: apps/quiz/models.py:153 154 | msgid "Quizzes" 155 | msgstr "Quiz" 156 | 157 | #: apps/quiz/models.py:192 apps/quiz/models.py:370 158 | #: apps/quiz/templates/quiz/sitting_detail.html:13 159 | #: apps/quiz/templates/quiz/sitting_list.html:13 160 | msgid "User" 161 | msgstr "Utente" 162 | 163 | #: apps/quiz/models.py:195 apps/quiz/templates/progress.html:60 164 | #: apps/quiz/templates/quiz/sitting_detail.html:15 165 | #: apps/quiz/templates/quiz/sitting_list.html:16 166 | msgid "Score" 167 | msgstr "Punteggio" 168 | 169 | #: apps/quiz/models.py:200 170 | msgid "User Progress" 171 | msgstr "Progressi dell'utente" 172 | 173 | #: apps/quiz/models.py:201 174 | msgid "User progress records" 175 | msgstr "registarzione progressi utente" 176 | 177 | #: apps/quiz/models.py:261 178 | msgid "error" 179 | msgstr "errore" 180 | 181 | #: apps/quiz/models.py:261 182 | msgid "category does not exist or invalid score" 183 | msgstr "categoria inesistente o punteggio non valido" 184 | 185 | #: apps/quiz/models.py:375 186 | msgid "Question Order" 187 | msgstr "ordine Domande" 188 | 189 | #: apps/quiz/models.py:378 190 | msgid "Question List" 191 | msgstr "Elenca domande" 192 | 193 | #: apps/quiz/models.py:381 194 | msgid "Incorrect questions" 195 | msgstr "Domande sbagliate" 196 | 197 | #: apps/quiz/models.py:383 198 | msgid "Current Score" 199 | msgstr "Punteggio Attuale" 200 | 201 | #: apps/quiz/models.py:386 202 | msgid "Complete" 203 | msgstr "Completo" 204 | 205 | #: apps/quiz/models.py:389 206 | msgid "User Answers" 207 | msgstr "Risposte utente" 208 | 209 | #: apps/quiz/models.py:392 210 | msgid "Start" 211 | msgstr "Inizio" 212 | 213 | #: apps/quiz/models.py:394 214 | msgid "End" 215 | msgstr "Fine" 216 | 217 | #: apps/quiz/models.py:399 218 | msgid "Can see completed exams." 219 | msgstr "Puoi vedere gli esami completati." 220 | 221 | #: apps/quiz/models.py:557 222 | msgid "Figure" 223 | msgstr "Immagine" 224 | 225 | #: apps/quiz/models.py:561 226 | msgid "Enter the question text that you want displayed" 227 | msgstr "Inserisci il testo della domanda che desideri visualizzare" 228 | 229 | #: apps/quiz/models.py:563 apps/quiz/models.py:575 230 | #: apps/quiz/templates/question.html:47 231 | #: apps/quiz/templates/quiz/sitting_detail.html:21 232 | msgid "Question" 233 | msgstr "Domanda" 234 | 235 | #: apps/quiz/models.py:567 236 | msgid "Explanation to be shown after the question has been answered." 237 | msgstr "" 238 | "Spiegazione da visulizzare dopo che è stata data la risposta alla domanda." 239 | 240 | #: apps/quiz/models.py:570 apps/quiz/templates/question.html:32 241 | #: apps/quiz/templates/result.html:21 apps/quiz/templates/result.html.py:87 242 | msgid "Explanation" 243 | msgstr "Spiegazione" 244 | 245 | #: apps/quiz/models.py:576 246 | msgid "Questions" 247 | msgstr "Domande" 248 | 249 | #: apps/quiz/templates/base.html:7 250 | msgid "Example Quiz Website" 251 | msgstr "Sito Quiz di esempio" 252 | 253 | #: apps/quiz/templates/correct_answer.html:6 254 | msgid "You answered the above question incorrectly" 255 | msgstr "Hai risposto alla domanda di cui sopra in modo non corretto" 256 | 257 | #: apps/quiz/templates/correct_answer.html:16 258 | msgid "This is the correct answer" 259 | msgstr "Questa è la risposta corretta" 260 | 261 | #: apps/quiz/templates/correct_answer.html:23 262 | msgid "This was your answer." 263 | msgstr "Questa è stata la tua risposta " 264 | 265 | #: apps/quiz/templates/progress.html:6 266 | msgid "Progress Page" 267 | msgstr "Pagina dei progressi" 268 | 269 | #: apps/quiz/templates/progress.html:7 270 | msgid "User Progress Page" 271 | msgstr "Pagina dei progressi dell'utente" 272 | 273 | #: apps/quiz/templates/progress.html:13 274 | msgid "Question Category Scores" 275 | msgstr "Punteggi Categoria Domanda " 276 | 277 | #: apps/quiz/templates/progress.html:20 278 | msgid "Correctly answererd" 279 | msgstr "Risposto correttamente" 280 | 281 | #: apps/quiz/templates/progress.html:21 282 | msgid "Incorrect" 283 | msgstr "Sbagliato" 284 | 285 | #: apps/quiz/templates/progress.html:50 286 | msgid "Previous exam papers" 287 | msgstr "Fogli esami precedenti" 288 | 289 | #: apps/quiz/templates/progress.html:52 290 | msgid "Below are the results of exams that you have sat." 291 | msgstr "Di seguito sono riportati i risultati degli esami sostenuti." 292 | 293 | #: apps/quiz/templates/progress.html:59 294 | msgid "Quiz Title" 295 | msgstr "Titolo del Quiz" 296 | 297 | #: apps/quiz/templates/progress.html:61 298 | msgid "Possible Score" 299 | msgstr "Possibile Punteggio" 300 | 301 | #: apps/quiz/templates/question.html:13 apps/quiz/templates/result.html:13 302 | msgid "The previous question" 303 | msgstr "La domanda precedente" 304 | 305 | #: apps/quiz/templates/question.html:22 306 | msgid "Your answer was" 307 | msgstr "La tua risposta è stata" 308 | 309 | #: apps/quiz/templates/question.html:47 310 | msgid "of" 311 | msgstr "di" 312 | 313 | #: apps/quiz/templates/question.html:52 314 | msgid "Question category" 315 | msgstr "Categoria domanda" 316 | 317 | #: apps/quiz/templates/question.html:74 318 | msgid "Check" 319 | msgstr "Verifica" 320 | 321 | #: apps/quiz/templates/quiz/category_list.html:3 322 | #: apps/quiz/templates/quiz/quiz_list.html:3 323 | #: apps/quiz/templates/quiz/sitting_list.html:3 324 | msgid "All Quizzes" 325 | msgstr "Tutti i Quiz" 326 | 327 | #: apps/quiz/templates/quiz/category_list.html:6 328 | msgid "Category list" 329 | msgstr "Lista Categorie" 330 | 331 | #: apps/quiz/templates/quiz/quiz_detail.html:11 332 | msgid "You will only get one attempt at this quiz" 333 | msgstr "Avrai un solo tentativo a disposizione per questo quiz" 334 | 335 | #: apps/quiz/templates/quiz/quiz_detail.html:16 336 | msgid "Start quiz" 337 | msgstr "Comincia il Quiz" 338 | 339 | #: apps/quiz/templates/quiz/quiz_list.html:6 340 | msgid "List of quizzes" 341 | msgstr "Lista dei quiz" 342 | 343 | #: apps/quiz/templates/quiz/quiz_list.html:14 344 | msgid "Exam" 345 | msgstr "Esame" 346 | 347 | #: apps/quiz/templates/quiz/quiz_list.html:15 348 | msgid "Single attempt" 349 | msgstr "Singolo tentivo" 350 | 351 | #: apps/quiz/templates/quiz/quiz_list.html:31 352 | #: apps/quiz/templates/quiz/sitting_list.html:42 353 | msgid "View details" 354 | msgstr "Vedi i dettagli" 355 | 356 | #: apps/quiz/templates/quiz/quiz_list.html:41 357 | msgid "There are no available quizzes" 358 | msgstr "Non ci sono quiz disponibili" 359 | 360 | #: apps/quiz/templates/quiz/sitting_detail.html:5 361 | msgid "Result of" 362 | msgstr "Risultato di" 363 | 364 | #: apps/quiz/templates/quiz/sitting_detail.html:5 365 | msgid "for" 366 | msgstr "per" 367 | 368 | #: apps/quiz/templates/quiz/sitting_detail.html:9 369 | msgid "Quiz title" 370 | msgstr "Titolo quiz" 371 | 372 | #: apps/quiz/templates/quiz/sitting_detail.html:14 373 | #: apps/quiz/templates/quiz/sitting_list.html:15 374 | msgid "Completed" 375 | msgstr "Completato" 376 | 377 | #: apps/quiz/templates/quiz/sitting_detail.html:22 378 | msgid "User answer" 379 | msgstr "Risposta utente" 380 | 381 | #: apps/quiz/templates/quiz/sitting_detail.html:41 382 | msgid "incorrect" 383 | msgstr "errato" 384 | 385 | #: apps/quiz/templates/quiz/sitting_detail.html:43 386 | msgid "Correct" 387 | msgstr "Corretto" 388 | 389 | #: apps/quiz/templates/quiz/sitting_detail.html:49 390 | msgid "Toggle whether correct" 391 | msgstr "Seleziona se corretto" 392 | 393 | #: apps/quiz/templates/quiz/sitting_list.html:6 394 | msgid "List of complete exams" 395 | msgstr "Lista edgli esami completati" 396 | 397 | #: apps/quiz/templates/quiz/sitting_list.html:28 398 | msgid "Filter" 399 | msgstr "Filtro" 400 | 401 | #: apps/quiz/templates/quiz/sitting_list.html:52 402 | msgid "There are no matching quizzes" 403 | msgstr "Non ci sono quiz corrispondenti" 404 | 405 | #: apps/quiz/templates/result.html:7 406 | msgid "Exam Results for" 407 | msgstr "Risultati esame per" 408 | 409 | #: apps/quiz/templates/result.html:32 410 | msgid "Exam results" 411 | msgstr "Risultati esame" 412 | 413 | #: apps/quiz/templates/result.html:34 414 | msgid "Exam title" 415 | msgstr "Titolo esame" 416 | 417 | #: apps/quiz/templates/result.html:38 418 | msgid "You answered" 419 | msgstr "Hai risposto" 420 | 421 | #: apps/quiz/templates/result.html:38 422 | msgid "questions correctly out of" 423 | msgstr "domande cui hai correttamente risposto su" 424 | 425 | #: apps/quiz/templates/result.html:38 426 | msgid "giving you" 427 | msgstr "dandoti" 428 | 429 | #: apps/quiz/templates/result.html:38 430 | msgid "percent correct" 431 | msgstr "percentuale corretta" 432 | 433 | #: apps/quiz/templates/result.html:48 434 | msgid "Review the questions below and try the exam again in the future" 435 | msgstr "Riesamina le domande qui sotto e riprova l'esame in futuro" 436 | 437 | #: apps/quiz/templates/result.html:52 438 | msgid "" 439 | "The result of this exam will be stored in your progress section so you can " 440 | "review and monitor your progression" 441 | msgstr "" 442 | " Il risultato di questo esame verrà memorizzato nella sezione di avanzamento " 443 | "in modo da poter verificare e monitorare i tuoi progressi" 444 | 445 | #: apps/quiz/templates/result.html:66 446 | msgid "Your session score is" 447 | msgstr "Il punteggio di questa sessione è " 448 | 449 | #: apps/quiz/templates/result.html:66 450 | msgid "out of a possible" 451 | msgstr "su un massimo di" 452 | 453 | #: apps/quiz/templates/result.html:84 454 | msgid "Your answer" 455 | msgstr "La tua risposta" 456 | 457 | #: apps/quiz/templates/single_complete.html:13 458 | msgid "You have already sat this exam and only one sitting is permitted" 459 | msgstr "Hai già sostenuto questo esame ed è consentito farlo solo una volta" 460 | 461 | #: apps/quiz/templates/single_complete.html:15 462 | msgid "This exam is only accessible to signed in users" 463 | msgstr "Questo esame è disponibile solo per gli utenti collegati" 464 | 465 | #: apps/quiz/templates/view_quiz_category.html:3 466 | msgid "Quizzes related to" 467 | msgstr "Quiz collegati a" 468 | 469 | #: apps/quiz/templates/view_quiz_category.html:6 470 | msgid "Quizzes in the" 471 | msgstr "Quiz nella" 472 | 473 | #: apps/quiz/templates/view_quiz_category.html:6 474 | msgid "category" 475 | msgstr "categoria" 476 | 477 | #: apps/quiz/templates/view_quiz_category.html:20 478 | msgid "There are no quizzes" 479 | msgstr "Non ci sono quiz" 480 | -------------------------------------------------------------------------------- /quiz/views.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from django.contrib.auth.decorators import login_required, permission_required 4 | from django.core.exceptions import PermissionDenied 5 | from django.shortcuts import get_object_or_404, render 6 | from django.utils.decorators import method_decorator 7 | from django.views.generic import DetailView, ListView, TemplateView, FormView 8 | 9 | from .forms import QuestionForm, EssayForm 10 | from .models import Quiz, Category, Progress, Sitting, Question 11 | from essay.models import Essay_Question 12 | 13 | 14 | class QuizMarkerMixin(object): 15 | @method_decorator(login_required) 16 | @method_decorator(permission_required('quiz.view_sittings')) 17 | def dispatch(self, *args, **kwargs): 18 | return super(QuizMarkerMixin, self).dispatch(*args, **kwargs) 19 | 20 | 21 | class SittingFilterTitleMixin(object): 22 | def get_queryset(self): 23 | queryset = super(SittingFilterTitleMixin, self).get_queryset() 24 | quiz_filter = self.request.GET.get('quiz_filter') 25 | if quiz_filter: 26 | queryset = queryset.filter(quiz__title__icontains=quiz_filter) 27 | 28 | return queryset 29 | 30 | 31 | class QuizListView(ListView): 32 | model = Quiz 33 | 34 | def get_queryset(self): 35 | queryset = super(QuizListView, self).get_queryset() 36 | return queryset.filter(draft=False) 37 | 38 | 39 | class QuizDetailView(DetailView): 40 | model = Quiz 41 | slug_field = 'url' 42 | 43 | def get(self, request, *args, **kwargs): 44 | self.object = self.get_object() 45 | 46 | if self.object.draft and not request.user.has_perm('quiz.change_quiz'): 47 | raise PermissionDenied 48 | 49 | context = self.get_context_data(object=self.object) 50 | return self.render_to_response(context) 51 | 52 | 53 | class CategoriesListView(ListView): 54 | model = Category 55 | 56 | 57 | class ViewQuizListByCategory(ListView): 58 | model = Quiz 59 | template_name = 'view_quiz_category.html' 60 | 61 | def dispatch(self, request, *args, **kwargs): 62 | self.category = get_object_or_404( 63 | Category, 64 | category=self.kwargs['category_name'] 65 | ) 66 | 67 | return super(ViewQuizListByCategory, self).\ 68 | dispatch(request, *args, **kwargs) 69 | 70 | def get_context_data(self, **kwargs): 71 | context = super(ViewQuizListByCategory, self)\ 72 | .get_context_data(**kwargs) 73 | 74 | context['category'] = self.category 75 | return context 76 | 77 | def get_queryset(self): 78 | queryset = super(ViewQuizListByCategory, self).get_queryset() 79 | return queryset.filter(category=self.category, draft=False) 80 | 81 | 82 | class QuizUserProgressView(TemplateView): 83 | template_name = 'progress.html' 84 | 85 | @method_decorator(login_required) 86 | def dispatch(self, request, *args, **kwargs): 87 | return super(QuizUserProgressView, self)\ 88 | .dispatch(request, *args, **kwargs) 89 | 90 | def get_context_data(self, **kwargs): 91 | context = super(QuizUserProgressView, self).get_context_data(**kwargs) 92 | progress, c = Progress.objects.get_or_create(user=self.request.user) 93 | context['cat_scores'] = progress.list_all_cat_scores 94 | context['exams'] = progress.show_exams() 95 | return context 96 | 97 | 98 | class QuizMarkingList(QuizMarkerMixin, SittingFilterTitleMixin, ListView): 99 | model = Sitting 100 | 101 | def get_queryset(self): 102 | queryset = super(QuizMarkingList, self).get_queryset()\ 103 | .filter(complete=True) 104 | 105 | user_filter = self.request.GET.get('user_filter') 106 | if user_filter: 107 | queryset = queryset.filter(user__username__icontains=user_filter) 108 | 109 | return queryset 110 | 111 | 112 | class QuizMarkingDetail(QuizMarkerMixin, DetailView): 113 | model = Sitting 114 | 115 | def post(self, request, *args, **kwargs): 116 | sitting = self.get_object() 117 | 118 | q_to_toggle = request.POST.get('qid', None) 119 | if q_to_toggle: 120 | q = Question.objects.get_subclass(id=int(q_to_toggle)) 121 | if int(q_to_toggle) in sitting.get_incorrect_questions: 122 | sitting.remove_incorrect_question(q) 123 | else: 124 | sitting.add_incorrect_question(q) 125 | 126 | return self.get(request) 127 | 128 | def get_context_data(self, **kwargs): 129 | context = super(QuizMarkingDetail, self).get_context_data(**kwargs) 130 | context['questions'] =\ 131 | context['sitting'].get_questions(with_answers=True) 132 | return context 133 | 134 | 135 | class QuizTake(FormView): 136 | form_class = QuestionForm 137 | template_name = 'question.html' 138 | result_template_name = 'result.html' 139 | single_complete_template_name = 'single_complete.html' 140 | 141 | def dispatch(self, request, *args, **kwargs): 142 | self.quiz = get_object_or_404(Quiz, url=self.kwargs['quiz_name']) 143 | if self.quiz.draft and not request.user.has_perm('quiz.change_quiz'): 144 | raise PermissionDenied 145 | 146 | try: 147 | self.logged_in_user = self.request.user.is_authenticated() 148 | except TypeError: 149 | self.logged_in_user = self.request.user.is_authenticated 150 | 151 | if self.logged_in_user: 152 | self.sitting = Sitting.objects.user_sitting(request.user, 153 | self.quiz) 154 | else: 155 | self.sitting = self.anon_load_sitting() 156 | 157 | if self.sitting is False: 158 | return render(request, self.single_complete_template_name) 159 | 160 | return super(QuizTake, self).dispatch(request, *args, **kwargs) 161 | 162 | def get_form(self, *args, **kwargs): 163 | if self.logged_in_user: 164 | self.question = self.sitting.get_first_question() 165 | self.progress = self.sitting.progress() 166 | else: 167 | self.question = self.anon_next_question() 168 | self.progress = self.anon_sitting_progress() 169 | 170 | if self.question.__class__ is Essay_Question: 171 | form_class = EssayForm 172 | else: 173 | form_class = self.form_class 174 | 175 | return form_class(**self.get_form_kwargs()) 176 | 177 | def get_form_kwargs(self): 178 | kwargs = super(QuizTake, self).get_form_kwargs() 179 | 180 | return dict(kwargs, question=self.question) 181 | 182 | def form_valid(self, form): 183 | if self.logged_in_user: 184 | self.form_valid_user(form) 185 | if self.sitting.get_first_question() is False: 186 | return self.final_result_user() 187 | else: 188 | self.form_valid_anon(form) 189 | if not self.request.session[self.quiz.anon_q_list()]: 190 | return self.final_result_anon() 191 | 192 | self.request.POST = {} 193 | 194 | return super(QuizTake, self).get(self, self.request) 195 | 196 | def get_context_data(self, **kwargs): 197 | context = super(QuizTake, self).get_context_data(**kwargs) 198 | context['question'] = self.question 199 | context['quiz'] = self.quiz 200 | if hasattr(self, 'previous'): 201 | context['previous'] = self.previous 202 | if hasattr(self, 'progress'): 203 | context['progress'] = self.progress 204 | return context 205 | 206 | def form_valid_user(self, form): 207 | progress, c = Progress.objects.get_or_create(user=self.request.user) 208 | guess = form.cleaned_data['answers'] 209 | is_correct = self.question.check_if_correct(guess) 210 | 211 | if is_correct is True: 212 | self.sitting.add_to_score(1) 213 | progress.update_score(self.question, 1, 1) 214 | else: 215 | self.sitting.add_incorrect_question(self.question) 216 | progress.update_score(self.question, 0, 1) 217 | 218 | if self.quiz.answers_at_end is not True: 219 | self.previous = {'previous_answer': guess, 220 | 'previous_outcome': is_correct, 221 | 'previous_question': self.question, 222 | 'answers': self.question.get_answers(), 223 | 'question_type': {self.question 224 | .__class__.__name__: True}} 225 | else: 226 | self.previous = {} 227 | 228 | self.sitting.add_user_answer(self.question, guess) 229 | self.sitting.remove_first_question() 230 | 231 | def final_result_user(self): 232 | results = { 233 | 'quiz': self.quiz, 234 | 'score': self.sitting.get_current_score, 235 | 'max_score': self.sitting.get_max_score, 236 | 'percent': self.sitting.get_percent_correct, 237 | 'sitting': self.sitting, 238 | 'previous': self.previous, 239 | } 240 | 241 | self.sitting.mark_quiz_complete() 242 | 243 | if self.quiz.answers_at_end: 244 | results['questions'] =\ 245 | self.sitting.get_questions(with_answers=True) 246 | results['incorrect_questions'] =\ 247 | self.sitting.get_incorrect_questions 248 | 249 | if self.quiz.exam_paper is False: 250 | self.sitting.delete() 251 | 252 | return render(self.request, self.result_template_name, results) 253 | 254 | def anon_load_sitting(self): 255 | if self.quiz.single_attempt is True: 256 | return False 257 | 258 | if self.quiz.anon_q_list() in self.request.session: 259 | return self.request.session[self.quiz.anon_q_list()] 260 | else: 261 | return self.new_anon_quiz_session() 262 | 263 | def new_anon_quiz_session(self): 264 | """ 265 | Sets the session variables when starting a quiz for the first time 266 | as a non signed-in user 267 | """ 268 | self.request.session.set_expiry(259200) # expires after 3 days 269 | questions = self.quiz.get_questions() 270 | question_list = [question.id for question in questions] 271 | 272 | if self.quiz.random_order is True: 273 | random.shuffle(question_list) 274 | 275 | if self.quiz.max_questions and (self.quiz.max_questions 276 | < len(question_list)): 277 | question_list = question_list[:self.quiz.max_questions] 278 | 279 | # session score for anon users 280 | self.request.session[self.quiz.anon_score_id()] = 0 281 | 282 | # session list of questions 283 | self.request.session[self.quiz.anon_q_list()] = question_list 284 | 285 | # session list of question order and incorrect questions 286 | self.request.session[self.quiz.anon_q_data()] = dict( 287 | incorrect_questions=[], 288 | order=question_list, 289 | ) 290 | 291 | return self.request.session[self.quiz.anon_q_list()] 292 | 293 | def anon_next_question(self): 294 | next_question_id = self.request.session[self.quiz.anon_q_list()][0] 295 | return Question.objects.get_subclass(id=next_question_id) 296 | 297 | def anon_sitting_progress(self): 298 | total = len(self.request.session[self.quiz.anon_q_data()]['order']) 299 | answered = total - len(self.request.session[self.quiz.anon_q_list()]) 300 | return (answered, total) 301 | 302 | def form_valid_anon(self, form): 303 | guess = form.cleaned_data['answers'] 304 | is_correct = self.question.check_if_correct(guess) 305 | 306 | if is_correct: 307 | self.request.session[self.quiz.anon_score_id()] += 1 308 | anon_session_score(self.request.session, 1, 1) 309 | else: 310 | anon_session_score(self.request.session, 0, 1) 311 | self.request\ 312 | .session[self.quiz.anon_q_data()]['incorrect_questions']\ 313 | .append(self.question.id) 314 | 315 | self.previous = {} 316 | if self.quiz.answers_at_end is not True: 317 | self.previous = {'previous_answer': guess, 318 | 'previous_outcome': is_correct, 319 | 'previous_question': self.question, 320 | 'answers': self.question.get_answers(), 321 | 'question_type': {self.question 322 | .__class__.__name__: True}} 323 | 324 | self.request.session[self.quiz.anon_q_list()] =\ 325 | self.request.session[self.quiz.anon_q_list()][1:] 326 | 327 | def final_result_anon(self): 328 | score = self.request.session[self.quiz.anon_score_id()] 329 | q_order = self.request.session[self.quiz.anon_q_data()]['order'] 330 | max_score = len(q_order) 331 | percent = int(round((float(score) / max_score) * 100)) 332 | session, session_possible = anon_session_score(self.request.session) 333 | if score is 0: 334 | score = "0" 335 | 336 | results = { 337 | 'score': score, 338 | 'max_score': max_score, 339 | 'percent': percent, 340 | 'session': session, 341 | 'possible': session_possible 342 | } 343 | 344 | del self.request.session[self.quiz.anon_q_list()] 345 | 346 | if self.quiz.answers_at_end: 347 | results['questions'] = sorted( 348 | self.quiz.question_set.filter(id__in=q_order) 349 | .select_subclasses(), 350 | key=lambda q: q_order.index(q.id)) 351 | 352 | results['incorrect_questions'] = ( 353 | self.request 354 | .session[self.quiz.anon_q_data()]['incorrect_questions']) 355 | 356 | else: 357 | results['previous'] = self.previous 358 | 359 | del self.request.session[self.quiz.anon_q_data()] 360 | 361 | return render(self.request, 'result.html', results) 362 | 363 | 364 | def anon_session_score(session, to_add=0, possible=0): 365 | """ 366 | Returns the session score for non-signed in users. 367 | If number passed in then add this to the running total and 368 | return session score. 369 | 370 | examples: 371 | anon_session_score(1, 1) will add 1 out of a possible 1 372 | anon_session_score(0, 2) will add 0 out of a possible 2 373 | x, y = anon_session_score() will return the session score 374 | without modification 375 | 376 | Left this as an individual function for unit testing 377 | """ 378 | if "session_score" not in session: 379 | session["session_score"], session["session_score_possible"] = 0, 0 380 | 381 | if possible > 0: 382 | session["session_score"] += to_add 383 | session["session_score_possible"] += possible 384 | 385 | return session["session_score"], session["session_score_possible"] 386 | -------------------------------------------------------------------------------- /quiz/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import re 3 | import json 4 | 5 | from django.db import models 6 | from django.core.exceptions import ValidationError, ImproperlyConfigured 7 | from django.core.validators import ( 8 | MaxValueValidator, validate_comma_separated_integer_list, 9 | ) 10 | from django.utils.translation import ugettext_lazy as _ 11 | from django.utils.timezone import now 12 | from django.utils.encoding import python_2_unicode_compatible 13 | from django.conf import settings 14 | 15 | from model_utils.managers import InheritanceManager 16 | 17 | 18 | class CategoryManager(models.Manager): 19 | 20 | def new_category(self, category): 21 | new_category = self.create(category=re.sub('\s+', '-', category) 22 | .lower()) 23 | 24 | new_category.save() 25 | return new_category 26 | 27 | 28 | @python_2_unicode_compatible 29 | class Category(models.Model): 30 | 31 | category = models.CharField( 32 | verbose_name=_("Category"), 33 | max_length=250, blank=True, 34 | unique=True, null=True) 35 | 36 | objects = CategoryManager() 37 | 38 | class Meta: 39 | verbose_name = _("Category") 40 | verbose_name_plural = _("Categories") 41 | 42 | def __str__(self): 43 | return self.category 44 | 45 | 46 | @python_2_unicode_compatible 47 | class SubCategory(models.Model): 48 | 49 | sub_category = models.CharField( 50 | verbose_name=_("Sub-Category"), 51 | max_length=250, blank=True, null=True) 52 | 53 | category = models.ForeignKey( 54 | Category, null=True, blank=True, 55 | verbose_name=_("Category"), on_delete=models.CASCADE) 56 | 57 | objects = CategoryManager() 58 | 59 | class Meta: 60 | verbose_name = _("Sub-Category") 61 | verbose_name_plural = _("Sub-Categories") 62 | 63 | def __str__(self): 64 | return self.sub_category + " (" + self.category.category + ")" 65 | 66 | 67 | @python_2_unicode_compatible 68 | class Quiz(models.Model): 69 | 70 | title = models.CharField( 71 | verbose_name=_("Title"), 72 | max_length=60, blank=False) 73 | 74 | description = models.TextField( 75 | verbose_name=_("Description"), 76 | blank=True, help_text=_("a description of the quiz")) 77 | 78 | url = models.SlugField( 79 | max_length=60, blank=False, 80 | help_text=_("a user friendly url"), 81 | verbose_name=_("user friendly url")) 82 | 83 | category = models.ForeignKey( 84 | Category, null=True, blank=True, 85 | verbose_name=_("Category"), on_delete=models.CASCADE) 86 | 87 | random_order = models.BooleanField( 88 | blank=False, default=False, 89 | verbose_name=_("Random Order"), 90 | help_text=_("Display the questions in " 91 | "a random order or as they " 92 | "are set?")) 93 | 94 | max_questions = models.PositiveIntegerField( 95 | blank=True, null=True, verbose_name=_("Max Questions"), 96 | help_text=_("Number of questions to be answered on each attempt.")) 97 | 98 | answers_at_end = models.BooleanField( 99 | blank=False, default=False, 100 | help_text=_("Correct answer is NOT shown after question." 101 | " Answers displayed at the end."), 102 | verbose_name=_("Answers at end")) 103 | 104 | exam_paper = models.BooleanField( 105 | blank=False, default=False, 106 | help_text=_("If yes, the result of each" 107 | " attempt by a user will be" 108 | " stored. Necessary for marking."), 109 | verbose_name=_("Exam Paper")) 110 | 111 | single_attempt = models.BooleanField( 112 | blank=False, default=False, 113 | help_text=_("If yes, only one attempt by" 114 | " a user will be permitted." 115 | " Non users cannot sit this exam."), 116 | verbose_name=_("Single Attempt")) 117 | 118 | pass_mark = models.SmallIntegerField( 119 | blank=True, default=0, 120 | verbose_name=_("Pass Mark"), 121 | help_text=_("Percentage required to pass exam."), 122 | validators=[MaxValueValidator(100)]) 123 | 124 | success_text = models.TextField( 125 | blank=True, help_text=_("Displayed if user passes."), 126 | verbose_name=_("Success Text")) 127 | 128 | fail_text = models.TextField( 129 | verbose_name=_("Fail Text"), 130 | blank=True, help_text=_("Displayed if user fails.")) 131 | 132 | draft = models.BooleanField( 133 | blank=True, default=False, 134 | verbose_name=_("Draft"), 135 | help_text=_("If yes, the quiz is not displayed" 136 | " in the quiz list and can only be" 137 | " taken by users who can edit" 138 | " quizzes.")) 139 | 140 | def save(self, force_insert=False, force_update=False, *args, **kwargs): 141 | self.url = re.sub('\s+', '-', self.url).lower() 142 | 143 | self.url = ''.join(letter for letter in self.url if 144 | letter.isalnum() or letter == '-') 145 | 146 | if self.single_attempt is True: 147 | self.exam_paper = True 148 | 149 | if self.pass_mark > 100: 150 | raise ValidationError('%s is above 100' % self.pass_mark) 151 | 152 | super(Quiz, self).save(force_insert, force_update, *args, **kwargs) 153 | 154 | class Meta: 155 | verbose_name = _("Quiz") 156 | verbose_name_plural = _("Quizzes") 157 | 158 | def __str__(self): 159 | return self.title 160 | 161 | def get_questions(self): 162 | return self.question_set.all().select_subclasses() 163 | 164 | @property 165 | def get_max_score(self): 166 | return self.get_questions().count() 167 | 168 | def anon_score_id(self): 169 | return str(self.id) + "_score" 170 | 171 | def anon_q_list(self): 172 | return str(self.id) + "_q_list" 173 | 174 | def anon_q_data(self): 175 | return str(self.id) + "_data" 176 | 177 | 178 | class ProgressManager(models.Manager): 179 | 180 | def new_progress(self, user): 181 | new_progress = self.create(user=user, 182 | score="") 183 | new_progress.save() 184 | return new_progress 185 | 186 | 187 | class Progress(models.Model): 188 | """ 189 | Progress is used to track an individual signed in users score on different 190 | quiz's and categories 191 | 192 | Data stored in csv using the format: 193 | category, score, possible, category, score, possible, ... 194 | """ 195 | user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE) 196 | 197 | score = models.CharField(max_length=1024, 198 | verbose_name=_("Score"), 199 | validators=[validate_comma_separated_integer_list]) 200 | 201 | objects = ProgressManager() 202 | 203 | class Meta: 204 | verbose_name = _("User Progress") 205 | verbose_name_plural = _("User progress records") 206 | 207 | @property 208 | def list_all_cat_scores(self): 209 | """ 210 | Returns a dict in which the key is the category name and the item is 211 | a list of three integers. 212 | 213 | The first is the number of questions correct, 214 | the second is the possible best score, 215 | the third is the percentage correct. 216 | 217 | The dict will have one key for every category that you have defined 218 | """ 219 | score_before = self.score 220 | output = {} 221 | 222 | for cat in Category.objects.all(): 223 | to_find = re.escape(cat.category) + r",(\d+),(\d+)," 224 | # group 1 is score, group 2 is highest possible 225 | 226 | match = re.search(to_find, self.score, re.IGNORECASE) 227 | 228 | if match: 229 | score = int(match.group(1)) 230 | possible = int(match.group(2)) 231 | 232 | try: 233 | percent = int(round((float(score) / float(possible)) 234 | * 100)) 235 | except: 236 | percent = 0 237 | 238 | output[cat.category] = [score, possible, percent] 239 | 240 | else: # if category has not been added yet, add it. 241 | self.score += cat.category + ",0,0," 242 | output[cat.category] = [0, 0] 243 | 244 | if len(self.score) > len(score_before): 245 | # If a new category has been added, save changes. 246 | self.save() 247 | 248 | return output 249 | 250 | def update_score(self, question, score_to_add=0, possible_to_add=0): 251 | """ 252 | Pass in question object, amount to increase score 253 | and max possible. 254 | 255 | Does not return anything. 256 | """ 257 | category_test = Category.objects.filter(category=question.category)\ 258 | .exists() 259 | 260 | if any([item is False for item in [category_test, 261 | score_to_add, 262 | possible_to_add, 263 | isinstance(score_to_add, int), 264 | isinstance(possible_to_add, int)]]): 265 | return _("error"), _("category does not exist or invalid score") 266 | 267 | to_find = re.escape(str(question.category)) +\ 268 | r",(?P\d+),(?P\d+)," 269 | 270 | match = re.search(to_find, self.score, re.IGNORECASE) 271 | 272 | if match: 273 | updated_score = int(match.group('score')) + abs(score_to_add) 274 | updated_possible = int(match.group('possible')) +\ 275 | abs(possible_to_add) 276 | 277 | new_score = ",".join( 278 | [ 279 | str(question.category), 280 | str(updated_score), 281 | str(updated_possible), "" 282 | ]) 283 | 284 | # swap old score for the new one 285 | self.score = self.score.replace(match.group(), new_score) 286 | self.save() 287 | 288 | else: 289 | # if not present but existing, add with the points passed in 290 | self.score += ",".join( 291 | [ 292 | str(question.category), 293 | str(score_to_add), 294 | str(possible_to_add), 295 | "" 296 | ]) 297 | self.save() 298 | 299 | def show_exams(self): 300 | """ 301 | Finds the previous quizzes marked as 'exam papers'. 302 | Returns a queryset of complete exams. 303 | """ 304 | return Sitting.objects.filter(user=self.user, complete=True) 305 | 306 | 307 | class SittingManager(models.Manager): 308 | 309 | def new_sitting(self, user, quiz): 310 | if quiz.random_order is True: 311 | question_set = quiz.question_set.all() \ 312 | .select_subclasses() \ 313 | .order_by('?') 314 | else: 315 | question_set = quiz.question_set.all() \ 316 | .select_subclasses() 317 | 318 | question_set = [item.id for item in question_set] 319 | 320 | if len(question_set) == 0: 321 | raise ImproperlyConfigured('Question set of the quiz is empty. ' 322 | 'Please configure questions properly') 323 | 324 | if quiz.max_questions and quiz.max_questions < len(question_set): 325 | question_set = question_set[:quiz.max_questions] 326 | 327 | questions = ",".join(map(str, question_set)) + "," 328 | 329 | new_sitting = self.create(user=user, 330 | quiz=quiz, 331 | question_order=questions, 332 | question_list=questions, 333 | incorrect_questions="", 334 | current_score=0, 335 | complete=False, 336 | user_answers='{}') 337 | return new_sitting 338 | 339 | def user_sitting(self, user, quiz): 340 | if quiz.single_attempt is True and self.filter(user=user, 341 | quiz=quiz, 342 | complete=True)\ 343 | .exists(): 344 | return False 345 | 346 | try: 347 | sitting = self.get(user=user, quiz=quiz, complete=False) 348 | except Sitting.DoesNotExist: 349 | sitting = self.new_sitting(user, quiz) 350 | except Sitting.MultipleObjectsReturned: 351 | sitting = self.filter(user=user, quiz=quiz, complete=False)[0] 352 | return sitting 353 | 354 | 355 | class Sitting(models.Model): 356 | """ 357 | Used to store the progress of logged in users sitting a quiz. 358 | Replaces the session system used by anon users. 359 | 360 | Question_order is a list of integer pks of all the questions in the 361 | quiz, in order. 362 | 363 | Question_list is a list of integers which represent id's of 364 | the unanswered questions in csv format. 365 | 366 | Incorrect_questions is a list in the same format. 367 | 368 | Sitting deleted when quiz finished unless quiz.exam_paper is true. 369 | 370 | User_answers is a json object in which the question PK is stored 371 | with the answer the user gave. 372 | """ 373 | 374 | user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE) 375 | 376 | quiz = models.ForeignKey(Quiz, verbose_name=_("Quiz"), on_delete=models.CASCADE) 377 | 378 | question_order = models.CharField( 379 | max_length=1024, 380 | verbose_name=_("Question Order"), 381 | validators=[validate_comma_separated_integer_list]) 382 | 383 | question_list = models.CharField( 384 | max_length=1024, 385 | verbose_name=_("Question List"), 386 | validators=[validate_comma_separated_integer_list]) 387 | 388 | incorrect_questions = models.CharField( 389 | max_length=1024, 390 | blank=True, 391 | verbose_name=_("Incorrect questions"), 392 | validators=[validate_comma_separated_integer_list]) 393 | 394 | current_score = models.IntegerField(verbose_name=_("Current Score")) 395 | 396 | complete = models.BooleanField(default=False, blank=False, 397 | verbose_name=_("Complete")) 398 | 399 | user_answers = models.TextField(blank=True, default='{}', 400 | verbose_name=_("User Answers")) 401 | 402 | start = models.DateTimeField(auto_now_add=True, 403 | verbose_name=_("Start")) 404 | 405 | end = models.DateTimeField(null=True, blank=True, verbose_name=_("End")) 406 | 407 | objects = SittingManager() 408 | 409 | class Meta: 410 | permissions = (("view_sittings", _("Can see completed exams.")),) 411 | 412 | def get_first_question(self): 413 | """ 414 | Returns the next question. 415 | If no question is found, returns False 416 | Does NOT remove the question from the front of the list. 417 | """ 418 | if not self.question_list: 419 | return False 420 | 421 | first, _ = self.question_list.split(',', 1) 422 | question_id = int(first) 423 | return Question.objects.get_subclass(id=question_id) 424 | 425 | def remove_first_question(self): 426 | if not self.question_list: 427 | return 428 | 429 | _, others = self.question_list.split(',', 1) 430 | self.question_list = others 431 | self.save() 432 | 433 | def add_to_score(self, points): 434 | self.current_score += int(points) 435 | self.save() 436 | 437 | @property 438 | def get_current_score(self): 439 | return self.current_score 440 | 441 | def _question_ids(self): 442 | return [int(n) for n in self.question_order.split(',') if n] 443 | 444 | @property 445 | def get_percent_correct(self): 446 | dividend = float(self.current_score) 447 | divisor = len(self._question_ids()) 448 | if divisor < 1: 449 | return 0 # prevent divide by zero error 450 | 451 | if dividend > divisor: 452 | return 100 453 | 454 | correct = int(round((dividend / divisor) * 100)) 455 | 456 | if correct >= 1: 457 | return correct 458 | else: 459 | return 0 460 | 461 | def mark_quiz_complete(self): 462 | self.complete = True 463 | self.end = now() 464 | self.save() 465 | 466 | def add_incorrect_question(self, question): 467 | """ 468 | Adds uid of incorrect question to the list. 469 | The question object must be passed in. 470 | """ 471 | if len(self.incorrect_questions) > 0: 472 | self.incorrect_questions += ',' 473 | self.incorrect_questions += str(question.id) + "," 474 | if self.complete: 475 | self.add_to_score(-1) 476 | self.save() 477 | 478 | @property 479 | def get_incorrect_questions(self): 480 | """ 481 | Returns a list of non empty integers, representing the pk of 482 | questions 483 | """ 484 | return [int(q) for q in self.incorrect_questions.split(',') if q] 485 | 486 | def remove_incorrect_question(self, question): 487 | current = self.get_incorrect_questions 488 | current.remove(question.id) 489 | self.incorrect_questions = ','.join(map(str, current)) 490 | self.add_to_score(1) 491 | self.save() 492 | 493 | @property 494 | def check_if_passed(self): 495 | return self.get_percent_correct >= self.quiz.pass_mark 496 | 497 | @property 498 | def result_message(self): 499 | if self.check_if_passed: 500 | return self.quiz.success_text 501 | else: 502 | return self.quiz.fail_text 503 | 504 | def add_user_answer(self, question, guess): 505 | current = json.loads(self.user_answers) 506 | current[question.id] = guess 507 | self.user_answers = json.dumps(current) 508 | self.save() 509 | 510 | def get_questions(self, with_answers=False): 511 | question_ids = self._question_ids() 512 | questions = sorted( 513 | self.quiz.question_set.filter(id__in=question_ids) 514 | .select_subclasses(), 515 | key=lambda q: question_ids.index(q.id)) 516 | 517 | if with_answers: 518 | user_answers = json.loads(self.user_answers) 519 | for question in questions: 520 | question.user_answer = user_answers[str(question.id)] 521 | 522 | return questions 523 | 524 | @property 525 | def questions_with_user_answers(self): 526 | return { 527 | q: q.user_answer for q in self.get_questions(with_answers=True) 528 | } 529 | 530 | @property 531 | def get_max_score(self): 532 | return len(self._question_ids()) 533 | 534 | def progress(self): 535 | """ 536 | Returns the number of questions answered so far and the total number of 537 | questions. 538 | """ 539 | answered = len(json.loads(self.user_answers)) 540 | total = self.get_max_score 541 | return answered, total 542 | 543 | 544 | @python_2_unicode_compatible 545 | class Question(models.Model): 546 | """ 547 | Base class for all question types. 548 | Shared properties placed here. 549 | """ 550 | 551 | quiz = models.ManyToManyField(Quiz, 552 | verbose_name=_("Quiz"), 553 | blank=True) 554 | 555 | category = models.ForeignKey(Category, 556 | verbose_name=_("Category"), 557 | blank=True, 558 | null=True, 559 | on_delete=models.CASCADE) 560 | 561 | sub_category = models.ForeignKey(SubCategory, 562 | verbose_name=_("Sub-Category"), 563 | blank=True, 564 | null=True, 565 | on_delete=models.CASCADE) 566 | 567 | figure = models.ImageField(upload_to='uploads/%Y/%m/%d', 568 | blank=True, 569 | null=True, 570 | verbose_name=_("Figure")) 571 | 572 | content = models.CharField(max_length=1000, 573 | blank=False, 574 | help_text=_("Enter the question text that " 575 | "you want displayed"), 576 | verbose_name=_('Question')) 577 | 578 | explanation = models.TextField(max_length=2000, 579 | blank=True, 580 | help_text=_("Explanation to be shown " 581 | "after the question has " 582 | "been answered."), 583 | verbose_name=_('Explanation')) 584 | 585 | objects = InheritanceManager() 586 | 587 | class Meta: 588 | verbose_name = _("Question") 589 | verbose_name_plural = _("Questions") 590 | ordering = ['category'] 591 | 592 | def __str__(self): 593 | return self.content 594 | --------------------------------------------------------------------------------