changeset 6832:234fefd7568a

issue2550559 - Pretty printing / formatting for Number types. add pretty(format='%0.3f') method to NumberHTMLProperty.
author John Rouillard <rouilj@ieee.org>
date Tue, 16 Aug 2022 22:39:04 -0400
parents e0b29e3fe995
children da9a78957bd4
files CHANGES.txt doc/customizing.txt roundup/cgi/templating.py test/test_templating.py
diffstat 4 files changed, 146 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Tue Aug 16 18:31:09 2022 -0400
+++ b/CHANGES.txt	Tue Aug 16 22:39:04 2022 -0400
@@ -37,6 +37,10 @@
   pip, setting UID tracker is run under. (John Rouillard)
 - issue2551140 - Added redis as a session and otk database for use
   with anydbm and sqlite primary databases. (John Rouillard)
+- issue2550559 - Pretty printing / formatting for Number types.
+  Added pretty(format='%0.3f') method to NumberHTMLProperty to
+  print numeric values. If value is None, return empty string
+  otherwise str() of value.
 
 2022-07-13 2.2.0
 
--- a/doc/customizing.txt	Tue Aug 16 18:31:09 2022 -0400
+++ b/doc/customizing.txt	Tue Aug 16 22:39:04 2022 -0400
@@ -3397,6 +3397,11 @@
             format (eg. "yesterday"). The format arguments are those used
             in the standard ``strftime`` call (see the `Python Library
             Reference: time module`__)
+
+            Number properties - takes a printf style format argument
+            (default: '%0.3f') and formats the number accordingly.
+            If the value can't be converted, '' is returned if the
+            value is ``None`` otherwise it is converted to a string.
 popcal      Generate a link to a popup calendar which may be used to
             edit the date field, for example::
 
--- a/roundup/cgi/templating.py	Tue Aug 16 18:31:09 2022 -0400
+++ b/roundup/cgi/templating.py	Tue Aug 16 22:39:04 2022 -0400
@@ -1957,6 +1957,21 @@
 
         return str(self._value)
 
+    def pretty(self, format="%0.3f"):
+        '''Pretty print number using printf format specifier.
+        
+           If value is not convertable, returns str(_value) or ""
+           if None.
+        '''
+        try:
+            return format%self._value
+        except TypeError:
+            value = self._value
+            if value is None:
+                return ''
+            else:
+                return str(value)
+
     def field(self, size=30, **kwargs):
         """ Render a form edit field for the property.
 
--- a/test/test_templating.py	Tue Aug 16 18:31:09 2022 -0400
+++ b/test/test_templating.py	Tue Aug 16 22:39:04 2022 -0400
@@ -305,6 +305,128 @@
             self.assertEqual(True,
                        greater_than <= now - timestamp < (greater_than + 1) )
 
+    def test_number__int__(self):
+        # test with number
+        p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test',
+                               2345678.2345678)
+        self.assertEqual(p.__int__(), 2345678)
+
+        property = MockNull(get_default_value = lambda: None)
+        p = NumberHTMLProperty(self.client, 'testnum', '1', property, 
+                               'test', None)
+        with self.assertRaises(TypeError) as e:
+            p.__int__()
+
+    def test_number__float__(self):
+        # test with number
+        p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test',
+                               2345678.2345678)
+        self.assertEqual(p.__float__(), 2345678.2345678)
+
+        property = MockNull(get_default_value = lambda: None)
+        p = NumberHTMLProperty(self.client, 'testnum', '1', property, 
+                               'test', None)
+        with self.assertRaises(TypeError) as e:
+            p.__float__()
+
+    def test_number_field(self):
+        import sys
+
+        _py3 = sys.version_info[0] > 2
+
+        # python2 truncates while python3 rounds. Sigh.
+        if _py3:
+            expected_val = 2345678.2345678
+        else:
+            expected_val = 2345678.23457
+
+        # test with number
+        p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test',
+                               2345678.2345678)
+        self.assertEqual(p.field(),
+                         ('<input name="testnum1@test" size="30" type="text" '
+                         'value="%s">')%expected_val)
+        self.assertEqual(p.field(size=10),
+                         ('<input name="testnum1@test" size="10" type="text" '
+                         'value="%s">')%expected_val)
+        self.assertEqual(p.field(size=10, dataprop="foo", dataprop2=5),
+                         ('<input dataprop="foo" dataprop2="5" '
+                          'name="testnum1@test" size="10" type="text" '
+                          'value="%s">'%expected_val))
+
+        self.assertEqual(p.field(size=10, klass="class1", 
+                                 **{ "class": "class2 class3",
+                                     "data-prop": "foo",
+                                     "data-prop2": 5}),
+                         ('<input class="class2 class3" data-prop="foo" '
+                          'data-prop2="5" klass="class1" '
+                          'name="testnum1@test" size="10" type="text" '
+                          'value="%s">')%expected_val)
+
+        # get plain representation if user can't edit
+        p.is_edit_ok = lambda: False
+        self.assertEqual(p.field(), p.plain())
+
+        # test with string which is wrong type
+        p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test',
+                               "2345678.2345678")
+        self.assertEqual(p.field(),
+                         ('<input name="testnum1@test" size="30" type="text" '
+                          'value="2345678.2345678">'))
+
+        # test with None value, pretend property.__default_value = Null which
+        #    is the default. It would be returned by get_default_value
+        #    which I mock.
+        property = MockNull(get_default_value = lambda: None)
+        p = NumberHTMLProperty(self.client, 'testnum', '1', property, 
+                               'test', None)
+        self.assertEqual(p.field(),
+                         ('<input name="testnum1@test" size="30" type="text" '
+                          'value="">'))
+
+    def test_number_plain(self):
+        import sys
+
+        _py3 = sys.version_info[0] > 2
+
+        # python2 truncates while python3 rounds. Sigh.
+        if _py3:
+            expected_val = 2345678.2345678
+        else:
+            expected_val = 2345678.23457
+
+        p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test',
+                               2345678.2345678)
+
+        self.assertEqual(p.plain(), "%s"%expected_val)
+
+    def test_number_pretty(self):
+        # test with number
+        p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test',
+                               2345678.2345678)
+        self.assertEqual(p.pretty(), "2345678.235")
+
+        # test with string which is wrong type
+        p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test',
+                               "2345678.2345678")
+        self.assertEqual(p.pretty(), "2345678.2345678")
+
+        # test with boolean
+        p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test',
+                               True)
+        self.assertEqual(p.pretty(), "1.000")
+
+        # test with None value, pretend property.__default_value = Null which
+        #    is the default. It would be returned by get_default_value
+        #    which I mock.
+        property = MockNull(get_default_value = lambda: None)
+        p = NumberHTMLProperty(self.client, 'testnum', '1', property, 
+                               'test', None)
+        self.assertEqual(p.pretty(), '')
+
+        with self.assertRaises(ValueError) as e:
+            p.pretty('%0.3')
+
     def test_string_url_quote(self):
         ''' test that urlquote quotes the string '''
         p = StringHTMLProperty(self.client, 'test', '1', None, 'test', 'test string< foo@bar')

Roundup Issue Tracker: http://roundup-tracker.org/