# Copyright 2018 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) import html import time import babel import pytz import logging from base64 import b64decode from odoo.tools import misc, mail from odoo import fields logger = logging.getLogger(__name__) try: from genshi.core import Markup except ImportError: logger.debug('Cannot import py3o.template') def format_multiline_value(value): if value: return Markup(html.escape(value).replace('\n', ''). replace('\t', '')) return "" def display_address(address_record, without_company=False): return address_record.display_address(without_company=without_company) def format_datetime(env, value, tz=False, dt_format='medium', lang_code=False): """ Formats the datetime in a given format. :param {str, datetime} value: naive datetime to format :param {str} tz: name of the timezone :param {str} dt_format: one of “full”, “long”, “medium”, or “short”, or a custom date/time pattern compatible with `babel` lib :param {str} lang_code: ISO code of the language to use This is a copy of the function introduced in tools/misc in odoo 13.0 reference commit: ef7871826edce400884fe032366781f59aafd8a6 """ if not value: return '' if isinstance(value, str): timestamp = fields.Datetime.from_string(value) else: timestamp = value tz_name = tz or env.user.tz or 'UTC' utc_datetime = pytz.utc.localize(timestamp, is_dst=False) try: context_tz = pytz.timezone(tz_name) localized_datetime = utc_datetime.astimezone(context_tz) except Exception: localized_datetime = utc_datetime lang = env['res.lang']._lang_get( lang_code or env.context.get('lang') or 'en_US') locale = babel.Locale.parse(lang.code) # Babel allows to format datetime in a specific language without changing # locale, So month 1 = January in English, and janvier in French # short: 1/5/16, 10:20 PM | 5/01/16 22:20 # medium: Jan 5, 2016, 10:20:31 PM | 5 janv. 2016 22:20:31 # Formatting available here : # http://babel.pocoo.org/en/latest/dates.html#date-fields return babel.dates.format_datetime( localized_datetime, dt_format, locale=locale) class Py3oParserContext(object): def __init__(self, env): self._env = env self.localcontext = { 'user': self._env.user, 'lang': self._env.lang, # Odoo default format methods 'o_format_lang': self._format_lang, # prefixes with o_ to avoid nameclash with default method provided # by py3o.template 'o_format_date': self._format_date, 'o_format_datetime': self._format_datetime, # give access to the time lib 'time': time, # keeps methods from report_sxw to ease migration 'display_address': display_address, 'formatLang': self._old_format_lang, 'format_multiline_value': format_multiline_value, 'html_sanitize': mail.html2plaintext, 'b64decode': b64decode, } def _format_lang(self, value, lang_code=False, digits=None, grouping=True, monetary=False, dp=False, currency_obj=False, no_break_space=True): env = self._env if lang_code: context = dict(env.context, lang=lang_code) env = env(context=context) formatted_value = misc.formatLang( env, value, digits=digits, grouping=grouping, monetary=monetary, dp=dp, currency_obj=currency_obj) if currency_obj and currency_obj.symbol and no_break_space: parts = [] if currency_obj.position == 'after': parts = formatted_value.rsplit(" ", 1) elif currency_obj and currency_obj.position == 'before': parts = formatted_value.split(" ", 1) if parts: formatted_value = "\N{NO-BREAK SPACE}".join(parts) return formatted_value def _format_date(self, value, lang_code=False, date_format=False): return misc.format_date( self._env, value, lang_code=lang_code, date_format=date_format) def _format_datetime( self, value, tz=False, dt_format='short', lang_code=False): return format_datetime( self._env, value, tz=tz, dt_format=dt_format, lang_code=lang_code) def _old_format_lang(self, value, digits=None, date=False, date_time=False, grouping=True, monetary=False, dp=False, currency_obj=False): """ :param value: The value to format :param digits: Number of digits to display by default :param date: True if value must be formatted as a date (default False) :param date_time: True if value must be formatted as a datetime (default False) :param grouping: If value is float and grouping is True, the value will be formatted with the appropriate separators between figures according to the current lang specifications :param monetary: If value is float and monetary is True and grouping is True the value will be formatted according to the monetary format defined for the current lang :param dp: Decimal precision :param currency_obj: If provided the currency symbol will be added to value at position defined by the currency object :return: The formatted value """ if not date and not date_time: return self._format_lang( value, digits=digits, grouping=grouping, monetary=monetary, dp=dp, currency_obj=currency_obj, no_break_space=True) return self._format_date(self._env, value)