From 5b92704e4109acaf56070520ec6a7f72fc4791f9 Mon Sep 17 00:00:00 2001 From: "Richard Mathot (OpenERP)" Date: Mon, 25 Nov 2013 09:04:46 +0100 Subject: [PATCH] [IMP] Code cleaning and refactoring bzr revid: rim@openerp.com-20131125080446-kail7m6xav7gf0xg --- addons/survey/controllers/__init__.py | 21 + addons/survey/controllers/main.py | 120 +++-- addons/survey/survey.py | 42 +- addons/survey/survey_view.xml | 4 +- addons/survey/views/survey_templates.xml | 624 +++++++++-------------- 5 files changed, 357 insertions(+), 454 deletions(-) diff --git a/addons/survey/controllers/__init__.py b/addons/survey/controllers/__init__.py index 8ee9bae18d9..672df6ec1f9 100644 --- a/addons/survey/controllers/__init__.py +++ b/addons/survey/controllers/__init__.py @@ -1 +1,22 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-TODAY OpenERP S.A. +# +# 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 . +# +############################################################################## + import main diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py index aa15188bdd2..56f3e14d16f 100644 --- a/addons/survey/controllers/main.py +++ b/addons/survey/controllers/main.py @@ -22,6 +22,7 @@ from openerp.addons.web import http from openerp.addons.web.http import request from openerp.addons.website.models import website +from openerp.osv import fields from openerp import SUPERUSER_ID import werkzeug @@ -48,9 +49,9 @@ class WebsiteSurvey(http.Controller): surveys = survey_obj.browse(cr, uid, survey_ids, context=context) return request.website.render('survey.list', {'surveys': surveys}) + # Survey displaying - @website.route(['/survey/fill/', - '/survey/fill//'], + @website.route(['/survey/fill//'], type='http', auth='public', multilang=True) def fill_survey(self, survey, token=None, **post): '''Display and validates a survey''' @@ -58,6 +59,8 @@ class WebsiteSurvey(http.Controller): survey_obj = request.registry['survey.survey'] user_input_obj = request.registry['survey.user_input'] + # Mettre methode start survey sur objet survey + # In case of bad survey, redirect to surveys list if survey_obj.exists(cr, uid, survey.id, context=context) == []: return werkzeug.utils.redirect("/survey/") @@ -68,7 +71,7 @@ class WebsiteSurvey(http.Controller): # In case of non open surveys if survey.state != 'open': - return request.website.render("survey.notopen") + return request.website.render("survey.notopen") # If enough surveys completed if survey.user_input_limit > 0: @@ -91,35 +94,34 @@ class WebsiteSurvey(http.Controller): else: user_input = user_input_obj.browse(cr, uid, [user_input_id], context=context)[0] + # Prevent opening of the survey if the deadline has turned out + # ! This will NOT disallow access to users who have already partially filled the survey ! + # if user_input.deadline > fields.date.now() and user_input.state == 'new': + # return request.website.render("survey.notopen") + # TODO check if this is ok + _logger.debug('Incoming data: %s', post) - # if user input.state = new => page -1 - # sinon, chercher la dernière page sur laquelle on a des infos enregistrées - # si c'est la dernière, afficher la page de conclusion + # Select the right page - # Display success message if totally succeeded - if post and post['next'] == "finished": + # if user_input.state == 'new' and not post: # Intro page + # data = {'survey': survey, 'page': None, 'token': user_input.token} + # return request.website.render('survey.survey', data) + if user_input.state == 'new': # First page + page, page_nr, last = self.find_next_page(survey, user_input) + data = {'survey': survey, 'page': page, 'page_nr': page_nr, 'token': user_input.token} + if last: + data.update({'last': True}) + return request.website.render('survey.survey', data) + elif user_input.state == 'done': # Display success message return request.website.render('survey.finished', {'survey': survey}) - - # Page selection - pagination = {'current': -1, 'next': 0} - # Default pagination if first opening - - if 'current' in post and 'next' in post and post['next'] != "finished": - oldnext = int(post['next']) - if oldnext not in range(0, len(survey.page_ids)): - raise Exception("This page does not exist") - else: - pagination['current'] = oldnext - if oldnext == len(survey.page_ids) - 1: - pagination['next'] = 'finished' - else: - pagination['next'] = oldnext + 1 - - return request.website.render('survey.survey', - {'survey': survey, - 'pagination': pagination, - 'token': user_input.token}) + elif user_input.state == 'skip': + page, page_nr, last = self.find_next_page(survey, user_input) + if last: + data.update({'last': True}) + data = {'survey': survey, 'page': page, 'page_nr': page_nr, 'token': user_input.token} + else: + return request.website.render("website.403") # @website.route(['/survey/prefill/'], type='json', auth='public', multilang=True): @@ -134,21 +136,30 @@ class WebsiteSurvey(http.Controller): type='http', auth='public', multilang=True) def submit(self, survey, **post): _logger.debug('Incoming data: %s', post) - page_nr = int(post['current']) - questions = survey.page_ids[page_nr].question_ids + page_id = int(post['page_id']) + cr, uid, context = request.cr, request.uid, request.context + questions_obj = request.registry['survey.question'] + questions_ids = questions_obj.search(cr, uid, [('page_id', '=', page_id)], + context=context) + questions = questions_obj.browse(cr, uid, questions_ids, context=context) errors = {} for question in questions: - answer_tag = "%s_%s_%s" % (survey.id, page_nr, question.id) - errors.update(self.validate_question(survey.id, page_nr, question, post, answer_tag)) + answer_tag = "%s_%s_%s" % (survey.id, page_id, question.id) + errors.update(self.validate_question(question, post, answer_tag)) ret = {} if (len(errors) != 0): ret['errors'] = errors else: cr, uid, context = request.cr, request.uid, request.context + user_input_obj = request.registry['survey.user_input'] + try: + user_input_id = user_input_obj.search(cr, uid, [('token', '=', post['token'])])[0] + except IndexError: # Invalid token + return request.website.render("website.403") # Store here data if allowed - ret['redirect'] = "nexturl" + ret['redirect'] = True return json.dumps(ret) # Printing routes @@ -161,9 +172,31 @@ class WebsiteSurvey(http.Controller): {'survey': survey, 'pagination': pagination}) + # Pagination + + def find_next_page(self, survey, user_input): + ''' Find the browse record of the first unfilled page ''' + if not user_input.user_input_line_ids: + return survey.page_ids[0], 0, len(survey.page_ids) == 1 + else: + filled_pages = set() + for user_input_line in user_input.user_input_line_ids: + filled_pages.add(user_input_line.page_id) + last = False + page_nr = 0 + nextpage = None + for page in survey.pages_ids: + if page in filled_pages: + page_nr = page_nr + 1 + else: + nextpage = page + if page_nr == len(survey.pages_ids): + last = True + return nextpage, page_nr, last + # Validation methods - def validate_question(self, survey_id, page_nr, question, post, answer_tag): + def validate_question(self, question, post, answer_tag): ''' Routing to the right question valider, depending on question type ''' try: checker = getattr(self, 'validate_' + question.type) @@ -171,9 +204,9 @@ class WebsiteSurvey(http.Controller): _logger.warning(question.type + ": This type of question has no validation method") return {} else: - return checker(survey_id, page_nr, question, post, answer_tag) + return checker(question, post, answer_tag) - def validate_free_text(self, survey_id, page_nr, question, post, answer_tag): + def validate_free_text(self, question, post, answer_tag): errors = {} answer = post[answer_tag].strip() # Empty answer to mandatory question @@ -181,7 +214,7 @@ class WebsiteSurvey(http.Controller): errors.update({answer_tag: question.constr_error_msg}) return errors - def validate_textbox(self, survey_id, page_nr, question, post, answer_tag): + def validate_textbox(self, question, post, answer_tag): errors = {} answer = post[answer_tag].strip() # Empty answer to mandatory question @@ -232,7 +265,7 @@ class WebsiteSurvey(http.Controller): pass return errors - def validate_numerical_box(self, survey_id, page_nr, question, post, answer_tag): + def validate_numerical_box(self, question, post, answer_tag): errors = {} answer = post[answer_tag].strip() # Empty answer to mandatory question @@ -246,7 +279,7 @@ class WebsiteSurvey(http.Controller): errors.update({answer_tag: question.constr_error_msg}) return errors - def validate_datetime(self, survey_id, page_nr, question, post, answer_tag): + def validate_datetime(self, question, post, answer_tag): errors = {} answer = post[answer_tag].strip() # Empty answer to mandatory question @@ -256,20 +289,17 @@ class WebsiteSurvey(http.Controller): # TODO when datepicker will be available return errors - # def validate_simple_choice(self, survey_id, page_nr, question, post, answer_tag): - # answer_tag = survey_id.__str__() + '*' + page_nr + '*' + question.id.__str__() + # def validate_simple_choice(self, question, post, answer_tag): # problems = [] # if question.constr_mandatory: # problems = problems + self.__has_empty_input(question, post, answer_tag) # return problems - # def validate_multiple_choice(self, survey_id, page_nr, question, post, answer_tag): - # answer_tag = survey_id.__str__() + '*' + page_nr + '*' + question.id.__str__() + # def validate_multiple_choice(self, question, post, answer_tag): # problems = [] # return problems - # def validate_matrix(self, survey_id, page_nr, question, post, answer_tag): - # answer_tag = survey_id.__str__() + '*' + page_nr + '*' + question.id.__str__() + # def validate_matrix(self, question, post, answer_tag): # problems = [] # return problems diff --git a/addons/survey/survey.py b/addons/survey/survey.py index a75339cdf68..c4f06110300 100644 --- a/addons/survey/survey.py +++ b/addons/survey/survey.py @@ -76,7 +76,7 @@ class survey_survey(osv.osv): base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') for survey_browse in self.browse(cr, uid, ids, context=context): - res[survey_browse.id] = urljoin(base_url, "survey/fill/%s/" + res[survey_browse.id] = urljoin(base_url, "survey/start/%s/" % survey_browse.id) return res @@ -96,7 +96,7 @@ class survey_survey(osv.osv): [('draft', 'Draft'), ('open', 'Open'), ('close', 'Closed'), ('cancel', 'Cancelled')], 'Status', required=1, readonly=1, translate=1), - 'visible_to_user': fields.boolean('Visible in the Surveys menu', + 'visible_to_user': fields.boolean('Public in website', help="If unchecked, only invited users will be able to open the survey."), 'auth_required': fields.boolean('Login required', help="Users with a public link will be requested to login before taking part to the survey", @@ -113,7 +113,7 @@ class survey_survey(osv.osv): 'user_input_ids': fields.one2many('survey.user_input', 'survey_id', 'User responses', readonly=1), 'public_url': fields.function(_get_public_url, - string="Public link", searchable=True, type="char", store=True), + string="Public link", type="char"), 'email_template_id': fields.many2one('email.template', 'Email Template', ondelete='set null'), 'thank_you_message': fields.html('Thank you message', translate=True, @@ -364,11 +364,15 @@ class survey_question(osv.osv): 'question_id', 'Suggested answers', oldname='answer_choice_ids'), # Display options - 'simple_choice_display': fields.selection([('1col', '1 column choices'), - ('2col', '2 columns choices'), - ('3col', '3 columns choices'), - ('horizontal', 'Horizontal choices'), - ('dropdown', 'Dropdown menu')], 'Display mode'), + 'column_nb': fields.selection([('12', '1 column choices'), + ('6', '2 columns choices'), + ('4', '3 columns choices'), + ('3', '4 columns choices'), + ('2', '6 columns choices') + ], 'Number of columns'), + 'display_mode': fields.selection([('columns', 'Columns'), + ('dropdown', 'Dropdown menu') + ], 'Display mode'), # Comments 'comments_allowed': fields.boolean('Allow comments', @@ -417,7 +421,8 @@ class survey_question(osv.osv): _defaults = { 'page_id': lambda s, cr, uid, c: c.get('page_id'), 'type': 'free_text', - 'simple_choice_display': '1col', + 'column_nb': '12', + 'display_mode': 'dropdown', 'constr_type': 'at least', 'constr_minimum_req_ans': 1, 'constr_error_msg': lambda s, cr, uid, c: @@ -579,9 +584,9 @@ class survey_user_input(osv.osv): 'Answer Type', required=1, oldname="response_type"), 'state': fields.selection([('new', 'Not started yet'), ('skip', 'Partially completed'), - ('done', 'Completed'), - ('test', 'Test')], 'Status', + ('done', 'Completed')], 'Status', readonly=True), + 'test_entry': fields.boolean('Test entry'), # Optional Identification data 'token': fields.char("Identification token", readonly=1), @@ -692,19 +697,20 @@ class survey_user_input_line(osv.osv): _columns = { 'user_input_id': fields.many2one('survey.user_input', 'User Input', ondelete='cascade', required=1), - 'survey_id': fields.many2one('survey.survey', 'Survey', required=1, - readonly=1, ondelete='cascade'), - - 'date_create': fields.datetime('Create Date', required=1), # drop - 'skipped': fields.boolean('Skipped'), + 'survey_id': fields.related('user_input_id', 'survey_id', + type="many2one", relation="survye.survey", string='Survey'), 'question_id': fields.many2one('survey.question', 'Question', ondelete='restrict'), + 'page_id': fields.related('question_id', 'page_id', type='many2one', + relation='survey.page', string="Page"), + 'date_create': fields.datetime('Create Date', required=1), # drop + 'skipped': fields.boolean('Skipped'), 'answer_type': fields.selection([('textbox', 'Text box'), ('numerical_box', 'Numerical box'), ('free_text', 'Free Text'), ('datetime', 'Date and Time'), - ('checkbox', 'Checkbox'), - ], 'Question Type', required=1), + ('checkbox', 'Checkbox')], + 'Question Type', required=1), 'value_text': fields.char("Text answer"), 'value_number': fields.float("Numerical answer"), 'value_date': fields.datetime("Date answer"), diff --git a/addons/survey/survey_view.xml b/addons/survey/survey_view.xml index 6c557851ece..4d3243e7468 100644 --- a/addons/survey/survey_view.xml +++ b/addons/survey/survey_view.xml @@ -423,7 +423,9 @@ - + + + diff --git a/addons/survey/views/survey_templates.xml b/addons/survey/views/survey_templates.xml index c3ee0ce27e2..62090a6e0c7 100644 --- a/addons/survey/views/survey_templates.xml +++ b/addons/survey/views/survey_templates.xml @@ -1,419 +1,263 @@ - - - - - - - - - - - -