diff --git a/CHANGELOG b/CHANGELOG index c467730688..dac0c39d78 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add sortable_values in tools * Remove default colors on graph and calendar * Add model, record and records attributes on Wizard * Check read access of wizard records diff --git a/trytond/tests/test_tools.py b/trytond/tests/test_tools.py index 3bbf584873..624722bf43 100644 --- a/trytond/tests/test_tools.py +++ b/trytond/tests/test_tools.py @@ -12,7 +12,7 @@ import sql.operators from trytond.tools import ( reduce_ids, reduce_domain, decimal_, is_instance_method, file_open, - strip_wildcard, lstrip_wildcard, rstrip_wildcard, slugify) + strip_wildcard, lstrip_wildcard, rstrip_wildcard, slugify, sortable_values) from trytond.tools.string_ import StringPartitioned, LazyString from trytond.tools.domain_inversion import ( domain_inversion, parse, simplify, merge, concat, unique_value, @@ -732,6 +732,25 @@ class DomainInversionTestCase(unittest.TestCase): extract_reference_models(domain, 'x'), {'model_A', 'model_B'}) self.assertEqual(extract_reference_models(domain, 'y'), set()) + def test_sortable_values(self): + def key(values): + return values + + values = [ + (('a', 1), ('b', None)), + (('a', 1), ('b', 3)), + (('a', 1), ('b', 2)), + ] + + with self.assertRaises(TypeError): + sorted(values, key=key) + self.assertEqual( + sorted(values, key=sortable_values(key)), [ + (('a', 1), ('b', 2)), + (('a', 1), ('b', 3)), + (('a', 1), ('b', None)), + ]) + def suite(): func = unittest.TestLoader().loadTestsFromTestCase diff --git a/trytond/tools/__init__.py b/trytond/tools/__init__.py index c7cd75443e..b29edcfb82 100644 --- a/trytond/tools/__init__.py +++ b/trytond/tools/__init__.py @@ -8,13 +8,14 @@ except ImportError: from .misc import ( file_open, get_smtp_server, reduce_ids, reduce_domain, grouped_slice, is_instance_method, resolve, strip_wildcard, - lstrip_wildcard, rstrip_wildcard, slugify) + lstrip_wildcard, rstrip_wildcard, slugify, sortable_values) from .decimal_ import decistmt __all__ = ['file_open', 'get_smtp_server', 'reduce_ids', 'reduce_domain', 'grouped_slice', 'is_instance_method', 'resolve', 'strip_wildcard', 'lstrip_wildcard', 'rstrip_wildcard', 'slugify', - 'decistmt', 'ClassProperty', 'cursor_dict', 'cached_property'] + 'decistmt', 'ClassProperty', 'cursor_dict', 'cached_property', + 'sortable_values'] class ClassProperty(property): diff --git a/trytond/tools/misc.py b/trytond/tools/misc.py index f14b46551e..a306cd0bf2 100644 --- a/trytond/tools/misc.py +++ b/trytond/tools/misc.py @@ -13,6 +13,7 @@ import types import unicodedata import warnings from array import array +from functools import wraps from itertools import islice from sql import Literal @@ -223,3 +224,14 @@ def slugify(value, hyphenate='-'): value = unicodedata.normalize('NFKD', value) value = str(_slugify_strip_re.sub('', value).strip()) return _slugify_hyphenate_re.sub(hyphenate, value) + + +def sortable_values(func): + "Decorator that makes list of couple values sortable" + @wraps(func) + def wrapper(*args, **kwargs): + result = list(func(*args, **kwargs)) + for i, (name, value) in enumerate(list(result)): + result[i] = (name, value is None, value) + return result + return wrapper