Skip to content

Commit

Permalink
Merge pull request #92 from mistercrunch/nogroupby
Browse files Browse the repository at this point in the history
Allowing not to group by on table view
  • Loading branch information
mistercrunch committed Dec 18, 2015
2 parents 1377c37 + 19eb177 commit 47bb6b6
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 63 deletions.
3 changes: 0 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@
List of TODO items for Panoramix

## Improvments
* GROUPED boolean for table view
* datasource in explore mode could be a dropdown
* [sql] make "Test Connection" test further
* in/notin filters autocomplete
* [druid] Allow for post aggregations (ratios!)
* in/notin filters autocomplete
* [sql] make "Test Connection" test further

## Better Javascript enables
* Async on Druidify! in exploration page
Expand Down
16 changes: 14 additions & 2 deletions panoramix/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ def __init__(self, viz):
'Columns',
choices=self.choicify(datasource.groupby_column_names),
description="One or many fields to pivot as columns"),
'all_columns': SelectMultipleSortableField(
'Columns',
choices=self.choicify(datasource.column_names),
description="Columns to display"),
'granularity': FreeFormSelectField(
'Time Granularity', default="one day",
choices=self.choicify([
Expand Down Expand Up @@ -322,6 +326,10 @@ def __init__(self, viz):
"Range Filter", default=True,
description=(
"Whether to display the time range interactive selector")),
'include_search': BetterBooleanField(
"Search Box", default=False,
description=(
"Whether to include a client side search box")),
'show_bubbles': BetterBooleanField(
"Show Bubbles", default=False,
description=(
Expand All @@ -335,10 +343,14 @@ def __init__(self, viz):
"Whether to display the min and max values of the axis")),
'rich_tooltip': BetterBooleanField(
"Rich Tooltip", default=True,
description="The rich tooltip shows a list of all series for that point in time"),
description=(
"The rich tooltip shows a list of all series for that"
" point in time")),
'y_axis_zero': BetterBooleanField(
"Y Axis Zero", default=False,
description="Force the Y axis to start at 0 instead of the minimum value"),
description=(
"Force the Y axis to start at 0 instead of the minimum "
"value")),
'y_log_scale': BetterBooleanField(
"Y Log", default=False,
description="Use a log scale for the Y axis"),
Expand Down
19 changes: 14 additions & 5 deletions panoramix/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,8 @@ def query(
is_timeseries=True,
timeseries_limit=15, row_limit=None,
inner_from_dttm=None, inner_to_dttm=None,
extras=None):
extras=None,
columns=None):

