diff --git a/zds/notification/managers.py b/zds/notification/managers.py index 91a5a24413..4d23928c94 100644 --- a/zds/notification/managers.py +++ b/zds/notification/managers.py @@ -74,10 +74,7 @@ def get_or_create_active(self, user, content_object): """ content_type = ContentType.objects.get_for_model(content_object) # the get_or_create boolean flag is kind of useless for us - subscription = self.get_or_create( - object_id=content_object.pk, - content_type__pk=content_type.pk, - user=user)[0] + subscription = self.get_or_create(object_id=content_object.pk, content_type__pk=content_type.pk, user=user)[0] if not subscription.is_active: subscription.activate() return subscription diff --git a/zds/tutorialv2/migrations/0031_quizzstat.py b/zds/tutorialv2/migrations/0031_quizzstat.py index 606a26c593..23ed354d75 100644 --- a/zds/tutorialv2/migrations/0031_quizzstat.py +++ b/zds/tutorialv2/migrations/0031_quizzstat.py @@ -7,19 +7,28 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0030_contentsuggestion'), + ("tutorialv2", "0030_contentsuggestion"), ] operations = [ migrations.CreateModel( - name='QuizzStat', + name="QuizzStat", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.TextField(verbose_name='url')), - ('question', models.TextField(verbose_name='question')), - ('answer', models.TextField(verbose_name='anwser')), - ('date_answer', models.DateField(auto_now=True, verbose_name='Date of answer')), - ('related_content', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.PublishableContent', verbose_name='Tutoriel lié')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("url", models.TextField(verbose_name="url")), + ("question", models.TextField(verbose_name="question")), + ("answer", models.TextField(verbose_name="anwser")), + ("date_answer", models.DateField(auto_now=True, verbose_name="Date of answer")), + ( + "related_content", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="tutorialv2.PublishableContent", + verbose_name="Tutoriel lié", + ), + ), ], ), ] diff --git a/zds/tutorialv2/migrations/0032_auto_20200927_1015.py b/zds/tutorialv2/migrations/0032_auto_20200927_1015.py index 14cc1a37a3..df5e093c6b 100644 --- a/zds/tutorialv2/migrations/0032_auto_20200927_1015.py +++ b/zds/tutorialv2/migrations/0032_auto_20200927_1015.py @@ -7,42 +7,60 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0031_quizzstat'), + ("tutorialv2", "0031_quizzstat"), ] operations = [ migrations.CreateModel( - name='QuizzAvailableAnswer', + name="QuizzAvailableAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer_label', models.TextField(verbose_name='Intitulé de la réponse')), - ('is_good', models.BooleanField(default=False, verbose_name='Est une réponse attendue')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("answer_label", models.TextField(verbose_name="Intitulé de la réponse")), + ("is_good", models.BooleanField(default=False, verbose_name="Est une réponse attendue")), ], ), migrations.CreateModel( - name='QuizzQuestion', + name="QuizzQuestion", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('url', models.TextField(verbose_name='url')), - ('question', models.TextField(verbose_name='question')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("url", models.TextField(verbose_name="url")), + ("question", models.TextField(verbose_name="question")), ], ), migrations.CreateModel( - name='QuizzUserAnswer', + name="QuizzUserAnswer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer', models.TextField(verbose_name='anwser')), - ('date_answer', models.DateField(auto_now=True, verbose_name='Date of answer')), - ('related_content', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.PublishableContent', verbose_name='Tutoriel lié')), - ('related_question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.QuizzQuestion', verbose_name='Question liée')), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("answer", models.TextField(verbose_name="anwser")), + ("date_answer", models.DateField(auto_now=True, verbose_name="Date of answer")), + ( + "related_content", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="tutorialv2.PublishableContent", + verbose_name="Tutoriel lié", + ), + ), + ( + "related_question", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="tutorialv2.QuizzQuestion", + verbose_name="Question liée", + ), + ), ], ), migrations.DeleteModel( - name='QuizzStat', + name="QuizzStat", ), migrations.AddField( - model_name='quizzavailableanswer', - name='related_question', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.QuizzQuestion', verbose_name='Question liée'), + model_name="quizzavailableanswer", + name="related_question", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="tutorialv2.QuizzQuestion", verbose_name="Question liée" + ), ), ] diff --git a/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py b/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py index 8672c4500a..1796f4fd8b 100644 --- a/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py +++ b/zds/tutorialv2/migrations/0033_quizzuseranswer_full_answer_id.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0032_auto_20200927_1015'), + ("tutorialv2", "0032_auto_20200927_1015"), ] operations = [ migrations.AddField( - model_name='quizzuseranswer', - name='full_answer_id', - field=models.CharField(default='id', max_length=64, verbose_name='Indentifiant de la réponse utilisateur'), + model_name="quizzuseranswer", + name="full_answer_id", + field=models.CharField(default="id", max_length=64, verbose_name="Indentifiant de la réponse utilisateur"), ), ] diff --git a/zds/tutorialv2/migrations/0034_auto_20200927_1105.py b/zds/tutorialv2/migrations/0034_auto_20200927_1105.py index 3214a25163..0c31ee2e15 100644 --- a/zds/tutorialv2/migrations/0034_auto_20200927_1105.py +++ b/zds/tutorialv2/migrations/0034_auto_20200927_1105.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('tutorialv2', '0033_quizzuseranswer_full_answer_id'), + ("tutorialv2", "0033_quizzuseranswer_full_answer_id"), ] operations = [ migrations.RenameField( - model_name='quizzavailableanswer', - old_name='answer_label', - new_name='label', + model_name="quizzavailableanswer", + old_name="answer_label", + new_name="label", ), ] diff --git a/zds/tutorialv2/models/quizz.py b/zds/tutorialv2/models/quizz.py index 8c4d4645c7..3de3418a5a 100644 --- a/zds/tutorialv2/models/quizz.py +++ b/zds/tutorialv2/models/quizz.py @@ -4,25 +4,31 @@ class QuizzQuestion(models.Model): - url = models.TextField(name='url', verbose_name='url', null=False, blank=False) + url = models.TextField(name="url", verbose_name="url", null=False, blank=False) question = models.TextField(name="question", verbose_name="question", null=False, blank=False) class QuizzAvailableAnswer(models.Model): label = models.TextField(name="label", verbose_name="Intitulé de la réponse") good_answer = models.BooleanField(name="is_good", verbose_name="Est une réponse attendue", default=False) - related_question = models.ForeignKey(QuizzQuestion, name="related_question", verbose_name="Question liée", - on_delete=models.CASCADE) + related_question = models.ForeignKey( + QuizzQuestion, name="related_question", verbose_name="Question liée", on_delete=models.CASCADE + ) class QuizzUserAnswer(models.Model): - related_content = models.ForeignKey(PublishableContent, - verbose_name='Tutoriel lié', - blank=True, null=True, - on_delete=models.CASCADE) + related_content = models.ForeignKey( + PublishableContent, verbose_name="Tutoriel lié", blank=True, null=True, on_delete=models.CASCADE + ) answer = models.TextField(name="answer", verbose_name="anwser", null=False, blank=False) date_answer = models.DateField(name="date_answer", verbose_name="Date of answer", null=False, auto_now=True) - related_question = models.ForeignKey(QuizzQuestion, name="related_question", verbose_name="Question liée", - on_delete=models.CASCADE) - full_answer_id = models.CharField(name="full_answer_id", verbose_name="Indentifiant de la réponse utilisateur", - blank=False, max_length=64, default='id') + related_question = models.ForeignKey( + QuizzQuestion, name="related_question", verbose_name="Question liée", on_delete=models.CASCADE + ) + full_answer_id = models.CharField( + name="full_answer_id", + verbose_name="Indentifiant de la réponse utilisateur", + blank=False, + max_length=64, + default="id", + ) diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index c5de86f6cf..6389699a27 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -114,21 +114,23 @@ CreateExtract.as_view(), name="create-extract", ), - re_path(r'^nouveau-quizz/(?P\d+)/(?P.+)/(?P.+)/(?P.+)/$', - CreateExtract.as_view(quizz=True), - name='create-quizz'), + re_path( + r"^nouveau-quizz/(?P\d+)/(?P.+)/(?P.+)/(?P.+)/$", + CreateExtract.as_view(quizz=True), + name="create-quizz", + ), re_path( r"^nouvelle-section/(?P\d+)/(?P.+)/(?P.+)/$", CreateExtract.as_view(), name="create-extract", ), - re_path(r'^nouveau-quizz/(?P\d+)/(?P.+)/(?P.+)/$', - CreateExtract.as_view(quizz=True), - name='create-quizz'), + re_path( + r"^nouveau-quizz/(?P\d+)/(?P.+)/(?P.+)/$", + CreateExtract.as_view(quizz=True), + name="create-quizz", + ), re_path(r"^nouvelle-section/(?P\d+)/(?P.+)/$", CreateExtract.as_view(), name="create-extract"), - re_path(r'^nouveau-quizz/(?P\d+)/(?P.+)/$', - CreateExtract.as_view(quizz=True), - name='create-quizz'), + re_path(r"^nouveau-quizz/(?P\d+)/(?P.+)/$", CreateExtract.as_view(quizz=True), name="create-quizz"), # edit: re_path( r"^editer-conteneur/(?P\d+)/(?P.+)/(?P.+)/" r"(?P.+)/$", @@ -201,6 +203,5 @@ re_path(r"^tags/$", TagsListView.as_view(), name="tags"), re_path(r"^$", RedirectView.as_view(pattern_name="publication:list", permanent=True), name="list"), # quizz - re_path(r"^reponses/(?P\d+)/(?P.+)/$", ContentQuizzStatistics.as_view(), - name="answer-quizz"), + re_path(r"^reponses/(?P\d+)/(?P.+)/$", ContentQuizzStatistics.as_view(), name="answer-quizz"), ] diff --git a/zds/tutorialv2/views/containers_extracts.py b/zds/tutorialv2/views/containers_extracts.py index ad988ebdf9..73c5c0a346 100644 --- a/zds/tutorialv2/views/containers_extracts.py +++ b/zds/tutorialv2/views/containers_extracts.py @@ -216,8 +216,7 @@ def form_valid(self, form): parent = search_container_or_404(self.versioned_object, self.kwargs) sha = parent.repo_add_extract( - form.cleaned_data["title"], form.cleaned_data["text"], form.cleaned_data["msg_commit"], - quizz=self.quizz + form.cleaned_data["title"], form.cleaned_data["text"], form.cleaned_data["msg_commit"], quizz=self.quizz ) # then save diff --git a/zds/tutorialv2/views/statistics.py b/zds/tutorialv2/views/statistics.py index 94bbfedf58..202aa378d0 100644 --- a/zds/tutorialv2/views/statistics.py +++ b/zds/tutorialv2/views/statistics.py @@ -29,28 +29,31 @@ def post(self, request, *args, **kwargs): super(ContentQuizzStatistics, self).post(request, *args, **kwargs) def form_valid(self, form): - url = form.cleaned_data['url'] - answers = {k: v for k, v in self.request.POST['result'].items()} + url = form.cleaned_data["url"] + answers = {k: v for k, v in self.request.POST["result"].items()} resp_id = str(uuid.uuid4()) for question, answers in answers.items(): db_question = QuizzQuestion.objects.filter(question=question, url=url).first() if not db_question: db_question = QuizzQuestion(question=question, url=url) db_question.save() - given_available_answers = self.request.POST['expected'][question] + given_available_answers = self.request.POST["expected"][question] answers_labels = list(given_available_answers.keys()) - known_labels = QuizzAvailableAnswer.objects.filter(related_question=db_question, - label__in=answers_labels).values_list('label', flat=True) + known_labels = QuizzAvailableAnswer.objects.filter( + related_question=db_question, label__in=answers_labels + ).values_list("label", flat=True) not_existing_answers = [l for l in answers_labels if l not in known_labels] QuizzAvailableAnswer.objects.exclude(label__in=answers_labels).filter(related_question=db_question).delete() for label in not_existing_answers: - db_answer = QuizzAvailableAnswer(related_question=db_question, label=label, - is_good=given_available_answers[label]) + db_answer = QuizzAvailableAnswer( + related_question=db_question, label=label, is_good=given_available_answers[label] + ) db_answer.save() - for answer in answers['labels']: - stat = QuizzUserAnswer(related_content=self.object, related_question=db_question, - full_answer_id=resp_id, answer=answer) + for answer in answers["labels"]: + stat = QuizzUserAnswer( + related_content=self.object, related_question=db_question, full_answer_id=resp_id, answer=answer + ) stat.save() self.success_url = self.object.get_absolute_url_online() return super(ContentQuizzStatistics, self).form_valid(form) @@ -369,48 +372,55 @@ def get_context_data(self, **kwargs): display_mode = self.get_display_mode(urls) all_stats = self.get_all_stats(urls, start_date, end_date, display_mode) quizz_stats = {} - base_questions = list(QuizzUserAnswer.objects - .filter(date_answer__range=(start_date, end_date), - related_content__pk=self.object.pk) - .values_list('related_question', flat=True)) - total_per_question = list(QuizzUserAnswer.objects.values('related_question__pk') - .filter(related_question__pk__in=base_questions, - date_answer__range=(start_date, end_date)) - .annotate(nb=Count('full_answer_id'))) - total_per_question = {a['related_question__pk']: a['nb'] for a in - total_per_question} - total_per_label = list(QuizzUserAnswer.objects.values( - 'related_question__pk', 'related_question__question', 'related_question__url', 'answer'). - filter(related_question__in=base_questions, - date_answer__range=(start_date, end_date)) - .annotate(nb=Count('answer'))) + base_questions = list( + QuizzUserAnswer.objects.filter( + date_answer__range=(start_date, end_date), related_content__pk=self.object.pk + ).values_list("related_question", flat=True) + ) + total_per_question = list( + QuizzUserAnswer.objects.values("related_question__pk") + .filter(related_question__pk__in=base_questions, date_answer__range=(start_date, end_date)) + .annotate(nb=Count("full_answer_id")) + ) + total_per_question = {a["related_question__pk"]: a["nb"] for a in total_per_question} + total_per_label = list( + QuizzUserAnswer.objects.values( + "related_question__pk", "related_question__question", "related_question__url", "answer" + ) + .filter(related_question__in=base_questions, date_answer__range=(start_date, end_date)) + .annotate(nb=Count("answer")) + ) for base_question in set(base_questions): full_answers_total = {} - url = '' - question = '' - for available_answer in QuizzAvailableAnswer.objects.filter( - related_question__pk=base_question).prefetch_related('related_question').all(): - full_answers_total[available_answer.label] = {'good': available_answer.is_good, 'nb': 0} + url = "" + question = "" + for available_answer in ( + QuizzAvailableAnswer.objects.filter(related_question__pk=base_question) + .prefetch_related("related_question") + .all() + ): + full_answers_total[available_answer.label] = {"good": available_answer.is_good, "nb": 0} url = available_answer.related_question.url question = available_answer.related_question.question for r in total_per_label: - if r['related_question__pk'] == base_question and \ - r['answer'].strip() == available_answer.label.strip(): - full_answers_total[available_answer.label]['nb'] = r['nb'] + if ( + r["related_question__pk"] == base_question + and r["answer"].strip() == available_answer.label.strip() + ): + full_answers_total[available_answer.label]["nb"] = r["nb"] if url not in quizz_stats: quizz_stats[url] = OrderedDict() - quizz_stats[url][question] = { - 'total': total_per_question[base_question], - 'responses': full_answers_total + quizz_stats[url][question] = {"total": total_per_question[base_question], "responses": full_answers_total} + context.update( + { + "display": display_mode, + "urls": urls, + "stats": all_stats[0], # Graph + "cumulative_stats_by_url": all_stats[1], # Table data + "referrers": all_stats[2], + "keywords": all_stats[3], + "quizz": quizz_stats, } - context.update({ - 'display': display_mode, - 'urls': urls, - 'stats': all_stats[0], # Graph - 'cumulative_stats_by_url': all_stats[1], # Table data - 'referrers': all_stats[2], - 'keywords': all_stats[3], - 'quizz': quizz_stats - }) + ) return context