Source code for fireant.formats

import re
from datetime import (
    date,
    datetime,
)

import numpy as np
import pandas as pd
from fireant.dataset.fields import DataType
from fireant.dataset.totals import TOTALS_MARKERS
from fireant.utils import (
    filter_kwargs,
)

RAW_VALUE = 'raw'
INF_VALUE = "Inf"
NULL_VALUE = 'null'
BLANK_VALUE = ''
TOTALS_VALUE = '$totals'
TOTALS_LABEL = 'Totals'

epoch = np.datetime64(datetime.utcfromtimestamp(0))
milliseconds = np.timedelta64(1, 'ms')

DATE_FORMATS = {
    'iso': '%Y-%m-%dT%H:%M:%S',
    'hour': '%Y-%m-%d %H:00',
    'day': '%Y-%m-%d',
    'week': 'W%W %Y-%m-%d',
    'month': '%b %Y',
    'quarter': '%Y-%q',
    'year': '%Y',
}


@filter_kwargs
def date_as_string(value, interval_key=None):
    f = DATE_FORMATS.get(interval_key, '%Y-%m-%d')
    return value.strftime(f)


@filter_kwargs
def date_as_millis(value):
    if isinstance(value, date) and not isinstance(value, datetime):
        value = datetime.combine(value, datetime.min.time())
    return int(1000 * value.timestamp())


@filter_kwargs
def return_none(value):
    return None


@filter_kwargs
def _format_decimal(value, thousands='', precision=None):
    if not isinstance(value, (int, float)):
        return value

    precision_pattern = '{{:{}.{}f}}'.format(thousands, precision) \
        if precision is not None \
        else '{{:{}f}}'.format(thousands)

    f_value = precision_pattern.format(value)
    if precision is None:
        f_value = f_value.rstrip('0').rstrip('.')
    return f_value


@filter_kwargs
def wrap_styling(value, prefix=None, suffix=None):
    if value is None:
        return value

    return '{prefix}{value}{suffix}' \
        .format(prefix=prefix or '',
                suffix=suffix or '',
                value=value)


@filter_kwargs
def _identity(value):
    return value


UNSAFE_CHARS = re.compile(r'[^\w\d\s\-:()]')


[docs]def json_value(value): """ This function will return only values safe for JSON """ if pd.isnull(value): return None if isinstance(value, float) and np.isinf(value): return 'inf' if value in TOTALS_MARKERS: return TOTALS_VALUE return value
[docs]def safe_value(value): if value is None or value is '' or pd.isnull(value): return NULL_VALUE if isinstance(value, float) and np.isinf(value): return 'inf' if value in TOTALS_MARKERS: return TOTALS_VALUE str_value = date_as_string(value, interval_key='iso') \ if isinstance(value, date) \ else str(value) return UNSAFE_CHARS.sub('$', str_value)
@filter_kwargs def _format_date_field(value, date_as=date_as_string, **kwargs): return date_as(value, **kwargs) RAW_FIELD_FORMATTER = { DataType.date: _format_date_field, }
[docs]def raw_value(value, field, date_as=date_as_string): """ Converts a raw metric value into a safe type. This will change dates into strings, NaNs into Nones, and np types into their corresponding python types. :param value: The raw metric value. :param field: The slicer field that the value represents. :param date_as: """ if value is None or pd.isnull(value): return None if isinstance(value, float) and np.isinf(value): return None if value in TOTALS_MARKERS: return TOTALS_VALUE formatter = RAW_FIELD_FORMATTER.get(field.data_type, _identity) return formatter(value, date_as=date_as, interval_key='iso')
def _format_number_field_value(value, **kwargs): number = _format_decimal(value, **kwargs) return wrap_styling(number, **kwargs) @filter_kwargs def _format_boolean_field(value): return str(value).lower() FIELD_DISPLAY_FORMATTER = { DataType.date: _format_date_field, DataType.number: _format_number_field_value, DataType.boolean: _format_boolean_field, DataType.text: return_none, }
[docs]def display_value(value, field, date_as=date_as_string): """ Converts a metric value into the display value by applying formatting. :param value: The raw metric value. :param field: The slicer field that the value represents. :param date_as: A format function for datetimes. :return: A formatted string containing the display value for the metric. """ if pd.isnull(value): return BLANK_VALUE if isinstance(value, float) and np.isinf(value): return INF_VALUE if value in TOTALS_MARKERS: return TOTALS_LABEL format_kwargs = {key: getattr(field, key, None) for key in ('prefix', 'suffix', 'thousands', 'precision', 'interval_key')} format_kwargs = {key: value for key, value in format_kwargs.items() if value is not None} formatter = FIELD_DISPLAY_FORMATTER.get(field.data_type, _identity) return formatter(value, date_as=date_as, **format_kwargs)