<commit_before>/**
 * @flow
 *
 * @Author: <NAME> <wahaj>
 * @Date:   2018-02-07T10:55:24+11:00
 * @Email:  <EMAIL>
 * @Last modified by:   mike
 * @Last modified time: 2018-02-28T15:21:43+11:00
 *
 * dbKoda - a modern, open source code editor, for MongoDB.
 * Copyright (C) 2017-2018 Southbank Software
 *
 * This file is part of dbKoda.
 *
 * dbKoda is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * dbKoda is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with dbKoda.  If not, see <http://www.gnu.org/licenses/>.
 */

import * as React from 'react';
import _ from 'lodash';
import { autorun } from 'mobx';
import type { WidgetState } from '~/api/Widget';
import type { PerformancePanelState } from '~/api/PerformancePanel';
import * as d3 from 'd3';
// $FlowFixMe
import 'd3-selection-multi';
import { PopoverInteractionKind } from '@blueprintjs/core';
// $FlowFixMe
import { Popover2 } from '@blueprintjs/labs';
import Legend from './Legend';
import Widget from './Widget';
import { convertUnits } from './Utils';
import './ProgressBarWidget.scss';

let colors = ['#A27EB7', '#DC5D3E', '#39B160', '#643798', '#2E547A', '#3333cc'];
const BAR_CLIPPATH_ID = 'barCP';
const vbWidth = 400;
const vbHeight = 50;
const barHeight = Math.round(vbHeight * 50 / 100);
const chartWidth = vbWidth - 60;

type Props = {
  performancePanel: PerformancePanelState & { highWaterMarkGroups: Object },
  widget: WidgetState,
  widgetStyle: *,
  rotate?: number
};

export default class ProgressBarWidget extends React.Component<Props> {
  _chartEl: *;
  _chart: *;
  _dataGroup: *;
  _valueRec: *;
  _autorunDisposer: *;
  _tipEl: *;
  _tipREl: *;
  _tip: *;
  _chartTotalEl: *;
  _totalDivisor: *;
  _chartLabel: *;
  _bVertical: false;
  _unit: any;
  _itemValues: *;
  hasRendered: *;
  toolTipLegend: *;
  colors: *;

  _createD3View = (bVertical: boolean) => {
    if (bVertical) {
      // $FlowFixMe
      this._bVertical = bVertical;
      this._tip = d3.select(this._tipREl).style('opacity', 0);
      d3.select(this._tipEl).style('opacity', 0);
    } else {
      this._tip = d3.select(this._tipEl).style('opacity', 0);
      d3.select(this._tipREl).style('opacity', 0);
    }
    this._chart = d3.select(this._chartEl).attrs({
      width: '100%',
      height: '100%',
      viewBox: this._bVertical
        ? `0 0 ${vbHeight} ${vbWidth}`
        : `0 0 ${vbWidth} ${vbHeight}`,
      perserveAspectRatio: 'xMinYMid'
    });

    if (!this._chart) return;

    const defs = this._chart.append('defs');

    // defined a clippath to clip unwanted area from rendering
    defs
      .append('clipPath')
      .attr('id', BAR_CLIPPATH_ID)
      .append('rect')
      .attrs({
        rx: 10,
        ry: 10,
        height: barHeight - 2,
        width: chartWidth,
        x: 0,
        y: 1
      });

    const posTransY = vbHeight / 2 - barHeight / 2;
    // chart background
    // $FlowFixMe
    const chartBG = this._chart
      .append('g')
      .attr('transform', `translate(30, ${posTransY})`);
    chartBG.append('rect').attrs({
      class: 'chart-bg',
      rx: 0,
      ry: 0,
      fill: 'gray',
      'clip-path': `url(#${BAR_CLIPPATH_ID})`,
      height: barHeight,
      width: chartWidth,
      x: 0
    });
    // $FlowFixMe
    this._dataGroup = this._chart
      .append('g')
      .attr('transform', `translate(30, ${posTransY})`);

    if (this._bVertical) {
      const rotatePos = vbWidth / 2 - barHeight;
      chartBG.attr(
        'transform',
        `translate(30, ${posTransY}) rotate(270 ${rotatePos} ${rotatePos})`
      );
      // $FlowFixMe
      this._dataGroup.attr(
        'transform',
        `translate(30, ${posTransY}) rotate(270 ${rotatePos} ${rotatePos})`
      );
    }
  };

  _removeD3View = () => {
    if (this._chart) {
      this._chart.selectAll('*').remove();
      this._dataGroup = null;
      this._chart = null;
    }
  };

