Module control.typ.numeric

Numeric types.

Expand source code
"""Numeric types. """

import re

from config import Config as C, Names as N
from control.html import HtmlElements as H
from control.typ.base import TypeBase

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


CW = C.web

QQ = H.icon(CW.unknown[N.generic])
Qq = H.icon(CW.unknown[N.generic], asChar=True)
Qn = H.icon(CW.unknown[N.number], asChar=True)

stripNonnumeric = re.compile(r"""[^-0-9.,]""")
stripFraction = re.compile(r"""[.,][0-9]*$""")
stripDecimal = re.compile(r"""[.,]""")
stripLeading = re.compile(r"""^0+""")
decimalSep = re.compile(r"""[.,]+""")


class Numeric(TypeBase):
    """Base class for numeric types: Int,  Decimal, Money."""

    widgetType = N.text
    rawType = None

    def normalize(self, strVal):

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

    @staticmethod
    def cleanNumber(strVal, asInt):
        """Normalizes the string representation of a number, both decimal and integer.

        Parameters
        ----------
        asInt: boolean
            Specifies whether the number is integer or decimal.

        Returns
        -------
        string
        """

        normalVal = str(strVal).strip()
        normalVal = stripNonnumeric.sub(E, normalVal)
        isNegative = normalVal.startswith(MIN)
        normalVal = normalVal.replace(MIN, E)
        if isNegative:
            normalVal = f"""{MIN}{normalVal}"""
        if asInt:
            normalVal = stripFraction.sub(E, normalVal)
            normalVal = stripDecimal.sub(E, normalVal)
        normalVal = stripLeading.sub(E, normalVal)
        if not asInt:
            parts = decimalSep.split(normalVal)
            if len(parts) > 2:
                parts = parts[0:2]
            normalVal = DOT.join(parts)
        return normalVal or (Qn if asInt else f"""{Qn}{DOT}{Qn}""")


class Int(Numeric):
    """Type class for integer numbers, negative ones and zero included."""

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


class Decimal(Numeric):
    """Type class for decimal numbers, negative ones and zero included."""

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


class Money(Decimal):
    """Type class for money quantities, negative ones and zero included."""

    def toDisplay(self, val, markup=True):
        if val is None:
            return None if markup is None else QQ if markup else Qq
        unit = "euro" if markup is None else EURO
        valBare = f"""{unit} {self.normalize(str(val))}"""
        return H.span(valBare) if markup else valBare

Classes

class Decimal

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

Expand source code
class Decimal(Numeric):
    """Type class for decimal numbers, negative ones and zero included."""

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

Ancestors

Subclasses

Class variables

var pattern
var rawType

Convert a string or number to a floating point number, if possible.

Inherited members

class Int

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

Expand source code
class Int(Numeric):
    """Type class for integer numbers, negative ones and zero included."""

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

Ancestors

Class variables

var pattern
var rawType

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.

>>> int('0b100', base=0)
4

Inherited members

class Money

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

Expand source code
class Money(Decimal):
    """Type class for money quantities, negative ones and zero included."""

    def toDisplay(self, val, markup=True):
        if val is None:
            return None if markup is None else QQ if markup else Qq
        unit = "euro" if markup is None else EURO
        valBare = f"""{unit} {self.normalize(str(val))}"""
        return H.span(valBare) if markup else valBare

Ancestors

Inherited members

class Numeric

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

Expand source code
class Numeric(TypeBase):
    """Base class for numeric types: Int,  Decimal, Money."""

    widgetType = N.text
    rawType = None

    def normalize(self, strVal):

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

    @staticmethod
    def cleanNumber(strVal, asInt):
        """Normalizes the string representation of a number, both decimal and integer.

        Parameters
        ----------
        asInt: boolean
            Specifies whether the number is integer or decimal.

        Returns
        -------
        string
        """

        normalVal = str(strVal).strip()
        normalVal = stripNonnumeric.sub(E, normalVal)
        isNegative = normalVal.startswith(MIN)
        normalVal = normalVal.replace(MIN, E)
        if isNegative:
            normalVal = f"""{MIN}{normalVal}"""
        if asInt:
            normalVal = stripFraction.sub(E, normalVal)
            normalVal = stripDecimal.sub(E, normalVal)
        normalVal = stripLeading.sub(E, normalVal)
        if not asInt:
            parts = decimalSep.split(normalVal)
            if len(parts) > 2:
                parts = parts[0:2]
            normalVal = DOT.join(parts)
        return normalVal or (Qn if asInt else f"""{Qn}{DOT}{Qn}""")

Ancestors

Subclasses

Class variables

var rawType
var widgetType

Static methods

def cleanNumber(strVal, asInt)

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

Parameters

asInt : boolean
Specifies whether the number is integer or decimal.

Returns

string
 
Expand source code
@staticmethod
def cleanNumber(strVal, asInt):
    """Normalizes the string representation of a number, both decimal and integer.

    Parameters
    ----------
    asInt: boolean
        Specifies whether the number is integer or decimal.

    Returns
    -------
    string
    """

    normalVal = str(strVal).strip()
    normalVal = stripNonnumeric.sub(E, normalVal)
    isNegative = normalVal.startswith(MIN)
    normalVal = normalVal.replace(MIN, E)
    if isNegative:
        normalVal = f"""{MIN}{normalVal}"""
    if asInt:
        normalVal = stripFraction.sub(E, normalVal)
        normalVal = stripDecimal.sub(E, normalVal)
    normalVal = stripLeading.sub(E, normalVal)
    if not asInt:
        parts = decimalSep.split(normalVal)
        if len(parts) > 2:
            parts = parts[0:2]
        normalVal = DOT.join(parts)
    return normalVal or (Qn if asInt else f"""{Qn}{DOT}{Qn}""")

Inherited members