From bc75ff183fad184e25a92bc53003f93519cf42cb Mon Sep 17 00:00:00 2001 From: Guillaume Pellerin Date: Thu, 18 Sep 2025 10:59:33 +0200 Subject: [PATCH] add quiz views, templates and validation --- .gitmodules | 3 + lib/Django-Quiz | 1 + poetry.lock | 36 +----- pyproject.toml | 1 - teleforma/migrations/0033_course_quizzes.py | 19 +++ teleforma/migrations/0034_quizvalidation.py | 32 +++++ teleforma/models/core.py | 7 +- teleforma/models/crfpa.py | 27 ++++ teleforma/static/teleforma/css/teleforma.css | 32 +++++ teleforma/static/teleforma/images/loading.png | Bin 0 -> 590 bytes teleforma/templates/quiz/correct_answer.html | 54 ++++---- teleforma/templates/quiz/question.html | 116 +++++++++--------- teleforma/templates/quiz/quiz_detail.html | 4 +- teleforma/templates/quiz/quiz_list.html | 2 +- teleforma/templates/quiz/result.html | 85 ++++++++----- .../templates/teleforma/course_detail.html | 60 +++++---- teleforma/templatetags/teleforma_tags.py | 18 ++- teleforma/urls.py | 8 +- teleforma/views/crfpa.py | 66 +++++++++- 19 files changed, 393 insertions(+), 178 deletions(-) create mode 160000 lib/Django-Quiz create mode 100644 teleforma/migrations/0033_course_quizzes.py create mode 100644 teleforma/migrations/0034_quizvalidation.py create mode 100644 teleforma/static/teleforma/images/loading.png diff --git a/.gitmodules b/.gitmodules index 8bf74f76..aca39801 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/pdfannotator"] path = lib/pdfannotator url = https://git.pilotsystems.net/probarreau/pdfannotator.git +[submodule "lib/Django-Quiz"] + path = lib/Django-Quiz + url = https://github.com/Parisson/Django-Quiz.git diff --git a/lib/Django-Quiz b/lib/Django-Quiz new file mode 160000 index 00000000..414ffcec --- /dev/null +++ b/lib/Django-Quiz @@ -0,0 +1 @@ +Subproject commit 414ffcec05588d41652cdd1799706abccad9b8b7 diff --git a/poetry.lock b/poetry.lock index 880add7c..7fa429ca 100644 --- a/poetry.lock +++ b/poetry.lock @@ -713,20 +713,6 @@ files = [ Django = ">=1.0" six = "*" -[[package]] -name = "django-model-utils" -version = "5.0.0" -description = "Django model mixins and utilities" -optional = false -python-versions = ">=3.8" -files = [ - {file = "django_model_utils-5.0.0-py3-none-any.whl", hash = "sha256:fec78e6c323d565a221f7c4edc703f4567d7bb1caeafe1acd16a80c5ff82056b"}, - {file = "django_model_utils-5.0.0.tar.gz", hash = "sha256:041cdd6230d2fbf6cd943e1969318bce762272077f4ecd333ab2263924b4e5eb"}, -] - -[package.dependencies] -Django = ">=3.2" - [[package]] name = "django-nvd3" version = "0.9.7" @@ -753,26 +739,6 @@ files = [ [package.dependencies] Django = "*" -[[package]] -name = "django-quiz-app" -version = "0.5.1" -description = "A configurable quiz app for Django." -optional = false -python-versions = "*" -files = [] -develop = false - -[package.dependencies] -Django = ">=1.5.1" -django-model-utils = ">=2.0.3" -Pillow = ">=2.5.0" - -[package.source] -type = "git" -url = "https://github.com/pilot-systems/Django-Quiz.git" -reference = "HEAD" -resolved_reference = "414ffcec05588d41652cdd1799706abccad9b8b7" - [[package]] name = "django-recaptcha" version = "2.0.6" @@ -2679,4 +2645,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "75063f62ed7197ea62b66b4106181196aeeeeb8c0513c3e3ae15173e4d52d6f5" +content-hash = "b1c45f071661223d1e19bf13115bb28c4ac30c7053b314db769523a7a96702c3" diff --git a/pyproject.toml b/pyproject.toml index 2b7e5943..a4702662 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,6 @@ django-storages = {extras = ["s3"], version = "^1.14.2"} boto3 = "^1.34.89" ipython = "^8.23.0" reportlab = "^4.2.0" -django-quiz-app = {git = "https://github.com/pilot-systems/Django-Quiz.git"} [build-system] diff --git a/teleforma/migrations/0033_course_quizzes.py b/teleforma/migrations/0033_course_quizzes.py new file mode 100644 index 00000000..91b4b57a --- /dev/null +++ b/teleforma/migrations/0033_course_quizzes.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.25 on 2025-09-11 16:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('quiz', '__first__'), + ('teleforma', '0032_alter_mediaread_duration'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='quizzes', + field=models.ManyToManyField(blank=True, related_name='courses', to='quiz.Quiz', verbose_name='Quizzes'), + ), + ] diff --git a/teleforma/migrations/0034_quizvalidation.py b/teleforma/migrations/0034_quizvalidation.py new file mode 100644 index 00000000..3e5e6e5c --- /dev/null +++ b/teleforma/migrations/0034_quizvalidation.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.25 on 2025-09-16 23:01 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('quiz', '__first__'), + ('teleforma', '0033_course_quizzes'), + ] + + operations = [ + migrations.CreateModel( + name='QuizValidation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('validated', models.BooleanField(verbose_name='validated')), + ('date_validated', models.DateTimeField(auto_now_add=True, null=True, verbose_name='date validated')), + ('quiz', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quiz_validation', to='quiz.quiz', verbose_name='quiz')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quiz_validation', to=settings.AUTH_USER_MODEL, verbose_name='user')), + ], + options={ + 'verbose_name': 'Quiz validation', + 'db_table': 'teleforma_quiz_validation', + 'ordering': ['-date_validated'], + }, + ), + ] diff --git a/teleforma/models/core.py b/teleforma/models/core.py index 185f6c1d..29f2cf3f 100644 --- a/teleforma/models/core.py +++ b/teleforma/models/core.py @@ -63,7 +63,7 @@ from ..fields import ShortTextField from sorl.thumbnail import default as sorl_default from pypdf import PdfWriter import httpx - +from quiz.models import Quiz from storages.backends.s3boto3 import S3Boto3Storage @@ -283,6 +283,9 @@ class Course(models.Model): corrections_shared = models.BooleanField("Corrections partagés", help_text="A utiliser avec le champ relatif dans la période.", default=False) + quizzes = models.ManyToManyField(Quiz, related_name="courses", + verbose_name=u'Quizzes', + blank=True) def __str__(self): return self.title @@ -1165,3 +1168,5 @@ class MediaRead(models.Model): user = models.ForeignKey(User, related_name='read', verbose_name=_('user'), blank=True, null=True, on_delete=models.SET_NULL) duration = models.DurationField(_("duration"), blank=True, null=True) + + diff --git a/teleforma/models/crfpa.py b/teleforma/models/crfpa.py index 13325b2e..6a71b83e 100644 --- a/teleforma/models/crfpa.py +++ b/teleforma/models/crfpa.py @@ -45,10 +45,12 @@ from django.urls.base import reverse_lazy from django.utils.translation import ugettext_lazy as _ from tinymce.models import HTMLField from django.core.cache import cache +from quiz.models import Quiz from ..models.core import (Course, Media, MetaCore, payment_choices, payment_schedule_choices) + app_label = 'teleforma' months_choices = [] @@ -683,3 +685,28 @@ class NewsItem(models.Model): def can_delete(self, request): return request.user.is_staff or request.user.id == self.creator.id + + +class QuizValidation(models.Model): + + user = models.ForeignKey(User, related_name="quiz_validation", verbose_name=_('user'), on_delete=models.CASCADE) + quiz = models.ForeignKey(Quiz, related_name="quiz_validation", verbose_name=_('quiz'), + blank=True, null=True, on_delete=models.SET_NULL) + validated = models.BooleanField(_('validated')) + date_validated = models.DateTimeField(_('date validated'), auto_now_add=True, null=True) + + def __str__(self): + return ' - '.join([str(self.quiz), self.user.username, str(self.date_validated)]) + + def validate(self): + self.validated = True + self.save() + + def reject(self): + self.validated = False + self.save() + + class Meta(MetaCore): + db_table = app_label + '_' + 'quiz_validation' + verbose_name = _('Quiz validation') + ordering = ['-date_validated'] diff --git a/teleforma/static/teleforma/css/teleforma.css b/teleforma/static/teleforma/css/teleforma.css index 33641618..8deb4233 100644 --- a/teleforma/static/teleforma/css/teleforma.css +++ b/teleforma/static/teleforma/css/teleforma.css @@ -1302,6 +1302,9 @@ table.listing tbody td.show_more_videos { .icon_wait{ background-image: url('/static/teleforma/images/wait.gif'); } +.icon_loading{ + background-image: url('/static/teleforma/images/loading.png'); +} .icon_rss,.icon_rss:hover{ background: url('/static/teleforma/images/feed-icon-14x14.png') no-repeat; background-position: 0ex .8ex; @@ -1760,6 +1763,22 @@ form.add_actus #id_text_parent{ scroll-margin-block-end: 1em; } +.course_sub_title { + color: #355ea2; + -moz-border-radius: 8px 0px 0px 0px; + -webkit-border-radius: 8px 0px 0px 0px; + border-radius: 8px 0px 0px 0px; + padding: 1em 0em 0.5em 0em; + font-weight: bold; + font-size: 1.1em; + display: flex; + flex-direction: row; + justify-content: space-between; + scroll-margin-block-start: 1em; + scroll-margin-block-end: 1em; +} + + .create-bbb-conference { font-size: 0.8em; } @@ -3380,4 +3399,17 @@ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra pad flex-direction: row-reverse; justify-content: start; font-weight: bold; +} + +#flex-container { + display: flex; + flex-direction: row; +} + +#flex-container > .flex-item { + flex: auto; +} + +#flex-container > .raw-item { + width: 5rem; } \ No newline at end of file diff --git a/teleforma/static/teleforma/images/loading.png b/teleforma/static/teleforma/images/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..16c2673081b941193e27688b9822751982a42fd1 GIT binary patch literal 590 zcmV-U0pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10ozGL zK~y-6mD9~j)KL`1@y~BYOD$Vy5K@9GH!?+1Ak-pTNI{EQxrho3rbSuM-_RAS>1c?eGcc``#q6L-3jfI(juK` zDMqJ7#*uX?dcFC2R9f8*wd(Yvrz~EsfO{77x*jcVB#6zUI10PAai)d+jj?NDmf=s# zuapu!v!H7|37Ie`6M{?#XP<qg^`J~Fq$wG^pF za{1!>C8ZNM4tsYnvq0Zuxfv4pxx&lutgbP$So(s_NG2>{+h1ONi)xbO3#ILHqVn)x z+GH+P7fi#!-OTs!;xso7$7NUQRtN98Ni~#0QwfYiQ!P~qI`*)$nzagZ*FLVMXsYAe zuVOHGTBRVXO1hy&G&xIK1DD%km_|Ow=$&ZhLMzFL-sj|&iuQtRoGB9q>xW`Uy2oN@ zDwWH}kgWsf?m9?pLAky~Dtr!CvWL;wH)07*qoM6N<$f(auF#sB~S literal 0 HcmV?d00001 diff --git a/teleforma/templates/quiz/correct_answer.html b/teleforma/templates/quiz/correct_answer.html index d98a10d9..0d2422c4 100644 --- a/teleforma/templates/quiz/correct_answer.html +++ b/teleforma/templates/quiz/correct_answer.html @@ -3,30 +3,38 @@ {% if user_was_incorrect %}
- {% trans "You answered the above question incorrectly" %} + {% trans "You answered the above question incorrectly" %}
{% endif %} - - - {% for answer in previous.answers %} - {% if answer.correct %} - - - - {% else %} - - - - {% endif %} - - {% endfor %} - -
{{ answer.content }}{% trans "This is the correct answer" %}
{{ answer.content }} - {% if previous.question_type.MCQuestion %} - {% if answer.id|add:"0" == previous.previous_answer|add:"0" %} - {% trans "This was your answer." %} - {% endif %} - {% endif %} -
+
+ + + {% for answer in previous.answers %} + {% if answer.correct %} + + + + {% else %} + + + + {% endif %} + + {% endfor %} + +
{{ answer.content }}{% trans "This is the correct answer" %}
{{ answer.content }} + {% if previous.question_type.MCQuestion %} + {% if answer.id|add:"0" == previous.previous_answer|add:"0" %} + {% trans "This was your answer." %} + {% endif %} + {% endif %} +
+
{% endif %} diff --git a/teleforma/templates/quiz/question.html b/teleforma/templates/quiz/question.html index 29733ebd..2d73127f 100644 --- a/teleforma/templates/quiz/question.html +++ b/teleforma/templates/quiz/question.html @@ -1,82 +1,88 @@ -{% extends "teleforma/base.html" %} +{% extends "teleforma/course_detail.html" %} {% load i18n%} - {% load quiz_tags %} -{% block title %} {{ quiz.title }} {% endblock %} -{% block description %} {{ quiz.title }} - {{ quiz.description }} {% endblock %} +{% block course %} -{% block content %} +
-{% if previous.answers %} +
+ {{ course.title }} +
-

