Browse Source
[RFR] split off the fusion server to its own module
[RFR] split off the fusion server to its own module
use libreoffice for conversions in the base version. Fixes #179pull/347/head
Holger Brunn
7 years ago
committed by
Laurent Mignon (ACSONE)
26 changed files with 466 additions and 256 deletions
-
123report_py3o/README.rst
-
3report_py3o/__manifest__.py
-
2report_py3o/demo/report_py3o.xml
-
1report_py3o/models/__init__.py
-
22report_py3o/models/ir_actions_report_xml.py
-
90report_py3o/models/py3o_report.py
-
4report_py3o/models/py3o_template.py
-
2report_py3o/security/ir.model.access.csv
-
BINreport_py3o/static/description/icon.png
-
112report_py3o/tests/test_report_py3o.py
-
2report_py3o/views/ir_report.xml
-
2report_py3o/views/py3o_template.xml
-
127report_py3o_fusion_server/README.rst
-
4report_py3o_fusion_server/__init__.py
-
30report_py3o_fusion_server/__manifest__.py
-
6report_py3o_fusion_server/demo/report_py3o.xml
-
6report_py3o_fusion_server/models/__init__.py
-
41report_py3o_fusion_server/models/ir_actions_report_xml.py
-
85report_py3o_fusion_server/models/py3o_report.py
-
0report_py3o_fusion_server/models/py3o_server.py
-
3report_py3o_fusion_server/security/ir.model.access.csv
-
BINreport_py3o_fusion_server/static/description/icon.png
-
4report_py3o_fusion_server/tests/__init__.py
-
38report_py3o_fusion_server/tests/test_report_py3o_fusion_server.py
-
13report_py3o_fusion_server/views/ir_report.xml
-
2report_py3o_fusion_server/views/py3o_server.xml
@ -1,5 +1,4 @@ |
|||
from . import ir_actions_report_xml |
|||
from . import py3o_template |
|||
from . import py3o_server |
|||
from . import report |
|||
from . import py3o_report |
@ -1,5 +1,3 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
access_py3o_template_admin,access_py3o_template_admin,model_py3o_template,base.group_no_one,1,1,1,1 |
|||
access_py3o_template_user,access_py3o_template_user,model_py3o_template,base.group_user,1,0,0,0 |
|||
access_py3o_server_admin,access_py3o_server_admin,model_py3o_server,base.group_no_one,1,1,1,1 |
|||
access_py3o_server_user,access_py3o_server_user,model_py3o_server,base.group_user,1,0,0,0 |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
@ -0,0 +1,127 @@ |
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
========================================== |
|||
Py3o Report Engine - Fusion server support |
|||
========================================== |
|||
|
|||
This addons was written to let a fusion server handle format conversion instead of local libreoffice. |
|||
|
|||
Installation |
|||
============ |
|||
|
|||
Install several additional components and Python libs: |
|||
|
|||
* `Py3o Fusion server <https://bitbucket.org/faide/py3o.fusion>`_, |
|||
* `Py3o render server <https://bitbucket.org/faide/py3o.renderserver>`_, |
|||
* a Java Runtime Environment (JRE), which can be OpenJDK, |
|||
* Libreoffice started in the background in headless mode, |
|||
* the Java driver for Libreoffice (Juno). |
|||
|
|||
It is also possible to use the Python driver for Libreoffice (PyUNO), but it is recommended to use the Java driver because it is more stable. |
|||
|
|||
The installation procedure below uses the Java driver. It has been successfully tested on Ubuntu 16.04 LTS ; if you use another OS, you may have to change a few details. |
|||
|
|||
Installation of py3o.fusion: |
|||
|
|||
.. code:: |
|||
|
|||
pip install py3o.fusion |
|||
pip install service-identity |
|||
|
|||
Installation of py3o.renderserver: |
|||
|
|||
.. code:: |
|||
|
|||
pip install py3o.renderserver |
|||
|
|||
Installation of Libreoffice and JRE on Debian/Ubuntu: |
|||
|
|||
.. code:: |
|||
|
|||
sudo apt-get install default-jre ure libreoffice-java-common libreoffice-writer |
|||
|
|||
You may have to install additionnal fonts. For example, to have the special unicode symbols for phone/fax/email in the PDF reports generated by Py3o, you should install the following package: |
|||
|
|||
.. code:: |
|||
|
|||
sudo apt-get install fonts-symbola |
|||
|
|||
At the end, with the dependencies, you should have the following py3o python libs: |
|||
|
|||
.. code:: |
|||
|
|||
% pip freeze | grep py3o |
|||
py3o.formats==0.3 |
|||
py3o.fusion==0.8.6 |
|||
py3o.renderclient==0.2 |
|||
py3o.renderers.juno==0.8 |
|||
py3o.renderserver==0.5.1 |
|||
py3o.template==0.9.11 |
|||
py3o.types==0.1.1 |
|||
|
|||
Start the Py3o Fusion server: |
|||
|
|||
.. code:: |
|||
|
|||
start-py3o-fusion --debug -s localhost |
|||
|
|||
Start the Py3o render server: |
|||
|
|||
.. code:: |
|||
|
|||
start-py3o-renderserver --java=/usr/lib/jvm/default-java/jre/lib/amd64/server/libjvm.so --ure=/usr/share --office=/usr/lib/libreoffice --driver=juno --sofficeport=8997 |
|||
|
|||
On the output of the Py3o render server, the first line looks like: |
|||
|
|||
.. code:: |
|||
|
|||
DEBUG:root:Starting JVM: /usr/lib/jvm/default-java/jre/lib/amd64/server/libjvm.so with options: -Djava.class.path=/usr/local/lib/python2.7/dist-packages/py3o/renderers/juno/py3oconverter.jar:/usr/share/java/juh.jar:/usr/share/java/jurt.jar:/usr/share/java/ridl.jar:/usr/share/java/unoloader.jar:/usr/share/java/java_uno.jar:/usr/lib/libreoffice/program/classes/unoil.jar -Xmx150M |
|||
|
|||
After **-Djava.class.path**, there is a list of Java libs with *.jar* extension ; check that each JAR file is really present on your filesystem. If one of the jar files is present in another directory, create a symlink that points to the real location of the file. If all the jar files are present on another directory, adapt the *--ure=* argument on the command line of Py3o render server. |
|||
|
|||
To check that the Py3o Fusion server is running fine, visit the URL http://<IP_address>:8765/form. On this web page, under the section *Target format*, make sure that you have a line *This server currently supports these formats: ods, odt, docx, doc, html, docbook, pdf, xls.*. |
|||
|
|||
Known issues / Roadmap |
|||
====================== |
|||
|
|||
* none yet |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues |
|||
<https://github.com/OCA/reporting-engine/issues>`_. In case of trouble, please |
|||
check there if your issue has already been reported. If you spotted it first, |
|||
help us smashing it by providing a detailed and welcomed feedback. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Florent Aide (`XCG Consulting <http://odoo.consulting/>`_) |
|||
* Laurent Mignon <laurent.mignon@acsone.eu>, |
|||
* Alexis de Lattre <alexis.delattre@akretion.com>, |
|||
* Guewen Baconnier <guewen.baconnier@camptocamp.com> |
|||
* Omar Castiñeira <omar@comunitea.com> |
|||
* Holger Brunn <hbrunn@therp.nl> |
|||
|
|||
Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list <mailto:community@mail.odoo.com>`_ or the `appropriate specialized mailinglist <https://odoo-community.org/groups>`_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues. |
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Therp BV <http://therp.nl> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from . import models |
@ -0,0 +1,30 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Therp BV <http://therp.nl> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
{ |
|||
'name': 'Py3o Report Engine - Fusion server support', |
|||
'summary': 'Let the fusion server handle format conversion.', |
|||
'version': '10.0.1.0.0', |
|||
'category': 'Reporting', |
|||
'license': 'AGPL-3', |
|||
'author': 'XCG Consulting,' |
|||
'ACSONE SA/NV,' |
|||
'Odoo Community Association (OCA)', |
|||
'website': 'https://github.com/OCA/reporting-engine', |
|||
'depends': ['report_py3o'], |
|||
'external_dependencies': { |
|||
'python': [ |
|||
'py3o.template', |
|||
'py3o.formats', |
|||
], |
|||
}, |
|||
'demo': [ |
|||
"demo/report_py3o.xml", |
|||
], |
|||
'data': [ |
|||
"views/ir_report.xml", |
|||
'security/ir.model.access.csv', |
|||
'views/py3o_server.xml', |
|||
], |
|||
'installable': True, |
|||
} |
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<odoo> |
|||
<record id="report_py3o.res_users_report_py3o" model="ir.actions.report.xml"> |
|||
<field name="py3o_is_local_fusion" eval="1"/> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,6 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Therp BV <http://therp.nl> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from . import ir_actions_report_xml |
|||
from . import py3o_report |
|||
from . import py3o_server |
@ -0,0 +1,41 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2013 XCG Consulting <http://odoo.consulting> |
|||
# © 2017 Therp BV <http://therp.nl> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
import logging |
|||
from openerp import _, api, fields, models |
|||
from odoo.exceptions import ValidationError |
|||
|
|||
logger = logging.getLogger(__name__) |
|||
|
|||
try: |
|||
from py3o.formats import Formats |
|||
except ImportError: |
|||
logger.debug('Cannot import py3o.formats') |
|||
|
|||
|
|||
class IrActionsReportXml(models.Model): |
|||
_inherit = 'ir.actions.report.xml' |
|||
|
|||
@api.multi |
|||
@api.constrains("py3o_is_local_fusion", "py3o_server_id", "py3o_filetype") |
|||
def _check_py3o_server_id(self): |
|||
for report in self: |
|||
if report.report_type != "py3o": |
|||
continue |
|||
is_native = Formats().get_format(report.py3o_filetype).native |
|||
if ((not is_native or not report.py3o_is_local_fusion) and |
|||
not report.py3o_server_id): |
|||
raise ValidationError(_( |
|||
"Can not use not native format in local fusion. " |
|||
"Please specify a Fusion Server")) |
|||
|
|||
py3o_is_local_fusion = fields.Boolean( |
|||
"Local Fusion", |
|||
help="Native formats will be processed without a server. " |
|||
"You must use this mode if you call methods on your model into " |
|||
"the template.", |
|||
default=True) |
|||
py3o_server_id = fields.Many2one( |
|||
"py3o.server", |
|||
"Fusion Server") |
@ -0,0 +1,85 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2013 XCG Consulting <http://odoo.consulting> |
|||
# © 2016 ACSONE SA/NV |
|||
# © 2017 Therp BV <http://therp.nl> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
import json |
|||
import logging |
|||
import os |
|||
import requests |
|||
import tempfile |
|||
from contextlib import closing |
|||
from openerp import _, api, models |
|||
from openerp.exceptions import UserError |
|||
from StringIO import StringIO |
|||
|
|||
logger = logging.getLogger(__name__) |
|||
|
|||
try: |
|||
from py3o.template import Template |
|||
from py3o.template.helpers import Py3oConvertor |
|||
except ImportError: |
|||
logger.debug('Cannot import py3o.template') |
|||
|
|||
|
|||
class Py3oReport(models.TransientModel): |
|||
_inherit = 'py3o.report' |
|||
|
|||
@api.multi |
|||
def _create_single_report(self, model_instance, data, save_in_attachment): |
|||
""" This function to generate our py3o report |
|||
""" |
|||
self.ensure_one() |
|||
report_xml = self.ir_actions_report_xml_id |
|||
filetype = report_xml.py3o_filetype |
|||
if report_xml.py3o_is_local_fusion: |
|||
result_path = super(Py3oReport, self)._create_single_report( |
|||
model_instance, data, save_in_attachment, |
|||
) |
|||
with closing(open(result_path, 'r')) as out_stream: |
|||
tmpl_data = out_stream.read() |
|||
datadict = {} |
|||
else: |
|||
result_fd, result_path = tempfile.mkstemp( |
|||
suffix='.' + filetype, prefix='p3o.report.tmp.') |
|||
tmpl_data = self.get_template(model_instance) |
|||
|
|||
in_stream = StringIO(tmpl_data) |
|||
with closing(os.fdopen(result_fd, 'w+')) as out_stream: |
|||
template = Template(in_stream, out_stream, escape_false=True) |
|||
localcontext = self._get_parser_context(model_instance, data) |
|||
expressions = template.get_all_user_python_expression() |
|||
py_expression = template.convert_py3o_to_python_ast( |
|||
expressions) |
|||
convertor = Py3oConvertor() |
|||
data_struct = convertor(py_expression) |
|||
datadict = data_struct.render(localcontext) |
|||
|
|||
# Call py3o.server to render the template in the desired format |
|||
files = { |
|||
'tmpl_file': tmpl_data, |
|||
} |
|||
fields = { |
|||
"targetformat": filetype, |
|||
"datadict": json.dumps(datadict), |
|||
"image_mapping": "{}", |
|||
"escape_false": "on", |
|||
} |
|||
if report_xml.py3o_is_local_fusion: |
|||
fields['skipfusion'] = '1' |
|||
r = requests.post( |
|||
report_xml.py3o_server_id.url, data=fields, files=files) |
|||
if r.status_code != 200: |
|||
# server says we have an issue... let's tell that to enduser |
|||
raise UserError( |
|||
_('Fusion server error %s') % r.text, |
|||
) |
|||
|
|||
chunk_size = 1024 |
|||
with open(result_path, 'w+') as fd: |
|||
for chunk in r.iter_content(chunk_size): |
|||
fd.write(chunk) |
|||
if len(model_instance) == 1: |
|||
self._postprocess_report( |
|||
result_path, model_instance.id, save_in_attachment) |
|||
return result_path |
@ -0,0 +1,3 @@ |
|||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" |
|||
access_py3o_server_admin,access_py3o_server_admin,model_py3o_server,base.group_no_one,1,1,1,1 |
|||
access_py3o_server_user,access_py3o_server_user,model_py3o_server,base.group_user,1,0,0,0 |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
@ -0,0 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Therp BV <http://therp.nl> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from . import test_report_py3o_fusion_server |
@ -0,0 +1,38 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 Therp BV <http://therp.nl> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
import mock |
|||
from odoo.exceptions import ValidationError |
|||
from odoo.addons.report_py3o.tests import test_report_py3o |
|||
|
|||
|
|||
@mock.patch( |
|||
'requests.post', mock.Mock( |
|||
return_value=mock.Mock( |
|||
status_code=200, |
|||
iter_content=mock.Mock(return_value=['test_result']), |
|||
) |
|||
) |
|||
) |
|||
class TestReportPy3oFusionServer(test_report_py3o.TestReportPy3o): |
|||
def setUp(self): |
|||
super(TestReportPy3oFusionServer, self).setUp() |
|||
py3o_server = self.env['py3o.server'].create({"url": "http://dummy"}) |
|||
# check the call to the fusion server |
|||
self.report.write({ |
|||
"py3o_server_id": py3o_server.id, |
|||
"py3o_filetype": 'pdf', |
|||
}) |
|||
|
|||
def test_no_local_fusion_without_fusion_server(self): |
|||
self.assertTrue(self.report.py3o_is_local_fusion) |
|||
with self.assertRaises(ValidationError) as e: |
|||
self.report.write({"py3o_server_id": None}) |
|||
self.assertEqual( |
|||
e.exception.name, |
|||
"Can not use not native format in local fusion. " |
|||
"Please specify a Fusion Server") |
|||
|
|||
def test_reports_no_local_fusion(self): |
|||
self.report.py3o_is_local_fusion = False |
|||
self.test_reports() |
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<odoo> |
|||
<record id="view_ir_actions_report_base" model="ir.ui.view"> |
|||
<field name="model">ir.actions.report.xml</field> |
|||
<field name="inherit_id" ref="report_py3o.py3o_report_view" /> |
|||
<field name="arch" type="xml"> |
|||
<field name="py3o_multi_in_one" position="after"> |
|||
<field name="py3o_is_local_fusion"/> |
|||
<field name="py3o_server_id" /> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue