-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Quiz sondage #6486
base: dev
Are you sure you want to change the base?
Quiz sondage #6486
Changes from 1 commit
0e4a448
fd130d5
37c472f
d68cce6
3b570b2
9fd81bb
2064551
cd7fc95
2d5471b
109edc7
15772e7
b2a0561
15c9c8e
db80c8d
cb5eeca
a2588ac
496cc30
5bf27a0
458f1c5
f76544b
7961f52
f7306b5
83a14cd
e721ba2
b858112
26d76be
6aa48a0
62b07c6
0052db6
9093ffa
0a638da
3ce3f5d
4d0e68d
0da5ae2
fbab71a
266f492
c9793a5
6816722
e6415af
f4f2353
baeae57
4bc5404
0cb1154
bce2090
77fc952
a32d1f1
034dc5e
02648d8
44f8626
9d9c10c
78f8174
c50d72b
4e369a1
50bd56c
60edb12
d265bac
eee0046
a07fb12
e205615
32a0fef
51d951e
19a2875
6234832
eed909a
0ac7e4d
3512eb3
33243c0
6ed7cff
bf95026
729bdb6
314caa1
4bbec24
91e89b6
e9c39bf
b2c6328
76ea273
e1003f2
53509b4
e71fb82
8348e85
079d5e7
59dcda2
23ca81b
275dc4c
40d2967
317b47f
d9d29b8
3a0fe77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,151 @@ | ||
{% load i18n %} | ||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/katex.min.css"> | ||
<script src="https://unpkg.com/[email protected]/dist/katex.min.js"></script> | ||
{% load emarkdown%} | ||
|
||
|
||
<div id="{{ tab_name }}" class="tabcontent"> | ||
|
||
|
||
{% for quizz_name, questions in quizz.items %} | ||
<header> | ||
<h3>{{ quizz_name }}</h3> | ||
<button class="init-stats-button" data-quizz="{{ quizz_name }}">Initialize Stats</button> | ||
</header> | ||
{% for question, stats in questions.items %} | ||
<article class="stat-graph"> | ||
<h4>{% trans graph_title %} <span id="formule">{{ question }}</span></h4> | ||
<ul class="quizz-answers"> | ||
{% for answer_name, answer_stats in stats.responses.items %} | ||
<li class="{% if answer_stats.good %}quizz-forget{% endif %} quizz-stats"> | ||
<span id="formule">{{ answer_name }}</span> | ||
<span> | ||
<progress aria-label="{% trans 'Résultat pour' %} {{ answer_name }}" | ||
max="{{ stats.total }}" value="{{ answer_stats.nb }}"/> | ||
</span> | ||
<span class="over-progress">{{answer_stats.nb}} / {{ stats.total }}</span> | ||
</li> | ||
{% endfor %} | ||
</ul> | ||
</article> | ||
{% endfor %} | ||
{% endfor %} | ||
<header> | ||
<h3 class="quizz_name">{{ quizz_name }}</h3> | ||
<button class="delete-quizz-button" data-quizz="{{ quizz_name }}"><i class="fas fa-trash-alt"></i></button> | ||
</header> | ||
{% for question, stats in questions.items %} | ||
<article class="stat-graph"> | ||
<h4>{% trans graph_title %} <span>{{ question|emarkdown }}</span></h4> | ||
<button class="delete-question-button" data-quizz="{{ quizz_name }}" data-question="{{question}}"><i | ||
class="fas fa-trash-alt"></i></button> | ||
<ul class="quizz-answers"> | ||
{% for answer_name, answer_stats in stats.responses.items %} | ||
<li class="{% if answer_stats.good %} quizz-good{% endif %} quizz-stats"> | ||
<span>{{ answer_name|emarkdown }}</span> | ||
<span> | ||
<progress aria-label="{% trans 'Résultat pour' %} {{ answer_name }}" max="{{ stats.total }}" | ||
value="{{ answer_stats.nb }}" /> | ||
</span> | ||
<span class="over-progress">{{answer_stats.nb}} / {{ stats.total }}</span> | ||
</li> | ||
{% endfor %} | ||
</ul> | ||
</article> | ||
{% endfor %} | ||
{% endfor %} | ||
</div> | ||
<script> | ||
|
||
var elements = document.querySelectorAll("#formule"); | ||
var regex = /[^\w\s]/g; // Expression régulière pour les caractères spéciaux | ||
|
||
for (var i = 0; i < elements.length; i++) { | ||
if (regex.test(elements[i].textContent)) { // Si l'élément contient des caractères spéciaux | ||
katex.render(elements[i].textContent, elements[i]); | ||
} | ||
else { | ||
elements[i].innerText = elements[i].textContent; // Remplacer la balise span par un texte simple | ||
function deleteQuizz(quizzName, question) { | ||
const csrfmiddlewaretoken = document.querySelector('input[name=\'csrfmiddlewaretoken\']').value | ||
const xhttp = new XMLHttpRequest() | ||
const url = 'http://0.0.0.0:8000/contenus/delete_quizz/' | ||
const data = question ? JSON.stringify({ 'quizzName': quizzName, 'question': question }) : JSON.stringify({ 'quizzName': quizzName }) | ||
xhttp.open('POST', url) | ||
xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest') | ||
xhttp.setRequestHeader('Content-Type', 'application/json') | ||
xhttp.setRequestHeader('X-CSRFToken', csrfmiddlewaretoken) | ||
xhttp.onload = function () { | ||
location.reload() | ||
} | ||
xhttp.send(data) | ||
} | ||
|
||
// Add an event listener to the quizz delete button | ||
var deleteButtons = document.querySelectorAll(".delete-quizz-button"); | ||
for (var i = 0; i < deleteButtons.length; i++) { | ||
deleteButtons[i].addEventListener('click', function () { | ||
var quizzName = this.getAttribute('data-quizz'); | ||
deleteQuizz(quizzName, ''); | ||
}); | ||
} | ||
|
||
|
||
// Add an event listener to the question delete button | ||
var deleteButtons = document.querySelectorAll(".delete-question-button"); | ||
for (var i = 0; i < deleteButtons.length; i++) { | ||
deleteButtons[i].addEventListener('click', function () { | ||
var quizzName = this.getAttribute('data-quizz'); | ||
var question = this.getAttribute('data-question'); | ||
deleteQuizz(quizzName, question); | ||
}); | ||
} | ||
|
||
|
||
// Replace the quizz name by the stats part of the quizz name | ||
var quizzNames = document.querySelectorAll(".quizz_name"); | ||
for (var i = 0; i < quizzNames.length; i++) { | ||
var quizzName = quizzNames[i].innerText; | ||
quizzName = quizzName.replace("#", ""); | ||
var parts = quizzName.split("/"); | ||
var statsPart = parts[parts.length - 2] + "/" + parts[parts.length - 1]; | ||
quizzNames[i].innerText = statsPart; | ||
} | ||
|
||
|
||
|
||
</script> | ||
|
||
<style> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tout ce qui suit devrait aller quelque part dans |
||
.quizz-chart { | ||
margin-top: 10px; | ||
} | ||
|
||
|
||
header { | ||
border: 1px solid #ddd; | ||
} | ||
|
||
|
||
/* Style for the header of each quiz */ | ||
.tabcontent header { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
margin-bottom: 10px; | ||
} | ||
|
||
.tabcontent h3 { | ||
font-size: 24px; | ||
margin: 0; | ||
} | ||
|
||
/* Style for the initialize stats button */ | ||
.delete-quizz-button, | ||
.delete-question-button { | ||
background-color: #e44833; | ||
border: none; | ||
color: white; | ||
padding: 10px; | ||
text-align: center; | ||
text-decoration: none; | ||
display: inline-block; | ||
font-size: 16px; | ||
margin-left: 10px; | ||
cursor: pointer; | ||
} | ||
|
||
.delete-stats-button:hover { | ||
background-color: #3e8e41; | ||
} | ||
|
||
/* Style for each question and its stats */ | ||
.stat-graph { | ||
margin-bottom: 20px; | ||
border: 1px solid #ddd; | ||
padding: 10px; | ||
} | ||
|
||
h4 span { | ||
display: inline-block; | ||
} | ||
|
||
li.quizz-stats { | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
width: 100%; | ||
} | ||
|
||
li.quizz-good { | ||
background-color: #a6f566; | ||
border-color: #155724; | ||
color: #155724; | ||
} | ||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,29 @@ | ||
import itertools | ||
import json | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On importe |
||
import uuid | ||
from collections import OrderedDict, Counter | ||
import logging | ||
import urllib.parse | ||
from datetime import timedelta, datetime, date | ||
from json import loads, dumps | ||
|
||
from django.shortcuts import get_object_or_404, redirect | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Essai de garder les imports liés à Django ensemble, donc déplace cette ligne plus bas. |
||
from django.views import View | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tu te sers de |
||
from django.db.models import Subquery | ||
import requests | ||
from django.conf import settings | ||
from django.contrib import messages | ||
from django.core.exceptions import PermissionDenied | ||
from django.db.models import Count | ||
from django.forms.utils import ErrorDict | ||
from django.http import StreamingHttpResponse | ||
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse, StreamingHttpResponse | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tu importes |
||
from django.utils.translation import gettext_lazy as _ | ||
from django.views.generic import FormView | ||
from django.views.generic import FormView, DeleteView | ||
from zds.member.decorator import LoggedWithReadWriteHability | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idem, cet import n'est pas utilisé. |
||
|
||
from zds.tutorialv2.forms import ContentCompareStatsURLForm, QuizzStatsForm | ||
from zds.tutorialv2.mixins import ( | ||
SingleContentDetailViewMixin, | ||
SingleContentViewMixin, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idem, pas utilisé. |
||
SingleOnlineContentDetailViewMixin, | ||
SingleOnlineContentFormViewMixin, | ||
) | ||
|
@@ -356,11 +361,7 @@ def build_quizz_stats(self, end_date, start_date): | |
.all() | ||
): | ||
full_answers_total[available_answer.label] = {"good": available_answer.is_good, "nb": 0} | ||
name = ( | ||
available_answer.related_question.url.split("/")[-2] | ||
+ "/" | ||
+ available_answer.related_question.url.split("/")[-1].split("#")[-1] | ||
) | ||
name = available_answer.related_question.url | ||
question = available_answer.related_question.question | ||
for r in total_per_label: | ||
if ( | ||
|
@@ -371,8 +372,72 @@ def build_quizz_stats(self, end_date, start_date): | |
if name not in quizz_stats: | ||
quizz_stats[name] = OrderedDict() | ||
quizz_stats[name][question] = {"total": total_per_question[base_question], "responses": full_answers_total} | ||
return quizz_stats | ||
sorted_quizz_stats = {} | ||
for name in sorted(quizz_stats.keys()): | ||
sorted_quizz_stats[name] = quizz_stats[name] | ||
return sorted_quizz_stats | ||
|
||
|
||
class QuizzContentStatistics(ContentStatisticsView): | ||
template_name = "tutorialv2/stats/quizz_stats.html" | ||
|
||
|
||
class DeleteQuizz(View): | ||
def get_start_and_end_dates(self): | ||
end_date = self.request.GET.get("end_date", None) | ||
try: | ||
end_date = datetime.strptime(end_date, "%Y-%m-%d").date() | ||
except TypeError: | ||
end_date = date.today() | ||
except ValueError: | ||
end_date = date.today() | ||
messages.error(self.request, _("La date de fin fournie est invalide.")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Euh, on veut afficher le message pour les deux exceptions Et idem pour la date de début juste en-dessous. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. en python le double catch c'est |
||
|
||
start_date = self.request.GET.get("start_date", None) | ||
try: | ||
start_date = datetime.strptime(start_date, "%Y-%m-%d").date() | ||
except TypeError: | ||
start_date = end_date - timedelta(days=7) | ||
except ValueError: | ||
start_date = end_date - timedelta(days=7) | ||
messages.error(self.request, _("La date de début fournie est invalide.")) | ||
|
||
if start_date > end_date: | ||
end_date, start_date = start_date, end_date | ||
|
||
return start_date, end_date | ||
|
||
def post(self, request): | ||
|
||
start_date, end_date = self.get_start_and_end_dates() | ||
|
||
data = json.loads(request.body) | ||
|
||
# Extract the quizzName from the data | ||
quizz_name = data.get("quizzName") | ||
question = data.get("question") | ||
|
||
if question: | ||
related_question_ids = QuizzQuestion.objects.filter(url=quizz_name, question=question).values_list( | ||
"id", flat=True | ||
) | ||
else: | ||
related_question_ids = QuizzQuestion.objects.filter(url=quizz_name).values_list("id", flat=True) | ||
|
||
try: | ||
QuizzUserAnswer.objects.filter( | ||
related_question_id__in=Subquery(related_question_ids), date_answer__range=(start_date, end_date) | ||
).delete() | ||
except Exception as e: | ||
return HttpResponseBadRequest(f"An error occurred while deleting the quiz: {str(e)}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Il ne devrait pas y avoir d'erreur à ce niveau-là. S'il y a des erreurs, c'est qu'il faut les éviter en amont. |
||
|
||
# Delete all QuizzUserAnswer objects | ||
QuizzUserAnswer.objects.all().delete() | ||
|
||
# Delete all QuizzAvailableAnswer objects | ||
QuizzAvailableAnswer.objects.all().delete() | ||
|
||
# Delete all QuizzQuestion objects | ||
QuizzQuestion.objects.all().delete() | ||
|
||
return StreamingHttpResponse(dumps({"status": "ok"})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non, on ne fait pas comme ça, notamment l'URL codée en dur, ça ne va pas (on fait comment quand on passera en prod ?). Inspire-toi ce qui est fait pour la liste des contenus à mettre en avant : http://0.0.0.0:8080/mise-en-avant/unes/requetes/ (connecté en tant qu'admin). Ça se passe là et là ou encore ici.