diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py index 753027cfb96..b6147ca937f 100644 --- a/addons/survey/controllers/main.py +++ b/addons/survey/controllers/main.py @@ -185,6 +185,14 @@ class Survey(http.Controller): 'deadline': answer.deadline, } + def _prepare_survey_finished_values(self, survey, answer, token=False): + values = {'survey': survey, 'answer': answer} + if token: + values['token'] = token + if survey.scoring_type != 'no_scoring' and survey.certification: + values['graph_data'] = json.dumps(answer._prepare_statistics()[0]) + return values + # ------------------------------------------------------------ # TAKING SURVEY ROUTES # ------------------------------------------------------------ @@ -348,6 +356,51 @@ class Survey(http.Controller): return '/survey/fill/%s/%s' % (survey_sudo.access_token, answer_token) + def _extract_comment_from_answers(self, question, answers): + """ Answers is a custom structure depending of the question type + that can contain question answers but also comments that need to be + extracted before validating and saving answers. + If multiple answers, they are listed in an array, except for matrix + where answers are structured differently. See input and output for + more info on data structures. + :param question: survey.question + :param answers: + * question_type: free_text, text_box, numerical_box, date, datetime + answers is a string containing the value + * question_type: simple_choice with no comment + answers is a string containing the value ('question_id_1') + * question_type: simple_choice with comment + ['question_id_1', {'comment': str}] + * question_type: multiple choice + ['question_id_1', 'question_id_2'] + [{'comment': str}] if holds a comment + * question_type: matrix + {'matrix_row_id_1': ['question_id_1', 'question_id_2'], + 'matrix_row_id_2': ['question_id_1', 'question_id_2'] + } + {'comment': str} if holds a comment + :return: tuple( + same structure without comment, + extracted comment for given question + ) """ + comment = None + answers_no_comment = [] + if answers: + if question.question_type == 'matrix': + if 'comment' in answers: + comment = answers['comment'].strip() + answers.pop('comment') + answers_no_comment = answers + else: + if not isinstance(answers, list): + answers = [answers] + for answer in answers: + if 'comment' in answer: + comment = answer['comment'].strip() + else: + answers_no_comment.append(answer) + if len(answers_no_comment) == 1: + answers_no_comment = answers_no_comment[0] + return answers_no_comment, comment + # ------------------------------------------------------------ # COMPLETED SURVEY ROUTES # ------------------------------------------------------------ @@ -375,65 +428,6 @@ class Survey(http.Controller): 'format_date': lambda date: format_date(request.env, date) }) - @http.route('/survey/results/', type='http', auth='user', website=True) - def survey_report(self, survey, answer_token=None, **post): - '''Display survey Results & Statistics for given survey.''' - current_filters = [] - filter_display_data = [] - - answers = survey.user_input_ids.filtered(lambda answer: answer.state != 'new' and not answer.test_entry) - filter_finish = post.get('finished') == 'true' - if post or filter_finish: - filter_data = self._get_filter_data(post) - current_filters = survey.filter_input_ids(filter_data, filter_finish) - filter_display_data = survey.get_filter_display_data(filter_data) - return request.render('survey.survey_page_statistics', - {'survey': survey, - 'answers': answers, - 'survey_dict': self._prepare_result_dict(survey, current_filters), - 'page_range': self.page_range, - 'current_filters': current_filters, - 'filter_display_data': filter_display_data, - 'filter_finish': filter_finish - }) - # Quick retroengineering of what is injected into the template for now: - # (TODO: flatten and simplify this) - # - # survey: a browse record of the survey - # survey_dict: very messy dict containing all the info to display answers - # {'page_ids': [ - # - # ... - # - # {'page': browse record of the page, - # 'question_ids': [ - # - # ... - # - # {'graph_data': data to be displayed on the graph - # 'input_summary': number of answered, skipped... - # 'prepare_result': { - # answers displayed in the tables - # } - # 'question': browse record of the question_ids - # } - # - # ... - # - # ] - # } - # - # ... - # - # ] - # } - # - # page_range: pager helper function - # current_filters: a list of ids - # filter_display_data: [{'labels': ['a', 'b'], question_text} ... ] - # filter_finish: boolean => only finished surveys or not - # - @http.route(['/survey//get_certification_preview'], type="http", auth="user", methods=['GET'], website=True) def survey_get_certification_preview(self, survey, **kwargs): if not request.env.user.has_group('survey.group_survey_user'): @@ -467,6 +461,85 @@ class Survey(http.Controller): return self._generate_report(succeeded_attempt, download=True) + def _generate_report(self, user_input, download=True): + report = request.env.ref('survey.certification_report').sudo().render_qweb_pdf([user_input.id], data={'report_type': 'pdf'})[0] + + report_content_disposition = content_disposition('Certification.pdf') + if not download: + content_split = report_content_disposition.split(';') + content_split[0] = 'inline' + report_content_disposition = ';'.join(content_split) + + return request.make_response(report, headers=[ + ('Content-Type', 'application/pdf'), + ('Content-Length', len(report)), + ('Content-Disposition', report_content_disposition), + ]) + + # ------------------------------------------------------------ + # REPORTING SURVEY ROUTES + # ------------------------------------------------------------ + + @http.route('/survey/results/', type='http', auth='user', website=True) + def survey_report(self, survey, answer_token=None, **post): + """ Display survey Results & Statistics for given survey. + + Quick retroengineering of what is injected into the template for now: + (TODO: flatten and simplify this) + + survey: a browse record of the survey + survey_dict: very messy dict containing all the info to display answers + {'page_ids': [ + + ... + + {'page': browse record of the page, + 'question_ids': [ + + ... + + {'graph_data': data to be displayed on the graph + 'input_summary': number of answered, skipped... + 'prepare_result': { + answers displayed in the tables + } + 'question': browse record of the question_ids + } + + ... + + ] + } + + ... + + ] + } + + page_range: pager helper function + current_filters: a list of ids + filter_display_data: [{'labels': ['a', 'b'], question_text} ... ] + filter_finish: boolean => only finished surveys or not + """ + current_filters = [] + filter_display_data = [] + + answers = survey.user_input_ids.filtered(lambda answer: answer.state != 'new' and not answer.test_entry) + filter_finish = post.get('finished') == 'true' + if post or filter_finish: + filter_data = self._get_filter_data(post) + current_filters = survey.filter_input_ids(filter_data, filter_finish) + filter_display_data = survey.get_filter_display_data(filter_data) + return request.render('survey.survey_page_statistics', { + 'survey': survey, + 'answers': answers, + 'survey_dict': self._prepare_result_dict(survey, current_filters), + 'page_range': self.page_range, + 'current_filters': current_filters, + 'filter_display_data': filter_display_data, + 'filter_finish': filter_finish + }) + def _prepare_result_dict(self, survey, current_filters=None): """Returns dictionary having values for rendering template""" current_filters = current_filters if current_filters else [] @@ -569,65 +642,3 @@ class Survey(http.Controller): 'success_rate': round((scoring_success_count / total_scoring_success) * 100, 1) if total_scoring_success > 0 else 0, 'graph_data': graph_data } - - def _prepare_survey_finished_values(self, survey, answer, token=False): - values = {'survey': survey, 'answer': answer} - if token: - values['token'] = token - if survey.scoring_type != 'no_scoring' and survey.certification: - values['graph_data'] = json.dumps(answer._prepare_statistics()[0]) - return values - - def _extract_comment_from_answers(self, question, answers): - """ - As answer is a custom structure depending of the question type - that can contain question answers but also comment that need to be extracted - before validating and saving answers. - If multiple answers, they are listed in an array, except for matrix where answers are structured differently. - See input and output for more info on data structures. - :param question: survey.question - :param answers: {str} answer (for simple questions) - or {list} [answer_1,...,answer_n] (for questions with multiple answers) - or {list} [answer_1, {'comment': comment}] (for questions with simple answers and with comment) - or {list} [answer_1,...,answer_n, {'comment': comment}] (for questions with multiple answers with comment) - or {list} [{sub_question_1: [answer_1,...,answer_n]}, {sub_question_2: [...]}] (for matrix without comment) - or {list} [{sub_question_1: [answer_1,...,answer_n]}, {sub_question_2: [...]}, {'comment': comment}] (for matrix with comment) - :return: answers_no_comment : {str} answer for simple choice (or text and number) question types - or {list} [answer_1,...,answer_n] for multiple choices question types except matrix - or {list} [{sub_question_1: [answer_1,...,answer_n]}, {sub_question_2: [...]}] for matrix - comment : {str} extracted comment for the given question - """ - comment = None - answers_no_comment = [] - if answers: - if question.question_type == 'matrix': - if 'comment' in answers: - comment = answers['comment'].strip() - answers.pop('comment') - answers_no_comment = answers - else: - if not isinstance(answers, list): - answers = [answers] - for answer in answers: - if 'comment' in answer: - comment = answer['comment'].strip() - else: - answers_no_comment.append(answer) - if len(answers_no_comment) == 1: - answers_no_comment = answers_no_comment[0] - return answers_no_comment, comment - - def _generate_report(self, user_input, download=True): - report = request.env.ref('survey.certification_report').sudo().render_qweb_pdf([user_input.id], data={'report_type': 'pdf'})[0] - - report_content_disposition = content_disposition('Certification.pdf') - if not download: - content_split = report_content_disposition.split(';') - content_split[0] = 'inline' - report_content_disposition = ';'.join(content_split) - - return request.make_response(report, headers=[ - ('Content-Type', 'application/pdf'), - ('Content-Length', len(report)), - ('Content-Disposition', report_content_disposition), - ]) diff --git a/addons/survey/static/src/js/survey_form.js b/addons/survey/static/src/js/survey_form.js index cae3ab20666..ecb74022d56 100644 --- a/addons/survey/static/src/js/survey_form.js +++ b/addons/survey/static/src/js/survey_form.js @@ -1,7 +1,6 @@ odoo.define('survey.form', function(require) { 'use strict'; -var ajax = require('web.ajax'); var field_utils = require('web.field_utils'); var publicWidget = require('web.public.widget'); var time = require('web.time'); @@ -24,7 +23,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ start: function () { var self = this; return this._super.apply(this, arguments).then(function() { - self.options = self.$target.find('form').data() + self.options = self.$target.find('form').data(); var $timer = $('.o_survey_timer'); if ($timer.length) { var timeLimitMinutes = self.options.timeLimitMinutes; @@ -70,9 +69,9 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ // Handle dropdownlist var $dropdownlist = $choiceItemGroup.find('select'); - var isDropdown = $dropdownlist.length > 0 + var isDropdown = $dropdownlist.length > 0; - var isOtherSelected = (isDropdown && $dropdownlist.val() === $otherItem.val()) || $otherItem.prop('checked') + var isOtherSelected = (isDropdown && $dropdownlist.val() === $otherItem.val()) || $otherItem.prop('checked'); if (isOtherSelected || $commentInput.hasClass('o_survey_comment')) { if (isDropdown) { @@ -100,7 +99,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ */ _onBreadcrumbClick: function (event) { event.preventDefault(); - this._submitForm({'previous_page_id': $(event.currentTarget).data('previousPageId')}) + this._submitForm({'previous_page_id': $(event.currentTarget).data('previousPageId')}); }, _onSubmit: function (event) { @@ -223,7 +222,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ _prepareSubmitChoices: function(params, $parent, questionId, selector) { var self = this; $parent.find(selector).each(function () { - if (this.value != '-1') { + if (this.value !== '-1') { params = self._prepareSubmitAnswer(params, questionId, this.value); } }); @@ -299,7 +298,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ } } }); - return params + return params; }, // INIT FIELDS TOOLS @@ -309,21 +308,21 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ * Initialize datetimepicker in correct format and with constraints */ _initDateTimePicker: function ($dateGroup) { - var disabledDates = [] + var disabledDates = []; - var minDateData = $dateGroup.data('mindate') + var minDateData = $dateGroup.data('mindate'); var minDate = minDateData ? this._formatDateTime(minDateData) : moment({ y: 1900 }); - var maxDateData = $dateGroup.data('maxdate') + var maxDateData = $dateGroup.data('maxdate'); var maxDate = maxDateData ? this._formatDateTime(maxDateData) : moment().add(200, "y"); var datetimepickerFormat = time.getLangDateFormat() if ($dateGroup.find('input').data('questionType') === 'datetime') { - datetimepickerFormat = time.getLangDatetimeFormat() + datetimepickerFormat = time.getLangDatetimeFormat(); } else { // Include min and max date in selectable values maxDate = moment(maxDate).add(1, "d"); minDate = moment(minDate).subtract(1, "d"); - disabledDates = [minDate, maxDate] + disabledDates = [minDate, maxDate]; } $dateGroup.datetimepicker({ @@ -360,8 +359,8 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({ _scrollToError: function ($target) { var scrollLocation = $target.offset().top; var navbarHeight = $('.o_main_navbar').height(); - if (navbarHeight != undefined) { - scrollLocation -= navbarHeight + if (navbarHeight) { + scrollLocation -= navbarHeight; } $('html, body').animate({ scrollTop: scrollLocation diff --git a/addons/survey/static/src/js/survey_result.js b/addons/survey/static/src/js/survey_result.js index d2a6659f10e..3919015af00 100644 --- a/addons/survey/static/src/js/survey_result.js +++ b/addons/survey/static/src/js/survey_result.js @@ -367,8 +367,8 @@ publicWidget.registry.SurveyResultWidget = publicWidget.Widget.extend({ var row_id = cell.data('row_id') | 0; var answer_id = cell.data('answer_id'); - var params = new URLSearchParams(window.location.search) - var filters = params.get('filters') ? params.get('filters') + "|" + row_id + ',' + answer_id : row_id + ',' + answer_id + var params = new URLSearchParams(window.location.search); + var filters = params.get('filters') ? params.get('filters') + "|" + row_id + ',' + answer_id : row_id + ',' + answer_id; params.set('filters', filters); window.location.href = window.location.pathname + '?' + params.toString(); @@ -379,7 +379,7 @@ publicWidget.registry.SurveyResultWidget = publicWidget.Widget.extend({ * @param {Event} ev */ _onClearFilterClick: function (ev) { - var params = new URLSearchParams(window.location.search) + var params = new URLSearchParams(window.location.search); params.delete('filters'); params.delete('finished'); window.location.href = window.location.pathname + '?' + params.toString(); @@ -390,7 +390,7 @@ publicWidget.registry.SurveyResultWidget = publicWidget.Widget.extend({ * @param {Event} ev */ _onFilterAllClick: function (ev) { - var params = new URLSearchParams(window.location.search) + var params = new URLSearchParams(window.location.search); params.delete('finished'); window.location.href = window.location.pathname + '?' + params.toString(); }, @@ -400,7 +400,7 @@ publicWidget.registry.SurveyResultWidget = publicWidget.Widget.extend({ * @param {Event} ev */ _onFilterFinishedClick: function (ev) { - var params = new URLSearchParams(window.location.search) + var params = new URLSearchParams(window.location.search); params.set('finished', true); window.location.href = window.location.pathname + '?' + params.toString(); },