[IMP] report_py3o, report_py3o_fusion_server: black, isort

pull/479/head
Laurent Mignon (ACSONE) 2019-11-19 14:36:37 +01:00 committed by Alexis de Lattre
parent 19961919e6
commit b2735878e4
6 changed files with 397 additions and 326 deletions

View File

@ -1,31 +1,21 @@
# 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': '12.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",
"demo/py3o_pdf_options.xml",
],
'data': [
"name": "Py3o Report Engine - Fusion server support",
"summary": "Let the fusion server handle format conversion.",
"version": "12.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", "demo/py3o_pdf_options.xml"],
"data": [
"views/ir_actions_report.xml",
'security/ir.model.access.csv',
'views/py3o_server.xml',
'views/py3o_pdf_options.xml',
"security/ir.model.access.csv",
"views/py3o_server.xml",
"views/py3o_pdf_options.xml",
],
'installable': True,
"installable": True,
}

View File

@ -2,14 +2,16 @@
# © 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__)
class IrActionsReport(models.Model):
_inherit = 'ir.actions.report'
_inherit = "ir.actions.report"
@api.multi
@api.constrains("py3o_is_local_fusion", "py3o_server_id")
@ -17,40 +19,52 @@ class IrActionsReport(models.Model):
for report in self:
if report.report_type != "py3o":
continue
if (not report.py3o_is_local_fusion and not report.py3o_server_id):
raise ValidationError(_(
"You can not use remote fusion without Fusion server. "
"Please specify a Fusion Server"))
if not report.py3o_is_local_fusion and not report.py3o_server_id:
raise ValidationError(
_(
"You can not use remote fusion without Fusion server. "
"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")
"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")
pdf_options_id = fields.Many2one(
'py3o.pdf.options', string='PDF Options', ondelete='restrict',
"py3o.pdf.options",
string="PDF Options",
ondelete="restrict",
help="PDF options can be set per report, but also per Py3o Server. "
"If both are defined, the options on the report are used.")
"If both are defined, the options on the report are used.",
)
@api.depends("lo_bin_path", "is_py3o_native_format", "report_type",
"py3o_server_id")
@api.depends(
"lo_bin_path", "is_py3o_native_format", "report_type", "py3o_server_id"
)
@api.multi
def _compute_py3o_report_not_available(self):
for rec in self:
if not rec.report_type == "py3o":
continue
if (not rec.is_py3o_native_format and
not rec.lo_bin_path and not rec.py3o_server_id):
if (
not rec.is_py3o_native_format
and not rec.lo_bin_path
and not rec.py3o_server_id
):
rec.is_py3o_report_not_available = True
rec.msg_py3o_report_not_available = _(
"A fusion server or a libreoffice runtime are required "
"to genereate the py3o report '%s'. If the libreoffice"
"runtime is already installed and is not found by "
"Odoo, you can provide the full path to the runtime by "
"setting the key 'py3o.conversion_command' into the "
"configuration parameters."
) % rec.name
rec.msg_py3o_report_not_available = (
_(
"A fusion server or a libreoffice runtime are required "
"to genereate the py3o report '%s'. If the libreoffice"
"runtime is already installed and is not found by "
"Odoo, you can provide the full path to the runtime by "
"setting the key 'py3o.conversion_command' into the "
"configuration parameters."
)
% rec.name
)

View File

