Coverage for control/typ/numeric.py : 93%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Numeric types. """
3import re
5from config import Config as C, Names as N
6from control.html import HtmlElements as H
7from control.typ.base import TypeBase
9from control.utils import E, EURO, MIN, DOT
12CW = C.web
14QQ = H.icon(CW.unknown[N.generic])
15Qq = H.icon(CW.unknown[N.generic], asChar=True)
16Qn = H.icon(CW.unknown[N.number], asChar=True)
18stripNonnumeric = re.compile(r"""[^-0-9.,]""")
19stripFraction = re.compile(r"""[.,][0-9]*$""")
20stripDecimal = re.compile(r"""[.,]""")
21stripLeading = re.compile(r"""^0+""")
22decimalSep = re.compile(r"""[.,]+""")
25class Numeric(TypeBase):
26 """Base class for numeric types: Int, Decimal, Money."""
28 widgetType = N.text
29 rawType = None
31 def normalize(self, strVal):
33 return Numeric.cleanNumber(strVal, self.rawType is int)
35 @staticmethod
36 def cleanNumber(strVal, asInt):
37 """Normalizes the string representation of a number, both decimal and integer.
39 Parameters
40 ----------
41 asInt: boolean
42 Specifies whether the number is integer or decimal.
44 Returns
45 -------
46 string
47 """
49 normalVal = str(strVal).strip()
50 normalVal = stripNonnumeric.sub(E, normalVal)
51 isNegative = normalVal.startswith(MIN)
52 normalVal = normalVal.replace(MIN, E)
53 if isNegative: 53 ↛ 54line 53 didn't jump to line 54, because the condition on line 53 was never true
54 normalVal = f"""{MIN}{normalVal}"""
55 if asInt:
56 normalVal = stripFraction.sub(E, normalVal)
57 normalVal = stripDecimal.sub(E, normalVal)
58 normalVal = stripLeading.sub(E, normalVal)
59 if not asInt:
60 parts = decimalSep.split(normalVal)
61 if len(parts) > 2: 61 ↛ 62line 61 didn't jump to line 62, because the condition on line 61 was never true
62 parts = parts[0:2]
63 normalVal = DOT.join(parts)
64 return normalVal or (Qn if asInt else f"""{Qn}{DOT}{Qn}""")
67class Int(Numeric):
68 """Type class for integer numbers, negative ones and zero included."""
70 rawType = int
71 pattern = """(^$)|(^0$)|(^-?[1-9][0-9]*$)"""
74class Decimal(Numeric):
75 """Type class for decimal numbers, negative ones and zero included."""
77 rawType = float
78 pattern = """(^$)|(^-?0$)|(^-?[1-9][0-9]*$)""" """|(^-?[0-9]+[.,][0-9]+$)"""
81class Money(Decimal):
82 """Type class for money quantities, negative ones and zero included."""
84 def toDisplay(self, val, markup=True):
85 if val is None:
86 return None if markup is None else QQ if markup else Qq
87 unit = "euro" if markup is None else EURO
88 valBare = f"""{unit} {self.normalize(str(val))}"""
89 return H.span(valBare) if markup else valBare