{% trans "The previous question" %}:

-

{{ previous.previous_question }}

+
+ Quiz - {{ quiz.title }} +
- {% if previous.previous_outcome %} -
- {% else %} -
- {% endif %} -

- Votre réponse est - - {{ previous.previous_outcome|yesno:"correct,incorrect" }} - -

+ {% block description %} {{ quiz.description }} {% endblock %} -
+ {% if previous.answers %} - {% include 'correct_answer.html' %} +
+ {% trans "The previous question" %}: +
-

{% trans "Explanation" %}:

-
-

{{ previous.previous_question.explanation }}

-
+
+

{{ previous.previous_question }}

+ {% include 'quiz/correct_answer.html' %} +
-
+

Votre réponse est + + {{ previous.previous_outcome|yesno:"correct,incorrect" }} + +

-{% endif %} +
{% trans "Explanation" %}:
+
+

{{ previous.previous_question.explanation }}

+
-
+ {% endif %} -{% if question %} +
-{% if progress %} -
-{% trans "Question" %} {{ progress.0|add:1 }} {% trans "of" %} {{ progress.1 }} -
-{% endif %} + {% if question %} -{% if question.category %}

- {% trans "Question category" %}: - {{ question.category }} -

{% endif %} + {% if progress %} +
+ {% trans "Question" %} {{ progress.0|add:1 }} {% trans "of" %} {{ progress.1 }} +
+ {% endif %} -

