From e42ee496f89a830c938a20389a9fc645dfbf96c8 Mon Sep 17 00:00:00 2001 From: Patrick Hoste Date: Mon, 2 Dec 2019 13:47:53 +0000 Subject: [PATCH] [IMP] website_slides: add comments on answers showed after answering a quiz PURPOSE : A quiz after a course content is a good way to check if the user gets the previous lesson. Despite, the user/student may not understand why his answer was wrong/correct. In this task we'll allow the teacher to add a comment next to the asnwer of a quiz. SPECIFICATION : The quiz creator will now be able to comment the answers to inform the frontend user why his answer is wrong/correct. He will be able to add the comments both in backend and frontend. Task ID : 2072566 PR : #41188 Signed-off-by: Thibault Delavallee (tde) --- addons/website_slides/controllers/main.py | 15 ++++-- .../website_slides/data/slide_slide_demo.xml | 17 ++++++ .../website_slides/models/slide_question.py | 1 + .../static/src/js/slides_course_quiz.js | 53 +++++++++++-------- .../js/slides_course_quiz_question_form.js | 46 +++++++++++++++- .../static/src/scss/website_slides.scss | 26 +++------ .../static/src/xml/slide_quiz.xml | 4 ++ .../static/src/xml/slide_quiz_create.xml | 36 +++++++++---- .../views/slide_question_views.xml | 1 + .../views/website_slides_templates_lesson.xml | 12 +++-- 10 files changed, 149 insertions(+), 62 deletions(-) diff --git a/addons/website_slides/controllers/main.py b/addons/website_slides/controllers/main.py index fbebb1613be..05d4b455b06 100644 --- a/addons/website_slides/controllers/main.py +++ b/addons/website_slides/controllers/main.py @@ -133,7 +133,8 @@ class WebsiteSlides(WebsiteProfile): 'answer_ids': [{ 'id': answer.id, 'text_value': answer.text_value, - 'is_correct': answer.is_correct if slide_completed or request.website.is_publisher() else None + 'is_correct': answer.is_correct if slide_completed or request.website.is_publisher() else None, + 'comment': answer.comment if request.website.is_publisher else None } for answer in question.sudo().answer_ids], } for question in slide.question_ids] } @@ -753,7 +754,8 @@ class WebsiteSlides(WebsiteProfile): 'answer_ids': [(0, 0, { 'sequence': answer['sequence'], 'text_value': answer['text_value'], - 'is_correct': answer['is_correct'] + 'is_correct': answer['is_correct'], + 'comment': answer['comment'] }) for answer in answer_ids] }) return request.env.ref('website_slides.lesson_content_quiz_question').render({ @@ -798,7 +800,6 @@ class WebsiteSlides(WebsiteProfile): return {'error': 'slide_quiz_incomplete'} user_bad_answers = user_answers.filtered(lambda answer: not answer.is_correct) - user_good_answers = user_answers - user_bad_answers self._set_viewed_slide(slide, quiz_attempts_inc=True) quiz_info = self._get_slide_quiz_partner_info(slide, quiz_done=True) @@ -815,8 +816,12 @@ class WebsiteSlides(WebsiteProfile): 'level_up': rank_progress['previous_rank']['lower_bound'] != rank_progress['new_rank']['lower_bound'] }) return { - 'goodAnswers': user_good_answers.ids, - 'badAnswers': user_bad_answers.ids, + 'answers': { + answer.question_id.id: { + 'is_correct': answer.is_correct, + 'comment': answer.comment + } for answer in user_answers + }, 'completed': slide.user_membership_id.sudo().completed, 'channel_completion': slide.channel_id.completion, 'quizKarmaWon': quiz_info['quiz_karma_won'], diff --git a/addons/website_slides/data/slide_slide_demo.xml b/addons/website_slides/data/slide_slide_demo.xml index 3d6ae6aeec0..d5925fe69f9 100644 --- a/addons/website_slides/data/slide_slide_demo.xml +++ b/addons/website_slides/data/slide_slide_demo.xml @@ -83,18 +83,21 @@ A fruit 1 + Correct ! A strawberry is a fruit because it's the product of a tree. A vegetable 2 + Incorrect ! A strawberry is not a vegetable. A table 3 + Incorrect ! A table is a piece of furniture. @@ -106,12 +109,14 @@ A shovel 1 + Correct ! A shovel is the perfect tool to dig a hole. A spoon 2 + Incorrect ! Good luck digging a hole with a spoon... @@ -270,12 +275,14 @@ Yes 1 + Correct ! No 2 + Incorrect ! @@ -287,18 +294,21 @@ Yes 1 + Correct ! Congratulations you have time to loose No 2 + Incorrect ! You really should read it. What was the question again ? 3 + Incorrect ! Seriously ? @@ -392,12 +402,14 @@ Yes 1 + Incorrect ! No 2 + Correct ! @@ -409,18 +421,21 @@ Yes 1 + Incorrect ! No 2 + Correct ! And also bananas 3 + Incorrect ! of course not ... @@ -666,12 +681,14 @@ Yes 1 + Correct ! You did it ! No 2 + Incorrect ! You better think twice... diff --git a/addons/website_slides/models/slide_question.py b/addons/website_slides/models/slide_question.py index a77d28bbcec..f14fce6f059 100644 --- a/addons/website_slides/models/slide_question.py +++ b/addons/website_slides/models/slide_question.py @@ -56,3 +56,4 @@ class SlideAnswer(models.Model): question_id = fields.Many2one('slide.question', string="Question", required=True, ondelete='cascade') text_value = fields.Char("Answer", required=True, translate=True) is_correct = fields.Boolean("Is correct answer") + comment = fields.Text("Comment", translate=True, help='This comment will be displayed to the user if he selects this answer') diff --git a/addons/website_slides/static/src/js/slides_course_quiz.js b/addons/website_slides/static/src/js/slides_course_quiz.js index ae0d6236512..1d3942986ca 100644 --- a/addons/website_slides/static/src/js/slides_course_quiz.js +++ b/addons/website_slides/static/src/js/slides_course_quiz.js @@ -88,8 +88,6 @@ odoo.define('website_slides.quiz', function (require) { start: function() { var self = this; return this._super.apply(this, arguments).then(function () { - self._renderAnswers(); - self._renderAnswersHighlighting(); self._renderValidationInfo(); self._bindSortable(); self._checkLocationHref(); @@ -207,8 +205,9 @@ odoo.define('website_slides.quiz', function (require) { * @private * Decorate the answers according to state */ - _renderAnswers: function () { + _disableAnswers: function () { var self = this; + this.$('.o_wslides_js_lesson_quiz_question').addClass('completed-disabled'); this.$('input[type=radio]').each(function () { $(this).prop('disabled', self.slide.readonly || self.slide.completed); }); @@ -217,29 +216,37 @@ odoo.define('website_slides.quiz', function (require) { /** * @private * Decorate the answer inputs according to the correction + * and adds the answer comment if any */ - _renderAnswersHighlighting: function () { + _renderAnswersHighlightingAndComments: function () { var self = this; - this.$('a.o_wslides_quiz_answer').each(function () { - var $answer = $(this); - var answerId = $answer.data('answerId'); - if (_.contains(self.quiz.goodAnswers, answerId)) { - $answer.removeClass('list-group-item-danger').addClass('list-group-item-success'); - $answer.find('i.fa').addClass('d-none'); - $answer.find('i.fa-check-circle').removeClass('d-none'); - } - else if (_.contains(self.quiz.badAnswers, answerId)) { - $answer.removeClass('list-group-item-success').addClass('list-group-item-danger'); - $answer.find('i.fa').addClass('d-none'); - $answer.find('i.fa-times-circle').removeClass('d-none'); - $answer.find('label input').prop('checked', false); - } - else { - if (!self.slide.completed) { + this.$('.o_wslides_js_lesson_quiz_question').each(function () { + var $question = $(this); + var questionId = $question.data('questionId'); + var isCorrect = self.quiz.answers[questionId].is_correct; + $question.find('a.o_wslides_quiz_answer ').each(function () { + var $answer = $(this); + if ($answer.find('input[type=radio]')[0].checked) { + if (isCorrect) { + $answer.removeClass('list-group-item-danger').addClass('list-group-item-success'); + $answer.find('i.fa').addClass('d-none'); + $answer.find('i.fa-check-circle').removeClass('d-none'); + } else { + $answer.removeClass('list-group-item-success').addClass('list-group-item-danger'); + $answer.find('i.fa').addClass('d-none'); + $answer.find('i.fa-times-circle').removeClass('d-none'); + $answer.find('label input').prop('checked', false); + } + } else { $answer.removeClass('list-group-item-danger list-group-item-success'); $answer.find('i.fa').addClass('d-none'); $answer.find('i.fa-circle').removeClass('d-none'); } + }); + var comment = self.quiz.answers[questionId].comment; + if (comment) { + $question.find('.o_wslides_quiz_answer_info').removeClass('d-none'); + $question.find('.o_wslides_quiz_answer_comment').text(comment); } }); }, @@ -273,6 +280,7 @@ odoo.define('website_slides.quiz', function (require) { } else { self.quiz = _.extend(self.quiz, data); if (data.completed) { + self._disableAnswers(); new SlideQuizFinishModal(self, { quiz: self.quiz, hasNext: self.slide.hasNext, @@ -282,7 +290,7 @@ odoo.define('website_slides.quiz', function (require) { self.trigger_up('slide_completed', {slide: self.slide, completion: data.channel_completion}); } self._hideEditOptions(); - self._renderAnswersHighlighting(); + self._renderAnswersHighlightingAndComments(); self._renderValidationInfo(); } }); @@ -301,7 +309,8 @@ odoo.define('website_slides.quiz', function (require) { answers.push({ 'id': $(this).data('answerId'), 'text_value': $(this).data('text'), - 'is_correct': $(this).data('isCorrect') + 'is_correct': $(this).data('isCorrect'), + 'comment': $(this).data('comment') }); }); return { diff --git a/addons/website_slides/static/src/js/slides_course_quiz_question_form.js b/addons/website_slides/static/src/js/slides_course_quiz_question_form.js index 04f8532eca9..cfa54ebd7dc 100644 --- a/addons/website_slides/static/src/js/slides_course_quiz_question_form.js +++ b/addons/website_slides/static/src/js/slides_course_quiz_question_form.js @@ -18,8 +18,11 @@ var QuestionFormWidget = publicWidget.Widget.extend({ events: { 'click .o_wslides_js_quiz_validate_question': '_validateQuestion', 'click .o_wslides_js_quiz_cancel_question': '_cancelValidation', + 'click .o_wslides_js_quiz_comment_answer': '_toggleAnswerLineComment', 'click .o_wslides_js_quiz_add_answer': '_addAnswerLine', 'click .o_wslides_js_quiz_remove_answer': '_removeAnswerLine', + 'click .o_wslides_js_quiz_remove_answer_comment': '_removeAnswerLineComment', + 'change .o_wslides_js_quiz_answer_comment > input[type=text]': '_onCommentChanged' }, /** @@ -51,6 +54,34 @@ var QuestionFormWidget = publicWidget.Widget.extend({ // Handlers //-------------------------------------------------------------------------- + /** + * + * @param commentInput + * @private + */ + _onCommentChanged: function (event) { + var input = event.currentTarget; + var commentIcon = $(input).closest('.o_wslides_js_quiz_answer').find('.o_wslides_js_quiz_comment_answer'); + if (input.value.trim() !== '') { + commentIcon.addClass('text-primary'); + commentIcon.removeClass('text-muted'); + } else { + commentIcon.addClass('text-muted'); + commentIcon.removeClass('text-primary'); + } + }, + + /** + * Toggle the input for commenting the answer line which will be + * seen by the frontend user when submitting the quiz. + * @param ev + * @private + */ + _toggleAnswerLineComment: function (ev) { + var commentLine = $(ev.currentTarget).closest('.o_wslides_js_quiz_answer').find('.o_wslides_js_quiz_answer_comment').toggleClass('d-none'); + commentLine.find('input[type=text]').focus(); + }, + /** * Adds a new answer line after the element the user clicked on * e.g. If there is 3 answer lines and the user click on the add @@ -74,6 +105,16 @@ var QuestionFormWidget = publicWidget.Widget.extend({ } }, + /** + * + * @param ev + * @private + */ + _removeAnswerLineComment: function (ev) { + var commentLine = $(ev.currentTarget).closest('.o_wslides_js_quiz_answer_comment').addClass('d-none'); + commentLine.find('input[type=text]').val('').change(); + }, + /** * Handler when user click on 'Save & New', 'Save & Close' * or 'Update' buttons. @@ -165,12 +206,13 @@ var QuestionFormWidget = publicWidget.Widget.extend({ var answers = []; var sequence = 1; $form.find('.o_wslides_js_quiz_answer').each(function () { - var value = $(this).find('input[type=text]').val(); + var value = $(this).find('.o_wslides_js_quiz_answer_value').val(); if (value.trim() !== "") { var answer = { 'sequence': sequence++, 'text_value': value, - 'is_correct': $(this).find('input[type=radio]').prop('checked') === true + 'is_correct': $(this).find('input[type=radio]').prop('checked') === true, + 'comment': $(this).find('.o_wslides_js_quiz_answer_comment > input[type=text]').val().trim() }; answers.push(answer); } diff --git a/addons/website_slides/static/src/scss/website_slides.scss b/addons/website_slides/static/src/scss/website_slides.scss index 474cb8eeec2..9f393d85124 100644 --- a/addons/website_slides/static/src/scss/website_slides.scss +++ b/addons/website_slides/static/src/scss/website_slides.scss @@ -83,12 +83,14 @@ $o-wslides-fs-side-width: 300px; } } - .o_wsildes_quiz_question_input { - .o_wslides_js_quiz_is_correct > label, - .o_wslides_js_quiz_add_answer, - .o_wslides_js_quiz_remove_answer { + .o_wslides_js_lesson_quiz { + i.o_wslides_js_quiz_icon { cursor: pointer; } + + i.o_wslides_js_quiz_icon:hover { + color: black !important; + } } .o_wslides_js_lesson_quiz_question { @@ -105,20 +107,6 @@ $o-wslides-fs-side-width: 300px; pointer-events: none; } - .o_wslides_js_quiz_edit_question, - .o_wslides_js_quiz_delete_question { - cursor: pointer; - } - - .o_wslides_js_quiz_sequence_handler { - cursor: pointer; - opacity: 0.4; - } - - .o_wslides_js_quiz_sequence_handler:hover { - opacity: 1; - } - &.completed-disabled{ pointer-events: none; } @@ -127,7 +115,7 @@ $o-wslides-fs-side-width: 300px; a.o_wslides_js_quiz_is_correct { color: black; input:checked + i.fa-check-circle-o { - color: $primary; + color: $primary !important; } } diff --git a/addons/website_slides/static/src/xml/slide_quiz.xml b/addons/website_slides/static/src/xml/slide_quiz.xml index 288aa36f8a1..5b1d6e960be 100644 --- a/addons/website_slides/static/src/xml/slide_quiz.xml +++ b/addons/website_slides/static/src/xml/slide_quiz.xml @@ -28,6 +28,10 @@ +
+ + +
diff --git a/addons/website_slides/static/src/xml/slide_quiz_create.xml b/addons/website_slides/static/src/xml/slide_quiz_create.xml index 0d19371e0b3..ab159543c8f 100644 --- a/addons/website_slides/static/src/xml/slide_quiz_create.xml +++ b/addons/website_slides/static/src/xml/slide_quiz_create.xml @@ -34,16 +34,32 @@
- - - - - - +
+
+
+ + +
+ + + +
+
+ + +
+
diff --git a/addons/website_slides/views/slide_question_views.xml b/addons/website_slides/views/slide_question_views.xml index 5aa44eab86b..5d4064255bd 100644 --- a/addons/website_slides/views/slide_question_views.xml +++ b/addons/website_slides/views/slide_question_views.xml @@ -16,6 +16,7 @@ + diff --git a/addons/website_slides/views/website_slides_templates_lesson.xml b/addons/website_slides/views/website_slides_templates_lesson.xml index 22e370837a2..71457ea0bb1 100644 --- a/addons/website_slides/views/website_slides_templates_lesson.xml +++ b/addons/website_slides/views/website_slides_templates_lesson.xml @@ -442,21 +442,21 @@
- + . .
- - + +
+
+ + +