<commit_before>import * as _ from 'underscore';
import PromiseRejectionError from '@/lib/promise-rejection-error';
import { durationHumanize } from '@/filters';
import template from './dashboard.html';
import shareDashboardTemplate from './share-dashboard.html';
import './dashboard.less';

function isWidgetPositionChanged(oldPosition, newPosition) {
  const fields = ['col', 'row', 'sizeX', 'sizeY', 'autoHeight'];
  oldPosition = _.pick(oldPosition, fields);
  newPosition = _.pick(newPosition, fields);
  return !!_.find(fields, key => newPosition[key] !== oldPosition[key]);
}

function collectWidgetPositions(widgets) {
  return _.chain(widgets)
    .map(widget => [widget.id, _.clone(widget.options.position)])
    .object()
    .value();
}

function getWidgetsWithChangedPositions(widgets, savedPositions) {
  return _.filter(widgets, (widget) => {
    const savedPosition = savedPositions[widget.id];
    if (!_.isObject(savedPosition)) {
      return true;
    }
    return isWidgetPositionChanged(savedPosition, widget.options.position);
  });
}

function DashboardCtrl(
  $rootScope,
  $routeParams,
  $location,
  $timeout,
  $q,
  $uibModal,
  Title,
  AlertDialog,
  Dashboard,
  currentUser,
  clientConfig,
  Events,
  dashboardGridOptions,
  toastr,
) {
  this.saveInProgress = false;

  // This variable should always be in sync with widgets
  let savedWidgetPositions = {};

  const saveDashboardLayout = (widgets) => {
    if (!this.dashboard.canEdit()) {
      return;
    }

    this.saveInProgress = true;
    const showMessages = true; // this.layoutEditing;
    // Temporarily disable grid editing (but allow user to use UI controls)
    this.dashboardGridOptions.draggable.enabled = false;
    this.dashboardGridOptions.resizable.enabled = false;
    return $q
      .all(_.map(widgets, widget => widget.$save()))
      .then(() => {
        if (showMessages) {
          toastr.success('Changes saved.');
        }
      })
      .catch(() => {
        if (showMessages) {
          toastr.error('Error saving changes.');
        }
      })
      .finally(() => {
        this.saveInProgress = false;
        // If user didn't disable editing mode while saving - restore grid
        this.dashboardGridOptions.draggable.enabled = this.layoutEditing;
        this.dashboardGridOptions.resizable.enabled = this.layoutEditing;
      });
  };

  this.layoutEditing = false;
  this.dashboardGridOptions = _.extend({}, dashboardGridOptions, {
    resizable: {
      enabled: false,
      handles: ['n', 'e', 's', 'w', 'ne', 'se', 'sw', 'nw'],
    },
    draggable: {
      enabled: false,
    },
  });

  this.isFullscreen = false;
  this.refreshRate = null;
  this.isGridDisabled = false;
  this.showPermissionsControl = clientConfig.showPermissionsControl;
  this.globalParameters = [];

  this.refreshRates = clientConfig.dashboardRefreshIntervals.map(interval => ({
    name: durationHumanize(interval),
    rate: interval,
  }));

  $rootScope.$on('gridster-mobile-changed', ($event, gridster) => {
    this.isGridDisabled = gridster.isMobile;
  });

  this.setRefreshRate = (rate, load = true) => {
    this.refreshRate = rate;
    if (rate !== null) {
      if (load) {
      this.loadDashboard(true);
      }
      this.autoRefresh();
    }
  };

  this.extractGlobalParameters = () => {
    let globalParams = {};
    this.dashboard.widgets.forEach((widget) => {
      if (widget.getQuery()) {
        widget
          .getQuery()
          .getParametersDefs()
          .filter(p => p.global)
          .forEach((param) => {
            const defaults = {};
            defaults[param.name] = _.create(Object.getPrototypeOf(param), param);
            defaults[param.name].locals = [];
            globalParams = _.defaults(globalParams, defaults);
            globalParams[param.name].locals.push(param);
          });
      }
    });
    this.globalParameters = _.values(globalParams);
  };

  this.onGlobalParametersChange = () => {
    this.globalParameters.forEach((global) => {
      global.locals.forEach((local) => {
        local.value = global.value;
      });
    });
  };

  const collectFilters = (dashboard, forceRefresh) => {
    const queryResultPromises = _.compact(this.dashboard.widgets.map(widget => widget.getQueryResult(forceRefresh)))
      .map(queryResult => queryResult.toPromise());

    $q.all(queryResultPromises).then((queryResults) => {
      const filters = {};
      queryResults.forEach((queryResult) => {
        const queryFilters = queryResult.getFilters();
        queryFilters.forEach((queryFilter) => {
          const hasQueryStringValue = _.has($location.search(), queryFilter.name);

          if (!(hasQueryStringValue || dashboard.dashboard_filters_enabled)) {
            // If dashboard filters not enabled, or no query string value given,
            // skip filters linking.
            return;
          }

          if (hasQueryStringValue) {
            queryFilter.current = $location.search()[queryFilter.name];
          }

          if (!_.has(filters, queryFilter.name)) {
            const filter = _.extend({}, queryFilter);
            filters[filter.name] = filter;
            filters[filter.name].originFilters = [];
          }

          // TODO: merge values.
          filters[queryFilter.name].originFilters.push(queryFilter);
        });
      });

      this.filters = _.values(filters);
      this.filtersOnChange = (filter) => {
        _.each(filter.originFilters, (originFilter) => {
          originFilter.current = filter.current;
        });
      };
    });
  };

  const renderDashboard = (dashboard, force) => {
    Title.set(dashboard.name);
    this.extractGlobalParameters();
    collectFilters(dashboard, force);
  };

  this.loadDashboard = _.throttle((force) => {
    this.dashboard = Dashboard.get(
      { slug: $routeParams.dashboardSlug },
      (dashboard) => {
        Events.record('view', 'dashboard', dashboard.id);
        renderDashboard(dashboard, force);

        if ($location.search().edit === true) {
          $location.search('edit', null);
          this.editLayout(true);
        }

        if ($location.search().refresh !== undefined) {
          if (this.refreshRate === null) {
            const refreshRate = Math.max(30, parseFloat($location.search().refresh));

            this.setRefreshRate({<commit_msg>Fix: dashboard was reloading when clicking on refresh.<commit_after>import * as _ from 'underscore';
import PromiseRejectionError from '@/lib/promise-rejection-error';
import { durationHumanize } from '@/filters';
import template from './dashboard.html';
import shareDashboardTemplate from './share-dashboard.html';
import './dashboard.less';

function isWidgetPositionChanged(oldPosition, newPosition) {
  const fields = ['col', 'row', 'sizeX', 'sizeY', 'autoHeight'];
  oldPosition = _.pick(oldPosition, fields);
  newPosition = _.pick(newPosition, fields);
  return !!_.find(fields, key => newPosition[key] !== oldPosition[key]);
}

function collectWidgetPositions(widgets) {
  return _.chain(widgets)
    .map(widget => [widget.id, _.clone(widget.options.position)])
    .object()
    .value();
}

function getWidgetsWithChangedPositions(widgets, savedPositions) {
  return _.filter(widgets, (widget) => {
    const savedPosition = savedPositions[widget.id];
    if (!_.isObject(savedPosition)) {
      return true;
    }
    return isWidgetPositionChanged(savedPosition, widget.options.position);
  });
}

function DashboardCtrl(
  $rootScope,
  $routeParams,
  $location,
  $timeout,
  $q,
  $uibModal,
  Title,
  AlertDialog,
  Dashboard,
  currentUser,
  clientConfig,
  Events,
  dashboardGridOptions,
  toastr,
) {
  this.saveInProgress = false;

  // This variable should always be in sync with widgets
  let savedWidgetPositions = {};

  const saveDashboardLayout = (widgets) => {
    if (!this.dashboard.canEdit()) {
      return;
    }

    this.saveInProgress = true;
    const showMessages = true; // this.layoutEditing;
    // Temporarily disable grid editing (but allow user to use UI controls)
    this.dashboardGridOptions.draggable.enabled = false;
    this.dashboardGridOptions.resizable.enabled = false;
    return $q
      .all(_.map(widgets, widget => widget.$save()))
      .then(() => {
        if (showMessages) {
          toastr.success('Changes saved.');
        }
      })
      .catch(() => {
        if (showMessages) {
          toastr.error('Error saving changes.');
        }
      })
      .finally(() => {
        this.saveInProgress = false;
        // If user didn't disable editing mode while saving - restore grid
        this.dashboardGridOptions.draggable.enabled = this.layoutEditing;
        this.dashboardGridOptions.resizable.enabled = this.layoutEditing;
      });
  };

  this.layoutEditing = false;
  this.dashboardGridOptions = _.extend({}, dashboardGridOptions, {
    resizable: {
      enabled: false,
      handles: ['n', 'e', 's', 'w', 'ne', 'se', 'sw', 'nw'],
    },
    draggable: {
      enabled: false,
    },
  });

  this.isFullscreen = false;
  this.refreshRate = null;
  this.isGridDisabled = false;
  this.showPermissionsControl = clientConfig.showPermissionsControl;
  this.globalParameters = [];

  this.refreshRates = clientConfig.dashboardRefreshIntervals.map(interval => ({
    name: durationHumanize(interval),
    rate: interval,
  }));

  $rootScope.$on('gridster-mobile-changed', ($event, gridster) => {
    this.isGridDisabled = gridster.isMobile;
  });

  this.setRefreshRate = (rate, load = true) => {
    this.refreshRate = rate;
    if (rate !== null) {
      if (load) {
      this.loadDashboard(true);
      }
      this.autoRefresh();
    }
  };

  this.extractGlobalParameters = () => {
    let globalParams = {};
    this.dashboard.widgets.forEach((widget) => {
      if (widget.getQuery()) {
        widget
          .getQuery()
          .getParametersDefs()
          .filter(p => p.global)
          .forEach((param) => {
            const defaults = {};
            defaults[param.name] = _.create(Object.getPrototypeOf(param), param);
            defaults[param.name].locals = [];
            globalParams = _.defaults(globalParams, defaults);
            globalParams[param.name].locals.push(param);
          });
      }
    });
    this.globalParameters = _.values(globalParams);
  };

  this.onGlobalParametersChange = () => {
    this.globalParameters.forEach((global) => {
      global.locals.forEach((local) => {
        local.value = global.value;
      });
    });
  };

  const collectFilters = (dashboard, forceRefresh) => {
    const queryResultPromises = _.compact(this.dashboard.widgets.map(widget => widget.getQueryResult(forceRefresh)))
      .map(queryResult => queryResult.toPromise());

    $q.all(queryResultPromises).then((queryResults) => {
      const filters = {};
      queryResults.forEach((queryResult) => {
        const queryFilters = queryResult.getFilters();
        queryFilters.forEach((queryFilter) => {
          const hasQueryStringValue = _.has($location.search(), queryFilter.name);

          if (!(hasQueryStringValue || dashboard.dashboard_filters_enabled)) {
            // If dashboard filters not enabled, or no query string value given,
            // skip filters linking.
            return;
          }

          if (hasQueryStringValue) {
            queryFilter.current = $location.search()[queryFilter.name];
          }

          if (!_.has(filters, queryFilter.name)) {
            const filter = _.extend({}, queryFilter);
            filters[filter.name] = filter;
            filters[filter.name].originFilters = [];
          }

          // TODO: merge values.
          filters[queryFilter.name].originFilters.push(queryFilter);
        });
      });

      this.filters = _.values(filters);
      this.filtersOnChange = (filter) => {
        _.each(filter.originFilters, (originFilter) => {
          originFilter.current = filter.current;
        });
      };
    });
  };

  const renderDashboard = (dashboard, force) => {
    Title.set(dashboard.name);
    this.extractGlobalParameters();
    collectFilters(dashboard, force);
  };

  this.loadDashboard = _.throttle((force) => {
    Dashboard.get(
      { slug: $routeParams.dashboardSlug },
      (dashboard) => {
        this.dashboard = dashboard;
        Events.record('view', 'dashboard', dashboard.id);
        renderDashboard(dashboard, force);

        if ($location.search().edit === true) {
          $location.search('edit', null);
          this.editLayout(true);
        }

        if ($location.search().refresh !== undefined) {
          if (this.refreshRate === null) {
            const refreshRate = Math.max(30, parseFloat($location.search().refresh));

            this.setRefreshRate({
{'fraction_non_alphanumeric': 0.14170452765157449, 'fraction_numerical': 0.00031333228889237035, 'mean_word_length': 2.7505875440658047, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3617082', 'n_tokens_mistral': 3662, 'n_tokens_neox': 3668, 'n_words': 691}