diff --git a/app/templates/panoramix/viz_highcharts.html b/app/templates/panoramix/viz_highcharts.html
index ef3667ace2..c3940726b9 100644
--- a/app/templates/panoramix/viz_highcharts.html
+++ b/app/templates/panoramix/viz_highcharts.html
@@ -3,6 +3,18 @@
{% endblock %}
+{% block extra_fields %}
+ {% if form.compare %}
+
{{ form.compare.label }}: {{ form.compare(class_="form-control") }}
+ {% endif %}
+ {% if form.compare %}
+
+ {{ form.rolling_type.label }}: {{ form.rolling_type(class_="form-control select2") }}
+ {{ form.rolling_periods.label }}: {{ form.rolling_periods(class_="form-control") }}
+
+ {% endif %}
+{% endblock %}
+
{% block tail %}
{{ super() }}
{% if viz.chart_type == "stock" %}
@@ -20,8 +32,6 @@ $( document ).ready(function() {
global: {
useUTC: false
},
-
-
});
$("#viz_type").click(function(){
$("#queryform").submit();
diff --git a/app/views.py b/app/views.py
index 2c7d1c60f6..339f87c314 100644
--- a/app/views.py
+++ b/app/views.py
@@ -7,69 +7,14 @@ from flask.ext.appbuilder.models.sqla.interface import SQLAInterface
from flask.ext.appbuilder import ModelView, CompactCRUDMixin, BaseView, expose
from app import appbuilder, db, models, viz, utils
import config
-from wtforms import Form, SelectMultipleField, SelectField, TextField
from wtforms.fields import Field
-class OmgWtForm(Form):
- field_order = (
- 'viz_type', 'granularity', 'since', 'group_by', 'limit')
- def fields(self):
- fields = []
- for field in self.field_order:
- if hasattr(self, field):
- obj = getattr(self, field)
- if isinstance(obj, Field):
- fields.append(getattr(self, field))
- return fields
-
-
-def form_factory(datasource, form_args=None):
- grain = ['all', 'none', 'minute', 'hour', 'day']
- limits = [0, 5, 10, 25, 50, 100, 500]
-
- if form_args:
- limit = form_args.get("limit")
- try:
- limit = int(limit)
- if limit not in limits:
- limits.append(limit)
- limits = sorted(limits)
- except:
- pass
-
- class QueryForm(OmgWtForm):
- viz_type = SelectField(
- 'Viz',
- choices=[(k, v.verbose_name) for k, v in viz.viz_types.items()])
- metrics = SelectMultipleField('Metrics', choices=datasource.metrics_combo)
- groupby = SelectMultipleField(
- 'Group by', choices=[
- (s, s) for s in datasource.groupby_column_names])
- #granularity = SelectField(
- # 'Time Granularity', choices=[(g, g) for g in grain])
- #since = SelectField(
- # 'Since', choices=[(s, s) for s in utils.since_l.keys()],
- # default="all")
- granularity = TextField('Time Granularity', default="one day")
- since = TextField('Since', default="one day ago")
- until = TextField('Until', default="now")
- limit = SelectField(
- 'Limit', choices=[(s, s) for s in limits])
- for i in range(10):
- setattr(QueryForm, 'flt_col_' + str(i), SelectField(
- 'Filter 1', choices=[(s, s) for s in datasource.filterable_column_names]))
- setattr(QueryForm, 'flt_op_' + str(i), SelectField(
- 'Filter 1', choices=[(m, m) for m in ['in', 'not in']]))
- setattr(QueryForm, 'flt_eq_' + str(i), TextField("Super"))
- return QueryForm
-
-
class ColumnInlineView(CompactCRUDMixin, ModelView):
datamodel = SQLAInterface(models.Column)
edit_columns = [
- 'column_name', 'datasource', 'groupby', 'count_distinct',
- 'sum', 'min', 'max']
+ 'column_name', 'description', 'datasource', 'groupby',
+ 'count_distinct', 'sum', 'min', 'max']
list_columns = [
'column_name', 'type', 'groupby', 'count_distinct',
'sum', 'min', 'max']
@@ -81,7 +26,8 @@ class MetricInlineView(CompactCRUDMixin, ModelView):
datamodel = SQLAInterface(models.Metric)
list_columns = ['metric_name', 'verbose_name', 'metric_type' ]
edit_columns = [
- 'metric_name', 'verbose_name', 'metric_type', 'datasource', 'json']
+ 'metric_name', 'description', 'verbose_name', 'metric_type',
+ 'datasource', 'json']
add_columns = [
'metric_name', 'verbose_name', 'metric_type', 'datasource', 'json']
appbuilder.add_view_no_menu(MetricInlineView)
@@ -89,12 +35,13 @@ appbuilder.add_view_no_menu(MetricInlineView)
class DatasourceModelView(ModelView):
datamodel = SQLAInterface(models.Datasource)
- list_columns = ['datasource_link', 'is_featured', 'is_hidden']
+ list_columns = ['datasource_link', 'owner', 'is_featured', 'is_hidden']
related_views = [ColumnInlineView, MetricInlineView]
edit_columns = [
- 'datasource_name', 'description', 'is_featured', 'is_hidden',
+ 'datasource_name', 'description', 'owner', 'is_featured', 'is_hidden',
'default_endpoint']
page_size = 100
+ order_columns = ['datasource_name']
appbuilder.add_view(
@@ -121,7 +68,6 @@ class Panoramix(BaseView):
viz_type = "table"
obj = viz.viz_types[viz_type](
datasource,
- form_class=form_factory(datasource, request.args),
form_data=request.args, view=self)
if request.args.get("json"):
return Response(
diff --git a/app/viz.py b/app/viz.py
index 215708c40c..ae3c5ca648 100644
--- a/app/viz.py
+++ b/app/viz.py
@@ -1,11 +1,12 @@
from pydruid.utils.filters import Dimension, Filter
from datetime import datetime
-from flask import render_template, flash
+from flask import render_template, 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 wtforms import Form, SelectMultipleField, SelectField, TextField
import config
@@ -15,13 +16,63 @@ CHART_ARGS = {
'render_to': 'chart',
}
+class OmgWtForm(Form):
+ field_order = (
+ 'viz_type', 'granularity', 'since', 'group_by', 'limit')
+ def fields(self):
+ fields = []
+ for field in self.field_order:
+ if hasattr(self, field):
+ obj = getattr(self, field)
+ if isinstance(obj, Field):
+ fields.append(getattr(self, field))
+ return fields
+
+
+def form_factory(datasource, form_args=None, extra_fields_dict=None):
+ extra_fields_dict = extra_fields_dict or {}
+ limits = [0, 5, 10, 25, 50, 100, 500]
+
+ if form_args:
+ limit = form_args.get("limit")
+ try:
+ limit = int(limit)
+ if limit not in limits:
+ limits.append(limit)
+ limits = sorted(limits)
+ except:
+ pass
+
+ class QueryForm(OmgWtForm):
+ viz_type = SelectField(
+ 'Viz',
+ choices=[(k, v.verbose_name) for k, v in viz_types.items()])
+ metrics = SelectMultipleField('Metrics', choices=datasource.metrics_combo)
+ groupby = SelectMultipleField(
+ 'Group by', choices=[
+ (s, s) for s in datasource.groupby_column_names])
+ granularity = TextField('Time Granularity', default="one day")
+ since = TextField('Since', default="one day ago")
+ until = TextField('Until', default="now")
+ limit = SelectField(
+ 'Limit', choices=[(s, s) for s in limits])
+ for i in range(10):
+ setattr(QueryForm, 'flt_col_' + str(i), SelectField(
+ 'Filter 1', choices=[(s, s) for s in datasource.filterable_column_names]))
+ setattr(QueryForm, 'flt_op_' + str(i), SelectField(
+ 'Filter 1', choices=[(m, m) for m in ['in', 'not in']]))
+ setattr(QueryForm, 'flt_eq_' + str(i), TextField("Super"))
+ for k, v in extra_fields_dict.items():
+ setattr(QueryForm, k, v)
+ return QueryForm
+
class BaseViz(object):
verbose_name = "Base Viz"
template = "panoramix/datasource.html"
- def __init__(self, datasource, form_class, form_data, view):
+ def __init__(self, datasource, form_data, view):
self.datasource = datasource
- self.form_class = form_class
+ self.form_class = self.form_class()
self.form_data = form_data
self.metrics = form_data.getlist('metrics') or ['count']
self.groupby = form_data.getlist('groupby') or []
@@ -33,6 +84,9 @@ class BaseViz(object):
self.df_prep()
self.form_prep()
+ def form_class(self):
+ return form_factory(self.datasource, request.args)
+
def query_filters(self):
args = self.form_data
# Building filters
@@ -177,6 +231,12 @@ class TimeSeriesViz(HighchartsViz):
columns=self.groupby,
values=metrics)
+ rolling_periods = request.args.get("rolling_periods")
+ rolling_type = request.args.get("rolling_type")
+ if rolling_periods and rolling_type:
+ if rolling_type == 'mean':
+ df = pd.rolling_mean(df, int(rolling_periods))
+
chart_js = serialize(
df, kind=self.chart_kind,
viz=self,
@@ -184,6 +244,16 @@ class TimeSeriesViz(HighchartsViz):
chart_type=self.chart_type, stacked=self.stacked, **CHART_ARGS)
return super(TimeSeriesViz, self).render(chart_js=chart_js)
+ def form_class(self):
+ return form_factory(self.datasource, request.args,
+ extra_fields_dict={
+ 'compare': TextField('Period Compare',),
+ 'rolling_type': SelectField(
+ 'Rolling',
+ choices=[(s, s) for s in ['mean', 'sum', 'std']]),
+ 'rolling_periods': TextField('Periods',),
+ })
+
def bake_query(self):
"""
Doing a 2 phase query where we limit the number of series.
diff --git a/config.py b/config.py
index 2470c5ab7c..72af1a5586 100644
--- a/config.py
+++ b/config.py
@@ -2,16 +2,23 @@ import os
from flask_appbuilder.security.manager import AUTH_OID, AUTH_REMOTE_USER, AUTH_DB, AUTH_LDAP, AUTH_OAUTH
basedir = os.path.abspath(os.path.dirname(__file__))
+"""
+All configuration in this file can be overridden by providing a local_config
+in your PYTHONPATH.
+
+There' a ``from local_config import *`` at the end of this file.
+"""
+
#---------------------------------------------------------
# Panoramix specifix config
#---------------------------------------------------------
ROW_LIMIT = 5000
-DRUID_HOST = '10.181.47.80'
+DRUID_HOST = '0.0.0.0'
DRUID_PORT = 8080
DRUID_BASE_ENDPOINT = 'druid/v2'
-COORDINATOR_HOST = '10.168.176.249'
+COORDINATOR_HOST = '0.0.0.0'
COORDINATOR_PORT = '8080'
COORDINATOR_BASE_ENDPOINT = 'druid/coordinator/v1'
#---------------------------------------------------------
@@ -118,3 +125,7 @@ IMG_UPLOAD_URL = '/static/uploads/'
#APP_THEME = "united.css"
#APP_THEME = "yeti.css"
+try:
+ from local_config import *
+except:
+ pass
diff --git a/requirements.txt b/requirements.txt
index c7c6de1f0c..e9ebcc9853 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
+flask-alembic
pydruid
parsedatetime
python-dateutil