[ADD] duration widget

bzr revid: xmo@openerp.com-20131128153630-pouwmygw6vgyuj1l
This commit is contained in:
Xavier Morel
2013-11-28 16:36:30 +01:00
parent 071ef9702b
commit ea56dece85
2 changed files with 104 additions and 14 deletions
+56 -5
View File
@@ -1,19 +1,22 @@
# -*- coding: utf-8 -*-
import cStringIO
import json
import datetime
import dateutil.relativedelta
import json
import logging
import math
import re
import urllib
import xml # FIXME use lxml and etree
import babel
import babel.dates
import dateutil.relativedelta
import Image
import math
import werkzeug.utils
from openerp.osv import osv, orm, fields
import openerp.tools
from openerp.osv import osv, orm, fields
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@@ -458,7 +461,7 @@ class QWeb(orm.AbstractModel):
""" eg: <span t-record="browse_record(res.partner, 1)" t-field="phone">+1 555 555 8069</span>"""
node_name = e.nodeName
assert node_name not in ("table", "tbody", "thead", "tfoot", "tr", "td",
"ol", "ul", "ol", "dl", "dt", "dd"),\
"li", "ul", "ol", "dl", "dt", "dd"),\
"RTE widgets do not work correctly on %r elements" % node_name
assert node_name != 't',\
"t-field can not be used on a t element, provide an actual HTML node"
@@ -776,6 +779,54 @@ class MonetaryConverter(osv.AbstractModel):
return self.qweb_object().eval_object(
options['display_currency'], options['_qweb_context'])
TIMEDELTA_UNITS = (
('year', 3600 * 24 * 365),
('month', 3600 * 24 * 30),
('week', 3600 * 24 * 7),
('day', 3600 * 24),
('hour', 3600),
('minute', 60),
('second', 1)
)
class DurationConverter(osv.AbstractModel):
""" ``duration`` converter, to display integral or fractional values as
human-readable time spans (e.g. 1.5 as "1 hour 30 minutes").
Can be used on any numerical field.
Has a mandatory option ``unit`` which can be one of ``second``, ``minute``,
``hour``, ``day``, ``week`` or ``year``, used to interpret the numerical
field value before converting it.
Sub-second values will be ignored.
"""
_name = 'ir.qweb.field.duration'
_inherit = 'ir.qweb.field'
def value_to_html(self, cr, uid, value, column, options=None, context=None):
units = dict(TIMEDELTA_UNITS)
if value < 0:
raise ValueError(_("Durations can't be negative"))
if not options or options.get('unit') not in units:
raise ValueError(_("A unit must be provided to duration widgets"))
locale = babel.Locale.parse(
self.user_lang(cr, uid, context=context).code)
print locale
factor = units[options['unit']]
sections = []
r = value * factor
for unit, secs_per_unit in TIMEDELTA_UNITS:
v, r = divmod(r, secs_per_unit)
if not v: continue
section = babel.dates.format_timedelta(
v*secs_per_unit, threshold=1, locale=locale)
if section:
sections.append(section)
return u' '.join(sections)
def get_field_type(column, options):
""" Gets a t-field's effective type from the field's column and its options
"""
@@ -22,15 +22,21 @@ class TestExport(common.TransactionCase):
def get_column(self, name):
return self.Model._all_columns[name].column
def get_converter(self, name):
def get_converter(self, name, type=None):
column = self.get_column(name)
try:
model = self.registry('ir.qweb.field.' + column._type)
except KeyError:
model = self.registry('ir.qweb.field')
return lambda value: model.value_to_html(
self.cr, self.uid, value, column)
for postfix in type, column._type, '':
fs = ['ir', 'qweb', 'field']
if postfix is None: continue
if postfix: fs.append(postfix)
try:
model = self.registry('.'.join(fs))
break
except KeyError: pass
return lambda value, options=None, context=None: model.value_to_html(
self.cr, self.uid, value, column, options=options, context=context)
class TestBasicExport(TestExport):
_model = 'test_converter.test_model'
@@ -297,5 +303,38 @@ class TestDatetimeExport(TestBasicExport):
# default lang/format is US
self.assertEqual(value, '05/03/2011 00:12:13')
# o2m, m2m?
# reference?
class TestDurationExport(TestBasicExport):
def setUp(self):
super(TestDurationExport, self).setUp()
self.registry('res.lang').load_lang(self.cr, self.uid, 'fr_FR')
def test_negative(self):
converter = self.get_converter('float', 'duration')
with self.assertRaises(ValueError):
converter(-4)
def test_missing_unit(self):
converter = self.get_converter('float', 'duration')
with self.assertRaises(ValueError):
converter(4)
def test_basic(self):
converter = self.get_converter('float', 'duration')
result = converter(4, {'unit': 'hour'}, {'lang': 'fr_FR'})
self.assertEqual(result, u'4 heures')
result = converter(50, {'unit': 'second'}, {'lang': 'fr_FR'})
self.assertEqual(result, u'50 secondes')
def test_multiple(self):
converter = self.get_converter('float', 'duration')
result = converter(1.5, {'unit': 'hour'}, {'lang': 'fr_FR'})
self.assertEqual(result, u"1 heure 30 minutes")
result = converter(72, {'unit': 'second'}, {'lang': 'fr_FR'})
self.assertEqual(result, u"1 minute 12 secondes")