{{ question.content }}

+ {% if question.category %}

+ {% trans "Question category" %}: + {{ question.category }} +

{% endif %} -{% if question.figure %} - {{ question.content }} -{% endif %} +
-
{% csrf_token %} - +

{{ question.content }}

-
    + {% if question.figure %} + {{ question.content }} + {% endif %} - {% for answer in form.answers %} -
  • - {{ answer }} -
  • - {% endfor %} + {% csrf_token %} + -
- -
+
    + {% for answer in form.answers %} +
  • + {{ answer }} +
  • + {% endfor %} +
-{% endif %} + -
+ +
+ + {% endif %} + + +
{% endblock %} diff --git a/teleforma/templates/quiz/quiz_detail.html b/teleforma/templates/quiz/quiz_detail.html index 6a66bc33..6032e90b 100644 --- a/teleforma/templates/quiz/quiz_detail.html +++ b/teleforma/templates/quiz/quiz_detail.html @@ -1,10 +1,10 @@ -{% extends 'teleforma/base.html' %} +{% extends 'teleforma/course_detail.html' %} {% load i18n %} {% block title %} {{ quiz.title }} {% endblock %} -{% block content %} +{% block course %} {% if quiz.single_attempt %} diff --git a/teleforma/templates/quiz/quiz_list.html b/teleforma/templates/quiz/quiz_list.html index 0b163759..029d9da9 100644 --- a/teleforma/templates/quiz/quiz_list.html +++ b/teleforma/templates/quiz/quiz_list.html @@ -27,7 +27,7 @@ {{ quiz.exam_paper }} {{ quiz.single_attempt }} - + {% trans "View details" %} diff --git a/teleforma/templates/quiz/result.html b/teleforma/templates/quiz/result.html index d5bb4d27..e1da6b5c 100644 --- a/teleforma/templates/quiz/result.html +++ b/teleforma/templates/quiz/result.html @@ -1,74 +1,81 @@ -{% extends "teleforma/base.html" %} +{% extends "quiz/question.html" %} {% load i18n %} - {% load quiz_tags %} -{% block title %} {{ quiz.title}} {% endblock %} -{% block description %} {% trans "Exam Results for" %} {{ quiz.title }} {% endblock %} +{% block course %} + +
+ +
+ {{ course.title }} +
+ +
+ Quiz - {{ quiz.title }} +
-{% block content %} +{% block description %} {{ quiz.description }} {% endblock %} {% if previous.answers %} -

{% trans "The previous question" %}:

-

{{ previous.previous_question }}

+
+ {% trans "The previous question" %}: +
+ +
+

{{ previous.previous_question }}

+ {% include 'quiz/correct_answer.html' %} +
+

Votre réponse est - - {{ previous.previous_outcome|yesno:"correct,incorrect" }} - + + {{ previous.previous_outcome|yesno:"correct,incorrect" }} +

- {% include 'correct_answer.html' %} -

{% trans "Explanation" %}:

-
+ +
{% trans "Explanation" %}:
+

{{ previous.previous_question.explanation }}

-
+ {% endif %} {% if max_score %}
-

{% trans "Exam results" %}

-

- {% trans "Exam title" %}: - {{ quiz.title }}

+
{% trans "Exam results" %}

- {% trans "You answered" %} {{ score }} {% trans "questions correctly out of" %} {{ max_score }}, {% trans "giving you" %} {{ percent }} {% trans "percent correct" %} + {% if percent >= quiz.pass_mark %} + 🎉 💯 + {% endif %} + + {% trans "You answered" %} {{ score }} {% trans "questions correctly out of" %} {{ max_score }}, {% trans "giving you" %} {{ percent }} {% trans "percent correct" %}

{% if quiz.pass_mark %} -
-

{{ sitting.result_message }}

