Merge lp:~bcim/openerp-reporting-engines/7.0-report_xls into lp:openerp-reporting-engines

Proposed by Jacques-Etienne Baudoux
Status: Merged
Merged at revision: 2
Proposed branch: lp:~bcim/openerp-reporting-engines/7.0-report_xls
Merge into: lp:openerp-reporting-engines
Diff against target: 361 lines (+341/-0)
4 files modified
report_xls/__init__.py (+24/-0)
report_xls/__openerp__.py (+44/-0)
report_xls/report_xls.py (+224/-0)
report_xls/utils.py (+49/-0)
To merge this branch: bzr merge lp:~bcim/openerp-reporting-engines/7.0-report_xls
Reviewer Review Type Date Requested Status
Joël Grand-Guillaume @ camptocamp code review, no tests Approve
Jacques-Etienne Baudoux (community) test Approve
Review via email: mp+195412@code.launchpad.net

Description of the change

To post a comment you must log in.
Revision history for this message
Jacques-Etienne Baudoux (jbaudoux) wrote :

Works on saas1

review: Approve (test)
Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

The beginning of the discussion of this module starts here:

https://code.launchpad.net/~luc-demeyer/server-env-tools/7.0-report_xls/+merge/192242

Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote :

LGTM, Thanks for this contribs

review: Approve (code review, no tests)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'report_xls'
2=== added file 'report_xls/__init__.py'
3--- report_xls/__init__.py 1970-01-01 00:00:00 +0000
4+++ report_xls/__init__.py 2013-11-15 16:03:10 +0000
5@@ -0,0 +1,24 @@
6+# -*- encoding: utf-8 -*-
7+##############################################################################
8+#
9+# OpenERP, Open Source Management Solution
10+#
11+# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
12+#
13+# This program is free software: you can redistribute it and/or modify
14+# it under the terms of the GNU Affero General Public License as
15+# published by the Free Software Foundation, either version 3 of the
16+# License, or (at your option) any later version.
17+#
18+# This program is distributed in the hope that it will be useful,
19+# but WITHOUT ANY WARRANTY; without even the implied warranty of
20+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+# GNU Affero General Public License for more details.
22+#
23+# You should have received a copy of the GNU Affero General Public License
24+# along with this program. If not, see <http://www.gnu.org/licenses/>.
25+#
26+##############################################################################
27+
28+from . import report_xls
29+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
30
31=== added file 'report_xls/__openerp__.py'
32--- report_xls/__openerp__.py 1970-01-01 00:00:00 +0000
33+++ report_xls/__openerp__.py 2013-11-15 16:03:10 +0000
34@@ -0,0 +1,44 @@
35+# -*- encoding: utf-8 -*-
36+##############################################################################
37+#
38+# OpenERP, Open Source Management Solution
39+#
40+# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
41+#
42+# This program is free software: you can redistribute it and/or modify
43+# it under the terms of the GNU Affero General Public License as
44+# published by the Free Software Foundation, either version 3 of the
45+# License, or (at your option) any later version.
46+#
47+# This program is distributed in the hope that it will be useful,
48+# but WITHOUT ANY WARRANTY; without even the implied warranty of
49+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50+# GNU Affero General Public License for more details.
51+#
52+# You should have received a copy of the GNU Affero General Public License
53+# along with this program. If not, see <http://www.gnu.org/licenses/>.
54+#
55+##############################################################################
56+{
57+ 'name': 'XLS report engine',
58+ 'version': '0.3',
59+ 'license': 'AGPL-3',
60+ 'author': 'Noviat',
61+ 'website': 'http://www.noviat.com',
62+ 'category': 'Reporting',
63+ 'description': """
64+
65+This module adds XLS export capabilities to the standard OpenERP reporting engine.
66+
67+In order to generate an XLS export you can define a report of type 'xls' or alternatively pass {'xls_export' : 1) via the context to create method of the report.
68+
69+ """,
70+ 'depends': ['base'],
71+ 'external_dependencies': {'python': ['xlwt']},
72+ 'demo_xml': [],
73+ 'init_xml': [],
74+ 'update_xml' : [],
75+ 'active': False,
76+ 'installable': True,
77+}
78+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
79
80=== added file 'report_xls/report_xls.py'
81--- report_xls/report_xls.py 1970-01-01 00:00:00 +0000
82+++ report_xls/report_xls.py 2013-11-15 16:03:10 +0000
83@@ -0,0 +1,224 @@
84+# -*- encoding: utf-8 -*-
85+##############################################################################
86+#
87+# OpenERP, Open Source Management Solution
88+#
89+#Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
90+#
91+# This program is free software: you can redistribute it and/or modify
92+# it under the terms of the GNU Affero General Public License as
93+# published by the Free Software Foundation, either version 3 of the
94+# License, or (at your option) any later version.
95+#
96+# This program is distributed in the hope that it will be useful,
97+# but WITHOUT ANY WARRANTY; without even the implied warranty of
98+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
99+# GNU Affero General Public License for more details.
100+#
101+# You should have received a copy of the GNU Affero General Public License
102+# along with this program. If not, see <http://www.gnu.org/licenses/>.
103+#
104+##############################################################################
105+
106+import xlwt
107+from xlwt.Style import default_style
108+import cStringIO
109+import datetime, time
110+import inspect
111+from types import CodeType
112+from openerp.report.report_sxw import *
113+from openerp import pooler
114+from openerp.tools.translate import translate, _
115+import logging
116+_logger = logging.getLogger(__name__)
117+
118+class AttrDict(dict):
119+ def __init__(self, *args, **kwargs):
120+ super(AttrDict, self).__init__(*args, **kwargs)
121+ self.__dict__ = self
122+
123+class report_xls(report_sxw):
124+
125+ xls_types = {
126+ 'bool': xlwt.Row.set_cell_boolean,
127+ 'date': xlwt.Row.set_cell_date,
128+ 'text': xlwt.Row.set_cell_text,
129+ 'number': xlwt.Row.set_cell_number,
130+ }
131+ xls_types_default = {
132+ 'bool': False,
133+ 'date': None,
134+ 'text': '',
135+ 'number': 0,
136+ }
137+
138+ # TO DO: move parameters infra to configurable data
139+
140+ # header/footer
141+ DT_FORMAT = '%Y-%m-%d %H:%M:%S'
142+ hf_params = {
143+ 'font_size': 8,
144+ 'font_style': 'I', # B: Bold, I: Italic, U: Underline
145+ }
146+ xls_headers = {
147+ 'standard': ''
148+ }
149+ xls_footers = {
150+ 'standard': ('&L&%(font_size)s&%(font_style)s' + datetime.now().strftime(DT_FORMAT) +
151+ '&R&%(font_size)s&%(font_style)s&P / &N') %hf_params
152+ }
153+
154+ # styles
155+ _pfc = '26' # default pattern fore_color
156+ _bc = '22' # borders color
157+ decimal_format = '#,##0.00'
158+ date_format = 'YYYY-MM-DD'
159+ xls_styles = {
160+ 'xls_title': 'font: bold true, height 240;',
161+ 'bold': 'font: bold true;',
162+ 'underline': 'font: underline true;',
163+ 'italic': 'font: italic true;',
164+ 'fill': 'pattern: pattern solid, fore_color %s;' %_pfc,
165+ 'fill_blue' : 'pattern: pattern solid, fore_color 27;',
166+ 'fill_grey' : 'pattern: pattern solid, fore_color 22;',
167+ 'borders_all': 'borders: left thin, right thin, top thin, bottom thin, ' \
168+ 'left_colour %s, right_colour %s, top_colour %s, bottom_colour %s;' %(_bc,_bc,_bc,_bc),
169+ 'left': 'align: horz left;',
170+ 'center': 'align: horz center;',
171+ 'right': 'align: horz right;',
172+ 'wrap': 'align: wrap true;',
173+ 'top': 'align: vert top;',
174+ 'bottom': 'align: vert bottom;',
175+ }
176+ # TO DO: move parameters supra to configurable data
177+
178+ def create(self, cr, uid, ids, data, context=None):
179+ self.pool = pooler.get_pool(cr.dbname)
180+ self.cr = cr
181+ self.uid = uid
182+ report_obj = self.pool.get('ir.actions.report.xml')
183+ report_ids = report_obj.search(cr, uid,
184+ [('report_name', '=', self.name[7:])], context=context)
185+ if report_ids:
186+ report_xml = report_obj.browse(cr, uid, report_ids[0], context=context)
187+ self.title = report_xml.name
188+ if report_xml.report_type == 'xls':
189+ return self.create_source_xls(cr, uid, ids, data, context)
190+ elif context.get('xls_export'):
191+ return self.create_source_xls(cr, uid, ids, data, context)
192+ return super(report_xls, self).create(cr, uid, ids, data, context)
193+
194+ def create_source_xls(self, cr, uid, ids, data, context=None):
195+ if not context: context = {}
196+ parser_instance = self.parser(cr, uid, self.name2, context)
197+ self.parser_instance = parser_instance
198+ objs = self.getObjects(cr, uid, ids, context)
199+ parser_instance.set_context(objs, data, ids, 'xls')
200+ objs = parser_instance.localcontext['objects']
201+ n = cStringIO.StringIO()
202+ wb = xlwt.Workbook(encoding='utf-8')
203+ _p = AttrDict(parser_instance.localcontext)
204+ _xs = self.xls_styles
205+ self.generate_xls_report(_p, _xs, data, objs, wb)
206+ wb.save(n)
207+ n.seek(0)
208+ return (n.read(), 'xls')
209+
210+ def render(self, wanted, col_specs, rowtype, render_space='empty'):
211+ """
212+ returns 'mako'-rendered col_specs
213+
214+ Input:
215+ - wanted: element from the wanted_list
216+ - col_specs : cf. specs[1:] documented in xls_row_template method
217+ - rowtype : 'header' or 'data'
218+ - render_space : type dict, (caller_space + localcontext) if not specified
219+ """
220+ if render_space == 'empty':
221+ render_space = {}
222+ caller_space = inspect.currentframe().f_back.f_back.f_locals
223+ localcontext = self.parser_instance.localcontext
224+ render_space.update(caller_space)
225+ render_space.update(localcontext)
226+ row = col_specs[wanted][rowtype][:]
227+ for i in range(len(row)):
228+ if isinstance(row[i], CodeType):
229+ row[i] = eval(row[i], render_space)
230+ row.insert(0, wanted)
231+ #_logger.warn('row O = %s', row)
232+ return row
233+
234+ def generate_xls_report(self, parser, xls_styles, data, objects, wb):
235+ """ override this method to create your excel file """
236+ raise NotImplementedError()
237+
238+ def xls_row_template(self, specs, wanted_list):
239+ """
240+ Returns a row template.
241+
242+ Input :
243+ - 'wanted_list': list of Columns that will be returned in the row_template
244+ - 'specs': list with Column Characteristics
245+ 0: Column Name (from wanted_list)
246+ 1: Column Colspan
247+ 2: Column Size (unit = the width of the character ’0′ as it appears in the sheet’s default font)
248+ 3: Column Type
249+ 4: Column Data
250+ 5: Column Formula (or 'None' for Data)
251+ 6: Column Style
252+ """
253+ r = []
254+ col = 0
255+ for w in wanted_list:
256+ found = False
257+ for s in specs:
258+ if s[0] == w:
259+ found = True
260+ s_len = len(s)
261+ c = list(s[:5])
262+ # set write_cell_func or formula
263+ if s_len > 5 and s[5] is not None:
264+ c.append({'formula': s[5]})
265+ else:
266+ c.append({'write_cell_func': report_xls.xls_types[c[3]]})
267+ # Set custom cell style
268+ if s_len > 6 and s[6] is not None:
269+ c.append(s[6])
270+ else:
271+ c.append(None)
272+ # Set cell formula
273+ if s_len > 7 and s[7] is not None:
274+ c.append(s[7])
275+ else:
276+ c.append(None)
277+ r.append((col, c[1], c))
278+ col += c[1]
279+ break
280+ if not found:
281+ _logger.warn("report_xls.xls_row_template, column '%s' not found in specs", w)
282+ return r
283+
284+ def xls_write_row(self, ws, row_pos, row_data, row_style=default_style, set_column_size=False):
285+ r = ws.row(row_pos)
286+ for col, size, spec in row_data:
287+ data = spec[4]
288+ formula = spec[5].get('formula') and xlwt.Formula(spec[5]['formula']) or None
289+ style = spec[6] and spec[6] or row_style
290+ if not data:
291+ # if no data, use default values
292+ data = report_xls.xls_types_default[spec[3]]
293+ if size != 1:
294+ if formula:
295+ ws.write_merge(row_pos, row_pos, col, col+size-1, data, style)
296+ else:
297+ ws.write_merge(row_pos, row_pos, col, col+size-1, data, style)
298+ else:
299+ if formula:
300+ ws.write(row_pos, col, formula, style)
301+ else:
302+ spec[5]['write_cell_func'](r, col, data, style)
303+ if set_column_size:
304+ ws.col(col).width = spec[2] * 256
305+ return row_pos + 1
306+
307+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
308
309=== added file 'report_xls/utils.py'
310--- report_xls/utils.py 1970-01-01 00:00:00 +0000
311+++ report_xls/utils.py 2013-11-15 16:03:10 +0000
312@@ -0,0 +1,49 @@
313+# -*- encoding: utf-8 -*-
314+##############################################################################
315+#
316+# OpenERP, Open Source Management Solution
317+#
318+# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
319+#
320+# This program is free software: you can redistribute it and/or modify
321+# it under the terms of the GNU Affero General Public License as
322+# published by the Free Software Foundation, either version 3 of the
323+# License, or (at your option) any later version.
324+#
325+# This program is distributed in the hope that it will be useful,
326+# but WITHOUT ANY WARRANTY; without even the implied warranty of
327+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
328+# GNU Affero General Public License for more details.
329+#
330+# You should have received a copy of the GNU Affero General Public License
331+# along with this program. If not, see <http://www.gnu.org/licenses/>.
332+#
333+##############################################################################
334+#
335+
336+def _render(code):
337+ return compile(code, '<string>', 'eval')
338+
339+def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
340+ # Code based upon utils from xlwt distribution
341+ """
342+ Convert numeric row/col notation to an Excel cell reference string in A1 notation.
343+ """
344+ d = col // 26
345+ m = col % 26
346+ chr1 = "" # Most significant character in AA1
347+ if row_abs:
348+ row_abs = '$'
349+ else:
350+ row_abs = ''
351+ if col_abs:
352+ col_abs = '$'
353+ else:
354+ col_abs = ''
355+ if d > 0:
356+ chr1 = chr(ord('A') + d - 1)
357+ chr2 = chr(ord('A') + m)
358+ # Zero index to 1-index
359+ return col_abs + chr1 + chr2 + row_abs + str(row + 1)
360+
361+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

Subscribers

People subscribed via source and target branches