[FIX] hr_attendance: invert condition
[odoo/odoo.git] / addons / report_webkit / webkit_report.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
5 # All Right Reserved
6 #
7 # Author : Nicolas Bessi (Camptocamp)
8 # Contributor(s) : Florent Xicluna (Wingo SA)
9 #
10 # WARNING: This program as such is intended to be used by professional
11 # programmers who take the whole responsability of assessing all potential
12 # consequences resulting from its eventual inadequacies and bugs
13 # End users who are looking for a ready-to-use solution with commercial
14 # garantees and support are strongly adviced to contract a Free Software
15 # Service Company
16 #
17 # This program is Free Software; you can redistribute it and/or
18 # modify it under the terms of the GNU General Public License
19 # as published by the Free Software Foundation; either version 2
20 # of the License, or (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 #
27 # You should have received a copy of the GNU General Public License
28 # along with this program; if not, write to the Free Software
29 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
30 #
31 ##############################################################################
32
33 import subprocess
34 import os
35 import sys
36 import report
37 import tempfile
38 import time
39 import logging
40
41 from mako.template import Template
42 from mako.lookup import TemplateLookup
43 from mako import exceptions
44
45 import netsvc
46 import pooler
47 from report_helper import WebKitHelper
48 from report.report_sxw import *
49 import addons
50 import tools
51 from tools.translate import _
52 from osv.osv import except_osv
53
54 _logger = logging.getLogger(__name__)
55
56 def mako_template(text):
57     """Build a Mako template.
58
59     This template uses UTF-8 encoding
60     """
61     tmp_lookup  = TemplateLookup() #we need it in order to allow inclusion and inheritance
62     return Template(text, input_encoding='utf-8', output_encoding='utf-8', lookup=tmp_lookup)
63
64 class WebKitParser(report_sxw):
65     """Custom class that use webkit to render HTML reports
66        Code partially taken from report openoffice. Thanks guys :)
67     """
68     def __init__(self, name, table, rml=False, parser=False,
69         header=True, store=False):
70         self.parser_instance = False
71         self.localcontext = {}
72         report_sxw.__init__(self, name, table, rml, parser,
73             header, store)
74
75     def get_lib(self, cursor, uid):
76         """Return the lib wkhtml path"""
77         proxy = self.pool.get('ir.config_parameter')
78         webkit_path = proxy.get_param(cursor, uid, 'webkit_path')
79
80         if not webkit_path:
81             try:
82                 defpath = os.environ.get('PATH', os.defpath).split(os.pathsep)
83                 if hasattr(sys, 'frozen'):
84                     defpath.append(os.getcwd())
85                     if tools.config['root_path']:
86                         defpath.append(os.path.dirname(tools.config['root_path']))
87                 webkit_path = tools.which('wkhtmltopdf', path=os.pathsep.join(defpath))
88             except IOError:
89                 webkit_path = None
90
91         if webkit_path:
92             return webkit_path
93
94         raise except_osv(
95                          _('Wkhtmltopdf library path is not set'),
96                          _('Please install executable on your system' \
97                          ' (sudo apt-get install wkhtmltopdf) or download it from here:' \
98                          ' http://code.google.com/p/wkhtmltopdf/downloads/list and set the' \
99                          ' path in the ir.config_parameter with the webkit_path key.' \
100                          'Minimal version is 0.9.9')
101                         )
102
103     def generate_pdf(self, comm_path, report_xml, header, footer, html_list, webkit_header=False):
104         """Call webkit in order to generate pdf"""
105         if not webkit_header:
106             webkit_header = report_xml.webkit_header
107         tmp_dir = tempfile.gettempdir()
108         out_filename = tempfile.mktemp(suffix=".pdf", prefix="webkit.tmp.")
109         file_to_del = [out_filename]
110         if comm_path:
111             command = [comm_path]
112         else:
113             command = ['wkhtmltopdf']
114
115         command.append('--quiet')
116         # default to UTF-8 encoding.  Use <meta charset="latin-1"> to override.
117         command.extend(['--encoding', 'utf-8'])
118         if header :
119             head_file = file( os.path.join(
120                                   tmp_dir,
121                                   str(time.time()) + '.head.html'
122                                  ),
123                                 'w'
124                             )
125             head_file.write(header)
126             head_file.close()
127             file_to_del.append(head_file.name)
128             command.extend(['--header-html', head_file.name])
129         if footer :
130             foot_file = file(  os.path.join(
131                                   tmp_dir,
132                                   str(time.time()) + '.foot.html'
133                                  ),
134                                 'w'
135                             )
136             foot_file.write(footer)
137             foot_file.close()
138             file_to_del.append(foot_file.name)
139             command.extend(['--footer-html', foot_file.name])
140
141         if webkit_header.margin_top :
142             command.extend(['--margin-top', str(webkit_header.margin_top).replace(',', '.')])
143         if webkit_header.margin_bottom :
144             command.extend(['--margin-bottom', str(webkit_header.margin_bottom).replace(',', '.')])
145         if webkit_header.margin_left :
146             command.extend(['--margin-left', str(webkit_header.margin_left).replace(',', '.')])
147         if webkit_header.margin_right :
148             command.extend(['--margin-right', str(webkit_header.margin_right).replace(',', '.')])
149         if webkit_header.orientation :
150             command.extend(['--orientation', str(webkit_header.orientation).replace(',', '.')])
151         if webkit_header.format :
152             command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
153         count = 0
154         for html in html_list :
155             html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
156             count += 1
157             html_file.write(html)
158             html_file.close()
159             file_to_del.append(html_file.name)
160             command.append(html_file.name)
161         command.append(out_filename)
162         stderr_fd, stderr_path = tempfile.mkstemp(text=True)
163         file_to_del.append(stderr_path)
164         try:
165             status = subprocess.call(command, stderr=stderr_fd)
166             os.close(stderr_fd) # ensure flush before reading
167             stderr_fd = None # avoid closing again in finally block
168             fobj = open(stderr_path, 'r')
169             error_message = fobj.read()
170             fobj.close()
171             if not error_message:
172                 error_message = _('No diagnosis message was provided')
173             else:
174                 error_message = _('The following diagnosis message was provided:\n') + error_message
175             if status :
176                 raise except_osv(_('Webkit error' ),
177                                  _("The command 'wkhtmltopdf' failed with error code = %s. Message: %s") % (status, error_message))
178             pdf_file = open(out_filename, 'rb')
179             pdf = pdf_file.read()
180             pdf_file.close()
181         finally:
182             if stderr_fd is not None:
183                 os.close(stderr_fd)
184             for f_to_del in file_to_del:
185                 try:
186                     os.unlink(f_to_del)
187                 except (OSError, IOError), exc:
188                     _logger.error('cannot remove file %s: %s', f_to_del, exc)
189         return pdf
190
191     def translate_call(self, src):
192         """Translate String."""
193         ir_translation = self.pool.get('ir.translation')
194         res = ir_translation._get_source(self.parser_instance.cr, self.parser_instance.uid,
195                                          None, 'report', self.parser_instance.localcontext.get('lang', 'en_US'), src)
196         if not res :
197             return src
198         return res
199
200     # override needed to keep the attachments storing procedure
201     def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
202         """generate the PDF"""
203
204         if context is None:
205             context={}
206         htmls = []
207         if report_xml.report_type != 'webkit':
208             return super(WebKitParser,self).create_single_pdf(cursor, uid, ids, data, report_xml, context=context)
209
210         self.parser_instance = self.parser(cursor,
211                                            uid,
212                                            self.name2,
213                                            context=context)
214
215         self.pool = pooler.get_pool(cursor.dbname)
216         objs = self.getObjects(cursor, uid, ids, context)
217         self.parser_instance.set_context(objs, data, ids, report_xml.report_type)
218
219         template =  False
220
221         if report_xml.report_file :
222             path = addons.get_module_resource(*report_xml.report_file.split(os.path.sep))
223             if os.path.exists(path) :
224                 template = file(path).read()
225         if not template and report_xml.report_webkit_data :
226             template =  report_xml.report_webkit_data
227         if not template :
228             raise except_osv(_('Error!'), _('Webkit report template not found!'))
229         header = report_xml.webkit_header.html
230         footer = report_xml.webkit_header.footer_html
231         if not header and report_xml.header:
232             raise except_osv(
233                   _('No header defined for this Webkit report!'),
234                   _('Please set a header in company settings.')
235               )
236         if not report_xml.header :
237             header = ''
238             default_head = addons.get_module_resource('report_webkit', 'default_header.html')
239             with open(default_head,'r') as f:
240                 header = f.read()
241         css = report_xml.webkit_header.css
242         if not css :
243             css = ''
244
245         #default_filters=['unicode', 'entity'] can be used to set global filter
246         body_mako_tpl = mako_template(template)
247         helper = WebKitHelper(cursor, uid, report_xml.id, context)
248         if report_xml.precise_mode:
249             for obj in objs:
250                 self.parser_instance.localcontext['objects'] = [obj]
251                 try :
252                     html = body_mako_tpl.render(helper=helper,
253                                                 css=css,
254                                                 _=self.translate_call,
255                                                 **self.parser_instance.localcontext)
256                     htmls.append(html)
257                 except Exception:
258                     msg = exceptions.text_error_template().render()
259                     _logger.error(msg)
260                     raise except_osv(_('Webkit render!'), msg)
261         else:
262             try :
263                 html = body_mako_tpl.render(helper=helper,
264                                             css=css,
265                                             _=self.translate_call,
266                                             **self.parser_instance.localcontext)
267                 htmls.append(html)
268             except Exception:
269                 msg = exceptions.text_error_template().render()
270                 _logger.error(msg)
271                 raise except_osv(_('Webkit render!'), msg)
272         head_mako_tpl = mako_template(header)
273         try :
274             head = head_mako_tpl.render(helper=helper,
275                                         css=css,
276                                         _=self.translate_call,
277                                         _debug=False,
278                                         **self.parser_instance.localcontext)
279         except Exception:
280             raise except_osv(_('Webkit render!'),
281                 exceptions.text_error_template().render())
282         foot = False
283         if footer :
284             foot_mako_tpl = mako_template(footer)
285             try :
286                 foot = foot_mako_tpl.render(helper=helper,
287                                             css=css,
288                                             _=self.translate_call,
289                                             **self.parser_instance.localcontext)
290             except:
291                 msg = exceptions.text_error_template().render()
292                 _logger.error(msg)
293                 raise except_osv(_('Webkit render!'), msg)
294         if report_xml.webkit_debug :
295             try :
296                 deb = head_mako_tpl.render(helper=helper,
297                                            css=css,
298                                            _debug=tools.ustr("\n".join(htmls)),
299                                            _=self.translate_call,
300                                            **self.parser_instance.localcontext)
301             except Exception:
302                 msg = exceptions.text_error_template().render()
303                 _logger.error(msg)
304                 raise except_osv(_('Webkit render!'), msg)
305             return (deb, 'html')
306         bin = self.get_lib(cursor, uid)
307         pdf = self.generate_pdf(bin, report_xml, head, foot, htmls)
308         return (pdf, 'pdf')
309
310
311     def create(self, cursor, uid, ids, data, context=None):
312         """We override the create function in order to handle generator
313            Code taken from report openoffice. Thanks guys :) """
314         pool = pooler.get_pool(cursor.dbname)
315         ir_obj = pool.get('ir.actions.report.xml')
316         report_xml_ids = ir_obj.search(cursor, uid,
317                 [('report_name', '=', self.name[7:])], context=context)
318         if report_xml_ids:
319
320             report_xml = ir_obj.browse(cursor,
321                                        uid,
322                                        report_xml_ids[0],
323                                        context=context)
324             report_xml.report_rml = None
325             report_xml.report_rml_content = None
326             report_xml.report_sxw_content_data = None
327             report_xml.report_sxw_content = None
328             report_xml.report_sxw = None
329         else:
330             return super(WebKitParser, self).create(cursor, uid, ids, data, context)
331         if report_xml.report_type != 'webkit' :
332             return super(WebKitParser, self).create(cursor, uid, ids, data, context)
333         result = self.create_source_pdf(cursor, uid, ids, data, report_xml, context)
334         if not result:
335             return (False,False)
336         return result
337
338 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: