[IMP] survey :- improve code.
[odoo/odoo.git] / addons / survey / survey.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 from osv import osv
24 from osv import fields
25 import tools
26 import netsvc
27 from tools.translate import _
28
29 from time import strftime
30 import datetime
31 import copy
32 from mx.DateTime import *
33 import os
34
35 class survey_type(osv.osv):
36     _name = 'survey.type'
37     _description = 'Survey Type'
38     _columns = {
39         'name' : fields.char("Name", size=128, required=1),
40         'code' : fields.char("Code", size=64),
41     }
42 survey_type()
43
44 class survey(osv.osv):
45     _name = 'survey'
46     _description = 'Survey'
47     _rec_name = 'title'
48
49     def default_get(self, cr, uid, fields, context={}):
50         data = super(survey, self).default_get(cr, uid, fields, context)
51         return data
52
53     _columns = {
54         'title' : fields.char('Survey Title', size=128, required=1),
55         'page_ids' : fields.one2many('survey.page', 'survey_id', 'Page'),
56         'date_open' : fields.datetime('Survey Open Date', readonly=1),
57         'date_close' : fields.datetime('Survey Close Date', readonly=1),
58         'max_response_limit' : fields.integer('Maximum Response Limit'),
59         'response_user' : fields.integer('Maximum Response per User',
60                      help="Set to one if  you require only one response per user"),
61         'state' : fields.selection([('draft', 'Draft'), ('open', 'Open'), ('close', 'Closed'), ('cancel', 'Cancelled')], 'Status', readonly=True),
62         'responsible_id' : fields.many2one('res.users', 'Responsible'),
63         'tot_start_survey' : fields.integer("Total Started Survey", readonly=1),
64         'tot_comp_survey' : fields.integer("Total Completed Survey", readonly=1),
65         'note' : fields.text('Description', size=128),
66         'history' : fields.one2many('survey.history', 'survey_id', 'History Lines', readonly=True),
67         'users': fields.many2many('res.users', 'survey_users_rel', 'sid', 'uid', 'Users'),
68         'send_response' : fields.boolean('E-mail Notification on Response'),
69         'type' : fields.many2one('survey.type', 'Type'),
70     }
71     _defaults = {
72         'state' : lambda * a: "draft",
73         'tot_start_survey' : lambda * a: 0,
74         'tot_comp_survey' : lambda * a: 0,
75         'send_response' : lambda * a: 1,
76         'response_user' : lambda * a:1,
77     }
78
79     def survey_draft(self, cr, uid, ids, arg):
80         self.write(cr, uid, ids, { 'state' : 'draft'})
81         return True
82
83     def survey_open(self, cr, uid, ids, arg):
84         self.write(cr, uid, ids, { 'state' : 'open', 'date_open':strftime("%Y-%m-%d %H:%M:%S")})
85         return True
86
87     def survey_close(self, cr, uid, ids, arg):
88         self.write(cr, uid, ids, { 'state' : 'close', 'date_close':strftime("%Y-%m-%d %H:%M:%S") })
89         return True
90
91     def survey_cancel(self, cr, uid, ids, arg):
92         self.write(cr, uid, ids, { 'state' : 'cancel' })
93         return True
94
95     def copy(self, cr, uid, id, default=None,context={}):
96         raise osv.except_osv(_('Error !'),_('You cannot duplicate the resource!'))
97
98
99 survey()
100
101 class survey_history(osv.osv):
102     _name = 'survey.history'
103     _description = 'Survey History'
104     _rec_name = 'date'
105     _columns = {
106         'survey_id' : fields.many2one('survey', 'Survey'),
107         'user_id' : fields.many2one('res.users', 'User', readonly=True),
108         'date' : fields.datetime('Date started', readonly=1),
109     }
110     _defaults = {
111          'date' : lambda * a: datetime.datetime.now()
112     }
113
114 survey_history()
115
116 class survey_page(osv.osv):
117     _name = 'survey.page'
118     _description = 'Survey Pages'
119     _rec_name = 'title'
120     _order = 'sequence'
121     _columns = {
122         'title' : fields.char('Page Title', size=128, required=1),
123         'survey_id' : fields.many2one('survey', 'Survey', ondelete='cascade'),
124         'question_ids' : fields.one2many('survey.question', 'page_id', 'Question'),
125         'sequence' : fields.integer('Page Nr'),
126         'note' : fields.text('Description'),
127     }
128     _defaults = {
129         'sequence' : lambda * a: 1
130     }
131
132     def default_get(self, cr, uid, fields, context={}):
133         data = super(survey_page, self).default_get(cr, uid, fields, context)
134         if context.get('line_order',False):
135             if len(context['line_order'][-1]) > 2 and type(context['line_order'][-1][2]) == type({}) and context['line_order'][-1][2].has_key('sequence'):
136                 data['sequence'] = context['line_order'][-1][2]['sequence'] + 1
137         if context.has_key('survey_id'):
138             data['survey_id'] = context['survey_id']
139         return data
140
141     def survey_save(self, cr, uid, ids, context):
142         search_obj = self.pool.get('ir.ui.view')
143         search_id = search_obj.search(cr,uid,[('model','=','survey.question.wiz'),('name','=','Survey Search')])
144         surv_name_wiz = self.pool.get('survey.name.wiz')
145         surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'transfer':True, 'page_no' : context.get('page_number',0) })
146         return {
147                 'view_type': 'form',
148                 "view_mode": 'form',
149                 'res_model': 'survey.question.wiz',
150                 'type': 'ir.actions.act_window',
151                 'target': 'new',
152                 'search_view_id':search_id[0],
153                 'context': context
154                 }
155
156     def copy(self, cr, uid, id, default=None,context={}):
157         raise osv.except_osv(_('Error !'),_('You cannot duplicate the resource!'))
158
159 survey_page()
160
161 class survey_question(osv.osv):
162     _name = 'survey.question'
163     _description = 'Survey Question'
164     _rec_name = 'question'
165     _order = 'sequence'
166
167     def _calc_response(self, cr, uid, ids, field_name, arg, context):
168         if len(ids) == 0:
169             return {}
170         val = {}
171         cr.execute("select question_id, count(id) as Total_response from survey_response_line where state='done' and question_id in (%s) group by question_id" % ",".join(map(str, map(int, ids))))
172         ids1 = copy.deepcopy(ids)
173         for rec in  cr.fetchall():
174             ids1.remove(rec[0])
175             val[rec[0]] = int(rec[1])
176         for id in ids1:
177             val[id] = 0
178         return val
179
180     _columns = {
181         'page_id' : fields.many2one('survey.page', 'Survey Page', ondelete='cascade', required=1),
182         'question' :  fields.char('Question', size=128, required=1),
183         'answer_choice_ids' : fields.one2many('survey.answer', 'question_id', 'Answer'),
184         'response_ids' : fields.one2many('survey.response.line', 'question_id', 'Response', readonly=1),
185         'is_require_answer' : fields.boolean('Require Answer to Question (optional)'),
186         'required_type' : fields.selection([('all','All'), ('at least','At Least'), ('at most','At Most'), ('exactly','Exactly'), ('a range','A Range')], 'Respondent must answer'),
187         'req_ans' : fields.integer('#Required Answer'),
188         'maximum_req_ans' : fields.integer('Maximum Required Answer'),
189         'minimum_req_ans' : fields.integer('Minimum Required Answer'),
190         'req_error_msg' : fields.text('Error Message'),
191         'allow_comment' : fields.boolean('Allow Comment Field'),
192         'sequence' : fields.integer('Sequence'),
193         'tot_resp' : fields.function(_calc_response, method=True, string="Total Response"),
194         'survey' : fields.related('page_id', 'survey_id', type='many2one', relation='survey', string='Survey'),
195         'descriptive_text' : fields.text('Descriptive Text', size=255),
196         'column_heading_ids' : fields.one2many('survey.question.column.heading', 'question_id',' Column heading'),
197         'type' : fields.selection([('multiple_choice_only_one_ans','Multiple Choice (Only One Answer)'),
198                                      ('multiple_choice_multiple_ans','Multiple Choice (Multiple Answer)'),
199                                      ('matrix_of_choices_only_one_ans','Matrix of Choices (Only One Answers Per Row)'),
200                                      ('matrix_of_choices_only_multi_ans','Matrix of Choices (Multiple Answers Per Row)'),
201                                      ('matrix_of_drop_down_menus','Matrix of Drop-down Menus'),
202                                      ('rating_scale','Rating Scale'),('single_textbox','Single Textbox'),
203                                      ('multiple_textboxes','Multiple Textboxes'),
204                                      ('multiple_textboxes_diff_type','Multiple Textboxes With Different Type'),
205                                      ('comment','Comment/Essay Box'),
206                                      ('numerical_textboxes','Numerical Textboxes'),('date','Date'),
207                                      ('date_and_time','Date and Time'),('descriptive_text','Descriptive Text'),
208                                      ('table','Table'),
209                                     ], 'Question Type',  required=1,),
210         'is_comment_require' : fields.boolean('Add Comment Field (optional)'),
211         'comment_label' : fields.char('Field Label', size = 255),
212         'comment_field_type' : fields.selection([('char', 'Single Line Of Text'), ('text', 'Paragraph of Text')], 'Comment Field Type'),
213         'comment_valid_type' : fields.selection([('do_not_validate', '''Don't Validate Comment Text.'''),
214                                                  ('must_be_specific_length', 'Must Be Specific Length'),
215                                                  ('must_be_whole_number', 'Must Be A Whole Number'),
216                                                  ('must_be_decimal_number', 'Must Be A Decimal Number'),
217                                                  ('must_be_date', 'Must Be A Date'),
218                                                  ('must_be_email_address', 'Must Be An Email Address'),
219                                                  ], 'Text Validation'),
220         'comment_minimum_no' : fields.integer('Minimum number'),
221         'comment_maximum_no' : fields.integer('Maximum number'),
222         'comment_minimum_float' : fields.float('Minimum decimal number'),
223         'comment_maximum_float' : fields.float('Maximum decimal number'),
224         'comment_minimum_date' : fields.date('Minimum date'),
225         'comment_maximum_date' : fields.date('Maximum date'),
226         'comment_valid_err_msg' : fields.text('Error message'),
227         'make_comment_field' : fields.boolean('Make Comment Field an Answer Choice'),
228         'make_comment_field_err_msg' : fields.text('Error message'),
229         'is_validation_require' : fields.boolean('Validate Text (optional)'),
230         'validation_type' : fields.selection([('do_not_validate', '''Don't Validate Comment Text.'''),\
231                                                  ('must_be_specific_length', 'Must Be Specific Length'),\
232                                                  ('must_be_whole_number', 'Must Be A Whole Number'),\
233                                                  ('must_be_decimal_number', 'Must Be A Decimal Number'),\
234                                                  ('must_be_date', 'Must Be A Date'),\
235                                                  ('must_be_email_address', 'Must Be An Email Address')\
236                                                  ], 'Text Validation'),
237         'validation_minimum_no' : fields.integer('Minimum number'),
238         'validation_maximum_no' : fields.integer('Maximum number'),
239         'validation_minimum_float' : fields.float('Minimum decimal number'),
240         'validation_maximum_float' : fields.float('Maximum decimal number'),
241         'validation_minimum_date' : fields.date('Minimum date'),
242         'validation_maximum_date' : fields.date('Maximum date'),
243         'validation_valid_err_msg' : fields.text('Error message'),
244         'numeric_required_sum' : fields.integer('Sum of all choices'),
245         'numeric_required_sum_err_msg' : fields.text('Error message'),
246         'rating_allow_one_column_require' : fields.boolean('Allow Only One Response per Column (Forced Ranking)'),
247         'in_visible_rating_weight':fields.boolean('Is Rating Scale Invisible?'),
248         'in_visible_menu_choice':fields.boolean('Is Menu Choice Invisible?'),
249         'in_visible_answer_type':fields.boolean('Is Answer Type Invisible?'),
250         'comment_column':fields.boolean('Add comment column in matrix'),
251         'column_name':fields.char('Column Name',size=256),
252         'no_of_rows' : fields.integer('No of Rows'),
253     }
254     _defaults = {
255          'sequence' : lambda * a: 1,
256          'type' : lambda * a: 'multiple_choice_multiple_ans',
257          'req_error_msg' : lambda * a: 'This question requires an answer.',
258          'required_type' : lambda * a: 'at least',
259          'req_ans' : lambda * a: 1,
260          'comment_field_type' : lambda * a: 'char',
261          'comment_label' : lambda * a: 'Other (please specify)',
262          'comment_valid_type' : lambda * a: 'do_not_validate',
263          'comment_valid_err_msg' : lambda * a : 'The comment you entered is in an invalid format.',
264          'validation_type' : lambda * a: 'do_not_validate',
265          'validation_valid_err_msg' : lambda * a : 'The comment you entered is in an invalid format.',
266          'numeric_required_sum_err_msg' : lambda * a :'The choices need to add up to [enter sum here].',
267          'make_comment_field_err_msg' : lambda * a : 'Please enter a comment.',
268          'in_visible_answer_type' : lambda * a: 1
269     }
270
271     def on_change_type(self, cr, uid, ids, type, context=None):
272         val = {}
273         val['is_require_answer'] = False
274         val['is_comment_require'] = False
275         val['is_validation_require'] = False
276         val['comment_column'] = False
277
278         if type in ['multiple_textboxes_diff_type']:
279             val['in_visible_answer_type'] = False
280             return {'value': val}
281
282         if type in ['rating_scale']:
283             val.update({'in_visible_rating_weight':False,'in_visible_menu_choice':True})
284             return {'value': val}
285
286         elif type in ['matrix_of_drop_down_menus']:
287             val.update({'in_visible_rating_weight':True,'in_visible_menu_choice':False})
288             return {'value': val}
289
290         elif type in ['single_textbox']:
291             val.update({'in_visible_rating_weight':True,'in_visible_menu_choice':True})
292             return {'value': val}
293
294         else:
295             val.update({'in_visible_rating_weight':True,'in_visible_menu_choice':True,'in_visible_answer_type':True})
296             return {'value': val}
297
298     def write(self, cr, uid, ids, vals, context=None):
299         questions = self.read(cr,uid, ids, ['answer_choice_ids', 'type', 'required_type','req_ans', 'minimum_req_ans', 'maximum_req_ans', 'column_heading_ids'])
300         for question in questions:
301             col_len = len(question['column_heading_ids'])
302             if vals.has_key('column_heading_ids'):
303                 for col in vals['column_heading_ids']:
304                     if type(col[2]) == type({}):
305                         col_len += 1
306                     else:
307                         col_len -= 1
308
309             if vals.has_key('type'):
310                 que_type = vals['type']
311             else:
312                 que_type = question['type']
313
314             if que_type in ['matrix_of_choices_only_one_ans', 'matrix_of_choices_only_multi_ans', 'matrix_of_drop_down_menus', 'rating_scale']:
315                 if not col_len:
316                     raise osv.except_osv(_('Error !'),_("You must enter one or more column heading."))
317             ans_len = len(question['answer_choice_ids'])
318
319             if vals.has_key('answer_choice_ids'):
320                 for ans in vals['answer_choice_ids']:
321                     if type(ans[2]) == type({}):
322                         ans_len += 1
323                     else:
324                         ans_len -= 1
325
326             if que_type not in ['descriptive_text', 'single_textbox', 'comment','table']:
327                 if not ans_len:
328                     raise osv.except_osv(_('Error !'),_("You must enter one or more Answer."))
329             req_type = ""
330
331             if vals.has_key('required_type'):
332                 req_type = vals['required_type']
333             else:
334                 req_type = question['required_type']
335
336             if que_type in ['multiple_choice_multiple_ans','matrix_of_choices_only_one_ans', 'matrix_of_choices_only_multi_ans', 'matrix_of_drop_down_menus', 'rating_scale','multiple_textboxes','numerical_textboxes','date','date_and_time']:
337                 if req_type in ['at least', 'at most', 'exactly']:
338                     if vals.has_key('req_ans'):
339                         if not vals['req_ans'] or  vals['req_ans'] > ans_len:
340                             raise osv.except_osv(_('Error !'),_("#Required Answer you entered is greater than the number of answer. Please use a number that is smaller than %d.") % (ans_len + 1))
341                     else:
342                         if not question['req_ans'] or  question['req_ans'] > ans_len:
343                             raise osv.except_osv(_('Error !'),_("#Required Answer you entered is greater than the number of answer. Please use a number that is smaller than %d.") % (ans_len + 1))
344
345                 if req_type == 'a range':
346                     minimum_ans = 0
347                     maximum_ans = 0
348                     if vals.has_key('minimum_req_ans'):
349                         minimum_ans = vals['minimum_req_ans']
350                         if not vals['minimum_req_ans'] or  vals['minimum_req_ans'] > ans_len:
351                             raise osv.except_osv(_('Error !'),_("Minimum Required Answer you entered is greater than the number of answer. Please use a number that is smaller than %d.") % (ans_len + 1))
352                     else:
353                         minimum_ans = question['minimum_req_ans']
354                         if not question['minimum_req_ans'] or  question['minimum_req_ans'] > ans_len:
355                             raise osv.except_osv(_('Error !'),_("Minimum Required Answer you entered is greater than the number of answer. Please use a number that is smaller than %d.") % (ans_len + 1))
356                     if vals.has_key('maximum_req_ans'):
357                         maximum_ans = vals['maximum_req_ans']
358                         if not vals['maximum_req_ans'] or vals['maximum_req_ans'] > ans_len:
359                             raise osv.except_osv(_('Error !'),_("Maximum Required Answer you entered for your maximum is greater than the number of answer. Please use a number that is smaller than %d.") % (ans_len + 1))
360                     else:
361                         maximum_ans = question['maximum_req_ans']
362                         if not question['maximum_req_ans'] or question['maximum_req_ans'] > ans_len:
363                             raise osv.except_osv(_('Error !'),_("Maximum Required Answer you entered for your maximum is greater than the number of answer. Please use a number that is smaller than %d.") % (ans_len + 1))
364                     if maximum_ans <= minimum_ans:
365                         raise osv.except_osv(_('Error !'),_("Maximum Required Answer is greater than Minimum Required Answer"))
366
367             if question['type'] ==  'matrix_of_drop_down_menus' and vals.has_key('column_heading_ids'):
368                 for col in vals['column_heading_ids']:
369                     if not col[2] or not col[2].has_key('menu_choice') or not col[2]['menu_choice']:
370                         raise osv.except_osv(_('Error !'),_("You must enter one or more menu choices in column heading"))
371                     elif not col[2] or not col[2].has_key('menu_choice') or col[2]['menu_choice'].strip() == '':
372                         raise osv.except_osv(_('Error !'),_("You must enter one or more menu choices in column heading (white spaces not allowed)"))
373
374         return super(survey_question, self).write(cr, uid, ids, vals, context=context)
375
376     def create(self, cr, uid, vals, context):
377         minimum_ans = 0
378         maximum_ans = 0
379         if vals.has_key('answer_choice_ids') and  not len(vals['answer_choice_ids']):
380             if vals.has_key('type') and vals['type'] not in ['descriptive_text', 'single_textbox', 'comment','table']:
381                 raise osv.except_osv(_('Error !'),_("You must enter one or more answer."))
382
383         if vals.has_key('column_heading_ids') and  not len(vals['column_heading_ids']):
384             if vals.has_key('type') and vals['type'] in ['matrix_of_choices_only_one_ans', 'matrix_of_choices_only_multi_ans', 'matrix_of_drop_down_menus', 'rating_scale']:
385                 raise osv.except_osv(_('Error !'),_("You must enter one or more column heading."))
386
387         if vals['type'] in ['multiple_choice_multiple_ans','matrix_of_choices_only_one_ans', 'matrix_of_choices_only_multi_ans', 'matrix_of_drop_down_menus', 'rating_scale','multiple_textboxes','numerical_textboxes','date','date_and_time']:
388             if vals.has_key('is_require_answer') and vals.has_key('required_type') and vals['required_type'] in ['at least', 'at most', 'exactly']:
389                 if vals.has_key('answer_choice_ids') and vals['req_ans'] > len(vals['answer_choice_ids']) or not vals['req_ans']:
390                     raise osv.except_osv(_('Error !'),_("#Required Answer you entered is greater than the number of answer. Please use a number that is smaller than %d.") % (len(vals['answer_choice_ids'])+1))
391
392             if vals.has_key('is_require_answer') and vals.has_key('required_type') and vals['required_type'] == 'a range':
393                 minimum_ans = vals['minimum_req_ans']
394                 maximum_ans = vals['maximum_req_ans']
395                 if vals.has_key('answer_choice_ids') or vals['minimum_req_ans'] > len(vals['answer_choice_ids']) or not vals['minimum_req_ans']:
396                     raise osv.except_osv(_('Error !'),_("Minimum Required Answer you entered is greater than the number of answer. Please use a number that is smaller than %d.") % (len(vals['answer_choice_ids'])+1))
397                 if vals.has_key('answer_choice_ids') or vals['maximum_req_ans'] > len(vals['answer_choice_ids']) or not vals['maximum_req_ans']:
398                     raise osv.except_osv(_('Error !'),_("Maximum Required Answer you entered for your maximum is greater than the number of answer. Please use a number that is smaller than %d.") % (len(vals['answer_choice_ids'])+1))
399                 if maximum_ans <= minimum_ans:
400                     raise osv.except_osv(_('Error !'),_("Maximum Required Answer is greater than Minimum Required Answer"))
401
402         if vals['type'] ==  'matrix_of_drop_down_menus':
403             for col in vals['column_heading_ids']:
404                 if not col[2] or not col[2].has_key('menu_choice') or not col[2]['menu_choice']:
405                     raise osv.except_osv(_('Error !'),_("You must enter one or more menu choices in column heading"))
406                 elif not col[2] or not col[2].has_key('menu_choice') or col[2]['menu_choice'].strip() == '':
407                     raise osv.except_osv(_('Error !'),_("You must enter one or more menu choices in column heading (white spaces not allowed)"))
408
409         res = super(survey_question, self).create(cr, uid, vals, context)
410         return res
411
412     def survey_save(self, cr, uid, ids, context):
413         search_obj = self.pool.get('ir.ui.view')
414         search_id = search_obj.search(cr,uid,[('model','=','survey.question.wiz'),('name','=','Survey Search')])
415         surv_name_wiz = self.pool.get('survey.name.wiz')
416         surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'transfer':True, 'page_no' : context.get('page_number',False) })
417         return {
418                 'view_type': 'form',
419                 "view_mode": 'form',
420                 'res_model': 'survey.question.wiz',
421                 'type': 'ir.actions.act_window',
422                 'target': 'new',
423                 'search_view_id':search_id[0],
424                 'context': context
425                 }
426
427     def default_get(self, cr, uid, fields, context={}):
428         data = super(survey_question, self).default_get(cr, uid, fields, context)
429         if context.get('line_order',False):
430             if len(context['line_order'][-1]) > 2 and type(context['line_order'][-1][2]) == type({}) and context['line_order'][-1][2].has_key('sequence'):
431                 data['sequence'] = context['line_order'][-1][2]['sequence'] + 1
432
433         if context.has_key('page_id'):
434             data['page_id']= context.get('page_id',False)
435         return data
436
437 survey_question()
438
439
440 class survey_question_column_heading(osv.osv):
441     _name = 'survey.question.column.heading'
442     _description = 'Survey Question Column Heading'
443     _rec_name = 'title'
444
445     def _get_in_visible_rating_weight(self,cr, uid, context={}):
446         if context.get('in_visible_rating_weight',False):
447             return context['in_visible_rating_weight']
448         return False
449     def _get_in_visible_menu_choice(self,cr, uid, context={}):
450         if context.get('in_visible_menu_choice',False):
451             return context['in_visible_menu_choice']
452         return False
453
454     _columns = {
455         'title' : fields.char('Column Heading', size=128, required=1),
456         'menu_choice' : fields.text('Menu Choice'),
457         'rating_weight' : fields.integer('Weight'),
458         'question_id' : fields.many2one('survey.question', 'Question', ondelete='cascade'),
459         'in_visible_rating_weight':fields.boolean('Is Rating Scale Invisible ??'),
460         'in_visible_menu_choice':fields.boolean('Is Menu Choice Invisible??')
461     }
462     _defaults={
463        'in_visible_rating_weight':_get_in_visible_rating_weight,
464        'in_visible_menu_choice':_get_in_visible_menu_choice,
465     }
466
467 survey_question_column_heading()
468
469 class survey_answer(osv.osv):
470     _name = 'survey.answer'
471     _description = 'Survey Answer'
472     _rec_name = 'answer'
473     _order = 'sequence'
474
475     def _calc_response_avg(self, cr, uid, ids, field_name, arg, context):
476         val = {}
477         for rec in self.browse(cr, uid, ids):
478             cr.execute("select count(question_id) ,(select count(answer_id) \
479                 from survey_response_answer sra, survey_response_line sa \
480                 where sra.response_id = sa.id and sra.answer_id = %d \
481                 and sa.state='done') as tot_ans from survey_response_line \
482                 where question_id = %d and state = 'done'"\
483                      % (rec.id, rec.question_id.id))
484             res = cr.fetchone()
485             if res[0]:
486                 avg = float(res[1]) * 100 / res[0]
487             else:
488                 avg = 0.0
489             val[rec.id] = {
490                 'response': res[1],
491                 'average': round(avg, 2),
492             }
493         return val
494
495     def _get_in_visible_answer_type(self,cr, uid, context={}):
496         if context.get('in_visible_answer_type',False):
497             return context.get('in_visible_answer_type',False)
498         return False
499
500     _columns = {
501         'question_id' : fields.many2one('survey.question', 'Question', ondelete='cascade'),
502         'answer' : fields.char('Answer', size=128, required=1),
503         'sequence' : fields.integer('Sequence'),
504         'response' : fields.function(_calc_response_avg, method=True, string="#Response", multi='sums'),
505         'average' : fields.function(_calc_response_avg, method=True, string="#Avg", multi='sums'),
506         'type' : fields.selection([('char','Character'),('date','Date'),('datetime','Date & Time'),('integer','Integer'),('float','Float'),('selection','Selection'),('email','Email')], "Type of Answer",required=1),
507         'menu_choice' : fields.text('Menu Choices'),
508         'in_visible_answer_type':fields.boolean('Is Answer Type Invisible??')
509     }
510     _defaults = {
511          'sequence' : lambda * a: 1,
512          'type' : lambda * a: 'char',
513          'in_visible_answer_type':_get_in_visible_answer_type,
514     }
515
516     def default_get(self, cr, uid, fields, context={}):
517         data = super(survey_answer, self).default_get(cr, uid, fields, context)
518         if context.get('line_order',False):
519             if len(context['line_order'][-1]) > 2 and type(context['line_order'][-1][2]) == type({}) and context['line_order'][-1][2].has_key('sequence'):
520                 data['sequence'] = context['line_order'][-1][2]['sequence'] + 1
521         return data
522
523 survey_answer()
524
525 class survey_response(osv.osv):
526     _name = "survey.response"
527     _rec_name = 'date_create'
528     _columns = {
529         'survey_id' : fields.many2one('survey', 'Survey', required=1, ondelete='cascade'),
530         'date_create' : fields.datetime('Create Date', required=1),
531         'user_id' : fields.many2one('res.users', 'User'),
532         'response_type' : fields.selection([('manually', 'Manually'), ('link', 'Link')], 'Response Type', required=1, readonly=1),
533         'question_ids' : fields.one2many('survey.response.line', 'response_id', 'Response Answer'),
534         'state' : fields.selection([('done', 'Finished '),('skip', 'Not Finished')], 'Status', readonly=True),
535     }
536     _defaults = {
537         'state' : lambda * a: "skip",
538         'response_type' : lambda * a: "manually",
539     }
540
541     def name_get(self, cr, uid, ids, context=None):
542         if not len(ids):
543             return []
544         reads = self.read(cr, uid, ids, ['user_id','date_create'], context)
545         res = []
546         for record in reads:
547             name = record['user_id'][1] + ' (' + record['date_create'].split('.')[0] + ')'
548             res.append((record['id'], name))
549         return res
550
551     def copy(self, cr, uid, id, default=None,context={}):
552         raise osv.except_osv(_('Error !'),_('You cannot duplicate the resource!'))
553
554 survey_response()
555
556 class survey_response_line(osv.osv):
557     _name = 'survey.response.line'
558     _description = 'Survey Response Line'
559     _rec_name = 'date_create'
560     _columns = {
561         'response_id' : fields.many2one('survey.response', 'Response', ondelete='cascade'),
562         'date_create' : fields.datetime('Create Date', required=1),
563         'state' : fields.selection([('draft', 'Draft'), ('done', 'Answered'),('skip', 'Skiped')], 'Status', readonly=True),
564         'question_id' : fields.many2one('survey.question', 'Question'),
565         'page_id' : fields.related('question_id', 'page_id', type='many2one', relation='survey.page', string='Page'),
566         'response_answer_ids' : fields.one2many('survey.response.answer', 'response_id', 'Response Answer'),
567         'response_table_ids' : fields.one2many('survey.tbl.column.heading', 'response_table_id', 'Response Answer'),
568         'comment' : fields.text('Notes'),
569         'single_text' : fields.char('Text', size=255),
570     }
571     _defaults = {
572         'state' : lambda * a: "draft",
573     }
574
575 survey_response_line()
576
577 class survey_tbl_column_heading(osv.osv):
578     _name = 'survey.tbl.column.heading'
579     _order = 'name'
580     _columns = {
581         'name' : fields.integer('Row Number'),
582         'column_id' : fields.many2one('survey.question.column.heading', 'Column'),
583         'value' : fields.char('Value', size = 255),
584         'response_table_id' : fields.many2one('survey.response.line', 'Response', ondelete='cascade'),
585     }
586
587 survey_tbl_column_heading()
588
589 class survey_response_answer(osv.osv):
590     _name = 'survey.response.answer'
591     _description = 'Survey Response Answer'
592     _rec_name = 'response_id'
593     _columns = {
594         'response_id' : fields.many2one('survey.response.line', 'Response', ondelete='cascade'),
595         'answer_id' : fields.many2one('survey.answer', 'Answer', required=1, ondelete='cascade'),
596         'column_id' : fields.many2one('survey.question.column.heading','Column'),
597         'answer' : fields.char('Value', size =255),
598         'value_choice' : fields.char('Value Choice', size =255),
599         'comment' : fields.text('Notes'),
600         'comment_field' : fields.char('Comment', size = 255)
601     }
602
603 survey_response_answer()
604
605 class res_users(osv.osv):
606     _inherit = "res.users"
607     _name = "res.users"
608     _columns = {
609         'survey_id': fields.many2many('survey', 'survey_users_rel', 'uid', 'sid', 'Groups'),
610     }
611
612 res_users()
613
614 class survey_request(osv.osv):
615     _name = "survey.request"
616     _order = 'date_deadline'
617     _rec_name = 'date_deadline'
618     _columns = {
619         'date_deadline' : fields.date("Deadline date"),
620         'user_id' : fields.many2one("res.users", "User"),
621         'email' : fields.char("E-mail", size=64),
622         'survey_id' : fields.many2one("survey", "Survey", required=1, ondelete='cascade'),
623         'response' : fields.many2one('survey.response', 'Answer'),
624         'state' : fields.selection([('draft','Draft'),('waiting_answer', 'Wating Answer'),('done', 'Done'),('cancel', 'Cancelled')], 'State', readonly=1)
625     }
626     _defaults = {
627         'state' : lambda * a: 'draft',
628         'date_deadline' : lambda * a :  (now() + RelativeDateTime(months=+1)).strftime("%Y-%m-%d %H:%M:%S")
629     }
630     def survey_req_waiting_answer(self, cr, uid, ids, arg):
631         self.write(cr, uid, ids, { 'state' : 'waiting_answer'})
632         return True
633
634     def survey_req_draft(self, cr, uid, ids, arg):
635         self.write(cr, uid, ids, { 'state' : 'draft'})
636         return True
637
638     def survey_req_done(self, cr, uid, ids, arg):
639         self.write(cr, uid, ids, { 'state' : 'done'})
640         return True
641
642     def survey_req_cancel(self, cr, uid, ids, arg):
643         self.write(cr, uid, ids, { 'state' : 'cancel'})
644         return True
645
646     def on_change_user(self, cr, uid, ids, user_id, context=None):
647         if user_id:
648             user_obj = self.pool.get('res.users')
649             user = user_obj.browse(cr, uid, user_id)
650             return {'value': {'email': user.address_id.email}}
651         return {}
652
653 survey_request()
654
655 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: