Skip to content

Commit

Permalink
[IMP] website_slides: allow multiple correct answers
Browse files Browse the repository at this point in the history
Purpose
=======
Currently the user interface let you specify several correct answers
per quiz question. But when you save the course, you get a notification
error. As those are simple quizzes, it might be interesting to simply
allow multiple answers.

Specifications
==============
Allow questions with multiple correct answers.
Back-end views: improve feedback when user forget to set at least one correct
answer and one incorrect answer per question.

Task-3366803

closes odoo#127065

Signed-off-by: Stéphane Debauche (std) <[email protected]>
  • Loading branch information
lm4649 committed Aug 23, 2023
1 parent 122ff3c commit bb4755f
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 6 deletions.
24 changes: 18 additions & 6 deletions addons/website_slides/models/slide_question.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,23 @@ class SlideQuestion(models.Model):
question = fields.Char("Question Name", required=True, translate=True)
slide_id = fields.Many2one('slide.slide', string="Content", required=True, ondelete='cascade')
answer_ids = fields.One2many('slide.answer', 'question_id', string="Answer", copy=True)
answers_validation_error = fields.Char("Error on Answers", compute='_compute_answers_validation_error')
# statistics
attempts_count = fields.Integer(compute='_compute_statistics', groups='website_slides.group_website_slides_officer')
attempts_avg = fields.Float(compute="_compute_statistics", digits=(6, 2), groups='website_slides.group_website_slides_officer')
done_count = fields.Integer(compute="_compute_statistics", groups='website_slides.group_website_slides_officer')

@api.constrains('answer_ids')
def _check_answers_integrity(self):
for question in self:
if len(question.answer_ids.filtered(lambda answer: answer.is_correct)) != 1:
raise ValidationError(_('Question "%s" must have 1 correct answer', question.question))
if len(question.answer_ids) < 2:
raise ValidationError(_('Question "%s" must have 1 correct answer and at least 1 incorrect answer', question.question))
questions_to_fix = [
f'- {question.slide_id.name}: {question.question}'
for question in self
if question.answers_validation_error
]
if questions_to_fix:
raise ValidationError(_(
'All questions must have at least one correct answer and one incorrect answer: \n%s\n',
'\n'.join(questions_to_fix)))

@api.depends('slide_id')
def _compute_statistics(self):
Expand All @@ -45,12 +50,19 @@ def _compute_statistics(self):
question.attempts_avg = stats.get('attempts_count', 0) / stats.get('attempts_unique', 1) if stats else 0
question.done_count = stats.get('done_count', 0) if stats else 0

@api.depends('answer_ids', 'answer_ids.is_correct')
def _compute_answers_validation_error(self):
for question in self:
correct = question.answer_ids.filtered('is_correct')
question.answers_validation_error = _(
'This question must have at least one correct answer and one incorrect answer.'
) if not correct or correct == question.answer_ids else ''

class SlideAnswer(models.Model):
_name = "slide.answer"
_rec_name = "text_value"
_description = "Slide Question's Answer"
_order = 'question_id, sequence'
_order = 'question_id, sequence, id'

sequence = fields.Integer("Sequence")
question_id = fields.Many2one('slide.question', string="Question", required=True, ondelete='cascade')
Expand Down
1 change: 1 addition & 0 deletions addons/website_slides/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from . import test_gamification_karma
from . import test_security
from . import test_slide_channel
from . import test_slide_question
from . import test_slide_resource
from . import test_slide_slide
from . import test_statistics
Expand Down
38 changes: 38 additions & 0 deletions addons/website_slides/tests/test_slide_question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command

from odoo.addons.website_slides.tests import common as slides_common
from odoo.tests.common import users

class TestSlideQuestionManagement(slides_common.SlidesCase):

@users('user_officer')
def test_compute_answers_validation_error(self):
channel = self.env['slide.channel'].create({
'name': 'Test compute answers channel',
'slide_ids': [Command.create({
'name': "Test compute answers validation error slide",
'slide_category': 'quiz',
'question_ids': [Command.create({
'question': 'Will test compute answers validation error pass?',
'answer_ids': [
Command.create({
'text_value': 'An incorrect answer',
}),
Command.create({
'is_correct': True,
'text_value': 'A correct answer',
})
]
})]
})]
})

question = channel.slide_ids[0].question_ids[0]
self.assertFalse(question.answers_validation_error)

for val in (False, True):
question.answer_ids[0].is_correct = val
question.answer_ids[1].is_correct = val
self.assertTrue(question.answers_validation_error)
6 changes: 6 additions & 0 deletions addons/website_slides/views/slide_question_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
<field name="model">slide.question</field>
<field name="arch" type="xml">
<form string="Quiz">
<div invisible="answers_validation_error == ''">
<div class="alert alert-info my-1 mx-2" role="alert" aria-label="Validation error">
<i class="fa fa-info-circle" aria-hidden="true"/>
<field name="answers_validation_error" class="ms-2" readonly="1"/>
</div>
</div>
<sheet>
<label for="question" string="Question Name"/>
<h1>
Expand Down

0 comments on commit bb4755f

Please sign in to comment.