[MIG] datetime_formatter: Migration to 13.0
parent
3e73fefc38
commit
8aed91993e
|
@ -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"],
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
* Jairo Llopis
|
* Jairo Llopis
|
||||||
* Vicent Cubells
|
* Vicent Cubells
|
||||||
* Ernesto Tejeda
|
* Ernesto Tejeda
|
||||||
|
* Víctor Martínez
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue