diff --git a/TODO.md b/TODO.md
index 47cb75831e..1ac1a3f078 100644
--- a/TODO.md
+++ b/TODO.md
@@ -2,18 +2,23 @@
List of TODO items for Panoramix
## Improvments
-* [druid] Allow for post aggregations (ratios!)
-* [sql] define column based grouping
* [sql] make "Test Connection" test further
-* csv export out of table view
+* csv export
+* json export (options)
* in/notin filters autocomplete
+* [druid] Allow for post aggregations (ratios!)
## Better Javascript enables
* Async on Druidify! in exploration page
-* Async form reload onchange of viz_type dropdown
-* Reintroduce query and stopwatch
-* Fix resize / refresh
* Configurable widget auto refresh in Dashboard view
## New Features
* Annotations layers
+
+## Low value
+* [sql] define column based grouping
+
+## Community
+* Creat a proper doc
+* Usage vid
+*
diff --git a/panoramix/static/panoramix.css b/panoramix/static/panoramix.css
index 21366799e9..85ce8bece6 100644
--- a/panoramix/static/panoramix.css
+++ b/panoramix/static/panoramix.css
@@ -11,6 +11,15 @@ form div {
color: white;
}
+.header span{
+ margin-left: 3px;
+}
+
+#timer {
+ width: 80px;
+ text-align: right;
+}
+
.notbtn {
cursor: default;
}
diff --git a/panoramix/static/panoramix.js b/panoramix/static/panoramix.js
index f6a868f1ca..9f28e83ed6 100644
--- a/panoramix/static/panoramix.js
+++ b/panoramix/static/panoramix.js
@@ -1,3 +1,4 @@
+var timer;
var px = (function() {
var visualizations = [];
@@ -14,18 +15,43 @@ var px = (function() {
}
function initializeWidget(data) {
+ var token = $('#' + data.token);
var name = data['viz_name'];
var initializer = visualizations[name];
- var widget = initializer ? initializer(data) : makeNullWidget();
+ var user_defined_widget = initializer ? initializer(data) : makeNullWidget();
+ var dttm = 0;
+ var timer;
+ var stopwatch = function () {
+ dttm += 10;
+ $('#timer').text(Math.round(dttm/10)/100 + " sec");
+ }
+ var done = function (data) {
+ clearInterval(timer);
+ token.find("img.loading").hide();
+ if(data !== undefined)
+ $("#query_container").html(data.query);
+ $('#timer').removeClass('btn-warning');
+ $('span.query').removeClass('disabled');
+ $('#timer').addClass('btn-success');
+ }
+ widget = {
+ render: function() {
+ timer = setInterval(stopwatch, 10);
+ user_defined_widget.render(done);
+ },
+ resize: function() {
+ user_defined_widget.resize();
+ },
+ };
return widget;
}
-function initializeDatasourceView() {
- function getParam(name) {
- name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
- var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
- results = regex.exec(location.search);
- return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+ function initializeDatasourceView() {
+ function getParam(name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
+ results = regex.exec(location.search);
+ return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
$(".select2").select2({dropdownAutoWidth : true});
diff --git a/panoramix/static/widgets/viz_bignumber.css b/panoramix/static/widgets/viz_bignumber.css
index 83c6c467e7..872a5715a2 100644
--- a/panoramix/static/widgets/viz_bignumber.css
+++ b/panoramix/static/widgets/viz_bignumber.css
@@ -1,4 +1,4 @@
-.viz_bignumber g.axis text {
+.big_number g.axis text {
font-size: 10px;
font-weight: normal;
color: gray;
@@ -8,18 +8,18 @@
font-weight: none;
}
-.viz_bignumber text.big {
+.big_number text.big {
stroke: black;
text-anchor: middle;
fill: black;
}
-.viz_bignumber g.tick line {
+.big_number g.tick line {
stroke-width: 1px;
stroke: grey;
}
-.viz_bignumber .domain {
+.big_number .domain {
fill: none;
stroke: black;
stroke-width: 1;
diff --git a/panoramix/static/widgets/viz_bignumber.js b/panoramix/static/widgets/viz_bignumber.js
index b59a1cd19e..e8af688d5c 100644
--- a/panoramix/static/widgets/viz_bignumber.js
+++ b/panoramix/static/widgets/viz_bignumber.js
@@ -4,7 +4,7 @@ px.registerWidget('big_number', function(data_attribute) {
var json_callback = data_attribute['json_endpoint'];
var div = d3.select('#' + token_name);
- function render() {
+ function render(done) {
d3.json(json_callback, function(error, payload){
json = payload.data;
div.html("");
@@ -12,6 +12,7 @@ px.registerWidget('big_number', function(data_attribute) {
if (error != null){
var err = '
' + error.responseText + '
';
div.html(err);
+ done(payload);
return '';
}
var color_range = [-1, 1];
@@ -136,6 +137,7 @@ px.registerWidget('big_number', function(data_attribute) {
div.select('g.digits').transition().duration(500).attr('opacity', 1);
div.select('g.axis').transition().duration(500).attr('opacity', 0);
});
+ done(payload);
});
};
diff --git a/panoramix/static/widgets/viz_markup.js b/panoramix/static/widgets/viz_markup.js
new file mode 100644
index 0000000000..be6bef7e7b
--- /dev/null
+++ b/panoramix/static/widgets/viz_markup.js
@@ -0,0 +1,11 @@
+px.registerWidget('markup', function(data_attribute) {
+
+ function refresh(done) {
+ $('#code').attr('rows', '15')
+ done();
+ }
+ return {
+ render: refresh,
+ resize: refresh,
+ };
+});
diff --git a/panoramix/static/widgets/viz_nvd3.js b/panoramix/static/widgets/viz_nvd3.js
index 156f080fe5..3ed37abaab 100644
--- a/panoramix/static/widgets/viz_nvd3.js
+++ b/panoramix/static/widgets/viz_nvd3.js
@@ -3,6 +3,7 @@ function viz_nvd3(data_attribute) {
var token = d3.select('#' + token_name);
var json_callback = data_attribute['json_endpoint'];
var chart = undefined;
+ var data = {};
function UTC(dttm){
return v = new Date(dttm.getUTCFullYear(), dttm.getUTCMonth(), dttm.getUTCDate(), dttm.getUTCHours(), dttm.getUTCMinutes(), dttm.getUTCSeconds());
@@ -27,17 +28,14 @@ function viz_nvd3(data_attribute) {
"#FFAA91", "#B4A76C", "#9CA299", "#565A5C"
];
var jtoken = $('#' + token_name);
- var loading = $('#' + token_name).find("img.loading");
var chart_div = $('#' + token_name).find("div.chart");
- var refresh = function() {
+ var refresh = function(done) {
chart_div.hide();
- loading.show();
$.getJSON(json_callback, function(payload) {
var data = payload.data;
var viz = payload;
var viz_type = viz.form_data.viz_type;
- $("#query_container").html(data.query);
nv.addGraph(function() {
if (viz_type === 'line') {
if (viz.form_data.show_brush) {
@@ -157,10 +155,11 @@ function viz_nvd3(data_attribute) {
return chart;
});
chart_div.show();
- loading.hide();
- }).fail(function(xhr) {
+ done(data);
+ })
+ .fail(function(xhr) {
var err = '' + xhr.responseText + '
';
- loading.hide();
+ done(data);
chart_div.show();
chart_div.html(err);
});
diff --git a/panoramix/static/widgets/viz_pivot_table.css b/panoramix/static/widgets/viz_pivot_table.css
new file mode 100644
index 0000000000..716da7732f
--- /dev/null
+++ b/panoramix/static/widgets/viz_pivot_table.css
@@ -0,0 +1,3 @@
+li.widget.pivot_table div.token {
+ overflow: auto;
+}
diff --git a/panoramix/static/widgets/viz_pivot_table.js b/panoramix/static/widgets/viz_pivot_table.js
new file mode 100644
index 0000000000..dfd5649a6b
--- /dev/null
+++ b/panoramix/static/widgets/viz_pivot_table.js
@@ -0,0 +1,32 @@
+px.registerWidget('pivot_table', function(data_attribute) {
+ var token_name = data_attribute['token'];
+ var token = $('#' + token_name);
+ var form_data = data_attribute.form_data;
+
+ function refresh(done) {
+ token.load(data_attribute.json_endpoint, function(response, status, xhr){
+ if(status=="error"){
+ var err = '' + xhr.responseText + '
';
+ 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();
+ });
+ }
+
+ return {
+ render: refresh,
+ resize: refresh,
+ };
+
+});
diff --git a/panoramix/static/widgets/viz_sunburst.js b/panoramix/static/widgets/viz_sunburst.js
index 9cd27c0421..9aef8335d5 100644
--- a/panoramix/static/widgets/viz_sunburst.js
+++ b/panoramix/static/widgets/viz_sunburst.js
@@ -4,7 +4,7 @@ Modified from http://bl.ocks.org/kerryrodden/7090426
function viz_sunburst(data_attribute) {
var token = d3.select('#' + data_attribute.token);
- var render = function() {
+ var render = function(done) {
// Breadcrumb dimensions: width, height, spacing, width of tip/tail.
var b = {
w: 100, h: 30, s: 3, t: 10
@@ -48,7 +48,7 @@ function viz_sunburst(data_attribute) {
}
var tree = buildHierarchy(json.data);
createVisualization(tree);
- token.select("img.loading").remove();
+ done(json);
});
// Main function to draw and set up the visualization, once we have the data.
diff --git a/panoramix/static/widgets/viz_table.css b/panoramix/static/widgets/viz_table.css
new file mode 100644
index 0000000000..57fefabecf
--- /dev/null
+++ b/panoramix/static/widgets/viz_table.css
@@ -0,0 +1,3 @@
+li.widget.table div.token {
+ overflow: auto;
+}
diff --git a/panoramix/static/widgets/viz_table.js b/panoramix/static/widgets/viz_table.js
new file mode 100644
index 0000000000..2272481b5e
--- /dev/null
+++ b/panoramix/static/widgets/viz_table.js
@@ -0,0 +1,31 @@
+px.registerWidget('table', function(data_attribute) {
+
+ var token_name = data_attribute['token'];
+ var token = $('#' + token_name);
+
+ function refresh(done) {
+ token.load(data_attribute.json_endpoint, function(response, status, xhr){
+ if(status=="error"){
+ var err = '' + xhr.responseText + '
';
+ token.html(err);
+ token.show();
+ done();
+ }
+ else{
+ var table = token.find('table').DataTable({
+ paging: false,
+ searching: false,
+ });
+ table.column('-1').order( 'desc' ).draw();
+ }
+ token.show();
+ done();
+ });
+ }
+
+ return {
+ render: refresh,
+ resize: refresh,
+ };
+
+});
diff --git a/panoramix/static/widgets/viz_wordcloud.js b/panoramix/static/widgets/viz_wordcloud.js
index afb28a41c9..b2b7d4e7bf 100644
--- a/panoramix/static/widgets/viz_wordcloud.js
+++ b/panoramix/static/widgets/viz_wordcloud.js
@@ -4,11 +4,12 @@ px.registerWidget('word_cloud', function(data_attribute) {
var json_callback = data_attribute['json_endpoint'];
var token = d3.select('#' + token_name);
- function refresh() {
+ function refresh(done) {
d3.json(json_callback, function(error, json) {
if (error != null){
var err = '' + error.responseText + '
';
token.html(err);
+ done();
return '';
}
var data = json.data;
@@ -62,6 +63,7 @@ px.registerWidget('word_cloud', function(data_attribute) {
})
.text(function(d) { return d.text; });
}
+ done(data);
});
}
diff --git a/panoramix/templates/panoramix/dashboard.html b/panoramix/templates/panoramix/dashboard.html
index ee1b81648d..e089d8883d 100644
--- a/panoramix/templates/panoramix/dashboard.html
+++ b/panoramix/templates/panoramix/dashboard.html
@@ -109,7 +109,10 @@ body {
- {{ viz_macros.viz_html(viz) }}
+
+
+ {{ viz_macros.viz_html(viz) }}
+
{% endfor %}
diff --git a/panoramix/templates/panoramix/explore.html b/panoramix/templates/panoramix/explore.html
index 1d842e850f..4a11000aef 100644
--- a/panoramix/templates/panoramix/explore.html
+++ b/panoramix/templates/panoramix/explore.html
@@ -33,8 +33,9 @@
{{ form.get_field("viz_type")(class_="select2") }}
- query
+ 0 sec
@@ -129,16 +130,18 @@
{% endblock %}
{% include 'appbuilder/flash.html' %}
diff --git a/panoramix/templates/panoramix/viz_bignumber.html b/panoramix/templates/panoramix/viz_bignumber.html
index b3227bff2d..fe549b71db 100644
--- a/panoramix/templates/panoramix/viz_bignumber.html
+++ b/panoramix/templates/panoramix/viz_bignumber.html
@@ -1,7 +1,5 @@
{% macro viz_html(viz) %}
-
-
-
+
{% endmacro %}
{% macro viz_js(viz) %}
diff --git a/panoramix/templates/panoramix/viz_nvd3.html b/panoramix/templates/panoramix/viz_nvd3.html
index 2f61e1a31b..d1314e32f5 100644
--- a/panoramix/templates/panoramix/viz_nvd3.html
+++ b/panoramix/templates/panoramix/viz_nvd3.html
@@ -1,8 +1,5 @@
{% macro viz_html(viz) %}
-
-
-
{% endmacro %}
{% macro viz_js(viz) %}
diff --git a/panoramix/templates/panoramix/viz_pivot_table.html b/panoramix/templates/panoramix/viz_pivot_table.html
index 357f1d40fd..9fe1398ea1 100644
--- a/panoramix/templates/panoramix/viz_pivot_table.html
+++ b/panoramix/templates/panoramix/viz_pivot_table.html
@@ -1,10 +1,8 @@
{% 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 }}
-
+ {{ viz.get_df().to_html(na_rep='', classes="dataframe table table-striped table-bordered table-condensed")|safe }}
{% else %}
-
-
+
{% endif %}
{% endmacro %}
@@ -12,26 +10,6 @@
{% if viz.form_data.get("async") != "true" %}
{% endif %}
diff --git a/panoramix/templates/panoramix/viz_sunburst.html b/panoramix/templates/panoramix/viz_sunburst.html
index dd55bea59f..5f4ce4ed8a 100644
--- a/panoramix/templates/panoramix/viz_sunburst.html
+++ b/panoramix/templates/panoramix/viz_sunburst.html
@@ -1,9 +1,5 @@
{% macro viz_html(viz) %}
-
-
-
-
-
+
{% endmacro %}
{% macro viz_js(viz) %}
@@ -11,4 +7,3 @@
{% macro viz_css(viz) %}
{% endmacro %}
-
diff --git a/panoramix/templates/panoramix/viz_table.html b/panoramix/templates/panoramix/viz_table.html
index 49cfbcb5dd..ef26a192fa 100644
--- a/panoramix/templates/panoramix/viz_table.html
+++ b/panoramix/templates/panoramix/viz_table.html
@@ -1,61 +1,36 @@
{% macro viz_html(viz) %}
{% if viz.request.args.get("async") == "true" %}
{% set df = viz.get_df() %}
-
-
-
- {% for col in df.columns if not col.endswith('__perc') %}
- {{ col }} |
+
+
+
+
+ {% for col in df.columns if not col.endswith('__perc') %}
+ {{ col }} |
+ {% endfor %}
+
+
+
+ {% for row in df.to_dict(orient="records") %}
+
+ {% for col in df.columns if not col.endswith('__perc') %}
+ {% if col + '__perc' in df.columns %}
+
+ {{ row[col] }}
+ |
+ {% else %}
+ {{ row[col] }} |
+ {% endif %}
+ {% endfor %}
+
{% endfor %}
-
-
-
- {% for row in df.to_dict(orient="records") %}
-
- {% for col in df.columns if not col.endswith('__perc') %}
- {% if col + '__perc' in df.columns %}
-
- {{ row[col] }}
- |
- {% else %}
- {{ row[col] }} |
- {% endif %}
- {% endfor %}
-
- {% endfor %}
-
-
- {% else %}
-
-
+
+
+
{% endif %}
{% endmacro %}
{% macro viz_js(viz) %}
- {% if viz.form_data.get("async") != "true" %}
-
- {% endif %}
{% endmacro %}
{% macro viz_css(viz) %}
diff --git a/panoramix/templates/panoramix/viz_word_cloud.html b/panoramix/templates/panoramix/viz_word_cloud.html
index 48766b29e8..fa8d7a5ff5 100644
--- a/panoramix/templates/panoramix/viz_word_cloud.html
+++ b/panoramix/templates/panoramix/viz_word_cloud.html
@@ -1,7 +1,5 @@
{% macro viz_html(viz) %}
-
-
-
+
{% endmacro %}
{% macro viz_js(viz) %}
diff --git a/panoramix/viz.py b/panoramix/viz.py
index 74a2fa56ef..63274b0a64 100644
--- a/panoramix/viz.py
+++ b/panoramix/viz.py
@@ -65,6 +65,7 @@ class BaseViz(object):
if k in form.data}
defaults.update(data)
self.form_data = defaults
+ self.query = ""
self.form_data['previous_viz_type'] = self.viz_type
self.token = self.form_data.get(
@@ -127,6 +128,7 @@ class BaseViz(object):
self.results = None
self.results = self.datasource.query(**query_obj)
+ self.query = self.results.query
df = self.results.df
if df is None or df.empty:
raise Exception("No data, review your incantations!")
@@ -201,6 +203,7 @@ class BaseViz(object):
def get_json(self):
payload = {
'data': json.loads(self.get_json_data()),
+ 'query': self.query,
'form_data': self.form_data,
}
return json.dumps(payload)
@@ -208,13 +211,18 @@ class BaseViz(object):
def get_json_data(self):
return json.dumps([])
+ @property
+ def json_endpoint(self):
+ return self.get_url(json="true")
+
def get_data_attribute(self):
content = {
'viz_name': self.viz_type,
- 'json_endpoint': self.get_url(json="true"),
+ 'json_endpoint': self.json_endpoint,
'token': self.token,
+ 'form_data': self.form_data,
}
- return json.dumps(content)
+ return dumps(content)
class TableViz(BaseViz):
viz_type = "table"
@@ -234,7 +242,14 @@ class TableViz(BaseViz):
is_timeseries = False
js_files = [
'lib/dataTables/jquery.dataTables.min.js',
- 'lib/dataTables/dataTables.bootstrap.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()
@@ -257,11 +272,14 @@ class PivotTableViz(BaseViz):
viz_type = "pivot_table"
verbose_name = "Pivot Table"
template = 'panoramix/viz_pivot_table.html'
- css_files = ['lib/dataTables/dataTables.bootstrap.css']
+ css_files = [
+ 'lib/dataTables/dataTables.bootstrap.css',
+ 'widgets/viz_pivot_table.css']
is_timeseries = False
js_files = [
'lib/dataTables/jquery.dataTables.min.js',
- 'lib/dataTables/dataTables.bootstrap.js']
+ 'lib/dataTables/dataTables.bootstrap.js',
+ 'widgets/viz_pivot_table.js']
fieldsets = (
{
'label': None,
@@ -275,6 +293,10 @@ 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')
@@ -318,6 +340,7 @@ class MarkupViz(BaseViz):
viz_type = "markup"
verbose_name = "Markup Widget"
template = 'panoramix/viz_markup.html'
+ js_files = ['widgets/viz_markup.js']
fieldsets = (
{
'label': None,