Merge lp:~camptocamp/openerp-reporting-engines/7.0-add-base_report_assembler-yvr into lp:openerp-reporting-engines

Proposed by Yannick Vaucher @ Camptocamp
Status: Merged
Merged at revision: 4
Proposed branch: lp:~camptocamp/openerp-reporting-engines/7.0-add-base_report_assembler-yvr
Merge into: lp:openerp-reporting-engines
Diff against target: 460 lines (+424/-0)
7 files modified
base_report_assembler/__init__.py (+24/-0)
base_report_assembler/__openerp__.py (+49/-0)
base_report_assembler/assembled_report.py (+42/-0)
base_report_assembler/i18n/base_report_assembler.pot (+36/-0)
base_report_assembler/i18n/fr.po (+37/-0)
base_report_assembler/ir_report.py (+110/-0)
base_report_assembler/report_assembler.py (+126/-0)
To merge this branch: bzr merge lp:~camptocamp/openerp-reporting-engines/7.0-add-base_report_assembler-yvr
Reviewer Review Type Date Requested Status
Guewen Baconnier @ Camptocamp Approve
Joël Grand-Guillaume @ camptocamp code review, no tests Approve
Nicolas Bessi - Camptocamp (community) code review, no test Approve
Pedro Manuel Baeza Needs Information
Review via email: mp+194304@code.launchpad.net

Description of the change

Add a module to merge two pdf report in one

To post a comment you must log in.
Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, Yannick, I have tried to use your module, but I'm unable to test it, because I don't find the way to do it. Can you explain to me how to activate it? It would be desirable also to have these instructions on the module description.

