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/258/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
-
82report_py3o/models/py3o_report.py
-
4report_py3o/models/py3o_template.py
-
2report_py3o/security/ir.model.access.csv
-
BINreport_py3o/static/description/icon.png
-
110report_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 ir_actions_report_xml |
||||
from . import py3o_template |
from . import py3o_template |
||||
from . import py3o_server |
|
||||
from . import report |
from . import report |
||||
from . import py3o_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 |
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_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_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