[MIG] datetime_formatter: Migration to 13.0

pull/2274/head
Víctor Martínez 2021-02-24 14:32:57 +01:00 committed by tautvydas.banevicius
parent 3e73fefc38
commit 8aed91993e
5 changed files with 26 additions and 51 deletions

View File

@ -6,7 +6,7 @@
{ {
"name": "Date & Time Formatter", "name": "Date & Time Formatter",
"summary": "Helper functions to give correct format to date[time] fields", "summary": "Helper functions to give correct format to date[time] fields",
"version": "12.0.1.0.0", "version": "13.0.1.0.0",
"category": "Tools", "category": "Tools",
"website": "https://github.com/OCA/server-tools", "website": "https://github.com/OCA/server-tools",
"author": "Grupo ESOC Ingeniería de Servicios, " "author": "Grupo ESOC Ingeniería de Servicios, "
@ -14,5 +14,5 @@
"Odoo Community Association (OCA)", "Odoo Community Association (OCA)",
"license": "AGPL-3", "license": "AGPL-3",
"installable": True, "installable": True,
"depends": ["base",], "depends": ["base"],
} }

View File

@ -17,14 +17,11 @@ class ResLang(models.Model):
_inherit = "res.lang" _inherit = "res.lang"
@api.model @api.model
@api.returns("self")
def best_match(self, lang=None, failure_safe=True): def best_match(self, lang=None, failure_safe=True):
"""Get best match of current default lang. """Get best match of current default lang.
:param str lang: :param str lang:
If a language in the form of "en_US" is supplied, it will have the If a language in the form of "en_US" is supplied, it will have the
highest priority. highest priority.
:param bool failure_safe: :param bool failure_safe:
If ``False`` and the best matched language is not found installed, If ``False`` and the best matched language is not found installed,
an exception will be raised. Otherwise, the first installed an exception will be raised. Otherwise, the first installed
@ -32,26 +29,17 @@ class ResLang(models.Model):
""" """
# Find some installed language, as fallback # Find some installed language, as fallback
first_installed = self.search([("active", "=", True)], limit=1) first_installed = self.search([("active", "=", True)], limit=1)
if not lang: if not lang:
lang = ( lang = (
# Object's language, if called like # Object's language, if called like
# ``record.lang.datetime_formatter(datetime_obj)`` # ``record.lang.datetime_formatter(datetime_obj)``
(self.ids and self[0].code) (self.ids and self[0].code)
or or self.env.context.get("lang")
# Context language or self.env.user.lang
self.env.context.get("lang") or first_installed.code
or
# User's language
self.env.user.lang
or
# First installed language found
first_installed.code
) )
# Get DB lang record # Get DB lang record
record = self.search([("code", "=", lang)]) record = self.search([("code", "=", lang)])
try: try:
record.ensure_one() record.ensure_one()
except ValueError: except ValueError:
@ -59,7 +47,6 @@ class ResLang(models.Model):
raise UserError(_("Best matched language (%s) not found.") % lang) raise UserError(_("Best matched language (%s) not found.") % lang)
else: else:
record = first_installed record = first_installed
return record return record
@api.model @api.model
@ -67,30 +54,24 @@ class ResLang(models.Model):
self, value, lang=None, template=MODE_DATETIME, separator=" ", failure_safe=True self, value, lang=None, template=MODE_DATETIME, separator=" ", failure_safe=True
): ):
"""Convert a datetime field to lang's default format. """Convert a datetime field to lang's default format.
:type value: `str`, `float` or `datetime.datetime` :type value: `str`, `float` or `datetime.datetime`
:param value: :param value:
Datetime that will be formatted to the user's preferred format. Datetime that will be formatted to the user's preferred format.
:param str lang: :param str lang:
See :param:`lang` from :meth:`~.best_match`. See :param:`lang` from :meth:`~.best_match`.
:param bool failure_safe: :param bool failure_safe:
See :param:`failure_safe` from :meth:`~.best_match`. See :param:`failure_safe` from :meth:`~.best_match`.
:param str template: :param str template:
Will be used to format :param:`value`. If it is one of the special Will be used to format :param:`value`. If it is one of the special
constants :const:`MODE_DATETIME`, :const:`MODE_DATE` or constants :const:`MODE_DATETIME`, :const:`MODE_DATE` or
:const:`MODE_TIME`, it will use the :param:`lang`'s default :const:`MODE_TIME`, it will use the :param:`lang`'s default
template for that mode. template for that mode.
:param str separator: :param str separator:
Only used when :param:`template` is :const:`MODE_DATETIME`, as the Only used when :param:`template` is :const:`MODE_DATETIME`, as the
separator between the date and time parts. separator between the date and time parts.
""" """
# Get the correct lang # Get the correct lang
lang = self.best_match(lang) lang = self.best_match(lang)
# Get the template # Get the template
if template in {MODE_DATETIME, MODE_DATE, MODE_TIME}: if template in {MODE_DATETIME, MODE_DATE, MODE_TIME}:
defaults = [] defaults = []
@ -99,7 +80,6 @@ class ResLang(models.Model):
if "TIME" in template: if "TIME" in template:
defaults.append(lang.time_format or DEFAULT_SERVER_TIME_FORMAT) defaults.append(lang.time_format or DEFAULT_SERVER_TIME_FORMAT)
template = separator.join(defaults) template = separator.join(defaults)
# Convert str to datetime objects # Convert str to datetime objects
if isinstance(value, str): if isinstance(value, str):
try: try:
@ -107,14 +87,11 @@ class ResLang(models.Model):
except ValueError: except ValueError:
# Probably failed due to value being only time # Probably failed due to value being only time
value = datetime.strptime(value, DEFAULT_SERVER_TIME_FORMAT) value = datetime.strptime(value, DEFAULT_SERVER_TIME_FORMAT)
# Time-only fields are floats for Odoo # Time-only fields are floats for Odoo
elif isinstance(value, float): elif isinstance(value, float):
# Patch values >= 24 hours # Patch values >= 24 hours
if value >= 24: if value >= 24:
template = template.replace("%H", "%d" % value) template = template.replace("%H", "%d" % value)
# Convert to time # Convert to time
value = (datetime.min + timedelta(hours=value)).time() value = (datetime.min + timedelta(hours=value)).time()
return value.strftime(template) return value.strftime(template)

