Browse Source

Merge pull request #605 from Tecnativa/9.0-mig-datetime_formatter

9.0 mig datetime formatter
pull/560/head
Pedro M. Baeza 8 years ago
committed by GitHub
parent
commit
78688805f0
  1. 72
      datetime_formatter/README.rst
  2. 5
      datetime_formatter/__init__.py
  3. 20
      datetime_formatter/__openerp__.py
  4. 12
      datetime_formatter/exceptions.py
  5. 30
      datetime_formatter/i18n/de.po
  6. 30
      datetime_formatter/i18n/es.po
  7. 30
      datetime_formatter/i18n/fr.po
  8. 30
      datetime_formatter/i18n/it.po
  9. 30
      datetime_formatter/i18n/sl.po
  10. 30
      datetime_formatter/i18n/tr.po
  11. 121
      datetime_formatter/models.py
  12. BIN
      datetime_formatter/static/description/icon.png
  13. 5
      datetime_formatter/tests/__init__.py
  14. 76
      datetime_formatter/tests/test_best_matcher.py
  15. 93
      datetime_formatter/tests/test_formatter.py

72
datetime_formatter/README.rst

@ -0,0 +1,72 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
=====================
Date & Time Formatter
=====================
This module was written to extend the functionality of Odoo language engine to
support formatting `Date`, `Time` and `Datetime` fields easily and allow you to
print them in the best format for the user.
Usage
=====
This module adds a technical programming feature, and it should be used by
addon developers, not by end users. This means that you must not expect to see
any changes if you are a user and install this, but if you find you have it
already installed, it's probably because you have another modules that depend
on this one.
If you are a developer, to use this module, you need to:
* Call anywhere in your code::
formatted_string = self.env["res.lang"].datetime_formatter(datetime_value)
* If you use Qweb::
<t t-esc="env['res.lang'].datetime_formatter(datetime_value)"/>
* If you call it from a record that has a `lang` field::
formatted_string = record.lang.datetime_formatter(record.datetime_field)
* ``models.ResLang.datetime_formatter`` docstring explains its usage.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/9.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Jairo Llopis <j.llopis@grupoesoc.es>
* Vicent Cubells <vicent.cubells@tecnativa.com>
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

5
datetime_formatter/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# © 2016 Tecnativa, S.L. - Vicent Cubells
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models

20
datetime_formatter/__openerp__.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# © 2016 Tecnativa, S.L. - Vicent Cubells
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Date & Time Formatter",
"summary": "Helper functions to give correct format to date[time] fields",
"version": "9.0.1.0.0",
"category": "Tools",
"website": "https://tecnativa.com",
"author": "Grupo ESOC Ingeniería de Servicios, "
"Tecnativa,"
"Odoo Community Association (OCA)",
"license": "AGPL-3",
"installable": True,
"depends": [
"base",
],
}

12
datetime_formatter/exceptions.py

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# © 2016 Tecnativa, S.L. - Vicent Cubells
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, exceptions
class BestMatchedLanguageNotFoundError(exceptions.MissingError):
def __init__(self, lang):
msg = (_("Best matched language (%s) not found.") % lang)
super(BestMatchedLanguageNotFoundError, self).__init__(msg)
self.lang = lang

30
datetime_formatter/i18n/de.po

@ -0,0 +1,30 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * datetime_formatter
#
# Translators:
# Rudolf Schnapka <rs@techno-flex.de>, 2016
msgid ""
msgstr ""
"Project-Id-Version: server-tools (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-01-19 14:44+0000\n"
"PO-Revision-Date: 2016-01-18 14:00+0000\n"
"Last-Translator: Rudolf Schnapka <rs@techno-flex.de>\n"
"Language-Team: German (http://www.transifex.com/oca/OCA-server-tools-8-0/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: datetime_formatter
#: code:addons/datetime_formatter/exceptions.py:9
#, python-format
msgid "Best matched language (%s) not found."
msgstr "Sprache (%s) mit bester Übereinstimmung nicht gefunden"
#. module: datetime_formatter
#: model:ir.model,name:datetime_formatter.model_res_lang
msgid "Languages"
msgstr "Sprachen"

30
datetime_formatter/i18n/es.po

@ -0,0 +1,30 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * datetime_formatter
#
# Translators:
# Antonio Trueba, 2016
msgid ""
msgstr ""
"Project-Id-Version: server-tools (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-01-19 14:44+0000\n"
"PO-Revision-Date: 2016-02-10 16:46+0000\n"
"Last-Translator: Antonio Trueba\n"
"Language-Team: Spanish (http://www.transifex.com/oca/OCA-server-tools-8-0/language/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: datetime_formatter
#: code:addons/datetime_formatter/exceptions.py:9
#, python-format
msgid "Best matched language (%s) not found."
msgstr ""
#. module: datetime_formatter
#: model:ir.model,name:datetime_formatter.model_res_lang
msgid "Languages"
msgstr "Idiomas"

30
datetime_formatter/i18n/fr.po

@ -0,0 +1,30 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * datetime_formatter
#
# Translators:
# Christophe CHAUVET <christophe.chauvet@gmail.com>, 2016
msgid ""
msgstr ""
"Project-Id-Version: server-tools (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-08-21 06:45+0000\n"
"PO-Revision-Date: 2016-08-21 07:41+0000\n"
"Last-Translator: Christophe CHAUVET <christophe.chauvet@gmail.com>\n"
"Language-Team: French (http://www.transifex.com/oca/OCA-server-tools-8-0/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: datetime_formatter
#: code:addons/datetime_formatter/exceptions.py:9
#, python-format
msgid "Best matched language (%s) not found."
msgstr ""
#. module: datetime_formatter
#: model:ir.model,name:datetime_formatter.model_res_lang
msgid "Languages"
msgstr "Langues"

30
datetime_formatter/i18n/it.po

@ -0,0 +1,30 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * datetime_formatter
#
# Translators:
# Paolo Valier, 2016
msgid ""
msgstr ""
"Project-Id-Version: server-tools (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-03-17 15:37+0000\n"
"PO-Revision-Date: 2016-03-13 09:34+0000\n"
"Last-Translator: Paolo Valier\n"
"Language-Team: Italian (http://www.transifex.com/oca/OCA-server-tools-8-0/language/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: datetime_formatter
#: code:addons/datetime_formatter/exceptions.py:9
#, python-format
msgid "Best matched language (%s) not found."
msgstr ""
#. module: datetime_formatter
#: model:ir.model,name:datetime_formatter.model_res_lang
msgid "Languages"
msgstr "Lingue"

30
datetime_formatter/i18n/sl.po

@ -0,0 +1,30 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * datetime_formatter
#
# Translators:
# Matjaž Mozetič <m.mozetic@matmoz.si>, 2015
msgid ""
msgstr ""
"Project-Id-Version: server-tools (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-27 14:01+0000\n"
"PO-Revision-Date: 2015-11-27 04:11+0000\n"
"Last-Translator: Matjaž Mozetič <m.mozetic@matmoz.si>\n"
"Language-Team: Slovenian (http://www.transifex.com/oca/OCA-server-tools-8-0/language/sl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sl\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
#. module: datetime_formatter
#: code:addons/datetime_formatter/exceptions.py:9
#, python-format
msgid "Best matched language (%s) not found."
msgstr "Najbolj ujemajoč jezik (%s) ni najden."
#. module: datetime_formatter
#: model:ir.model,name:datetime_formatter.model_res_lang
msgid "Languages"
msgstr "Jeziki"

30
datetime_formatter/i18n/tr.po

@ -0,0 +1,30 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * datetime_formatter
#
# Translators:
# Ahmet Altınışık <aaltinisik@altinkaya.com.tr>, 2016
msgid ""
msgstr ""
"Project-Id-Version: server-tools (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-01-19 14:44+0000\n"
"PO-Revision-Date: 2016-01-31 12:31+0000\n"
"Last-Translator: Ahmet Altınışık <aaltinisik@altinkaya.com.tr>\n"
"Language-Team: Turkish (http://www.transifex.com/oca/OCA-server-tools-8-0/language/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: tr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: datetime_formatter
#: code:addons/datetime_formatter/exceptions.py:9
#, python-format
msgid "Best matched language (%s) not found."
msgstr "En iyi eşleşen dil (%s) bulunamadı."
#. module: datetime_formatter
#: model:ir.model,name:datetime_formatter.model_res_lang
msgid "Languages"
msgstr "Diller"

121
datetime_formatter/models.py

@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# © 2016 Tecnativa, S.L. - Vicent Cubells
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from datetime import datetime, timedelta
from openerp import api, fields, models
from openerp.tools import (DEFAULT_SERVER_DATE_FORMAT,
DEFAULT_SERVER_TIME_FORMAT)
from . import exceptions as ex
# Available modes for :param:`.ResLang.datetime_formatter.template`
MODE_DATETIME = "MODE_DATETIME"
MODE_DATE = "MODE_DATE"
MODE_TIME = "MODE_TIME"
class ResLang(models.Model):
_inherit = "res.lang"
@api.model
@api.returns('self')
def best_match(self, lang=None, failure_safe=True):
"""Get best match of current default lang.
:param str lang:
If a language in the form of "en_US" is supplied, it will have the
highest priority.
:param bool failure_safe:
If ``False`` and the best matched language is not found installed,
an exception will be raised. Otherwise, the first installed
language found in the DB will be returned.
"""
# Find some installed language, as fallback
first_installed = self.search([("active", "=", True)], limit=1)
if not lang:
lang = (
# Object's language, if called like
# ``record.lang.datetime_formatter(datetime_obj)``
(self.ids and self[0].code) or
# Context language
self.env.context.get("lang") or
# User's language
self.env.user.lang or
# First installed language found
first_installed.code)
# Get DB lang record
record = self.search([("code", "=", lang)])
try:
record.ensure_one()
except ValueError:
if not failure_safe:
raise ex.BestMatchedLanguageNotFoundError(lang)
else:
record = first_installed
return record
@api.model
def datetime_formatter(self, value, lang=None, template=MODE_DATETIME,
separator=" ", failure_safe=True):
"""Convert a datetime field to lang's default format.
:type value: `str`, `float` or `datetime.datetime`
:param value:
Datetime that will be formatted to the user's preferred format.
:param str lang:
See :param:`lang` from :meth:`~.best_match`.
:param bool failure_safe:
See :param:`failure_safe` from :meth:`~.best_match`.
:param str template:
Will be used to format :param:`value`. If it is one of the special
constants :const:`MODE_DATETIME`, :const:`MODE_DATE` or
:const:`MODE_TIME`, it will use the :param:`lang`'s default
template for that mode.
:param str separator:
Only used when :param:`template` is :const:`MODE_DATETIME`, as the
separator between the date and time parts.
"""
# Get the correct lang
lang = self.best_match(lang)
# Get the template
if template in {MODE_DATETIME, MODE_DATE, MODE_TIME}:
defaults = []
if "DATE" in template:
defaults.append(lang.date_format or
DEFAULT_SERVER_DATE_FORMAT)
if "TIME" in template:
defaults.append(lang.time_format or
DEFAULT_SERVER_TIME_FORMAT)
template = separator.join(defaults)
# Convert str to datetime objects
if isinstance(value, (str, unicode)):
try:
value = fields.Datetime.from_string(value)
except ValueError:
# Probably failed due to value being only time
value = datetime.strptime(value, DEFAULT_SERVER_TIME_FORMAT)
# Time-only fields are floats for Odoo
elif isinstance(value, float):
# Patch values >= 24 hours
if value >= 24:
template = template.replace("%H", "%d" % value)
# Convert to time
value = (datetime.min + timedelta(hours=value)).time()
return value.strftime(template)

BIN
datetime_formatter/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

5
datetime_formatter/tests/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# © 2016 Tecnativa, S.L. - Vicent Cubells
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_best_matcher, test_formatter

