]> git.parisson.com Git - teleforma.git/commitdiff
add quiz views, templates and validation
authorGuillaume Pellerin <guillaume.pellerin@parisson.com>
Thu, 18 Sep 2025 08:59:33 +0000 (10:59 +0200)
committerGuillaume Pellerin <guillaume.pellerin@parisson.com>
Thu, 18 Sep 2025 08:59:33 +0000 (10:59 +0200)
19 files changed:
.gitmodules
lib/Django-Quiz [new submodule]
poetry.lock
pyproject.toml
teleforma/migrations/0033_course_quizzes.py [new file with mode: 0644]
teleforma/migrations/0034_quizvalidation.py [new file with mode: 0644]
teleforma/models/core.py
teleforma/models/crfpa.py
teleforma/static/teleforma/css/teleforma.css
teleforma/static/teleforma/images/loading.png [new file with mode: 0644]
teleforma/templates/quiz/correct_answer.html
teleforma/templates/quiz/question.html
teleforma/templates/quiz/quiz_detail.html
teleforma/templates/quiz/quiz_list.html
teleforma/templates/quiz/result.html
teleforma/templates/teleforma/course_detail.html
teleforma/templatetags/teleforma_tags.py
teleforma/urls.py
teleforma/views/crfpa.py

index 8bf74f7680728a0ac6d136d7d5e6afb018d7e51e..aca39801310f654901dcf872efda782db1faaa9e 100644 (file)
@@ -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 (submodule)
index 0000000..414ffce
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 414ffcec05588d41652cdd1799706abccad9b8b7
index 880add7cf101adb830506bf853308a530ea3eadb..7fa429ca08f0cece4f0ca81de7a4e239ff47da49 100644 (file)
@@ -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"
index 2b7e59433927ed95570be1a24946e53f93420ee1..a4702662a92f5a48d7759032a860425e94203e53 100644 (file)
@@ -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 (file)
index 0000000..91b4b57
--- /dev/null
@@ -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 (file)
index 0000000..3e5e6e5
--- /dev/null
@@ -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'],
+            },
+        ),
+    ]
index 185f6c1d0f35e9cd128ac5206bab65a75ac77ad0..29f2cf3f064fd753249f53e33505c2d73ae4203f 100644 (file)
@@ -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)
+
+
index 13325b2efda43872a04bd2e1656f27893e66e004..6a71b83e77844f67ea85be663ddb4f37e5b452e4 100644 (file)
@@ -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']
index 3364161867e6da7c3f48e333b757e6bc9e49ae90..8deb42336665a931bd6bd5e0ce04cdc1f107b547 100644 (file)
@@ -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 (file)
index 0000000..16c2673
Binary files /dev/null and b/teleforma/static/teleforma/images/loading.png differ
index d98a10d96d9c2b41a03f958f7ef080c50e86a933..0d2422c4b492cd4c85a507164e1d54cc5d7f7157 100644 (file)
@@ -3,30 +3,38 @@
 
   {% if user_was_incorrect %}
     <div class="alert alert-error">
-         <strong>{% trans "You answered the above question incorrectly" %}</strong>
+      <strong>{% trans "You answered the above question incorrectly" %}</strong>
     </div>
   {% endif %}
 
-  <table class="table table-striped table-bordered">
-    <tbody>
-         {% for answer in previous.answers %}
-        {% if answer.correct %}
-           <tr class="success">
-                 <td>{{ answer.content }}</td>
-                 <td><strong>{% trans "This is the correct answer" %}</strong></td>
-        {% else %}
-               <tr>
-         <td>{{ answer.content }}</td>
-                 <td>
-                       {% if previous.question_type.MCQuestion %}
-                         {% if answer.id|add:"0" == previous.previous_answer|add:"0" %}
-                           {% trans "This was your answer." %}
-                         {% endif %}
-                       {% endif %}
-                 </td>
-         {% endif %}
-           </tr>
-       {% endfor %}
-    </tbody>
-  </table>
+  <div style="padding: 1em">
+    <table class="table table-striped table-bordered">
+      <tbody>
+        {% for answer in previous.answers %}
+          {% if answer.correct %}
+          <tr class="success">
+          <td>{{ answer.content }}</td>
+          <td><strong>{% trans "This is the correct answer" %}</strong></td>
+          {% else %}
+        <tr
+          {% if previous.question_type.MCQuestion %}
+            {% if answer.id|add:"0" == previous.previous_answer|add:"0" %}
+              class="error"
+            {% endif %}
+          {% endif %}
+        >
+            <td>{{ answer.content }}</td>
+          <td>
+          {% if previous.question_type.MCQuestion %}
+            {% if answer.id|add:"0" == previous.previous_answer|add:"0" %}
+              {% trans "This was your answer." %}
+            {% endif %}
+          {% endif %}
+          </td>
+        {% endif %}
+          </tr>
+      {% endfor %}
+      </tbody>
+    </table>
+  </div>
 {% endif %}
index 29733ebd67ba8eb25ea853fed2ad4b4d853b1b70..2d73127f80c5d4279e0671bcc9ee888a315932fa 100644 (file)
@@ -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 %}
+<div class="course_media">
 
-{% if previous.answers %}
+       <div class="course_main_title">
+           {{ course.title }}
+         </div>
 
-  <p class="muted"><small>{% trans "The previous question" %}:</small></p>
-  <p>{{ previous.previous_question }}</p>
+       <div class="course_title">
+                Quiz - {{ quiz.title }}
+       </div>
 
-  {% if previous.previous_outcome %}
-       <div class="alert alert-success">
-  {% else %}
-       <div class="alert alert-warning">
-  {% endif %}
-         <p><small>
-               Votre réponse est </small>
-               <strong>
-                 {{ previous.previous_outcome|yesno:"correct,incorrect" }}
-               </strong>
-         </p>
+       {% block description %} {{ quiz.description }} {% endblock %}
 
-       </div>
+       {% if previous.answers %}
 
-       {% include 'correct_answer.html' %}
+  <div class="course_sub_title">
+    {% trans "The previous question" %}:
+  </div>
 
-       <p><strong>{% trans "Explanation" %}:</strong></p>
-       <div class="well " style="background-color: #fcf8e3;">
-         <p>{{ previous.previous_question.explanation }}</p>
-       </div>
+  <div class="course_content">
+    <p class="lead">{{ previous.previous_question }}</p>
+    {% include 'quiz/correct_answer.html' %}
+  </div>
 
-       <hr>
+  <p>Votre réponse est
+  <strong>
+    {{ previous.previous_outcome|yesno:"correct,incorrect" }}
+  </strong>
+  </p>
 
-{% endif %}
+  <div class="course_sub_title">{% trans "Explanation" %}:</div>
+  <div class="well course_content">
+    <p>{{ previous.previous_question.explanation }}</p>
+  </div>
 
-<br />
+       {% endif %}
 
-{% if question %}
+       <br />
 
-{% if progress %}
-<div style="float: right;">
-{% trans "Question" %} {{ progress.0|add:1 }} {% trans "of" %} {{ progress.1 }}
-</div>
-{% endif %}
+       {% if question %}
 
-{% if question.category %}<p>
-  <small class="muted">{% trans "Question category" %}:</small>
-  <strong>{{ question.category }}</strong>
-</p>{% endif %}
+               {% if progress %}
+               <div class="course_sub_title">
+                       {% trans "Question" %} {{ progress.0|add:1 }} {% trans "of" %} {{ progress.1 }}
+               </div>
+               {% endif %}
 
-<p class="lead">{{ question.content }}</p>
+               {% if question.category %}<p>
+                 <small class="muted">{% trans "Question category" %}:</small>
+                 <strong>{{ question.category }}</strong>
+               </p>{% endif %}
 
-{% if question.figure %}
-    <img src="{{ question.figure.url }}" alt="{{ question.content }}" />
-{% endif %}
+               <div class="course_content">
 
-<form action="" method="POST">{% csrf_token %}
-  <input type=hidden name="question_id" value="{{ question.id }}">
+                       <p class="lead">{{ question.content }}</p>
 
-  <ul class="list-group">
+                       {% if question.figure %}
+                           <img src="{{ question.figure.url }}" alt="{{ question.content }}" />
+                       {% endif %}
 
-       {% for answer in form.answers %}
-         <li class="list-group-item">
-           {{ answer }}
-         </li>
-       {% endfor %}
+                       <form action="" method="POST">{% csrf_token %}
+                         <input type=hidden name="question_id" value="{{ question.id }}">
 
