from pypika import functions as fn
from pypika.queries import QueryBuilder
from fireant import utils
[docs]class Reference(object):
def __init__(self, dimension, reference_type, delta=False, delta_percent=False):
self.dimension = dimension
self.reference_type = reference_type
self.key = reference_type.key + '_delta_percent' \
if delta_percent \
else reference_type.key + '_delta' \
if delta \
else reference_type.key
self.label = reference_type.label + ' Δ%' \
if delta_percent \
else reference_type.label + ' Δ' \
if delta \
else reference_type.label
self.time_unit = reference_type.time_unit
self.interval = reference_type.interval
self.delta = delta_percent or delta
self.delta_percent = delta_percent
def __eq__(self, other):
return isinstance(self, Reference) \
and self.dimension == other.dimension \
and self.key == other.key
def __hash__(self):
return hash('reference{}{}'.format(self.key, self.dimension))
def __repr__(self):
return '{}({})'.format(self.key, self.dimension.key)
[docs]class ReferenceType(object):
def __init__(self, key, label, time_unit: str, interval: int):
self.key = key
self.label = label
self.time_unit = time_unit
self.interval = interval
def __call__(self, dimension, delta=False, delta_percent=False):
return Reference(dimension, self, delta=delta, delta_percent=delta_percent)
def __eq__(self, other):
return isinstance(self, ReferenceType) \
and self.time_unit == other.time_unit \
and self.interval == other.interval
def __hash__(self):
return hash('reference' + self.key)
DayOverDay = ReferenceType('dod', 'DoD', 'day', 1)
WeekOverWeek = ReferenceType('wow', 'WoW', 'week', 1)
MonthOverMonth = ReferenceType('mom', 'MoM', 'month', 1)
QuarterOverQuarter = ReferenceType('qoq', 'QoQ', 'quarter', 1)
YearOverYear = ReferenceType('yoy', 'YoY', 'year', 1)
[docs]def reference_key(metric, reference):
"""
Format a metric key for a reference.
:return:
A string that is used as the key for a reference metric.
"""
key = metric.key
if reference is not None:
return '{}_{}'.format(key, reference.key)
return key
[docs]def reference_label(metric, reference):
"""
Format a metric label for a reference.
:return:
A string that is used as the display value for a reference metric.
"""
label = metric.label or metric.key
if reference is not None:
return '{} ({})'.format(label, reference.label)
return label
[docs]def reference_prefix(metric, reference):
"""
Return the prefix for a metric displayed for a reference (or no Reference)
:return:
A string that is used as the prefix for a reference metric.
"""
if reference is not None and reference.delta_percent:
return None
return metric.prefix
[docs]def reference_suffix(metric, reference):
"""
Return the suffix for a metric displayed for a reference (or no Reference)
:return:
A string that is used as the suffix for a reference metric.
"""
if reference is not None and reference.delta_percent:
return '%'
return metric.suffix
[docs]def reference_term(reference: Reference,
original_query: QueryBuilder,
ref_query: QueryBuilder):
"""
Part of query building. Given a reference, the original slicer query, and the ref query, creates the pypika for
the reference that should be selected in the reference container query.
:param reference:
:param original_query:
:param ref_query:
:return:
"""
def original_field(metric):
return original_query.field(utils.format_metric_key(metric.key))
def ref_field(metric):
return ref_query.field(utils.format_metric_key(metric.key))
if reference.delta:
if reference.delta_percent:
return lambda metric: (original_field(metric) - ref_field(metric)) \
* \
(100 / fn.NullIf(ref_field(metric), 0))
return lambda metric: original_field(metric) - ref_field(metric)
return ref_field