Merge lp:~camptocamp/openerp-reporting-engines/7.0-add-base_report_assembler-yvr into lp:openerp-reporting-engines
- 7.0-add-base_report_assembler-yvr
- Merge into 7.0
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 |
Related bugs: |
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 |
Commit message
Description of the change
Add a module to merge two pdf report in one
Pedro Manuel Baeza (pedro.baeza) wrote : | # |
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote : | # |
This module doesn't install configuration for report it is only the base.
You can use account_
https:/
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote : | # |
Assemblage has other meanings than only art purpose:
http://
Assemblage seems more accurate than assembly to me.
Assembly is more used for a group of person or a governement entity.
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
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
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!
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
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote : | # |
LGTM.
Fixed some neat picks
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote : | # |
LGTM, thanks
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote : | # |
Thanks
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.
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-
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
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 |
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.