You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by gr...@apache.org on 2017/11/16 08:27:17 UTC

[incubator-superset] branch master updated: Further refactoring around dashboards (#3843)

This is an automated email from the ASF dual-hosted git repository.

graceguo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 1c545d3  Further refactoring around dashboards (#3843)
1c545d3 is described below

commit 1c545d3a2d4337322ad361c62b6380bbef2c9584
Author: Maxime Beauchemin <ma...@gmail.com>
AuthorDate: Thu Nov 16 00:27:15 2017 -0800

    Further refactoring around dashboards (#3843)
    
    I was wondering what was left to do in order to remove Dashboard.jsx
    and superset.js, and it looks like they can just be pulled out.
    
    I am so happy to get rid of what used to be the messiest JS files in the
    whole repo.
    
    Thanks @graceguo!
---
 .../assets/javascripts/dashboard/Dashboard.jsx     | 381 ---------------------
 superset/assets/javascripts/modules/superset.js    | 262 --------------
 2 files changed, 643 deletions(-)

diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx
deleted file mode 100644
index 9e67647..0000000
--- a/superset/assets/javascripts/dashboard/Dashboard.jsx
+++ /dev/null
@@ -1,381 +0,0 @@
-import React from 'react';
-import { render } from 'react-dom';
-import d3 from 'd3';
-import { Alert } from 'react-bootstrap';
-import moment from 'moment';
-
-import GridLayout from './components/GridLayout';
-import Header from './components/Header';
-import { appSetup } from '../common';
-import AlertsWrapper from '../components/AlertsWrapper';
-import { t } from '../locales';
-import '../../stylesheets/dashboard.css';
-
-const superset = require('../modules/superset');
-const urlLib = require('url');
-const utils = require('../modules/utils');
-
-let px;
-
-appSetup();
-
-export function getInitialState(boostrapData) {
-  const dashboard = Object.assign(
-    {},
-    utils.controllerInterface,
-    boostrapData.dashboard_data,
-    { common: boostrapData.common });
-  dashboard.firstLoad = true;
-
-  dashboard.posDict = {};
-  if (dashboard.position_json) {
-    dashboard.position_json.forEach((position) => {
-      dashboard.posDict[position.slice_id] = position;
-    });
-  }
-  dashboard.refreshTimer = null;
-  const state = Object.assign({}, boostrapData, { dashboard });
-  return state;
-}
-
-function unload() {
-  const message = t('You have unsaved changes.');
-  window.event.returnValue = message; // Gecko + IE
-  return message; // Gecko + Webkit, Safari, Chrome etc.
-}
-
-function onBeforeUnload(hasChanged) {
-  if (hasChanged) {
-    window.addEventListener('beforeunload', unload);
-  } else {
-    window.removeEventListener('beforeunload', unload);
-  }
-}
-
-function renderAlert() {
-  render(
-    <div className="container-fluid">
-      <Alert bsStyle="warning">
-        <strong>{t('You have unsaved changes.')}</strong> {t('Click the')} &nbsp;
-        <i className="fa fa-save" />&nbsp;
-        {t('button on the top right to save your changes.')}
-      </Alert>
-    </div>,
-    document.getElementById('alert-container'),
-  );
-}
-
-function initDashboardView(dashboard) {
-  render(
-    <div>
-      <AlertsWrapper initMessages={dashboard.common.flash_messages} />
-      <Header dashboard={dashboard} />
-    </div>,
-    document.getElementById('dashboard-header'),
-  );
-  // eslint-disable-next-line no-param-reassign
-  dashboard.reactGridLayout = render(
-    <GridLayout dashboard={dashboard} />,
-    document.getElementById('grid-container'),
-  );
-
-  // Displaying widget controls on hover
-  $('.react-grid-item').hover(
-    function () {
-      $(this).find('.chart-controls').fadeIn(300);
-    },
-    function () {
-      $(this).find('.chart-controls').fadeOut(300);
-    },
-  );
-  $('div.grid-container').css('visibility', 'visible');
-
-  $('div.widget').click(function (e) {
-    const $this = $(this);
-    const $target = $(e.target);
-
-    if ($target.hasClass('slice_info')) {
-      $this.find('.slice_description').slideToggle(0, function () {
-        $this.find('.refresh').click();
-      });
-    } else if ($target.hasClass('controls-toggle')) {
-      $this.find('.chart-controls').toggle();
-    }
-  });
-  px.initFavStars();
-  $('[data-toggle="tooltip"]').tooltip({ container: 'body' });
-}
-
-export function dashboardContainer(dashboard, datasources, userid) {
-  return Object.assign({}, dashboard, {
-    type: 'dashboard',
-    filters: {},
-    curUserId: userid,
-    init() {
-      this.sliceObjects = [];
-      dashboard.slices.forEach((data) => {
-        if (data.error) {
-          const html = `<div class="alert alert-danger">${data.error}</div>`;
-          $(`#slice_${data.slice_id}`).find('.token').html(html);
-        } else {
-          const slice = px.Slice(data, datasources[data.form_data.datasource], this);
-          $(`#slice_${data.slice_id}`).find('a.refresh').click(() => {
-            slice.render(true);
-          });
-          this.sliceObjects.push(slice);
-        }
-      });
-      this.loadPreSelectFilters();
-      this.renderSlices(this.sliceObjects);
-      this.firstLoad = false;
-      this.bindResizeToWindowResize();
-    },
-    onChange() {
-      onBeforeUnload(true);
-      renderAlert();
-    },
-    onSave() {
-      onBeforeUnload(false);
-      $('#alert-container').html('');
-    },
-    loadPreSelectFilters() {
-      try {
-        const filters = JSON.parse(px.getParam('preselect_filters') || '{}');
-        for (const sliceId in filters) {
-          for (const col in filters[sliceId]) {
-            this.setFilter(sliceId, col, filters[sliceId][col], false, false);
-          }
-        }
-      } catch (e) {
-        // console.error(e);
-      }
-    },
-    setFilter(sliceId, col, vals, refresh) {
-      this.addFilter(sliceId, col, vals, false, refresh);
-    },
-    done(slice) {
-      const refresh = slice.getWidgetHeader().find('.refresh');
-      const data = slice.data;
-      const cachedWhen = moment.utc(data.cached_dttm).fromNow();
-      if (data !== undefined && data.is_cached) {
-        refresh
-        .addClass('danger')
-        .attr(
-          'title',
-          t('Served from data cached %s . Click to force refresh.', cachedWhen))
-        .tooltip('fixTitle');
-      } else {
-        refresh
-        .removeClass('danger')
-        .attr('title', t('Click to force refresh'))
-        .tooltip('fixTitle');
-      }
-    },
-    effectiveExtraFilters(sliceId) {
-      const f = [];
-      const immuneSlices = this.metadata.filter_immune_slices || [];
-      if (sliceId && immuneSlices.includes(sliceId)) {
-        // The slice is immune to dashboard filters
-        return f;
-      }
-
-      // Building a list of fields the slice is immune to filters on
-      let immuneToFields = [];
-      if (
-            sliceId &&
-            this.metadata.filter_immune_slice_fields &&
-            this.metadata.filter_immune_slice_fields[sliceId]) {
-        immuneToFields = this.metadata.filter_immune_slice_fields[sliceId];
-      }
-      for (const filteringSliceId in this.filters) {
-        if (filteringSliceId === sliceId.toString()) {
-          // Filters applied by the slice don't apply to itself
-          continue;
-        }
-        for (const field in this.filters[filteringSliceId]) {
-          if (!immuneToFields.includes(field)) {
-            f.push({
-              col: field,
-              op: 'in',
-              val: this.filters[filteringSliceId][field],
-            });
-          }
-        }
-      }
-      return f;
-    },
-    addFilter(sliceId, col, vals, merge = true, refresh = true) {
-      if (
-        this.getSlice(sliceId) && (
-          ['__from', '__to', '__time_col', '__time_grain', '__time_origin', '__granularity']
-            .indexOf(col) >= 0 ||
-            this.getSlice(sliceId).formData.groupby.indexOf(col) !== -1
-        )
-      ) {
-        if (!(sliceId in this.filters)) {
-          this.filters[sliceId] = {};
-        }
-        if (!(col in this.filters[sliceId]) || !merge) {
-          this.filters[sliceId][col] = vals;
-
-          // d3.merge pass in array of arrays while some value form filter components
-          // from and to filter box require string to be process and return
-        } else if (this.filters[sliceId][col] instanceof Array) {
-          this.filters[sliceId][col] = d3.merge([this.filters[sliceId][col], vals]);
-        } else {
-          this.filters[sliceId][col] = d3.merge([[this.filters[sliceId][col]], vals])[0] || '';
-        }
-        if (refresh) {
-          this.refreshExcept(sliceId);
-        }
-      }
-      this.updateFilterParamsInUrl();
-    },
-    readFilters() {
-      // Returns a list of human readable active filters
-      return JSON.stringify(this.filters, null, '  ');
-    },
-    updateFilterParamsInUrl() {
-      const urlObj = urlLib.parse(location.href, true);
-      urlObj.query = urlObj.query || {};
-      urlObj.query.preselect_filters = this.readFilters();
-      urlObj.search = null;
-      history.pushState(urlObj.query, window.title, urlLib.format(urlObj));
-    },
-    bindResizeToWindowResize() {
-      let resizeTimer;
-      const dash = this;
-      $(window).on('resize', () => {
-        clearTimeout(resizeTimer);
-        resizeTimer = setTimeout(() => {
-          dash.sliceObjects.forEach((slice) => {
-            slice.resize();
-          });
-        }, 500);
-      });
-    },
-    stopPeriodicRender() {
-      if (this.refreshTimer) {
-        clearTimeout(this.refreshTimer);
-        this.refreshTimer = null;
-      }
-    },
-    renderSlices(slices, force = false, interval = 0) {
-      if (!interval) {
-        slices.forEach(slice => slice.render(force));
-        return;
-      }
-      const meta = this.metadata;
-      const refreshTime = Math.max(interval, meta.stagger_time || 5000); // default 5 seconds
-      if (typeof meta.stagger_refresh !== 'boolean') {
-        meta.stagger_refresh = meta.stagger_refresh === undefined ?
-          true : meta.stagger_refresh === 'true';
-      }
-      const delay = meta.stagger_refresh ? refreshTime / (slices.length - 1) : 0;
-      slices.forEach((slice, i) => {
-        setTimeout(() => slice.render(force), delay * i);
-      });
-    },
-    startPeriodicRender(interval) {
-      this.stopPeriodicRender();
-      const dash = this;
-      const immune = this.metadata.timed_refresh_immune_slices || [];
-      const refreshAll = () => {
-        const slices = dash.sliceObjects
-          .filter(slice => immune.indexOf(slice.data.slice_id) === -1);
-        dash.fetchSlices(slices, true, interval * 0.2);
-      };
-      const fetchAndRender = function () {
-        refreshAll();
-        if (interval > 0) {
-          dash.refreshTimer = setTimeout(function () {
-            fetchAndRender();
-          }, interval);
-        }
-      };
-      fetchAndRender();
-    },
-    refreshExcept(sliceId) {
-      const immune = this.metadata.filter_immune_slices || [];
-      const slices = this.sliceObjects.filter(slice =>
-        slice.data.slice_id !== sliceId && immune.indexOf(slice.data.slice_id) === -1);
-      this.renderSlices(slices);
-    },
-    clearFilters(sliceId) {
-      delete this.filters[sliceId];
-      this.refreshExcept(sliceId);
-      this.updateFilterParamsInUrl();
-    },
-    removeFilter(sliceId, col, vals) {
-      if (sliceId in this.filters) {
-        if (col in this.filters[sliceId]) {
-          const a = [];
-          this.filters[sliceId][col].forEach(function (v) {
-            if (vals.indexOf(v) < 0) {
-              a.push(v);
-            }
-          });
-          this.filters[sliceId][col] = a;
-        }
-      }
-      this.refreshExcept(sliceId);
-      this.updateFilterParamsInUrl();
-    },
-    getSlice(sliceId) {
-      const id = parseInt(sliceId, 10);
-      let i = 0;
-      let slice = null;
-      while (i < this.sliceObjects.length) {
-        // when the slice is found, assign to slice and break;
-        if (this.sliceObjects[i].data.slice_id === id) {
-          slice = this.sliceObjects[i];
-          break;
-        }
-        i++;
-      }
-      return slice;
-    },
-    getAjaxErrorMsg(error) {
-      const respJSON = error.responseJSON;
-      return (respJSON && respJSON.message) ? respJSON.message :
-              error.responseText;
-    },
-    addSlicesToDashboard(sliceIds) {
-      const getAjaxErrorMsg = this.getAjaxErrorMsg;
-      $.ajax({
-        type: 'POST',
-        url: `/superset/add_slices/${dashboard.id}/`,
-        data: {
-          data: JSON.stringify({ slice_ids: sliceIds }),
-        },
-        success() {
-          // Refresh page to allow for slices to re-render
-          window.location.reload();
-        },
-        error(error) {
-          const errorMsg = getAjaxErrorMsg(error);
-          utils.showModal({
-            title: t('Error'),
-            body: t('Sorry, there was an error adding slices to this dashboard: %s', errorMsg),
-          });
-        },
-      });
-    },
-    updateDashboardTitle(title) {
-      this.dashboard_title = title;
-      this.onChange();
-    },
-  });
-}
-
-$(document).ready(() => {
-  // Getting bootstrapped data from the DOM
-  utils.initJQueryAjax();
-  const dashboardData = $('.dashboard').data('bootstrap');
-
-  const state = getInitialState(dashboardData);
-  px = superset(state);
-  const dashboard = dashboardContainer(state.dashboard, state.datasources, state.userId);
-  initDashboardView(dashboard);
-  dashboard.init();
-});
diff --git a/superset/assets/javascripts/modules/superset.js b/superset/assets/javascripts/modules/superset.js
deleted file mode 100644
index 9d58f02..0000000
--- a/superset/assets/javascripts/modules/superset.js
+++ /dev/null
@@ -1,262 +0,0 @@
-/* eslint camel-case: 0 */
-import $ from 'jquery';
-import Mustache from 'mustache';
-import vizMap from '../../visualizations/main';
-import { getExploreUrl } from '../explore/exploreUtils';
-import { applyDefaultFormData } from '../explore/stores/store';
-import { t } from '../locales';
-
-const utils = require('./utils');
-
-/* eslint wrap-iife: 0 */
-const px = function (state) {
-  let slice;
-  const timeout = state.common.conf.SUPERSET_WEBSERVER_TIMEOUT;
-  function getParam(name) {
-    /* eslint no-useless-escape: 0 */
-    const formattedName = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
-    const regex = new RegExp('[\\?&]' + formattedName + '=([^&#]*)');
-    const results = regex.exec(location.search);
-    return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
-  }
-  function initFavStars() {
-    const baseUrl = '/superset/favstar/';
-    // Init star behavihor for favorite
-    function show() {
-      if ($(this).hasClass('selected')) {
-        $(this).html('<i class="fa fa-star"></i>');
-      } else {
-        $(this).html('<i class="fa fa-star-o"></i>');
-      }
-    }
-    $('.favstar')
-    .attr('title', t('Click to favorite/unfavorite'))
-    .css('cursor', 'pointer')
-    .each(show)
-    .each(function () {
-      let url = baseUrl + $(this).attr('class_name');
-      const star = this;
-      url += '/' + $(this).attr('obj_id') + '/';
-      $.getJSON(url + 'count/', function (data) {
-        if (data.count > 0) {
-          $(star).addClass('selected').each(show);
-        }
-      });
-    })
-    .click(function () {
-      $(this).toggleClass('selected');
-      let url = baseUrl + $(this).attr('class_name');
-      url += '/' + $(this).attr('obj_id') + '/';
-      if ($(this).hasClass('selected')) {
-        url += 'select/';
-      } else {
-        url += 'unselect/';
-      }
-      $.get(url);
-      $(this).each(show);
-    })
-    .tooltip();
-  }
-  const Slice = function (data, datasource, controller) {
-    const token = $('#token_' + data.slice_id);
-    const controls = $('#controls_' + data.slice_id);
-    const containerId = 'con_' + data.slice_id;
-    const selector = '#' + containerId;
-    const container = $(selector);
-    const sliceId = data.slice_id;
-    const formData = applyDefaultFormData(data.form_data);
-    const sliceCell = $(`#${data.slice_id}-cell`);
-    slice = {
-      data,
-      formData,
-      container,
-      containerId,
-      datasource,
-      selector,
-      getWidgetHeader() {
-        return this.container.parents('div.widget').find('.chart-header');
-      },
-      render_template(s) {
-        const context = {
-          width: this.width,
-          height: this.height,
-        };
-        return Mustache.render(s, context);
-      },
-      jsonEndpoint(data) {
-        return this.endpoint(data, 'json');
-      },
-      endpoint(data, endpointType = 'json') {
-        let endpoint = getExploreUrl(data, endpointType, this.force);
-        if (endpoint.charAt(0) !== '/') {
-          // Known issue for IE <= 11:
-          // https://connect.microsoft.com/IE/feedbackdetail/view/1002846/pathname-incorrect-for-out-of-document-elements
-          endpoint = '/' + endpoint;
-        }
-        return endpoint;
-      },
-      d3format(col, number) {
-        // uses the utils memoized d3format function and formats based on
-        // column level defined preferences
-        let format = '.3s';
-        if (this.datasource.column_formats[col]) {
-          format = this.datasource.column_formats[col];
-        }
-        return utils.d3format(format, number);
-      },
-      /* eslint no-shadow: 0 */
-      always(data) {
-        if (data && data.query) {
-          slice.viewSqlQuery = data.query;
-        }
-      },
-      done(payload) {
-        Object.assign(data, payload);
-
-        token.find('img.loading').hide();
-        container.fadeTo(0.5, 1);
-        sliceCell.removeClass('slice-cell-highlight');
-        container.show();
-
-        $('.query-and-save button').removeAttr('disabled');
-        this.always(data);
-        controller.done(this);
-      },
-      getErrorMsg(xhr) {
-        let msg = '';
-        if (!xhr.responseText) {
-          const status = xhr.status;
-          if (status === 0) {
-            // This may happen when the worker in gunicorn times out
-            msg += (
-              t('The server could not be reached. You may want to ' +
-              'verify your connection and try again.'));
-          } else {
-            msg += (t('An unknown error occurred. (Status: %s )', status));
-          }
-        }
-        return msg;
-      },
-      error(msg, xhr) {
-        let errorMsg = msg;
-        token.find('img.loading').hide();
-        container.fadeTo(0.5, 1);
-        sliceCell.removeClass('slice-cell-highlight');
-        let errHtml = '';
-        let o;
-        try {
-          o = JSON.parse(msg);
-          if (o.error) {
-            errorMsg = o.error;
-          }
-        } catch (e) {
-          // pass
-        }
-        if (errorMsg) {
-          errHtml += `<div class="alert alert-danger">${errorMsg}</div>`;
-        }
-        if (xhr) {
-          if (xhr.statusText === 'timeout') {
-            errHtml += (
-              '<div class="alert alert-warning">' +
-              'Query timeout - visualization query are set to time out ' +
-              `at ${timeout} seconds.</div>`);
-          } else {
-            const extendedMsg = this.getErrorMsg(xhr);
-            if (extendedMsg) {
-              errHtml += `<div class="alert alert-danger">${extendedMsg}</div>`;
-            }
-          }
-        }
-        container.html(errHtml);
-        container.show();
-        $('span.query').removeClass('disabled');
-        $('.query-and-save button').removeAttr('disabled');
-        this.always(o);
-        controller.error(this);
-      },
-      clearError() {
-        $(selector + ' div.alert').remove();
-      },
-      width() {
-        return container.width();
-      },
-      height() {
-        let others = 0;
-        const widget = container.parents('.widget');
-        const sliceDescription = widget.find('.slice_description');
-        if (sliceDescription.is(':visible')) {
-          others += widget.find('.slice_description').height() + 25;
-        }
-        others += widget.find('.chart-header').height();
-        return widget.height() - others - 10;
-      },
-      bindResizeToWindowResize() {
-        let resizeTimer;
-        const slice = this;
-        $(window).on('resize', function () {
-          clearTimeout(resizeTimer);
-          resizeTimer = setTimeout(function () {
-            slice.resize();
-          }, 500);
-        });
-      },
-      render(force) {
-        if (force === undefined) {
-          this.force = false;
-        } else {
-          this.force = force;
-        }
-        const formDataExtra = Object.assign({}, formData);
-        formDataExtra.extra_filters = controller.effectiveExtraFilters(sliceId);
-        controls.find('a.exploreChart').attr('href', getExploreUrl(formDataExtra));
-        controls.find('a.exportCSV').attr('href', getExploreUrl(formDataExtra, 'csv'));
-        token.find('img.loading').show();
-        container.fadeTo(0.5, 0.25);
-        sliceCell.addClass('slice-cell-highlight');
-        container.css('height', this.height());
-        $.ajax({
-          url: this.jsonEndpoint(formDataExtra),
-          timeout: timeout * 1000,
-          success: (queryResponse) => {
-            try {
-              vizMap[formData.viz_type](this, queryResponse);
-              this.done(queryResponse);
-            } catch (e) {
-              this.error(t('An error occurred while rendering the visualization: %s', e));
-            }
-          },
-          error: (err) => {
-            this.error(err.responseText, err);
-          },
-        });
-      },
-      resize() {
-        this.render();
-      },
-      addFilter(col, vals, merge = true, refresh = true) {
-        controller.addFilter(sliceId, col, vals, merge, refresh);
-      },
-      setFilter(col, vals, refresh = true) {
-        controller.setFilter(sliceId, col, vals, refresh);
-      },
-      getFilters() {
-        return controller.filters[sliceId];
-      },
-      clearFilter() {
-        controller.clearFilter(sliceId);
-      },
-      removeFilter(col, vals) {
-        controller.removeFilter(sliceId, col, vals);
-      },
-    };
-    return slice;
-  };
-  // Export public functions
-  return {
-    getParam,
-    initFavStars,
-    Slice,
-  };
-};
-module.exports = px;

-- 
To stop receiving notification emails like this one, please contact
['"commits@superset.apache.org" <co...@superset.apache.org>'].