[IMP] survey: auto submit for questions and matrix with simple choice

Purpose
=======

Speed up survey completion.

Specifications
==============

This commit apply an auto commit when user select a choice on a simple choice
question or on a matrix question with simple choice.

The auto commit happens following those conditions:
- Only on simple choice and matrix with simple choice questions
- For matrix, only when last subquestion is answered
- Only when Back mode is active
- If this is not the last question

Link
====

PR #46768
Task 2208574
This commit is contained in:
David Beguin
2020-03-31 16:25:53 +00:00
committed by Aurélien Warnon
parent 176e5a5066
commit 280c6bf1d6
5 changed files with 52 additions and 13 deletions
+3 -3
View File
@@ -286,9 +286,9 @@ class Survey(http.Controller):
answer_sudo.last_displayed_page_id.id if answer_sudo.last_displayed_page_id else 0)
if next_page_or_question:
pages_or_questions = survey_sudo._get_pages_or_questions(answer_sudo)
if pages_or_questions.ids.index(next_page_or_question.id) == len(pages_or_questions) - 1:
data.update({'survey_last': True})
data.update({
'survey_last': survey_sudo._is_last_page_or_question(answer_sudo, next_page_or_question)
})
if answer_sudo.is_session_answer and next_page_or_question.is_time_limited:
data.update({
@@ -188,7 +188,8 @@ class UserInputSession(http.Controller):
def _prepare_manage_session_values(self, survey):
is_last_question = False
if survey.question_ids:
is_last_question = survey.session_question_id == survey.question_ids[-1]
most_voted_answers = survey._get_session_most_voted_answers()
is_last_question = survey._is_last_page_or_question(most_voted_answers, survey.session_question_id)
values = {
'survey': survey,
+22 -1
View File
@@ -549,7 +549,7 @@ class Survey(models.Model):
if page_or_question_id == 0:
return pages_or_questions[0]
current_page_index = pages_or_questions.ids.index(next(p.id for p in pages_or_questions if p.id == page_or_question_id))
current_page_index = pages_or_questions.ids.index(page_or_question_id)
# Get previous and we are on first page OR Get Next and we are on last page
if (go_back and current_page_index == 0) or (not go_back and current_page_index == len(pages_or_questions) - 1):
@@ -582,6 +582,27 @@ class Survey(models.Model):
return Question
def _is_last_page_or_question(self, user_input, page_or_question):
""" This method checks if the given question or page is the last one.
This includes conditional questions configuration. If the given question is normally not the last one but
every following questions are inactive due to conditional questions configurations (and user choices),
the given question will be the last one.
For section, we check in each following section if there is an active question.
If yes, the given page is not the last one.
"""
pages_or_questions = self._get_pages_or_questions(user_input)
current_page_index = pages_or_questions.ids.index(page_or_question.id)
next_page_or_question_candidates = pages_or_questions[current_page_index + 1:]
if next_page_or_question_candidates:
inactive_questions = user_input._get_inactive_conditional_questions()
if self.questions_layout == 'page_per_question':
return not any(next_question not in inactive_questions for next_question in next_page_or_question_candidates)
elif self.questions_layout == 'page_per_section':
for section in next_page_or_question_candidates:
return not any(next_question not in inactive_questions for next_question in section.question_ids)
return True
def _get_survey_questions(self, answer=None, page_id=None, question_id=None):
""" Returns a tuple containing: the survey question and the passed question_id / page_id
based on the question_layout and the fact that it's a session or not.
@@ -111,6 +111,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
} else {
$choiceInput.prop("checked", !$choiceInput.prop("checked")).trigger('change');
}
// Avoid selection key to be typed into the textbox if 'other' is selected by key
event.preventDefault();
}
@@ -146,9 +147,20 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
var $matrixBtn = $target.closest('.o_survey_matrix_btn');
if ($target.attr('type') === 'radio') {
var isQuestionComplete = false;
if ($matrixBtn.length > 0) {
$matrixBtn.closest('tr').find('td').removeClass('o_survey_selected');
$matrixBtn.addClass('o_survey_selected');
if (this.options.questionsLayout === 'page_per_question') {
var subQuestionsIds = $matrixBtn.closest('table').data('subQuestions');
var completedQuestions = [];
subQuestionsIds.forEach(function (id) {
if (self.$('tr#' + id).find('input:checked').length !== 0) {
completedQuestions.push(id);
}
});
isQuestionComplete = completedQuestions.length === subQuestionsIds.length;
}
} else {
var previouslySelectedAnswer = $choiceItemGroup.find('label.o_survey_selected');
previouslySelectedAnswer.removeClass('o_survey_selected');
@@ -156,6 +168,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
var newlySelectedAnswer = $target.closest('label');
if (newlySelectedAnswer.find('input').val() !== previouslySelectedAnswer.find('input').val()) {
newlySelectedAnswer.addClass('o_survey_selected');
isQuestionComplete = this.options.questionsLayout === 'page_per_question';
}
// Conditional display
@@ -187,6 +200,11 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
}
}
}
// Submit Form
var isLastQuestion = this.$('button[value="finish"]').length !== 0;
if (!isLastQuestion && this.options.usersCanGoBack && isQuestionComplete) {
this._submitForm({});
}
} else { // $target.attr('type') === 'checkbox'
if ($matrixBtn.length > 0) {
$matrixBtn.toggleClass('o_survey_selected', !$matrixBtn.hasClass('o_survey_selected'));
+7 -8
View File
@@ -88,6 +88,7 @@
class="d-flex flex-grow-1"
t-att-data-answer-token="answer.access_token"
t-att-data-survey-token="survey.access_token"
t-att-data-users-can-go-back="survey.users_can_go_back"
t-att-data-session-in-progress="answer.is_session_answer"
t-att-data-is-start-screen="answer.state == 'new'"
t-att-data-readonly="survey_form_readonly"
@@ -186,7 +187,9 @@
<div class="row">
<div class="offset-3 col-6 text-center mt16">
<button type="submit" t-att-value="'next' if not survey_last else 'finish'" class="btn btn-primary">
Continue</button><span class="font-weight-bold text-muted ml-2 d-none d-md-inline"> or press Enter</span>
<t t-if="not survey_last">Continue</t>
<t t-else="">Submit</t>
</button><span class="font-weight-bold text-muted ml-2 d-none d-md-inline"> or press Enter</span>
</div>
</div>
</t>
@@ -221,12 +224,8 @@
<div class="row">
<div class="offset-3 col-6 text-center mt16">
<button type="submit" t-att-value="'next' if not survey_last else 'finish'" class="btn btn-primary">
<t t-if="answer.is_session_answer">
Submit
</t>
<t t-else="">
Continue
</t>
<t t-if="answer.is_session_answer or survey_last">Submit</t>
<t t-else="">Continue</t>
</button>
<span class="font-weight-bold text-muted ml-2 d-none d-md-inline">or press Enter</span>
</div>
@@ -532,7 +531,7 @@
<t t-set="item_idx" t-value="0"/>
<!-- For matrix, we have an extra check because we have rows * columns total options -->
<t t-set="useKeySelection" t-value="useKeySelection and (len(question.suggested_answer_ids) * len(question.matrix_row_ids)) &lt; len(letters)" />
<tr class="bg-white text-white" t-foreach="question.matrix_row_ids" t-as="row_label">
<tr class="bg-white text-white" t-foreach="question.matrix_row_ids" t-as="row_label" t-att-id="row_label.id">
<th class="border-0 font-weight-normal text-left"><span t-field="row_label.value" /></th>
<t t-foreach="question.suggested_answer_ids" t-as="col_label">
<t t-set="answer" t-value="answer_lines.filtered(lambda line: line.suggested_answer_id == col_label and line.matrix_row_id == row_label)"/>