[FIX] survey: lint JS files and reorganize route code

PURPOSE

As new features are about to land in survey, notably live interactions [1]
and new survey building [2] performing a pre cleaning is necessary. In this
PR we clean survey models by: removing unnecessary fields, cleaning some code
and finally renaming models.

SPECIFICATIONS

This commit lints JS files located in survey as some typos and mistakes
were recently introduced. Code related to routes is also slightly reorganized
to better understand file main sections.

LINKS

[0] Related to Task ID 2061901 (survey models cleaning and preparation)
[1] Task ID 1972640 (live interactions)
[2] Task ID 2119587 (new frontend for building surveys)

PR #40765
This commit is contained in:
Thibault Delavallée
2019-12-05 15:21:28 +00:00
parent a6e9ee91d1
commit bb3d4634df
3 changed files with 150 additions and 140 deletions
+132 -121
View File
@@ -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/<model("survey.survey"):survey>', 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/<model("survey.survey"):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/<model("survey.survey"):survey>', 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),
])
+13 -14
View File
@@ -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
+5 -5
View File
@@ -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();
},