View File

@ -3,3 +3,4 @@
* Jairo Llopis * Jairo Llopis
* Vicent Cubells * Vicent Cubells
* Ernesto Tejeda * Ernesto Tejeda
* Víctor Martínez

View File

@ -8,42 +8,49 @@ from odoo.tests.common import TransactionCase
class BasicCase(TransactionCase): class BasicCase(TransactionCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.langs = ("en_US", "es_ES", "it_IT", "pt_PT", "zh_CN") self.langs = (
self.env.ref("base.lang_en"),
self.env.ref("base.lang_es"),
self.env.ref("base.lang_it"),
self.env.ref("base.lang_pt"),
self.env.ref("base.lang_zh_CN"),
)
self.rl = self.env["res.lang"] self.rl = self.env["res.lang"]
for lang in self.langs: for lang in self.langs:
if not self.rl.search([("code", "=", lang)]): self.rl.load_lang(lang.code)
self.rl.load_lang(lang)
def test_explicit(self): def test_explicit(self):
"""When an explicit lang is used.""" """When an explicit lang is used."""
for lang in self.langs: for lang in self.langs:
self.assertEqual(self.rl.best_match(lang).code, lang) self.assertEqual(self.rl.best_match(lang.code).code, lang.code)
def test_record(self): def test_record(self):
"""When called from a ``res.lang`` record.""" """When called from a ``res.lang`` record."""
rl = self.rl.with_context(lang="it_IT") rl = self.rl.with_context(lang="it_IT")
rl.env.user.lang = "pt_PT" rl.env.user.lang = "pt_PT"
for lang in self.langs: for lang in self.langs:
self.assertEqual(rl.search([("code", "=", lang)]).best_match().code, lang) self.assertEqual(
rl.search([("code", "=", lang.code)]).best_match().code, lang.code
)
def test_context(self): def test_context(self):
"""When called with a lang in context.""" """When called with a lang in context."""
self.env.user.lang = "pt_PT" self.env.user.lang = "pt_PT"
for lang in self.langs: for lang in self.langs:
self.assertEqual(self.rl.with_context(lang=lang).best_match().code, lang) self.assertEqual(
self.rl.with_context(lang=lang.code).best_match().code, lang.code
)
def test_user(self): def test_user(self):
"""When lang not specified in context.""" """When lang not specified in context."""
for lang in self.langs: for lang in self.langs:
self.env.user.lang = lang self.env.user.lang = lang.code
# Lang is False in context # Lang is False in context
self.assertEqual(self.rl.with_context(lang=False).best_match().code, lang) self.assertEqual(
self.rl.with_context(lang=False).best_match().code, lang.code
)
# Lang not found in context # Lang not found in context
self.assertEqual(self.rl.with_context(dict()).best_match().code, lang) self.assertEqual(self.rl.with_context(dict()).best_match().code, lang.code)
def test_first_installed(self): def test_first_installed(self):
"""When falling back to first installed language.""" """When falling back to first installed language."""
@ -56,10 +63,8 @@ class BasicCase(TransactionCase):
self.env.user.lang = False self.env.user.lang = False
self.rl = self.rl.with_context(lang=False) self.rl = self.rl.with_context(lang=False)
first = self.rl.search([("active", "=", True)], limit=1) first = self.rl.search([("active", "=", True)], limit=1)
# Safe mode # Safe mode
self.assertEqual(self.rl.best_match("fake_LANG").code, first.code) self.assertEqual(self.rl.best_match("fake_LANG").code, first.code)
# Unsafe mode # Unsafe mode
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.rl.best_match("fake_LANG", failure_safe=False) self.rl.best_match("fake_LANG", failure_safe=False)

View File

@ -27,12 +27,10 @@ class FormatterCase(TransactionCase):
def tearDown(self): def tearDown(self):
# This should be returned # This should be returned
self.expected = self.dt.strftime(self.format) self.expected = self.dt.strftime(self.format)
# Pass a datetime object # Pass a datetime object
self.assertEqual( self.assertEqual(
self.expected, self.rl.datetime_formatter(self.dt, **self.kwargs) self.expected, self.rl.datetime_formatter(self.dt, **self.kwargs)
) )
# When the date comes as a string # When the date comes as a string
if isinstance(self.dt, datetime.datetime): if isinstance(self.dt, datetime.datetime):
self.dt_str = self.dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) self.dt_str = self.dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
@ -40,17 +38,14 @@ class FormatterCase(TransactionCase):
self.dt_str = self.dt.strftime(DEFAULT_SERVER_DATE_FORMAT) self.dt_str = self.dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
elif isinstance(self.dt, datetime.time): elif isinstance(self.dt, datetime.time):
self.dt_str = self.dt.strftime(DEFAULT_SERVER_TIME_FORMAT) self.dt_str = self.dt.strftime(DEFAULT_SERVER_TIME_FORMAT)
# Pass a string # Pass a string
self.assertEqual( self.assertEqual(
self.expected, self.rl.datetime_formatter(self.dt_str, **self.kwargs) self.expected, self.rl.datetime_formatter(self.dt_str, **self.kwargs)
) )
# Pass a unicode # Pass a unicode
self.assertEqual( self.assertEqual(
self.expected, self.rl.datetime_formatter(str(self.dt_str), **self.kwargs) self.expected, self.rl.datetime_formatter(str(self.dt_str), **self.kwargs)
) )
super().tearDown() super().tearDown()
def test_datetime(self): def test_datetime(self):
@ -69,14 +64,11 @@ class FormatterCase(TransactionCase):
self.format = self.t_fmt self.format = self.t_fmt
self.kwargs = {"template": MODE_TIME} self.kwargs = {"template": MODE_TIME}
self.dt = self.dt.time() self.dt = self.dt.time()
# Test float times # Test float times
for n in range(50): for n in range(50):
n = n + random() n = n + random()
# Patch values with >= 24 hours # Patch values with >= 24 hours
fmt = self.format.replace("%H", "%02d" % n) fmt = self.format.replace("%H", "%02d" % n)
time = (datetime.datetime.min + datetime.timedelta(hours=n)).time() time = (datetime.datetime.min + datetime.timedelta(hours=n)).time()
self.assertEqual( self.assertEqual(
time.strftime(fmt), self.rl.datetime_formatter(n, **self.kwargs) time.strftime(fmt), self.rl.datetime_formatter(n, **self.kwargs)