Supporting arbitrary expressions

This commit is contained in:
Maxime Beauchemin 2015-10-06 11:50:04 -07:00
parent 36c7406786
commit 6db1b97a0e
4 changed files with 63 additions and 9 deletions

View File

@ -0,0 +1,26 @@
"""empty message
Revision ID: 1e2841a4128
Revises: 5a7bad26f2a7
Create Date: 2015-10-05 22:11:00.537054
"""
# revision identifiers, used by Alembic.
revision = '1e2841a4128'
down_revision = '5a7bad26f2a7'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('table_columns', sa.Column('expression', sa.Text(), nullable=True))
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('table_columns', 'expression')
### end Alembic commands ###

View File

@ -11,7 +11,8 @@ from sqlalchemy import (
from sqlalchemy import Table
from sqlalchemy import create_engine, MetaData, desc, select, and_
from sqlalchemy.orm import relationship
from sqlalchemy.sql import table, literal_column, text
from sqlalchemy.sql import table, literal_column, text, column
from sqlalchemy.sql.elements import ColumnClause
from flask import request
from copy import deepcopy, copy
@ -338,6 +339,7 @@ class SqlaTable(Model, Queryable, AuditMixinNullable):
inner_from_dttm=None, inner_to_dttm=None,
extras=None):
cols = {col.column_name: col for col in self.columns}
qry_start_dttm = datetime.now()
if not self.main_dttm_col:
raise Exception(
@ -360,9 +362,25 @@ class SqlaTable(Model, Queryable, AuditMixinNullable):
if groupby:
select_exprs = [literal_column(s) for s in groupby]
groupby_exprs = [literal_column(s) for s in groupby]
inner_groupby_exprs = [
literal_column(s).label('__' + s) for s in groupby]
select_exprs = []
groupby_exprs = []
inner_select_exprs = []
inner_groupby_exprs = []
for s in groupby:
col = cols[s]
expr = col.expression
if expr:
outer = ColumnClause(expr, is_literal=True).label(s)
inner = ColumnClause(expr, is_literal=True).label('__' + s)
else:
outer = literal_column(s).label(s)
inner = literal_column(s).label('__' + s)
groupby_exprs.append(outer)
select_exprs.append(outer)
inner_groupby_exprs.append(inner)
inner_select_exprs.append(inner)
if granularity != "all":
select_exprs += [timestamp]
groupby_exprs += [timestamp]
@ -384,9 +402,14 @@ class SqlaTable(Model, Queryable, AuditMixinNullable):
where_clause_and = []
for col, op, eq in filter:
col_obj = cols[col]
if op in ('in', 'not in'):
values = eq.split(",")
cond = literal_column(col).in_(values)
if col_obj.expression:
cond = ColumnClause(
col_obj.expression, is_literal=True).in_(values)
else:
cond = literal_column(col).in_(values)
if op == 'not in':
cond = ~cond
where_clause_and.append(cond)
@ -397,16 +420,16 @@ class SqlaTable(Model, Queryable, AuditMixinNullable):
qry = qry.limit(row_limit)
if timeseries_limit and groupby:
subq = select(inner_groupby_exprs)
subq = select(inner_select_exprs)
subq = subq.select_from(table(self.table_name))
subq = subq.where(and_(*(where_clause_and + inner_time_filter)))
subq = subq.group_by(*inner_groupby_exprs)
subq = subq.order_by(desc(main_metric_expr))
subq = subq.limit(timeseries_limit)
on_clause = []
for gb in groupby:
for i, gb in enumerate(groupby):
on_clause.append(
literal_column(gb) == literal_column("__" + gb))
groupby_exprs[i] == literal_column("__" + gb))
from_clause = from_clause.join(subq.alias(), and_(*on_clause))
@ -414,6 +437,7 @@ class SqlaTable(Model, Queryable, AuditMixinNullable):
engine = self.database.get_sqla_engine()
sql = str(qry.compile(engine, compile_kwargs={"literal_binds": True}))
print sql
df = read_sql_query(
sql=sql,
con=engine
@ -548,6 +572,7 @@ class TableColumn(Model, AuditMixinNullable):
max = Column(Boolean, default=False)
min = Column(Boolean, default=False)
filterable = Column(Boolean, default=False)
expression = Column(Text, default='')
description = Column(Text, default='')
def __repr__(self):

View File

@ -58,6 +58,8 @@ function viz_nvd3(token_name, json_callback) {
} else if (viz_type === 'dist_bar') {
var chart = nv.models.multiBarChart()
.showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.reduceXTicks(false)
.rotateLabels(45)
.groupSpacing(0.1); //Distance between each group of bars.
chart.xAxis
.showMaxMin(false);

View File

@ -42,7 +42,8 @@ class TableColumnInlineView(CompactCRUDMixin, PanoramixModelView):
can_delete = False
edit_columns = [
'column_name', 'description', 'groupby', 'filterable', 'table',
'count_distinct', 'sum', 'min', 'max']
'count_distinct', 'sum', 'min', 'max', 'expression']
add_columns = edit_columns
list_columns = [
'column_name', 'type', 'groupby', 'filterable', 'count_distinct',
'sum', 'min', 'max']