mirror of
https://github.com/apache/superset.git
synced 2026-09-09 08:44:32 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eea9e68801 | ||
|
|
e62b52f08e | ||
|
|
7ad740461e | ||
|
|
1060484836 | ||
|
|
445f1e204c | ||
|
|
010a69de83 |
@@ -66,6 +66,7 @@ setup(
|
|||||||
'pandas==0.20.3',
|
'pandas==0.20.3',
|
||||||
'parsedatetime==2.0.0',
|
'parsedatetime==2.0.0',
|
||||||
'pathlib2==2.3.0',
|
'pathlib2==2.3.0',
|
||||||
|
'polyline==1.3.2',
|
||||||
'pydruid==0.3.1',
|
'pydruid==0.3.1',
|
||||||
'PyHive>=0.4.0',
|
'PyHive>=0.4.0',
|
||||||
'python-dateutil==2.6.0',
|
'python-dateutil==2.6.0',
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 511 KiB |
@@ -1723,5 +1723,37 @@ export const controls = {
|
|||||||
t('Partitions whose height to parent height proportions are ' +
|
t('Partitions whose height to parent height proportions are ' +
|
||||||
'below this value are pruned'),
|
'below this value are pruned'),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
line_column: {
|
||||||
|
type: 'SelectControl',
|
||||||
|
label: t('Lines column'),
|
||||||
|
default: null,
|
||||||
|
description: t('The database columns that contains lines information'),
|
||||||
|
mapStateToProps: state => ({
|
||||||
|
choices: (state.datasource) ? state.datasource.all_cols : [],
|
||||||
|
}),
|
||||||
|
validators: [v.nonEmpty],
|
||||||
|
},
|
||||||
|
line_type: {
|
||||||
|
type: 'SelectControl',
|
||||||
|
label: t('Lines encoding'),
|
||||||
|
clearable: false,
|
||||||
|
default: 'json',
|
||||||
|
description: t('The encoding format of the lines'),
|
||||||
|
choices: [
|
||||||
|
['polyline', 'Polyline'],
|
||||||
|
['json', 'JSON'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
line_width: {
|
||||||
|
type: 'TextControl',
|
||||||
|
label: t('Line width'),
|
||||||
|
renderTrigger: true,
|
||||||
|
isInt: true,
|
||||||
|
default: 10,
|
||||||
|
description: t('The width of the lines'),
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
export default controls;
|
export default controls;
|
||||||
|
|||||||
@@ -397,6 +397,28 @@ export const visTypes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
deck_path: {
|
||||||
|
label: t('Deck.gl - Grid'),
|
||||||
|
requiresTime: true,
|
||||||
|
controlPanelSections: [
|
||||||
|
{
|
||||||
|
label: t('Query'),
|
||||||
|
expanded: true,
|
||||||
|
controlSetRows: [
|
||||||
|
['line_column', 'line_type'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('Map'),
|
||||||
|
expanded: true,
|
||||||
|
controlSetRows: [
|
||||||
|
['mapbox_style', 'viewport'],
|
||||||
|
['color_picker', 'line_width'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
deck_screengrid: {
|
deck_screengrid: {
|
||||||
label: t('Deck.gl - Screen grid'),
|
label: t('Deck.gl - Screen grid'),
|
||||||
requiresTime: true,
|
requiresTime: true,
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { PathLayer } from 'deck.gl';
|
||||||
|
|
||||||
|
import DeckGLContainer from './DeckGLContainer';
|
||||||
|
|
||||||
|
function deckPath(slice, payload, setControlValue) {
|
||||||
|
const fd = slice.formData;
|
||||||
|
const c = fd.color_picker;
|
||||||
|
const fixedColor = [c.r, c.g, c.b, 255 * c.a];
|
||||||
|
const data = payload.data.paths.map((path) => {
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
width: fd.line_width,
|
||||||
|
color: fixedColor,
|
||||||
|
};
|
||||||
|
});;
|
||||||
|
|
||||||
|
const layer = new PathLayer({
|
||||||
|
id: `path-layer-${slice.containerId}`,
|
||||||
|
data,
|
||||||
|
rounded: true,
|
||||||
|
widthScale: 1,
|
||||||
|
});
|
||||||
|
const viewport = {
|
||||||
|
...fd.viewport,
|
||||||
|
width: slice.width(),
|
||||||
|
height: slice.height(),
|
||||||
|
};
|
||||||
|
ReactDOM.render(
|
||||||
|
<DeckGLContainer
|
||||||
|
mapboxApiAccessToken={payload.data.mapboxApiKey}
|
||||||
|
viewport={viewport}
|
||||||
|
layers={[layer]}
|
||||||
|
mapStyle={fd.mapbox_style}
|
||||||
|
setControlValue={setControlValue}
|
||||||
|
/>,
|
||||||
|
document.getElementById(slice.containerId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
module.exports = deckPath;
|
||||||
@@ -41,5 +41,6 @@ const vizMap = {
|
|||||||
deck_screengrid: require('./deckgl/screengrid.jsx'),
|
deck_screengrid: require('./deckgl/screengrid.jsx'),
|
||||||
deck_grid: require('./deckgl/grid.jsx'),
|
deck_grid: require('./deckgl/grid.jsx'),
|
||||||
deck_hex: require('./deckgl/hex.jsx'),
|
deck_hex: require('./deckgl/hex.jsx'),
|
||||||
|
deck_path: require('./deckgl/path.jsx'),
|
||||||
};
|
};
|
||||||
export default vizMap;
|
export default vizMap;
|
||||||
|
|||||||
@@ -146,6 +146,9 @@ def load_examples(load_test_data):
|
|||||||
print('Loading flights data')
|
print('Loading flights data')
|
||||||
data.load_flights()
|
data.load_flights()
|
||||||
|
|
||||||
|
print('Loading bart lines data')
|
||||||
|
data.load_bart_lines()
|
||||||
|
|
||||||
|
|
||||||
@manager.option(
|
@manager.option(
|
||||||
'-d', '--datasource',
|
'-d', '--datasource',
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import random
|
|||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from sqlalchemy import BigInteger, Date, DateTime, Float, String
|
from sqlalchemy import BigInteger, Date, DateTime, Float, String, Text
|
||||||
import geohash
|
import geohash
|
||||||
|
import polyline
|
||||||
|
|
||||||
from superset import app, db, utils
|
from superset import app, db, utils
|
||||||
from superset.connectors.connector_registry import ConnectorRegistry
|
from superset.connectors.connector_registry import ConnectorRegistry
|
||||||
@@ -1519,3 +1520,33 @@ def load_flights():
|
|||||||
db.session.merge(obj)
|
db.session.merge(obj)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
obj.fetch_metadata()
|
obj.fetch_metadata()
|
||||||
|
|
||||||
|
|
||||||
|
def load_bart_lines():
|
||||||
|
tbl_name = 'bart_lines'
|
||||||
|
with gzip.open(os.path.join(DATA_FOLDER, 'bart-lines.json.gz')) as f:
|
||||||
|
df = pd.read_json(f, encoding='latin-1')
|
||||||
|
df['path_json'] = df.path.map(json.dumps)
|
||||||
|
df['polyline'] = df.path.map(polyline.encode)
|
||||||
|
del df['path']
|
||||||
|
df.to_sql(
|
||||||
|
tbl_name,
|
||||||
|
db.engine,
|
||||||
|
if_exists='replace',
|
||||||
|
chunksize=500,
|
||||||
|
dtype={
|
||||||
|
'color': String(255),
|
||||||
|
'name': String(255),
|
||||||
|
'polyline': Text,
|
||||||
|
'path_json': Text,
|
||||||
|
},
|
||||||
|
index=False)
|
||||||
|
print("Creating table {} reference".format(tbl_name))
|
||||||
|
tbl = db.session.query(TBL).filter_by(table_name=tbl_name).first()
|
||||||
|
if not tbl:
|
||||||
|
tbl = TBL(table_name=tbl_name)
|
||||||
|
tbl.description = "BART lines"
|
||||||
|
tbl.database = get_or_create_main_db()
|
||||||
|
db.session.merge(tbl)
|
||||||
|
db.session.commit()
|
||||||
|
tbl.fetch_metadata()
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
{% extends "appbuilder/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div id="loginbox" style="margin-top:50px;" class="mainbox col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
|
||||||
|
<center>
|
||||||
|
<a href="/login/google">
|
||||||
|
<img width="300" src="https://developers.google.com/accounts/images/sign-in-with-google.png">
|
||||||
|
</a>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
+58
-3
@@ -998,8 +998,7 @@ class Superset(BaseSupersetView):
|
|||||||
mimetype='application/json')
|
mimetype='application/json')
|
||||||
|
|
||||||
@log_this
|
@log_this
|
||||||
@has_access_api
|
@expose("/explore_json/<datasource_type>/<datasource_id>/")
|
||||||
@expose('/explore_json/<datasource_type>/<datasource_id>/')
|
|
||||||
def explore_json(self, datasource_type, datasource_id):
|
def explore_json(self, datasource_type, datasource_id):
|
||||||
try:
|
try:
|
||||||
viz_obj = self.get_viz(
|
viz_obj = self.get_viz(
|
||||||
@@ -1012,7 +1011,7 @@ class Superset(BaseSupersetView):
|
|||||||
utils.error_msg_from_exception(e),
|
utils.error_msg_from_exception(e),
|
||||||
stacktrace=traceback.format_exc())
|
stacktrace=traceback.format_exc())
|
||||||
|
|
||||||
if not self.datasource_access(viz_obj.datasource):
|
if not self.datasource_access(viz_obj.datasource) and request.args.get("restful") != "true":
|
||||||
return json_error_response(DATASOURCE_ACCESS_ERR, status=404)
|
return json_error_response(DATASOURCE_ACCESS_ERR, status=404)
|
||||||
|
|
||||||
if request.args.get('csv') == 'true':
|
if request.args.get('csv') == 'true':
|
||||||
@@ -1788,6 +1787,7 @@ class Superset(BaseSupersetView):
|
|||||||
@expose('/dashboard/<dashboard_id>/')
|
@expose('/dashboard/<dashboard_id>/')
|
||||||
def dashboard(self, dashboard_id):
|
def dashboard(self, dashboard_id):
|
||||||
"""Server side rendering for a dashboard"""
|
"""Server side rendering for a dashboard"""
|
||||||
|
logging.info("in dashboard")
|
||||||
session = db.session()
|
session = db.session()
|
||||||
qry = session.query(models.Dashboard)
|
qry = session.query(models.Dashboard)
|
||||||
if dashboard_id.isdigit():
|
if dashboard_id.isdigit():
|
||||||
@@ -1845,6 +1845,61 @@ class Superset(BaseSupersetView):
|
|||||||
bootstrap_data=json.dumps(bootstrap_data),
|
bootstrap_data=json.dumps(bootstrap_data),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@expose("/dashboard_json/<dashboard_id>/")
|
||||||
|
def dashboard_json(self, dashboard_id):
|
||||||
|
"""Server side rendering for a dashboard"""
|
||||||
|
session = db.session()
|
||||||
|
qry = session.query(models.Dashboard)
|
||||||
|
if dashboard_id.isdigit():
|
||||||
|
qry = qry.filter_by(id=int(dashboard_id))
|
||||||
|
else:
|
||||||
|
qry = qry.filter_by(slug=dashboard_id)
|
||||||
|
|
||||||
|
dash = qry.one()
|
||||||
|
datasources = set()
|
||||||
|
for slc in dash.slices:
|
||||||
|
datasource = slc.datasource
|
||||||
|
if datasource:
|
||||||
|
datasources.add(datasource)
|
||||||
|
|
||||||
|
# Commenting until we figure out Authentication from a service
|
||||||
|
# for datasource in datasources:
|
||||||
|
# if datasource and not self.datasource_access(datasource):
|
||||||
|
# flash(
|
||||||
|
# __(get_datasource_access_error_msg(datasource.name)),
|
||||||
|
# "danger")
|
||||||
|
# return redirect(
|
||||||
|
# 'superset/request_access/?'
|
||||||
|
# 'dashboard_id={dash.id}&'.format(**locals()))
|
||||||
|
|
||||||
|
# Hack to log the dashboard_id properly, even when getting a slug
|
||||||
|
@log_this
|
||||||
|
def dashboard(**kwargs): # noqa
|
||||||
|
pass
|
||||||
|
dashboard(dashboard_id=dash.id)
|
||||||
|
|
||||||
|
dash_edit_perm = check_ownership(dash, raise_if_false=False)
|
||||||
|
dash_save_perm = \
|
||||||
|
dash_edit_perm and self.can_access('can_save_dash', 'Superset')
|
||||||
|
|
||||||
|
standalone_mode = request.args.get("standalone") == "true"
|
||||||
|
|
||||||
|
dashboard_data = dash.data
|
||||||
|
dashboard_data.update({
|
||||||
|
'standalone_mode': standalone_mode,
|
||||||
|
'dash_save_perm': dash_save_perm,
|
||||||
|
'dash_edit_perm': dash_edit_perm,
|
||||||
|
})
|
||||||
|
|
||||||
|
bootstrap_data = {
|
||||||
|
'user_id': g.user.get_id(),
|
||||||
|
'dashboard_data': dashboard_data,
|
||||||
|
'datasources': {ds.uid: ds.data for ds in datasources},
|
||||||
|
'common': self.common_bootsrap_payload(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_success(json.dumps(bootstrap_data))
|
||||||
|
|
||||||
@has_access
|
@has_access
|
||||||
@expose('/sync_druid/', methods=['POST'])
|
@expose('/sync_druid/', methods=['POST'])
|
||||||
@log_this
|
@log_this
|
||||||
|
|||||||
+42
-8
@@ -30,6 +30,7 @@ from pandas.tseries.frequencies import to_offset
|
|||||||
import simplejson as json
|
import simplejson as json
|
||||||
from six import PY3, string_types, text_type
|
from six import PY3, string_types, text_type
|
||||||
from six.moves import reduce
|
from six.moves import reduce
|
||||||
|
import polyline
|
||||||
|
|
||||||
from superset import app, cache, get_manifest_file, utils
|
from superset import app, cache, get_manifest_file, utils
|
||||||
from superset.utils import DTTM_ALIAS, merge_extra_filters
|
from superset.utils import DTTM_ALIAS, merge_extra_filters
|
||||||
@@ -1814,13 +1815,14 @@ class BaseDeckGLViz(BaseViz):
|
|||||||
gb = []
|
gb = []
|
||||||
|
|
||||||
spatial = fd.get('spatial')
|
spatial = fd.get('spatial')
|
||||||
if spatial.get('type') == 'latlong':
|
if spatial:
|
||||||
gb += [spatial.get('lonCol')]
|
if spatial.get('type') == 'latlong':
|
||||||
gb += [spatial.get('latCol')]
|
gb += [spatial.get('lonCol')]
|
||||||
elif spatial.get('type') == 'delimited':
|
gb += [spatial.get('latCol')]
|
||||||
gb += [spatial.get('lonlatCol')]
|
elif spatial.get('type') == 'delimited':
|
||||||
elif spatial.get('type') == 'geohash':
|
gb += [spatial.get('lonlatCol')]
|
||||||
gb += [spatial.get('geohashCol')]
|
elif spatial.get('type') == 'geohash':
|
||||||
|
gb += [spatial.get('geohashCol')]
|
||||||
|
|
||||||
if fd.get('dimension'):
|
if fd.get('dimension'):
|
||||||
gb += [fd.get('dimension')]
|
gb += [fd.get('dimension')]
|
||||||
@@ -1881,8 +1883,10 @@ class DeckScatterViz(BaseDeckGLViz):
|
|||||||
return super(DeckScatterViz, self).query_obj()
|
return super(DeckScatterViz, self).query_obj()
|
||||||
|
|
||||||
def get_metrics(self):
|
def get_metrics(self):
|
||||||
|
self.metric = None
|
||||||
if self.point_radius_fixed.get('type') == 'metric':
|
if self.point_radius_fixed.get('type') == 'metric':
|
||||||
return [self.point_radius_fixed.get('value')]
|
self.metric = self.point_radius_fixed.get('value')
|
||||||
|
return [self.metric]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_properties(self, d):
|
def get_properties(self, d):
|
||||||
@@ -1917,6 +1921,36 @@ class DeckGrid(BaseDeckGLViz):
|
|||||||
verbose_name = _('Deck.gl - 3D Grid')
|
verbose_name = _('Deck.gl - 3D Grid')
|
||||||
|
|
||||||
|
|
||||||
|
class DeckPathViz(BaseDeckGLViz):
|
||||||
|
|
||||||
|
"""deck.gl's PathLayer"""
|
||||||
|
|
||||||
|
viz_type = 'deck_path'
|
||||||
|
verbose_name = _('Deck.gl - Paths')
|
||||||
|
deser_map = {
|
||||||
|
'json': json.loads,
|
||||||
|
'polyline': polyline.decode,
|
||||||
|
}
|
||||||
|
|
||||||
|
def query_obj(self):
|
||||||
|
d = super(DeckPathViz, self).query_obj()
|
||||||
|
d['groupby'] = []
|
||||||
|
d['metrics'] = []
|
||||||
|
d['columns'] = [self.form_data.get('line_column')]
|
||||||
|
return d
|
||||||
|
|
||||||
|
def get_data(self, df):
|
||||||
|
fd = self.form_data
|
||||||
|
deser = self.deser_map[fd.get('line_type')]
|
||||||
|
paths = [deser(s) for s in df[fd.get('line_column')]]
|
||||||
|
|
||||||
|
d = {
|
||||||
|
'mapboxApiKey': config.get('MAPBOX_API_KEY'),
|
||||||
|
'paths': paths,
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
class DeckHex(BaseDeckGLViz):
|
class DeckHex(BaseDeckGLViz):
|
||||||
|
|
||||||
"""deck.gl's DeckLayer"""
|
"""deck.gl's DeckLayer"""
|
||||||
|
|||||||
Reference in New Issue
Block a user