Skip to content

Commit

Permalink
Merge pull request #23 from mistercrunch/where
Browse files Browse the repository at this point in the history
Custom WHERE clause for tables (not druid) + error handling refactor
  • Loading branch information
mistercrunch committed Sep 9, 2015
2 parents 67c5f63 + fab0670 commit 53fe171
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 33 deletions.
12 changes: 10 additions & 2 deletions panoramix/forms.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from wtforms import Field, Form, SelectMultipleField, SelectField, TextField
from copy import copy


class OmgWtForm(Form):
Expand All @@ -24,7 +25,8 @@ def field_css_classes(self, fieldname):
return ""


def form_factory(datasource, viz, form_args=None):
def form_factory(viz):
datasource = viz.datasource
from panoramix.viz import viz_types
row_limits = [10, 50, 100, 500, 1000, 5000, 10000]
series_limits = [0, 5, 10, 25, 50, 100, 500]
Expand Down Expand Up @@ -57,6 +59,7 @@ def form_factory(datasource, viz, form_args=None):
'x': SelectField('X Axis', choices=datasource.metrics_combo),
'y': SelectField('Y Axis', choices=datasource.metrics_combo),
'size': SelectField('Bubble Size', choices=datasource.metrics_combo),
'where': TextField('Custom WHERE clause'),
}
field_css_classes = {k: ['form-control'] for k in px_form_fields.keys()}
select2 = [
Expand All @@ -70,7 +73,7 @@ def form_factory(datasource, viz, form_args=None):
field_css_classes[field] += ['select2']

class QueryForm(OmgWtForm):
field_order = viz.form_fields
field_order = copy(viz.form_fields)
css_classes = field_css_classes

for i in range(10):
Expand All @@ -85,4 +88,9 @@ class QueryForm(OmgWtForm):
ff = [ff]
for s in ff:
setattr(QueryForm, s, px_form_fields[s])

# datasource type specific form elements
if datasource.__class__.__name__ == 'Table':
QueryForm.field_order += ['where']
setattr(QueryForm, 'where', px_form_fields['where'])
return QueryForm
14 changes: 10 additions & 4 deletions panoramix/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from sqlalchemy import Table as sqlaTable
from sqlalchemy import create_engine, MetaData, desc, select, and_
from sqlalchemy.orm import relationship
from sqlalchemy.sql import table, literal_column
from sqlalchemy.sql import table, literal_column, text

from copy import deepcopy, copy
from collections import namedtuple
Expand Down Expand Up @@ -99,7 +99,9 @@ def query_bkp(
limit_spec=None,
filter=None,
is_timeseries=True,
timeseries_limit=15, row_limit=None):
timeseries_limit=15,
row_limit=None,
extras=None):
"""
Unused, legacy way of querying by building a SQL string without
using the sqlalchemy expression API (new approach which supports
Expand Down Expand Up @@ -192,7 +194,8 @@ def query(
limit_spec=None,
filter=None,
is_timeseries=True,
timeseries_limit=15, row_limit=None):
timeseries_limit=15, row_limit=None,
extras=None):

qry_start_dttm = datetime.now()
timestamp = literal_column(
Expand Down Expand Up @@ -236,6 +239,8 @@ def query(
if op == 'not in':
cond = ~cond
where_clause_and.append(cond)
if extras and 'where' in extras:
where_clause_and += [text(extras['where'])]
qry = qry.where(and_(*where_clause_and))
qry = qry.order_by(desc(main_metric_expr))
qry = qry.limit(row_limit)
Expand Down Expand Up @@ -530,7 +535,8 @@ def query(
filter=None,
is_timeseries=True,
timeseries_limit=None,
row_limit=None):
row_limit=None,
extras=None):
qry_start_dttm = datetime.now()

# add tzinfo to native datetime with config
Expand Down
3 changes: 2 additions & 1 deletion panoramix/templates/panoramix/datasource.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% extends "panoramix/base.html" %}
{% block head_css %}
{{super()}}
{% set datasource = viz.datasource %}
<style>
.select2-container-multi .select2-choices {
height: 70px;
Expand Down Expand Up @@ -97,7 +98,7 @@ <h3>{{ viz.verbose_name }}
<hr/>
{% block viz %}
{% if error_msg %}
<span class="alert alert-danger">{{ error_msg }}</span>
<div class="alert alert-danger">{{ error_msg }}</div>
{% endif %}
{% endblock %}

Expand Down
4 changes: 2 additions & 2 deletions panoramix/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def table(self, table_id):
json.dumps(obj.get_query(), indent=4),
status=200,
mimetype="application/json")
return obj.render()
return obj.check_and_render()

@has_access
@expose("/datasource/<datasource_name>/")
Expand Down Expand Up @@ -224,7 +224,7 @@ def datasource(self, datasource_name):
if not hasattr(obj, 'df') or obj.df is None or obj.df.empty:
return obj.render_no_data()

return obj.render()
return obj.check_and_render()

@has_access
@expose("/refresh_datasources/")
Expand Down
48 changes: 24 additions & 24 deletions panoramix/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __init__(self, datasource, form_data, view):
self.error_msg = str(e)

def form_class(self):
return form_factory(self.datasource, self, request.args)
return form_factory(self)

def query_filters(self):
args = self.form_data
Expand Down Expand Up @@ -86,6 +86,12 @@ def query_obj(self):
if from_dttm >= to_dttm:
flash("The date range doesn't seem right.", "danger")
from_dttm = to_dttm # Making them identicial to not raise

# extras are used to query elements specific to a datasource type
# for instance the extra where clause that applies only to Tables
extras = {
'where': args.get("where")
}
d = {
'granularity': granularity,
'from_dttm': from_dttm,
Expand All @@ -96,13 +102,20 @@ def query_obj(self):
'row_limit': row_limit,
'filter': self.query_filters(),
'timeseries_limit': limit,
'extras': extras,
}
return d

def render_no_data(self):
self.template = "panoramix/no_data.html"
return BaseViz.render(self)

def check_and_render(self, *args, **kwards):
if self.error_msg:
return BaseViz.render(self, error_msg=self.error_msg)
else:
return self.render(*args, **kwards)

def render(self, *args, **kwargs):
form = self.form_class(self.form_data)
return self.view.render_template(
Expand All @@ -123,9 +136,6 @@ def query_obj(self):
return d

def render(self):
if self.error_msg:
return super(TableViz, self).render(error_msg=self.error_msg)

df = self.df
if df is None or df.empty:
return super(TableViz, self).render(error_msg="No data.")
Expand All @@ -138,9 +148,6 @@ def render(self):
df[m + '__perc'] = np.rint((df[m] / np.max(df[m])) * 100)
return super(TableViz, self).render(df=df)

def form_class(self):
return form_factory(self.datasource, self, request.args)


class HighchartsViz(BaseViz):
verbose_name = "Base Highcharts Viz"
Expand All @@ -159,9 +166,6 @@ class BubbleViz(HighchartsViz):
'viz_type', 'since', 'until',
'series', 'entity', 'x', 'y', 'size', 'limit']

def form_class(self):
return form_factory(self.datasource, self, request.args)

def query_obj(self):
d = super(BubbleViz, self).query_obj()
d['granularity'] = 'all'
Expand All @@ -184,17 +188,14 @@ def query_obj(self):
return d

def render(self):
if not self.error_msg:
df = self.df.fillna(0)
df['x'] = df[[self.x_metric]]
df['y'] = df[[self.y_metric]]
df['z'] = df[[self.z_metric]]
df['name'] = df[[self.entity]]
df['group'] = df[[self.series]]
chart = HighchartBubble(df)
return super(BubbleViz, self).render(chart_js=chart.javascript_cmd)
else:
return super(BubbleViz, self).render(error_msg=self.error_msg)
df = self.df.fillna(0)
df['x'] = df[[self.x_metric]]
df['y'] = df[[self.y_metric]]
df['z'] = df[[self.z_metric]]
df['name'] = df[[self.entity]]
df['group'] = df[[self.series]]
chart = HighchartBubble(df)
return super(BubbleViz, self).render(chart_js=chart.javascript_cmd)


class TimeSeriesViz(HighchartsViz):
Expand Down Expand Up @@ -243,9 +244,6 @@ def render(self):
**CHART_ARGS)
return super(TimeSeriesViz, self).render(chart_js=chart.javascript_cmd)

def form_class(self):
return form_factory(self.datasource, self, request.args)

def bake_query(self):
"""
Doing a 2 phase query where we limit the number of series.
Expand Down Expand Up @@ -283,6 +281,7 @@ class TimeSeriesStackedBarViz(TimeSeriesViz):
class DistributionBarViz(HighchartsViz):
verbose_name = "Distribution - Bar Chart"
chart_type = "column"
form_fields = BaseViz.form_fields + ['limit']

def query_obj(self):
d = super(DistributionBarViz, self).query_obj()
Expand All @@ -305,6 +304,7 @@ def render(self):
class DistributionPieViz(HighchartsViz):
verbose_name = "Distribution - Pie Chart"
chart_type = "pie"
form_fields = BaseViz.form_fields + ['limit']

def query_obj(self):
d = super(DistributionPieViz, self).query_obj()
Expand Down

0 comments on commit 53fe171

Please sign in to comment.