@ -2,223 +2,283 @@
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
import logging
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
logger = logging.getLogger(__name__)
class Py3oPdfOptions(models.Model):
_name = 'py3o.pdf.options'
_description = 'Define PDF export options for Libreoffice'
_name = "py3o.pdf.options"
_description = "Define PDF export options for Libreoffice"
name = fields.Char(required=True)
# GENERAL TAB
# UseLosslessCompression (bool)
image_compression = fields.Selection([
('lossless', 'Lossless Compression'),
('jpeg', 'JPEG Compression'),
], string='Image Compression', default='jpeg')
image_compression = fields.Selection(
[("lossless", "Lossless Compression"), ("jpeg", "JPEG Compression")],
string="Image Compression",
default="jpeg",
)
# Quality (int)
image_jpeg_quality = fields.Integer(
string='Image JPEG Quality', default=90,
help="Enter a percentage between 0 and 100.")
string="Image JPEG Quality",
default=90,
help="Enter a percentage between 0 and 100.",
)
# ReduceImageResolution (bool) and MaxImageResolution (int)
image_reduce_resolution = fields.Selection([
('none', 'Disable'),
('75', '75 DPI'),
('150', '150 DPI'),
('300', '300 DPI'),
('600', '600 DPI'),
('1200', '1200 DPI'),
], string='Reduce Image Resolution', default='300')
watermark = fields.Boolean('Sign With Watermark')
image_reduce_resolution = fields.Selection(
[
("none", "Disable"),
("75", "75 DPI"),
("150", "150 DPI"),
("300", "300 DPI"),
("600", "600 DPI"),
("1200", "1200 DPI"),
],
string="Reduce Image Resolution",
default="300",
)
watermark = fields.Boolean("Sign With Watermark")
# Watermark (string)
watermark_text = fields.Char('WaterMark Text')
watermark_text = fields.Char("WaterMark Text")
# UseTaggedPDF (bool)
tagged_pdf = fields.Boolean('Tagged PDF (add document structure)')
tagged_pdf = fields.Boolean("Tagged PDF (add document structure)")
# SelectPdfVersion (int)
# 0 = PDF 1.4 (default selection).
# 1 = PDF/A-1 (ISO 19005-1:2005)
pdfa = fields.Boolean(
'Archive PDF/A-1a (ISO 19005-1)',
"Archive PDF/A-1a (ISO 19005-1)",
help="If you enable this option, you will not be able to "
"password-protect the document or apply other security settings.")
"password-protect the document or apply other security settings.",
)
# ExportFormFields (bool)
pdf_form = fields.Boolean('Create PDF Form', default=True)
pdf_form = fields.Boolean("Create PDF Form", default=True)
# FormsType (int)
pdf_form_format = fields.Selection([
('0', 'FDF'),
('1', 'PDF'),
('2', 'HTML'),
('3', 'XML'),
], string='Submit Format', default='0')
pdf_form_format = fields.Selection(
[("0", "FDF"), ("1", "PDF"), ("2", "HTML"), ("3", "XML")],
string="Submit Format",
default="0",
)
# AllowDuplicateFieldNames (bool)
pdf_form_allow_duplicate = fields.Boolean('Allow Duplicate Field Names')
pdf_form_allow_duplicate = fields.Boolean("Allow Duplicate Field Names")
# ExportBookmarks (bool)
export_bookmarks = fields.Boolean('Export Bookmarks', default=True)
export_bookmarks = fields.Boolean("Export Bookmarks", default=True)
# ExportPlaceholders (bool)
export_placeholders = fields.Boolean('Export Placeholders', default=True)
export_placeholders = fields.Boolean("Export Placeholders", default=True)
# ExportNotes (bool)
export_comments = fields.Boolean('Export Comments')
export_comments = fields.Boolean("Export Comments")
# ExportHiddenSlides (bool) ??
export_hidden_slides = fields.Boolean(
'Export Automatically Insered Blank Pages')
export_hidden_slides = fields.Boolean("Export Automatically Insered Blank Pages")
# Doesn't make sense to have the option "View PDF after export" ! :)
# INITIAL VIEW TAB
# InitialView (int)
initial_view = fields.Selection([
('0', 'Page Only'),
('1', 'Bookmarks and Page'),
('2', 'Thumbnails and Page'),
], string='Panes', default='0')
initial_view = fields.Selection(
[("0", "Page Only"), ("1", "Bookmarks and Page"), ("2", "Thumbnails and Page")],
string="Panes",
default="0",
)
# InitialPage (int)
initial_page = fields.Integer(string='Initial Page', default=1)
initial_page = fields.Integer(string="Initial Page", default=1)
# Magnification (int)
magnification = fields.Selection([
('0', 'Default'),
('1', 'Fit in Window'),
('2', 'Fit Width'),
('3', 'Fit Visible'),
('4', 'Zoom'),
], string='Magnification', default='0')
magnification = fields.Selection(
[
("0", "Default"),
("1", "Fit in Window"),
("2", "Fit Width"),
("3", "Fit Visible"),
("4", "Zoom"),
],
string="Magnification",
default="0",
)
# Zoom (int)
zoom = fields.Integer(
string='Zoom Factor', default=100,
help='Possible values: from 50 to 1600')
string="Zoom Factor", default=100, help="Possible values: from 50 to 1600"
)
# PageLayout (int)
page_layout = fields.Selection([
('0', 'Default'),
('1', 'Single Page'),
('2', 'Continuous'),
('3', 'Continuous Facing'),
], string='Page Layout', default='0')
page_layout = fields.Selection(
[
("0", "Default"),
("1", "Single Page"),
("2", "Continuous"),
("3", "Continuous Facing"),
],
string="Page Layout",
default="0",
)
# USER INTERFACE TAB
# ResizeWindowToInitialPage (bool)
resize_windows_initial_page = fields.Boolean(
string='Resize Windows to Initial Page')
string="Resize Windows to Initial Page"
)
# CenterWindow (bool)
center_window = fields.Boolean(string='Center Window on Screen')
center_window = fields.Boolean(string="Center Window on Screen")
# OpenInFullScreenMode (bool)
open_fullscreen = fields.Boolean(string='Open in Full Screen Mode')
open_fullscreen = fields.Boolean(string="Open in Full Screen Mode")
# DisplayPDFDocumentTitle (bool)
display_document_title = fields.Boolean(string='Display Document Title')
display_document_title = fields.Boolean(string="Display Document Title")
# HideViewerMenubar (bool)
hide_menubar = fields.Boolean(string='Hide Menubar')
hide_menubar = fields.Boolean(string="Hide Menubar")
# HideViewerToolbar (bool)
hide_toolbar = fields.Boolean(string='Hide Toolbar')
hide_toolbar = fields.Boolean(string="Hide Toolbar")
# HideViewerWindowControls (bool)
hide_window_controls = fields.Boolean(string='Hide Windows Controls')
hide_window_controls = fields.Boolean(string="Hide Windows Controls")
# OpenBookmarkLevels (int) -1 = all (default) from 1 to 10
open_bookmark_levels = fields.Selection([
('-1', 'All Levels'),
('1', '1'),
('2', '2'),
('3', '3'),
('4', '4'),
('5', '5'),
('6', '6'),
('7', '7'),
('8', '8'),
('9', '9'),
('10', '10'),
], default='-1', string='Visible Bookmark Levels')
open_bookmark_levels = fields.Selection(
[
("-1", "All Levels"),
("1", "1"),
("2", "2"),
("3", "3"),
("4", "4"),
("5", "5"),
("6", "6"),
("7", "7"),
("8", "8"),
("9", "9"),
("10", "10"),
],
default="-1",
string="Visible Bookmark Levels",
)
# LINKS TAB
# ExportBookmarksToPDFDestination (bool)
export_bookmarks_named_dest = fields.Boolean(
string='Export Bookmarks as Named Destinations')
string="Export Bookmarks as Named Destinations"
)
# ConvertOOoTargetToPDFTarget (bool)
convert_doc_ref_to_pdf_target = fields.Boolean(
string='Convert Document References to PDF Targets')
string="Convert Document References to PDF Targets"
)
# ExportLinksRelativeFsys (bool)
export_filesystem_urls = fields.Boolean(
string='Export URLs Relative to Filesystem')
export_filesystem_urls = fields.Boolean(string="Export URLs Relative to Filesystem")
# PDFViewSelection -> mnDefaultLinkAction (int)
cross_doc_link_action = fields.Selection([
('0', 'Default'),
('1', 'Open with PDF Reader Application'),
('2', 'Open with Internet Browser'),
], string='Cross-document Links', default='0')
cross_doc_link_action = fields.Selection(
[
("0", "Default"),
("1", "Open with PDF Reader Application"),
("2", "Open with Internet Browser"),
],
string="Cross-document Links",
default="0",
)
# SECURITY TAB
# EncryptFile (bool)
encrypt = fields.Boolean('Encrypt')
encrypt = fields.Boolean("Encrypt")
# DocumentOpenPassword (char)
document_password = fields.Char(string='Document Password')
document_password = fields.Char(string="Document Password")
# RestrictPermissions (bool)
restrict_permissions = fields.Boolean('Restrict Permissions')
restrict_permissions = fields.Boolean("Restrict Permissions")
# PermissionPassword (char)
permission_password = fields.Char(string='Permission Password')
permission_password = fields.Char(string="Permission Password")
# TODO PreparedPasswords + PreparedPermissionPassword
# I don't see those fields in the LO interface !
# But they are used in the LO code...
# Printing (int)
printing = fields.Selection([
('0', 'Not Permitted'),
('1', 'Low Resolution (150 dpi)'),
('2', 'High Resolution'),
], string='Printing', default='2')
printing = fields.Selection(
[
("0", "Not Permitted"),
("1", "Low Resolution (150 dpi)"),
("2", "High Resolution"),
],
string="Printing",
default="2",
)
# Changes (int)
changes = fields.Selection([
('0', 'Not Permitted'),
('1', 'Inserting, Deleting and Rotating Pages'),
('2', 'Filling in Form Fields'),
('3', 'Commenting, Filling in Form Fields'),
('4', 'Any Except Extracting Pages'),
], string='Changes', default='4')
changes = fields.Selection(
[
("0", "Not Permitted"),
("1", "Inserting, Deleting and Rotating Pages"),
("2", "Filling in Form Fields"),
("3", "Commenting, Filling in Form Fields"),
("4", "Any Except Extracting Pages"),
],
string="Changes",
default="4",
)
# EnableCopyingOfContent (bool)
content_copying_allowed = fields.Boolean(
string='Enable Copying of Content', default=True)
string="Enable Copying of Content", default=True
)
# EnableTextAccessForAccessibilityTools (bool)
text_access_accessibility_tools_allowed = fields.Boolean(
string='Enable Text Access for Accessibility Tools', default=True)
# DIGITAL SIGNATURE TAB
# This will be possible but not easy
# Because the certificate parameter is a pointer to a certificate
# already registered in LO
# On Linux LO reuses the Mozilla certificate store (on Windows the
# one from Windows)
# But there seems to be some possibilities to send this certificate via API
# It seems you can add temporary certificates during runtime:
# https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1security_1_1XCertificateContainer.html
# Here is an API to retrieve the known certificates:
# https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1xml_1_1crypto_1_1XSecurityEnvironment.html
# Thanks to 'samuel_m' on libreoffice-dev IRC chan for pointing me to this
string="Enable Text Access for Accessibility Tools", default=True
)
"""
DIGITAL SIGNATURE TAB
This will be possible but not easy
Because the certificate parameter is a pointer to a certificate
already registered in LO
On Linux LO reuses the Mozilla certificate store (on Windows the
one from Windows)
But there seems to be some possibilities to send this certificate via API
It seems you can add temporary certificates during runtime:
https://api.libreoffice.org/docs/idl/ref/
interfacecom_1_1sun_1_1star_1_1security_1_1XCertificateContainer.html
Here is an API to retrieve the known certificates:
https://api.libreoffice.org/docs/idl/ref/
interfacecom_1_1sun_1_1star_1_1xml_1_1crypto_1_1XSecurityEnvironment.html
Thanks to 'samuel_m' on libreoffice-dev IRC chan for pointing me to this
"""
@api.constrains(
'image_jpeg_quality', 'initial_page', 'pdfa',
'cross_doc_link_action', 'magnification', 'zoom')
"image_jpeg_quality",
"initial_page",
"pdfa",
"cross_doc_link_action",
"magnification",
"zoom",
)
def check_pdf_options(self):
for opt in self:
if opt.image_jpeg_quality > 100 or opt.image_jpeg_quality < 1:
raise ValidationError(_(
"The parameter Image JPEG Quality must be between 1 %%"
" and 100 %% (current value: %s %%)")
% opt.image_jpeg_quality)
raise ValidationError(
_(
"The parameter Image JPEG Quality must be between 1 %%"
" and 100 %% (current value: %s %%)"
)
% opt.image_jpeg_quality
)
if opt.initial_page < 1:
raise ValidationError(_(
"The initial page parameter must be strictly positive "
"(current value: %d)") % opt.initial_page)
if opt.pdfa and opt.cross_doc_link_action == '1':
raise ValidationError(_(
"The PDF/A option is not compatible with "
"'Cross-document Links' = "
"'Open with PDF Reader Application'."))
if opt.magnification == '4' and (opt.zoom < 50 or opt.zoom > 1600):
raise ValidationError(_(
"The value of the zoom factor must be between 50 and 1600 "
"(current value: %d)") % opt.zoom)
raise ValidationError(
_(
"The initial page parameter must be strictly positive "
"(current value: %d)"
)
% opt.initial_page
)
if opt.pdfa and opt.cross_doc_link_action == "1":
raise ValidationError(
_(
"The PDF/A option is not compatible with "
"'Cross-document Links' = "
"'Open with PDF Reader Application'."
)
)
if opt.magnification == "4" and (opt.zoom < 50 or opt.zoom > 1600):
raise ValidationError(
_(
"The value of the zoom factor must be between 50 and 1600 "
"(current value: %d)"
)
% opt.zoom
)
@api.onchange('encrypt')
@api.onchange("encrypt")
def encrypt_change(self):
if not self.encrypt:
self.document_password = False
@api.onchange('restrict_permissions')
@api.onchange("restrict_permissions")
def restrict_permissions_change(self):
if not self.restrict_permissions:
self.permission_password = False
@api.onchange('pdfa')
@api.onchange("pdfa")
def pdfa_change(self):
if self.pdfa:
self.pdf_form = False
@ -229,87 +289,97 @@ class Py3oPdfOptions(models.Model):
self.ensure_one()
options = {}
# GENERAL TAB
if self.image_compression == 'lossless':
options['UseLosslessCompression'] = True
if self.image_compression == "lossless":
options["UseLosslessCompression"] = True
else:
options['UseLosslessCompression'] = False
options['Quality'] = self.image_jpeg_quality
if self.image_reduce_resolution != 'none':
options['ReduceImageResolution'] = True
options['MaxImageResolution'] = int(self.image_reduce_resolution)
options["UseLosslessCompression"] = False
options["Quality"] = self.image_jpeg_quality
if self.image_reduce_resolution != "none":
options["ReduceImageResolution"] = True
options["MaxImageResolution"] = int(self.image_reduce_resolution)
else:
options['ReduceImageResolution'] = False
options["ReduceImageResolution"] = False
if self.watermark and self.watermark_text:
options['Watermark'] = self.watermark_text
options["Watermark"] = self.watermark_text
if self.pdfa:
options['SelectPdfVersion'] = 1
options['UseTaggedPDF'] = self.tagged_pdf
options["SelectPdfVersion"] = 1
options["UseTaggedPDF"] = self.tagged_pdf
else:
options['SelectPdfVersion'] = 0
options["SelectPdfVersion"] = 0
if self.pdf_form and self.pdf_form_format and not self.pdfa:
options['ExportFormFields'] = True
options['FormsType'] = int(self.pdf_form_format)
options['AllowDuplicateFieldNames'] = self.pdf_form_allow_duplicate
options["ExportFormFields"] = True
options["FormsType"] = int(self.pdf_form_format)
options["AllowDuplicateFieldNames"] = self.pdf_form_allow_duplicate
else:
options['ExportFormFields'] = False
options["ExportFormFields"] = False
options.update({
'ExportBookmarks': self.export_bookmarks,
'ExportPlaceholders': self.export_placeholders,
'ExportNotes': self.export_comments,
'ExportHiddenSlides': self.export_hidden_slides,
})
options.update(
{
"ExportBookmarks": self.export_bookmarks,
"ExportPlaceholders": self.export_placeholders,
"ExportNotes": self.export_comments,
"ExportHiddenSlides": self.export_hidden_slides,
}
)
# INITIAL VIEW TAB
options.update({
'InitialView': int(self.initial_view),
'InitialPage': self.initial_page,
'Magnification': int(self.magnification),
'PageLayout': int(self.page_layout),
})
options.update(
{
"InitialView": int(self.initial_view),
"InitialPage": self.initial_page,
"Magnification": int(self.magnification),
"PageLayout": int(self.page_layout),
}
)
if self.magnification == '4':
options['Zoom'] = self.zoom
if self.magnification == "4":
options["Zoom"] = self.zoom
# USER INTERFACE TAB
options.update({
'ResizeWindowToInitialPage': self.resize_windows_initial_page,
'CenterWindow': self.center_window,
'OpenInFullScreenMode': self.open_fullscreen,
'DisplayPDFDocumentTitle': self.display_document_title,
'HideViewerMenubar': self.hide_menubar,
'HideViewerToolbar': self.hide_toolbar,
'HideViewerWindowControls': self.hide_window_controls,
})
options.update(
{
"ResizeWindowToInitialPage": self.resize_windows_initial_page,
"CenterWindow": self.center_window,
"OpenInFullScreenMode": self.open_fullscreen,
"DisplayPDFDocumentTitle": self.display_document_title,
"HideViewerMenubar": self.hide_menubar,
"HideViewerToolbar": self.hide_toolbar,
"HideViewerWindowControls": self.hide_window_controls,
}
)
if self.open_bookmark_levels:
options['OpenBookmarkLevels'] = int(self.open_bookmark_levels)
options["OpenBookmarkLevels"] = int(self.open_bookmark_levels)
# LINKS TAB
options.update({
'ExportBookmarksToPDFDestination':
self.export_bookmarks_named_dest,
'ConvertOOoTargetToPDFTarget': self.convert_doc_ref_to_pdf_target,
'ExportLinksRelativeFsys': self.export_filesystem_urls,
'PDFViewSelection': int(self.cross_doc_link_action),
})
options.update(
{
"ExportBookmarksToPDFDestination": self.export_bookmarks_named_dest,
"ConvertOOoTargetToPDFTarget": self.convert_doc_ref_to_pdf_target,
"ExportLinksRelativeFsys": self.export_filesystem_urls,
"PDFViewSelection": int(self.cross_doc_link_action),
}
)
# SECURITY TAB
if not self.pdfa:
if self.encrypt and self.document_password:
options['EncryptFile'] = True
options['DocumentOpenPassword'] = self.document_password
options["EncryptFile"] = True
options["DocumentOpenPassword"] = self.document_password
if self.restrict_permissions and self.permission_password:
options.update({
'RestrictPermissions': True,
'PermissionPassword': self.permission_password,
'Printing': int(self.printing),
'Changes': int(self.changes),
'EnableCopyingOfContent': self.content_copying_allowed,
'EnableTextAccessForAccessibilityTools':
self.text_access_accessibility_tools_allowed,
})
# fmt: off
options.update(
{
"RestrictPermissions": True,
"PermissionPassword": self.permission_password,
"Printing": int(self.printing),
"Changes": int(self.changes),
"EnableCopyingOfContent": self.content_copying_allowed,
"EnableTextAccessForAccessibilityTools":
self.text_access_accessibility_tools_allowed,
}
)
# fmt: on
logger.debug(
'Py3o PDF options ID %s converted to %s', self.id, options)
logger.debug("Py3o PDF options ID %s converted to %s", self.id, options)
return options

