diff --git a/TODO.md b/TODO.md
index 35e5c47bf2..f5b4dab466 100644
--- a/TODO.md
+++ b/TODO.md
@@ -20,6 +20,7 @@ List of TODO items for Panoramix
An example of a layer might be "holidays" or "site outages", ...
* **Worth doing? User defined groups:** People could define mappings in the UI of say "Countries I follow" and apply it to different datasets. For now, this is done by writing CASE-WHEN-type expression which is probably good enough.
+
## Easy-ish fix
* datasource in explore mode could be a dropdown
* Create a set of slices and dashboard on top of the World Bank dataset that ship with load_examples
diff --git a/panoramix/migrations/versions/43df8de3a5f4_dash_json.py b/panoramix/migrations/versions/43df8de3a5f4_dash_json.py
new file mode 100644
index 0000000000..410fc380b7
--- /dev/null
+++ b/panoramix/migrations/versions/43df8de3a5f4_dash_json.py
@@ -0,0 +1,21 @@
+"""empty message
+
+Revision ID: 43df8de3a5f4
+Revises: 7dbf98566af7
+Create Date: 2016-01-18 23:43:16.073483
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '43df8de3a5f4'
+down_revision = '7dbf98566af7'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import mysql
+
+def upgrade():
+ op.add_column('dashboards', sa.Column('json_metadata', sa.Text(), nullable=True))
+
+def downgrade():
+ op.drop_column('dashboards', 'json_metadata')
diff --git a/panoramix/migrations/versions/7dbf98566af7_slice_description.py b/panoramix/migrations/versions/7dbf98566af7_slice_description.py
new file mode 100644
index 0000000000..b29e70d3d4
--- /dev/null
+++ b/panoramix/migrations/versions/7dbf98566af7_slice_description.py
@@ -0,0 +1,21 @@
+"""empty message
+
+Revision ID: 7dbf98566af7
+Revises: 8e80a26a31db
+Create Date: 2016-01-17 22:00:23.640788
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '7dbf98566af7'
+down_revision = '8e80a26a31db'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import mysql
+
+def upgrade():
+ op.add_column('slices', sa.Column('description', sa.Text(), nullable=True))
+
+def downgrade():
+ op.drop_column('slices', 'description')
diff --git a/panoramix/models.py b/panoramix/models.py
index 63e3690ce4..2b46687208 100644
--- a/panoramix/models.py
+++ b/panoramix/models.py
@@ -66,6 +66,7 @@ class Slice(Model, AuditMixinNullable):
datasource_name = Column(String(2000))
viz_type = Column(String(250))
params = Column(Text)
+ description = Column(Text)
table = relationship(
'SqlaTable', foreign_keys=[table_id], backref='slices')
@@ -88,6 +89,10 @@ class Slice(Model, AuditMixinNullable):
form_data=d)
return viz
+ @property
+ def description_markeddown(self):
+ return utils.markdown(self.description)
+
@property
def datasource_id(self):
return self.table_id or self.druid_datasource_id
@@ -154,6 +159,7 @@ class Dashboard(Model, AuditMixinNullable):
position_json = Column(Text)
description = Column(Text)
css = Column(Text)
+ json_metadata = Column(Text)
slug = Column(String(255), unique=True)
slices = relationship(
'Slice', secondary=dashboard_slices, backref='dashboards')
@@ -165,6 +171,10 @@ class Dashboard(Model, AuditMixinNullable):
def url(self):
return "/panoramix/dashboard/{}/".format(self.slug or self.id)
+ @property
+ def metadata_dejson(self):
+ return json.loads(self.json_metadata)
+
def dashboard_link(self):
return '{self.dashboard_title}'.format(self=self)
diff --git a/panoramix/static/panoramix.css b/panoramix/static/panoramix.css
index 2637204370..486d97c7fa 100644
--- a/panoramix/static/panoramix.css
+++ b/panoramix/static/panoramix.css
@@ -11,7 +11,18 @@ html>body{
margin-left: 365px;
}
+.slice_description{
+ padding: 8px;
+ margin: 5px;
+ border: 1px solid #DDD;
+ background-color: #F8F8F8;
+ border-radius: 5px;
+ font-size: 12px;
+}
+.slice_info{
+ cursor: pointer;
+}
.padded{
padding: 10px;
@@ -23,7 +34,7 @@ html>body{
}
.slice_container {
- height: 100%;
+ //height: 100%;
}
.container-fluid {
text-align: left;
diff --git a/panoramix/static/panoramix.js b/panoramix/static/panoramix.js
index df585fae7a..4d6c1a7a7a 100644
--- a/panoramix/static/panoramix.js
+++ b/panoramix/static/panoramix.js
@@ -106,7 +106,13 @@ var px = (function() {
return token.width();
},
height: function(){
- return token.height() - 25;
+ var others = 0;
+ var widget = container.parents('.widget');
+ var slice_description = widget.find('.slice_description');
+ if (slice_description.is(":visible"))
+ others += widget.find('.slice_description').height() + 25;
+ others += widget.find('.slice_header').height();
+ return widget.height() - others;
},
render: function() {
$('.btn-group.results span').attr('disabled','disabled');
@@ -416,9 +422,17 @@ var px = (function() {
}).data('gridster');
$("div.gridster").css('visibility', 'visible');
$("#savedash").click(function() {
+ var expanded_slices = {};
+ $.each($(".slice_info"), function(i, d){
+ var widget = $(this).parents('.widget');
+ var slice_description = widget.find('.slice_description');
+ if(slice_description.is(":visible"))
+ expanded_slices[$(d).attr('slice_id')] = true;
+ });
var data = {
positions: gridster.serialize(),
- css: $("#dash_css").val()
+ css: $("#dash_css").val(),
+ expanded_slices: expanded_slices,
};
$.ajax({
type: "POST",
@@ -432,6 +446,13 @@ var px = (function() {
var li = $(this).parents("li");
gridster.remove_widget(li);
});
+ $(".slice_info").click(function(){
+ var widget = $(this).parents('.widget');
+ var slice_description = widget.find('.slice_description');
+ slice_description.slideToggle(500, function(){
+ widget.find('.refresh').click();
+ });
+ });
$("table.slice_header").mouseover(function() {
$(this).find("td.icons nobr").show();
});
diff --git a/panoramix/static/widgets/viz_nvd3.js b/panoramix/static/widgets/viz_nvd3.js
index b9ebf14bd4..b2aaaeeb18 100644
--- a/panoramix/static/widgets/viz_nvd3.js
+++ b/panoramix/static/widgets/viz_nvd3.js
@@ -6,7 +6,7 @@ function viz_nvd3(slice) {
$.getJSON(slice.jsonEndpoint(), function(payload) {
var fd = payload.form_data;
var viz_type = fd.viz_type;
- var f = d3.format('.4s');
+ var f = d3.format('.3s');
nv.addGraph(function() {
if (viz_type === 'line') {
if (fd.show_brush) {
@@ -49,6 +49,7 @@ function viz_nvd3(slice) {
} else if (viz_type === 'pie') {
chart = nv.models.pieChart()
chart.showLegend(fd.show_legend);
+ chart.valueFormat(f);
if (fd.donut) {
chart.donut(true);
chart.donutLabelsOutside(true);
@@ -99,8 +100,13 @@ function viz_nvd3(slice) {
chart.showLegend(fd.show_legend);
}
- // make space for labels on right
- //chart.height($(".chart").height() - 50).margin({"right": 50});
+ var height = slice.height();
+ if(chart.hasOwnProperty("x2Axis")) {
+ height += 30;
+ }
+ chart.height(height);
+ slice.container.css('height', height + 'px');
+
if ((viz_type === "line" || viz_type === "area") && fd.rich_tooltip) {
chart.useInteractiveGuideline(true);
}
@@ -122,7 +128,9 @@ function viz_nvd3(slice) {
else if (fd.x_axis_format !== undefined) {
chart.xAxis.tickFormat(px.timeFormatFactory(fd.x_axis_format));
}
- if (fd.contribution || fd.num_period_compare) {
+ if (chart.yAxis !== undefined)
+ chart.yAxis.tickFormat(d3.format('.3s'));
+ if (fd.contribution || fd.num_period_compare || viz_type == 'compare') {
chart.yAxis.tickFormat(d3.format('.3p'));
if (chart.y2Axis != undefined) {
chart.y2Axis.tickFormat(d3.format('.3p'));
@@ -140,15 +148,9 @@ function viz_nvd3(slice) {
d3.select(slice.selector).append("svg")
.datum(payload.data)
.transition().duration(500)
+ .attr('height', height)
.call(chart);
- // if it is a two axis chart, rescale it down just a little so it fits in the div.
- if(chart.hasOwnProperty("x2Axis")) {
- two_axis_chart = $(slice.selector + " > svg");
- w = two_axis_chart.width();
- h = two_axis_chart.height();
- two_axis_chart.get(0).setAttribute('viewBox', '0 0 '+w+' '+(h+30));
- }
return chart;
});
slice.done(payload);
diff --git a/panoramix/static/widgets/viz_table.js b/panoramix/static/widgets/viz_table.js
index 650b20ad51..e6b17cd6c2 100644
--- a/panoramix/static/widgets/viz_table.js
+++ b/panoramix/static/widgets/viz_table.js
@@ -79,13 +79,13 @@ px.registerViz('table', function(slice) {
paging: false,
searching: form_data.include_search,
});
- slice.container.find('.tooltip').remove();
// Sorting table by main column
if (form_data.metrics.length > 0) {
var main_metric = form_data.metrics[0];
datatable.column(data.columns.indexOf(main_metric)).order( 'desc' ).draw();
}
slice.done(json);
+ slice.container.parents('.widget').find('.tooltip').remove();
}).fail(function(xhr){
slice.error(xhr.responseText);
});
diff --git a/panoramix/templates/panoramix/dashboard.html b/panoramix/templates/panoramix/dashboard.html
index c0181a4524..bd196c89d3 100644
--- a/panoramix/templates/panoramix/dashboard.html
+++ b/panoramix/templates/panoramix/dashboard.html
@@ -91,25 +91,35 @@ body {