diff --git a/report_py3o_fusion_server/__manifest__.py b/report_py3o_fusion_server/__manifest__.py index 18b5126d2..805362dc6 100644 --- a/report_py3o_fusion_server/__manifest__.py +++ b/report_py3o_fusion_server/__manifest__.py @@ -1,31 +1,21 @@ # Copyright 2017 Therp BV # 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, } diff --git a/report_py3o_fusion_server/models/ir_actions_report.py b/report_py3o_fusion_server/models/ir_actions_report.py index 6c8927d85..bda8982ea 100644 --- a/report_py3o_fusion_server/models/ir_actions_report.py +++ b/report_py3o_fusion_server/models/ir_actions_report.py @@ -2,14 +2,16 @@ # © 2017 Therp BV # 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 + ) diff --git a/report_py3o_fusion_server/models/py3o_pdf_options.py b/report_py3o_fusion_server/models/py3o_pdf_options.py index d292fa18e..7a6399e09 100644 --- a/report_py3o_fusion_server/models/py3o_pdf_options.py +++ b/report_py3o_fusion_server/models/py3o_pdf_options.py @@ -2,223 +2,283 @@ # @author: Alexis de Lattre # 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 diff --git a/report_py3o_fusion_server/models/py3o_report.py b/report_py3o_fusion_server/models/py3o_report.py index 318b7a8fa..6b264073f 100644 --- a/report_py3o_fusion_server/models/py3o_report.py +++ b/report_py3o_fusion_server/models/py3o_report.py @@ -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 diff --git a/report_py3o_fusion_server/models/py3o_server.py b/report_py3o_fusion_server/models/py3o_server.py index cab1ee2f8..d7dd210f2 100644 --- a/report_py3o_fusion_server/models/py3o_server.py +++ b/report_py3o_fusion_server/models/py3o_server.py @@ -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.", + ) diff --git a/report_py3o_fusion_server/tests/test_report_py3o_fusion_server.py b/report_py3o_fusion_server/tests/test_report_py3o_fusion_server.py index bf9debd07..407f7fee5 100644 --- a/report_py3o_fusion_server/tests/test_report_py3o_fusion_server.py +++ b/report_py3o_fusion_server/tests/test_report_py3o_fusion_server.py @@ -1,62 +1,55 @@ # Copyright 2017 Therp BV # 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)