diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 6c30531bda..f62611a247 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Add configuration for report convert command * Use process pool to run cron * Lock only running cron record * Add option to skip tests that require network diff --git a/trytond/doc/topics/configuration.rst b/trytond/doc/topics/configuration.rst index 86c9eb2d0b..85b27bd485 100644 --- a/trytond/doc/topics/configuration.rst +++ b/trytond/doc/topics/configuration.rst @@ -790,6 +790,30 @@ The timeout duration of the select call when listening on a channel. Default: ``5`` +.. _config-report: + +report +------ + +.. _config-report.convert_command: + +convert_command +--------------- + +The command to convert document between formats. + +The available keywords are: + + - ``%(directory)s``: the temporary working directory + - ``%(input_format)s``: the format of the file to convert + - ``%(input_extension)s``: the extension of the file to convert + - ``%(input_path)s``: the path of the file to convert + - ``%(output_format)s``: the format to which the file must be converted + - ``%(output_extension)s``: the extension for the converted file + - ``%(output_path)s``: the path where the converted file must be written + +The command must write the result in ``%(output_path)s``. + .. _config-html: html diff --git a/trytond/trytond/config.py b/trytond/trytond/config.py index 997630a945..617e70cb27 100644 --- a/trytond/trytond/config.py +++ b/trytond/trytond/config.py @@ -97,6 +97,7 @@ class TrytonConfigParser(configparser.ConfigParser): self.set('bus', 'long_polling_timeout', str(5 * 60)) self.set('bus', 'cache_timeout', '5') self.set('bus', 'select_timeout', '5') + self.add_section('report') self.add_section('html') self.update_environ() self.update_etc() diff --git a/trytond/trytond/report/report.py b/trytond/trytond/report/report.py index 8795ec2e19..33a6609fbf 100644 --- a/trytond/trytond/report/report.py +++ b/trytond/trytond/report/report.py @@ -6,6 +6,8 @@ import logging import math import operator import os +import pathlib +import shutil import subprocess import tempfile import time @@ -31,6 +33,7 @@ except ImportError: from genshi.filters import Translator from genshi.template.text import TextTemplate +from trytond.config import config from trytond.exceptions import UserError from trytond.i18n import gettext from trytond.pool import Pool, PoolBase @@ -110,6 +113,13 @@ NO_BREAKING_SPACE = '\u00A0' # round it to 100. REPORT_NAME_MAX_LENGTH = 100 +CONVERT_COMMAND = config.get( + 'report', 'convert_command', + default='soffice --headless --nolockcheck --nodefault --norestore ' + '--convert-to "%(output_extension)s" ' + '--outdir "%(directory)s" ' + '"%(input_path)s"') + class TranslateFactory: @@ -382,42 +392,48 @@ class Report(URLMixin, PoolBase): if input_format == output_format and output_format in MIMETYPES: return output_format, data - dtemp = tempfile.mkdtemp(prefix='trytond_') - path = os.path.join( - dtemp, report.report_name + os.extsep + input_format) - oext = FORMAT2EXT.get(output_format, output_format) + directory = tempfile.mkdtemp(prefix='trytond_') + input_extension = FORMAT2EXT.get(input_format, input_format) + output_extension = FORMAT2EXT.get(output_format, output_format) + path = pathlib.Path(directory, report.report_name) + input_path = path.with_suffix(os.extsep + input_extension) + output_path = path.with_suffix(os.extsep + output_extension) mode = 'w+' if isinstance(data, str) else 'wb+' - with open(path, mode) as fp: + with open(input_path, mode) as fp: fp.write(data) try: - cmd = ['soffice', - '--headless', '--nolockcheck', '--nodefault', '--norestore', - '--convert-to', oext, '--outdir', dtemp, path] - output = os.path.splitext(path)[0] + os.extsep + oext + cmd = CONVERT_COMMAND % { + 'directory': directory, + 'input_format': input_format, + 'input_extension': input_extension, + 'input_path': input_path, + 'output_format': output_format, + 'output_extension': output_extension, + 'output_path': output_path, + } for count in range(retry, -1, -1): if count != retry: time.sleep(0.02 * (retry - count)) try: - subprocess.check_call(cmd, timeout=timeout) + subprocess.check_call(cmd, timeout=timeout, shell=True) except subprocess.CalledProcessError: if count: continue logger.error( - "fail to convert %s to %s", report.report_name, oext, - exc_info=True) + "fail to convert %s to %s", + report.report_name, output_format, exc_info=True) break - if os.path.exists(output): - with open(output, 'rb') as fp: - return oext, fp.read() + if os.path.exists(output_path): + with open(output_path, 'rb') as fp: + return output_extension, fp.read() else: logger.error( - 'fail to convert %s to %s', report.report_name, oext) + 'fail to convert %s to %s', + report.report_name, output_format) return input_format, data finally: try: - os.remove(path) - os.remove(output) - os.rmdir(dtemp) + shutil.rmtree(directory, ignore_errors=True) except OSError: pass