  _recreateD3View = () => {
    const bVertical = this.props.widget.showVertical === true;
    this._removeD3View();
    this._createD3View(bVertical);
  };

  _updateD3ViewData = (data: Object) => {
    if (this.props.widget.colorList) {
      colors = this.props.widget.colorList;
    }
    // eslint-disable-next-line
    const bVertical = this.props.widget.showVertical === true;
    const sData = Object.keys(data); // .sort(); // sort according to keys to keep the color same
    let arrData = [];
    let sumOfValues = 0;
    let sumOfHWM = 0;
    const { stats } = this.props.performancePanel;
    if (sData.length > 1) {
      // Case where there are more than 1 data items to display
      for (let I = 0; I < sData.length; I += 1) {
        if (I === 0 && this.props.widget.firstValueIsHighWaterMark) {
          // Discard first value, set high water mark.
          this._totalDivisor = data[sData[I]];
          sumOfValues = data[sData[I]];
        }
        arrData.push({
          // $FlowFixMe
          color: colors[I],
          value: data[sData[I]],
          key: sData[I]
        });
      }
      if (!this.props.widget.maintainOrder) {
        arrData = _.sortBy(arrData, [
          function(o) {
            return o.value;
          }
        ]);
        arrData = _.reverse(arrData);
      }
      let displayOrder = 0;
      arrData = arrData.map(elem => {
        if (!this.props.widget.firstValueIsHighWaterMark) {
          sumOfValues += elem.value;
          // $FlowFixMe
          elem.sumValue = sumOfValues;
          sumOfHWM += stats[elem.key].hwm;
        } else {
          // $FlowFixMe
          elem.sumValue = elem.value;
        }
        // $FlowFixMe
        elem.displayOrder = displayOrder;
        displayOrder += 1;
        return elem;
      });
      if (
        !this.props.widget.firstValueIsHighWaterMark ||
        this.props.widget.waterMarkGroup
      ) {
        arrData = _.reverse(arrData);
      }
      if (this.props.widget.firstValueIsHighWaterMark) {
        this._chartLabel = sumOfValues;
      } else {
        this._chartLabel = sumOfHWM; // for multi item chart it will show the sum of value in the text label
      }
    } else if (sData.length === 1) {
      // specific case of only one data item
      // $FlowFixMe
      arrData.push({
        // $FlowFixMe
        color: colors[0],
        value: data[sData[0]],
        key: sData[0],
        sumValue: data[sData[0]],
        displayOrder: 0
      });
      sumOfHWM = stats[sData[0]].hwm;
      sumOfValues = arrData[0].value;
      if (this.props.widget.items.length === 1) {
        // If it is a single chart, just show the real value.
        this._chartLabel = data[Object.keys(data)[0]];
      } else if (!this.props.widget.firstValueIsHighWaterMark) {
        // First value is high water mark, show first value.
        this._chartLabel = sumOfHWM;
      } else {
        this._chartLabel = sumOfValues; // If it is a stacked chart, show the sum of the values.
      }
    } else {
      return;
    }
    if (this.props.widget.useHighWaterMark) {
      const newHighWaterMark = sumOfHWM;
      if (
        !this._totalDivisor ||
        (this._totalDivisor > 0 && sumOfHWM >= this._totalDivisor) // update HighWaterMark only it is not set or smaller then the current sum of values
      ) {
        this._totalDivisor = newHighWaterMark;
      }
    } else if (!this.props.widget.firstValueIsHighWaterMark) {
      this._totalDivisor = sumOfHWM;
    }

    // If part of high water mark group, set group value.
    if (this.props.widget.waterMarkGroup) {
      if (!this.props.performancePanel.highWaterMarkGroups) {
        this.props.performancePanel.highWaterMarkGroups = {};
      }
      // $FlowFixMe
      const highestValue = this.props.performancePanel
        .highWaterMarkGroups[this.props.widget.waterMarkGroup];
      if (this._totalDivisor > highestValue) {
        // $FlowFixMe
        this.props.performancePanel.highWaterMarkGroups[
          this.props.widget.waterMarkGroup
        ] = this._totalDivisor;
      }
      // $FlowFixMe
      this._totalDivisor = this.props.performancePanel.highWaterMarkGroups[
        this.props.widget.waterMarkGroup
      ];
    }<commit_msg>update progress bar flow error<commit_after>/**
 * @flow
 *
 * @Author: <NAME> <wahaj>
 * @Date:   2018-02-07T10:55:24+11:00
 * @Email:  <EMAIL>
 * @Last modified by:   mike
 * @Last modified time: 2018-02-28T15:21:43+11:00
 *
 * dbKoda - a modern, open source code editor, for MongoDB.
 * Copyright (C) 2017-2018 Southbank Software
 *
 * This file is part of dbKoda.
 *
 * dbKoda is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * dbKoda is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with dbKoda.  If not, see <http://www.gnu.org/licenses/>.
 */

import * as React from 'react';
import _ from 'lodash';
import { autorun } from 'mobx';
import type { WidgetState } from '~/api/Widget';
import type { PerformancePanelState } from '~/api/PerformancePanel';
import * as d3 from 'd3';
// $FlowFixMe
import 'd3-selection-multi';
import { PopoverInteractionKind } from '@blueprintjs/core';
// $FlowFixMe
import { Popover2 } from '@blueprintjs/labs';
import Legend from './Legend';
import Widget from './Widget';
import { convertUnits } from './Utils';
import './ProgressBarWidget.scss';

let colors = ['#A27EB7', '#DC5D3E', '#39B160', '#643798', '#2E547A', '#3333cc'];
const BAR_CLIPPATH_ID = 'barCP';
const vbWidth = 400;
const vbHeight = 50;
const barHeight = Math.round(vbHeight * 50 / 100);
const chartWidth = vbWidth - 60;

type Props = {
  performancePanel: PerformancePanelState & { highWaterMarkGroups: Object },
  widget: WidgetState & { highWaterMarkGroups: any, firstValueIsHighWaterMark: boolean, waterMarkGroup: any, colorList: any, maintainOrder: boolean },
  widgetStyle: *,
  rotate?: number
};

export default class ProgressBarWidget extends React.Component<Props> {
  _chartEl: *;
  _chart: *;
  _dataGroup: *;
  _valueRec: *;
  _autorunDisposer: *;
  _tipEl: *;
  _tipREl: *;
  _tip: *;
  _chartTotalEl: *;
  _totalDivisor: *;
  _chartLabel: *;
  _bVertical: false;
  _unit: any;
  _itemValues: *;
  hasRendered: *;
  toolTipLegend: *;
  colors: *;

  _createD3View = (bVertical: boolean) => {
    if (bVertical) {
      // $FlowFixMe
      this._bVertical = bVertical;
      this._tip = d3.select(this._tipREl).style('opacity', 0);
      d3.select(this._tipEl).style('opacity', 0);
    } else {
      this._tip = d3.select(this._tipEl).style('opacity', 0);
      d3.select(this._tipREl).style('opacity', 0);
    }
    this._chart = d3.select(this._chartEl).attrs({
      width: '100%',
      height: '100%',
      viewBox: this._bVertical
        ? `0 0 ${vbHeight} ${vbWidth}`
        : `0 0 ${vbWidth} ${vbHeight}`,
      perserveAspectRatio: 'xMinYMid'
    });

    if (!this._chart) return;

    const defs = this._chart.append('defs');

    // defined a clippath to clip unwanted area from rendering
    defs
      .append('clipPath')
      .attr('id', BAR_CLIPPATH_ID)
      .append('rect')
      .attrs({
        rx: 10,
        ry: 10,
        height: barHeight - 2,
        width: chartWidth,
        x: 0,
        y: 1
      });

    const posTransY = vbHeight / 2 - barHeight / 2;
    // chart background
    // $FlowFixMe
    const chartBG = this._chart
      .append('g')
      .attr('transform', `translate(30, ${posTransY})`);
    chartBG.append('rect').attrs({
      class: 'chart-bg',
      rx: 0,
      ry: 0,
      fill: 'gray',
      'clip-path': `url(#${BAR_CLIPPATH_ID})`,
      height: barHeight,
      width: chartWidth,
      x: 0
    });
    // $FlowFixMe
    this._dataGroup = this._chart
      .append('g')
      .attr('transform', `translate(30, ${posTransY})`);

    if (this._bVertical) {
      const rotatePos = vbWidth / 2 - barHeight;
      chartBG.attr(
        'transform',
        `translate(30, ${posTransY}) rotate(270 ${rotatePos} ${rotatePos})`
      );
      // $FlowFixMe
      this._dataGroup.attr(
        'transform',
        `translate(30, ${posTransY}) rotate(270 ${rotatePos} ${rotatePos})`
      );
    }
  };

  _removeD3View = () => {
    if (this._chart) {
      this._chart.selectAll('*').remove();
      this._dataGroup = null;
      this._chart = null;
    }
  };

  _recreateD3View = () => {
    const bVertical = this.props.widget.showVertical === true;
    this._removeD3View();
    this._createD3View(bVertical);
  };

  _updateD3ViewData = (data: Object) => {
    if (this.props.widget.colorList) {
      colors = this.props.widget.colorList;
    }
    // eslint-disable-next-line
    const bVertical = this.props.widget.showVertical === true;
    const sData = Object.keys(data); // .sort(); // sort according to keys to keep the color same
    let arrData = [];
    let sumOfValues = 0;
    let sumOfHWM = 0;
    const { stats } = this.props.performancePanel;
    if (sData.length > 1) {
      // Case where there are more than 1 data items to display
      for (let I = 0; I < sData.length; I += 1) {
        if (I === 0 && this.props.widget.firstValueIsHighWaterMark) {
          // Discard first value, set high water mark.
          this._totalDivisor = data[sData[I]];
          sumOfValues = data[sData[I]];
        }
        arrData.push({
          // $FlowFixMe
          color: colors[I],
          value: data[sData[I]],
          key: sData[I]
        });
      }
      if (!this.props.widget.maintainOrder) {
        arrData = _.sortBy(arrData, [
          function(o) {
            return o.value;
          }
        ]);
        arrData = _.reverse(arrData);
      }
      let displayOrder = 0;
      arrData = arrData.map(elem => {
        if (!this.props.widget.firstValueIsHighWaterMark) {
          sumOfValues += elem.value;
          // $FlowFixMe
          elem.sumValue = sumOfValues;
          sumOfHWM += stats[elem.key].hwm;
        } else {
          // $FlowFixMe
          elem.sumValue = elem.value;
        }
        // $FlowFixMe
        elem.displayOrder = displayOrder;
        displayOrder += 1;
        return elem;
      });
      if (
        !this.props.widget.firstValueIsHighWaterMark ||
        this.props.widget.waterMarkGroup
      ) {
        arrData = _.reverse(arrData);
      }
      if (this.props.widget.firstValueIsHighWaterMark) {
        this._chartLabel = sumOfValues;
      } else {
        this._chartLabel = sumOfHWM; // for multi item chart it will show the sum of value in the text label
      }
    } else if (sData.length === 1) {
      // specific case of only one data item
      // $FlowFixMe
      arrData.push({
        // $FlowFixMe
        color: colors[0],
        value: data[sData[0]],
        key: sData[0],
        sumValue: data[sData[0]],
        displayOrder: 0
      });
      sumOfHWM = stats[sData[0]].hwm;
      sumOfValues = arrData[0].value;
      if (this.props.widget.items.length === 1) {
        // If it is a single chart, just show the real value.
        this._chartLabel = data[Object.keys(data)[0]];
      } else if (!this.props.widget.firstValueIsHighWaterMark) {
        // First value is high water mark, show first value.
        this._chartLabel = sumOfHWM;
      } else {
        this._chartLabel = sumOfValues; // If it is a stacked chart, show the sum of the values.
      }
    } else {
      return;
    }
    if (this.props.widget.useHighWaterMark) {
      const newHighWaterMark = sumOfHWM;
      if (
        !this._totalDivisor ||
        (this._totalDivisor > 0 && sumOfHWM >= this._totalDivisor) // update HighWaterMark only it is not set or smaller then the current sum of values
      ) {
        this._totalDivisor = newHighWaterMark;
      }
    } else if (!this.props.widget.firstValueIsHighWaterMark) {
      this._totalDivisor = sumOfHWM;
    }

    // If part of high water mark group, set group value.
    if (this.props.widget.waterMarkGroup) {
      if (!this.props.performancePanel.highWaterMarkGroups) {
        this.props.performancePanel.highWaterMarkGroups = {};
      }
      const highestValue = this.props.performancePanel
        .highWaterMarkGroups[this.props.widget.waterMarkGroup];
      if (this._totalDivisor > highestValue) {
        this.props.performancePanel.highWaterMarkGroups[
          this.props.widget.waterMarkGroup
        ] = this._totalDivisor;
      }
      this._totalDivisor = this.props.performancePanel.highWaterMarkGroups[
        this.props.widget.waterMarkGroup
      ];
    }
{'fraction_non_alphanumeric': 0.13126988718542087, 'fraction_numerical': 0.01816603991900492, 'mean_word_length': 2.4297619047619046, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '2141463', 'n_tokens_mistral': 5769, 'n_tokens_neox': 5501, 'n_words': 1371}