From 2993ff1d75ff2391ffab388544f198cbb78364a9 Mon Sep 17 00:00:00 2001 From: Daniel Darabos Date: Mon, 12 Dec 2016 16:58:13 +0000 Subject: [PATCH] Add NVD3's bullet chart (#1775) * Add NVD3's bullet chart. * Add empty lines before nested function definitions. * Add thumbnail for bullet chart. * Add bullet chart to gallery.rst. * Add "requiresTime: false", fix indentation. * Avoid scaling bullet chart vertically. * Use a default if no range is specified. * Fix coloring of bullet chart. --- docs/gallery.rst | 3 + .../assets/images/viz_thumbnails/bullet.png | Bin 0 -> 8764 bytes .../javascripts/explorev2/stores/fields.js | 42 ++++++++++++ .../javascripts/explorev2/stores/store.js | 5 ++ .../javascripts/explorev2/stores/visTypes.js | 16 +++++ superset/assets/visualizations/main.js | 1 + superset/assets/visualizations/nvd3_vis.js | 11 ++- superset/forms.py | 30 ++++++++ superset/viz.py | 64 ++++++++++++++++++ 9 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 superset/assets/images/viz_thumbnails/bullet.png diff --git a/docs/gallery.rst b/docs/gallery.rst index e25cb19287..f0c7dfaec5 100644 --- a/docs/gallery.rst +++ b/docs/gallery.rst @@ -49,6 +49,9 @@ Gallery .. image:: _static/img/viz_thumbnails/big_number_total.png :scale: 25 % +.. image:: _static/img/viz_thumbnails/bullet.png + :scale: 25 % + .. image:: _static/img/viz_thumbnails/dist_bar.png :scale: 25 % diff --git a/superset/assets/images/viz_thumbnails/bullet.png b/superset/assets/images/viz_thumbnails/bullet.png new file mode 100644 index 0000000000000000000000000000000000000000..7a66a41c7af6bbcc8f8c9432dfc0134ef9474fe5 GIT binary patch literal 8764 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 category21(d[colorKey])); + if (vizType !== 'bullet') { + chart.color((d) => category21(d[colorKey])); + } if (fd.x_axis_label && fd.x_axis_label !== '' && chart.xAxis) { let distance = 0; diff --git a/superset/forms.py b/superset/forms.py index aa9ac038d0..805cb47b72 100755 --- a/superset/forms.py +++ b/superset/forms.py @@ -987,6 +987,36 @@ class FormFactory(object): ], "description": _("The color for points and clusters in RGB") }), + 'ranges': (TextField, { + "label": _("Ranges"), + "default": "", + "description": _("Ranges to highlight with shading") + }), + 'range_labels': (TextField, { + "label": _("Range labels"), + "default": "", + "description": _("Labels for the ranges") + }), + 'markers': (TextField, { + "label": _("Markers"), + "default": "", + "description": _("List of values to mark with triangles") + }), + 'marker_labels': (TextField, { + "label": _("Marker labels"), + "default": "", + "description": _("Labels for the markers") + }), + 'marker_lines': (TextField, { + "label": _("Marker lines"), + "default": "", + "description": _("List of values to mark with lines") + }), + 'marker_line_labels': (TextField, { + "label": _("Marker line labels"), + "default": "", + "description": _("Labels for the marker lines") + }), } # Override default arguments with form overrides diff --git a/superset/viz.py b/superset/viz.py index 6f0a4ba1a9..2c142c443d 100755 --- a/superset/viz.py +++ b/superset/viz.py @@ -896,6 +896,69 @@ class BubbleViz(NVD3Viz): return chart_data +class BulletViz(NVD3Viz): + + """Based on the NVD3 bullet chart""" + + viz_type = "bullet" + verbose_name = _("Bullet Chart") + is_timeseries = False + fieldsets = ({ + 'label': None, + 'fields': ( + 'metric', + 'ranges', 'range_labels', + 'markers', 'marker_labels', + 'marker_lines', 'marker_line_labels', + ) + },) + + def query_obj(self): + form_data = self.form_data + d = super(BulletViz, self).query_obj() + self.metric = form_data.get('metric') + + def as_strings(field): + value = form_data.get(field) + return value.split(',') if value else [] + + def as_floats(field): + return [float(x) for x in as_strings(field)] + + self.ranges = as_floats('ranges') + self.range_labels = as_strings('range_labels') + self.markers = as_floats('markers') + self.marker_labels = as_strings('marker_labels') + self.marker_lines = as_floats('marker_lines') + self.marker_line_labels = as_strings('marker_line_labels') + + d['metrics'] = [ + self.metric, + ] + if not self.metric: + raise Exception("Pick a metric to display") + return d + + def get_df(self, query_obj=None): + df = super(BulletViz, self).get_df(query_obj) + df = df.fillna(0) + df['metric'] = df[[self.metric]] + return df + + def get_data(self): + df = self.get_df() + values = df['metric'].values + return { + 'measures': values.tolist(), + 'ranges': self.ranges or [0, values.max() * 1.1], + 'rangeLabels': self.range_labels or None, + 'markers': self.markers or None, + 'markerLabels': self.marker_labels or None, + 'markerLines': self.marker_lines or None, + 'markerLineLabels': self.marker_line_labels or None, + } + + class BigNumberViz(BaseViz): """Put emphasis on a single metric with this big number viz""" @@ -2008,6 +2071,7 @@ viz_types_list = [ DistributionBarViz, DistributionPieViz, BubbleViz, + BulletViz, MarkupViz, WordCloudViz, BigNumberViz,