76
datetime_formatter/tests/test_best_matcher.py

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# © 2016 Tecnativa, S.L. - Vicent Cubells
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase
from .. import exceptions
class BasicCase(TransactionCase):
def setUp(self):
super(BasicCase, self).setUp()
self.langs = ("en_US", "es_ES", "it_IT", "pt_PT", "zh_CN")
self.rl = self.env["res.lang"]
for lang in self.langs:
if not self.rl.search([("code", "=", lang)]):
self.rl.load_lang(lang)
def test_explicit(self):
"""When an explicit lang is used."""
for lang in self.langs:
self.assertEqual(self.rl.best_match(lang).code, lang)
def test_record(self):
"""When called from a ``res.lang`` record."""
rl = self.rl.with_context(lang="it_IT")
rl.env.user.lang = "pt_PT"
for lang in self.langs:
self.assertEqual(
rl.search([("code", "=", lang)]).best_match().code,
lang)
def test_context(self):
"""When called with a lang in context."""
self.env.user.lang = "pt_PT"
for lang in self.langs:
self.assertEqual(
self.rl.with_context(lang=lang).best_match().code,
lang)
def test_user(self):
"""When lang not specified in context."""
for lang in self.langs:
self.env.user.lang = lang
# Lang is False in context
self.assertEqual(
self.rl.with_context(lang=False).best_match().code,
lang)
# Lang not found in context
self.assertEqual(
self.rl.with_context(dict()).best_match().code,
lang)
def test_first_installed(self):
"""When falling back to first installed language."""
first = self.rl.search([("active", "=", True)], limit=1)
self.env.user.lang = False
self.assertEqual(
self.rl.with_context(lang=False).best_match().code,
first.code)
def test_unavailable(self):
"""When matches to an unavailable language."""
self.env.user.lang = False
self.rl = self.rl.with_context(lang=False)
first = self.rl.search([("active", "=", True)], limit=1)
# Safe mode
self.assertEqual(self.rl.best_match("fake_LANG").code, first.code)
# Unsafe mode
with self.assertRaises(exceptions.BestMatchedLanguageNotFoundError):
self.rl.best_match("fake_LANG", failure_safe=False)

93
datetime_formatter/tests/test_formatter.py

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
# © 2016 Tecnativa, S.L. - Vicent Cubells
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import datetime
from random import random
from openerp.tests.common import TransactionCase
from openerp.tools import (DEFAULT_SERVER_DATE_FORMAT,
DEFAULT_SERVER_TIME_FORMAT,
DEFAULT_SERVER_DATETIME_FORMAT)
from ..models import MODE_DATE, MODE_TIME, MODE_DATETIME
class FormatterCase(TransactionCase):
def setUp(self):
super(FormatterCase, self).setUp()
self.rl = self.env["res.lang"]
self.bm = self.rl.best_match()
self.dt = datetime.datetime.now()
self.d_fmt = self.bm.date_format or DEFAULT_SERVER_DATE_FORMAT
self.t_fmt = self.bm.time_format or DEFAULT_SERVER_TIME_FORMAT
self.kwargs = dict()
def tearDown(self):
# This should be returned
self.expected = self.dt.strftime(self.format)
# Pass a datetime object
self.assertEqual(
self.expected,
self.rl.datetime_formatter(
self.dt,
**self.kwargs))
# When the date comes as a string
if isinstance(self.dt, datetime.datetime):
self.dt_str = self.dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
elif isinstance(self.dt, datetime.date):
self.dt_str = self.dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
elif isinstance(self.dt, datetime.time):
self.dt_str = self.dt.strftime(DEFAULT_SERVER_TIME_FORMAT)
# Pass a string
self.assertEqual(
self.expected,
self.rl.datetime_formatter(
self.dt_str,
**self.kwargs))
# Pass a unicode
self.assertEqual(
self.expected,
self.rl.datetime_formatter(
unicode(self.dt_str),
**self.kwargs))
super(FormatterCase, self).tearDown()
def test_datetime(self):
"""Format a datetime."""
self.format = "%s %s" % (self.d_fmt, self.t_fmt)
self.kwargs = {"template": MODE_DATETIME}
def test_date(self):
"""Format a date."""
self.format = self.d_fmt
self.kwargs = {"template": MODE_DATE}
self.dt = self.dt.date()
def test_time(self):
"""Format times, including float ones."""
self.format = self.t_fmt
self.kwargs = {"template": MODE_TIME}
self.dt = self.dt.time()
# Test float times
for n in range(50):
n = n + random()
# Patch values with >= 24 hours
fmt = self.format.replace("%H", "%02d" % n)
time = (datetime.datetime.min +
datetime.timedelta(hours=n)).time()
self.assertEqual(
time.strftime(fmt),
self.rl.datetime_formatter(n, **self.kwargs))
def test_custom_separator(self):
"""Format a datetime with a custom separator."""
sep = "T"
self.format = "%s%s%s" % (self.d_fmt, sep, self.t_fmt)
self.kwargs = {"template": MODE_DATETIME, "separator": sep}
Loading…
Cancel
Save