def _check_required_grain(self, adhoc_datasources=None): """Integrity check for required_grain settings""" errors = [] for metric in self.get_metrics(adhoc_fms=adhoc_datasources).values(): if not metric.required_grain: continue for field in metric.required_grain: if not self.has_dimension(field, adhoc_fms=adhoc_datasources): errors.append( "Metric %s references unknown dimension %s in required_grain" % (metric.name, field)) for ds in self.get_field_managers(adhoc_fms=adhoc_datasources): for table in ds.metadata.tables.values(): if not is_active(table): continue for column in table.c: if not is_active(column): continue if not column.zillion.required_grain: continue for field in column.zillion.required_grain: if not self.has_dimension(field, adhoc_fms=adhoc_datasources): errors.append( "Column %s->%s references unknown dimension %s in required_grain" % (ds.name, column_fullname(column), field)) return errors
def replace_non_named_formula_args(formula, column): """Do formula arg replacement but raise an error if any named args are present""" format_args = get_string_format_args(formula) raiseif( any([x != "" for x in format_args]), "Formula has unexpected named format arguments: %s" % formula, ) if format_args: formula = formula.format( *[column_fullname(column) for i in format_args]) return formula
def get_ds_expression(self, column, label=True): """Get the datasource-level sql expression for this metric **Parameters:** * **column** - (*Column*) A SQLAlchemy column that supports this metric * **label** - (*bool, optional*) If true, label the expression with the field name """ expr = column aggr = aggregation_to_sqla_func(self.aggregation) skip_aggr = False ds_formula = column.zillion.field_ds_formula(self.name) if ds_formula: if contains_sql_keywords(ds_formula): raise DisallowedSQLException( "Formula contains disallowed sql: %s" % ds_formula) if contains_aggregation(ds_formula): info( "Datasource formula contains aggregation, skipping default logic" ) skip_aggr = True expr = sa.literal_column(ds_formula) if not skip_aggr: if self.aggregation in [ AggregationTypes.COUNT, AggregationTypes.COUNT_DISTINCT, ]: if self.rounding: info("Ignoring rounding for count field: %s" % self.name) if label: return aggr(expr).label(self.name) return aggr(expr) if self.weighting_metric: w_column = get_table_field_column(column.table, self.weighting_metric) w_column_name = column_fullname(w_column) # NOTE: 1.0 multiplication is a hack to ensure results are not rounded # to integer values improperly by some database dialects such as sqlite expr = sa.func.SUM( sa.text("1.0") * expr * sa.text(w_column_name)) / sa.func.SUM( sa.text(w_column_name)) else: expr = aggr(expr) if label: return expr.label(self.name) return expr
def default_field_name(column): """Get the default field name from a SQLAlchemy column **Parameters:** * **column** - (*SQLAlchemy column*) A column to get the default field name for **Returns:** (*str*) - The default field name for the column """ return field_safe_name(column_fullname(column))
def _check_fields_have_type(self, adhoc_datasources=None): """Integrity check for field types""" errors = [] for ds in self.get_field_managers(adhoc_fms=adhoc_datasources): for table in ds.metadata.tables.values(): if not is_active(table): continue for column in table.c: if not is_active(column): continue for field in column.zillion.get_field_names(): if not (self.has_metric(field, adhoc_fms=adhoc_datasources) or self.has_dimension( field, adhoc_fms=adhoc_datasources)): errors.append( "Field %s for column %s->%s is not defined as a metric or dimension" % (field, ds.name, column_fullname(column))) return errors