2010-01-20 14:28:28 +00:00
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
2013-10-31 15:45:33 +00:00
# Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
2010-01-20 14:28:28 +00:00
#
2013-10-21 09:03:19 +00:00
# This program is free software: you can redistribute it and / or modify
2010-12-08 13:59:16 +00:00
# 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.
2010-01-20 14:28:28 +00:00
#
# 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
2010-12-08 13:59:16 +00:00
# GNU Affero General Public License for more details.
2010-01-20 14:28:28 +00:00
#
2010-12-08 13:59:16 +00:00
# You should have received a copy of the GNU Affero General Public License
2013-10-31 15:45:33 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2010-01-20 14:28:28 +00:00
#
##############################################################################
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
from openerp . tools . translate import _
2013-11-29 16:10:32 +00:00
from openerp . tools import DEFAULT_SERVER_DATETIME_FORMAT as DF
2014-03-19 14:29:52 +00:00
from openerp . addons . website . models . website import slug
2013-11-29 08:49:14 +00:00
from urlparse import urljoin
2014-04-03 10:46:37 +00:00
from itertools import product
from collections import Counter
2014-06-18 15:08:08 +00:00
from collections import OrderedDict
2013-11-29 08:49:14 +00:00
2013-11-29 16:10:32 +00:00
import datetime
2013-11-29 08:49:14 +00:00
import logging
2013-11-29 16:10:32 +00:00
import re
import uuid
2013-11-29 08:49:14 +00:00
_logger = logging . getLogger ( __name__ )
2013-10-21 09:03:19 +00:00
2014-04-17 16:03:06 +00:00
class survey_stage ( osv . Model ) :
""" Stages for Kanban view of surveys """
_name = ' survey.stage '
_description = ' Survey Stage '
2014-06-12 12:42:46 +00:00
_order = ' sequence,id '
2014-04-17 16:03:06 +00:00
_columns = {
' name ' : fields . char ( string = " Name " , required = True , translate = True ) ,
' sequence ' : fields . integer ( string = " Sequence " ) ,
' closed ' : fields . boolean ( string = " Closed " , help = " If closed, people won ' t be able to answer to surveys in this column. " ) ,
' fold ' : fields . boolean ( string = " Folded in kanban view " )
}
_defaults = {
' sequence ' : 1 ,
' closed ' : False
}
_sql_constraints = [
( ' positive_sequence ' , ' CHECK(sequence >= 0) ' , ' Sequence number MUST be a natural ' )
]
2010-01-27 13:55:23 +00:00
2014-01-20 12:30:38 +00:00
class survey_survey ( osv . Model ) :
2013-10-22 15:10:51 +00:00
''' Settings for a multi-page/multi-question survey.
2013-11-19 08:28:50 +00:00
Each survey can have one or more attached pages , and each page can display
2013-10-22 15:10:51 +00:00
one or more questions .
'''
_name = ' survey.survey '
2010-01-20 14:28:28 +00:00
_description = ' Survey '
_rec_name = ' title '
2013-10-21 09:03:19 +00:00
_inherit = [ ' mail.thread ' , ' ir.needaction_mixin ' ]
2010-01-27 13:55:23 +00:00
2013-10-22 15:10:51 +00:00
# Protected methods #
2013-11-19 08:28:50 +00:00
def _has_questions ( self , cr , uid , ids , context = None ) :
2013-10-25 09:56:54 +00:00
""" Ensure that this survey has at least one page with at least one
2013-11-20 15:13:01 +00:00
question . """
2013-10-28 16:15:31 +00:00
for survey in self . browse ( cr , uid , ids , context = context ) :
2013-10-25 09:56:54 +00:00
if not survey . page_ids or not [ page . question_ids
for page in survey . page_ids if page . question_ids ] :
2013-11-19 08:28:50 +00:00
return False
return True
2013-10-24 09:26:33 +00:00
## Function fields ##
2014-03-25 08:51:55 +00:00
def _is_designed ( self , cr , uid , ids , name , arg , context = None ) :
res = dict ( )
for survey in self . browse ( cr , uid , ids , context = context ) :
if not survey . page_ids or not [ page . question_ids
for page in survey . page_ids if page . question_ids ] :
res [ survey . id ] = False
else :
res [ survey . id ] = True
return res
2014-01-21 16:03:54 +00:00
def _get_tot_sent_survey ( self , cr , uid , ids , name , arg , context = None ) :
""" Returns the number of invitations sent for this survey, be they
( partially ) completed or not """
res = dict ( ( id , 0 ) for id in ids )
sur_res_obj = self . pool . get ( ' survey.user_input ' )
for id in ids :
res [ id ] = sur_res_obj . search ( cr , uid , # SUPERUSER_ID,
[ ( ' survey_id ' , ' = ' , id ) , ( ' type ' , ' = ' , ' link ' ) ] ,
context = context , count = True )
return res
2013-10-21 09:03:19 +00:00
def _get_tot_start_survey ( self , cr , uid , ids , name , arg , context = None ) :
2013-10-23 09:03:42 +00:00
""" Returns the number of started instances of this survey, be they
completed or not """
2013-10-21 09:03:19 +00:00
res = dict ( ( id , 0 ) for id in ids )
2013-10-28 07:51:21 +00:00
sur_res_obj = self . pool . get ( ' survey.user_input ' )
2013-10-21 09:03:19 +00:00
for id in ids :
2013-10-28 16:15:31 +00:00
res [ id ] = sur_res_obj . search ( cr , uid , # SUPERUSER_ID,
2014-01-21 16:03:54 +00:00
[ ' & ' , ( ' survey_id ' , ' = ' , id ) , ' | ' , ( ' state ' , ' = ' , ' skip ' ) , ( ' state ' , ' = ' , ' done ' ) ] ,
2013-10-28 07:51:21 +00:00
context = context , count = True )
2013-10-21 09:03:19 +00:00
return res
def _get_tot_comp_survey ( self , cr , uid , ids , name , arg , context = None ) :
2013-10-23 09:03:42 +00:00
""" Returns the number of completed instances of this survey """
2013-10-21 09:03:19 +00:00
res = dict ( ( id , 0 ) for id in ids )
2013-10-28 07:51:21 +00:00
sur_res_obj = self . pool . get ( ' survey.user_input ' )
2013-10-21 09:03:19 +00:00
for id in ids :
2013-10-28 16:15:31 +00:00
res [ id ] = sur_res_obj . search ( cr , uid , # SUPERUSER_ID,
2013-10-28 07:51:21 +00:00
[ ( ' survey_id ' , ' = ' , id ) , ( ' state ' , ' = ' , ' done ' ) ] ,
context = context , count = True )
2013-10-21 09:03:19 +00:00
return res
def _get_public_url ( self , cr , uid , ids , name , arg , context = None ) :
2013-10-23 09:03:42 +00:00
""" Computes a public URL for the survey """
2014-08-07 14:56:40 +00:00
if context and context . get ( ' relative_url ' ) :
base_url = ' / '
else :
base_url = self . pool [ ' ir.config_parameter ' ] . get_param ( cr , uid , ' web.base.url ' )
2014-03-19 14:29:52 +00:00
res = { }
for survey in self . browse ( cr , uid , ids , context = context ) :
res [ survey . id ] = urljoin ( base_url , " survey/start/ %s " % slug ( survey ) )
return res
2014-01-28 12:47:07 +00:00
2014-04-07 13:31:40 +00:00
def _get_public_url_html ( self , cr , uid , ids , name , arg , context = None ) :
""" Computes a public URL for the survey (html-embeddable version) """
urls = self . _get_public_url ( cr , uid , ids , name , arg , context = context )
for id , url in urls . iteritems ( ) :
urls [ id ] = ' <a href= " %s " > %s </a> ' % ( url , _ ( " Click here to start survey " ) )
return urls
2014-01-28 12:47:07 +00:00
def _get_print_url ( self , cr , uid , ids , name , arg , context = None ) :
""" Computes a printing URL for the survey """
2014-08-07 14:56:40 +00:00
if context and context . get ( ' relative_url ' ) :
base_url = ' / '
else :
base_url = self . pool [ ' ir.config_parameter ' ] . get_param ( cr , uid , ' web.base.url ' )
2014-03-19 14:29:52 +00:00
res = { }
for survey in self . browse ( cr , uid , ids , context = context ) :
res [ survey . id ] = urljoin ( base_url , " survey/print/ %s " % slug ( survey ) )
return res
2014-01-28 12:47:07 +00:00
def _get_result_url ( self , cr , uid , ids , name , arg , context = None ) :
""" Computes an URL for the survey results """
2014-08-07 14:56:40 +00:00
if context and context . get ( ' relative_url ' ) :
base_url = ' / '
else :
base_url = self . pool [ ' ir.config_parameter ' ] . get_param ( cr , uid , ' web.base.url ' )
2014-03-19 14:29:52 +00:00
res = { }
for survey in self . browse ( cr , uid , ids , context = context ) :
res [ survey . id ] = urljoin ( base_url , " survey/results/ %s " % slug ( survey ) )
return res
2010-01-27 13:55:23 +00:00
2013-10-22 15:10:51 +00:00
# Model fields #
2010-01-20 14:28:28 +00:00
_columns = {
2014-01-21 11:04:15 +00:00
' title ' : fields . char ( ' Title ' , required = 1 , translate = True ) ,
2013-11-26 12:53:03 +00:00
' res_model ' : fields . char ( ' Category ' ) ,
2014-07-06 14:44:26 +00:00
' page_ids ' : fields . one2many ( ' survey.page ' , ' survey_id ' , ' Pages ' , copy = True ) ,
' stage_id ' : fields . many2one ( ' survey.stage ' , string = " Stage " , ondelete = " set null " , copy = False ) ,
2013-10-23 09:03:42 +00:00
' auth_required ' : fields . boolean ( ' Login required ' ,
2013-11-20 15:13:01 +00:00
help = " Users with a public link will be requested to login before taking part to the survey " ,
oldname = " authenticate " ) ,
' users_can_go_back ' : fields . boolean ( ' Users can go back ' ,
help = " If checked, users can go back to previous pages. " ) ,
2014-01-21 16:03:54 +00:00
' tot_sent_survey ' : fields . function ( _get_tot_sent_survey ,
string = " Number of sent surveys " , type = " integer " ) ,
2013-10-23 09:03:42 +00:00
' tot_start_survey ' : fields . function ( _get_tot_start_survey ,
string = " Number of started surveys " , type = " integer " ) ,
' tot_comp_survey ' : fields . function ( _get_tot_comp_survey ,
string = " Number of completed surveys " , type = " integer " ) ,
2013-11-15 07:51:50 +00:00
' description ' : fields . html ( ' Description ' , translate = True ,
oldname = " description " , help = " A long description of the purpose of the survey " ) ,
2012-07-05 12:43:05 +00:00
' color ' : fields . integer ( ' Color Index ' ) ,
2013-10-23 09:03:42 +00:00
' user_input_ids ' : fields . one2many ( ' survey.user_input ' , ' survey_id ' ,
2013-11-20 16:15:19 +00:00
' User responses ' , readonly = 1 ) ,
2014-03-25 08:51:55 +00:00
' designed ' : fields . function ( _is_designed , string = " Is designed? " ,
type = " boolean " ) ,
2013-10-23 09:03:42 +00:00
' public_url ' : fields . function ( _get_public_url ,
2013-11-25 08:04:46 +00:00
string = " Public link " , type = " char " ) ,
2014-04-07 13:31:40 +00:00
' public_url_html ' : fields . function ( _get_public_url_html ,
string = " Public link (html version) " , type = " char " ) ,
2014-01-28 12:47:07 +00:00
' print_url ' : fields . function ( _get_print_url ,
string = " Print link " , type = " char " ) ,
' result_url ' : fields . function ( _get_result_url ,
string = " Results link " , type = " char " ) ,
2013-10-23 09:03:42 +00:00
' email_template_id ' : fields . many2one ( ' email.template ' ,
' Email Template ' , ondelete = ' set null ' ) ,
2013-11-15 07:51:50 +00:00
' thank_you_message ' : fields . html ( ' Thank you message ' , translate = True ,
2014-01-31 15:26:40 +00:00
help = " This message will be displayed when survey is completed " ) ,
2015-01-06 11:26:30 +00:00
' quizz_mode ' : fields . boolean ( string = ' Quiz mode ' )
2010-01-20 14:28:28 +00:00
}
2013-11-19 08:28:50 +00:00
2014-04-17 16:03:06 +00:00
def _default_stage ( self , cr , uid , context = None ) :
ids = self . pool [ ' survey.stage ' ] . search ( cr , uid , [ ] , limit = 1 , context = context )
if ids :
return ids [ 0 ]
return False
2010-01-20 14:28:28 +00:00
_defaults = {
2014-04-09 14:59:45 +00:00
' color ' : 0 ,
2014-04-17 16:03:06 +00:00
' stage_id ' : lambda self , * a , * * kw : self . _default_stage ( * a , * * kw )
2010-01-20 14:28:28 +00:00
}
2014-03-21 14:54:34 +00:00
def _read_group_stage_ids ( self , cr , uid , ids , domain , read_group_order = None , access_rights_uid = None , context = None ) :
""" Read group customization in order to display all the stages in the
kanban view , even if they are empty """
stage_obj = self . pool . get ( ' survey.stage ' )
order = stage_obj . _order
access_rights_uid = access_rights_uid or uid
if read_group_order == ' stage_id desc ' :
order = ' %s desc ' % order
stage_ids = stage_obj . _search ( cr , uid , [ ] , order = order , access_rights_uid = access_rights_uid , context = context )
result = stage_obj . name_get ( cr , access_rights_uid , stage_ids , context = context )
# restore order of the search
result . sort ( lambda x , y : cmp ( stage_ids . index ( x [ 0 ] ) , stage_ids . index ( y [ 0 ] ) ) )
fold = { }
for stage in stage_obj . browse ( cr , access_rights_uid , stage_ids , context = context ) :
fold [ stage . id ] = stage . fold or False
return result , fold
_group_by_full = {
' stage_id ' : _read_group_stage_ids
}
2013-11-20 15:13:01 +00:00
# Public methods #
2013-10-25 09:56:54 +00:00
2014-03-25 15:27:36 +00:00
def copy_data ( self , cr , uid , id , default = None , context = None ) :
current_rec = self . read ( cr , uid , id , fields = [ ' title ' ] , context = context )
2012-09-24 16:26:45 +00:00
title = _ ( " %s (copy) " ) % ( current_rec . get ( ' title ' ) )
2014-07-06 14:44:26 +00:00
default = dict ( default or { } , title = title )
return super ( survey_survey , self ) . copy_data ( cr , uid , id , default ,
2013-10-28 16:15:31 +00:00
context = context )
2010-02-05 11:25:40 +00:00
2013-12-12 11:04:35 +00:00
def next_page ( self , cr , uid , user_input , page_id , go_back = False , context = None ) :
''' The next page to display to the user, knowing that page_id is the id
of the last displayed page .
If page_id == 0 , it will always return the first page of the survey .
If all the pages have been displayed and go_back == False , it will
return None
If go_back == True , it will return the * previous * page instead of the
next page .
. . note : :
It is assumed here that a careful user will not try to set go_back
to True if she knows that the page to display is the first one !
( doing this will probably cause a giant worm to eat her house ) '''
survey = user_input . survey_id
pages = list ( enumerate ( survey . page_ids ) )
# First page
if page_id == 0 :
return ( pages [ 0 ] [ 1 ] , 0 , len ( pages ) == 1 )
current_page_index = pages . index ( ( filter ( lambda p : p [ 1 ] . id == page_id , pages ) ) [ 0 ] )
# All the pages have been displayed
if current_page_index == len ( pages ) - 1 and not go_back :
return ( None , - 1 , False )
# Let's get back, baby!
elif go_back and survey . users_can_go_back :
return ( pages [ current_page_index - 1 ] [ 1 ] , current_page_index - 1 , False )
else :
# This will show the last page
if current_page_index == len ( pages ) - 2 :
return ( pages [ current_page_index + 1 ] [ 1 ] , current_page_index + 1 , True )
# This will show a regular page
else :
return ( pages [ current_page_index + 1 ] [ 1 ] , current_page_index + 1 , False )
2014-07-25 11:47:09 +00:00
def filter_input_ids ( self , cr , uid , survey , filters , finished = False , context = None ) :
2014-04-08 15:09:36 +00:00
''' If user applies any filters, then this function returns list of
filtered user_input_id and label ' s strings for display data in web.
: param filters : list of dictionary ( having : row_id , ansewr_id )
: param finished : True for completely filled survey , Falser otherwise .
: returns list of filtered user_input_ids .
'''
2014-06-18 15:08:08 +00:00
context = context if context else { }
2014-04-08 15:09:36 +00:00
if filters :
input_line_obj = self . pool . get ( ' survey.user_input_line ' )
domain_filter , choice , filter_display_data = [ ] , [ ] , [ ]
for filter in filters :
row_id , answer_id = filter [ ' row_id ' ] , filter [ ' answer_id ' ]
if row_id == 0 :
choice . append ( answer_id )
else :
domain_filter . extend ( [ ' | ' , ( ' value_suggested_row.id ' , ' = ' , row_id ) , ( ' value_suggested.id ' , ' = ' , answer_id ) ] )
if choice :
domain_filter . insert ( 0 , ( ' value_suggested.id ' , ' in ' , choice ) )
else :
domain_filter = domain_filter [ 1 : ]
line_ids = input_line_obj . search ( cr , uid , domain_filter , context = context )
filtered_input_ids = [ input . user_input_id . id for input in input_line_obj . browse ( cr , uid , line_ids , context = context ) ]
else :
filtered_input_ids , filter_display_data = [ ] , [ ]
if finished :
user_input = self . pool . get ( ' survey.user_input ' )
if not filtered_input_ids :
2014-07-25 11:47:09 +00:00
current_filters = user_input . search ( cr , uid , [ ( ' survey_id ' , ' = ' , survey . id ) ] , context = context )
2014-04-08 15:09:36 +00:00
user_input_objs = user_input . browse ( cr , uid , current_filters , context = context )
else :
user_input_objs = user_input . browse ( cr , uid , filtered_input_ids , context = context )
return [ input . id for input in user_input_objs if input . state == ' done ' ]
return filtered_input_ids
def get_filter_display_data ( self , cr , uid , filters , context ) :
''' Returns data to display current filters
: param filters : list of dictionary ( having : row_id , answer_id )
: param finished : True for completely filled survey , False otherwise .
: returns list of dict having data to display filters .
'''
filter_display_data = [ ]
if filters :
question_obj = self . pool . get ( ' survey.question ' )
label_obj = self . pool . get ( ' survey.label ' )
for filter in filters :
row_id , answer_id = filter [ ' row_id ' ] , filter [ ' answer_id ' ]
question_id = label_obj . browse ( cr , uid , answer_id , context = context ) . question_id . id
question = question_obj . browse ( cr , uid , question_id , context = context )
if row_id == 0 :
labels = label_obj . browse ( cr , uid , [ answer_id ] , context = context )
else :
labels = label_obj . browse ( cr , uid , [ row_id , answer_id ] , context = context )
filter_display_data . append ( { ' question_text ' : question . question , ' labels ' : [ label . value for label in labels ] } )
return filter_display_data
2014-06-18 15:08:08 +00:00
def prepare_result ( self , cr , uid , question , current_filters = None , context = None ) :
2014-04-08 15:09:36 +00:00
''' Compute statistical data for questions by counting number of vote per choice on basis of filter '''
2014-06-18 15:08:08 +00:00
current_filters = current_filters if current_filters else [ ]
context = context if context else { }
2014-08-27 12:59:03 +00:00
result_summary = { }
2014-06-18 15:08:08 +00:00
2014-04-08 15:09:36 +00:00
#Calculate and return statistics for choice
if question . type in [ ' simple_choice ' , ' multiple_choice ' ] :
2014-08-08 15:04:47 +00:00
answers = { }
comments = [ ]
[ answers . update ( { label . id : { ' text ' : label . value , ' count ' : 0 , ' answer_id ' : label . id } } ) for label in question . labels_ids ]
for input_line in question . user_input_line_ids :
if input_line . answer_type == ' suggestion ' and answers . get ( input_line . value_suggested . id ) and ( not ( current_filters ) or input_line . user_input_id . id in current_filters ) :
answers [ input_line . value_suggested . id ] [ ' count ' ] + = 1
if input_line . answer_type == ' text ' and ( not ( current_filters ) or input_line . user_input_id . id in current_filters ) :
comments . append ( input_line )
2014-07-25 12:48:57 +00:00
result_summary = { ' answers ' : answers . values ( ) , ' comments ' : comments }
2014-04-08 15:09:36 +00:00
#Calculate and return statistics for matrix
if question . type == ' matrix ' :
2014-06-18 15:08:08 +00:00
rows = OrderedDict ( )
answers = OrderedDict ( )
res = dict ( )
2014-07-25 12:48:57 +00:00
comments = [ ]
2014-04-08 15:09:36 +00:00
[ rows . update ( { label . id : label . value } ) for label in question . labels_ids_2 ]
[ answers . update ( { label . id : label . value } ) for label in question . labels_ids ]
for cell in product ( rows . keys ( ) , answers . keys ( ) ) :
res [ cell ] = 0
for input_line in question . user_input_line_ids :
2014-07-01 12:51:00 +00:00
if input_line . answer_type == ' suggestion ' and ( not ( current_filters ) or input_line . user_input_id . id in current_filters ) :
2014-04-08 15:09:36 +00:00
res [ ( input_line . value_suggested_row . id , input_line . value_suggested . id ) ] + = 1
2014-07-25 12:48:57 +00:00
if input_line . answer_type == ' text ' and ( not ( current_filters ) or input_line . user_input_id . id in current_filters ) :
comments . append ( input_line )
result_summary = { ' answers ' : answers , ' rows ' : rows , ' result ' : res , ' comments ' : comments }
2014-04-08 15:09:36 +00:00
#Calculate and return statistics for free_text, textbox, datetime
if question . type in [ ' free_text ' , ' textbox ' , ' datetime ' ] :
result_summary = [ ]
for input_line in question . user_input_line_ids :
if not ( current_filters ) or input_line . user_input_id . id in current_filters :
result_summary . append ( input_line )
#Calculate and return statistics for numerical_box
if question . type == ' numerical_box ' :
result_summary = { ' input_lines ' : [ ] }
all_inputs = [ ]
for input_line in question . user_input_line_ids :
if not ( current_filters ) or input_line . user_input_id . id in current_filters :
all_inputs . append ( input_line . value_number )
result_summary [ ' input_lines ' ] . append ( input_line )
if all_inputs :
result_summary . update ( { ' average ' : round ( sum ( all_inputs ) / len ( all_inputs ) , 2 ) ,
' max ' : round ( max ( all_inputs ) , 2 ) ,
' min ' : round ( min ( all_inputs ) , 2 ) ,
2014-09-03 09:06:50 +00:00
' sum ' : sum ( all_inputs ) ,
2014-04-08 15:09:36 +00:00
' most_comman ' : Counter ( all_inputs ) . most_common ( 5 ) } )
return result_summary
2014-06-18 15:08:08 +00:00
def get_input_summary ( self , cr , uid , question , current_filters = None , context = None ) :
2014-04-08 15:09:36 +00:00
''' Returns overall summary of question e.g. answered, skipped, total_inputs on basis of filter '''
2014-06-18 15:08:08 +00:00
current_filters = current_filters if current_filters else [ ]
context = context if context else { }
2014-04-08 15:09:36 +00:00
result = { }
if question . survey_id . user_input_ids :
total_input_ids = current_filters or [ input_id . id for input_id in question . survey_id . user_input_ids if input_id . state != ' new ' ]
result [ ' total_inputs ' ] = len ( total_input_ids )
question_input_ids = [ ]
for user_input in question . user_input_line_ids :
if not user_input . skipped :
question_input_ids . append ( user_input . user_input_id . id )
result [ ' answered ' ] = len ( set ( question_input_ids ) & set ( total_input_ids ) )
result [ ' skipped ' ] = result [ ' total_inputs ' ] - result [ ' answered ' ]
return result
# Actions
2014-01-28 12:47:07 +00:00
def action_start_survey ( self , cr , uid , ids , context = None ) :
''' Open the website page with the survey form '''
2014-02-07 16:26:15 +00:00
trail = " "
2014-08-07 14:56:40 +00:00
context = dict ( context or { } , relative_url = True )
if ' survey_token ' in context :
2014-03-19 14:29:52 +00:00
trail = " / " + context [ ' survey_token ' ]
2012-10-11 09:36:46 +00:00
return {
2014-01-28 12:47:07 +00:00
' type ' : ' ir.actions.act_url ' ,
' name ' : " Start Survey " ,
' target ' : ' self ' ,
2014-02-07 16:26:15 +00:00
' url ' : self . read ( cr , uid , ids , [ ' public_url ' ] , context = context ) [ 0 ] [ ' public_url ' ] + trail
2012-10-11 09:36:46 +00:00
}
2013-10-25 09:56:54 +00:00
def action_send_survey ( self , cr , uid , ids , context = None ) :
''' Open a window to compose an email, pre-filled with the survey
message '''
2013-11-19 09:16:25 +00:00
if not self . _has_questions ( cr , uid , ids , context = None ) :
2014-01-28 12:47:07 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot send an invitation for a survey that has no questions. ' ) )
2013-10-21 09:03:19 +00:00
2013-10-25 09:56:54 +00:00
survey_browse = self . pool . get ( ' survey.survey ' ) . browse ( cr , uid , ids ,
context = context ) [ 0 ]
2014-04-08 15:06:52 +00:00
if survey_browse . stage_id . closed :
2013-10-25 09:56:54 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) ,
2014-04-08 15:19:59 +00:00
_ ( " You cannot send invitations for closed surveys. " ) )
2013-10-21 09:03:19 +00:00
2013-10-25 09:56:54 +00:00
assert len ( ids ) == 1 , ' This option should only be used for a single \
survey at a time . '
2013-10-21 09:03:19 +00:00
ir_model_data = self . pool . get ( ' ir.model.data ' )
2013-12-30 15:35:32 +00:00
templates = ir_model_data . get_object_reference ( cr , uid ,
' survey ' , ' email_template_survey ' )
template_id = templates [ 1 ] if len ( templates ) > 0 else False
2013-10-21 09:03:19 +00:00
ctx = dict ( context )
2014-01-28 12:47:07 +00:00
ctx . update ( { ' default_model ' : ' survey.survey ' ,
' default_res_id ' : ids [ 0 ] ,
' default_survey_id ' : ids [ 0 ] ,
' default_use_template ' : bool ( template_id ) ,
' default_template_id ' : template_id ,
2014-04-08 15:06:52 +00:00
' default_composition_mode ' : ' comment ' }
2014-01-28 12:47:07 +00:00
)
2012-08-21 05:36:25 +00:00
return {
2013-10-21 09:03:19 +00:00
' type ' : ' ir.actions.act_window ' ,
2012-08-21 05:36:25 +00:00
' view_type ' : ' form ' ,
' view_mode ' : ' form ' ,
2013-10-21 09:03:19 +00:00
' res_model ' : ' survey.mail.compose.message ' ,
2012-08-21 05:36:25 +00:00
' target ' : ' new ' ,
2013-10-21 09:03:19 +00:00
' context ' : ctx ,
2012-04-17 05:50:10 +00:00
}
2012-10-11 09:36:46 +00:00
2014-01-28 12:47:07 +00:00
def action_print_survey ( self , cr , uid , ids , context = None ) :
''' Open the website page with the survey printable view '''
2014-02-07 16:26:15 +00:00
trail = " "
2014-08-07 14:56:40 +00:00
context = dict ( context or { } , relative_url = True )
if ' survey_token ' in context :
2014-03-19 14:29:52 +00:00
trail = " / " + context [ ' survey_token ' ]
2014-01-28 12:47:07 +00:00
return {
' type ' : ' ir.actions.act_url ' ,
' name ' : " Print Survey " ,
' target ' : ' self ' ,
2014-02-07 16:26:15 +00:00
' url ' : self . read ( cr , uid , ids , [ ' print_url ' ] , context = context ) [ 0 ] [ ' print_url ' ] + trail
2014-01-28 12:47:07 +00:00
}
def action_result_survey ( self , cr , uid , ids , context = None ) :
''' Open the website page with the survey results view '''
2014-08-07 14:56:40 +00:00
context = dict ( context or { } , relative_url = True )
2014-01-28 12:47:07 +00:00
return {
' type ' : ' ir.actions.act_url ' ,
' name ' : " Results of the Survey " ,
' target ' : ' self ' ,
' url ' : self . read ( cr , uid , ids , [ ' result_url ' ] , context = context ) [ 0 ] [ ' result_url ' ]
}
def action_test_survey ( self , cr , uid , ids , context = None ) :
''' Open the website page with the survey form into test mode '''
2014-08-07 14:56:40 +00:00
context = dict ( context or { } , relative_url = True )
2014-01-28 12:47:07 +00:00
return {
' type ' : ' ir.actions.act_url ' ,
' name ' : " Results of the Survey " ,
' target ' : ' self ' ,
2014-03-19 14:29:52 +00:00
' url ' : self . read ( cr , uid , ids , [ ' public_url ' ] , context = context ) [ 0 ] [ ' public_url ' ] + " /phantom "
2014-01-28 12:47:07 +00:00
}
2013-11-20 15:13:01 +00:00
2010-01-20 14:28:28 +00:00
2014-03-20 15:33:51 +00:00
2014-01-20 12:30:38 +00:00
class survey_page ( osv . Model ) :
2013-10-23 12:22:55 +00:00
''' A page for a survey.
Pages are essentially containers , allowing to group questions by ordered
screens .
2013-10-28 07:51:21 +00:00
. . note : :
2013-10-28 14:43:14 +00:00
A page should be deleted if the survey it belongs to is deleted . '''
2013-10-23 12:22:55 +00:00
2010-01-20 14:28:28 +00:00
_name = ' survey.page '
2013-10-23 12:22:55 +00:00
_description = ' Survey Page '
2010-01-20 14:28:28 +00:00
_rec_name = ' title '
2014-06-12 12:42:46 +00:00
_order = ' sequence,id '
2013-10-23 12:22:55 +00:00
# Model Fields #
2010-01-20 14:28:28 +00:00
_columns = {
2014-01-21 11:04:15 +00:00
' title ' : fields . char ( ' Page Title ' , required = 1 ,
2013-10-23 12:22:55 +00:00
translate = True ) ,
' survey_id ' : fields . many2one ( ' survey.survey ' , ' Survey ' ,
2014-01-02 13:23:57 +00:00
ondelete = ' cascade ' , required = True ) ,
2013-10-23 12:22:55 +00:00
' question_ids ' : fields . one2many ( ' survey.question ' , ' page_id ' ,
2014-07-06 14:44:26 +00:00
' Questions ' , copy = True ) ,
2013-10-23 12:22:55 +00:00
' sequence ' : fields . integer ( ' Page number ' ) ,
2013-11-15 07:51:50 +00:00
' description ' : fields . html ( ' Description ' ,
2013-10-23 12:22:55 +00:00
help = " An introductory text to your page " , translate = True ,
oldname = " note " ) ,
2010-01-20 14:28:28 +00:00
}
2013-12-12 11:04:35 +00:00
_defaults = {
' sequence ' : 10
}
2010-01-20 14:28:28 +00:00
2013-10-23 12:22:55 +00:00
# Public methods #
2014-03-25 15:27:36 +00:00
def copy_data ( self , cr , uid , ids , default = None , context = None ) :
current_rec = self . read ( cr , uid , ids , fields = [ ' title ' ] , context = context )
2012-09-24 16:26:45 +00:00
title = _ ( " %s (copy) " ) % ( current_rec . get ( ' title ' ) )
2014-07-06 14:44:26 +00:00
default = dict ( default or { } , title = title )
return super ( survey_page , self ) . copy_data ( cr , uid , ids , default ,
2013-10-23 12:22:55 +00:00
context = context )
2010-02-05 11:25:40 +00:00
2010-01-20 14:28:28 +00:00
2014-01-20 12:30:38 +00:00
class survey_question ( osv . Model ) :
2013-10-23 12:22:55 +00:00
''' Questions that will be asked in a survey.
2013-10-28 14:43:14 +00:00
Each question can have one of more suggested answers ( eg . in case of
2013-11-06 15:24:06 +00:00
dropdown choices , multi - answer checkboxes , radio buttons . . . ) . '''
2010-01-20 14:28:28 +00:00
_name = ' survey.question '
2013-11-19 13:28:18 +00:00
_description = ' Survey Question '
2010-01-20 14:28:28 +00:00
_rec_name = ' question '
2014-06-12 12:42:46 +00:00
_order = ' sequence,id '
2010-01-20 14:28:28 +00:00
2013-10-23 12:22:55 +00:00
# Model fields #
2010-01-20 14:28:28 +00:00
_columns = {
2013-10-24 09:26:33 +00:00
# Question metadata
2013-10-23 12:22:55 +00:00
' page_id ' : fields . many2one ( ' survey.page ' , ' Survey page ' ,
2014-04-09 09:10:09 +00:00
ondelete = ' cascade ' , required = 1 ) ,
2013-10-23 12:22:55 +00:00
' survey_id ' : fields . related ( ' page_id ' , ' survey_id ' , type = ' many2one ' ,
2014-02-05 09:52:46 +00:00
relation = ' survey.survey ' , string = ' Survey ' ) ,
2013-11-05 15:28:21 +00:00
' sequence ' : fields . integer ( string = ' Sequence ' ) ,
2013-10-23 12:22:55 +00:00
2013-10-24 09:26:33 +00:00
# Question
2014-04-07 13:31:40 +00:00
' question ' : fields . char ( ' Question Name ' , required = 1 , translate = True ) ,
2014-02-05 08:16:26 +00:00
' description ' : fields . html ( ' Description ' , help = " Use this field to add \
2013-10-29 15:17:31 +00:00
additional explanations about your question " , translate=True,
oldname = ' descriptive_text ' ) ,
2013-10-23 12:22:55 +00:00
2013-10-24 09:26:33 +00:00
# Answer
2014-04-07 13:31:40 +00:00
' type ' : fields . selection ( [ ( ' free_text ' , ' Long Text Zone ' ) ,
( ' textbox ' , ' Text Input ' ) ,
( ' numerical_box ' , ' Numerical Value ' ) ,
2013-10-23 12:22:55 +00:00
( ' datetime ' , ' Date and Time ' ) ,
2014-04-07 13:31:40 +00:00
( ' simple_choice ' , ' Multiple choice: only one answer ' ) ,
( ' multiple_choice ' , ' Multiple choice: multiple answers allowed ' ) ,
2014-05-21 09:52:05 +00:00
( ' matrix ' , ' Matrix ' ) ] , ' Type of Question ' , size = 15 , required = 1 ) ,
2014-04-07 13:31:40 +00:00
' matrix_subtype ' : fields . selection ( [ ( ' simple ' , ' One choice per row ' ) ,
( ' multiple ' , ' Multiple choices per row ' ) ] , ' Matrix Type ' ) ,
2013-11-15 15:14:35 +00:00
' labels_ids ' : fields . one2many ( ' survey.label ' ,
2014-07-06 14:44:26 +00:00
' question_id ' , ' Types of answers ' , oldname = ' answer_choice_ids ' , copy = True ) ,
2013-11-26 12:53:03 +00:00
' labels_ids_2 ' : fields . one2many ( ' survey.label ' ,
2014-07-06 14:44:26 +00:00
' question_id_2 ' , ' Rows of the Matrix ' , copy = True ) ,
2013-12-06 16:10:56 +00:00
# labels are used for proposed choices
# if question.type == simple choice | multiple choice
# -> only labels_ids is used
# if question.type == matrix
# -> labels_ids are the columns of the matrix
# -> labels_ids_2 are the rows of the matrix
2013-11-15 15:14:35 +00:00
# Display options
2014-04-07 13:31:40 +00:00
' column_nb ' : fields . selection ( [ ( ' 12 ' , ' 1 ' ) ,
( ' 6 ' , ' 2 ' ) ,
( ' 4 ' , ' 3 ' ) ,
( ' 3 ' , ' 4 ' ) ,
( ' 2 ' , ' 6 ' ) ] ,
2013-11-26 12:53:03 +00:00
' Number of columns ' ) ,
2014-04-09 09:10:09 +00:00
# These options refer to col-xx-[12|6|4|3|2] classes in Bootstrap
2014-04-07 13:31:40 +00:00
' display_mode ' : fields . selection ( [ ( ' columns ' , ' Radio Buttons/Checkboxes ' ) ,
( ' dropdown ' , ' Selection Box ' ) ] ,
2013-11-29 08:49:14 +00:00
' Display mode ' ) ,
2013-11-15 15:14:35 +00:00
2013-10-24 14:56:39 +00:00
# Comments
2014-04-07 13:31:40 +00:00
' comments_allowed ' : fields . boolean ( ' Show Comments Field ' ,
2013-10-24 14:56:39 +00:00
oldname = " allow_comment " ) ,
2014-04-07 13:31:40 +00:00
' comments_message ' : fields . char ( ' Comment Message ' , translate = True ) ,
' comment_count_as_answer ' : fields . boolean ( ' Comment Field is an Answer Choice ' ,
2013-11-15 15:14:35 +00:00
oldname = ' make_comment_field ' ) ,
2013-10-24 14:56:39 +00:00
# Validation
' validation_required ' : fields . boolean ( ' Validate entry ' ,
oldname = ' is_validation_require ' ) ,
2014-04-08 14:39:12 +00:00
' validation_email ' : fields . boolean ( ' Input must be an email ' ) ,
' validation_length_min ' : fields . integer ( ' Minimum Text Length ' ) ,
' validation_length_max ' : fields . integer ( ' Maximum Text Length ' ) ,
2013-10-31 08:08:56 +00:00
' validation_min_float_value ' : fields . float ( ' Minimum value ' ) ,
' validation_max_float_value ' : fields . float ( ' Maximum value ' ) ,
2014-04-08 14:39:12 +00:00
' validation_min_date ' : fields . datetime ( ' Minimum Date ' ) ,
' validation_max_date ' : fields . datetime ( ' Maximum Date ' ) ,
2013-11-29 08:49:14 +00:00
' validation_error_msg ' : fields . char ( ' Error message ' ,
oldname = ' validation_valid_err_msg ' ,
translate = True ) ,
2013-10-23 12:22:55 +00:00
2013-12-06 09:17:44 +00:00
# Constraints on number of answers (matrices)
2014-04-07 13:31:40 +00:00
' constr_mandatory ' : fields . boolean ( ' Mandatory Answer ' ,
2013-10-24 09:26:33 +00:00
oldname = " is_require_answer " ) ,
2013-11-15 15:14:35 +00:00
' constr_error_msg ' : fields . char ( " Error message " ,
2014-04-07 13:31:40 +00:00
oldname = ' req_error_msg ' , translate = True ) ,
2014-01-23 11:21:07 +00:00
' user_input_line_ids ' : fields . one2many ( ' survey.user_input_line ' ,
2014-03-03 13:05:44 +00:00
' question_id ' , ' Answers ' ,
domain = [ ( ' skipped ' , ' = ' , False ) ] ) ,
2010-01-20 14:28:28 +00:00
}
2014-04-09 09:10:09 +00:00
2010-01-20 14:28:28 +00:00
_defaults = {
2014-04-07 13:31:40 +00:00
' page_id ' : lambda self , cr , uid , context : context . get ( ' page_id ' ) ,
2013-12-12 11:04:35 +00:00
' sequence ' : 10 ,
2013-10-31 14:05:45 +00:00
' type ' : ' free_text ' ,
2013-11-26 12:53:03 +00:00
' matrix_subtype ' : ' simple ' ,
2013-11-25 08:04:46 +00:00
' column_nb ' : ' 12 ' ,
2014-04-07 13:31:40 +00:00
' display_mode ' : ' columns ' ,
2014-04-08 15:25:26 +00:00
' constr_error_msg ' : lambda s , cr , uid , c : _ ( ' This question requires an answer. ' ) ,
2013-11-15 15:14:35 +00:00
' validation_error_msg ' : lambda s , cr , uid , c : _ ( ' The answer you entered has an invalid format. ' ) ,
2013-11-18 16:12:59 +00:00
' validation_required ' : False ,
2014-04-07 13:31:40 +00:00
' comments_message ' : lambda s , cr , uid , c : _ ( ' If other, precise: ' ) ,
2010-01-20 14:28:28 +00:00
}
2014-04-09 09:10:09 +00:00
2013-11-15 15:14:35 +00:00
_sql_constraints = [
( ' positive_len_min ' , ' CHECK (validation_length_min >= 0) ' , ' A length must be positive! ' ) ,
( ' positive_len_max ' , ' CHECK (validation_length_max >= 0) ' , ' A length must be positive! ' ) ,
( ' validation_length ' , ' CHECK (validation_length_min <= validation_length_max) ' , ' Max length cannot be smaller than min length! ' ) ,
( ' validation_float ' , ' CHECK (validation_min_float_value <= validation_max_float_value) ' , ' Max value cannot be smaller than min value! ' ) ,
2014-04-08 14:51:18 +00:00
( ' validation_date ' , ' CHECK (validation_min_date <= validation_max_date) ' , ' Max date cannot be smaller than min date! ' )
2013-11-15 15:14:35 +00:00
]
2010-01-20 14:28:28 +00:00
2014-03-25 15:27:36 +00:00
def copy_data ( self , cr , uid , ids , default = None , context = None ) :
current_rec = self . read ( cr , uid , ids , context = context )
question = _ ( " %s (copy) " ) % ( current_rec . get ( ' question ' ) )
2014-07-06 14:44:26 +00:00
default = dict ( default or { } , question = question )
return super ( survey_question , self ) . copy_data ( cr , uid , ids , default ,
2014-03-25 08:51:55 +00:00
context = context )
2013-11-29 09:27:32 +00:00
# Validation methods
def validate_question ( self , cr , uid , question , post , answer_tag , context = None ) :
2013-11-29 16:10:32 +00:00
''' Validate question, depending on question type and parameters '''
2013-11-29 09:27:32 +00:00
try :
checker = getattr ( self , ' validate_ ' + question . type )
except AttributeError :
2013-11-29 16:10:32 +00:00
_logger . warning ( question . type + " : This type of question has no validation method " )
2013-11-29 09:27:32 +00:00
return { }
else :
return checker ( cr , uid , question , post , answer_tag , context = context )
def validate_free_text ( self , cr , uid , question , post , answer_tag , context = None ) :
errors = { }
answer = post [ answer_tag ] . strip ( )
# Empty answer to mandatory question
if question . constr_mandatory and not answer :
errors . update ( { answer_tag : question . constr_error_msg } )
return errors
def validate_textbox ( self , cr , uid , question , post , answer_tag , context = None ) :
errors = { }
answer = post [ answer_tag ] . strip ( )
# Empty answer to mandatory question
if question . constr_mandatory and not answer :
errors . update ( { answer_tag : question . constr_error_msg } )
2014-04-09 13:39:09 +00:00
# Email format validation
# Note: this validation is very basic:
# all the strings of the form
# <something>@<anything>.<extension>
# will be accepted
if answer and question . validation_email :
if not re . match ( r " [^@]+@[^@]+ \ .[^@]+ " , answer ) :
errors . update ( { answer_tag : _ ( ' This answer must be an email address ' ) } )
2013-11-29 09:27:32 +00:00
# Answer validation (if properly defined)
2014-04-09 13:39:09 +00:00
# Length of the answer must be in a range
if answer and question . validation_required :
if not ( question . validation_length_min < = len ( answer ) < = question . validation_length_max ) :
errors . update ( { answer_tag : question . validation_error_msg } )
2013-11-29 09:27:32 +00:00
return errors
def validate_numerical_box ( self , cr , uid , question , post , answer_tag , context = None ) :
errors = { }
answer = post [ answer_tag ] . strip ( )
# Empty answer to mandatory question
if question . constr_mandatory and not answer :
errors . update ( { answer_tag : question . constr_error_msg } )
# Checks if user input is a number
if answer :
try :
2014-04-09 13:39:09 +00:00
floatanswer = float ( answer )
2013-11-29 09:27:32 +00:00
except ValueError :
2014-04-09 13:39:09 +00:00
errors . update ( { answer_tag : _ ( ' This is not a number ' ) } )
# Answer validation (if properly defined)
2014-04-09 13:56:59 +00:00
if answer and question . validation_required :
2014-04-09 13:39:09 +00:00
# Answer is not in the right range
2014-04-09 13:56:59 +00:00
try :
floatanswer = float ( answer ) # check that it is a float has been done hereunder
if not ( question . validation_min_float_value < = floatanswer < = question . validation_max_float_value ) :
errors . update ( { answer_tag : question . validation_error_msg } )
except ValueError :
pass
2013-11-29 09:27:32 +00:00
return errors
def validate_datetime ( self , cr , uid , question , post , answer_tag , context = None ) :
errors = { }
answer = post [ answer_tag ] . strip ( )
# Empty answer to mandatory question
if question . constr_mandatory and not answer :
errors . update ( { answer_tag : question . constr_error_msg } )
# Checks if user input is a datetime
2014-04-09 13:39:09 +00:00
if answer :
try :
2014-04-09 13:56:59 +00:00
dateanswer = datetime . datetime . strptime ( answer , DF )
2014-04-09 13:39:09 +00:00
except ValueError :
errors . update ( { answer_tag : _ ( ' This is not a date/time ' ) } )
2014-04-09 13:56:59 +00:00
return errors
2014-04-09 13:39:09 +00:00
# Answer validation (if properly defined)
2014-04-09 13:56:59 +00:00
if answer and question . validation_required :
2014-04-09 13:39:09 +00:00
# Answer is not in the right range
2014-04-09 13:56:59 +00:00
try :
dateanswer = datetime . datetime . strptime ( answer , DF )
if not ( datetime . datetime . strptime ( question . validation_min_date , DF ) < = dateanswer < = datetime . datetime . strptime ( question . validation_max_date , DF ) ) :
errors . update ( { answer_tag : question . validation_error_msg } )
except ValueError : # check that it is a datetime has been done hereunder
pass
2013-11-29 09:27:32 +00:00
return errors
def validate_simple_choice ( self , cr , uid , question , post , answer_tag , context = None ) :
errors = { }
if question . comments_allowed :
2014-04-09 13:39:09 +00:00
comment_tag = " %s _ %s " % ( answer_tag , ' comment ' )
2013-11-29 09:27:32 +00:00
# Empty answer to mandatory question
if question . constr_mandatory and not answer_tag in post :
errors . update ( { answer_tag : question . constr_error_msg } )
if question . constr_mandatory and answer_tag in post and post [ answer_tag ] . strip ( ) == ' ' :
errors . update ( { answer_tag : question . constr_error_msg } )
# Answer is a comment and is empty
if question . constr_mandatory and answer_tag in post and post [ answer_tag ] == " -1 " and question . comment_count_as_answer and comment_tag in post and not post [ comment_tag ] . strip ( ) :
errors . update ( { answer_tag : question . constr_error_msg } )
return errors
2013-12-09 12:25:15 +00:00
def validate_multiple_choice ( self , cr , uid , question , post , answer_tag , context = None ) :
errors = { }
if question . constr_mandatory :
answer_candidates = dict_keys_startswith ( post , answer_tag )
comment_flag = answer_candidates . pop ( ( " %s _ %s " % ( answer_tag , - 1 ) ) , None )
2014-01-22 09:45:16 +00:00
if question . comments_allowed :
2014-04-09 13:39:09 +00:00
comment_answer = answer_candidates . pop ( ( " %s _ %s " % ( answer_tag , ' comment ' ) ) , ' ' ) . strip ( )
2013-12-09 12:25:15 +00:00
# There is no answer neither comments (if comments count as answer)
2014-04-09 14:13:07 +00:00
if not answer_candidates and question . comment_count_as_answer and ( not comment_flag or not comment_answer ) :
2013-12-09 12:25:15 +00:00
errors . update ( { answer_tag : question . constr_error_msg } )
# There is no answer at all
2014-04-09 14:13:07 +00:00
if not answer_candidates and not question . comment_count_as_answer :
2013-12-09 12:25:15 +00:00
errors . update ( { answer_tag : question . constr_error_msg } )
return errors
2013-11-29 09:27:32 +00:00
2013-12-06 16:10:56 +00:00
def validate_matrix ( self , cr , uid , question , post , answer_tag , context = None ) :
errors = { }
if question . constr_mandatory :
lines_number = len ( question . labels_ids_2 )
2013-12-09 12:25:15 +00:00
answer_candidates = dict_keys_startswith ( post , answer_tag )
2014-04-09 14:42:24 +00:00
comment_answer = answer_candidates . pop ( ( " %s _ %s " % ( answer_tag , ' comment ' ) ) , ' ' ) . strip ( )
2013-12-09 12:25:15 +00:00
# Number of lines that have been answered
2013-12-06 16:10:56 +00:00
if question . matrix_subtype == ' simple ' :
2013-12-09 12:25:15 +00:00
answer_number = len ( answer_candidates )
elif question . matrix_subtype == ' multiple ' :
answer_number = len ( set ( [ sk . rsplit ( ' _ ' , 1 ) [ 0 ] for sk in answer_candidates . keys ( ) ] ) )
else :
raise RuntimeError ( " Invalid matrix subtype " )
2014-04-09 14:42:24 +00:00
# Validate that each line has been answered
if answer_number != lines_number :
errors . update ( { answer_tag : question . constr_error_msg } )
2013-12-19 08:49:00 +00:00
return errors
2013-11-29 09:27:32 +00:00
2010-01-20 14:28:28 +00:00
2014-01-20 12:30:38 +00:00
class survey_label ( osv . Model ) :
2013-10-28 14:43:14 +00:00
''' A suggested answer for a question '''
2013-11-15 15:14:35 +00:00
_name = ' survey.label '
2013-10-28 14:43:14 +00:00
_rec_name = ' value '
2014-06-12 12:42:46 +00:00
_order = ' sequence,id '
2013-11-19 13:28:18 +00:00
_description = ' Survey Label '
2013-10-28 14:43:14 +00:00
2014-04-09 09:10:09 +00:00
def _check_question_not_empty ( self , cr , uid , ids , context = None ) :
''' Ensure that field question_id XOR field question_id_2 is not null '''
for label in self . browse ( cr , uid , ids , context = context ) :
# 'bool()' is required in order to make '!=' act as XOR with objects
return bool ( label . question_id ) != bool ( label . question_id_2 )
2013-10-28 14:43:14 +00:00
_columns = {
' question_id ' : fields . many2one ( ' survey.question ' , ' Question ' ,
2013-11-26 12:53:03 +00:00
ondelete = ' cascade ' ) ,
' question_id_2 ' : fields . many2one ( ' survey.question ' , ' Question ' ,
ondelete = ' cascade ' ) ,
' sequence ' : fields . integer ( ' Label Sequence order ' ) ,
2013-11-21 11:04:57 +00:00
' value ' : fields . char ( " Suggested value " , translate = True ,
2014-01-06 07:48:38 +00:00
required = True ) ,
2014-04-15 09:14:46 +00:00
' quizz_mark ' : fields . float ( ' Score for this answer ' , help = " A positive score indicates a correct answer; a negative or null score indicates a wrong answer " ) ,
2013-10-28 14:43:14 +00:00
}
2014-06-12 12:42:46 +00:00
_defaults = {
' sequence ' : 10 ,
2013-12-12 11:04:35 +00:00
}
2014-04-09 09:10:09 +00:00
_constraints = [
( _check_question_not_empty , " A label must be attached to one and only one question " , [ ' question_id ' , ' question_id_2 ' ] )
]
2013-10-28 14:43:14 +00:00
2014-01-20 12:30:38 +00:00
class survey_user_input ( osv . Model ) :
2013-10-24 09:26:33 +00:00
''' Metadata for a set of one user ' s answers to a particular survey '''
2013-10-23 12:22:55 +00:00
_name = " survey.user_input "
2010-01-20 14:28:28 +00:00
_rec_name = ' date_create '
2013-11-19 13:28:18 +00:00
_description = ' Survey User Input '
2013-10-21 09:03:19 +00:00
2014-01-31 15:26:40 +00:00
def _quizz_get_score ( self , cr , uid , ids , name , args , context = None ) :
ret = dict ( )
for user_input in self . browse ( cr , uid , ids , context = context ) :
2014-02-07 12:57:42 +00:00
ret [ user_input . id ] = sum ( [ uil . quizz_mark for uil in user_input . user_input_line_ids ] or [ 0.0 ] )
2014-01-31 15:26:40 +00:00
return ret
2010-01-20 14:28:28 +00:00
_columns = {
2013-10-28 07:51:21 +00:00
' survey_id ' : fields . many2one ( ' survey.survey ' , ' Survey ' , required = True ,
2013-11-29 08:49:14 +00:00
readonly = 1 , ondelete = ' restrict ' ) ,
2013-10-25 09:56:54 +00:00
' date_create ' : fields . datetime ( ' Creation Date ' , required = True ,
2013-11-29 08:49:14 +00:00
readonly = 1 ) ,
2013-12-12 11:04:35 +00:00
' deadline ' : fields . datetime ( " Deadline " ,
2014-04-09 09:10:09 +00:00
help = " Date by which the person can open the survey and submit answers " ,
2013-11-29 08:49:14 +00:00
oldname = " date_deadline " ) ,
2013-10-24 09:26:33 +00:00
' type ' : fields . selection ( [ ( ' manually ' , ' Manually ' ) , ( ' link ' , ' Link ' ) ] ,
2013-11-29 08:49:14 +00:00
' Answer Type ' , required = 1 , readonly = 1 ,
oldname = " response_type " ) ,
2013-10-24 09:26:33 +00:00
' state ' : fields . selection ( [ ( ' new ' , ' Not started yet ' ) ,
2013-11-29 08:49:14 +00:00
( ' skip ' , ' Partially completed ' ) ,
( ' done ' , ' Completed ' ) ] ,
' Status ' ,
readonly = True ) ,
2013-11-26 15:17:26 +00:00
' test_entry ' : fields . boolean ( ' Test entry ' , readonly = 1 ) ,
' token ' : fields . char ( " Identification token " , readonly = 1 , required = 1 ) ,
2013-10-24 09:26:33 +00:00
# Optional Identification data
2013-10-21 09:03:19 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Partner ' , readonly = 1 ) ,
2013-11-21 11:04:57 +00:00
' email ' : fields . char ( " E-mail " , readonly = 1 ) ,
2013-10-24 09:26:33 +00:00
2013-12-12 11:04:35 +00:00
# Displaying data
' last_displayed_page_id ' : fields . many2one ( ' survey.page ' ,
' Last displayed page ' ) ,
2013-10-24 09:26:33 +00:00
# The answers !
2013-11-28 07:44:18 +00:00
' user_input_line_ids ' : fields . one2many ( ' survey.user_input_line ' ,
2013-11-29 08:49:14 +00:00
' user_input_id ' , ' Answers ' ) ,
2014-01-06 07:48:38 +00:00
2014-01-28 12:47:07 +00:00
# URLs used to display the answers
2014-07-06 14:44:26 +00:00
' result_url ' : fields . related ( ' survey_id ' , ' result_url ' , type = ' char ' ,
string = " Public link to the survey results " ) ,
' print_url ' : fields . related ( ' survey_id ' , ' print_url ' , type = ' char ' ,
string = " Public link to the empty survey " ) ,
2014-01-28 12:47:07 +00:00
2014-01-31 15:26:40 +00:00
' quizz_score ' : fields . function ( _quizz_get_score , type = " float " , string = " Score for the quiz " )
2010-01-28 07:32:59 +00:00
}
_defaults = {
2013-10-29 15:17:31 +00:00
' date_create ' : fields . datetime . now ,
2013-10-24 09:26:33 +00:00
' type ' : ' manually ' ,
' state ' : ' new ' ,
2013-10-29 15:17:31 +00:00
' token ' : lambda s , cr , uid , c : uuid . uuid4 ( ) . __str__ ( ) ,
2014-02-07 12:57:42 +00:00
' quizz_score ' : 0.0 ,
2010-01-20 14:28:28 +00:00
}
[IMP]:survey,crm_hr,hr_evaluation.(Ref-YSA,APA)
-Rename Defination into Definition.
-When you click on test, answer or edit survey, remove the first screen with the selection box to select the survey.
-Add survey Types in demo data: Human Resources, Customer Feeback, Supplier Selection.
-In search view of surveys, remove the vertical separator between draft and open buttons, for type field set widget=selection.
-The button for responsible=my in the search view must be default="1".
-change demo data so that some surveys belongs to admin and some to demo user.
-The browse Response report must be renamed into "Print Answers".
-Browse the answer in the screen instead of printing a PDF.
-In the history one2many, first put the date and then the user.
-I started a survey but did not finished it. so that's normal but I was not able to restart the survey (it says I already answered).
-Removed the "Que: " prefix in each report.
bzr revid: apa@tinyerp.com-20100223093223-vwhgkn8kmdo8e6mo
2010-02-23 09:32:23 +00:00
2013-11-20 16:15:19 +00:00
_sql_constraints = [
2014-04-09 09:10:09 +00:00
( ' unique_token ' , ' UNIQUE (token) ' , ' A token must be unique! ' ) ,
( ' deadline_in_the_past ' , ' CHECK (deadline >= date_create) ' , ' The deadline cannot be in the past ' )
2013-11-20 16:15:19 +00:00
]
2014-03-25 15:27:36 +00:00
def copy_data ( self , cr , uid , id , default = None , context = None ) :
2014-01-28 12:47:07 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You cannot duplicate this \
element ! ' ))
2013-11-29 16:10:32 +00:00
2014-01-28 12:47:07 +00:00
def do_clean_emptys ( self , cr , uid , automatic = False , context = None ) :
2014-04-09 09:10:09 +00:00
''' Remove empty user inputs that have been created manually
( used as a cronjob declared in data / survey_cron . xml ) '''
2013-11-29 08:49:14 +00:00
empty_user_input_ids = self . search ( cr , uid , [ ( ' type ' , ' = ' , ' manually ' ) ,
2013-11-29 16:10:32 +00:00
( ' state ' , ' = ' , ' new ' ) ,
( ' date_create ' , ' < ' , ( datetime . datetime . now ( ) - datetime . timedelta ( hours = 1 ) ) . strftime ( DF ) ) ] ,
2013-11-29 08:49:14 +00:00
context = context )
2013-11-21 11:04:57 +00:00
if empty_user_input_ids :
2013-11-29 08:49:14 +00:00
self . unlink ( cr , uid , empty_user_input_ids , context = context )
2013-10-21 09:03:19 +00:00
def action_survey_resent ( self , cr , uid , ids , context = None ) :
2014-01-28 12:47:07 +00:00
''' Sent again the invitation '''
2013-10-21 09:03:19 +00:00
record = self . browse ( cr , uid , ids [ 0 ] , context = context )
2014-07-06 14:44:26 +00:00
context = dict ( context or { } )
2013-10-21 09:03:19 +00:00
context . update ( {
' survey_resent_token ' : True ,
' default_partner_ids ' : record . partner_id and [ record . partner_id . id ] or [ ] ,
' default_multi_email ' : record . email or " " ,
' default_public ' : ' email_private ' ,
} )
2014-01-28 12:47:07 +00:00
return self . pool . get ( ' survey.survey ' ) . action_send_survey ( cr , uid ,
2013-10-28 16:15:31 +00:00
[ record . survey_id . id ] , context = context )
2013-10-21 09:03:19 +00:00
2014-01-28 12:47:07 +00:00
def action_view_answers ( self , cr , uid , ids , context = None ) :
''' Open the website page with the survey form '''
user_input = self . read ( cr , uid , ids , [ ' print_url ' , ' token ' ] , context = context ) [ 0 ]
return {
' type ' : ' ir.actions.act_url ' ,
' name ' : " View Answers " ,
' target ' : ' self ' ,
2014-03-19 14:29:52 +00:00
' url ' : ' %s / %s ' % ( user_input [ ' print_url ' ] , user_input [ ' token ' ] )
2014-01-28 12:47:07 +00:00
}
def action_survey_results ( self , cr , uid , ids , context = None ) :
''' Open the website page with the survey results '''
return {
' type ' : ' ir.actions.act_url ' ,
' name ' : " Survey Results " ,
' target ' : ' self ' ,
' url ' : self . read ( cr , uid , ids , [ ' result_url ' ] , context = context ) [ 0 ] [ ' result_url ' ]
}
2013-10-23 09:03:42 +00:00
2010-01-20 14:28:28 +00:00
2014-01-20 12:30:38 +00:00
class survey_user_input_line ( osv . Model ) :
2013-11-28 07:44:18 +00:00
_name = ' survey.user_input_line '
2013-11-19 13:28:18 +00:00
_description = ' Survey User Input Line '
2010-01-20 14:28:28 +00:00
_rec_name = ' date_create '
2014-01-31 15:26:40 +00:00
2014-04-09 09:10:09 +00:00
def _answered_or_skipped ( self , cr , uid , ids , context = None ) :
for uil in self . browse ( cr , uid , ids , context = context ) :
# 'bool()' is required in order to make '!=' act as XOR with objects
return uil . skipped != bool ( uil . answer_type )
def _check_answer_type ( self , cr , uid , ids , context = None ) :
for uil in self . browse ( cr , uid , ids , context = None ) :
if uil . answer_type :
if uil . answer_type == ' text ' :
# 'bool()' is required in order to make '!=' act as XOR with objects
return bool ( uil . value_text )
elif uil . answer_type == ' number ' :
2014-04-09 13:56:59 +00:00
return ( uil . value_number == 0 ) or ( uil . value_number != False )
2014-04-09 09:10:09 +00:00
elif uil . answer_type == ' date ' :
return bool ( uil . value_date )
elif uil . answer_type == ' free_text ' :
return bool ( uil . value_free_text )
elif uil . answer_type == ' suggestion ' :
return bool ( uil . value_suggested )
return True
2010-01-20 14:28:28 +00:00
_columns = {
2013-10-24 09:26:33 +00:00
' user_input_id ' : fields . many2one ( ' survey.user_input ' , ' User Input ' ,
2013-11-29 08:49:14 +00:00
ondelete = ' cascade ' , required = 1 ) ,
2013-10-24 09:26:33 +00:00
' question_id ' : fields . many2one ( ' survey.question ' , ' Question ' ,
2014-04-09 09:10:09 +00:00
ondelete = ' restrict ' , required = 1 ) ,
2013-11-25 08:04:46 +00:00
' page_id ' : fields . related ( ' question_id ' , ' page_id ' , type = ' many2one ' ,
2013-11-29 08:49:14 +00:00
relation = ' survey.page ' , string = " Page " ) ,
2013-11-28 07:44:18 +00:00
' survey_id ' : fields . related ( ' user_input_id ' , ' survey_id ' ,
2013-11-29 08:49:14 +00:00
type = " many2one " , relation = " survey.survey " ,
2014-01-28 12:47:07 +00:00
string = ' Survey ' , store = True ) ,
2013-12-12 11:04:35 +00:00
' date_create ' : fields . datetime ( ' Create Date ' , required = 1 ) ,
2013-11-25 08:04:46 +00:00
' skipped ' : fields . boolean ( ' Skipped ' ) ,
2013-11-28 07:44:18 +00:00
' answer_type ' : fields . selection ( [ ( ' text ' , ' Text ' ) ,
2013-11-29 08:49:14 +00:00
( ' number ' , ' Number ' ) ,
( ' date ' , ' Date ' ) ,
( ' free_text ' , ' Free Text ' ) ,
( ' suggestion ' , ' Suggestion ' ) ] ,
' Answer Type ' ) ,
2013-10-28 14:43:14 +00:00
' value_text ' : fields . char ( " Text answer " ) ,
' value_number ' : fields . float ( " Numerical answer " ) ,
' value_date ' : fields . datetime ( " Date answer " ) ,
' value_free_text ' : fields . text ( " Free Text answer " ) ,
2013-12-16 15:32:44 +00:00
' value_suggested ' : fields . many2one ( ' survey.label ' , " Suggested answer " ) ,
' value_suggested_row ' : fields . many2one ( ' survey.label ' , " Row answer " ) ,
2014-01-31 15:26:40 +00:00
' quizz_mark ' : fields . float ( " Score given for this answer " )
2013-10-24 09:26:33 +00:00
}
2014-01-31 15:26:40 +00:00
2013-10-24 09:26:33 +00:00
_defaults = {
' skipped ' : False ,
2013-12-12 11:04:35 +00:00
' date_create ' : fields . datetime . now ( )
2010-01-20 14:28:28 +00:00
}
2014-04-09 09:10:09 +00:00
_constraints = [
( _answered_or_skipped , " A question cannot be unanswered and skipped " , [ ' skipped ' , ' answer_type ' ] ) ,
( _check_answer_type , " The answer must be in the right type " , [ ' answer_type ' , ' text ' , ' number ' , ' date ' , ' free_text ' , ' suggestion ' ] )
]
2010-01-20 14:28:28 +00:00
2014-04-09 14:45:47 +00:00
def __get_mark ( self , cr , uid , value_suggested , context = None ) :
try :
mark = self . pool . get ( ' survey.label ' ) . browse ( cr , uid , int ( value_suggested ) , context = context ) . quizz_mark
2014-07-01 13:56:50 +00:00
except AttributeError :
mark = 0.0
2014-04-09 14:45:47 +00:00
except KeyError :
mark = 0.0
except ValueError :
mark = 0.0
return mark
2014-02-05 08:16:26 +00:00
def create ( self , cr , uid , vals , context = None ) :
value_suggested = vals . get ( ' value_suggested ' )
if value_suggested :
2014-04-09 14:45:47 +00:00
vals . update ( { ' quizz_mark ' : self . __get_mark ( cr , uid , value_suggested ) } )
2014-02-05 08:16:26 +00:00
return super ( survey_user_input_line , self ) . create ( cr , uid , vals , context = context )
2014-01-31 15:26:40 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
value_suggested = vals . get ( ' value_suggested ' )
if value_suggested :
2014-04-09 14:50:09 +00:00
vals . update ( { ' quizz_mark ' : self . __get_mark ( cr , uid , value_suggested ) } )
2014-01-31 15:26:40 +00:00
return super ( survey_user_input_line , self ) . write ( cr , uid , ids , vals , context = context )
2014-03-25 15:27:36 +00:00
def copy_data ( self , cr , uid , id , default = None , context = None ) :
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You cannot duplicate this \
element ! ' ))
2013-11-29 16:10:32 +00:00
def save_lines ( self , cr , uid , user_input_id , question , post , answer_tag ,
context = None ) :
''' Save answers to questions, depending on question type
If an answer already exists for question and user_input_id , it will be
overwritten ( in order to maintain data consistency ) . '''
try :
saver = getattr ( self , ' save_line_ ' + question . type )
except AttributeError :
_logger . error ( question . type + " : This type of question has no saving function " )
return False
else :
saver ( cr , uid , user_input_id , question , post , answer_tag , context = context )
def save_line_free_text ( self , cr , uid , user_input_id , question , post , answer_tag , context = None ) :
vals = {
' user_input_id ' : user_input_id ,
' question_id ' : question . id ,
' page_id ' : question . page_id . id ,
' survey_id ' : question . survey_id . id ,
2014-01-06 07:48:38 +00:00
' skipped ' : False ,
2013-11-29 16:10:32 +00:00
}
2013-12-12 11:04:35 +00:00
if answer_tag in post and post [ answer_tag ] . strip ( ) != ' ' :
2013-11-29 16:10:32 +00:00
vals . update ( { ' answer_type ' : ' free_text ' , ' value_free_text ' : post [ answer_tag ] } )
else :
2013-12-16 15:32:44 +00:00
vals . update ( { ' answer_type ' : None , ' skipped ' : True } )
2013-11-29 16:10:32 +00:00
old_uil = self . search ( cr , uid , [ ( ' user_input_id ' , ' = ' , user_input_id ) ,
( ' survey_id ' , ' = ' , question . survey_id . id ) ,
( ' question_id ' , ' = ' , question . id ) ] ,
context = context )
if old_uil :
self . write ( cr , uid , old_uil [ 0 ] , vals , context = context )
else :
self . create ( cr , uid , vals , context = context )
return True
def save_line_textbox ( self , cr , uid , user_input_id , question , post , answer_tag , context = None ) :
vals = {
' user_input_id ' : user_input_id ,
' question_id ' : question . id ,
' page_id ' : question . page_id . id ,
' survey_id ' : question . survey_id . id ,
2013-12-18 07:20:04 +00:00
' skipped ' : False
2013-11-29 16:10:32 +00:00
}
2013-12-12 11:04:35 +00:00
if answer_tag in post and post [ answer_tag ] . strip ( ) != ' ' :
2013-11-29 16:10:32 +00:00
vals . update ( { ' answer_type ' : ' text ' , ' value_text ' : post [ answer_tag ] } )
else :
2013-12-16 15:32:44 +00:00
vals . update ( { ' answer_type ' : None , ' skipped ' : True } )
2013-11-29 16:10:32 +00:00
old_uil = self . search ( cr , uid , [ ( ' user_input_id ' , ' = ' , user_input_id ) ,
( ' survey_id ' , ' = ' , question . survey_id . id ) ,
( ' question_id ' , ' = ' , question . id ) ] ,
context = context )
if old_uil :
self . write ( cr , uid , old_uil [ 0 ] , vals , context = context )
else :
self . create ( cr , uid , vals , context = context )
return True
def save_line_numerical_box ( self , cr , uid , user_input_id , question , post , answer_tag , context = None ) :
vals = {
' user_input_id ' : user_input_id ,
' question_id ' : question . id ,
' page_id ' : question . page_id . id ,
' survey_id ' : question . survey_id . id ,
2013-12-18 07:20:04 +00:00
' skipped ' : False
2013-11-29 16:10:32 +00:00
}
2013-12-12 11:04:35 +00:00
if answer_tag in post and post [ answer_tag ] . strip ( ) != ' ' :
2013-11-29 16:10:32 +00:00
vals . update ( { ' answer_type ' : ' number ' , ' value_number ' : float ( post [ answer_tag ] ) } )
else :
2013-12-16 15:32:44 +00:00
vals . update ( { ' answer_type ' : None , ' skipped ' : True } )
2013-11-29 16:10:32 +00:00
old_uil = self . search ( cr , uid , [ ( ' user_input_id ' , ' = ' , user_input_id ) ,
( ' survey_id ' , ' = ' , question . survey_id . id ) ,
( ' question_id ' , ' = ' , question . id ) ] ,
context = context )
if old_uil :
self . write ( cr , uid , old_uil [ 0 ] , vals , context = context )
else :
self . create ( cr , uid , vals , context = context )
return True
def save_line_datetime ( self , cr , uid , user_input_id , question , post , answer_tag , context = None ) :
vals = {
' user_input_id ' : user_input_id ,
' question_id ' : question . id ,
' page_id ' : question . page_id . id ,
' survey_id ' : question . survey_id . id ,
2013-12-18 07:20:04 +00:00
' skipped ' : False
2013-11-29 16:10:32 +00:00
}
2013-12-16 07:37:43 +00:00
if answer_tag in post and post [ answer_tag ] . strip ( ) != ' ' :
2013-12-06 16:10:56 +00:00
vals . update ( { ' answer_type ' : ' date ' , ' value_date ' : post [ answer_tag ] } )
else :
2013-12-16 15:32:44 +00:00
vals . update ( { ' answer_type ' : None , ' skipped ' : True } )
2013-12-06 16:10:56 +00:00
old_uil = self . search ( cr , uid , [ ( ' user_input_id ' , ' = ' , user_input_id ) ,
( ' survey_id ' , ' = ' , question . survey_id . id ) ,
( ' question_id ' , ' = ' , question . id ) ] ,
context = context )
if old_uil :
self . write ( cr , uid , old_uil [ 0 ] , vals , context = context )
else :
self . create ( cr , uid , vals , context = context )
return True
2013-12-16 07:37:43 +00:00
def save_line_simple_choice ( self , cr , uid , user_input_id , question , post , answer_tag , context = None ) :
vals = {
' user_input_id ' : user_input_id ,
' question_id ' : question . id ,
' page_id ' : question . page_id . id ,
' survey_id ' : question . survey_id . id ,
2013-12-18 07:20:04 +00:00
' skipped ' : False
2013-12-16 07:37:43 +00:00
}
old_uil = self . search ( cr , uid , [ ( ' user_input_id ' , ' = ' , user_input_id ) ,
( ' survey_id ' , ' = ' , question . survey_id . id ) ,
( ' question_id ' , ' = ' , question . id ) ] ,
context = context )
if old_uil :
2014-04-10 15:01:02 +00:00
self . unlink ( cr , uid , old_uil , context = context )
if answer_tag in post and post [ answer_tag ] . strip ( ) != ' ' :
vals . update ( { ' answer_type ' : ' suggestion ' , ' value_suggested ' : post [ answer_tag ] } )
2013-12-16 07:37:43 +00:00
else :
2014-04-10 15:01:02 +00:00
vals . update ( { ' answer_type ' : None , ' skipped ' : True } )
2014-07-25 10:29:15 +00:00
# '-1' indicates 'comment count as an answer' so do not need to record it
if post . get ( answer_tag ) and post . get ( answer_tag ) != ' -1 ' :
self . create ( cr , uid , vals , context = context )
2014-04-10 15:01:02 +00:00
comment_answer = post . pop ( ( " %s _ %s " % ( answer_tag , ' comment ' ) ) , ' ' ) . strip ( )
if comment_answer :
2014-07-25 10:29:15 +00:00
vals . update ( { ' answer_type ' : ' text ' , ' value_text ' : comment_answer , ' skipped ' : False , ' value_suggested ' : False } )
2013-12-16 07:37:43 +00:00
self . create ( cr , uid , vals , context = context )
2014-04-10 15:01:02 +00:00
2013-12-16 07:37:43 +00:00
return True
2013-11-29 16:10:32 +00:00
2013-12-18 16:14:19 +00:00
def save_line_multiple_choice ( self , cr , uid , user_input_id , question , post , answer_tag , context = None ) :
vals = {
' user_input_id ' : user_input_id ,
' question_id ' : question . id ,
' page_id ' : question . page_id . id ,
' survey_id ' : question . survey_id . id ,
' skipped ' : False
}
old_uil = self . search ( cr , uid , [ ( ' user_input_id ' , ' = ' , user_input_id ) ,
( ' survey_id ' , ' = ' , question . survey_id . id ) ,
( ' question_id ' , ' = ' , question . id ) ] ,
context = context )
if old_uil :
self . unlink ( cr , uid , old_uil , context = context )
ca = dict_keys_startswith ( post , answer_tag )
2014-04-10 14:52:47 +00:00
comment_answer = ca . pop ( ( " %s _ %s " % ( answer_tag , ' comment ' ) ) , ' ' ) . strip ( )
2013-12-18 16:14:19 +00:00
if len ( ca ) > 0 :
for a in ca :
2014-07-25 10:29:15 +00:00
# '-1' indicates 'comment count as an answer' so do not need to record it
if a != ( ' %s _ %s ' % ( answer_tag , ' -1 ' ) ) :
vals . update ( { ' answer_type ' : ' suggestion ' , ' value_suggested ' : ca [ a ] } )
self . create ( cr , uid , vals , context = context )
2014-04-10 14:52:47 +00:00
if comment_answer :
2014-07-25 10:29:15 +00:00
vals . update ( { ' answer_type ' : ' text ' , ' value_text ' : comment_answer , ' value_suggested ' : False } )
2014-04-10 14:52:47 +00:00
self . create ( cr , uid , vals , context = context )
if not ca and not comment_answer :
2013-12-18 16:14:19 +00:00
vals . update ( { ' answer_type ' : None , ' skipped ' : True } )
self . create ( cr , uid , vals , context = context )
return True
2013-11-29 16:10:32 +00:00
2013-12-19 14:00:24 +00:00
def save_line_matrix ( self , cr , uid , user_input_id , question , post , answer_tag , context = None ) :
vals = {
' user_input_id ' : user_input_id ,
' question_id ' : question . id ,
' page_id ' : question . page_id . id ,
' survey_id ' : question . survey_id . id ,
' skipped ' : False
}
old_uil = self . search ( cr , uid , [ ( ' user_input_id ' , ' = ' , user_input_id ) ,
( ' survey_id ' , ' = ' , question . survey_id . id ) ,
( ' question_id ' , ' = ' , question . id ) ] ,
context = context )
if old_uil :
self . unlink ( cr , uid , old_uil , context = context )
no_answers = True
2014-04-10 14:52:47 +00:00
ca = dict_keys_startswith ( post , answer_tag )
comment_answer = ca . pop ( ( " %s _ %s " % ( answer_tag , ' comment ' ) ) , ' ' ) . strip ( )
if comment_answer :
vals . update ( { ' answer_type ' : ' text ' , ' value_text ' : comment_answer } )
self . create ( cr , uid , vals , context = context )
no_answers = False
2013-12-24 08:46:49 +00:00
if question . matrix_subtype == ' simple ' :
2013-12-19 14:00:24 +00:00
for row in question . labels_ids_2 :
2013-12-24 08:46:49 +00:00
a_tag = " %s _ %s " % ( answer_tag , row . id )
2013-12-19 14:00:24 +00:00
if a_tag in ca :
no_answers = False
2013-12-24 08:46:49 +00:00
vals . update ( { ' answer_type ' : ' suggestion ' , ' value_suggested ' : ca [ a_tag ] , ' value_suggested_row ' : row . id } )
2013-12-19 14:00:24 +00:00
self . create ( cr , uid , vals , context = context )
2013-12-24 08:46:49 +00:00
elif question . matrix_subtype == ' multiple ' :
for col in question . labels_ids :
for row in question . labels_ids_2 :
a_tag = " %s _ %s _ %s " % ( answer_tag , row . id , col . id )
if a_tag in ca :
no_answers = False
vals . update ( { ' answer_type ' : ' suggestion ' , ' value_suggested ' : col . id , ' value_suggested_row ' : row . id } )
self . create ( cr , uid , vals , context = context )
2013-12-19 14:00:24 +00:00
if no_answers :
vals . update ( { ' answer_type ' : None , ' skipped ' : True } )
self . create ( cr , uid , vals , context = context )
return True
2013-12-18 07:20:04 +00:00
2013-11-29 16:10:32 +00:00
def dict_keys_startswith ( dictionary , string ) :
2013-11-29 08:49:14 +00:00
''' Returns a dictionary containing the elements of <dict> whose keys start
with < string > .
. . note : :
This function uses dictionary comprehensions ( Python > = 2.7 ) '''
return { k : dictionary [ k ] for k in filter ( lambda key : key . startswith ( string ) , dictionary . keys ( ) ) }