# For backward compatibility
if granularity not in self.dttm_cols:
Expand Down Expand Up @@ -427,15 +428,20 @@ def query(
select_exprs.append(outer)
inner_groupby_exprs.append(inner)
inner_select_exprs.append(inner)
elif columns:
for s in columns:
select_exprs.append(s)
metrics_exprs = []

if is_timeseries:
if is_timeseries and groupby:
select_exprs += [timestamp]
groupby_exprs += [timestamp]

select_exprs += metrics_exprs
qry = select(select_exprs)
from_clause = table(self.table_name)
qry = qry.group_by(*groupby_exprs)
if groupby:
qry = qry.group_by(*groupby_exprs)

time_filter = [
timestamp >= from_dttm.isoformat(),
Expand Down Expand Up @@ -466,7 +472,8 @@ def query(
having_clause_and += [text(extras['having'])]
qry = qry.where(and_(*(time_filter + where_clause_and)))
qry = qry.having(and_(*having_clause_and))
qry = qry.order_by(desc(main_metric_expr))
if groupby:
qry = qry.order_by(desc(main_metric_expr))
qry = qry.limit(row_limit)

if timeseries_limit and groupby:
Expand All @@ -492,6 +499,7 @@ def query(
con=engine
)
sql = sqlparse.format(sql, reindent=True)
print(sql)
return QueryResult(
df=df, duration=datetime.now() - qry_start_dttm, query=sql)

Expand Down Expand Up @@ -779,7 +787,8 @@ def query(
timeseries_limit=None,
row_limit=None,
inner_from_dttm=None, inner_to_dttm=None,
extras=None):
extras=None,
select=None):
qry_start_dttm = datetime.now()

inner_from_dttm = inner_from_dttm or from_dttm
Expand Down
6 changes: 6 additions & 0 deletions panoramix/static/panoramix.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
html>body{
margin: 0px; !important
}
.container-fluid {
text-align: left;
}
input[type="checkbox"] {
display: inline-block;
width: 16px;
Expand Down
28 changes: 13 additions & 15 deletions panoramix/static/widgets/viz_pivot_table.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,24 @@ px.registerWidget('pivot_table', function(data_attribute) {
var form_data = data_attribute.form_data;

function refresh(done) {
token.load(data_attribute.json_endpoint, function(response, status, xhr){
if(status=="error"){
$.getJSON(data_attribute.json_endpoint, function(json){
token.html(json.data);
if (form_data.groupby.length == 1){
var table = token.find('table').DataTable({
paging: false,
searching: false,
});
table.column('-1').order( 'desc' ).draw();
}
token.show();
done(json);
}).fail(function(xhr){
var err = '<div class="alert alert-danger">' + xhr.responseText + '</div>';
token.html(err);
token.show();
}
else{
if (form_data.groupby.length == 1){
var table = token.find('table').DataTable({
paging: false,
searching: false,
});
table.column('-1').order( 'desc' ).draw();
}
}
token.show();
done();
done();
});
}

return {
render: refresh,
resize: refresh,
Expand Down
63 changes: 49 additions & 14 deletions panoramix/static/widgets/viz_table.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,63 @@ px.registerWidget('table', function(data_attribute) {
var token = $('#' + token_name);

function refresh(done) {
token.load(data_attribute.json_endpoint, function(response, status, xhr){
if(status=="error"){
var err = '<div class="alert alert-danger">' + xhr.responseText + '</div>';
token.html(err);
token.show();
done();
$.getJSON(data_attribute.json_endpoint, function(json){
var data = json.data;
var metrics = json.form_data.metrics;
function col(c){
arr = [];
for (var i=0; i<data.records.length; i++){
arr.push(json.data.records[i][c]);
}
return arr;
}
else{
var table = token.find('table').DataTable({
paging: false,
searching: false,
});
table.column('-1').order( 'desc' ).draw();
maxes = {};
for (var i=0; i<metrics.length; i++){
maxes[metrics[i]] = d3.max(col(metrics[i]));
}

var table = d3.select('#' + token_name).append('table')
.attr('class', 'dataframe table table-striped table-bordered table-condensed table-hover');
table.append('thead').append('tr')
.selectAll('th')
.data(data.columns).enter()
.append('th')
.text(function(d){return d});
table.append('tbody')
.selectAll('tr')
.data(data.records).enter()
.append('tr')
.selectAll('td')
.data(function(row, i) {
return data.columns.map(function(c) {return [c, row];});
}).enter()
.append('td')
.style('background-image', function(d){
var perc = Math.round((d[1][d[0]] / maxes[d[0]]) * 100);
if (perc !== NaN)
return "linear-gradient(to right, lightgrey, lightgrey " + perc + "%, rgba(0,0,0,0) " + perc + "%"
})
.html(function(d){return d[1][d[0]]});
var datatable = token.find('table').DataTable({
paging: false,
searching: data_attribute.form_data.include_search,
});
// Sorting table by main column
if (data_attribute.form_data.metrics.length > 0) {
var main_metric = data_attribute.form_data.metrics[0];
datatable.column(data.columns.indexOf(main_metric)).order( 'desc' ).draw();
}
done(json);
}).fail(function(xhr){
var err = '<div class="alert alert-danger">' + xhr.responseText + '</div>';
token.html(err);
token.show();
done();
});
}

return {
render: refresh,
resize: refresh,
resize: function(){},
};

});
11 changes: 0 additions & 11 deletions panoramix/templates/panoramix/viz_pivot_table.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
{% macro viz_html(viz) %}
{% if viz.request.args.get("async") == "true" %}
{{ viz.get_df().to_html(na_rep='', classes="dataframe table table-striped table-bordered table-condensed")|safe }}
{% else %}
<div id="{{ viz.token }}" style="display: none;overflow: auto; height: 100%;"></div>
{% endif %}
{% endmacro %}

{% macro viz_js(viz) %}
{% if viz.form_data.get("async") != "true" %}
<script>
$( document ).ready(function() {
});
</script>
{% endif %}
{% endmacro %}

{% macro viz_css(viz) %}
Expand Down
2 changes: 1 addition & 1 deletion panoramix/templates/panoramix/viz_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% if viz.request.args.get("async") == "true" %}
{% set df = viz.get_df() %}
<div class="table">
<table class="dataframe table table-striped table-bordered table-condensed">
<table class="dataframe table table-striped table-bordered table-condensed table-hover">
<thead>
<tr>
{% for col in df.columns if not col.endswith('__perc') %}
Expand Down
50 changes: 38 additions & 12 deletions panoramix/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,25 +242,43 @@ class TableViz(BaseViz):
'fields': (
'granularity',
('since', 'until'),
'metrics', 'groupby',
'row_limit'
'row_limit',
('include_search', None),
)
},
{
'label': "GROUP BY",
'fields': (
'groupby',
'metrics',
)
},
{
'label': "NOT GROUPED BY",
'fields': (
'all_columns',
)
},)
css_files = ['lib/dataTables/dataTables.bootstrap.css']
is_timeseries = False
js_files = [
'lib/d3.min.js',
'lib/dataTables/jquery.dataTables.min.js',
'lib/dataTables/dataTables.bootstrap.js',
'widgets/viz_table.js',
]
css_files = ['widgets/viz_table.css']

@property
def json_endpoint(self):
return self.get_url(async='true', standalone='true', skip_libs='true')

def query_obj(self):
d = super(TableViz, self).query_obj()
fd = self.form_data
if fd.get('all_columns') and (fd.get('groupby') or fd.get('metrics')):
raise Exception(
"Choose either fields to [Group By] and [Metrics] or "
"[Columns], not both")
if fd.get('all_columns'):
d['columns'] = fd.get('all_columns')
d['groupby'] = []
d['is_timeseries'] = False
d['timeseries_limit'] = None
return d
Expand All @@ -271,10 +289,15 @@ def get_df(self):
self.form_data.get("granularity") == "all" and
'timestamp' in df):
del df['timestamp']
for m in self.metrics:
df[m + '__perc'] = np.rint((df[m] / np.max(df[m])) * 100)
return df

def get_json_data(self):
df = self.get_df()
return dumps(dict(
records=df.to_dict(orient="records"),
columns=df.columns,
))


class PivotTableViz(BaseViz):
viz_type = "pivot_table"
Expand All @@ -301,10 +324,6 @@ class PivotTableViz(BaseViz):
)
},)

@property
def json_endpoint(self):
return self.get_url(async='true', standalone='true', skip_libs='true')

def query_obj(self):
d = super(PivotTableViz, self).query_obj()
groupby = self.form_data.get('groupby')
Expand Down Expand Up @@ -343,6 +362,13 @@ def get_df(self):
)
return df

def get_json_data(self):
return dumps(self.get_df().to_html(
na_rep='',
classes=(
"dataframe table table-striped table-bordered "
"table-condensed table-hover")))


class MarkupViz(BaseViz):
viz_type = "markup"
Expand Down

0 comments on commit 47bb6b6

Please sign in to comment.