Add help for each value to selection and multiselection

issue9649
review320021002
This commit is contained in:
Cédric Krier
2021-02-14 14:28:55 +01:00
parent 0a7268f6ef
commit 964fc0a74b
12 changed files with 113 additions and 25 deletions
+1
View File
@@ -1,3 +1,4 @@
* Add help for each value to selection and multiselection
* Use safe_join in SharedDataMiddlewareIndex (issue10068)
* Use menu name for window opened from menu
* Add trigonometric functions to sqlite backend
+15 -3
View File
@@ -446,7 +446,7 @@ A binary field. It will be represented in Python by a ``bytes`` instance.
Selection
---------
.. class:: Selection(selection, string[, sort[, selection_change_with[, translate[, \**options]]]])
.. class:: Selection(selection, string[, sort[, selection_change_with[, translate[, help_selection[, \**options]]]]])
A string field with limited values to choose from.
@@ -494,6 +494,10 @@ A string field with limited values to choose from.
If true, the human-readable values will be translated. Default value is
``True``.
.. attribute:: Selection.help_selection
A dictionary mapping the selection value with its help string.
Instance methods:
.. method:: Selection.translated([name])
@@ -506,7 +510,7 @@ Instance methods:
MultiSelection
--------------
.. class:: MultiSelection(selection, string[, sort[, translate[, \**options]]])
.. class:: MultiSelection(selection, string[, sort[, translate[, help_selection[, \**options]]]])
A list field with limited values to choose from.
@@ -526,6 +530,10 @@ A list field with limited values to choose from.
Same as :attr:`Selection.translate_selection`
.. attribute:: MultiSelection.help_selection
Same as :attr:`Selection.help_selection`
Instance methods:
.. method:: MultiSelection.translated([name])
@@ -536,7 +544,7 @@ Instance methods:
Reference
---------
.. class:: Reference(string[, selection[, sort[, selection_change_with[, translated[,search_order[, search_context[, \**options]]]]]]])
.. class:: Reference(string[, selection[, sort[, selection_change_with[, translate[, help_selection[,search_order[, search_context[, \**options]]]]]]]])
A field that refers to a record of a model. It will be represented in Python by
a ``str`` instance like this::
@@ -563,6 +571,10 @@ But a ``tuple`` can be used to search or set value.
Same as :attr:`Selection.translate_selection`.
.. attribute:: Reference.help_selection
Same as :attr:`Selection.help_selection`.
.. attribute:: Reference.datetime_field
Same as :attr:`Many2One.datetime_field`
+9
View File
@@ -167,6 +167,11 @@ List of attributes shared by many form elements:
* ``symbol``: Only on numerical fields, the name of field which provides
the symbol to display.
.. _common-attributes-help_field:
* ``help_field``: The name of Dict field mapping the Selection value with
its help string.
form
^^^^
@@ -289,6 +294,8 @@ Display a field of the object with the value of the current record.
* ``symbol``: see in common-attributes-symbol_.
* ``help_field``: see in common-attributes-help_field_.
image
^^^^^
@@ -609,6 +616,8 @@ field
* ``symbol``: see in common-attributes-symbol_.
* ``help_field``: see in common-attributes-help_field_.
prefix or suffix
^^^^^^^^^^^^^^^^
+12
View File
@@ -108,9 +108,18 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_dict_schema_selection_sorted_help">
<field name="text">If the selection must be sorted on label.</field>
</record>
<record model="ir.message" id="msg_dict_schema_help_selection">
<field name="text">Help Selection</field>
</record>
<record model="ir.message" id="msg_dict_schema_help_selection_help">
<field name="text">The key followed by the help text separated by a ":", one per line.</field>
</record>
<record model="ir.message" id="msg_dict_schema_selection_json">
<field name="text">Selection JSON</field>
</record>
<record model="ir.message" id="msg_dict_schema_help_selection_json">
<field name="text">Help Selection JSON</field>
</record>
<record model="ir.message" id="msg_sequence">
<field name="text">Sequence</field>
</record>
@@ -199,6 +208,9 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_dict_schema_invalid_selection">
<field name="text">Invalid selection in schema "%(schema)s".</field>
</record>
<record model="ir.message" id="msg_dict_schema_invalid_help_selection">
<field name="text">Invalid help selection in schema "%(schema)s".</field>
</record>
<record model="ir.message" id="msg_recursion_error">
<field name="text">Recursion error: Record "%(rec_name)s" with parent "%(parent_rec_name)s" was configured as ancestor of itself.</field>
</record>
+1
View File
@@ -105,6 +105,7 @@ attlist.field &= [a:defaultValue = "1"] attribute completion { "0" | "1" }?
attlist.field &= attribute string { text }?
attlist.field &= [a:defaultValue = "1"] attribute factor { text }?
attlist.field &= attribute filename { text }?
attlist.field &= attribute help_field { text }?
attlist.field &=
[a:defaultValue = "0"] attribute toolbar { "0" | "1" }?
attlist.field &= attribute symbol { text }?
+8
View File
@@ -465,6 +465,14 @@
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">help_field</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute a:defaultValue="0">
+1
View File
@@ -63,6 +63,7 @@ attlist.field &= [a:defaultValue = "1"] attribute completion { "0" | "1" }?
attlist.field &= attribute string { text }?
attlist.field &= [a:defaultValue = "1"] attribute factor { text }?
attlist.field &= attribute filename { text }?
attlist.field &= attribute help_field { text }?
attlist.field &= attribute view_ids { text }?
attlist.field &= attribute symbol { text }?
prefix = element prefix { attlist.affix, empty }
+8
View File
@@ -248,6 +248,14 @@
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">help_field</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
+26 -8
View File
@@ -63,6 +63,13 @@ class DictSchemaMixin(object):
'invisible': ~Eval('type_').in_(['selection', 'multiselection']),
}, depends=['type_'],
help=lazy_gettext('ir.msg_dict_schema_selection_sorted_help'))
help_selection = fields.Text(
lazy_gettext('ir.msg_dict_schema_help_selection'), translate=True,
states={
'invisible': ~Eval('type_').in_(['selection', 'multiselection']),
},
depends=['type_'],
help=lazy_gettext('is.msg_dict_schema_help_selection_help'))
selection_json = fields.Function(fields.Char(
lazy_gettext('ir.msg_dict_schema_selection_json'),
states={
@@ -70,6 +77,13 @@ class DictSchemaMixin(object):
['selection', 'multiselection']),
},
depends=['type_']), 'get_selection_json')
help_selection_json = fields.Function(fields.Char(
lazy_gettext('ir.msg_dict_schema_help_selection_json'),
states={
'invisible': ~Eval('type_').in_(
['selection', 'multiselection']),
},
depends=['type_']), 'get_selection_json')
_relation_fields_cache = Cache('_dict_schema_mixin.get_relation_fields')
@classmethod
@@ -119,15 +133,17 @@ class DictSchemaMixin(object):
for schema in schemas:
if schema.type_ not in {'selection', 'multiselection'}:
continue
try:
dict(json.loads(schema.get_selection_json()))
except Exception:
raise SelectionError(
gettext('ir.msg_dict_schema_invalid_selection',
schema=schema.rec_name))
for name in ['selection', 'help_selection']:
try:
dict(json.loads(schema.get_selection_json(name + '_json')))
except Exception:
raise SelectionError(
gettext('ir.msg_dict_schema_invalid_%s' % name,
schema=schema.rec_name))
def get_selection_json(self, name=None):
db_selection = self.selection or ''
def get_selection_json(self, name):
field = name[:-len('_json')]
db_selection = getattr(self, field) or ''
selection = [[w.strip() for w in v.split(':', 1)]
for v in db_selection.splitlines() if v]
return json.dumps(selection, separators=(',', ':'))
@@ -154,6 +170,8 @@ class DictSchemaMixin(object):
english_key.selection_json))
selection.update(dict(json.loads(record.selection_json)))
new_key['selection'] = list(selection.items())
new_key['help_selection'] = dict(
json.loads(record.help_selection_json))
new_key['sort'] = record.selection_sorted
elif record.type_ in ('float', 'numeric'):
new_key['digits'] = (16, record.digits)
+4 -3
View File
@@ -21,9 +21,9 @@ class MultiSelection(SelectionMixin, Field):
_py_type = list
def __init__(self, selection, string='', sort=True, translate=True,
help='', required=False, readonly=False, domain=None, states=None,
select=False, on_change=None, on_change_with=None, depends=None,
context=None, loading='eager'):
help='', help_selection=None, required=False, readonly=False,
domain=None, states=None, select=False, on_change=None,
on_change_with=None, depends=None, context=None, loading='eager'):
"""
:param selection: A list or a function name that returns a list.
The list must be a list of tuples. First member is the value
@@ -41,6 +41,7 @@ class MultiSelection(SelectionMixin, Field):
self.selection_change_with = set()
self.sort = sort
self.translate_selection = translate
self.help_selection = help_selection
__init__.__doc__ += Field.__init__.__doc__
def set_rpc(self, model):
+5 -4
View File
@@ -24,10 +24,10 @@ class Reference(SelectionMixin, Field):
def __init__(self, string='', selection=None, sort=True,
selection_change_with=None, translate=True, search_order=None,
search_context=None, help='', required=False, readonly=False,
domain=None, states=None, select=False, on_change=None,
on_change_with=None, depends=None, context=None, loading='lazy',
datetime_field=None):
search_context=None, help='', help_selection=None, required=False,
readonly=False, domain=None, states=None, select=False,
on_change=None, on_change_with=None, depends=None, context=None,
loading='lazy', datetime_field=None):
'''
:param selection: A list or a function name that returns a list.
The list must be a list of tuples. First member is an internal name
@@ -57,6 +57,7 @@ class Reference(SelectionMixin, Field):
self.selection_change_with |= set(selection_change_with)
self.sort = sort
self.translate_selection = translate
self.help_selection = help_selection
self.__search_order = None
self.search_order = search_order
self.__search_context = None
+23 -7
View File
@@ -1,6 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import warnings
from itertools import chain
from sql.conditionals import Case
@@ -40,19 +41,33 @@ class SelectionMixin(Field):
selection = self.selection.copy()
else:
selection = self.selection
if self.help_selection:
help_selection = {}
for key, source in self.help_selection.items():
if not isinstance(source, LazyString):
source = Translation.get_source(
name, 'selection', language, source) or source
help_selection[key] = str(source)
else:
help_selection = None
definition['selection'] = selection
definition['selection_change_with'] = list(self.selection_change_with)
definition['sort'] = self.sort
definition['help_selection'] = help_selection
return definition
def definition_translations(self, model, language):
name = '%s,%s' % (model.__name__, self.name)
selection = []
sources = []
if not isinstance(self.selection, str) and self.translate_selection:
for key, source in self.selection:
if not isinstance(source, LazyString):
selection.append(
(name, 'selection', language, source))
sources.append(self.selection)
if self.help_selection:
sources.append(self.help_selection.items())
for key, source in chain(*sources):
if not isinstance(source, LazyString):
selection.append(
(name, 'selection', language, source))
return super().definition_translations(model, language) + selection
@@ -66,9 +81,9 @@ class Selection(SelectionMixin, Field):
def __init__(self, selection, string='', sort=True,
selection_change_with=None, translate=True, help='',
required=False, readonly=False, domain=None, states=None,
select=False, on_change=None, on_change_with=None, depends=None,
context=None, loading='eager'):
help_selection=None, required=False, readonly=False, domain=None,
states=None, select=False, on_change=None, on_change_with=None,
depends=None, context=None, loading='eager'):
'''
:param selection: A list or a function name that returns a list.
The list must be a list of tuples. First member is the value
@@ -91,6 +106,7 @@ class Selection(SelectionMixin, Field):
self.selection_change_with |= set(selection_change_with)
self.sort = sort
self.translate_selection = translate
self.help_selection = help_selection
__init__.__doc__ += Field.__init__.__doc__
def set_rpc(self, model):