-
- +

{{ sitting.result_message }}

{% endif %}
- {% endif %} - -
- {% if possible %}

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

-
- {% endif %} {% if questions %} {% for question in questions %} -

- {{ question.content }} +

+ {{ question.content }}

{% correct_answer_for_all question %} @@ -82,11 +89,25 @@

{{ question.explanation }}

-
{% endfor %} {% endif %} +

+ + + +
{% endblock %} diff --git a/teleforma/templates/teleforma/course_detail.html b/teleforma/templates/teleforma/course_detail.html index bc2007df..cbf69dc5 100644 --- a/teleforma/templates/teleforma/course_detail.html +++ b/teleforma/templates/teleforma/course_detail.html @@ -48,41 +48,53 @@ {{ course.description }}{% endif %}
- {% if type.name == 'Quiz' %} -
- {% if course.quiz.all %} - - - {% for quiz in course.quiz.all %} - - - {% endfor %} - -
{{quiz.title}}{{quiz.description}}
- {% else %} -

Aucun quiz

- {% endif %} -
- - {% else %} {% if show_media %} - {% block conference %} - {% include "teleforma/inc/conference_list.html" %} - {% endblock %} + {% block conference %} + {% include "teleforma/inc/conference_list.html" %} + {% endblock %} - {% block media %} - {% include "teleforma/inc/media_list.html" %} - {% endblock %} + {% block media %} + {% include "teleforma/inc/media_list.html" %} + {% endblock %} {% endif %} {% block document %} {% include "teleforma/inc/document_list.html" %} {% endblock %} - {% endif %} {% endfor %} +
+ {% if course.quizzes.all %} +
+ Quizes +
+
+ + + {% for quiz in course.quizzes.all %} + + + + + + {% endfor %} + +
{{ quiz.title }}{{ quiz.description }} + {% if quiz|quiz_validated:user %} + + {% else %} + + {% endif %} +
+
+ {% else %} +

Aucun quiz