I have found also a terminology problem, because you are using a term that is not correct: assemblage, that is an art concept (http://en.wikipedia.org/wiki/Assemblage_(art)). It's better to use the term "assembly", and I also would suggest "pdf_assembly", to be even more accurate.

Regards.

review: Needs Information
Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

This module doesn't install configuration for report it is only the base.

You can use account_invoice_assemble to test it:
https://code.launchpad.net/~camptocamp/account-invoice-report/7.0-add-invoice_report_assemble-yvr

Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

Assemblage has other meanings than only art purpose:

http://dictionary.reference.com/browse/assemblage

Assemblage seems more accurate than assembly to me.

Assembly is more used for a group of person or a governement entity.

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

Hi,

I'm also in favor of having a better description in the module. May be you can link some module that use this base in there so the user can find out how to use it properly ?

Regards,

Joël

review: Needs Fixing (code review, no tests)
Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

Thanks that's a nice module.

Besides the changes requested above, I would just propose to replace the french translation "Companie" (misspelled BTW) by "Société" as this word is used usually.

Can you rename the argument "cursor" to "cr" where it occurs?

l.283,293: needs """ for docstring

Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

Thanks for the reviews!

I fixed the docstrings and improved a bit the description of the module.

Ready for reviews!

Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

@Pedro I prefer to keep report assemblage. Main output of reports is pdf so it is implicit. Furthermore I think it would be better to extend this module to merge something else even if we want to extract pdf merge function.

8. By Nicolas Bessi - Camptocamp

[PEP8]

9. By Nicolas Bessi - Camptocamp

[PEP8] and correction of doc string

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :

LGTM.
Fixed some neat picks

review: Approve (code review, no test)
Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote :

LGTM, thanks

review: Approve (code review, no tests)
Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

Thanks

review: Approve
Revision history for this message
Niels Huylebroeck (red15) wrote :

Wouldn't a more logical place to merge this with be the lp:report-print-send project ?

This project has a module 'printer_tray' that is dependant on this module, I only found this merge by googling the module name.

Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

Hello Niels,

No this module creates a new type of reporting. You can use it to generate a single pdf from 2 other pdf reports.

Furthermore an other branche has it as a dependancy: account-invoice-report in module invoice_report_assemble.

To find a module you can use apps.openerp.com

Maybe we can remove dependancy on it from printer_tray I'll have to check that.

Cheers,

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'base_report_assembler'
2=== added file 'base_report_assembler/__init__.py'
3--- base_report_assembler/__init__.py 1970-01-01 00:00:00 +0000
4+++ base_report_assembler/__init__.py 2014-01-31 11:09:41 +0000
5@@ -0,0 +1,24 @@
6+# -*- coding: utf-8 -*-
7+##############################################################################
8+#
9+# Author: Yannick Vaucher
10+# Copyright 2013 Camptocamp SA
11+#
12+# This program is free software: you can redistribute it and/or modify
13+# it under the terms of the GNU Affero General Public License as
14+# published by the Free Software Foundation, either version 3 of the
15+# License, or (at your option) any later version.
16+#
17+# This program is distributed in the hope that it will be useful,
18+# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+# GNU Affero General Public License for more details.
21+#
22+# You should have received a copy of the GNU Affero General Public License
23+# along with this program. If not, see <http://www.gnu.org/licenses/>.
24+#
25+##############################################################################
26+from . import report_assembler
27+from . import assembled_report
28+from . import ir_report
29+
30
31=== added file 'base_report_assembler/__openerp__.py'
32--- base_report_assembler/__openerp__.py 1970-01-01 00:00:00 +0000
33+++ base_report_assembler/__openerp__.py 2014-01-31 11:09:41 +0000
34@@ -0,0 +1,49 @@
35+# -*- coding: utf-8 -*-
36+##############################################################################
37+#
38+# Author: Yannick Vaucher
39+# Copyright 2013 Camptocamp SA
40+#
41+# This program is free software: you can redistribute it and/or modify
42+# it under the terms of the GNU Affero General Public License as
43+# published by the Free Software Foundation, either version 3 of the
44+# License, or (at your option) any later version.
45+#
46+# This program is distributed in the hope that it will be useful,
47+# but WITHOUT ANY WARRANTY; without even the implied warranty of
48+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49+# GNU Affero General Public License for more details.
50+#
51+# You should have received a copy of the GNU Affero General Public License
52+# along with this program. If not, see <http://www.gnu.org/licenses/>.
53+#
54+##############################################################################
55+{'name': 'Base Report Assembler',
56+ 'version': '1.0',
57+ 'category': 'Report',
58+ 'description': """
59+Base Report Assembler
60+=====================
61+
62+Defines a new type of report which is an assemblage of multiple other reports
63+of the same object.
64+
65+For example you can merge the pdf output of a rml invoice report with the pdf
66+output of a webkit payment slip.
67+
68+To install this assemblage option for specific object you can install
69+the folling module(s):
70+
71+- Invoices: invoice_report_assemble (lp:account-invoice-report)
72+
73+""",
74+ 'author': 'Camptocamp',
75+ 'maintainer': 'Camptocamp',
76+ 'website': 'http://www.camptocamp.com/',
77+ 'depends': ['base'],
78+ 'data': [],
79+ 'test': [],
80+ 'installable': True,
81+ 'auto_install': False,
82+ 'application': False,
83+ }
84
85=== added file 'base_report_assembler/assembled_report.py'
86--- base_report_assembler/assembled_report.py 1970-01-01 00:00:00 +0000
87+++ base_report_assembler/assembled_report.py 2014-01-31 11:09:41 +0000
88@@ -0,0 +1,42 @@
89+# -*- coding: utf-8 -*-
90+##############################################################################
91+#
92+# Author: Yannick Vaucher
93+# Copyright 2013 Camptocamp SA
94+#
95+# This program is free software: you can redistribute it and/or modify
96+# it under the terms of the GNU Affero General Public License as
97+# published by the Free Software Foundation, either version 3 of the
98+# License, or (at your option) any later version.
99+#
100+# This program is distributed in the hope that it will be useful,
101+# but WITHOUT ANY WARRANTY; without even the implied warranty of
102+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
103+# GNU Affero General Public License for more details.
104+#
105+# You should have received a copy of the GNU Affero General Public License
106+# along with this program. If not, see <http://www.gnu.org/licenses/>.
107+#
108+##############################################################################
109+from openerp.osv import orm, fields
110+
111+class AssembledReport(orm.Model):
112+ _name = 'assembled.report'
113+
114+ _order = 'sequence'
115+
116+ _columns = {
117+ 'report_id': fields.many2one(
118+ 'ir.actions.report.xml', 'Report',
119+ domain="[('model', '=', model),"
120+ "('report_type', '!=', 'assemblage')]", required=True),
121+ 'model': fields.char('Object model'),
122+ 'sequence': fields.integer('Sequence', required=True),
123+ 'company_id': fields.many2one('res.company', 'Company', required=True),
124+ }
125+
126+ _defaults = {
127+ 'sequence': 1,
128+ 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(
129+ cr, uid, 'assembled.report', context=c)
130+ }
131
132=== added directory 'base_report_assembler/i18n'
133=== added file 'base_report_assembler/i18n/base_report_assembler.pot'
134--- base_report_assembler/i18n/base_report_assembler.pot 1970-01-01 00:00:00 +0000
135+++ base_report_assembler/i18n/base_report_assembler.pot 2014-01-31 11:09:41 +0000
136@@ -0,0 +1,36 @@
137+# Translation of OpenERP Server.
138+# This file contains the translation of the following modules:
139+# * base_report_assembler
140+#
141+msgid ""
142+msgstr ""
143+"Project-Id-Version: OpenERP Server 7.0\n"
144+"Report-Msgid-Bugs-To: \n"
145+"POT-Creation-Date: 2013-11-05 14:55+0000\n"
146+"PO-Revision-Date: 2013-11-05 14:55+0000\n"
147+"Last-Translator: <>\n"
148+"Language-Team: \n"
149+"MIME-Version: 1.0\n"
150+"Content-Type: text/plain; charset=UTF-8\n"
151+"Content-Transfer-Encoding: \n"
152+"Plural-Forms: \n"
153+
154+#. module: base_report_assembler
155+#: field:assembled.report,model:0
156+msgid "Object model"
157+msgstr ""
158+
159+#. module: base_report_assembler
160+#: field:assembled.report,sequence:0
161+msgid "Sequence"
162+msgstr ""
163+
164+#. module: base_report_assembler
165+#: field:assembled.report,company_id:0
166+msgid "Company"
167+msgstr ""
168+
169+#. module: base_report_assembler
170+#: field:assembled.report,report_id:0
171+msgid "Report"
172+msgstr ""
173
174=== added file 'base_report_assembler/i18n/fr.po'
175--- base_report_assembler/i18n/fr.po 1970-01-01 00:00:00 +0000
176+++ base_report_assembler/i18n/fr.po 2014-01-31 11:09:41 +0000
177@@ -0,0 +1,37 @@
178+# Translation of OpenERP Server.
179+# This file contains the translation of the following modules:
180+# * base_report_assembler
181+#
182+msgid ""
183+msgstr ""
184+"Project-Id-Version: OpenERP Server 7.0\n"
185+"Report-Msgid-Bugs-To: \n"
186+"POT-Creation-Date: 2013-11-05 14:55+0000\n"
187+"PO-Revision-Date: 2013-11-05 14:55+0000\n"
188+"Last-Translator: <>\n"
189+"Language-Team: \n"
190+"MIME-Version: 1.0\n"
191+"Content-Type: text/plain; charset=UTF-8\n"
192+"Content-Transfer-Encoding: \n"
193+"Plural-Forms: \n"
194+
195+#. module: base_report_assembler
196+#: field:assembled.report,model:0
197+msgid "Object model"
198+msgstr "Modèle d'object"
199+
200+#. module: base_report_assembler
201+#: field:assembled.report,sequence:0
202+msgid "Sequence"
203+msgstr "Séquence"
204+
205+#. module: base_report_assembler
206+#: field:assembled.report,company_id:0
207+msgid "Company"
208+msgstr "Société"
209+
210+#. module: base_report_assembler
211+#: field:assembled.report,report_id:0
212+msgid "Report"
213+msgstr "Rapport"
214+
215
216=== added file 'base_report_assembler/ir_report.py'
217--- base_report_assembler/ir_report.py 1970-01-01 00:00:00 +0000
218+++ base_report_assembler/ir_report.py 2014-01-31 11:09:41 +0000
219@@ -0,0 +1,110 @@
220+# -*- coding: utf-8 -*-
221+##############################################################################
222+#
223+# Author: Yannick Vaucher
224+# Copyright 2013 Camptocamp SA
225+#
226+# This program is free software: you can redistribute it and/or modify
227+# it under the terms of the GNU Affero General Public License as
228+# published by the Free Software Foundation, either version 3 of the
229+# License, or (at your option) any later version.
230+#
231+# This program is distributed in the hope that it will be useful,
232+# but WITHOUT ANY WARRANTY; without even the implied warranty of
233+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
234+# GNU Affero General Public License for more details.
235+#
236+# You should have received a copy of the GNU Affero General Public License
237+# along with this program. If not, see <http://www.gnu.org/licenses/>.
238+#
239+##############################################################################
240+from openerp.osv import orm
241+from openerp import netsvc
242+from openerp.report.report_sxw import rml_parse
243+from report_assembler import PDFReportAssembler
244+
245+
246+def register_report(name, model, parser=rml_parse):
247+ """ Register the report into the services """
248+ name = 'report.%s' % name
249+ if netsvc.Service._services.get(name, False):
250+ service = netsvc.Service._services[name]
251+ if isinstance(service, PDFReportAssembler):
252+ #already instantiated properly, skip it
253+ return
254+ if hasattr(service, 'parser'):
255+ parser = service.parser
256+ del netsvc.Service._services[name]
257+ PDFReportAssembler(name, model, parser=parser)
258+
259+
260+class ReportAssembleXML(orm.Model):
261+
262+ _name = 'ir.actions.report.xml'
263+ _inherit = 'ir.actions.report.xml'
264+
265+ def __init__(self, pool, cr):
266+ super(ReportAssembleXML, self).__init__(pool, cr)
267+
268+ def register_all(self, cursor):
269+ value = super(ReportAssembleXML, self).register_all(cursor)
270+ cursor.execute("SELECT * FROM ir_act_report_xml WHERE report_type = 'assemblage'")
271+ records = cursor.dictfetchall()
272+ for record in records:
273+ register_report(record['report_name'], record['model'])
274+ return value
275+
276+ def unlink(self, cursor, user, ids, context=None):
277+ """ Delete report and unregister it """
278+ trans_obj = self.pool.get('ir.translation')
279+ trans_ids = trans_obj.search(
280+ cursor,
281+ user,
282+ [('type', '=', 'report'), ('res_id', 'in', ids)]
283+ )
284+ trans_obj.unlink(cursor, user, trans_ids)
285+
286+ # Warning: we cannot unregister the services at the moment
287+ # because they are shared across databases. Calling a deleted
288+ # report will fail so it's ok.
289+
290+ res = super(ReportAssembleXML, self).unlink(
291+ cursor,
292+ user,
293+ ids,
294+ context)
295+ return res
296+
297+ def create(self, cursor, user, vals, context=None):
298+ """ Create report and register it """
299+ res = super(ReportAssembleXML, self).create(cursor, user, vals, context)
300+ if vals.get('report_type', '') == 'assemblage':
301+ # I really look forward to virtual functions :S
302+ register_report(
303+ vals['report_name'],
304+ vals['model'])
305+ return res
306+
307+ def write(self, cr, uid, ids, vals, context=None):
308+ """ Edit report and manage its registration """
309+ if isinstance(ids, (int, long)):
310+ ids = [ids]
311+ for rep in self.browse(cr, uid, ids, context=context):
312+ if rep.report_type != 'assemblage':
313+ continue
314+ if (vals.get('report_name', False)
315+ and vals['report_name'] != rep.report_name):
316+ report_name = vals['report_name']
317+ else:
318+ report_name = rep.report_name
319+
320+ register_report(
321+ report_name,
322+ vals.get('model', rep.model),
323+ False
324+ )
325+ res = super(ReportAssembleXML, self).write(cr, uid, ids, vals, context)
326+ return res
327+
328+
329+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
330
331=== added file 'base_report_assembler/report_assembler.py'
332--- base_report_assembler/report_assembler.py 1970-01-01 00:00:00 +0000
333+++ base_report_assembler/report_assembler.py 2014-01-31 11:09:41 +0000
334@@ -0,0 +1,126 @@
335+# -*- coding: utf-8 -*-
336+##############################################################################
337+#
338+# Author: Yannick Vaucher
339+# Copyright 2013 Camptocamp SA
340+#
341+# This program is free software: you can redistribute it and/or modify
342+# it under the terms of the GNU Affero General Public License as
343+# published by the Free Software Foundation, either version 3 of the
344+# License, or (at your option) any later version.
345+#
346+# This program is distributed in the hope that it will be useful,
347+# but WITHOUT ANY WARRANTY; without even the implied warranty of
348+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
349+# GNU Affero General Public License for more details.
350+#
351+# You should have received a copy of the GNU Affero General Public License
352+# along with this program. If not, see <http://www.gnu.org/licenses/>.
353+#
354+##############################################################################
355+import time
356+import base64
357+from PyPDF2 import PdfFileReader, PdfFileWriter
358+from StringIO import StringIO
359+
360+from openerp.netsvc import ExportService
361+from openerp.report import report_sxw
362+from openerp import pooler
363+
364+_POLLING_DELAY = 0.25
365+
366+
367+def assemble_pdf(pdf_list):
368+ """
369+ Assemble a list of pdf
370+ """
371+ # Even though we are using PyPDF2 we can't use PdfFileMerger
372+ # as this issue still exists in mostly used wktohtml reports version
373+ # http://code.google.com/p/wkhtmltopdf/issues/detail?id=635
374+ #merger = PdfFileMerger()
375+ #merger.append(fileobj=StringIO(invoice_pdf))
376+ #merger.append(fileobj=StringIO(bvr_pdf))
377+
378+ #with tempfile.TemporaryFile() as merged_pdf:
379+ #merger.write(merged_pdf)
380+ #return merged_pdf.read(), 'pdf'
381+
382+ output = PdfFileWriter()
383+ for pdf in pdf_list:
384+ reader = PdfFileReader(StringIO(pdf))
385+ for page in range(reader.getNumPages()):
386+ output.addPage(reader.getPage(page))
387+ s = StringIO()
388+ output.write(s)
389+ return s.getvalue()
390+
391+
392+class PDFReportAssembler(report_sxw.report_sxw):
393+ """ PDFReportAssembler allows to put 2 pdf reports in one single pdf"""
394+
395+ def _generate_all_pdf(self, cr, uid, ids, data, report_ids, context=None):
396+ """
397+ Return a list of pdf encoded in base64
398+ """
399+ pool = pooler.get_pool(cr.dbname)
400+ report_obj = pool.get('ir.actions.report.xml')
401+
402+ spool = ExportService._services['report']
403+
404+ pdf_reports = []
405+ report_list = report_obj.browse(cr, uid, report_ids, context=context)
406+ for report in report_list:
407+
408+ report_key = spool.exp_report(cr.dbname, uid, report.report_name,
409+ ids, datas=data, context=context)
410+ while 1:
411+ res = spool.exp_report_get(cr.dbname, uid, report_key)
412+ if res.get('state'):
413+ break
414+ time.sleep(_POLLING_DELAY)
415+ pdf = base64.b64decode(res.get('result'))
416+ pdf_reports.append(pdf)
417+ return pdf_reports
418+
419+ def _get_report_ids(self, cr, uid, ids, context=None):
420+ """
421+ Hook to define the list of report to print
422+ """
423+ return []
424+
425+ def create_single_pdf(self, cr, uid, ids, data, report_xml, context=None):
426+ """Call both report to assemble both pdf"""
427+
428+ report_ids = self._get_report_ids(cr, uid, ids, context=context)
429+
430+ pdf_reports = self._generate_all_pdf(cr, uid, ids, data, report_ids, context=context)
431+
432+ pdf_assemblage = assemble_pdf(pdf_reports)
433+ return pdf_assemblage, 'pdf'
434+
435+ def create(self, cr, uid, ids, data, context=None):
436+ """We override the create function in order to handle generator
437+ Code taken from report openoffice. Thanks guys :) """
438+ pool = pooler.get_pool(cr.dbname)
439+ ir_obj = pool.get('ir.actions.report.xml')
440+ report_xml_ids = ir_obj.search(cr, uid,
441+ [('report_name', '=', self.name[7:])], context=context)
442+ if report_xml_ids:
443+
444+ report_xml = ir_obj.browse(cr,
445+ uid,
446+ report_xml_ids[0],
447+ context=context)
448+ report_xml.report_rml = None
449+ report_xml.report_rml_content = None
450+ report_xml.report_sxw_content_data = None
451+ report_xml.report_sxw_content = None
452+ report_xml.report_sxw = None
453+ else:
454+ return super(PDFReportAssembler, self).create(cr, uid, ids, data, context)
455+ if report_xml.report_type != 'assemblage':
456+ return super(PDFReportAssembler, self).create(cr, uid, ids, data, context)
457+ result = self.create_source_pdf(cr, uid, ids, data, report_xml, context)
458+ if not result:
459+ return (False, False)
460+ return result

Subscribers

People subscribed via source and target branches