Use server's locale in Integer and Float input widgets
authorThierry Florac <thierry.florac@onf.fr>
Thu, 25 Oct 2018 15:33:24 +0200
changeset 158 e46b0edbc87f
parent 157 0ab206c35370
child 159 4d8dc6cfe24f
Use server's locale in Integer and Float input widgets
src/pyams_form/interfaces/form.py
src/pyams_form/widget/__init__.py
--- a/src/pyams_form/interfaces/form.py	Tue Oct 23 12:08:33 2018 +0200
+++ b/src/pyams_form/interfaces/form.py	Thu Oct 25 15:33:24 2018 +0200
@@ -543,6 +543,18 @@
                         default=False)
 
 
+class IIntegerWidget(ITextWidget):
+    """Integer widget interface"""
+
+
+class IFloatWidget(ITextWidget):
+    """Float widget interface"""
+
+    precision = Int(title=_("Widget display precision"),
+                    description=_("Precision to use to display current value"),
+                    default=2)
+
+
 class IDateWidget(ITextWidget):
     """Date widget interface"""
 
--- a/src/pyams_form/widget/__init__.py	Tue Oct 23 12:08:33 2018 +0200
+++ b/src/pyams_form/widget/__init__.py	Thu Oct 25 15:33:24 2018 +0200
@@ -14,6 +14,7 @@
 
 import inspect
 import json
+import locale
 import os
 
 import venusian
@@ -26,7 +27,8 @@
 from z3c.form.browser.text import TextWidget
 from z3c.form.browser.textarea import TextAreaWidget
 from z3c.form.button import ButtonAction
-from z3c.form.converter import BaseDataConverter, DatetimeDataConverter as BaseDatetimeDataConverter
+from z3c.form.converter import BaseDataConverter, DatetimeDataConverter as BaseDatetimeDataConverter, \
+    FormatterValidationError
 from z3c.form.interfaces import DISPLAY_MODE, IButtonAction, IDataConverter, IFieldWidget, INPUT_MODE, ITextWidget, \
     IWidgetLayoutTemplate, NO_VALUE
 from z3c.form.widget import FieldWidget, WidgetLayoutFactory, WidgetTemplateFactory
@@ -37,7 +39,8 @@
 from zope.schema.interfaces import IChoice, IDate, IDatetime, IFloat, IInt, IList, ISet, ITime, ITuple
 
 from pyams_form.interfaces.form import IActionWidget, ICloseWidget, IColorWidget, IDateWidget, IDatetimeWidget, \
-    IFormLayer, IHTMLWidget, IResetWidget, ISEOTextLineWidget, ISelect2Widget, ITextLineListWidget, ITimeWidget
+    IFormLayer, IHTMLWidget, IResetWidget, ISEOTextLineWidget, ISelect2Widget, ITextLineListWidget, ITimeWidget, \
+    IIntegerWidget, IFloatWidget
 from pyams_form.schema import IActionButton, ICloseButton, IResetButton
 from pyams_skin.interfaces.tinymce import ITinyMCEConfiguration
 from pyams_utils.adapter import adapter_config
@@ -303,8 +306,28 @@
 # Integer field widget
 #
 
+@adapter_config(context=(IInt, IIntegerWidget), provides=IDataConverter)
+class IntegerFieldDataConverter(BaseDataConverter):
+    """Integer field data converter"""
+
+    error_message = _("Invalid integer value")
+
+    def toWidgetValue(self, value):
+        if value is self.field.missing_value:
+            return ''
+        return locale.format_string('%d', value, grouping=True)
+
+    def toFieldValue(self, value):
+        if not value:
+            return self.field.missing_value
+        try:
+            return locale.atoi(value)
+        except ValueError:
+            raise FormatterValidationError(self.errorMessage, value)
+
+
 @widgettemplate_config(mode=INPUT_MODE, template='templates/integer-input.pt', layer=IFormLayer)
-@implementer_only(ITextWidget)
+@implementer_only(IIntegerWidget)
 class IntegerWidget(TextWidget):
     """Integer widget"""
 
@@ -327,18 +350,39 @@
 # Float field widget
 #
 
+@adapter_config(context=(IFloat, IFloatWidget), provides=IDataConverter)
+class FloatFieldDataConverter(BaseDataConverter):
+    """Float field data converter"""
+
+    error_message = _("Invalid floating value")
+
+    def toWidgetValue(self, value):
+        if value is self.field.missing_value:
+            return ''
+        return locale.format_string('%.{0}f'.format(self.widget.precision), value, grouping=True)
+
+    def toFieldValue(self, value):
+        if not value:
+            return self.field.missing_value
+        try:
+            return locale.atof(value)
+        except ValueError:
+            raise FormatterValidationError(self.error_message, value)
+
+
 @widgettemplate_config(mode=INPUT_MODE, template='templates/float-input.pt', layer=IFormLayer)
-@implementer_only(ITextWidget)
+@implementer_only(IFloatWidget)
 class FloatWidget(TextWidget):
     """Float widget"""
 
+    precision = FieldProperty(IFloatWidget['precision'])
+
     @property
     def validate_rules(self):
-        rules = {'number': True}
-        if self.field.min is not None:
-            rules['min'] = self.field.min
-        if self.field.max is not None:
-            rules['max'] = self.field.max
+        conv = locale.localeconv()
+        rules = {'pattern': '\{}?[\d{}]+{}?\d*'.format(conv.get('negative_sign', '-'),
+                                                       conv.get('mon_thousands_sep', ''),
+                                                       conv.get('decimal_point', '\.'))}
         return json.dumps(rules)