diff --git a/TODO.md b/TODO.md
index f6a28803fd80..6bbbfc32a7f9 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,5 +1,8 @@
# TODO
+* in/notin filters autocomplete
+* Highstock, sort legend based on y value: 
* compare time ranges
-* Label
+* Add verbose_name and label method to metrics and columns
* CSV
* Save / bookmark / url shortener
+* on save, process metadata / generate metrics
diff --git a/app.db b/app.db
index f0bf7ecb2de2..5c4db1afcfce 100644
Binary files a/app.db and b/app.db differ
diff --git a/app/highchart.py b/app/highchart.py
new file mode 100644
index 000000000000..00df150106d4
--- /dev/null
+++ b/app/highchart.py
@@ -0,0 +1,158 @@
+import pandas
+import copy
+from pandas.io.json import dumps
+
+
+class Highchart(object):
+ def __init__(
+ self, df,
+ chart_type="spline",
+ target_div="#chart",
+ polar=False,
+ width=None,
+ height=None,
+ show_legend=True,
+ stockchart=False,
+ title=None,
+ tooltip=None,
+ sort_columns=False,
+ secondary_y=None,
+ mark_right=False,
+ compare=False,
+ stacked=False,
+ logx=False,
+ logy=False,
+ xlim=None,
+ ylim=None,
+ grid=False,
+ zoom=None):
+ self.df = df
+ self.chart_type = chart_type
+ self.chart = chart = {}
+ self.stockchart = stockchart
+ self.sort_columns = sort_columns
+ self.secondary_y = secondary_y or []
+ self.mark_right = mark_right
+ self.compare = compare
+ self.logx = logx
+ self.logy = logy
+ self.xlim = xlim
+ self.ylim = ylim
+ self.zoom = zoom
+ self.polar = polar
+ self.grid = grid
+ self.stacked = stacked
+
+ chart['chart'] = {}
+ chart['chart']["type"] = chart_type
+ chart['chart']['renderTo'] = target_div
+
+ if width:
+ chart['chart']["width"] = width
+ if height:
+ chart['chart']["height"] = height
+
+ chart['chart']['polar'] = polar
+
+ chart["legend"] = {
+ "enabled": show_legend
+ }
+ chart["title"] = {"text": title}
+
+ if tooltip:
+ chart['tooltip'] = tooltip
+ if self.zoom:
+ chart["zoomType"] = self.zoom
+
+ self.serialize_series()
+ self.serialize_xaxis()
+ self.serialize_yaxis()
+
+ self.chart = chart
+
+ def serialize_series(self):
+ df = self.df
+ chart = self.chart
+ if self.sort_columns:
+ df = df.sort_index()
+ series = df.to_dict('series')
+ chart["series"] = []
+ for name, data in series.items():
+ if df[name].dtype.kind not in "biufc":
+ continue
+ sec = name in self.secondary_y
+ d = {
+ "name": name if not sec or self.mark_right else name + " (right)",
+ "yAxis": int(sec),
+ "data": zip(df.index, data.tolist())
+ }
+ if self.polar:
+ d['data'] = [v for k, v in d['data']]
+ if self.compare:
+ d['compare'] = self.compare # either `value` or `percent`
+ if self.chart_type in ("area", "column", "bar") and self.stacked:
+ d["stacking"] = 'normal'
+ #if kwargs.get("style"):
+ # d["dashStyle"] = pd2hc_linestyle(kwargs["style"].get(name, "-"))
+ chart["series"].append(d)
+
+ def serialize_xaxis(self):
+ df = self.df
+ x_axis = {}
+ if df.index.name:
+ x_axis["title"] = {"text": df.index.name}
+ if df.index.dtype.kind in "M":
+ x_axis["type"] = "datetime"
+ if df.index.dtype.kind == 'O':
+ x_axis['categories'] = sorted(list(df.index)) if self.sort_columns else list(df.index)
+ print list(df.index)
+ if self.grid:
+ x_axis["gridLineWidth"] = 1
+ x_axis["gridLineDashStyle"] = "Dot"
+ if self.logx:
+ x_axis["type"] = 'logarithmic'
+ if self.xlim:
+ x_axis["min"] = self.xlim[0]
+ x_axis["max"] = self.xlim[1]
+ '''
+ if "rot" in kwargs:
+ x_axis["labels"] = {"rotation": kwargs["rot"]}
+ if "fontsize" in kwargs:
+ x_axis.setdefault("labels", {})["style"] = {"fontSize": kwargs["fontsize"]}
+ if "xticks" in kwargs:
+ x_axis["tickPositions"] = kwargs["xticks"]
+ '''
+ self.chart['xAxis'] = x_axis
+
+ def serialize_yaxis(self):
+ yAxis = {}
+ chart = self.chart
+ if self.grid:
+ yAxis["gridLineWidth"] = 1
+ yAxis["gridLineDashStyle"] = "Dot"
+ if self.logy:
+ yAxis["type"] = 'logarithmic'
+ if self.ylim:
+ yAxis["min"] = self.ylim[0]
+ yAxis["max"] = self.ylim[1]
+ '''
+ if "rot" in kwargs:
+ yAxis["labels"] = {"rotation": kwargs["rot"]}
+ if "fontsize" in kwargs:
+ yAxis.setdefault("labels", {})["style"] = {"fontSize": kwargs["fontsize"]}
+ if "yticks" in kwargs:
+ yAxis["tickPositions"] = kwargs["yticks"]
+ '''
+ chart["yAxis"] = [yAxis]
+ if self.secondary_y:
+ yAxis2 = copy.deepcopy(yAxis)
+ yAxis2["opposite"] = True
+ chart["yAxis"].append(yAxis2)
+
+
+ @property
+ def javascript_cmd(self):
+ js = dumps(self.chart)
+ if self.stockchart:
+ return "new Highcharts.StockChart(%s);" % js
+ return "new Highcharts.Chart(%s);" %js
diff --git a/app/templates/panoramix/viz_highcharts.html b/app/templates/panoramix/viz_highcharts.html
index c3940726b9e2..efb4d19276a7 100644
--- a/app/templates/panoramix/viz_highcharts.html
+++ b/app/templates/panoramix/viz_highcharts.html
@@ -17,7 +17,7 @@
{% block tail %}
{{ super() }}
-{% if viz.chart_type == "stock" %}
+{% if viz.stockchart %}
{% else %}
diff --git a/app/viz.py b/app/viz.py
index f529c2a73fc4..4cc1ccb0a1fc 100644
--- a/app/viz.py
+++ b/app/viz.py
@@ -1,19 +1,18 @@
from pydruid.utils.filters import Dimension, Filter
from datetime import datetime
-from flask import render_template, flash, request
+from flask import flash, request
import pandas as pd
-from pandas_highcharts.core import serialize
-from pydruid.utils import aggregators as agg
from collections import OrderedDict
from app import utils
+from app.highchart import Highchart
from wtforms import Form, SelectMultipleField, SelectField, TextField
import config
CHART_ARGS = {
- 'figsize': (None, 700),
+ 'height': 700,
'title': None,
- 'render_to': 'chart',
+ 'target_div': 'chart',
}
class OmgWtForm(Form):
@@ -220,8 +219,8 @@ class HighchartsViz(BaseViz):
class TimeSeriesViz(HighchartsViz):
verbose_name = "Time Series - Line Chart"
- chart_kind = "spline"
- chart_type = 'stock'
+ chart_type = "spline"
+ stockchart = True
def render(self):
metrics = self.metrics
@@ -237,12 +236,14 @@ def render(self):
if rolling_type == 'mean':
df = pd.rolling_mean(df, int(rolling_periods))
- chart_js = serialize(
- df, kind=self.chart_kind,
- viz=self,
+ chart = Highchart(
+ df,
compare=self.compare,
- chart_type=self.chart_type, stacked=self.stacked, **CHART_ARGS)
- return super(TimeSeriesViz, self).render(chart_js=chart_js)
+ chart_type=self.chart_type,
+ stacked=self.stacked,
+ stockchart=self.stockchart,
+ **CHART_ARGS)
+ return super(TimeSeriesViz, self).render(chart_js=chart.javascript_cmd)
def form_class(self):
return form_factory(self.datasource, request.args,
@@ -297,23 +298,23 @@ class TimeSeriesCompareViz(TimeSeriesViz):
class TimeSeriesAreaViz(TimeSeriesViz):
verbose_name = "Time Series - Stacked Area Chart"
stacked=True
- chart_kind = "area"
+ chart_type = "area"
class TimeSeriesBarViz(TimeSeriesViz):
verbose_name = "Time Series - Bar Chart"
- chart_kind = "bar"
+ chart_type = "column"
class TimeSeriesStackedBarViz(TimeSeriesViz):
verbose_name = "Time Series - Stacked Bar Chart"
- chart_kind = "bar"
+ chart_type = "column"
stacked = True
class DistributionBarViz(HighchartsViz):
verbose_name = "Distribution - Bar Chart"
- chart_kind = "bar"
+ chart_type = "column"
def query_obj(self):
d = super(DistributionBarViz, self).query_obj()
@@ -326,14 +327,15 @@ def render(self):
index=self.groupby,
values=self.metrics)
df = df.sort(self.metrics[0], ascending=False)
- chart_js = serialize(
- df, kind=self.chart_kind, **CHART_ARGS)
- return super(DistributionBarViz, self).render(chart_js=chart_js)
+ chart = Highchart(
+ df, chart_type=self.chart_type, **CHART_ARGS)
+ return super(DistributionBarViz, self).render(
+ chart_js=chart.javascript_cmd)
class DistributionPieViz(HighchartsViz):
verbose_name = "Distribution - Pie Chart"
- chart_kind = "pie"
+ chart_type = "pie"
def query_obj(self):
d = super(DistributionPieViz, self).query_obj()
@@ -346,9 +348,10 @@ def render(self):
index=self.groupby,
values=[self.metrics[0]])
df = df.sort(self.metrics[0], ascending=False)
- chart_js = serialize(
- df, kind=self.chart_kind, **CHART_ARGS)
- return super(DistributionPieViz, self).render(chart_js=chart_js)
+ chart = Highchart(
+ df, chart_type=self.chart_type, **CHART_ARGS)
+ return super(DistributionPieViz, self).render(
+ chart_js=chart.javascript_cmd)
viz_types = OrderedDict([
['table', TableViz],
diff --git a/requirements.txt b/requirements.txt
index 40c6da8e83af..e629f4fbbc02 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,6 @@ flask
flask-alembic
flask-appbuilder
pandas
-pandas-highcharts
parsedatetime
pydruid
python-dateutil