[IMP] : removed button from tree
[odoo/odoo.git] / addons / survey / wizard / survey_answer.py
1 ## -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21 import os
22 import datetime
23 from lxml import etree
24 from time import strftime
25
26 import base64
27 import tools
28 import netsvc
29 from osv import osv
30 from osv import fields
31 from tools import to_xml
32 from tools.translate import _
33 import addons
34 from tools.safe_eval import safe_eval
35
36 class survey_question_wiz(osv.osv_memory):
37     _name = 'survey.question.wiz'
38     _columns = {
39         'name': fields.integer('Number'),
40     }
41
42     def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
43         """
44         Fields View Get method :- generate the new view and display the survey pages of selected survey.
45         """
46         if context is None:
47             context = {}
48         result = super(survey_question_wiz, self).fields_view_get(cr, uid, view_id, \
49                                         view_type, context, toolbar,submenu)
50
51         surv_name_wiz = self.pool.get('survey.name.wiz')
52         survey_obj = self.pool.get('survey')
53         page_obj = self.pool.get('survey.page')
54         que_obj = self.pool.get('survey.question')
55         ans_obj = self.pool.get('survey.answer')
56         sur_response_obj = self.pool.get('survey.response')
57         que_col_head = self.pool.get('survey.question.column.heading')
58         user_obj = self.pool.get('res.users')
59         mail_message = self.pool.get('mail.message')
60         
61         if view_type in ['form']:
62             wiz_id = 0
63             sur_name_rec = None
64             if 'sur_name_id' in context:
65                 sur_name_rec = surv_name_wiz.browse(cr, uid, context['sur_name_id'], context=context)
66             elif 'survey_id' in context:
67                 res_data = {
68                     'survey_id': context.get('survey_id', False),
69                     'page_no': -1,
70                     'page': 'next',
71                     'transfer': 1,
72                     'response': 0
73                 }
74                 wiz_id = surv_name_wiz.create(cr, uid, res_data)
75                 sur_name_rec = surv_name_wiz.browse(cr, uid, wiz_id, context=context)
76                 context.update({'sur_name_id' :wiz_id})
77
78             if context.has_key('active_id'):
79                 context.pop('active_id')
80
81             survey_id = context.get('survey_id', False)
82             if not survey_id:
83                 # Try one more time to find it
84                 if sur_name_rec and sur_name_rec.survey_id:
85                     survey_id = sur_name_rec.survey_id.id
86                 else:
87                     # raise osv.except_osv(_('Error!'), _("Cannot locate survey for the question wizard!"))
88                     # If this function is called without a survey_id in
89                     # its context, it makes no sense to return any view.
90                     # Just return the default, empty view for this object,
91                     # in order to please random calls to this fn().
92                     return super(survey_question_wiz, self).\
93                                 fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context,
94                                         toolbar=toolbar, submenu=submenu)
95             sur_rec = survey_obj.browse(cr, uid, survey_id, context=context)
96             p_id = map(lambda x:x.id, sur_rec.page_ids)
97             total_pages = len(p_id)
98             pre_button = False
99             readonly = 0
100
101             if context.get('response_id', False) \
102                             and int(context['response_id'][0]) > 0:
103                 readonly = 1
104
105             if not sur_name_rec.page_no + 1 :
106                 surv_name_wiz.write(cr, uid, [context['sur_name_id'],], {'store_ans':{}})
107
108             sur_name_read = surv_name_wiz.browse(cr, uid, context['sur_name_id'], context=context)
109             page_number = int(sur_name_rec.page_no)
110             if sur_name_read.transfer or not sur_name_rec.page_no + 1:
111                 surv_name_wiz.write(cr, uid, [context['sur_name_id']], {'transfer':False})
112                 flag = False
113                 fields = {}
114                 if sur_name_read.page == "next" or sur_name_rec.page_no == -1:
115                     if total_pages > sur_name_rec.page_no + 1:
116                         if ((context.has_key('active') and not context.get('active', False)) \
117                                     or not context.has_key('active')) and not sur_name_rec.page_no + 1:
118                             if sur_rec.state != "open" :
119                                 raise osv.except_osv(_('Warning !'),_("You can not answer because the survey is not open"))
120                             cr.execute('select count(id) from survey_history where user_id=%s\
121                                                     and survey_id=%s', (uid,survey_id))
122                             res = cr.fetchone()[0]
123                             user_limit = survey_obj.browse(cr, uid, survey_id)
124                             user_limit = user_limit.response_user
125                             if user_limit and res >= user_limit:
126                                 raise osv.except_osv(_('Warning !'),_("You can not answer this survey more than %s times") % (user_limit))
127
128                         if sur_rec.max_response_limit and sur_rec.max_response_limit <= sur_rec.tot_start_survey and not sur_name_rec.page_no + 1:
129                             survey_obj.write(cr, uid, survey_id, {'state':'close', 'date_close':strftime("%Y-%m-%d %H:%M:%S")})
130
131                         p_id = p_id[sur_name_rec.page_no + 1]
132                         surv_name_wiz.write(cr, uid, [context['sur_name_id'],], {'page_no' : sur_name_rec.page_no + 1})
133                         flag = True
134                         page_number += 1
135                     if sur_name_rec.page_no > - 1:
136                         pre_button = True
137                 else:
138                     if sur_name_rec.page_no != 0:
139                         p_id = p_id[sur_name_rec.page_no - 1]
140                         surv_name_wiz.write(cr, uid, [context['sur_name_id'],],\
141                                              {'page_no' : sur_name_rec.page_no - 1})
142                         flag = True
143                         page_number -= 1
144
145                     if sur_name_rec.page_no > 1:
146                         pre_button = True
147                 if flag:
148                     pag_rec = page_obj.browse(cr, uid, p_id, context=context)
149                     xml_form = etree.Element('form', {'string': tools.ustr(pag_rec.title)})
150                     xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '4'})
151                     if context.has_key('response_id') and context.get('response_id', False) \
152                          and int(context.get('response_id',0)[0]) > 0:
153                         # TODO: l10n, cleanup this code to make it readable. Or template?
154                         xml_group = etree.SubElement(xml_form, 'group', {'col': '40', 'colspan': '4'})
155                         record = sur_response_obj.browse(cr, uid, context['response_id'][context['response_no']])
156                         etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr('Answer Of :- ' + record.user_id.name + ',  Date :- ' + record.date_create.split('.')[0]  )), 'align':"0.0"})
157                         etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr(" Answer :- " + str(context.get('response_no',0) + 1) +"/" + str(len(context.get('response_id',0))) )), 'align':"0.0"})
158                         if context.get('response_no',0) > 0:
159                             etree.SubElement(xml_group, 'button', {'colspan':"1",'icon':"gtk-go-back",'name':"action_forward_previous",'string': tools.ustr("Previous Answer"),'type':"object"})
160                         if context.get('response_no',0) + 1 < len(context.get('response_id',0)):
161                             etree.SubElement(xml_group, 'button', {'colspan':"1",'icon': "gtk-go-forward", 'name':"action_forward_next",'string': tools.ustr("Next Answer") ,'type':"object",'context' : tools.ustr(context)})
162
163                     if wiz_id:
164                         fields["wizardid_" + str(wiz_id)] = {'type':'char', 'size' : 255, 'string':"", 'views':{}}
165                         etree.SubElement(xml_form, 'field', {'invisible':'1','name': "wizardid_" + str(wiz_id),'default':str(lambda *a: 0)})
166
167                     if pag_rec.note:
168                         xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '4'})
169                         for que_test in pag_rec.note.split('\n'):
170                             etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr(que_test)), 'align':"0.0"})
171                     que_ids = pag_rec.question_ids
172                     qu_no = 0
173
174                     for que in que_ids:
175                         qu_no += 1
176                         que_rec = que_obj.browse(cr, uid, que.id, context=context)
177                         descriptive_text = ""
178                         separator_string = tools.ustr(qu_no) + "." + tools.ustr(que_rec.question)
179                         if ((context.has_key('active') and not context.get('active',False)) or not context.has_key('active')) and que_rec.is_require_answer:
180                             star = '*'
181                         else:
182                             star = ''
183                         xml_group = etree.SubElement(xml_form, 'group', {'col': '2', 'colspan': '4'})
184
185                         if context.has_key('active') and context.get('active',False) and \
186                                     context.has_key('edit'):
187                             xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '2'})
188                             etree.SubElement(xml_group, 'separator', {'string': star+to_xml(separator_string), 'colspan': '3'})
189                             xml_group1 = etree.SubElement(xml_form, 'group', {'col': '2', 'colspan': '2'})
190                             context.update({'question_id' : tools.ustr(que.id),'page_number': sur_name_rec.page_no , 'transfer' : sur_name_read.transfer, 'page_id' : p_id})
191                             etree.SubElement(xml_group1, 'button', {'string':'','icon': "gtk-edit", 'type' :'object', 'name':"action_edit_question", 'context' : tools.ustr(context)})
192                             etree.SubElement(xml_group1, 'button', {'string':'','icon': "gtk-delete", 'type' :'object','name':"action_delete_question", 'context' : tools.ustr(context)})
193                         else:
194                             xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '4'})
195                             etree.SubElement(xml_group, 'separator', {'string': star+to_xml(separator_string), 'colspan': '4'})
196
197                         ans_ids = que_rec.answer_choice_ids
198                         xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '4'})
199
200                         if que_rec.type == 'multiple_choice_only_one_ans':
201                             selection = []
202                             for ans in ans_ids:
203                                 selection.append((tools.ustr(ans.id), ans.answer))
204                             xml_group = etree.SubElement(xml_group, 'group', {'col': '2', 'colspan': '2'})
205                             etree.SubElement(xml_group, 'field', {'readonly':str(readonly), 'name': tools.ustr(que.id) + "_selection"})
206                             fields[tools.ustr(que.id) + "_selection"] = {'type':'selection', 'selection' :selection, 'string':"Answer"}
207
208                         elif que_rec.type == 'multiple_choice_multiple_ans':
209                             xml_group = etree.SubElement(xml_group, 'group', {'col': '4', 'colspan': '4'})
210                             for ans in ans_ids:
211                                 etree.SubElement(xml_group, 'field', {'readonly':str(readonly), 'name': tools.ustr(que.id) + "_" + tools.ustr(ans.id)})
212                                 fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id)] = {'type':'boolean', 'string':ans.answer}
213
214                         elif que_rec.type in ['matrix_of_choices_only_one_ans', 'rating_scale']:
215                             if que_rec.comment_column:
216                                 col = "4"
217                                 colspan = "4"
218                             else:
219                                col = "2"
220                                colspan = "2"
221                             xml_group = etree.SubElement(xml_group, 'group', {'col': tools.ustr(col), 'colspan': tools.ustr(colspan)})
222                             for row in ans_ids:
223                                 etree.SubElement(xml_group, 'newline')
224                                 etree.SubElement(xml_group, 'field', {'readonly': str(readonly), 'name': tools.ustr(que.id) + "_selection_" + tools.ustr(row.id),'string':to_xml(tools.ustr(row.answer))})
225                                 selection = [('','')]
226                                 for col in que_rec.column_heading_ids:
227                                     selection.append((str(col.id), col.title))
228                                 fields[tools.ustr(que.id) + "_selection_" + tools.ustr(row.id)] = {'type':'selection', 'selection' : selection, 'string': "Answer"}
229                                 if que_rec.comment_column:
230                                    fields[tools.ustr(que.id) + "_commentcolumn_"+tools.ustr(row.id) + "_field"] = {'type':'char', 'size' : 255, 'string':tools.ustr(que_rec.column_name), 'views':{}}
231                                    etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_commentcolumn_"+tools.ustr(row.id)+ "_field"})
232
233                         elif que_rec.type == 'matrix_of_choices_only_multi_ans':
234                             xml_group = etree.SubElement(xml_group, 'group', {'col': str(len(que_rec.column_heading_ids) + 1), 'colspan': '4'})
235                             etree.SubElement(xml_group, 'separator', {'string': '.','colspan': '1'})
236                             for col in que_rec.column_heading_ids:
237                                 etree.SubElement(xml_group, 'separator', {'string': tools.ustr(col.title),'colspan': '1'})
238                             for row in ans_ids:
239                                 etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr(row.answer)) +' :-', 'align': '0.0'})
240                                 for col in que_col_head.browse(cr, uid, [head.id for head in  que_rec.column_heading_ids]):
241                                     etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_" + tools.ustr(row.id) + "_" + tools.ustr(col.id), 'nolabel':"1"})
242                                     fields[tools.ustr(que.id) + "_" + tools.ustr(row.id)  + "_" + tools.ustr(col.id)] = {'type':'boolean', 'string': col.title}
243
244                         elif que_rec.type == 'matrix_of_drop_down_menus':
245                             xml_group = etree.SubElement(xml_group, 'group', {'col': str(len(que_rec.column_heading_ids) + 1), 'colspan': '4'})
246                             etree.SubElement(xml_group, 'separator', {'string': '.','colspan': '1'})
247                             for col in que_rec.column_heading_ids:
248                                 etree.SubElement(xml_group, 'separator', {'string': tools.ustr(col.title),'colspan': '1'})
249                             for row in ans_ids:
250                                 etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr(row.answer))+' :-', 'align': '0.0'})
251                                 for col in que_rec.column_heading_ids:
252                                     selection = []
253                                     if col.menu_choice:
254                                         for item in col.menu_choice.split('\n'):
255                                             if item and not item.strip() == '': selection.append((item ,item))
256                                     etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_" + tools.ustr(row.id) + "_" + tools.ustr(col.id),'nolabel':'1'})
257                                     fields[tools.ustr(que.id) + "_" + tools.ustr(row.id)  + "_" + tools.ustr(col.id)] = {'type':'selection', 'string': col.title, 'selection':selection}
258
259                         elif que_rec.type == 'multiple_textboxes':
260                             xml_group = etree.SubElement(xml_group, 'group', {'col': '4', 'colspan': '4'})
261                             type = "char"
262                             if que_rec.is_validation_require:
263                                 if que_rec.validation_type in ['must_be_whole_number']:
264                                     type = "integer"
265                                 elif que_rec.validation_type in ['must_be_decimal_number']:
266                                     type = "float"
267                                 elif que_rec.validation_type in ['must_be_date']:
268                                     type = "date"
269                             for ans in ans_ids:
270                                 etree.SubElement(xml_group, 'field', {'readonly': str(readonly), 'width':"300",'colspan': '1','name': tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"})
271                                 if type == "char" :
272                                     fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"] = {'type':'char', 'size':255, 'string':ans.answer}
273                                 else:
274                                     fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"] = {'type': str(type), 'string':ans.answer}
275
276                         elif que_rec.type == 'numerical_textboxes':
277                             xml_group = etree.SubElement(xml_group, 'group', {'col': '4', 'colspan': '4'})
278                             for ans in ans_ids:
279                                 etree.SubElement(xml_group, 'field', {'readonly': str(readonly), 'width':"300",'colspan': '1','name': tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_numeric"})
280                                 fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_numeric"] = {'type':'integer', 'string':ans.answer}
281
282                         elif que_rec.type == 'date':
283                             xml_group = etree.SubElement(xml_group, 'group', {'col': '4', 'colspan': '4'})
284                             for ans in ans_ids:
285                                 etree.SubElement(xml_group, 'field', {'readonly': str(readonly), 'width':"300",'colspan': '1','name': tools.ustr(que.id) + "_" + tools.ustr(ans.id)})
286                                 fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id)] = {'type':'date', 'string':ans.answer}
287
288                         elif que_rec.type == 'date_and_time':
289                             xml_group = etree.SubElement(xml_group, 'group', {'col': '4', 'colspan': '4'})
290                             for ans in ans_ids:
291                                 etree.SubElement(xml_group, 'field', {'readonly': str(readonly), 'width':"300",'colspan': '1','name': tools.ustr(que.id) + "_" + tools.ustr(ans.id)})
292                                 fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id)] = {'type':'datetime', 'string':ans.answer}
293
294                         elif que_rec.type == 'descriptive_text':
295                             if que_rec.descriptive_text:
296                                 for que_test in que_rec.descriptive_text.split('\n'):
297                                     etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr(que_test)), 'align':"0.0"})
298
299                         elif que_rec.type == 'single_textbox':
300                             etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_single", 'nolabel':"1" ,'colspan':"4"})
301                             fields[tools.ustr(que.id) + "_single"] = {'type':'char', 'size': 255, 'string':"single_textbox", 'views':{}}
302
303                         elif que_rec.type == 'comment':
304                             etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_comment", 'nolabel':"1" ,'colspan':"4"})
305                             fields[tools.ustr(que.id) + "_comment"] = {'type':'text', 'string':"Comment/Eassy Box", 'views':{}}
306
307                         elif que_rec.type == 'table':
308                             xml_group = etree.SubElement(xml_group, 'group', {'col': str(len(que_rec.column_heading_ids)), 'colspan': '4'})
309                             for col in que_rec.column_heading_ids:
310                                 etree.SubElement(xml_group, 'separator', {'string': tools.ustr(col.title),'colspan': '1'})
311                             for row in range(0,que_rec.no_of_rows):
312                                 for col in que_rec.column_heading_ids:
313                                     etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_table_" + tools.ustr(col.id) +"_"+ tools.ustr(row), 'nolabel':"1"})
314                                     fields[tools.ustr(que.id) + "_table_" + tools.ustr(col.id) +"_"+ tools.ustr(row)] = {'type':'char','size':255,'views':{}}
315
316                         elif que_rec.type == 'multiple_textboxes_diff_type':
317                             xml_group = etree.SubElement(xml_group, 'group', {'col': '4', 'colspan': '4'})
318                             for ans in ans_ids:
319                                 if ans.type == "email" :
320                                     fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"] = {'type':'char', 'size':255, 'string':ans.answer}
321                                     etree.SubElement(xml_group, 'field', {'readonly': str(readonly), 'widget':'email','width':"300",'colspan': '1','name': tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"})
322                                 else:
323                                     etree.SubElement(xml_group, 'field', {'readonly': str(readonly), 'width':"300",'colspan': '1','name': tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"})
324                                     if ans.type == "char" :
325                                         fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"] = {'type':'char', 'size':255, 'string':ans.answer}
326                                     elif ans.type in ['integer','float','date','datetime']:
327                                         fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"] = {'type': str(ans.type), 'string':ans.answer}
328                                     else:
329                                         selection = []
330                                         if ans.menu_choice:
331                                             for item in ans.menu_choice.split('\n'):
332                                                 if item and not item.strip() == '': selection.append((item ,item))
333                                         fields[tools.ustr(que.id) + "_" + tools.ustr(ans.id) + "_multi"] = {'type':'selection', 'selection' : selection, 'string':ans.answer}
334
335                         if que_rec.type in ['multiple_choice_only_one_ans', 'multiple_choice_multiple_ans', 'matrix_of_choices_only_one_ans', 'matrix_of_choices_only_multi_ans', 'matrix_of_drop_down_menus', 'rating_scale'] and que_rec.is_comment_require:
336                             if que_rec.type in ['multiple_choice_only_one_ans', 'multiple_choice_multiple_ans'] and que_rec.comment_field_type in ['char','text'] and que_rec.make_comment_field:
337                                 etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_otherfield", 'colspan':"4"})
338                                 fields[tools.ustr(que.id) + "_otherfield"] = {'type':'boolean', 'string':que_rec.comment_label, 'views':{}}
339                                 if que_rec.comment_field_type == 'char':
340                                     etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_other", 'nolabel':"1" ,'colspan':"4"})
341                                     fields[tools.ustr(que.id) + "_other"] = {'type': 'char', 'string': '', 'size':255, 'views':{}}
342                                 elif que_rec.comment_field_type == 'text':
343                                     etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_other", 'nolabel':"1" ,'colspan':"4"})
344                                     fields[tools.ustr(que.id) + "_other"] = {'type': 'text', 'string': '', 'views':{}}
345                             else:
346                                 if que_rec.comment_field_type == 'char':
347                                     etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr(que_rec.comment_label)),'colspan':"4"})
348                                     etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_other", 'nolabel':"1" ,'colspan':"4"})
349                                     fields[tools.ustr(que.id) + "_other"] = {'type': 'char', 'string': '', 'size':255, 'views':{}}
350                                 elif que_rec.comment_field_type == 'text':
351                                     etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr(que_rec.comment_label)),'colspan':"4"})
352                                     etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_other", 'nolabel':"1" ,'colspan':"4"})
353                                     fields[tools.ustr(que.id) + "_other"] = {'type': 'text', 'string': '', 'views':{}}
354
355                     etree.SubElement(xml_form, 'separator', {'colspan': '4'})
356                     xml_group = etree.SubElement(xml_form, 'group', {'col': '6', 'colspan': '4'})
357                     etree.SubElement(xml_group, 'field', {'name': 'progress_bar_' + tools.ustr(page_number) , 'widget':'progressbar'})
358                     fields['progress_bar_' + tools.ustr(page_number)] = {'type':'float', 'string':"Progress", 'views':{}}
359                     etree.SubElement(xml_group, 'label', {'string': tools.ustr(page_number+ 1) + "/" + tools.ustr(total_pages)})
360                     etree.SubElement(xml_group, 'button', {'icon': "gtk-cancel", 'special': "cancel",'string':"Cancel"})
361
362                     if pre_button:
363                         etree.SubElement(xml_group, 'button', {'colspan':"1",'icon':"gtk-go-back",'name':"action_previous",'string':"Previous",'type':"object"})
364                     but_string = "Next"
365                     if int(page_number) + 1 == total_pages:
366                         but_string = "Done"
367
368                     if context.has_key('active') and context.get('active',False) and int(page_number) + 1 == total_pages and context.has_key('response_id') and context.has_key('response_no') and  context.get('response_no',0) + 1 == len(context.get('response_id',0)):
369                         etree.SubElement(xml_group, 'button', {'icon': "gtk-go-forward", 'special' : 'cancel','string': tools.ustr("Done") ,'context' : tools.ustr(context)})
370                     elif context.has_key('active') and context.get('active', False) and int(page_number) + 1 == total_pages and context.has_key('response_id'):
371                         etree.SubElement(xml_group, 'button', {'icon': "gtk-go-forward", 'name':"action_forward_next",'string': tools.ustr("Next Answer") ,'type':"object",'context' : tools.ustr(context)})
372                     elif context.has_key('active') and context.get('active',False) and int(page_number) + 1 == total_pages:
373                         etree.SubElement(xml_group, 'button', {'icon': "gtk-go-forward", 'special': "cancel", 'string' : 'Done', 'context' : tools.ustr(context)})
374                     else:
375                         etree.SubElement(xml_group, 'button', {'icon': "gtk-go-forward", 'name':"action_next",'string': tools.ustr(but_string) ,'type':"object",'context' : tools.ustr(context)})
376
377                     if context.has_key('active') and context.get('active',False) and context.has_key('edit'):
378                         etree.SubElement(xml_form, 'separator', {'string' : '','colspan': '4'})
379                         context.update({'page_id' : tools.ustr(p_id),'page_number' : sur_name_rec.page_no , 'transfer' : sur_name_read.transfer})
380                         xml_group3 = etree.SubElement(xml_form, 'group', {'col': '4', 'colspan': '4'})
381                         etree.SubElement(xml_group3, 'button', {'string' :'Add Page','icon': "gtk-new", 'type' :'object','name':"action_new_page", 'context' : tools.ustr(context)})
382                         etree.SubElement(xml_group3, 'button', {'string' :'Edit Page','icon': "gtk-edit", 'type' :'object','name':"action_edit_page", 'context' : tools.ustr(context)})
383                         etree.SubElement(xml_group3, 'button', {'string' :'Delete Page','icon': "gtk-delete", 'type' :'object','name':"action_delete_page", 'context' : tools.ustr(context)})
384                         etree.SubElement(xml_group3, 'button', {'string' :'Add Question','icon': "gtk-new", 'type' :'object','name':"action_new_question", 'context' : tools.ustr(context)})
385
386                     root = xml_form.getroottree()
387                     result['arch'] = etree.tostring(root)
388                     result['fields'] = fields
389                     result['context'] = context
390                 else:
391                     survey_obj.write(cr, uid, survey_id, {'tot_comp_survey' : sur_rec.tot_comp_survey + 1})
392                     sur_response_obj.write(cr, uid, [sur_name_read.response], {'state' : 'done'})
393
394                     # mark the survey request as done; call 'survey_req_done' on its actual model
395                     survey_req_obj = self.pool.get(context.get('active_model'))
396                     if survey_req_obj and hasattr(survey_req_obj, 'survey_req_done'):
397                         survey_req_obj.survey_req_done(cr, uid, context.get('active_ids', []), context=context)
398
399                     if sur_rec.send_response:
400                         survey_data = survey_obj.browse(cr, uid, survey_id)
401                         response_id = surv_name_wiz.read(cr, uid, context.get('sur_name_id',False))['response']
402                         context.update({'response_id':response_id})
403                         report = self.create_report(cr, uid, [survey_id], 'report.survey.browse.response', survey_data.title,context)
404                         attachments = {}
405                         file = open(addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf")
406                         file_data = ""
407                         while 1:
408                             line = file.readline()
409                             file_data += line
410                             if not line:
411                                 break
412
413                         attachments[survey_data.title + ".pdf"] = file_data
414                         file.close()
415                         os.remove(addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf")
416                         
417                         user_email = user_obj.browse(cr, uid, uid, context).user_email
418                         resp_email = survey_data.responsible_id and survey_data.responsible_id.user_email or False
419
420                         if user_email and resp_email:
421                             user_name = user_obj.browse(cr, uid, uid, context=context).name
422                             mail = "Hello " + survey_data.responsible_id.name + ",\n\n " + str(user_name) + " has given the Response Of " + survey_data.title + " Survey.\nThe Response has been attached herewith.\n\n Thanks."
423                             mail_message.schedule_with_attach(cr, uid, user_email, [resp_email], "Survey Answer Of " + str(user_name) , mail, attachments=attachments, context=context)
424
425                     xml_form = etree.Element('form', {'string': _('Complete Survey Answer')})
426                     etree.SubElement(xml_form, 'separator', {'string': 'Complete Survey', 'colspan': "4"})
427                     etree.SubElement(xml_form, 'label', {'string': 'Thanks for your Answer'})
428                     etree.SubElement(xml_form, 'newline')
429                     etree.SubElement(xml_form, 'button', {'icon': "gtk-go-forward", 'special':"cancel",'string':"OK",'colspan':"2"})
430                     root = xml_form.getroottree()
431                     result['arch'] = etree.tostring(root)
432                     result['fields'] = {}
433                     result['context'] = context
434         return result
435
436     def create_report(self, cr, uid, res_ids, report_name=False, file_name=False, context=None):
437         """
438         If any user give answer of survey then last create report of this answer and if 'E-mail Notification on Answer' set True in survey  then send mail on responsible person of this survey and attach survey answer report in pdf format.
439         """
440         if not report_name or not res_ids:
441             return (False, Exception('Report name and Resources ids are required !!!'))
442         try:
443             uid = 1
444             service = netsvc.LocalService(report_name);
445             (result, format) = service.create(cr, uid, res_ids, {}, context)
446             ret_file_name = addons.get_module_resource('survey', 'report') + file_name + '.pdf'
447             fp = open(ret_file_name, 'wb+');
448             fp.write(result);
449             fp.close();
450             
451             # hr.applicant: if survey answered directly in system: attach report to applicant
452             if context.get('active_model') == 'hr.applicant':
453                 result = base64.b64encode(result)
454                 file_name = file_name + '.pdf'
455                 ir_attachment = self.pool.get('ir.attachment').create(cr, uid, 
456                                                                       {'name': file_name,
457                                                                        'datas': result,
458                                                                        'datas_fname': file_name,
459                                                                        'res_model': context.get('active_model'),
460                                                                        'res_id': context.get('active_ids')[0]},
461                                                                       context=context)
462
463         except Exception,e:
464             return (False, str(e))
465
466         return (True, ret_file_name)
467
468     def default_get(self, cr, uid, fields_list, context=None):
469         """
470         Assign Default value in particular field. If Browse Answers wizard run then read the value into database and Assigne to a particular fields.
471         """
472         value = {}
473         if context is None:
474             context = {}
475         for field in fields_list:
476             if field.split('_')[0] == 'progress':
477                 tot_page_id = self.pool.get('survey').browse(cr, uid, context.get('survey_id',False))
478                 tot_per = (float(100) * (int(field.split('_')[2]) + 1) / len(tot_page_id.page_ids))
479                 value[field] = tot_per
480         response_obj = self.pool.get('survey.response')
481         surv_name_wiz = self.pool.get('survey.name.wiz')
482
483         if context.has_key('response_id') and context.get('response_id') and int(context['response_id'][0]) > 0:
484             data = super(survey_question_wiz, self).default_get(cr, uid, fields_list, context)
485             response_ans = response_obj.browse(cr, uid, context['response_id'][context['response_no']])
486             fields_list.sort()
487
488             for que in response_ans.question_ids:
489                 for field in fields_list:
490                     if field.split('_')[0] != "progress" and field.split('_')[0] == str(que.question_id.id):
491                         if que.response_answer_ids and len(field.split('_')) == 4 and field.split('_')[1] == "commentcolumn" and field.split('_')[3] == "field":
492                             for ans in que.response_answer_ids:
493                                 if str(field.split('_')[2]) == str(ans.answer_id.id):
494                                     value[field] = ans.comment_field
495
496                         if que.response_table_ids and len(field.split('_')) == 4 and field.split('_')[1] == "table":
497                             for ans in que.response_table_ids:
498                                 if str(field.split('_')[2]) == str(ans.column_id.id) and str(field.split('_')[3]) ==  str(ans.name):
499                                     value[field] = ans.value
500
501                         if que.comment and (field.split('_')[1] == "comment" or field.split('_')[1] == "other"):
502                             value[field] = str(que.comment)
503
504                         elif que.single_text and field.split('_')[1] == "single":
505                             value[field] = str(que.single_text)
506
507                         elif que.response_answer_ids and len(field.split('_')) == 3 and field.split('_')[1] == "selection":
508                             for ans in que.response_answer_ids:
509                                 if str(field.split('_')[2]) == str( ans.answer_id.id):
510                                     value[field] = str(ans.column_id.id)
511
512                         elif que.response_answer_ids and len(field.split('_')) == 2 and field.split('_')[1] == "selection":
513                             value[field] = str(que.response_answer_ids[0].answer_id.id)
514
515                         elif que.response_answer_ids and len(field.split('_')) == 3 and field.split('_')[2] != "multi" and field.split('_')[2] != "numeric":
516                             for ans in que.response_answer_ids:
517                                 if str(field.split('_')[1]) == str( ans.answer_id.id) and str(field.split('_')[2]) == str(ans.column_id.id):
518                                     if ans.value_choice:
519                                         value[field] = ans.value_choice
520                                     else:
521                                         value[field] = True
522
523                         else:
524                             for ans in que.response_answer_ids:
525                                 if str(field.split('_')[1]) == str( ans.answer_id.id):
526                                     value[field] = ans.answer
527
528         else:
529             if not context.has_key('sur_name_id'):
530                 return value
531             if context.has_key('active') and context.get('active',False):
532                 return value
533             
534             sur_name_read = surv_name_wiz.read(cr, uid, context.get('sur_name_id',False))
535             ans_list = []
536
537             for key,val in safe_eval(sur_name_read.get('store_ans',"{}")).items():
538                 for field in fields_list:
539                     if field in list(val):
540                         value[field] = val[field]
541
542         return value
543
544     def create(self, cr, uid, vals, context=None):
545         """
546         Create the Answer of survey and store in survey.response object, and if set validation of question then check the value of question if value is wrong then raise the exception.
547         """
548         if context is None: context = {}
549
550         survey_question_wiz_id = super(survey_question_wiz,self).create(cr, uid, {'name': vals.get('name')}, context=context)
551         if context.has_key('active') and context.get('active',False):
552             return survey_question_wiz_id
553
554         for key,val in vals.items():
555             if key.split('_')[0] == "progress":
556                 vals.pop(key)
557             if not context.has_key('sur_name_id') and key.split('_')[0] == "wizardid":
558                 context.update({'sur_name_id': int(key.split('_')[1])})
559                 vals.pop(key)
560
561         click_state = True
562         click_update = []
563         surv_name_wiz = self.pool.get('survey.name.wiz')
564         surv_all_resp_obj = self.pool.get('survey.response')
565         surv_tbl_column_obj = self.pool.get('survey.tbl.column.heading')
566         survey_obj = self.pool.get('survey')
567         resp_obj = self.pool.get('survey.response.line')
568         res_ans_obj = self.pool.get('survey.response.answer')
569         que_obj = self.pool.get('survey.question')
570         sur_name_read = surv_name_wiz.read(cr, uid, context.get('sur_name_id',False), [])
571         response_id =  0
572
573         if not sur_name_read['response']:
574             response_id = surv_all_resp_obj.create(cr, uid, {'response_type':'link', 'user_id':uid, 'date_create':datetime.datetime.now(), 'survey_id' : context['survey_id']})
575             surv_name_wiz.write(cr, uid, [context.get('sur_name_id', False)], {'response' : tools.ustr(response_id)})
576         else:
577             response_id = int(sur_name_read['response'])
578
579         if response_id not in surv_all_resp_obj.search(cr, uid, []):
580             response_id = surv_all_resp_obj.create(cr, uid, {'response_type':'link', 'user_id':uid, 'date_create':datetime.datetime.now(), 'survey_id' : context.get('survey_id',False)})
581             surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'response' : tools.ustr(response_id)})
582
583         #click first time on next button then increemnet on total start suvey
584         if not safe_eval(sur_name_read['store_ans']):
585             his_id = self.pool.get('survey.history').create(cr, uid, {'user_id': uid, \
586                                               'date': strftime('%Y-%m-%d %H:%M:%S'), 'survey_id': sur_name_read['survey_id'][0]})
587             survey_id = sur_name_read['survey_id'][0]
588             sur_rec = survey_obj.read(cr, uid, survey_id)
589             survey_obj.write(cr, uid, survey_id,  {'tot_start_survey' : sur_rec['tot_start_survey'] + 1})
590             if context.has_key('cur_id'):
591                 if context.has_key('request') and context.get('request',False):
592                     self.pool.get(context.get('object',False)).write(cr, uid, [int(context.get('cur_id',False))], {'response' : response_id})
593                     self.pool.get(context.get('object',False)).survey_req_done(cr, uid, [int(context.get('cur_id'))], context)
594                 else:
595                     self.pool.get(context.get('object',False)).write(cr, uid, [int(context.get('cur_id',False))], {'response' : response_id})
596         if sur_name_read['store_ans'] and type(sur_name_read['store_ans']) == dict:
597             for key,val in sur_name_read['store_ans'].items():
598                 for field in vals:
599                     if field.split('_')[0] == val['question_id']:
600                         click_state = False
601                         click_update.append(key)
602                         break
603         else:
604             sur_name_read['store_ans'] = {}
605         if click_state:
606             que_li = []
607             resp_id_list = []
608             for key, val in vals.items():
609                 que_id = key.split('_')[0]
610                 if que_id not in que_li:
611                     que_li.append(que_id)
612                     que_rec = que_obj.read(cr, uid, [int(que_id)], [])[0]
613                     res_data =  {
614                         'question_id': que_id,
615                         'date_create': datetime.datetime.now(),
616                         'state': 'done',
617                         'response_id': response_id
618                     }
619                     resp_id = resp_obj.create(cr, uid, res_data)
620                     resp_id_list.append(resp_id)
621                     sur_name_read['store_ans'].update({resp_id:{'question_id':que_id}})
622                     surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'store_ans':sur_name_read['store_ans']})
623                     select_count = 0
624                     numeric_sum = 0
625                     selected_value = []
626                     matrix_list = []
627                     comment_field = False
628                     comment_value = False
629                     response_list = []
630
631                     for key1, val1 in vals.items():
632                         if val1 and key1.split('_')[1] == "table" and key1.split('_')[0] == que_id:
633                             surv_tbl_column_obj.create(cr, uid, {'response_table_id' : resp_id,'column_id':key1.split('_')[2], 'name':key1.split('_')[3], 'value' : val1})
634                             sur_name_read['store_ans'][resp_id].update({key1:val1})
635                             select_count += 1
636
637                         elif val1 and key1.split('_')[1] == "otherfield" and key1.split('_')[0] == que_id:
638                             comment_field = True
639                             sur_name_read['store_ans'][resp_id].update({key1:val1})
640                             select_count += 1
641                             surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'store_ans':sur_name_read['store_ans']})
642                             continue
643
644                         elif val1 and key1.split('_')[1] == "selection" and key1.split('_')[0] == que_id:
645                             if len(key1.split('_')) > 2:
646                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id':resp_id, 'answer_id':key1.split('_')[-1], 'column_id' : val1})
647                                 selected_value.append(val1)
648                                 response_list.append(str(ans_create_id) + "_" + str(key1.split('_')[-1]))
649                             else:
650                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id':resp_id, 'answer_id':val1})
651                             sur_name_read['store_ans'][resp_id].update({key1:val1})
652                             select_count += 1
653
654                         elif key1.split('_')[1] == "other" and key1.split('_')[0] == que_id:
655                             if not val1:
656                                 comment_value = True
657                             else:
658                                 error = False
659                                 if que_rec['is_comment_require'] and que_rec['comment_valid_type'] == 'must_be_specific_length':
660                                     if (not val1 and  que_rec['comment_minimum_no']) or len(val1) <  que_rec['comment_minimum_no'] or len(val1) > que_rec['comment_maximum_no']:
661                                         error = True
662                                 elif que_rec['is_comment_require'] and  que_rec['comment_valid_type'] in ['must_be_whole_number', 'must_be_decimal_number', 'must_be_date']:
663                                     error = False
664                                     try:
665                                         if que_rec['comment_valid_type'] == 'must_be_whole_number':
666                                             value = int(val1)
667                                             if value <  que_rec['comment_minimum_no'] or value > que_rec['comment_maximum_no']:
668                                                 error = True
669                                         elif que_rec['comment_valid_type'] == 'must_be_decimal_number':
670                                             value = float(val1)
671                                             if value <  que_rec['comment_minimum_float'] or value > que_rec['comment_maximum_float']:
672                                                 error = True
673                                         elif que_rec['comment_valid_type'] == 'must_be_date':
674                                             value = datetime.datetime.strptime(val1, "%Y-%m-%d")
675                                             if value <  datetime.datetime.strptime(que_rec['comment_minimum_date'], "%Y-%m-%d") or value >  datetime.datetime.strptime(que_rec['comment_maximum_date'], "%Y-%m-%d"):
676                                                 error = True
677                                     except:
678                                         error = True
679                                 elif que_rec['is_comment_require'] and  que_rec['comment_valid_type'] == 'must_be_email_address':
680                                     import re
681                                     if re.match("^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", val1) == None:
682                                             error = True
683                                 if error:
684                                     for res in resp_id_list:
685                                         sur_name_read['store_ans'].pop(res)
686                                     raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "'  \n" + tools.ustr(que_rec['comment_valid_err_msg']))
687
688                                 resp_obj.write(cr, uid, resp_id, {'comment':val1})
689                                 sur_name_read['store_ans'][resp_id].update({key1:val1})
690
691                         elif val1 and key1.split('_')[1] == "comment" and key1.split('_')[0] == que_id:
692                             resp_obj.write(cr, uid, resp_id, {'comment':val1})
693                             sur_name_read['store_ans'][resp_id].update({key1:val1})
694                             select_count += 1
695
696                         elif val1 and key1.split('_')[0] == que_id and (key1.split('_')[1] == "single"  or (len(key1.split('_')) > 2 and key1.split('_')[2] == 'multi')):
697                             error = False
698                             if que_rec['is_validation_require'] and que_rec['validation_type'] == 'must_be_specific_length':
699                                 if (not val1 and  que_rec['validation_minimum_no']) or len(val1) <  que_rec['validation_minimum_no'] or len(val1) > que_rec['validation_maximum_no']:
700                                     error = True
701                             elif que_rec['is_validation_require'] and que_rec['validation_type'] in ['must_be_whole_number', 'must_be_decimal_number', 'must_be_date']:
702                                 error = False
703                                 try:
704                                     if que_rec['validation_type'] == 'must_be_whole_number':
705                                         value = int(val1)
706                                         if value <  que_rec['validation_minimum_no'] or value > que_rec['validation_maximum_no']:
707                                             error = True
708                                     elif que_rec['validation_type'] == 'must_be_decimal_number':
709                                         value = float(val1)
710                                         if value <  que_rec['validation_minimum_float'] or value > que_rec['validation_maximum_float']:
711                                             error = True
712                                     elif que_rec['validation_type'] == 'must_be_date':
713                                         value = datetime.datetime.strptime(val1, "%Y-%m-%d")
714                                         if value <  datetime.datetime.strptime(que_rec['validation_minimum_date'], "%Y-%m-%d") or value >  datetime.datetime.strptime(que_rec['validation_maximum_date'], "%Y-%m-%d"):
715                                             error = True
716                                 except:
717                                     error = True
718                             elif que_rec['is_validation_require'] and que_rec['validation_type'] == 'must_be_email_address':
719                                 import re
720                                 if re.match("^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", val1) == None:
721                                         error = True
722                             if error:
723                                 for res in resp_id_list:
724                                     sur_name_read['store_ans'].pop(res)
725                                 raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "'  \n" + tools.ustr(que_rec['validation_valid_err_msg']))
726
727                             if key1.split('_')[1] == "single" :
728                                 resp_obj.write(cr, uid, resp_id, {'single_text':val1})
729                             else:
730                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id':resp_id, 'answer_id':key1.split('_')[1], 'answer' : val1})
731
732                             sur_name_read['store_ans'][resp_id].update({key1:val1})
733                             select_count += 1
734
735                         elif val1 and que_id == key1.split('_')[0] and len(key1.split('_')) > 2 and key1.split('_')[2] == 'numeric':
736                             if not val1=="0":
737                                 try:
738                                     numeric_sum += int(val1)
739                                     ans_create_id = res_ans_obj.create(cr, uid, {'response_id':resp_id, 'answer_id':key1.split('_')[1], 'answer' : val1})
740                                     sur_name_read['store_ans'][resp_id].update({key1:val1})
741                                     select_count += 1
742                                 except:
743                                     for res in resp_id_list:
744                                         sur_name_read['store_ans'].pop(res)
745                                     raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' \n" + _("Please enter an integer value"))
746
747                         elif val1 and que_id == key1.split('_')[0] and len(key1.split('_')) == 3:
748                             if type(val1) == type('') or type(val1) == type(u''):
749                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id':resp_id, 'answer_id':key1.split('_')[1], 'column_id' : key1.split('_')[2], 'value_choice' : val1})
750                                 sur_name_read['store_ans'][resp_id].update({key1:val1})
751                             else:
752                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id':resp_id, 'answer_id':key1.split('_')[1], 'column_id' : key1.split('_')[2]})
753                                 sur_name_read['store_ans'][resp_id].update({key1:True})
754
755                             matrix_list.append(key1.split('_')[0] + '_' + key1.split('_')[1])
756                             select_count += 1
757
758                         elif val1 and que_id == key1.split('_')[0] and len(key1.split('_')) == 2:
759                             ans_create_id = res_ans_obj.create(cr, uid, {'response_id':resp_id, 'answer_id':key1.split('_')[-1], 'answer' : val1})
760                             sur_name_read['store_ans'][resp_id].update({key1:val1})
761                             select_count += 1
762                         surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'store_ans':sur_name_read['store_ans']})
763
764                     for key,val in vals.items():
765                         if val and key.split('_')[1] == "commentcolumn" and key.split('_')[0] == que_id:
766                             for res_id in response_list:
767                                 if key.split('_')[2] in res_id.split('_')[1]:
768                                     a = res_ans_obj.write(cr, uid, [res_id.split('_')[0]], {'comment_field':val})
769                                     sur_name_read['store_ans'][resp_id].update({key:val})
770
771                     if comment_field and comment_value:
772                         for res in resp_id_list:
773                             sur_name_read['store_ans'].pop(res)
774                         raise osv.except_osv(_('Warning !'), "'" + que_rec['question']  + "' " + tools.ustr(que_rec['make_comment_field_err_msg']))
775
776                     if que_rec['type'] == "rating_scale" and que_rec['rating_allow_one_column_require'] and len(selected_value) > len(list(set(selected_value))):
777                         for res in resp_id_list:
778                             sur_name_read['store_ans'].pop(res)
779                         raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "'\n" + _("You cannot select the same answer more than one time"))
780
781                     if not select_count:
782                         resp_obj.write(cr, uid, resp_id, {'state':'skip'})
783
784                     if que_rec['numeric_required_sum'] and numeric_sum > que_rec['numeric_required_sum']:
785                         for res in resp_id_list:
786                             sur_name_read['store_ans'].pop(res)
787                         raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['numeric_required_sum_err_msg']))
788
789                     if que_rec['type'] in ['multiple_textboxes_diff_type', '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'] and que_rec['is_require_answer']:
790                         if matrix_list:
791                             if (que_rec['required_type'] == 'all' and len(list(set(matrix_list))) < len(que_rec['answer_choice_ids'])) or \
792                             (que_rec['required_type'] == 'at least' and len(list(set(matrix_list))) < que_rec['req_ans']) or \
793                             (que_rec['required_type'] == 'at most' and len(list(set(matrix_list))) > que_rec['req_ans']) or \
794                             (que_rec['required_type'] == 'exactly' and len(list(set(matrix_list))) != que_rec['req_ans']) or \
795                             (que_rec['required_type'] == 'a range' and (len(list(set(matrix_list))) < que_rec['minimum_req_ans'] or len(list(set(matrix_list))) > que_rec['maximum_req_ans'])):
796                                 for res in resp_id_list:
797                                     sur_name_read['store_ans'].pop(res)
798                                 raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg']))
799
800                         elif (que_rec['required_type'] == 'all' and select_count < len(que_rec['answer_choice_ids'])) or \
801                             (que_rec['required_type'] == 'at least' and select_count < que_rec['req_ans']) or \
802                             (que_rec['required_type'] == 'at most' and select_count > que_rec['req_ans']) or \
803                             (que_rec['required_type'] == 'exactly' and select_count != que_rec['req_ans']) or \
804                             (que_rec['required_type'] == 'a range' and (select_count < que_rec['minimum_req_ans'] or select_count > que_rec['maximum_req_ans'])):
805                             for res in resp_id_list:
806                                 sur_name_read['store_ans'].pop(res)
807                             raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg']))
808
809                     if que_rec['type'] in ['multiple_choice_only_one_ans','single_textbox','comment'] and  que_rec['is_require_answer'] and select_count <= 0:
810                         for res in resp_id_list:
811                             sur_name_read['store_ans'].pop(res)
812                         raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg']))
813
814         else:
815             resp_id_list = []
816             for update in click_update:
817                 que_rec = que_obj.read(cr, uid , [int(sur_name_read['store_ans'][update]['question_id'])], [])[0]
818                 res_ans_obj.unlink(cr, uid,res_ans_obj.search(cr, uid, [('response_id', '=', update)]))
819                 surv_tbl_column_obj.unlink(cr, uid,surv_tbl_column_obj.search(cr, uid, [('response_table_id', '=', update)]))
820                 resp_id_list.append(update)
821                 sur_name_read['store_ans'].update({update:{'question_id':sur_name_read['store_ans'][update]['question_id']}})
822                 surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'store_ans':sur_name_read['store_ans']})
823                 select_count = 0
824                 numeric_sum = 0
825                 selected_value = []
826                 matrix_list = []
827                 comment_field = False
828                 comment_value = False
829                 response_list = []
830
831                 for key, val in vals.items():
832                     ans_id_len = key.split('_')
833                     if ans_id_len[0] == sur_name_read['store_ans'][update]['question_id']:
834                         if val and key.split('_')[1] == "table":
835                             surv_tbl_column_obj.create(cr, uid, {'response_table_id' : update,'column_id':key.split('_')[2], 'name':key.split('_')[3], 'value' : val})
836                             sur_name_read['store_ans'][update].update({key:val})
837                             resp_obj.write(cr, uid, update, {'state': 'done'})
838
839                         elif val and key.split('_')[1] == "otherfield" :
840                             comment_field = True
841                             sur_name_read['store_ans'][update].update({key:val})
842                             select_count += 1
843                             surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'store_ans':sur_name_read['store_ans']})
844                             continue
845
846                         elif val and key.split('_')[1] == "selection":
847                             if len(key.split('_')) > 2:
848                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id':update, 'answer_id':key.split('_')[-1], 'column_id' : val})
849                                 selected_value.append(val)
850                                 response_list.append(str(ans_create_id) + "_" + str(key.split('_')[-1]))
851                             else:
852                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id':update, 'answer_id': val})
853                             resp_obj.write(cr, uid, update, {'state': 'done'})
854                             sur_name_read['store_ans'][update].update({key:val})
855                             select_count += 1
856
857                         elif key.split('_')[1] == "other":
858                             if not val:
859                                 comment_value = True
860                             else:
861                                 error = False
862                                 if que_rec['is_comment_require'] and  que_rec['comment_valid_type'] == 'must_be_specific_length':
863                                     if (not val and  que_rec['comment_minimum_no']) or len(val) <  que_rec['comment_minimum_no'] or len(val) > que_rec['comment_maximum_no']:
864                                         error = True
865                                 elif que_rec['is_comment_require'] and  que_rec['comment_valid_type'] in ['must_be_whole_number', 'must_be_decimal_number', 'must_be_date']:
866                                     try:
867                                         if que_rec['comment_valid_type'] == 'must_be_whole_number':
868                                             value = int(val)
869                                             if value <  que_rec['comment_minimum_no'] or value > que_rec['comment_maximum_no']:
870                                                 error = True
871                                         elif que_rec['comment_valid_type'] == 'must_be_decimal_number':
872                                             value = float(val)
873                                             if value <  que_rec['comment_minimum_float'] or value > que_rec['comment_maximum_float']:
874                                                 error = True
875                                         elif que_rec['comment_valid_type'] == 'must_be_date':
876                                             value = datetime.datetime.strptime(val, "%Y-%m-%d")
877                                             if value <  datetime.datetime.strptime(que_rec['comment_minimum_date'], "%Y-%m-%d") or value >  datetime.datetime.strptime(que_rec['comment_maximum_date'], "%Y-%m-%d"):
878                                                 error = True
879                                     except:
880                                         error = True
881                                 elif que_rec['is_comment_require'] and  que_rec['comment_valid_type'] == 'must_be_email_address':
882                                     import re
883                                     if re.match("^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", val) == None:
884                                             error = True
885                                 if error:
886                                     raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "'  \n" + tools.ustr(que_rec['comment_valid_err_msg']))
887                                 resp_obj.write(cr, uid, update, {'comment':val,'state': 'done'})
888                                 sur_name_read['store_ans'][update].update({key:val})
889
890                         elif val and key.split('_')[1] == "comment":
891                             resp_obj.write(cr, uid, update, {'comment':val,'state': 'done'})
892                             sur_name_read['store_ans'][update].update({key:val})
893                             select_count += 1
894
895                         elif val and (key.split('_')[1] == "single"  or (len(key.split('_')) > 2 and key.split('_')[2] == 'multi')):
896                             error = False
897                             if que_rec['is_validation_require'] and que_rec['validation_type'] == 'must_be_specific_length':
898                                 if (not val and  que_rec['validation_minimum_no']) or len(val) <  que_rec['validation_minimum_no'] or len(val) > que_rec['validation_maximum_no']:
899                                     error = True
900                             elif que_rec['is_validation_require'] and que_rec['validation_type'] in ['must_be_whole_number', 'must_be_decimal_number', 'must_be_date']:
901                                 error = False
902                                 try:
903                                     if que_rec['validation_type'] == 'must_be_whole_number':
904                                         value = int(val)
905                                         if value <  que_rec['validation_minimum_no'] or value > que_rec['validation_maximum_no']:
906                                             error = True
907                                     elif que_rec['validation_type'] == 'must_be_decimal_number':
908                                         value = float(val)
909                                         if value <  que_rec['validation_minimum_float'] or value > que_rec['validation_maximum_float']:
910                                             error = True
911                                     elif que_rec['validation_type'] == 'must_be_date':
912                                         value = datetime.datetime.strptime(val, "%Y-%m-%d")
913                                         if value <  datetime.datetime.strptime(que_rec['validation_minimum_date'], "%Y-%m-%d") or value >  datetime.datetime.strptime(que_rec['validation_maximum_date'], "%Y-%m-%d"):
914                                             error = True
915                                 except Exception ,e:
916                                     error = True
917                             elif que_rec['is_validation_require'] and  que_rec['validation_type'] == 'must_be_email_address':
918                                 import re
919                                 if re.match("^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", val) == None:
920                                         error = True
921                             if error:
922                                 raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "'  \n" + tools.ustr(que_rec['validation_valid_err_msg']))
923                             if key.split('_')[1] == "single" :
924                                 resp_obj.write(cr, uid, update, {'single_text':val,'state': 'done'})
925                             else:
926                                 resp_obj.write(cr, uid, update, {'state': 'done'})
927                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id':update, 'answer_id':ans_id_len[1], 'answer' : val})
928                             sur_name_read['store_ans'][update].update({key:val})
929                             select_count += 1
930
931                         elif val and len(key.split('_')) > 2 and key.split('_')[2] == 'numeric':
932                             if not val=="0":
933                                 try:
934                                     numeric_sum += int(val)
935                                     resp_obj.write(cr, uid, update, {'state': 'done'})
936                                     ans_create_id = res_ans_obj.create(cr, uid, {'response_id': update, 'answer_id':ans_id_len[1], 'answer' : val})
937                                     sur_name_read['store_ans'][update].update({key:val})
938                                     select_count += 1
939                                 except:
940                                     raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "'\n" + _("Please enter an integer value"))
941
942                         elif val and len(key.split('_')) == 3:
943                             resp_obj.write(cr, uid, update, {'state': 'done'})
944                             if type(val) == type('') or type(val) == type(u''):
945                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id': update, 'answer_id':ans_id_len[1], 'column_id' : ans_id_len[2], 'value_choice' : val})
946                                 sur_name_read['store_ans'][update].update({key:val})
947                             else:
948                                 ans_create_id = res_ans_obj.create(cr, uid, {'response_id': update, 'answer_id':ans_id_len[1], 'column_id' : ans_id_len[2]})
949                                 sur_name_read['store_ans'][update].update({key:True})
950                             matrix_list.append(key.split('_')[0] + '_' + key.split('_')[1])
951                             select_count += 1
952
953                         elif val and len(key.split('_')) == 2:
954                             resp_obj.write(cr, uid, update, {'state': 'done'})
955                             ans_create_id = res_ans_obj.create(cr, uid, {'response_id': update, 'answer_id':ans_id_len[-1], 'answer' : val})
956                             sur_name_read['store_ans'][update].update({key:val})
957                             select_count += 1
958                         surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'store_ans': sur_name_read['store_ans']})
959
960                 for key,val in vals.items():
961                     if val and key.split('_')[1] == "commentcolumn" and key.split('_')[0] == sur_name_read['store_ans'][update]['question_id']:
962                         for res_id in response_list:
963                             if key.split('_')[2] in res_id.split('_')[1]:
964                                 a = res_ans_obj.write(cr, uid, [res_id.split('_')[0]], {'comment_field':val})
965                                 sur_name_read['store_ans'][update].update({key:val})
966
967                 if comment_field and comment_value:
968                     raise osv.except_osv(_('Warning !'), "'" + que_rec['question']  + "' " + tools.ustr(que_rec['make_comment_field_err_msg']))
969
970                 if que_rec['type'] == "rating_scale" and que_rec['rating_allow_one_column_require'] and len(selected_value) > len(list(set(selected_value))):
971                     raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "\n" + _("You cannot select same answer more than one time'"))
972
973                 if que_rec['numeric_required_sum'] and numeric_sum > que_rec['numeric_required_sum']:
974                     raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['numeric_required_sum_err_msg']))
975
976                 if not select_count:
977                     resp_obj.write(cr, uid, update, {'state': 'skip'})
978
979                 if que_rec['type'] in ['multiple_textboxes_diff_type','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'] and que_rec['is_require_answer']:
980                     if matrix_list:
981                         if (que_rec['required_type'] == 'all' and len(list(set(matrix_list))) < len(que_rec['answer_choice_ids'])) or \
982                         (que_rec['required_type'] == 'at least' and len(list(set(matrix_list))) < que_rec['req_ans']) or \
983                         (que_rec['required_type'] == 'at most' and len(list(set(matrix_list))) > que_rec['req_ans']) or \
984                         (que_rec['required_type'] == 'exactly' and len(list(set(matrix_list))) != que_rec['req_ans']) or \
985                         (que_rec['required_type'] == 'a range' and (len(list(set(matrix_list))) < que_rec['minimum_req_ans'] or len(list(set(matrix_list))) > que_rec['maximum_req_ans'])):
986                             raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg']))
987
988                     elif (que_rec['required_type'] == 'all' and select_count < len(que_rec['answer_choice_ids'])) or \
989                         (que_rec['required_type'] == 'at least' and select_count < que_rec['req_ans']) or \
990                         (que_rec['required_type'] == 'at most' and select_count > que_rec['req_ans']) or \
991                         (que_rec['required_type'] == 'exactly' and select_count != que_rec['req_ans']) or \
992                         (que_rec['required_type'] == 'a range' and (select_count < que_rec['minimum_req_ans'] or select_count > que_rec['maximum_req_ans'])):
993                             raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg']))
994
995                 if que_rec['type'] in ['multiple_choice_only_one_ans','single_textbox','comment'] and  que_rec['is_require_answer'] and select_count <= 0:
996                     raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg']))
997
998         return survey_question_wiz_id
999
1000     def action_new_question(self,cr, uid, ids, context=None):
1001         """
1002         New survey.Question form.
1003         """
1004         if context is None:
1005             context = {}
1006         for key,val in context.items():
1007             if type(key) == type(True):
1008                 context.pop(key)
1009         view_id = self.pool.get('ir.ui.view').search(cr,uid,[('model','=','survey.question'),\
1010                             ('name','=','survey_question_wizard_test')])
1011         return {
1012             'view_type': 'form',
1013             "view_mode": 'form',
1014             'res_model': 'survey.question',
1015             'type': 'ir.actions.act_window',
1016             'target': 'new',
1017             'view_id': view_id,
1018             'context': context
1019         }
1020
1021     def action_new_page(self, cr, uid, ids, context=None):
1022         """
1023         New survey.Page form.
1024         """
1025         if context is None:
1026             context = {}
1027         for key,val in context.items():
1028             if type(key) == type(True):
1029                 context.pop(key)
1030         view_id = self.pool.get('ir.ui.view').search(cr,uid,[('model','=','survey.page'),\
1031                                         ('name','=','survey_page_wizard_test')])
1032         return {
1033             'view_type': 'form',
1034             "view_mode": 'form',
1035             'res_model': 'survey.page',
1036             'type': 'ir.actions.act_window',
1037             'target': 'new',
1038             'view_id': view_id,
1039             'context': context
1040         }
1041
1042     def action_edit_page(self,cr, uid, ids, context=None):
1043         """
1044         Edit survey.page.
1045         """
1046         if context is None:
1047             context = {}
1048         for key,val in context.items():
1049             if type(key) == type(True):
1050                 context.pop(key)
1051         view_id = self.pool.get('ir.ui.view').search(cr,uid,[('model','=','survey.page'),\
1052                                 ('name','=','survey_page_wizard_test')])
1053         return {
1054             'view_type': 'form',
1055             "view_mode": 'form',
1056             'res_model': 'survey.page',
1057             'type': 'ir.actions.act_window',
1058             'target': 'new',
1059             'res_id': int(context.get('page_id',0)),
1060             'view_id': view_id,
1061             'context': context
1062         }
1063
1064     def action_delete_page(self,cr, uid, ids, context=None):
1065         """
1066         Delete survey.page.
1067         """
1068         if context is None:
1069             context = {}
1070         for key,val in context.items():
1071             if type(key) == type(True):
1072                 context.pop(key)
1073
1074         self.pool.get('survey.page').unlink(cr, uid, [context.get('page_id',False)])
1075         search_id = self.pool.get('ir.ui.view').search(cr,uid,[('model','=','survey.question.wiz'),\
1076                                             ('name','=','Survey Search')])
1077         surv_name_wiz = self.pool.get('survey.name.wiz')
1078         surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], \
1079                     {'transfer':True, 'page_no' : context.get('page_number',False) })
1080         return {
1081             'view_type': 'form',
1082             "view_mode": 'form',
1083             'res_model': 'survey.question.wiz',
1084             'type': 'ir.actions.act_window',
1085             'target': 'new',
1086             'search_view_id':search_id[0],
1087             'context': context
1088         }
1089
1090     def action_edit_question(self,cr, uid, ids, context=None):
1091         """
1092         Edit survey.question.
1093         """
1094         if context is None:
1095             context = {}
1096         for key,val in context.items():
1097             if type(key) == type(True):
1098                 context.pop(key)
1099         view_id = self.pool.get('ir.ui.view').search(cr,uid,[('model','=','survey.question'),\
1100                                 ('name','=','survey_question_wizard_test')])
1101         return {
1102             'view_type': 'form',
1103             "view_mode": 'form',
1104             'res_model': 'survey.question',
1105             'type': 'ir.actions.act_window',
1106             'target': 'new',
1107             'res_id' : int(context.get('question_id',0)),
1108             'view_id': view_id,
1109             'context': context
1110         }
1111
1112     def action_delete_question(self,cr, uid, ids, context=None):
1113         """
1114         Delete survey.question.
1115         """
1116         if context is None:
1117             context = {}
1118         for key,val in context.items():
1119             if type(key) == type(True):
1120                 context.pop(key)
1121
1122         que_obj = self.pool.get('survey.question')
1123         que_obj.unlink(cr, uid, [context.get('question_id',False)])
1124         search_id = self.pool.get('ir.ui.view').search(cr,uid,[('model','=','survey.question.wiz'),\
1125                                         ('name','=','Survey Search')])
1126         surv_name_wiz = self.pool.get('survey.name.wiz')
1127         surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)],\
1128                      {'transfer':True, 'page_no' : context.get('page_number',0) })
1129         return {
1130                 'view_type': 'form',
1131                 "view_mode": 'form',
1132                 'res_model': 'survey.question.wiz',
1133                 'type': 'ir.actions.act_window',
1134                 'target': 'new',
1135                 'search_view_id': search_id[0],
1136                 'context': context
1137                 }
1138
1139     def action_forward_previous(self, cr, uid, ids, context=None):
1140         """
1141         Goes to previous Survey Answer.
1142         """
1143         if context is None:
1144             context = {}
1145         search_obj = self.pool.get('ir.ui.view')
1146         surv_name_wiz = self.pool.get('survey.name.wiz')
1147         search_id = search_obj.search(cr,uid,[('model','=','survey.question.wiz'),\
1148                                               ('name','=','Survey Search')])
1149         wiz_id = surv_name_wiz.create(cr,uid, {'survey_id': context.get('survey_id',False),'page_no' :-1,'page':'next','transfer' :1,'response':0})
1150         context.update({'sur_name_id' :wiz_id, 'response_no': context.get('response_no',0) - 1})
1151
1152         if context.get('response_no',0) + 1 > len(context.get('response_id',0)):
1153             return {}
1154         return {
1155             'view_type': 'form',
1156             "view_mode": 'form',
1157             'res_model': 'survey.question.wiz',
1158             'type': 'ir.actions.act_window',
1159             'target': 'new',
1160             'search_view_id': search_id[0],
1161             'context': context
1162         }
1163
1164     def action_forward_next(self, cr, uid, ids, context=None):
1165         """
1166         Goes to Next Survey Answer.
1167         """
1168         if context is None:
1169             context = {}
1170         search_obj = self.pool.get('ir.ui.view')
1171         surv_name_wiz = self.pool.get('survey.name.wiz')
1172         search_id = search_obj.search(cr,uid,[('model','=','survey.question.wiz'),\
1173                                     ('name','=','Survey Search')])
1174         wiz_id = surv_name_wiz.create(cr,uid, {'survey_id' : context.get('survey_id',False),'page_no' :-1,'page':'next','transfer' :1,'response':0})
1175         context.update({'sur_name_id' :wiz_id, 'response_no' : context.get('response_no',0) + 1})
1176
1177         if context.get('response_no',0) + 1 > len(context.get('response_id',0)):
1178             return {}
1179         return {
1180             'view_type': 'form',
1181             "view_mode": 'form',
1182             'res_model': 'survey.question.wiz',
1183             'type': 'ir.actions.act_window',
1184             'target': 'new',
1185             'search_view_id': search_id[0],
1186             'context': context
1187         }
1188
1189     def action_next(self, cr, uid, ids, context=None):
1190         """
1191         Goes to Next page.
1192         """
1193         if context is None:
1194             context = {}
1195         surv_name_wiz = self.pool.get('survey.name.wiz')
1196         search_obj = self.pool.get('ir.ui.view')
1197         search_id = search_obj.search(cr,uid,[('model','=','survey.question.wiz'),('name','=','Survey Search')])
1198         surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'transfer':True, 'page':'next'})
1199         return {
1200             'view_type': 'form',
1201             "view_mode": 'form',
1202             'res_model': 'survey.question.wiz',
1203             'type': 'ir.actions.act_window',
1204             'target': 'new',
1205             'search_view_id': search_id[0],
1206             'context': context
1207         }
1208
1209     def action_previous(self, cr, uid, ids, context=None):
1210         """
1211         Goes to previous page.
1212         """
1213         if context is None:
1214             context = {}
1215         surv_name_wiz = self.pool.get('survey.name.wiz')
1216         search_obj = self.pool.get('ir.ui.view')
1217         search_id = search_obj.search(cr,uid,[('model','=','survey.question.wiz'),\
1218                                     ('name','=','Survey Search')])
1219         surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'transfer':True, 'page':'previous'})
1220         return {
1221             'view_type': 'form',
1222             "view_mode": 'form',
1223             'res_model': 'survey.question.wiz',
1224             'type': 'ir.actions.act_window',
1225             'target': 'new',
1226             'search_view_id': search_id[0],
1227             'context': context
1228         }
1229
1230 survey_question_wiz()
1231
1232 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: