[IMP] website_slides: allow multiple correct answers
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/odoo#127065 Signed-off-by: Stéphane Debauche (std) <std@odoo.com>
This commit is contained in:
@@ -15,6 +15,7 @@ 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')
|
||||
@@ -22,11 +23,15 @@ class SlideQuestion(models.Model):
|
||||
|
||||
@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):
|
||||
@@ -45,12 +50,19 @@ class SlideQuestion(models.Model):
|
||||
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')
|
||||
|
||||
@@ -7,6 +7,7 @@ from . import test_embed_detection
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user