Hide keyboard shortcuts

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. """ 

2 

3import re 

4 

5from config import Config as C, Names as N 

6from control.html import HtmlElements as H 

7from control.typ.base import TypeBase 

8 

9from control.utils import E, EURO, MIN, DOT 

10 

11 

12CW = C.web 

13 

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) 

17 

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"""[.,]+""") 

23 

24 

25class Numeric(TypeBase): 

26 """Base class for numeric types: Int, Decimal, Money.""" 

27 

28 widgetType = N.text 

29 rawType = None 

30 

31 def normalize(self, strVal): 

32 

33 return Numeric.cleanNumber(strVal, self.rawType is int) 

34 

35 @staticmethod 

36 def cleanNumber(strVal, asInt): 

37 """Normalizes the string representation of a number, both decimal and integer. 

38 

39 Parameters 

40 ---------- 

41 asInt: boolean 

42 Specifies whether the number is integer or decimal. 

43 

44 Returns 

45 ------- 

46 string 

47 """ 

48 

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}""") 

65 

66 

67class Int(Numeric): 

68 """Type class for integer numbers, negative ones and zero included.""" 

69 

70 rawType = int 

71 pattern = """(^$)|(^0$)|(^-?[1-9][0-9]*$)""" 

72 

73 

74class Decimal(Numeric): 

75 """Type class for decimal numbers, negative ones and zero included.""" 

76 

77 rawType = float 

78 pattern = """(^$)|(^-?0$)|(^-?[1-9][0-9]*$)""" """|(^-?[0-9]+[.,][0-9]+$)""" 

79 

80 

81class Money(Decimal): 

82 """Type class for money quantities, negative ones and zero included.""" 

83 

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