[IMP] survey: allow host to go to previous question in session
PURPOSE ======= For now, the host can't go back to the previous screen on a live session. Why would he want to go back? Misclick, want to display again the result, other SPECIFICATION ============= On a live session, let the host go back to the previous screen. Do not "restart" the questions but display the graph with the results. This back capability is independant of the "back button" survey option. Even if the back button is disabled on the survey configuration, the host must still be able to go back during a session. Task ID: 2351759 closes odoo/odoo#59659 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -72,7 +72,7 @@ class UserInputSession(http.Controller):
|
||||
return request.render('survey.user_input_session_manage', template_values)
|
||||
|
||||
@http.route('/survey/session/next_question/<string:survey_token>', type='json', auth='user', website=True)
|
||||
def survey_session_next_question(self, survey_token, **kwargs):
|
||||
def survey_session_next_question(self, survey_token, go_back=False, **kwargs):
|
||||
""" This route is called when the host goes to the next question of the session.
|
||||
|
||||
It's not a regular 'request.render' route because we handle the transition between
|
||||
@@ -99,7 +99,7 @@ class UserInputSession(http.Controller):
|
||||
if survey.session_state == 'ready':
|
||||
survey._session_open()
|
||||
|
||||
next_question = survey._get_session_next_question()
|
||||
next_question = survey._get_session_next_question(go_back)
|
||||
|
||||
# using datetime.datetime because we want the millis portion
|
||||
if next_question:
|
||||
@@ -196,14 +196,16 @@ class UserInputSession(http.Controller):
|
||||
return {"error": "survey_wrong"}
|
||||
|
||||
def _prepare_manage_session_values(self, survey):
|
||||
is_last_question = False
|
||||
is_first_question, is_last_question = False, False
|
||||
if survey.question_ids:
|
||||
most_voted_answers = survey._get_session_most_voted_answers()
|
||||
is_first_question = survey._is_first_page_or_question(survey.session_question_id)
|
||||
is_last_question = survey._is_last_page_or_question(most_voted_answers, survey.session_question_id)
|
||||
|
||||
values = {
|
||||
'survey': survey,
|
||||
'is_last_question': is_last_question,
|
||||
'is_first_question': is_first_question,
|
||||
}
|
||||
|
||||
values.update(self._prepare_question_results_values(survey, request.env['survey.user_input.line']))
|
||||
|
||||
@@ -573,6 +573,17 @@ class Survey(models.Model):
|
||||
return section
|
||||
return Question
|
||||
|
||||
def _is_first_page_or_question(self, page_or_question):
|
||||
""" This method checks if the given question or page is the first one to display.
|
||||
If the first section of the survey as a description, this will be the first screen to display.
|
||||
else, the first question will be the first screen to be displayed.
|
||||
This methods is used for survey session management where the host should not be able to go back on the
|
||||
first page or question."""
|
||||
first_section_has_description = self.page_ids and not is_html_empty(self.page_ids[0].description)
|
||||
is_first_page_or_question = (first_section_has_description and page_or_question == self.page_ids[0]) or \
|
||||
(not first_section_has_description and page_or_question == self.question_ids[0])
|
||||
return is_first_page_or_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
|
||||
@@ -681,7 +692,7 @@ class Survey(models.Model):
|
||||
self.sudo().write({'session_state': 'in_progress'})
|
||||
self.sudo().flush(['session_state'])
|
||||
|
||||
def _get_session_next_question(self):
|
||||
def _get_session_next_question(self, go_back):
|
||||
self.ensure_one()
|
||||
|
||||
if not self.question_ids or not self.env.user.has_group('survey.group_survey_user'):
|
||||
@@ -690,7 +701,7 @@ class Survey(models.Model):
|
||||
most_voted_answers = self._get_session_most_voted_answers()
|
||||
return self._get_next_page_or_question(
|
||||
most_voted_answers,
|
||||
self.session_question_id.id if self.session_question_id else 0)
|
||||
self.session_question_id.id if self.session_question_id else 0, go_back=go_back)
|
||||
|
||||
def _get_session_most_voted_answers(self):
|
||||
""" In sessions of survey that has conditional questions, as the survey is passed at the same time by
|
||||
|
||||
@@ -32,6 +32,7 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
self.surveyId = self.$el.data('surveyId');
|
||||
self.surveyAccessToken = self.$el.data('surveyAccessToken');
|
||||
self.isStartScreen = self.$el.data('isStartScreen');
|
||||
self.isFirstQuestion = self.$el.data('isFirstQuestion');
|
||||
self.isLastQuestion = self.$el.data('isLastQuestion');
|
||||
// scoring props
|
||||
self.isScoredQuestion = self.$el.data('isScoredQuestion');
|
||||
@@ -148,13 +149,11 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
|
||||
if (screenToDisplay === 'userInputs') {
|
||||
this._setShowInputs(true);
|
||||
this.$('.o_survey_session_navigation_previous').removeClass('d-none');
|
||||
} else if (screenToDisplay === 'results') {
|
||||
this._setShowAnswers(true);
|
||||
// when showing results, stop refreshing answers
|
||||
clearInterval(this.resultsRefreshInterval);
|
||||
delete this.resultsRefreshInterval;
|
||||
this.$('.o_survey_session_navigation_previous').removeClass('d-none');
|
||||
} else if (['leaderboard', 'leaderboardFinal'].includes(screenToDisplay)
|
||||
&& !['leaderboard', 'leaderboardFinal'].includes(this.currentScreen)) {
|
||||
if (this.isLastQuestion) {
|
||||
@@ -185,7 +184,6 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
|
||||
if (screenToDisplay === 'question') {
|
||||
this._setShowInputs(false);
|
||||
this.$('.o_survey_session_navigation_previous').addClass('d-none');
|
||||
} else if (screenToDisplay === 'userInputs') {
|
||||
this._setShowAnswers(false);
|
||||
// resume refreshing answers if necessary
|
||||
@@ -193,10 +191,17 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
this.resultsRefreshInterval = setInterval(this._refreshResults.bind(this), 2000);
|
||||
}
|
||||
} else if (screenToDisplay === 'results') {
|
||||
this.leaderBoard.hideLeaderboard();
|
||||
if (this.leaderBoard) {
|
||||
this.leaderBoard.hideLeaderboard();
|
||||
}
|
||||
// when showing results, stop refreshing answers
|
||||
clearInterval(this.resultsRefreshInterval);
|
||||
delete this.resultsRefreshInterval;
|
||||
} else if (screenToDisplay === 'previousQuestion') {
|
||||
if (this.isFirstQuestion) {
|
||||
return; // nothing to go back to, we're on the first question
|
||||
}
|
||||
this._nextQuestion(true);
|
||||
}
|
||||
|
||||
this.currentScreen = screenToDisplay;
|
||||
@@ -272,14 +277,16 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
_getPreviousScreen: function () {
|
||||
if (this.currentScreen === 'userInputs' && this.isScoredQuestion) {
|
||||
return 'question';
|
||||
} else if (this.currentScreen === 'results' ||
|
||||
(this.currentScreen === 'leaderboard' && !this.isScoredQuestion)) {
|
||||
} else if ((this.currentScreen === 'results' && this.isScoredQuestion) ||
|
||||
(this.currentScreen === 'leaderboard' && !this.isScoredQuestion) ||
|
||||
(this.currentScreen === 'leaderboardFinal' && this.isScoredQuestion)) {
|
||||
return 'userInputs';
|
||||
} else if (this.currentScreen === 'leaderboard' && this.isScoredQuestion) {
|
||||
} else if ((this.currentScreen === 'leaderboard' && this.isScoredQuestion) ||
|
||||
(this.currentScreen === 'leaderboardFinal' && !this.isScoredQuestion)){
|
||||
return 'results';
|
||||
}
|
||||
|
||||
return this.currentScreen;
|
||||
return 'previousQuestion';
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -294,7 +301,7 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
* @param {MouseEvent} ev
|
||||
* @private
|
||||
*/
|
||||
_nextQuestion: function () {
|
||||
_nextQuestion: function (goBack) {
|
||||
var self = this;
|
||||
|
||||
this.isStartScreen = false;
|
||||
@@ -309,7 +316,10 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
});
|
||||
|
||||
var nextQuestionPromise = this._rpc({
|
||||
route: _.str.sprintf('/survey/session/next_question/%s', self.surveyAccessToken)
|
||||
route: _.str.sprintf('/survey/session/next_question/%s', self.surveyAccessToken),
|
||||
params: {
|
||||
'go_back': goBack,
|
||||
}
|
||||
});
|
||||
|
||||
// avoid refreshing results while transitioning
|
||||
@@ -318,14 +328,28 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
delete this.resultsRefreshInterval;
|
||||
}
|
||||
|
||||
Promise.all([fadeOutPromise, nextQuestionPromise]).then(function (results) {
|
||||
Promise.all([fadeOutPromise, nextQuestionPromise]).then(async function (results) {
|
||||
if (results[1]) {
|
||||
var $renderedTemplate = $(results[1]);
|
||||
self.$el.replaceWith($renderedTemplate);
|
||||
self.attachTo($renderedTemplate);
|
||||
self.$el.fadeIn(self.fadeInOutTime, function () {
|
||||
|
||||
// Ensure new question is fully loaded before force loading previous question screen.
|
||||
await self.attachTo($renderedTemplate);
|
||||
if (goBack) {
|
||||
// As we arrive on "question" screen, simulate going to the results screen or leaderboard.
|
||||
self._setShowInputs(true);
|
||||
self._setShowAnswers(true);
|
||||
if (self.sessionShowLeaderboard && self.isScoredQuestion) {
|
||||
self.currentScreen = 'leaderboard';
|
||||
self.leaderBoard.showLeaderboard(false, self.isScoredQuestion);
|
||||
} else {
|
||||
self.currentScreen = 'results';
|
||||
self._refreshResults();
|
||||
}
|
||||
} else {
|
||||
self._startTimer();
|
||||
});
|
||||
}
|
||||
self.$el.fadeIn(self.fadeInOutTime);
|
||||
} else if (self.sessionShowLeaderboard) {
|
||||
// Display last screen if leaderboard activated
|
||||
self.isLastQuestion = true;
|
||||
@@ -546,6 +570,8 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
this.currentScreen = 'question';
|
||||
}
|
||||
|
||||
this.$('.o_survey_session_navigation_previous').toggleClass('d-none', !!this.isFirstQuestion);
|
||||
|
||||
this._setShowInputs(this.currentScreen === 'userInputs');
|
||||
},
|
||||
|
||||
|
||||
@@ -129,6 +129,36 @@ tour.register('test_survey_session_manage_tour', {
|
||||
}, {
|
||||
trigger: '.o_survey_session_text_answer_container:contains("11/11/2011")',
|
||||
run: function () {} // check attendee 2 answer is displayed
|
||||
}, {
|
||||
trigger: 'h1',
|
||||
run: previousScreen
|
||||
}, {
|
||||
trigger: 'h1:contains("Text Question")',
|
||||
run: function () {} // check text question is displayed
|
||||
}, {
|
||||
trigger: '.o_survey_session_progress_small:contains("3 / 3")',
|
||||
run: function () {} // check we have 3 answers
|
||||
}, {
|
||||
trigger: '.o_survey_session_text_answer_container:contains("Attendee 1 is the best")',
|
||||
run: function () {} // check attendee 1 answer is displayed
|
||||
}, {
|
||||
trigger: '.o_survey_session_text_answer_container:contains("Attendee 2 rulez")',
|
||||
run: function () {} // check attendee 2 answer is displayed
|
||||
}, {
|
||||
trigger: '.o_survey_session_text_answer_container:contains("Attendee 3 will crush you")',
|
||||
run: function () {} // check attendee 3 answer is displayed
|
||||
}, {
|
||||
trigger: 'h1',
|
||||
run: nextScreen
|
||||
}, {
|
||||
trigger: '.o_survey_session_progress_small:contains("2 / 3")',
|
||||
run: function () {} // check we have 2 answers
|
||||
}, {
|
||||
trigger: '.o_survey_session_text_answer_container:contains("10/10/2010")',
|
||||
run: function () {} // check attendee 1 answer is displayed
|
||||
}, {
|
||||
trigger: '.o_survey_session_text_answer_container:contains("11/11/2011")',
|
||||
run: function () {} // check attendee 2 answer is displayed
|
||||
}, {
|
||||
trigger: 'h1',
|
||||
run: nextScreen
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
t-att-data-question-type="question.question_type"
|
||||
t-att-data-has-correct-answers="any(answer.is_correct for answer in question.suggested_answer_ids)"
|
||||
t-att-data-answers-validity="answers_validity"
|
||||
t-att-data-is-first-question="is_first_question"
|
||||
t-att-data-is-last-question="is_last_question"
|
||||
t-att-data-current-screen="'question' if is_scored_question else 'userInputs'"
|
||||
t-att-data-show-bar-chart="show_bar_chart"
|
||||
@@ -100,7 +101,7 @@
|
||||
<a role="button"
|
||||
class="font-weight-bold fa fa-chevron-right o_survey_session_navigation o_survey_session_navigation_next p-3" />
|
||||
<a role="button"
|
||||
class="font-weight-bold fa fa-chevron-left o_survey_session_navigation o_survey_session_navigation_previous p-3 d-none" />
|
||||
class="font-weight-bold fa fa-chevron-left o_survey_session_navigation o_survey_session_navigation_previous p-3" />
|
||||
<div class="o_survey_session_results flex-column flex-grow-1">
|
||||
<div class="row">
|
||||
<div class="col-lg-12"><h1 t-esc="question.title"></h1></div>
|
||||
|
||||
Reference in New Issue
Block a user