Skip to content

Commit

Permalink
[IMP] Conditional survey questions' usability
Browse files Browse the repository at this point in the history
* Allow to configure conditional questions before saving the page
* Skip pages in the survey without visible questions
* Hide questions and pages without visible questions in the print overview
* Restore method inheritance
* Adapt to Odoo 14.0 datamodel
  • Loading branch information
StefanRijnhart committed Sep 8, 2020
1 parent a4f322c commit c77e337
Show file tree
Hide file tree
Showing 16 changed files with 311 additions and 130 deletions.
27 changes: 5 additions & 22 deletions survey_conditional_questions/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,19 @@
##############################################################################
#
# Copyright (C) 2015 ADHOC SA (http://www.adhoc.com.ar)
# All Rights Reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# © 2015 ADHOC SA (http://www.adhoc.com.ar)
# © 2020 Opener B.V. (https://opener.amsterdam)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Survey Conditional Questions',
'version': '11.0.1.1.0',
'version': '11.0.1.2.0',
'category': 'Warehouse Management',
'sequence': 14,
'summary': '',
'author': 'ADHOC SA',
'author': 'ADHOC SA, Opener B.V.',
'website': 'www.adhoc.com.ar',
'license': 'AGPL-3',
'images': [
],
'depends': [
'survey',
'website'
],
'data': [
'views/survey_question_views.xml',
Expand Down
84 changes: 26 additions & 58 deletions survey_conditional_questions/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# directory
##############################################################################

import json
import logging
from odoo.addons.survey.controllers.main import WebsiteSurvey
from odoo import http
Expand All @@ -14,62 +15,29 @@

class SurveyConditional(WebsiteSurvey):

# TODO deberiamos heredar esto correctamente
@http.route()
def fill_survey(self, survey, token, prev=None, **post):
'''Display and validates a survey'''
Survey = request.env['survey.survey']
UserInput = request.env['survey.user_input']

# Controls if the survey can be displayed
errpage = self._check_bad_cases(survey)
if errpage:
return errpage

# Load the user_input
user_input = UserInput.sudo().search([('token', '=', token)], limit=1)
if not user_input: # Invalid token
return request.render("website.403")

# Do not display expired survey (even if some pages have already been
# displayed -- There's a time for everything!)
errpage = self._check_deadline(user_input)
if errpage:
return errpage

# Select the right page
if user_input.state == 'new': # First page
page, page_nr, last = Survey.next_page(
user_input, 0, go_back=False)
data = {'survey': survey, 'page': page,
'page_nr': page_nr, 'token': user_input.token}
data['hide_question_ids'] = UserInput.get_list_questions(
survey, user_input)
if last:
data.update({'last': True})
return request.render('survey.survey', data)
elif user_input.state == 'done': # Display success message
return request.render(
'survey.sfinished',
{'survey': survey, 'token': token, 'user_input': user_input})
elif user_input.state == 'skip':
flag = (True if prev and prev == 'prev' else False)
page, page_nr, last = Survey.next_page(
user_input, user_input.last_displayed_page_id.id, go_back=flag)

# special case if you click "previous" from the last page,
# then leave the survey, then reopen it from the URL, avoid crash
if not page:
page, page_nr, last = Survey.next_page(
user_input, user_input.last_displayed_page_id.id,
go_back=True)

data = {'survey': survey, 'page': page,
'page_nr': page_nr, 'token': user_input.token}
if last:
data.update({'last': True})
data['hide_question_ids'] = UserInput.get_list_questions(
survey, user_input)
return request.render('survey.survey', data)
@http.route(
['/survey/hidden/<model("survey.survey"):survey>/<string:token>'
'/<int:stored>'],
type='http', auth='public', website=True)
def hidden(self, survey, token, stored, **post):
""" Pass the lists of hidden questions and pages to be applied in the
Javascript.
:param stored: indicate if we can rely on stored answers from a
completed survey, or if we have to determine the hidden questions from
the answers filled in so far. """
ret = {'hidden_pages': [], 'hidden_questions': []}
user_input = request.env['survey.user_input'].sudo().search(
[('token', '=', token)])
if stored:
questions = user_input.user_input_line_ids.filtered(
'hidden').mapped('question_id')
else:
return request.render("website.403")
questions = user_input.get_hidden_questions()
for question in questions:
question_tag = '%s_%s_%s' % (
question.survey_id.id, question.page_id.id, question.id)
ret['hidden_questions'].append(question_tag)
for page in questions.mapped('page_id'):
if not page.question_ids - questions:
ret['hidden_pages'].append(page.id)
return json.dumps(ret)
4 changes: 2 additions & 2 deletions survey_conditional_questions/i18n/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ msgid "Conditional Question"
msgstr "Conditional Question"

#. module: survey_conditional_questions
#: help:survey.question,question_conditional_id:0
#: help:survey.question,triggering_question_id:0
msgid "In order to edit this field you should first save the question"
msgstr "In order to edit this field you should first save the question"

#. module: survey_conditional_questions
#: field:survey.question,question_conditional_id:0
#: field:survey.question,triggering_question_id:0
msgid "Question"
msgstr "Question"

Expand Down
4 changes: 2 additions & 2 deletions survey_conditional_questions/i18n/es.po
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ msgid "Conditional Question"
msgstr "Pregunta Condicional"

#. module: survey_conditional_questions
#: model:ir.model.fields,help:survey_conditional_questions.field_survey_question_question_conditional_id
#: model:ir.model.fields,help:survey_conditional_questions.field_survey_question_triggering_question_id
msgid "In order to edit this field you should first save the question"
msgstr "Para editar este campo , primero debe guardar la pregunta"

#. module: survey_conditional_questions
#: model:ir.model.fields,field_description:survey_conditional_questions.field_survey_question_question_conditional_id
#: model:ir.model.fields,field_description:survey_conditional_questions.field_survey_question_triggering_question_id
msgid "Question"
msgstr "Pregunta"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ msgid "Conditional Question"
msgstr ""

#. module: survey_conditional_questions
#: help:survey.question,question_conditional_id:0
#: help:survey.question,triggering_question_id:0
msgid "In order to edit this field you should first save the question"
msgstr ""

#. module: survey_conditional_questions
#: field:survey.question,question_conditional_id:0
#: field:survey.question,triggering_question_id:0
msgid "Question"
msgstr ""

Expand Down
37 changes: 37 additions & 0 deletions survey_conditional_questions/migrations/11.0.1.2.0/pre-migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import logging


def migrate(cr, version):
logger = logging.getLogger(
'survey_conditional_questions.migrations.11.0.1.3.0')

def rename(old, new):
""" Rename column if the old column exists and the new one does not """
cr.execute(
""" SELECT EXISTS(
SELECT *
FROM information_schema.columns
WHERE table_name='survey_question'
AND column_name=%s) """, (new,))
if cr.fetchone()[0]:
logger.info('Column %s already exists', new)
return
cr.execute(
""" SELECT EXISTS(
SELECT *
FROM information_schema.columns
WHERE table_name='survey_question'
AND column_name=%s) """, (old,))
if not cr.fetchone()[0]:
logger.info('Column %s does not exist', old)
return
logger.info('Renaming column %s to %s', new, old)
cr.execute(
""" ALTER TABLE survey_question
RENAME COLUMN %s to %s """ % (old, new))

for old, new in [
('conditional', 'is_conditional'),
('question_conditional_id', 'triggering_question_id'),
('answer_id', 'triggering_answer_id')]:
rename(old, new)
2 changes: 2 additions & 0 deletions survey_conditional_questions/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
# For copyright and license notices, see __manifest__.py file in module root
# directory
##############################################################################
from . import survey_survey
from . import survey_question
from . import survey_user_input
from . import survey_user_input_line
33 changes: 9 additions & 24 deletions survey_conditional_questions/models/survey_question.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,41 @@
# directory
##############################################################################
from odoo import api, fields, models
import logging

_logger = logging.getLogger(__name__)


class SurveyQuestion(models.Model):
_inherit = 'survey.question'

conditional = fields.Boolean(
is_conditional = fields.Boolean(
'Conditional Question',
copy=False,
# we add copy = false to avoid wrong link on survey copy,
# should be improoved
)
question_conditional_id = fields.Many2one(
triggering_question_id = fields.Many2one(
'survey.question',
'Question',
copy=False,
domain="[('survey_id', '=', survey_id)]",
help="In order to edit this field you should"
" first save the question"
)
answer_id = fields.Many2one(
triggering_answer_id = fields.Many2one(
'survey.label',
'Answer',
copy=False,
)

@api.multi
def validate_question(self, post, answer_tag):
''' Validate question, depending on question
type and parameters '''
""" Skip validation of hidden questions """
self.ensure_one()
try:
checker = getattr(self, 'validate_' + self.type)
except AttributeError:
_logger.warning(
checker.type +
": This type of question has no validation method")
return {}
else:
# TODO deberiamos emprolijar esto
if not self.question_conditional_id:
return checker(post, answer_tag)
if self.triggering_question_id:
input_answer_ids = self.env['survey.user_input_line'].search(
[('user_input_id.token', '=', post.get('token')),
('question_id', '=', self.question_conditional_id.id)])
('question_id', '=', self.triggering_question_id.id)])
for answers in input_answer_ids:
value_suggested = answers.value_suggested
if self.conditional and self.answer_id != value_suggested:
if (self.is_conditional and
self.triggering_answer_id != value_suggested):
return {}
else:
return checker(post, answer_tag)
return super(SurveyQuestion, self).validate_question(post, answer_tag)
28 changes: 28 additions & 0 deletions survey_conditional_questions/models/survey_survey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
##############################################################################
# For copyright and license notices, see __manifest__.py file in module root
# directory
##############################################################################
from odoo import api, models


class SurveySurvey(models.Model):
_inherit = 'survey.survey'

@api.model
def next_page(self, user_input, page_id, go_back=False):
""" Skip pages that only have hidden questions on them,
except if its the last page or the first page (in which case there
is a configuration error in the survey). """
questions_to_hide = user_input.get_hidden_questions()
res = super(SurveySurvey, self).next_page(
user_input, page_id, go_back=go_back)
page, index, last = res
if page and not (page.question_ids - questions_to_hide):
if (not go_back and not last) or (go_back and index):
# Mark every question on this hidden page as hidden.
for question in page.question_ids:
self.env['survey.user_input_line'].update_hidden(
user_input, question)
return self.next_page(
user_input, page.id, go_back=go_back)
return res
24 changes: 12 additions & 12 deletions survey_conditional_questions/models/survey_user_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ class SurveyUserInput(models.Model):
_inherit = 'survey.user_input'

@api.model
def get_list_questions(self, survey, user_input):
obj_questions = self.env['survey.question']
questions_to_hide = []
question_ids = obj_questions.search(
[('survey_id', '=', survey.id)])
for question in question_ids.filtered('conditional'):
for question2 in question_ids.filtered(
lambda x: x == question.question_conditional_id):
input_answer_ids = user_input.user_input_line_ids.filtered(
def get_hidden_questions(self):
""" Return the questions that should be hidden based on the current
user input """
questions_to_hide = self.env['survey.question']
questions = self.survey_id.mapped('page_ids.question_ids')
for question in questions.filtered('is_conditional'):
for question2 in questions.filtered(
lambda x: x == question.triggering_question_id):
input_answer_ids = self.user_input_line_ids.filtered(
lambda x: x.question_id == question2)
for answers in input_answer_ids.filtered(
lambda x: x.value_suggested != question.answer_id):
questions_to_hide.append(question.id)
if question.triggering_answer_id not in (
input_answer_ids.mapped('value_suggested')):
questions_to_hide += question
return questions_to_hide

0 comments on commit c77e337

Please sign in to comment.