View File

@ -5,13 +5,14 @@
import json
import logging
import os
import requests
import tempfile
from datetime import datetime
from contextlib import closing
from datetime import datetime
from io import BytesIO
import requests
from openerp import _, api, models
from openerp.exceptions import UserError
from io import BytesIO
logger = logging.getLogger(__name__)
@ -19,11 +20,11 @@ try:
from py3o.template import Template
from py3o.template.helpers import Py3oConvertor
except ImportError:
logger.debug('Cannot import py3o.template')
logger.debug("Cannot import py3o.template")
class Py3oReport(models.TransientModel):
_inherit = 'py3o.report'
_inherit = "py3o.report"
@api.multi
def _create_single_report(self, model_instance, data):
@ -33,40 +34,32 @@ class Py3oReport(models.TransientModel):
report_xml = self.ir_actions_report_id
filetype = report_xml.py3o_filetype
if not report_xml.py3o_server_id:
return super(Py3oReport, self)._create_single_report(
model_instance, data,
)
return super(Py3oReport, self)._create_single_report(model_instance, data)
elif report_xml.py3o_is_local_fusion:
result_path = super(
Py3oReport, self.with_context(
report_py3o_skip_conversion=True,
)
)._create_single_report(
model_instance, data
)
with closing(open(result_path, 'rb')) as out_stream:
Py3oReport, self.with_context(report_py3o_skip_conversion=True)
)._create_single_report(model_instance, data)
with closing(open(result_path, "rb")) as out_stream:
tmpl_data = out_stream.read()
datadict = {}
else:
result_fd, result_path = tempfile.mkstemp(
suffix='.' + filetype, prefix='p3o.report.tmp.')
suffix="." + filetype, prefix="p3o.report.tmp."
)
tmpl_data = self.get_template(model_instance)
in_stream = BytesIO(tmpl_data)
with closing(os.fdopen(result_fd, 'wb+')) as out_stream:
with closing(os.fdopen(result_fd, "wb+")) 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)
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,
}
files = {"tmpl_file": tmpl_data}
fields = {
"targetformat": filetype,
"datadict": json.dumps(datadict),
@ -74,37 +67,41 @@ class Py3oReport(models.TransientModel):
"escape_false": "on",
}
if report_xml.py3o_is_local_fusion:
fields['skipfusion'] = '1'
fields["skipfusion"] = "1"
url = report_xml.py3o_server_id.url
logger.info(
'Connecting to %s to convert report %s to %s',
url, report_xml.report_name, filetype)
if filetype == 'pdf':
options = report_xml.pdf_options_id or\
report_xml.py3o_server_id.pdf_options_id
"Connecting to %s to convert report %s to %s",
url,
report_xml.report_name,
filetype,
)
if filetype == "pdf":
options = (
report_xml.pdf_options_id or report_xml.py3o_server_id.pdf_options_id
)
if options:
pdf_options_dict = options.odoo2libreoffice_options()
fields['pdf_options'] = json.dumps(pdf_options_dict)
logger.debug('PDF Export options: %s', pdf_options_dict)
fields["pdf_options"] = json.dumps(pdf_options_dict)
logger.debug("PDF Export options: %s", pdf_options_dict)
start_chrono = datetime.now()
r = requests.post(url, data=fields, files=files)
if r.status_code != 200:
# server says we have an issue... let's tell that to enduser
logger.error('Py3o fusion server error: %s', r.text)
raise UserError(
_('Fusion server error %s') % r.text,
)
logger.error("Py3o fusion server error: %s", r.text)
raise UserError(_("Fusion server error %s") % r.text)
chunk_size = 1024
with open(result_path, 'w+b') as fd:
with open(result_path, "w+b") as fd:
for chunk in r.iter_content(chunk_size):
fd.write(chunk)
end_chrono = datetime.now()
convert_seconds = (end_chrono - start_chrono).total_seconds()
logger.info(
'Report %s converted to %s in %s seconds',
report_xml.report_name, filetype, convert_seconds)
"Report %s converted to %s in %s seconds",
report_xml.report_name,
filetype,
convert_seconds,
)
if len(model_instance) == 1:
self._postprocess_report(
model_instance, result_path)
self._postprocess_report(model_instance, result_path)
return result_path

View File

@ -4,16 +4,21 @@ from odoo import fields, models
class Py3oServer(models.Model):
_name = 'py3o.server'
_description = 'Py3o server'
_rec_name = 'url'
_name = "py3o.server"
_description = "Py3o server"
_rec_name = "url"
url = fields.Char(
"Py3o Fusion Server URL", required=True,
"Py3o Fusion Server URL",
required=True,
help="If your Py3o Fusion server is on the same machine and runs "
"on the default port, the URL is http://localhost:8765/form")
"on the default port, the URL is http://localhost:8765/form",
)
is_active = fields.Boolean("Active", default=True)
pdf_options_id = fields.Many2one(
'py3o.pdf.options', string='PDF Options', ondelete='restrict',
"py3o.pdf.options",
string="PDF Options",
ondelete="restrict",
help="PDF options can be set per Py3o Server but also per report. "
"If both are defined, the options on the report are used.")
"If both are defined, the options on the report are used.",
)

View File

@ -1,62 +1,55 @@
# 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.models.ir_actions_report import \
PY3O_CONVERSION_COMMAND_PARAMETER
from odoo.addons.report_py3o.models.ir_actions_report import (
PY3O_CONVERSION_COMMAND_PARAMETER,
)
from odoo.addons.report_py3o.tests import test_report_py3o
@mock.patch(
'requests.post', mock.Mock(
"requests.post",
mock.Mock(
return_value=mock.Mock(
status_code=200,
iter_content=mock.Mock(return_value=[b'test_result']),
status_code=200, iter_content=mock.Mock(return_value=[b"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"})
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',
})
self.report.write({"py3o_server_id": py3o_server.id, "py3o_filetype": "pdf"})
self.py3o_server = py3o_server
def test_no_local_fusion_without_fusion_server(self):
self.assertTrue(self.report.py3o_is_local_fusion)
# Fusion server is only required if not local...
self.report.write({
"py3o_server_id": None,
"py3o_is_local_fusion": True,
})
self.report.write({
"py3o_server_id": self.py3o_server.id,
"py3o_is_local_fusion": True,
})
self.report.write({
"py3o_server_id": self.py3o_server.id,
"py3o_is_local_fusion": False,
})
self.report.write({"py3o_server_id": None, "py3o_is_local_fusion": True})
self.report.write(
{"py3o_server_id": self.py3o_server.id, "py3o_is_local_fusion": True}
)
self.report.write(
{"py3o_server_id": self.py3o_server.id, "py3o_is_local_fusion": False}
)
with self.assertRaises(ValidationError) as e:
self.report.write({
"py3o_server_id": None,
"py3o_is_local_fusion": False,
})
self.report.write({"py3o_server_id": None, "py3o_is_local_fusion": False})
self.assertEqual(
e.exception.name,
"You can not use remote fusion without Fusion server. "
"Please specify a Fusion Server")
"Please specify a Fusion Server",
)
def test_reports_no_local_fusion(self):
self.report.py3o_is_local_fusion = False
self.test_reports()
def test_odoo2libreoffice_options(self):
for options in self.env['py3o.pdf.options'].search([]):
for options in self.env["py3o.pdf.options"].search([]):
options_dict = options.odoo2libreoffice_options()
self.assertIsInstance(options_dict, dict)
@ -73,8 +66,9 @@ class TestReportPy3oFusionServer(test_report_py3o.TestReportPy3o):
self.assertFalse(self.report.msg_py3o_report_not_available)
# specify a wrong lo bin path and a non native format.
self.env['ir.config_parameter'].set_param(
PY3O_CONVERSION_COMMAND_PARAMETER, "/wrong_path")
self.env["ir.config_parameter"].set_param(
PY3O_CONVERSION_COMMAND_PARAMETER, "/wrong_path"
)
self.report.py3o_filetype = "pdf"
self.report.refresh()
# no native and no bin path, everything is still OK since a fusion
@ -91,8 +85,9 @@ class TestReportPy3oFusionServer(test_report_py3o.TestReportPy3o):
self.assertTrue(self.report.msg_py3o_report_not_available)
# if we set a libreffice runtime, the report is available again
self.env['ir.config_parameter'].set_param(
PY3O_CONVERSION_COMMAND_PARAMETER, "libreoffice")
self.env["ir.config_parameter"].set_param(
PY3O_CONVERSION_COMMAND_PARAMETER, "libreoffice"
)
self.report.refresh()
self.assertTrue(self.report.lo_bin_path)
self.assertFalse(self.report.is_py3o_report_not_available)