From 66ee7968ddc4b26b7e54965982b5ae1c379667d9 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 6 Jun 2018 20:40:55 -0700 Subject: [PATCH 1/4] Improve time shift (#5140) * Improve time shift color and pattern * Revert change * Fix js unit test * Move code to better place, add unit test * Move classed code to backend * Remove console.log * Remove 1 hour time compare * Remove unused import (cherry picked from commit 57e1256) --- .../visualizations/nvd3_viz_spec.jsx | 6 ++-- superset/assets/src/modules/colors.js | 3 +- superset/assets/src/utils/common.js | 3 ++ .../assets/src/visualizations/nvd3_vis.css | 30 +++++++++++++++++++ .../assets/src/visualizations/nvd3_vis.js | 7 ++--- superset/viz.py | 7 ++++- 6 files changed, 46 insertions(+), 10 deletions(-) diff --git a/superset/assets/spec/javascripts/visualizations/nvd3_viz_spec.jsx b/superset/assets/spec/javascripts/visualizations/nvd3_viz_spec.jsx index c9ed4d87063ed..f1b58f1763ed5 100644 --- a/superset/assets/spec/javascripts/visualizations/nvd3_viz_spec.jsx +++ b/superset/assets/spec/javascripts/visualizations/nvd3_viz_spec.jsx @@ -20,9 +20,9 @@ describe('nvd3 viz', () => { expect(formatLabel(['foo'], verboseMap)).to.equal('Foo'); expect(formatLabel(['foo', 'bar', 'baz'], verboseMap)).to.equal('Foo, Bar, baz'); }); - it('deals with --- properly', () => { - expect(formatLabel(['foo', '---'], verboseMap)).to.equal('Foo ---'); - expect(formatLabel(['foo', 'bar', 'baz', '---'], verboseMap)).to.equal('Foo, Bar, baz ---'); + it('deals with time shift properly', () => { + expect(formatLabel(['foo', '1 hour offset'], verboseMap)).to.equal('Foo, 1 hour offset'); + expect(formatLabel(['foo', 'bar', 'baz', '2 hours offset'], verboseMap)).to.equal('Foo, Bar, baz, 2 hours offset'); }); }); }); diff --git a/superset/assets/src/modules/colors.js b/superset/assets/src/modules/colors.js index ac206b0b172c6..8825a7ad4985e 100644 --- a/superset/assets/src/modules/colors.js +++ b/superset/assets/src/modules/colors.js @@ -1,4 +1,5 @@ import d3 from 'd3'; +import { TIME_SHIFT_PATTERN } from '../utils/common'; export const brandColor = '#00A699'; export const colorPrimary = { r: 0, g: 122, b: 135, a: 1 }; @@ -181,7 +182,7 @@ export const getColorFromScheme = (function () { const selectedScheme = scheme ? ALL_COLOR_SCHEMES[scheme] : ALL_COLOR_SCHEMES.bnbColors; let stringifyS = String(s).toLowerCase(); // next line is for superset series that should have the same color - stringifyS = stringifyS.replace(' ---', ''); + stringifyS = stringifyS.split(', ').filter(k => !TIME_SHIFT_PATTERN.test(k)).join(', '); if (forcedColor && !forcedColors[stringifyS]) { forcedColors[stringifyS] = forcedColor; diff --git a/superset/assets/src/utils/common.js b/superset/assets/src/utils/common.js index a00fea329a037..c5bdfb9f6d53c 100644 --- a/superset/assets/src/utils/common.js +++ b/superset/assets/src/utils/common.js @@ -12,6 +12,9 @@ export const DEFAULT_LONGITUDE = -122.405293; export const DEFAULT_LATITUDE = 37.772123; export const DEFAULT_ZOOM = 11; +// Regexp for the label added to time shifted series (1 hour offset, 2 days offset, etc.) +export const TIME_SHIFT_PATTERN = /\d+ \w+ offset/; + export function kmToPixels(kilometers, latitude, zoomLevel) { // Algorithm from: http://wiki.openstreetmap.org/wiki/Zoom_levels const latitudeRad = latitude * (Math.PI / 180); diff --git a/superset/assets/src/visualizations/nvd3_vis.css b/superset/assets/src/visualizations/nvd3_vis.css index fed0d013dc531..c6a8198d64f4c 100644 --- a/superset/assets/src/visualizations/nvd3_vis.css +++ b/superset/assets/src/visualizations/nvd3_vis.css @@ -64,3 +64,33 @@ g.opacityHigh path, line.opacityHigh { stroke-opacity: .8 } +g.time-shift-0 path, line.time-shift-0 { + stroke-dasharray: 5, 5; +} +g.time-shift-1 path, line.time-shift-1 { + stroke-dasharray: 1, 5; +} +g.time-shift-2 path, line.time-shift-2 { + stroke-dasharray: 10, 5; +} +g.time-shift-3 path, line.time-shift-3 { + stroke-dasharray: 5, 1; +} +g.time-shift-4 path, line.time-shift-4 { + stroke-dasharray: 5, 10; +} +g.time-shift-5 path, line.time-shift-5 { + stroke-dasharray: 0.9; +} +g.time-shift-6 path, line.time-shift-6 { + stroke-dasharray: 15, 10, 5; +} +g.time-shift-7 path, line.time-shift-7 { + stroke-dasharray: 15, 10, 5, 10; +} +g.time-shift-8 path, line.time-shift-8 { + stroke-dasharray: 15, 10, 5, 10, 15; +} +g.time-shift-9 path, line.time-shift-9 { + stroke-dasharray: 5, 5, 1, 5; +} diff --git a/superset/assets/src/visualizations/nvd3_vis.js b/superset/assets/src/visualizations/nvd3_vis.js index a9f38b5c53093..bac5f2a641fdb 100644 --- a/superset/assets/src/visualizations/nvd3_vis.js +++ b/superset/assets/src/visualizations/nvd3_vis.js @@ -15,7 +15,7 @@ import AnnotationTypes, { } from '../modules/AnnotationTypes'; import { customizeToolTip, d3TimeFormatPreset, d3FormatPreset, tryNumify } from '../modules/utils'; import { formatDateVerbose } from '../modules/dates'; -import { isTruthy } from '../utils/common'; +import { isTruthy, TIME_SHIFT_PATTERN } from '../utils/common'; import { t } from '../locales'; // CSS @@ -103,11 +103,8 @@ export function formatLabel(input, verboseMap = {}) { const verboseLkp = s => verboseMap[s] || s; let label; if (Array.isArray(input) && input.length) { - const verboseLabels = input.filter(s => s !== '---').map(verboseLkp); + const verboseLabels = input.map(l => TIME_SHIFT_PATTERN.test(l) ? l : verboseLkp(l)); label = verboseLabels.join(', '); - if (input.length > verboseLabels.length) { - label += ' ---'; - } } else { label = verboseLkp(input); } diff --git a/superset/viz.py b/superset/viz.py index f93e5a2ac6b6d..c41e07c4fce71 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -1194,6 +1194,8 @@ def run_extra_queries(self): if not isinstance(time_compare, list): time_compare = [time_compare] + classes = ['time-shift-{}'.format(i) for i in range(10)] + i = 0 for option in time_compare: query_object = self.query_obj() delta = utils.parse_human_timedelta(option) @@ -1209,10 +1211,13 @@ def run_extra_queries(self): df2 = self.get_df_payload(query_object).get('df') if df2 is not None and DTTM_ALIAS in df2: + classed = classes[i % len(classes)] + i += 1 label = '{} offset'. format(option) df2[DTTM_ALIAS] += delta df2 = self.process_data(df2) - self._extra_chart_data.append((label, df2)) + self._extra_chart_data.extend(self.to_series( + df2, classed=classed, title_suffix=label)) def get_data(self, df): fd = self.form_data From 1f0d2fa0ab0306456bb5ffe86242d275a96d1558 Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Thu, 26 Jul 2018 22:30:29 -0700 Subject: [PATCH 2/4] Remove dashboard transition related config (#5505) (cherry picked from commit 723bbe4) --- superset/config.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/superset/config.py b/superset/config.py index 99f6f2c7e8087..458ada6442821 100644 --- a/superset/config.py +++ b/superset/config.py @@ -417,14 +417,6 @@ class CeleryConfig(object): # using flask-compress ENABLE_FLASK_COMPRESS = True -# Dashboard v1 deprecation configuration -DASH_V2_IS_DEFAULT_VIEW_FOR_EDITORS = True -CAN_FALLBACK_TO_DASH_V1_EDIT_MODE = True - -# these are incorporated into messages displayed to users -PLANNED_V2_AUTO_CONVERT_DATE = None # e.g. '2018-06-16' -V2_FEEDBACK_URL = None # e.g., 'https://goo.gl/forms/...' - try: if CONFIG_PATH_ENV_VAR in os.environ: # Explicitly import config module that is not in pythonpath; useful From 37d1d4d32d4126657ef11e60f9b421a9d616b008 Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Fri, 27 Jul 2018 11:59:32 -0700 Subject: [PATCH 3/4] set default layout for new added charts (#5425) (cherry picked from commit daf2116) --- .../src/dashboard/reducers/getInitialState.js | 28 ++++++++++++++----- .../assets/src/dashboard/util/constants.js | 1 + .../src/dashboard/util/newComponentFactory.js | 15 +++++++--- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/superset/assets/src/dashboard/reducers/getInitialState.js b/superset/assets/src/dashboard/reducers/getInitialState.js index 4d0b26c68f786..071ca9e3ce738 100644 --- a/superset/assets/src/dashboard/reducers/getInitialState.js +++ b/superset/assets/src/dashboard/reducers/getInitialState.js @@ -9,7 +9,11 @@ import { getColorFromScheme } from '../../modules/colors'; import findFirstParentContainerId from '../util/findFirstParentContainer'; import getEmptyLayout from '../util/getEmptyLayout'; import newComponentFactory from '../util/newComponentFactory'; -import { DASHBOARD_HEADER_ID } from '../util/constants'; +import { + DASHBOARD_HEADER_ID, + GRID_DEFAULT_CHART_WIDTH, + GRID_COLUMN_COUNT, +} from '../util/constants'; import { DASHBOARD_HEADER_TYPE, CHART_TYPE, @@ -55,6 +59,10 @@ export default function(bootstrapData) { // find root level chart container node for newly-added slices const parentId = findFirstParentContainerId(layout); + const parent = layout[parentId]; + let newSlicesContainer; + let newSlicesContainerWidth = 0; + const chartQueries = {}; const slices = {}; const sliceIds = new Set(); @@ -84,20 +92,26 @@ export default function(bootstrapData) { sliceIds.add(key); - // if chart is newly added from explore view, add a row in layout + // if there are newly added slices from explore view, fill slices into 1 or more rows if (!chartIdToLayoutId[key] && layout[parentId]) { - const parent = layout[parentId]; - const rowContainer = newComponentFactory(ROW_TYPE); - layout[rowContainer.id] = rowContainer; - parent.children.push(rowContainer.id); + if ( + newSlicesContainerWidth === 0 || + newSlicesContainerWidth + GRID_DEFAULT_CHART_WIDTH > GRID_COLUMN_COUNT + ) { + newSlicesContainer = newComponentFactory(ROW_TYPE); + layout[newSlicesContainer.id] = newSlicesContainer; + parent.children.push(newSlicesContainer.id); + newSlicesContainerWidth = 0; + } const chartHolder = newComponentFactory(CHART_TYPE, { chartId: slice.slice_id, }); layout[chartHolder.id] = chartHolder; - rowContainer.children.push(chartHolder.id); + newSlicesContainer.children.push(chartHolder.id); chartIdToLayoutId[chartHolder.meta.chartId] = chartHolder.id; + newSlicesContainerWidth += GRID_DEFAULT_CHART_WIDTH; } } diff --git a/superset/assets/src/dashboard/util/constants.js b/superset/assets/src/dashboard/util/constants.js index 3b6a2ecd46ce5..b26cbff85fdbb 100644 --- a/superset/assets/src/dashboard/util/constants.js +++ b/superset/assets/src/dashboard/util/constants.js @@ -23,6 +23,7 @@ export const GRID_MIN_COLUMN_COUNT = 1; export const GRID_MIN_ROW_UNITS = 5; export const GRID_MAX_ROW_UNITS = 100; export const GRID_MIN_ROW_HEIGHT = GRID_GUTTER_SIZE; +export const GRID_DEFAULT_CHART_WIDTH = 4; // Header types export const SMALL_HEADER = 'SMALL_HEADER'; diff --git a/superset/assets/src/dashboard/util/newComponentFactory.js b/superset/assets/src/dashboard/util/newComponentFactory.js index 8d259afa4b658..18b433b0135eb 100644 --- a/superset/assets/src/dashboard/util/newComponentFactory.js +++ b/superset/assets/src/dashboard/util/newComponentFactory.js @@ -11,18 +11,25 @@ import { TAB_TYPE, } from './componentTypes'; -import { MEDIUM_HEADER, BACKGROUND_TRANSPARENT } from './constants'; +import { + MEDIUM_HEADER, + BACKGROUND_TRANSPARENT, + GRID_DEFAULT_CHART_WIDTH, +} from './constants'; const typeToDefaultMetaData = { - [CHART_TYPE]: { width: 3, height: 30 }, - [COLUMN_TYPE]: { width: 3, background: BACKGROUND_TRANSPARENT }, + [CHART_TYPE]: { width: GRID_DEFAULT_CHART_WIDTH, height: 50 }, + [COLUMN_TYPE]: { + width: GRID_DEFAULT_CHART_WIDTH, + background: BACKGROUND_TRANSPARENT, + }, [DIVIDER_TYPE]: null, [HEADER_TYPE]: { text: 'New header', headerSize: MEDIUM_HEADER, background: BACKGROUND_TRANSPARENT, }, - [MARKDOWN_TYPE]: { width: 3, height: 30 }, + [MARKDOWN_TYPE]: { width: GRID_DEFAULT_CHART_WIDTH, height: 50 }, [ROW_TYPE]: { background: BACKGROUND_TRANSPARENT }, [TABS_TYPE]: null, [TAB_TYPE]: { text: 'New Tab' }, From 8dd71843c67b97d3ff6d7094539b77932a78a72a Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Tue, 14 Aug 2018 10:56:45 -0700 Subject: [PATCH 4/4] [Dashobard]Fix bad merge (#5624) (cherry picked from commit 9f6ac08) --- superset/views/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/superset/views/core.py b/superset/views/core.py index c689997d77b2a..7ee9b193b01c0 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -1634,7 +1634,6 @@ def _set_dash_metadata(dashboard, data): # remove leading and trailing white spaces in the dumped json dashboard.position_json = json.dumps( positions, indent=None, separators=(',', ':'), sort_keys=True) - dashboard.position_json = json.dumps(positions, sort_keys=True) md = dashboard.params_dict dashboard.css = data.get('css') dashboard.dashboard_title = data['dashboard_title']