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
=== added directory 'base_report_assembler'
=== added file 'base_report_assembler/__init__.py'
--- base_report_assembler/__init__.py 1970-01-01 00:00:00 +0000
+++ base_report_assembler/__init__.py 2014-01-31 11:09:41 +0000
@@ -0,0 +1,24 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Yannick Vaucher
5# Copyright 2013 Camptocamp SA
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##############################################################################
21from . import report_assembler
22from . import assembled_report
23from . import ir_report
24
025
=== added file 'base_report_assembler/__openerp__.py'
--- base_report_assembler/__openerp__.py 1970-01-01 00:00:00 +0000
+++ base_report_assembler/__openerp__.py 2014-01-31 11:09:41 +0000
@@ -0,0 +1,49 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Yannick Vaucher
5# Copyright 2013 Camptocamp SA
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{'name': 'Base Report Assembler',
22 'version': '1.0',
23 'category': 'Report',
24 'description': """
25Base Report Assembler
26=====================
27
28Defines a new type of report which is an assemblage of multiple other reports
29of the same object.
30
31For example you can merge the pdf output of a rml invoice report with the pdf
32output of a webkit payment slip.
33
34To install this assemblage option for specific object you can install
35the folling module(s):
36
37- Invoices: invoice_report_assemble (lp:account-invoice-report)
38
39""",
40 'author': 'Camptocamp',
41 'maintainer': 'Camptocamp',
42 'website': 'http://www.camptocamp.com/',
43 'depends': ['base'],
44 'data': [],
45 'test': [],
46 'installable': True,
47 'auto_install': False,
48 'application': False,
49 }
050
=== added file 'base_report_assembler/assembled_report.py'
--- base_report_assembler/assembled_report.py 1970-01-01 00:00:00 +0000
+++ base_report_assembler/assembled_report.py 2014-01-31 11:09:41 +0000
@@ -0,0 +1,42 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Yannick Vaucher
5# Copyright 2013 Camptocamp SA
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##############################################################################
21from openerp.osv import orm, fields
22
23class AssembledReport(orm.Model):
24 _name = 'assembled.report'
25
26 _order = 'sequence'
27
28 _columns = {
29 'report_id': fields.many2one(
30 'ir.actions.report.xml', 'Report',
31 domain="[('model', '=', model),"
32 "('report_type', '!=', 'assemblage')]", required=True),
33 'model': fields.char('Object model'),
34 'sequence': fields.integer('Sequence', required=True),
35 'company_id': fields.many2one('res.company', 'Company', required=True),
36 }
37
38 _defaults = {
39 'sequence': 1,
40 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(
41 cr, uid, 'assembled.report', context=c)
42 }
043
=== added directory 'base_report_assembler/i18n'
=== added file 'base_report_assembler/i18n/base_report_assembler.pot'
--- base_report_assembler/i18n/base_report_assembler.pot 1970-01-01 00:00:00 +0000
+++ base_report_assembler/i18n/base_report_assembler.pot 2014-01-31 11:09:41 +0000
@@ -0,0 +1,36 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * base_report_assembler
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2013-11-05 14:55+0000\n"
10"PO-Revision-Date: 2013-11-05 14:55+0000\n"
11"Last-Translator: <>\n"
12"Language-Team: \n"
13"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"
16"Plural-Forms: \n"
17
18#. module: base_report_assembler
19#: field:assembled.report,model:0
20msgid "Object model"
21msgstr ""
22
23#. module: base_report_assembler
24#: field:assembled.report,sequence:0
25msgid "Sequence"
26msgstr ""
27
28#. module: base_report_assembler
29#: field:assembled.report,company_id:0
30msgid "Company"
31msgstr ""
32
33#. module: base_report_assembler
34#: field:assembled.report,report_id:0
35msgid "Report"
36msgstr ""
037
=== added file 'base_report_assembler/i18n/fr.po'
--- base_report_assembler/i18n/fr.po 1970-01-01 00:00:00 +0000
+++ base_report_assembler/i18n/fr.po 2014-01-31 11:09:41 +0000
@@ -0,0 +1,37 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * base_report_assembler
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2013-11-05 14:55+0000\n"
10"PO-Revision-Date: 2013-11-05 14:55+0000\n"
11"Last-Translator: <>\n"
12"Language-Team: \n"
13"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"
16"Plural-Forms: \n"
17
18#. module: base_report_assembler
19#: field:assembled.report,model:0
20msgid "Object model"
21msgstr "Modèle d'object"
22
23#. module: base_report_assembler
24#: field:assembled.report,sequence:0
25msgid "Sequence"
26msgstr "Séquence"
27
28#. module: base_report_assembler
29#: field:assembled.report,company_id:0
30msgid "Company"
31msgstr "Société"
32
33#. module: base_report_assembler
34#: field:assembled.report,report_id:0
35msgid "Report"
36msgstr "Rapport"
37
038
=== added file 'base_report_assembler/ir_report.py'
--- base_report_assembler/ir_report.py 1970-01-01 00:00:00 +0000
+++ base_report_assembler/ir_report.py 2014-01-31 11:09:41 +0000
@@ -0,0 +1,110 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Yannick Vaucher
5# Copyright 2013 Camptocamp SA
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##############################################################################
21from openerp.osv import orm
22from openerp import netsvc
23from openerp.report.report_sxw import rml_parse
24from report_assembler import PDFReportAssembler
25
26
27def register_report(name, model, parser=rml_parse):
28 """ Register the report into the services """
29 name = 'report.%s' % name
30 if netsvc.Service._services.get(name, False):
31 service = netsvc.Service._services[name]
32 if isinstance(service, PDFReportAssembler):
33 #already instantiated properly, skip it
34 return
35 if hasattr(service, 'parser'):
36 parser = service.parser
37 del netsvc.Service._services[name]
38 PDFReportAssembler(name, model, parser=parser)
39
40
41class ReportAssembleXML(orm.Model):
42
43 _name = 'ir.actions.report.xml'
44 _inherit = 'ir.actions.report.xml'
45
46 def __init__(self, pool, cr):
47 super(ReportAssembleXML, self).__init__(pool, cr)
48
49 def register_all(self, cursor):
50 value = super(ReportAssembleXML, self).register_all(cursor)
51 cursor.execute("SELECT * FROM ir_act_report_xml WHERE report_type = 'assemblage'")
52 records = cursor.dictfetchall()
53 for record in records:
54 register_report(record['report_name'], record['model'])
55 return value
56
57 def unlink(self, cursor, user, ids, context=None):
58 """ Delete report and unregister it """
59 trans_obj = self.pool.get('ir.translation')
60 trans_ids = trans_obj.search(
61 cursor,
62 user,
63 [('type', '=', 'report'), ('res_id', 'in', ids)]
64 )
65 trans_obj.unlink(cursor, user, trans_ids)
66
67 # Warning: we cannot unregister the services at the moment
68 # because they are shared across databases. Calling a deleted
69 # report will fail so it's ok.
70
71 res = super(ReportAssembleXML, self).unlink(
72 cursor,
73 user,
74 ids,
75 context)
76 return res
77
78 def create(self, cursor, user, vals, context=None):
79 """ Create report and register it """
80 res = super(ReportAssembleXML, self).create(cursor, user, vals, context)
81 if vals.get('report_type', '') == 'assemblage':
82 # I really look forward to virtual functions :S
83 register_report(
84 vals['report_name'],
85 vals['model'])
86 return res
87
88 def write(self, cr, uid, ids, vals, context=None):
89 """ Edit report and manage its registration """
90 if isinstance(ids, (int, long)):
91 ids = [ids]
92 for rep in self.browse(cr, uid, ids, context=context):
93 if rep.report_type != 'assemblage':
94 continue
95 if (vals.get('report_name', False)
96 and vals['report_name'] != rep.report_name):
97 report_name = vals['report_name']
98 else:
99 report_name = rep.report_name
100
101 register_report(
102 report_name,
103 vals.get('model', rep.model),
104 False
105 )
106 res = super(ReportAssembleXML, self).write(cr, uid, ids, vals, context)
107 return res
108
109
110# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0111
=== added file 'base_report_assembler/report_assembler.py'
--- base_report_assembler/report_assembler.py 1970-01-01 00:00:00 +0000
+++ base_report_assembler/report_assembler.py 2014-01-31 11:09:41 +0000
@@ -0,0 +1,126 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Yannick Vaucher
5# Copyright 2013 Camptocamp SA
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##############################################################################
21import time
22import base64
23from PyPDF2 import PdfFileReader, PdfFileWriter
24from StringIO import StringIO
25
26from openerp.netsvc import ExportService
27from openerp.report import report_sxw
28from openerp import pooler
29
30_POLLING_DELAY = 0.25
31
32
33def assemble_pdf(pdf_list):
34 """
35 Assemble a list of pdf
36 """
37 # Even though we are using PyPDF2 we can't use PdfFileMerger
38 # as this issue still exists in mostly used wktohtml reports version
39 # http://code.google.com/p/wkhtmltopdf/issues/detail?id=635
40 #merger = PdfFileMerger()
41 #merger.append(fileobj=StringIO(invoice_pdf))
42 #merger.append(fileobj=StringIO(bvr_pdf))
43
44 #with tempfile.TemporaryFile() as merged_pdf:
45 #merger.write(merged_pdf)
46 #return merged_pdf.read(), 'pdf'
47
48 output = PdfFileWriter()
49 for pdf in pdf_list:
50 reader = PdfFileReader(StringIO(pdf))
51 for page in range(reader.getNumPages()):
52 output.addPage(reader.getPage(page))
53 s = StringIO()
54 output.write(s)
55 return s.getvalue()
56
57
58class PDFReportAssembler(report_sxw.report_sxw):
59 """ PDFReportAssembler allows to put 2 pdf reports in one single pdf"""
60
61 def _generate_all_pdf(self, cr, uid, ids, data, report_ids, context=None):
62 """
63 Return a list of pdf encoded in base64
64 """
65 pool = pooler.get_pool(cr.dbname)
66 report_obj = pool.get('ir.actions.report.xml')
67
68 spool = ExportService._services['report']
69
70 pdf_reports = []
71 report_list = report_obj.browse(cr, uid, report_ids, context=context)
72 for report in report_list:
73
74 report_key = spool.exp_report(cr.dbname, uid, report.report_name,
75 ids, datas=data, context=context)
76 while 1:
77 res = spool.exp_report_get(cr.dbname, uid, report_key)
78 if res.get('state'):
79 break
80 time.sleep(_POLLING_DELAY)
81 pdf = base64.b64decode(res.get('result'))
82 pdf_reports.append(pdf)
83 return pdf_reports
84
85 def _get_report_ids(self, cr, uid, ids, context=None):
86 """
87 Hook to define the list of report to print
88 """
89 return []
90
91 def create_single_pdf(self, cr, uid, ids, data, report_xml, context=None):
92 """Call both report to assemble both pdf"""
93
94 report_ids = self._get_report_ids(cr, uid, ids, context=context)
95
96 pdf_reports = self._generate_all_pdf(cr, uid, ids, data, report_ids, context=context)
97
98 pdf_assemblage = assemble_pdf(pdf_reports)
99 return pdf_assemblage, 'pdf'
100
101 def create(self, cr, uid, ids, data, context=None):
102 """We override the create function in order to handle generator
103 Code taken from report openoffice. Thanks guys :) """
104 pool = pooler.get_pool(cr.dbname)
105 ir_obj = pool.get('ir.actions.report.xml')
106 report_xml_ids = ir_obj.search(cr, uid,
107 [('report_name', '=', self.name[7:])], context=context)
108 if report_xml_ids:
109
110 report_xml = ir_obj.browse(cr,
111 uid,
112 report_xml_ids[0],
113 context=context)
114 report_xml.report_rml = None
115 report_xml.report_rml_content = None
116 report_xml.report_sxw_content_data = None
117 report_xml.report_sxw_content = None
118 report_xml.report_sxw = None
119 else:
120 return super(PDFReportAssembler, self).create(cr, uid, ids, data, context)
121 if report_xml.report_type != 'assemblage':
122 return super(PDFReportAssembler, self).create(cr, uid, ids, data, context)
123 result = self.create_source_pdf(cr, uid, ids, data, report_xml, context)
124 if not result:
125 return (False, False)
126 return result

Subscribers

People subscribed via source and target branches