mirror of https://github.com/apache/superset.git
Improvements
This commit is contained in:
parent
bd1d8eb242
commit
c6dca0f27d
|
@ -1,3 +1,4 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
*.db
|
*.db
|
||||||
tmp
|
tmp
|
||||||
|
local_config
|
||||||
|
|
7
TODO.md
7
TODO.md
|
@ -1,8 +1,5 @@
|
||||||
# TODO
|
# TODO
|
||||||
* STOCK CHART + compare time ranges
|
* compare time ranges
|
||||||
* Save a chart
|
|
||||||
* Datasource + Owner
|
|
||||||
* Column description
|
|
||||||
* Label
|
* Label
|
||||||
* CSV
|
* CSV
|
||||||
* Bookmarks / url shortener
|
* Save / bookmark / url shortener
|
||||||
|
|
|
@ -19,20 +19,5 @@ class MyIndexView(IndexView):
|
||||||
appbuilder = AppBuilder(
|
appbuilder = AppBuilder(
|
||||||
app, db.session, base_template='panoramix/base.html',
|
app, db.session, base_template='panoramix/base.html',
|
||||||
indexview=MyIndexView)
|
indexview=MyIndexView)
|
||||||
#appbuilder.app_name = 'Panoramix'
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
from sqlalchemy.engine import Engine
|
|
||||||
from sqlalchemy import event
|
|
||||||
|
|
||||||
#Only include this for SQLLite constraints
|
|
||||||
@event.listens_for(Engine, "connect")
|
|
||||||
def set_sqlite_pragma(dbapi_connection, connection_record):
|
|
||||||
# Will force sqllite contraint foreign keys
|
|
||||||
cursor = dbapi_connection.cursor()
|
|
||||||
cursor.execute("PRAGMA foreign_keys=ON")
|
|
||||||
cursor.close()
|
|
||||||
"""
|
|
||||||
|
|
||||||
from app import views
|
from app import views
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from flask.ext.appbuilder import Model
|
from flask.ext.appbuilder import Model
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from flask.ext.appbuilder.models.mixins import AuditMixin, FileColumn, ImageColumn
|
from flask.ext.appbuilder.models.mixins import AuditMixin, FileColumn, ImageColumn
|
||||||
|
from flask.ext.appbuilder.security.sqla.models import User
|
||||||
from sqlalchemy import Column, Integer, String, ForeignKey, Text, Boolean
|
from sqlalchemy import Column, Integer, String, ForeignKey, Text, Boolean
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from app import db, utils
|
from app import db, utils
|
||||||
|
@ -18,6 +19,9 @@ class Datasource(Model, AuditMixin):
|
||||||
is_hidden = Column(Boolean, default=False)
|
is_hidden = Column(Boolean, default=False)
|
||||||
description = Column(Text)
|
description = Column(Text)
|
||||||
default_endpoint = Column(Text)
|
default_endpoint = Column(Text)
|
||||||
|
user_id = Column(Integer,
|
||||||
|
ForeignKey('ab_user.id'))
|
||||||
|
owner = relationship('User', backref='datasources', foreign_keys=[user_id])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metrics_combo(self):
|
def metrics_combo(self):
|
||||||
|
@ -111,6 +115,7 @@ class Metric(Model):
|
||||||
ForeignKey('datasources.datasource_name'))
|
ForeignKey('datasources.datasource_name'))
|
||||||
datasource = relationship('Datasource', backref='metrics')
|
datasource = relationship('Datasource', backref='metrics')
|
||||||
json = Column(Text)
|
json = Column(Text)
|
||||||
|
description = Column(Text)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def json_obj(self):
|
def json_obj(self):
|
||||||
|
@ -132,6 +137,7 @@ class Column(Model, AuditMixin):
|
||||||
max = Column(Boolean, default=False)
|
max = Column(Boolean, default=False)
|
||||||
min = Column(Boolean, default=False)
|
min = Column(Boolean, default=False)
|
||||||
filterable = Column(Boolean, default=False)
|
filterable = Column(Boolean, default=False)
|
||||||
|
description = Column(Text)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.column_name
|
return self.column_name
|
||||||
|
@ -150,8 +156,6 @@ class Column(Model, AuditMixin):
|
||||||
json=json.dumps({
|
json=json.dumps({
|
||||||
'type': 'count', 'name': 'count'})
|
'type': 'count', 'name': 'count'})
|
||||||
))
|
))
|
||||||
if self.datasource.datasource_name == 'platform' and self.column_name=='subject_id':
|
|
||||||
print((self.column_name, self.type, self.isnum))
|
|
||||||
|
|
||||||
if self.sum and self.isnum:
|
if self.sum and self.isnum:
|
||||||
mt = self.type.lower() + 'Sum'
|
mt = self.type.lower() + 'Sum'
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</header>
|
</header>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% block messages %}
|
{% block messages %}
|
||||||
{% include 'appbuilder/flash.html' %}
|
{% include 'appbuilder/flash.html' %}
|
||||||
|
@ -20,6 +20,10 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="container-fluid">
|
||||||
|
{% block content_fluid %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% block footer %}
|
{% block footer %}
|
||||||
<footer>
|
<footer>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{% extends "appbuilder/base.html" %}
|
{% extends "appbuilder/base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="jumbotron">
|
<div class="container">
|
||||||
<div class="container">
|
<div class="jumbotron">
|
||||||
<h1>Panoramix</h1>
|
<h1>Panoramix</h1>
|
||||||
<p>Panoramix is an interactive visualization platform built on top of Druid.io</p>
|
<p>Panoramix is an interactive visualization platform built on top of Druid.io</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<img width="250" src="/static/tux_panoramix.png">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
|
||||||
<img width="250" src="/static/tux_panoramix.png">
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -13,5 +13,8 @@
|
||||||
box-shadow: 0px 3px 3px #AAA;
|
box-shadow: 0px 3px 3px #AAA;
|
||||||
z-index:999;
|
z-index:999;
|
||||||
}
|
}
|
||||||
|
.panel.panel-primary {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -10,12 +10,15 @@
|
||||||
padding-right:0;
|
padding-right:0;
|
||||||
padding-left:0;
|
padding-left:0;
|
||||||
}
|
}
|
||||||
form div.select2-container.form-control {
|
form div.form-control {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px !important;
|
||||||
|
}
|
||||||
|
form input.form-control {
|
||||||
|
margin-bottom: 5px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content_fluid %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -36,6 +39,7 @@ form div.select2-container.form-control {
|
||||||
</div>
|
</div>
|
||||||
<div>{{ form.groupby.label }}: {{ form.groupby(class_="form-control select2") }}</div>
|
<div>{{ form.groupby.label }}: {{ form.groupby(class_="form-control select2") }}</div>
|
||||||
<div>{{ form.limit.label }}: {{ form.limit(class_="form-control select2") }}</div>
|
<div>{{ form.limit.label }}: {{ form.limit(class_="form-control select2") }}</div>
|
||||||
|
{% block extra_fields %}{% endblock %}
|
||||||
<hr>
|
<hr>
|
||||||
<h4>Filters</h4>
|
<h4>Filters</h4>
|
||||||
<div id="flt0" style="display: none;">
|
<div id="flt0" style="display: none;">
|
||||||
|
|
|
@ -3,6 +3,18 @@
|
||||||
<div id="chart"></div>
|
<div id="chart"></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_fields %}
|
||||||
|
{% if form.compare %}
|
||||||
|
<div>{{ form.compare.label }}: {{ form.compare(class_="form-control") }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if form.compare %}
|
||||||
|
<div class="row">
|
||||||
|
<span class="col col-sm-5">{{ form.rolling_type.label }}: {{ form.rolling_type(class_="form-control select2") }}</span>
|
||||||
|
<span class="col col-sm-4">{{ form.rolling_periods.label }}: {{ form.rolling_periods(class_="form-control") }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block tail %}
|
{% block tail %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% if viz.chart_type == "stock" %}
|
{% if viz.chart_type == "stock" %}
|
||||||
|
@ -20,8 +32,6 @@ $( document ).ready(function() {
|
||||||
global: {
|
global: {
|
||||||
useUTC: false
|
useUTC: false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
$("#viz_type").click(function(){
|
$("#viz_type").click(function(){
|
||||||
$("#queryform").submit();
|
$("#queryform").submit();
|
||||||
|
|
68
app/views.py
68
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 flask.ext.appbuilder import ModelView, CompactCRUDMixin, BaseView, expose
|
||||||
from app import appbuilder, db, models, viz, utils
|
from app import appbuilder, db, models, viz, utils
|
||||||
import config
|
import config
|
||||||
from wtforms import Form, SelectMultipleField, SelectField, TextField
|
|
||||||
from wtforms.fields import Field
|
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):
|
class ColumnInlineView(CompactCRUDMixin, ModelView):
|
||||||
datamodel = SQLAInterface(models.Column)
|
datamodel = SQLAInterface(models.Column)
|
||||||
edit_columns = [
|
edit_columns = [
|
||||||
'column_name', 'datasource', 'groupby', 'count_distinct',
|
'column_name', 'description', 'datasource', 'groupby',
|
||||||
'sum', 'min', 'max']
|
'count_distinct', 'sum', 'min', 'max']
|
||||||
list_columns = [
|
list_columns = [
|
||||||
'column_name', 'type', 'groupby', 'count_distinct',
|
'column_name', 'type', 'groupby', 'count_distinct',
|
||||||
'sum', 'min', 'max']
|
'sum', 'min', 'max']
|
||||||
|
@ -81,7 +26,8 @@ class MetricInlineView(CompactCRUDMixin, ModelView):
|
||||||
datamodel = SQLAInterface(models.Metric)
|
datamodel = SQLAInterface(models.Metric)
|
||||||
list_columns = ['metric_name', 'verbose_name', 'metric_type' ]
|
list_columns = ['metric_name', 'verbose_name', 'metric_type' ]
|
||||||
edit_columns = [
|
edit_columns = [
|
||||||
'metric_name', 'verbose_name', 'metric_type', 'datasource', 'json']
|
'metric_name', 'description', 'verbose_name', 'metric_type',
|
||||||
|
'datasource', 'json']
|
||||||
add_columns = [
|
add_columns = [
|
||||||
'metric_name', 'verbose_name', 'metric_type', 'datasource', 'json']
|
'metric_name', 'verbose_name', 'metric_type', 'datasource', 'json']
|
||||||
appbuilder.add_view_no_menu(MetricInlineView)
|
appbuilder.add_view_no_menu(MetricInlineView)
|
||||||
|
@ -89,12 +35,13 @@ appbuilder.add_view_no_menu(MetricInlineView)
|
||||||
|
|
||||||
class DatasourceModelView(ModelView):
|
class DatasourceModelView(ModelView):
|
||||||
datamodel = SQLAInterface(models.Datasource)
|
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]
|
related_views = [ColumnInlineView, MetricInlineView]
|
||||||
edit_columns = [
|
edit_columns = [
|
||||||
'datasource_name', 'description', 'is_featured', 'is_hidden',
|
'datasource_name', 'description', 'owner', 'is_featured', 'is_hidden',
|
||||||
'default_endpoint']
|
'default_endpoint']
|
||||||
page_size = 100
|
page_size = 100
|
||||||
|
order_columns = ['datasource_name']
|
||||||
|
|
||||||
|
|
||||||
appbuilder.add_view(
|
appbuilder.add_view(
|
||||||
|
@ -121,7 +68,6 @@ class Panoramix(BaseView):
|
||||||
viz_type = "table"
|
viz_type = "table"
|
||||||
obj = viz.viz_types[viz_type](
|
obj = viz.viz_types[viz_type](
|
||||||
datasource,
|
datasource,
|
||||||
form_class=form_factory(datasource, request.args),
|
|
||||||
form_data=request.args, view=self)
|
form_data=request.args, view=self)
|
||||||
if request.args.get("json"):
|
if request.args.get("json"):
|
||||||
return Response(
|
return Response(
|
||||||
|
|
76
app/viz.py
76
app/viz.py
|
@ -1,11 +1,12 @@
|
||||||
from pydruid.utils.filters import Dimension, Filter
|
from pydruid.utils.filters import Dimension, Filter
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flask import render_template, flash
|
from flask import render_template, flash, request
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from pandas_highcharts.core import serialize
|
from pandas_highcharts.core import serialize
|
||||||
from pydruid.utils import aggregators as agg
|
from pydruid.utils import aggregators as agg
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from app import utils
|
from app import utils
|
||||||
|
from wtforms import Form, SelectMultipleField, SelectField, TextField
|
||||||
import config
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,13 +16,63 @@ CHART_ARGS = {
|
||||||
'render_to': 'chart',
|
'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):
|
class BaseViz(object):
|
||||||
verbose_name = "Base Viz"
|
verbose_name = "Base Viz"
|
||||||
template = "panoramix/datasource.html"
|
template = "panoramix/datasource.html"
|
||||||
def __init__(self, datasource, form_class, form_data, view):
|
def __init__(self, datasource, form_data, view):
|
||||||
self.datasource = datasource
|
self.datasource = datasource
|
||||||
self.form_class = form_class
|
self.form_class = self.form_class()
|
||||||
self.form_data = form_data
|
self.form_data = form_data
|
||||||
self.metrics = form_data.getlist('metrics') or ['count']
|
self.metrics = form_data.getlist('metrics') or ['count']
|
||||||
self.groupby = form_data.getlist('groupby') or []
|
self.groupby = form_data.getlist('groupby') or []
|
||||||
|
@ -33,6 +84,9 @@ class BaseViz(object):
|
||||||
self.df_prep()
|
self.df_prep()
|
||||||
self.form_prep()
|
self.form_prep()
|
||||||
|
|
||||||
|
def form_class(self):
|
||||||
|
return form_factory(self.datasource, request.args)
|
||||||
|
|
||||||
def query_filters(self):
|
def query_filters(self):
|
||||||
args = self.form_data
|
args = self.form_data
|
||||||
# Building filters
|
# Building filters
|
||||||
|
@ -177,6 +231,12 @@ class TimeSeriesViz(HighchartsViz):
|
||||||
columns=self.groupby,
|
columns=self.groupby,
|
||||||
values=metrics)
|
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(
|
chart_js = serialize(
|
||||||
df, kind=self.chart_kind,
|
df, kind=self.chart_kind,
|
||||||
viz=self,
|
viz=self,
|
||||||
|
@ -184,6 +244,16 @@ class TimeSeriesViz(HighchartsViz):
|
||||||
chart_type=self.chart_type, stacked=self.stacked, **CHART_ARGS)
|
chart_type=self.chart_type, stacked=self.stacked, **CHART_ARGS)
|
||||||
return super(TimeSeriesViz, self).render(chart_js=chart_js)
|
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):
|
def bake_query(self):
|
||||||
"""
|
"""
|
||||||
Doing a 2 phase query where we limit the number of series.
|
Doing a 2 phase query where we limit the number of series.
|
||||||
|
|
15
config.py
15
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
|
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__))
|
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
|
# Panoramix specifix config
|
||||||
#---------------------------------------------------------
|
#---------------------------------------------------------
|
||||||
ROW_LIMIT = 5000
|
ROW_LIMIT = 5000
|
||||||
|
|
||||||
DRUID_HOST = '10.181.47.80'
|
DRUID_HOST = '0.0.0.0'
|
||||||
DRUID_PORT = 8080
|
DRUID_PORT = 8080
|
||||||
DRUID_BASE_ENDPOINT = 'druid/v2'
|
DRUID_BASE_ENDPOINT = 'druid/v2'
|
||||||
|
|
||||||
COORDINATOR_HOST = '10.168.176.249'
|
COORDINATOR_HOST = '0.0.0.0'
|
||||||
COORDINATOR_PORT = '8080'
|
COORDINATOR_PORT = '8080'
|
||||||
COORDINATOR_BASE_ENDPOINT = 'druid/coordinator/v1'
|
COORDINATOR_BASE_ENDPOINT = 'druid/coordinator/v1'
|
||||||
#---------------------------------------------------------
|
#---------------------------------------------------------
|
||||||
|
@ -118,3 +125,7 @@ IMG_UPLOAD_URL = '/static/uploads/'
|
||||||
#APP_THEME = "united.css"
|
#APP_THEME = "united.css"
|
||||||
#APP_THEME = "yeti.css"
|
#APP_THEME = "yeti.css"
|
||||||
|
|
||||||
|
try:
|
||||||
|
from local_config import *
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
flask-alembic
|
||||||
pydruid
|
pydruid
|
||||||
parsedatetime
|
parsedatetime
|
||||||
python-dateutil
|
python-dateutil
|
||||||
|
|
Loading…
Reference in New Issue