From aaba76b770ad76206ebaa24db076a1edfdd560ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Warnon?= Date: Wed, 25 Aug 2021 13:19:59 +0000 Subject: [PATCH] [FIX] survey: correctly consider all non-answered questions as skipped Currently, if the survey participant does not answer questions of type: - simple_choice - multiple_choice - matrix The system does not register any survey.user_input_line with the "skipped" attribute set to True. Meaning that this question's answer will not appear in the survey statistics as skipped (in fact it will not appear at all). This commit makes sure we correctly save a user_input_line set as skipped=True when not answering to those types of questions. A unit test has been added to make sure that we consider every non-answered question type as properly skipped within the survey statistics. Task-2622869 X-original-commit: 87ea302044ff7ac26888000101254b1af2524046 Part-of: odoo/odoo#76606 --- addons/survey/models/survey_question.py | 1 + addons/survey/models/survey_user_input.py | 11 ++++++++ addons/survey/tests/common.py | 32 ++++++++++++++++++++++- addons/survey/tests/test_survey.py | 28 ++++++++++++-------- 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/addons/survey/models/survey_question.py b/addons/survey/models/survey_question.py index ff3204f7d45..9159871264d 100644 --- a/addons/survey/models/survey_question.py +++ b/addons/survey/models/survey_question.py @@ -393,6 +393,7 @@ class SurveyQuestion(models.Model): if question.question_type in ['simple_choice', 'multiple_choice', 'matrix']: answer_lines = all_lines.filtered( lambda line: line.answer_type == 'suggestion' or ( + line.skipped and not line.answer_type) or ( line.answer_type == 'char_box' and question.comment_count_as_answer) ) comment_line_ids = all_lines.filtered(lambda line: line.answer_type == 'char_box') diff --git a/addons/survey/models/survey_user_input.py b/addons/survey/models/survey_user_input.py index 8f006543fec..e6aab8ba70a 100644 --- a/addons/survey/models/survey_user_input.py +++ b/addons/survey/models/survey_user_input.py @@ -274,6 +274,12 @@ class SurveyUserInput(models.Model): def _save_line_choice(self, question, old_answers, answers, comment): if not (isinstance(answers, list)): answers = [answers] + + if not answers: + # add a False answer to force saving a skipped line + # this will make this question correctly considered as skipped in statistics + answers = [False] + vals_list = [] if question.question_type == 'simple_choice': @@ -291,6 +297,11 @@ class SurveyUserInput(models.Model): def _save_line_matrix(self, question, old_answers, answers, comment): vals_list = [] + if not answers and question.matrix_row_ids: + # add a False answer to force saving a skipped line + # this will make this question correctly considered as skipped in statistics + answers = {question.matrix_row_ids[0].id: [False]} + if answers: for row_key, row_answer in answers.items(): for answer in row_answer: diff --git a/addons/survey/tests/common.py b/addons/survey/tests/common.py index b917606ad8a..1103b699422 100644 --- a/addons/survey/tests/common.py +++ b/addons/survey/tests/common.py @@ -147,7 +147,7 @@ class SurveyCase(common.TransactionCase): return self.env['survey.user_input.line'].create(base_alvals) # ------------------------------------------------------------ - # UTILS + # UTILS / CONTROLLER ENDPOINTS FLOWS # ------------------------------------------------------------ def _access_start(self, survey): @@ -216,6 +216,36 @@ class SurveyCase(common.TransactionCase): post_data.update(**additional_post_data) return post_data + # ------------------------------------------------------------ + # UTILS / TOOLS + # ------------------------------------------------------------ + + def _assert_skipped_question(self, question, survey_user): + statistics = question._prepare_statistics(survey_user.user_input_line_ids) + question_data = next( + (question_data + for question_data in statistics + if question_data.get('question') == question), + False + ) + self.assertTrue(bool(question_data)) + self.assertEqual(len(question_data.get('answer_input_skipped_ids')), 1) + + def _create_one_question_per_type(self): + all_questions = self.env['survey.question'] + for (question_type, dummy) in self.env['survey.question']._fields['question_type'].selection: + kwargs = {} + if question_type == 'multiple_choice': + kwargs['labels'] = [{'value': 'MChoice0'}, {'value': 'MChoice1'}] + elif question_type == 'simple_choice': + kwargs['labels'] = [] + elif question_type == 'matrix': + kwargs['labels'] = [{'value': 'Column0'}, {'value': 'Column1'}] + kwargs['labels_2'] = [{'value': 'Row0'}, {'value': 'Row1'}] + all_questions |= self._add_question(self.page_0, 'Q0', question_type, **kwargs) + + return all_questions + class TestSurveyCommon(SurveyCase): def setUp(self): diff --git a/addons/survey/tests/test_survey.py b/addons/survey/tests/test_survey.py index f1c2dfc602c..dc6469b1f56 100644 --- a/addons/survey/tests/test_survey.py +++ b/addons/survey/tests/test_survey.py @@ -11,17 +11,7 @@ class TestSurveyInternals(common.TestSurveyCommon): @users('survey_manager') def test_answer_validation_mandatory(self): """ For each type of question check that mandatory questions correctly check for complete answers """ - for (question_type, text) in self.env['survey.question']._fields['question_type'].selection: - kwargs = {} - if question_type == 'multiple_choice': - kwargs['labels'] = [{'value': 'MChoice0'}, {'value': 'MChoice1'}] - elif question_type == 'simple_choice': - kwargs['labels'] = [] - elif question_type == 'matrix': - kwargs['labels'] = [{'value': 'Column0'}, {'value': 'Column1'}] - kwargs['labels_2'] = [{'value': 'Row0'}, {'value': 'Row1'}] - question = self._add_question(self.page_0, 'Q0', question_type, **kwargs) - + for question in self._create_one_question_per_type(): self.assertDictEqual( question.validate_question(''), {question.id: 'TestError'} @@ -113,3 +103,19 @@ class TestSurveyInternals(common.TestSurveyCommon): question.validate_question('valid'), {} ) + + @users('survey_manager') + def test_skipped_values(self): + """ Create one question per type of questions. + Make sure they are correctly registered as 'skipped' after saving an empty answer for each + of them. """ + + questions = self._create_one_question_per_type() + survey_user = self.survey._create_answer(user=self.survey_user) + + for question in questions: + answer = '' if question.question_type in ['char_box', 'text_box'] else None + survey_user.save_lines(question, answer) + + for question in questions: + self._assert_skipped_question(question, survey_user)