Skip to content

Commit

Permalink
[bugfix] make MetricsControl work with DECK visualizations (apache#5376)
Browse files Browse the repository at this point in the history
* [bugfix] make MetricsControl work with DECK visualizations

* Add unit tests

(cherry picked from commit 709f056)
(cherry picked from commit 23262ae988310ea406ff374a2ece86949be1c228)
  • Loading branch information
mistercrunch committed Jul 26, 2018
1 parent f5aee02 commit 3ff256e
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default class FixedOrMetricControl extends React.Component {
renderPopover() {
const value = this.props.value || this.props.default;
const type = value.type || controlTypes.fixed;
const metrics = this.props.datasource ? this.props.datasource.metrics : null;
const metricsCombo = this.props.datasource ? this.props.datasource.metrics_combo : null;
return (
<Popover id="filter-popover">
<div style={{ width: '240px' }}>
Expand All @@ -87,7 +87,7 @@ export default class FixedOrMetricControl extends React.Component {
<SelectControl
{...controls.metric}
name="metric"
options={metrics}
choices={metricsCombo}
onFocus={() => { this.setType(controlTypes.metric); }}
onChange={this.setMetric}
value={this.state.metricValue}
Expand Down
1 change: 1 addition & 0 deletions superset/assets/src/explore/visTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ export const visTypes = {
},
{
label: t('Grid'),
expanded: true,
controlSetRows: [
['grid_size', 'color_picker'],
],
Expand Down
2 changes: 2 additions & 0 deletions superset/assets/src/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const Logger = {
if (!addEventHandlers[eventName]) {
addEventHandlers[eventName] = log.addEvent.bind(log);
} else {
// eslint-disable-next-line no-console
console.warn(`Duplicate event handler for event '${eventName}'`);
}
});
Expand All @@ -20,6 +21,7 @@ export const Logger = {
if (addEventHandlers[eventName]) {
addEventHandlers[eventName](eventName, eventBody, sendNow);
} else {
// eslint-disable-next-line no-console
console.warn(`No event handler for event '${eventName}'`);
}
},
Expand Down
4 changes: 4 additions & 0 deletions superset/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ class NullValueException(SupersetException):

class SupersetTemplateException(SupersetException):
pass


class SpatialException(SupersetException):
pass
50 changes: 36 additions & 14 deletions superset/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from six.moves import cPickle as pkl, reduce

from superset import app, cache, get_manifest_file, utils
from superset.exceptions import NullValueException
from superset.exceptions import NullValueException, SpatialException
from superset.utils import DTTM_ALIAS, JS_MAX_INTEGER, merge_extra_filters


Expand Down Expand Up @@ -2085,6 +2085,17 @@ def process_spatial_query_obj(self, key, group_by):
elif spatial.get('type') == 'geohash':
group_by += [spatial.get('geohashCol')]

@staticmethod
def parse_coordinates(s):
if not s:
return None
try:
p = Point(s)
except Exception:
raise SpatialException(
_('Invalid spatial point encountered: %s' % s))
return (p.latitude, p.longitude)

def process_spatial_data_obj(self, key, df):
spatial = self.form_data.get(key)
if spatial is None:
Expand All @@ -2095,19 +2106,15 @@ def process_spatial_data_obj(self, key, df):
pd.to_numeric(df[spatial.get('latCol')], errors='coerce'),
))
elif spatial.get('type') == 'delimited':

def tupleify(s):
p = Point(s)
return (p.latitude, p.longitude)

df[key] = df[spatial.get('lonlatCol')].apply(tupleify)
lon_lat_col = spatial.get('lonlatCol')
df[key] = df[lon_lat_col].apply(self.parse_coordinates)

if spatial.get('reverseCheckbox'):
df[key] = [
tuple(reversed(o)) if isinstance(o, (list, tuple)) else (0, 0)
for o in df[key]
]
del df[spatial.get('lonlatCol')]
del df[lon_lat_col]
elif spatial.get('type') == 'geohash':
latlong = df[spatial.get('geohashCol')].map(geohash.decode)
df[key] = list(zip(latlong.apply(lambda x: x[0]),
Expand All @@ -2117,7 +2124,6 @@ def tupleify(s):
if df.get(key) is None:
raise NullValueException(_('Encountered invalid NULL spatial entry, \
please consider filtering those out'))

return df

def query_obj(self):
Expand Down Expand Up @@ -2151,6 +2157,8 @@ def get_js_columns(self, d):
def get_data(self, df):
if df is None:
return None

# Processing spatial info
for key in self.spatial_control_keys:
df = self.process_spatial_data_obj(key, df)

Expand Down Expand Up @@ -2197,15 +2205,17 @@ def get_metrics(self):

def get_properties(self, d):
return {
'metric': d.get(self.metric),
'radius': self.fixed_value if self.fixed_value else d.get(self.metric),
'metric': d.get(self.metric_label),
'radius': self.fixed_value if self.fixed_value else d.get(self.metric_label),
'cat_color': d.get(self.dim) if self.dim else None,
'position': d.get('spatial'),
DTTM_ALIAS: d.get(DTTM_ALIAS),
}

def get_data(self, df):
fd = self.form_data
self.metric_label = \
self.get_metric_label(self.metric) if self.metric else None
self.point_radius_fixed = fd.get('point_radius_fixed')
self.fixed_value = None
self.dim = self.form_data.get('dimension')
Expand All @@ -2231,10 +2241,14 @@ def query_obj(self):
def get_properties(self, d):
return {
'position': d.get('spatial'),
'weight': d.get(self.metric) or 1,
'weight': d.get(self.metric_label) or 1,
'__timestamp': d.get(DTTM_ALIAS) or d.get('__time'),
}

def get_data(self, df):
self.metric_label = self.get_metric_label(self.metric)
return super(DeckScreengrid, self).get_data(df)


class DeckGrid(BaseDeckGLViz):

Expand All @@ -2247,9 +2261,13 @@ class DeckGrid(BaseDeckGLViz):
def get_properties(self, d):
return {
'position': d.get('spatial'),
'weight': d.get(self.metric) or 1,
'weight': d.get(self.metric_label) or 1,
}

def get_data(self, df):
self.metric_label = self.get_metric_label(self.metric)
return super(DeckGrid, self).get_data(df)


class DeckPathViz(BaseDeckGLViz):

Expand Down Expand Up @@ -2303,9 +2321,13 @@ class DeckHex(BaseDeckGLViz):
def get_properties(self, d):
return {
'position': d.get('spatial'),
'weight': d.get(self.metric) or 1,
'weight': d.get(self.metric_label) or 1,
}

def get_data(self, df):
self.metric_label = self.get_metric_label(self.metric)
return super(DeckHex, self).get_data(df)


class DeckGeoJson(BaseDeckGLViz):

Expand Down
28 changes: 28 additions & 0 deletions tests/viz_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from mock import Mock, patch
import pandas as pd

from superset import app
from superset.exceptions import SpatialException
from superset.utils import DTTM_ALIAS
import superset.viz as viz
from .utils import load_fixture
Expand Down Expand Up @@ -947,3 +949,29 @@ def test_geojson_query_obj(self):
assert results['metrics'] == []
assert results['groupby'] == []
assert results['columns'] == ['test_col']

def test_parse_coordinates(self):
form_data = load_fixture('deck_path_form_data.json')
datasource = {'type': 'table'}
viz_instance = viz.BaseDeckGLViz(datasource, form_data)

coord = viz_instance.parse_coordinates('1.23, 3.21')
self.assertEquals(coord, (1.23, 3.21))

coord = viz_instance.parse_coordinates('1.23 3.21')
self.assertEquals(coord, (1.23, 3.21))

self.assertEquals(viz_instance.parse_coordinates(None), None)

self.assertEquals(viz_instance.parse_coordinates(''), None)

def test_parse_coordinates_raises(self):
form_data = load_fixture('deck_path_form_data.json')
datasource = {'type': 'table'}
test_viz_deckgl = viz.BaseDeckGLViz(datasource, form_data)

with self.assertRaises(SpatialException):
test_viz_deckgl.parse_coordinates('NULL')

with self.assertRaises(SpatialException):
test_viz_deckgl.parse_coordinates('fldkjsalkj,fdlaskjfjadlksj')

0 comments on commit 3ff256e

Please sign in to comment.