superset/panoramix/views.py

331 lines
9.7 KiB
Python
Raw Normal View History

from datetime import datetime
2015-07-20 19:29:16 -04:00
import json
2015-09-05 12:23:46 -04:00
import logging
2015-07-16 20:55:36 -04:00
2015-07-20 19:29:16 -04:00
from flask import request, redirect, flash, Response
2015-07-15 13:12:32 -04:00
from flask.ext.appbuilder.models.sqla.interface import SQLAInterface
from flask.ext.appbuilder import ModelView, CompactCRUDMixin, BaseView, expose
2015-09-05 12:23:46 -04:00
from flask.ext.appbuilder.security.decorators import has_access
2015-07-27 01:25:32 -04:00
from pydruid.client import doublesum
from wtforms.validators import ValidationError
2015-07-29 17:48:57 -04:00
from flask.ext.appbuilder.actions import action
2015-09-05 12:23:46 -04:00
from panoramix import appbuilder, db, models, viz, utils, app
2015-09-09 13:37:59 -04:00
def validate_json(form, field):
try:
json.loads(field.data)
except Exception as e:
2015-09-09 13:37:59 -04:00
logging.exception(e)
raise ValidationError("Json isn't valid")
2015-07-15 13:12:32 -04:00
2015-09-09 13:37:59 -04:00
2015-07-29 17:48:57 -04:00
class DeleteMixin(object):
2015-09-09 13:37:59 -04:00
@action(
"muldelete", "Delete", "Delete all Really?", "fa-trash", single=False)
2015-07-29 17:48:57 -04:00
def muldelete(self, items):
self.datamodel.delete_all(items)
self.update_redirect()
return redirect(self.get_redirect())
2015-08-03 11:34:58 -04:00
class TableColumnInlineView(CompactCRUDMixin, ModelView):
datamodel = SQLAInterface(models.TableColumn)
can_delete = False
edit_columns = [
'column_name', 'description', 'groupby', 'filterable', 'table',
2015-08-03 11:34:58 -04:00
'count_distinct', 'sum', 'min', 'max']
list_columns = [
2015-08-11 00:12:21 -04:00
'column_name', 'type', 'groupby', 'filterable', 'count_distinct',
2015-08-03 11:34:58 -04:00
'sum', 'min', 'max']
page_size = 100
appbuilder.add_view_no_menu(TableColumnInlineView)
2015-07-15 13:12:32 -04:00
class ColumnInlineView(CompactCRUDMixin, ModelView):
datamodel = SQLAInterface(models.Column)
2015-07-15 20:38:03 -04:00
edit_columns = [
2015-07-21 14:56:05 -04:00
'column_name', 'description', 'datasource', 'groupby',
'count_distinct', 'sum', 'min', 'max']
2015-07-15 20:38:03 -04:00
list_columns = [
2015-08-11 00:12:21 -04:00
'column_name', 'type', 'groupby', 'filterable', 'count_distinct',
2015-07-15 20:38:03 -04:00
'sum', 'min', 'max']
2015-07-15 13:12:32 -04:00
can_delete = False
2015-07-22 19:21:46 -04:00
page_size = 100
def post_update(self, col):
col.generate_metrics()
2015-07-15 13:12:32 -04:00
appbuilder.add_view_no_menu(ColumnInlineView)
2015-09-09 13:37:59 -04:00
2015-08-05 02:41:00 -04:00
class SqlMetricInlineView(CompactCRUDMixin, ModelView):
datamodel = SQLAInterface(models.SqlMetric)
2015-09-09 13:37:59 -04:00
list_columns = ['metric_name', 'verbose_name', 'metric_type']
2015-08-05 02:41:00 -04:00
edit_columns = [
'metric_name', 'description', 'verbose_name', 'metric_type',
2015-09-09 13:37:59 -04:00
'expression', 'table']
2015-08-05 02:41:00 -04:00
add_columns = edit_columns
page_size = 100
appbuilder.add_view_no_menu(SqlMetricInlineView)
2015-07-15 20:38:03 -04:00
class MetricInlineView(CompactCRUDMixin, ModelView):
datamodel = SQLAInterface(models.Metric)
2015-09-09 13:37:59 -04:00
list_columns = ['metric_name', 'verbose_name', 'metric_type']
2015-07-15 20:38:03 -04:00
edit_columns = [
2015-07-21 14:56:05 -04:00
'metric_name', 'description', 'verbose_name', 'metric_type',
'datasource', 'json']
2015-07-15 20:38:03 -04:00
add_columns = [
'metric_name', 'verbose_name', 'metric_type', 'datasource', 'json']
2015-07-22 19:21:46 -04:00
page_size = 100
validators_columns = {
'json': [validate_json],
}
2015-07-15 20:38:03 -04:00
appbuilder.add_view_no_menu(MetricInlineView)
2015-07-15 13:12:32 -04:00
class ClusterModelView(ModelView, DeleteMixin):
datamodel = SQLAInterface(models.Cluster)
add_columns = [
'cluster_name',
'coordinator_host', 'coordinator_port', 'coordinator_endpoint',
'broker_host', 'broker_port', 'broker_endpoint',
]
edit_columns = add_columns
list_columns = ['cluster_name', 'metadata_last_refreshed']
appbuilder.add_view(
ClusterModelView,
2015-08-01 20:16:39 -04:00
"Druid Clusters",
icon="fa-server",
category="Admin",
2015-08-01 20:16:39 -04:00
category_icon='fa-cogs',)
2015-09-12 00:49:04 -04:00
class SliceModelView(ModelView, DeleteMixin):
datamodel = SQLAInterface(models.Slice)
2015-09-13 22:07:54 -04:00
list_columns = ['slice_link', 'viz_type', 'datasource', 'created_by']
2015-09-12 00:49:04 -04:00
appbuilder.add_view(
SliceModelView,
"Slices",
icon="fa-bar-chart",
category="",
category_icon='',)
2015-09-13 22:07:54 -04:00
class DashboardModelView(ModelView, DeleteMixin):
datamodel = SQLAInterface(models.Dashboard)
list_columns = ['dashboard_link', 'created_by']
edit_columns = ['dashboard_title', 'slices',]
add_columns = edit_columns
appbuilder.add_view(
DashboardModelView,
"Dashboards",
icon="fa-dashboard",
category="",
category_icon='',)
2015-08-03 11:34:58 -04:00
class DatabaseView(ModelView, DeleteMixin):
datamodel = SQLAInterface(models.Database)
list_columns = ['database_name']
add_columns = ['database_name', 'sqlalchemy_uri']
edit_columns = add_columns
appbuilder.add_view(
DatabaseView,
"Databases",
icon="fa-database",
category="Admin",
category_icon='fa-cogs',)
class TableView(ModelView, DeleteMixin):
datamodel = SQLAInterface(models.Table)
list_columns = ['table_link', 'database']
2015-08-06 01:42:42 -04:00
add_columns = ['table_name', 'database', 'default_endpoint']
2015-09-09 13:37:59 -04:00
edit_columns = [
'table_name', 'database', 'main_datetime_column', 'default_endpoint']
2015-08-05 02:41:00 -04:00
related_views = [TableColumnInlineView, SqlMetricInlineView]
2015-08-03 11:34:58 -04:00
def post_add(self, table):
2015-08-03 11:34:58 -04:00
table.fetch_metadata()
def post_update(self, table):
table.fetch_metadata()
appbuilder.add_view(
TableView,
"Tables",
icon='fa-table',)
2015-07-29 17:48:57 -04:00
class DatasourceModelView(ModelView, DeleteMixin):
2015-07-15 13:12:32 -04:00
datamodel = SQLAInterface(models.Datasource)
list_columns = [
'datasource_link', 'cluster', 'owner', 'is_featured', 'is_hidden']
2015-07-15 20:38:03 -04:00
related_views = [ColumnInlineView, MetricInlineView]
edit_columns = [
'datasource_name', 'cluster', 'description', 'owner',
'is_featured', 'is_hidden', 'default_endpoint']
2015-07-15 13:12:32 -04:00
page_size = 100
2015-07-27 01:25:32 -04:00
base_order = ('datasource_name', 'asc')
2015-07-15 13:12:32 -04:00
def post_add(self, datasource):
datasource.generate_metrics()
def post_update(self, datasource):
datasource.generate_metrics()
2015-07-15 13:12:32 -04:00
appbuilder.add_view(
DatasourceModelView,
2015-08-01 20:16:39 -04:00
"Druid Datasources",
2015-08-03 11:34:58 -04:00
icon="fa-cube")
2015-07-15 13:12:32 -04:00
2015-07-27 17:16:18 -04:00
@app.route('/health')
def health():
return "OK"
@app.route('/ping')
def ping():
return "OK"
2015-07-15 13:12:32 -04:00
class Panoramix(BaseView):
2015-08-03 11:34:58 -04:00
@has_access
@expose("/table/<table_id>/")
def table(self, table_id):
table = (
db.session
.query(models.Table)
.filter_by(id=table_id)
.first()
)
2015-09-13 02:25:43 -04:00
if not table:
flash("The table seem to have been deleted", "alert")
2015-08-03 11:34:58 -04:00
viz_type = request.args.get("viz_type")
if not viz_type and table.default_endpoint:
return redirect(table.default_endpoint)
if not viz_type:
viz_type = "table"
obj = viz.viz_types[viz_type](
table,
form_data=request.args, view=self)
2015-09-15 12:17:59 -04:00
if request.args.get("json") == "true":
2015-09-15 15:33:26 -04:00
try:
payload = obj.get_json()
status=200
except Exception as e:
payload = str(e)
status=500
2015-08-03 11:34:58 -04:00
return Response(
2015-09-15 15:33:26 -04:00
payload,
status=status,
2015-08-03 11:34:58 -04:00
mimetype="application/json")
2015-09-15 12:17:59 -04:00
else:
return self.render_template("panoramix/viz.html", viz=obj)
2015-08-03 11:34:58 -04:00
2015-07-23 02:17:51 -04:00
@has_access
2015-07-15 13:12:32 -04:00
@expose("/datasource/<datasource_name>/")
def datasource(self, datasource_name):
2015-07-15 20:38:03 -04:00
viz_type = request.args.get("viz_type")
2015-07-15 13:12:32 -04:00
datasource = (
db.session
.query(models.Datasource)
.filter_by(datasource_name=datasource_name)
.first()
)
2015-07-15 20:38:03 -04:00
if not viz_type and datasource.default_endpoint:
return redirect(datasource.default_endpoint)
if not viz_type:
viz_type = "table"
2015-07-15 13:12:32 -04:00
obj = viz.viz_types[viz_type](
datasource,
form_data=request.args, view=self)
2015-07-20 19:29:16 -04:00
if request.args.get("json"):
return Response(
json.dumps(obj.get_query(), indent=4),
status=200,
mimetype="application/json")
2015-08-13 21:08:04 -04:00
if not hasattr(obj, 'df') or obj.df is None or obj.df.empty:
2015-07-15 13:12:32 -04:00
return obj.render_no_data()
2015-08-13 21:08:04 -04:00
return obj.check_and_render()
2015-07-15 13:12:32 -04:00
2015-09-12 00:49:04 -04:00
@has_access
@expose("/save/")
def save(self):
session = db.session()
obj = models.Slice(
params=json.dumps(request.args.to_dict()),
viz_type=request.args.get('viz_type'),
datasource_name=request.args.get('datasource_name'),
datasource_id=request.args.get('datasource_id'),
datasource_type=request.args.get('datasource_type'),
slice_name=request.args.get('slice_name', 'junk'),
)
session.add(obj)
session.commit()
session.close()
return "super!"
2015-09-13 02:25:43 -04:00
@has_access
2015-09-13 22:07:54 -04:00
@expose("/dashboard/<id_>/")
def dashboard(self, id_):
session = db.session()
dashboard = (
session
.query(models.Dashboard)
.filter(models.Dashboard.id == id_)
.first()
)
return self.render_template(
"panoramix/dashboard.html", dashboard=dashboard)
2015-09-13 02:25:43 -04:00
2015-07-23 02:17:51 -04:00
@has_access
2015-07-15 13:12:32 -04:00
@expose("/refresh_datasources/")
2015-07-15 20:38:03 -04:00
def refresh_datasources(self):
session = db.session()
for cluster in session.query(models.Cluster).all():
cluster.refresh_datasources()
cluster.metadata_last_refreshed = datetime.now()
flash(
"Refreshed metadata from cluster "
"[" + cluster.cluster_name + "]",
'info')
session.commit()
2015-07-15 13:12:32 -04:00
return redirect("/datasourcemodelview/list/")
2015-07-27 01:25:32 -04:00
@expose("/autocomplete/<datasource>/<column>/")
def autocomplete(self, datasource, column):
client = utils.get_pydruid_client()
top = client.topn(
datasource=datasource,
granularity='all',
intervals='2013-10-04/2020-10-10',
aggregations={"count": doublesum("count")},
dimension=column,
metric='count',
threshold=1000,
)
values = sorted([d[column] for d in top[0]['result']])
return json.dumps(values)
2015-07-15 13:12:32 -04:00
appbuilder.add_view_no_menu(Panoramix)
appbuilder.add_link(
2015-08-01 20:16:39 -04:00
"Refresh Druid Metadata",
2015-07-15 13:12:32 -04:00
href='/panoramix/refresh_datasources/',
category='Admin',
2015-08-01 20:16:39 -04:00
category_icon='fa-cogs',
icon="fa-cog")