+ {% endif %} +
+ +
Webclasses{% if course.description %} - {{ course.description }}{% endif %} diff --git a/teleforma/templatetags/teleforma_tags.py b/teleforma/templatetags/teleforma_tags.py index 216528ca..52984cc2 100644 --- a/teleforma/templatetags/teleforma_tags.py +++ b/teleforma/templatetags/teleforma_tags.py @@ -53,12 +53,13 @@ from teleforma.views.core import get_course_conferences from ..exam.models import Quota, Script from ..models.core import Document, Professor -from ..models.crfpa import IEJ, Course, NewsItem, Training +from ..models.crfpa import IEJ, Course, NewsItem, Training, QuizValidation from ..views import get_courses from ..utils import generate_hash from collections import defaultdict + register = template.Library() # more translations for template variables @@ -515,3 +516,18 @@ def generate_msg_id(context, message): # if not context['user'].is_staff or context.get('list_view', None): # media = media.filter(is_published = True) # return list(media) + + + +@register.filter +def one_more(_1, _2): + # to pass more than one argument to filter + return _1, _2 + +@register.filter +def quiz_validated(quiz, user): + validations = QuizValidation.objects.filter(quiz=quiz, user=user, validated=True) + if validations: + return validations[0].date_validated + else: + return '' diff --git a/teleforma/urls.py b/teleforma/urls.py index 3d97a65f..da2ab662 100644 --- a/teleforma/urls.py +++ b/teleforma/urls.py @@ -66,10 +66,11 @@ from .views.crfpa import (AnnalsCourseView, AnnalsIEJView, AnnalsView, RegistrationPDFViewDownload, RetractationView, UserAddView, UserCompleteView, UserLoginView, UsersExportView, UsersView, WriteView, update_training, - UserAddUseYourLawOriginView) + UserAddUseYourLawOriginView, QuizQuestionView) from .views.payment import (PaymentStartView, bank_auto, bank_cancel, bank_fail, bank_success) + htdocs_forma = os.path.dirname(__file__) + '/static/teleforma/' profile_view = CRFPAProfileView() document = DocumentView() @@ -284,5 +285,8 @@ urlpatterns = [ # must be called on channels instance path('live_conference_notify', - LiveConferenceNotify.as_view(), name='teleforma-live-conference-notify') + LiveConferenceNotify.as_view(), name='teleforma-live-conference-notify'), + + # QUIZZ + url(r'^desk/periods/(?P.*)/courses/(?P.*)/quiz/(?P[\w-]+)/$', QuizQuestionView.as_view(), name="teleforma-quiz"), ] diff --git a/teleforma/views/crfpa.py b/teleforma/views/crfpa.py index 89771f46..32bd922e 100644 --- a/teleforma/views/crfpa.py +++ b/teleforma/views/crfpa.py @@ -59,23 +59,28 @@ from postman.forms import AnonymousWriteForm from postman.views import WriteView as PostmanWriteView from xlwt import Workbook from django.conf import settings +from quiz.views import QuizTake +from quiz.models import Quiz from ..decorators import access_required from ..forms import (CorrectorForm, NewsItemForm, RetractationForm, UserForm, WriteForm, get_unique_username, UserUseYourLawOriginForm) from ..models.core import Course, CourseType, Document, NamePaginator, Period from ..models.crfpa import (IEJ, Discount, NewsItem, Parameters, Payback, - Payment, Profile, Student, Training, months_choices, payment_choices) + Payment, Profile, Student, Training, months_choices, payment_choices, + QuizValidation) from ..views.core import (CourseAccessMixin, PDFTemplateResponseMixin, format_courses, get_courses, get_periods) from ..views.profile import ProfileView + def get_course_code(obj): if obj: return str(obj.code) else: return '' + def get_crfpa_courses(user, date_order=False, num_order=False, period=None): courses = [] @@ -1196,3 +1201,62 @@ class CRFPAProfileView(ProfileView): payment = payment[0] return render(request, template, {'profile' : profile, 'usr': user, 'payment':payment}) + + +class QuizQuestionView(CourseAccessMixin, QuizTake): + + template_name = 'quiz/question.html' + + def _init(self): + self.user = self.get_user() + self.period = Period.objects.get(pk=self.kwargs['period_id']) + self.course = Course.objects.get(pk=self.kwargs['course_id']) + self.quiz = Quiz.objects.get(url=self.kwargs['quiz_name']) + + def get_user(self): + user_id = self.request.user.id + return User.objects.get(id=user_id) + + def get_context_data(self, **kwargs): + context = super(QuizQuestionView, self).get_context_data(**kwargs) + self._init() + context['period_id'] = self.kwargs['period_id'] + context['period'] = self.period + context['course_id'] = self.kwargs['course_id'] + context['course'] = self.course + context['quiz'] = self.quiz + return context + + def final_result_user(self): + self._init() + + results = { + 'quiz': self.quiz, + 'score': self.sitting.get_current_score, + 'max_score': self.sitting.get_max_score, + 'percent': self.sitting.get_percent_correct, + 'sitting': self.sitting, + 'previous': self.previous, + 'period': self.period, + 'course': self.course, + 'quiz': self.quiz, + 'all_courses': get_courses(self.user, num_order=True, period=self.period), + } + + self.sitting.mark_quiz_complete() + + if self.quiz.answers_at_end: + results['questions'] =\ + self.sitting.get_questions(with_answers=True) + results['incorrect_questions'] =\ + self.sitting.get_incorrect_questions + + if self.quiz.exam_paper is False: + self.sitting.delete() + + if self.sitting.get_percent_correct >= self.quiz.pass_mark: + validation = QuizValidation( + user=self.user, quiz=self.quiz, validated=True) + validation.save() + + return render(self.request, 'quiz/result.html', results) -- 2.39.5