-  </ul>
-  <input type="submit" value={% trans "Check" %} class="btn btn-large btn-block btn-warning" >
-</form>
+                         <ul class="list-group">
+                               {% for answer in form.answers %}
+                                 <li class="list-group-item">
+                                   {{ answer }}
+                                 </li>
+                               {% endfor %}
+                         </ul>
 
-{% endif %}
+                         <input style="padding: 5px; margin: 5px" type="submit" value={% trans "Check" %} class="btn btn-large btn-block btn-warning" >
 
-<hr>
+                       </form>
 
+               </div>
+
+       {% endif %}
+
+
+</div>
 
 {% endblock %}
index 6a66bc337ca72c58ffca7f5c141dd9256775cf45..6032e90b8fdb121be921283fb6443b4f8c57d2c1 100644 (file)
@@ -1,10 +1,10 @@
-{% extends 'teleforma/base.html' %}
+{% extends 'teleforma/course_detail.html' %}
 {% load i18n %}
 {% block title %}
 {{ quiz.title }}
 {% endblock %}
 
-{% block content %}
+{% block course %}
 <!--<h2>{{ quiz.title }}</h2>-->
 <!--<h3>{% trans "Category" %}: {{ quiz.category }}</h3>-->
 {% if quiz.single_attempt %}
index 0b16375917fc3f466eb07ab4d2dc75d8ce979314..029d9da9745831b76b4ec278d7919443d8820f1b 100644 (file)
@@ -27,7 +27,7 @@
                          <td>{{ quiz.exam_paper }}</td>
                          <td>{{ quiz.single_attempt }}</td>
                          <td>
-                               <a href="{% url 'quiz_start_page' slug=quiz.url %}">
+                               <a href="{% url 'teleforma-quiz' period_id=period.id pk=course.pk quiz_name=quiz.name %}">
                                  {% trans "View details" %}
                                </a>
                        </tr>
index d5bb4d279be8eccc856021af8e2aa6da2611ef06..e1da6b5c0e2c20101f69748a0296ad1e1be02aed 100644 (file)
@@ -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 %}
+
+<div class="course_media">
+
+<div class="course_main_title">
+    {{ course.title }}
+  </div>
+
+<div class="course_title">
+   Quiz - {{ quiz.title }}
+</div>
 
-{% block content %}
+{% block description %} {{ quiz.description }} {% endblock %}
 
   {% if previous.answers %}
 
-  <p class="muted"><small>{% trans "The previous question" %}:</small></p>
-  <p>{{ previous.previous_question }}</p>
+  <div class="course_sub_title">
+    {% trans "The previous question" %}:
+  </div>
+
+  <div class="course_content">
+    <p class="lead">{{ previous.previous_question }}</p>
+    {% include 'quiz/correct_answer.html' %}
+  </div>
+
   <p>Votre réponse est
-       <strong>
-         {{ previous.previous_outcome|yesno:"correct,incorrect" }}
-       </strong>
+  <strong>
+    {{ previous.previous_outcome|yesno:"correct,incorrect" }}
+  </strong>
   </p>
-  {% include 'correct_answer.html' %}
-  <p><strong>{% trans "Explanation" %}:</strong></p>
-  <div class="well " style="background-color: #fcf8e3;">
+
+  <div class="course_sub_title">{% trans "Explanation" %}:</div>
+  <div class="well course_content">
     <p>{{ previous.previous_question.explanation }}</p>
   </div>
-  <hr>
+
 
   {% endif %}
 
   {% if max_score %}
 
   <div>
-       <h2>{% trans "Exam results" %}</h2>
-       <p>
-         <small class="muted">{% trans "Exam title" %}: </small>
-         <strong>{{ quiz.title }}</strong></p>
+       <div class="course_title">{% trans "Exam results" %}</div>
 
        <p class="lead">
-         {% trans "You answered" %} {{ score }} {% trans "questions correctly out of" %} {{ max_score }}, {% trans "giving you" %} {{ percent }} {% trans "percent correct" %}
+    {% if percent >= quiz.pass_mark %}
+      &#127881; &#128175;
+    {% endif %}
+
+         {% trans "You answered" %} <b>{{ score }}</b> {% trans "questions correctly out of" %} <b>{{ max_score }}</b>, {% trans "giving you" %} <b>{{ percent }}</b> {% trans "percent correct" %}
        </p>
 
        {% if quiz.pass_mark %}
-       <hr>
-        <p class="lead">{{ sitting.result_message }}</p>
-       <hr>
-
+    <p class="lead">{{ sitting.result_message }}</p>
        {% endif %}
       
   </div>
 
-
   {% endif %}
 
-
-  <hr>
-
   {% if possible %}
 
   <p class="lead">
        {% trans "Your session score is" %} {{ session }} {% trans "out of a possible" %} {{ possible }}
   </p>
 
-  <hr>
-
   {% endif %}
 
   {% if questions %}
 
     {% for question in questions %}
 
-      <p class="lead">
-               {{ question.content }}
+    <p class="lead">
+                 {{ question.content }}
          </p>
 
          {% correct_answer_for_all question %}
                <p>{{ question.explanation }}</p>
          </div>
 
-         <hr>
 
   {% endfor %}
 
   {% endif %}
 
+  <br><br>
+
+  <div id="flex-container">
+    <div class="flex-item" id="flex">
+      <a href="{% url 'teleforma-desk-period-course' period.id course.id %}" class="component_icon button icon_previous">Retour à la matière</a>
+    </div>
+    <div class="flex-item" id="flex">
+      <a href="{% url 'teleforma-quiz' period_id=period.id course_id=course.pk quiz_name=quiz.url %}" class="component_icon button icon_loading">Refaire le quiz</a>
+    </div>
+    <div class="flex-item" id="flex">
+      <a class="component_icon button icon_next">Quiz suivant</a>
+    </div>
+  </div>
+
+</div>
 
 {% endblock %}
index bc2007dfb3810f4e6df526be97f12fbce511e6e9..cbf69dc57f5d6e252cac38cd3de7ac174f7d75a1 100644 (file)
       {{ course.description }}{% endif %}
     </div>
 
-    {% if type.name == 'Quiz' %}
-    <div class="course_content">
-      {% if course.quiz.all %}
-      <table class="listing" width="100%">
-        <tbody>
-          {% for quiz in course.quiz.all %}
-          <td class="border-top"><a href="{% url 'quiz_start_page' slug=quiz.url %}">{{quiz.title}}</a></td>
-          <td class="border-top">{{quiz.description}}</td>
-          {% endfor %}
-        </tbody>
-      </table>
-      {% else %}
-      <p>Aucun quiz</p>
-      {% endif %}
-    </div>
-
-    {% 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 %}
 
   </div>
   {% endfor %}
 
+  <div class="course">
+    {% if course.quizzes.all %}
+    <div class="course_title" id="webclasses">
+      Quizes
+    </div>
+    <div class="course_content">
+      <table class="listing" width="100%">
+        <tbody>
+          {% for quiz in course.quizzes.all %}
+          <tr>
+            <td class="border-top"><a href="{% url 'teleforma-quiz' period_id=period.id course_id=course.id quiz_name=quiz.url %}">{{ quiz.title }}</a></td>
+            <td class="border-top">{{ quiz.description }}</td>
+            <td class="border-top" width="5%" align="center">
+              {% if quiz|quiz_validated:user %}
+                <img src="{{ STATIC_URL }}teleforma/images/ok.png" style="vertical-align:middle" alt="" title="{% trans "Validated" %}" />
+                {% else %}
+                    <img src="{{ STATIC_URL }}teleforma/images/delete.png" style="vertical-align:middle" alt="" title="{% trans ' not viewed yet' %}" />
+              {% endif %}
+            </td>
+          </tr>
+          {% endfor %}
+        </tbody>
+      </table>
+    </div>
+    {% else %}
+    <p>Aucun quiz</p>
+    {% endif %}
+  </div>
+
+
   <div class="course">
     <div class="course_title" id="webclasses">Webclasses{% if course.description %} -
       {{ course.description }}{% endif %}
index 216528cab85cc8b77627d77e92c6620c6ea97173..52984cc2ce3562597ec38308017bbea5d55618c7 100644 (file)
@@ -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 ''
index 3d97a65fe2c0b54a1fb7ced24167f50ab3e09eb0..da2ab662dc6de75d2f67db0ddd353484dea3bdde 100644 (file)
@@ -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<period_id>.*)/courses/(?P<course_id>.*)/quiz/(?P<quiz_name>[\w-]+)/$', QuizQuestionView.as_view(), name="teleforma-quiz"),
 ]
index 89771f46ecef9220f673a1af364cf1cb2b1caede..32bd922eb814cd9b0295a77bbd21aa6f18e1ab53 100644 (file)
@@ -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)