<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import _ from 'underscore';

import {getFilters} from '../route-utils';
import IssueStore from '../issue-store';
import Client from '../github-client';
import {getCardColumn, UNCATEGORIZED_NAME} from '../helpers';
import Loadable from './loadable';
import LabelBadge from './label-badge';

import d3 from 'd3';
import gantt from '../gantt-chart';


const filterByMilestoneAndKanbanColumn = (cards) => {
  const data = {};
  const columns = {};
  const columnCounts = {}; // key is columnName
  const add = (card) => {
    if (card.issue.milestone) {
      const column = getCardColumn(card);
      const columnName = column.name;
      columns[columnName] = column;
      const msCounts = data[card.issue.milestone.title] || {};
      data[card.issue.milestone.title] = msCounts;
      msCounts[columnName] = msCounts[columnName] || 0;
      msCounts[columnName] += 1;

      columnCounts[columnName] = columnCounts[columnName] || 0;
      columnCounts[columnName] += 1;
    } else {
      // TODO: Should account for issues not in a milestone somehow
    }
  };

  cards.forEach((card) => {
    add(card);
  });
  return {data, columns: _.values(columns), columnCounts};
};


const GanttChart = React.createClass({
  componentDidMount() {
    this.renderChart();
  },
  componentDidUpdate() {
    this.renderChart();
  },
  renderChart() {
    const {milestones, data, columns} = this.props;
    const {ganttWrapper} = this.refs;
    const now = new Date();

    ReactDOM.findDOMNode(ganttWrapper).innerHTML = '';

    const tasks = milestones.map((milestone) => {
      const {createdAt, dueOn, title, state, closedIssues, openIssues} = milestone;
      const dueAt = dueOn ? new Date(dueOn) : null;
      let status;
      if (dueAt && dueAt.getTime() < now.getTime()) {
        status = 'milestone-status-overdue';
      } else {
        status = `milestone-status-${state}`;
      }
      const segments = [];
      if (closedIssues) {
        segments.push({count: closedIssues, color: '666666', title: 'Closed Issues'});
      }
      let accountedForCount = 0;
      _.each(columns, ({name, color}) => {
        if (data[milestone.title]) {
          const count = data[milestone.title][name] || 0;
          if (count) {
            accountedForCount += count;
            segments.push({count, color, title:name});
          }
        }
      });
      if (accountedForCount !== openIssues) {
        segments.push({count: openIssues - accountedForCount, color: 'ffffff', title: 'Other Open Issues'});
      }
      return {
        startDate: createdAt,
        endDate: dueAt || now,
        taskName: title,
        status: status,
        segments: segments
      };
    });

    const taskStatus = {
      'milestone-status-overdue': 'milestone-status-overdue',
        'milestone-status-open' : 'milestone-status-open',
        'milestone-status-closed' : 'milestone-status-closed'
    };

    const taskNames = tasks.map(({taskName}) => taskName).sort();

    tasks.sort(function(a, b) {
        return a.endDate - b.endDate;
    });
    const maxDate = tasks[tasks.length - 1].endDate;
    tasks.sort(function(a, b) {
        return a.startDate - b.startDate;
    });
    // const minDate = tasks[0].startDate;

    const format = '%H:%M';

    const chart = gantt(taskNames.length).taskTypes(taskNames).taskStatus(taskStatus).tickFormat(format).selector('#the-gantt-chart');
    chart(tasks);


    function changeTimeDomain(timeDomainString) {
        let format;
        switch (timeDomainString) {
          case '1hr':
          	format = '%H:%M:%S';
          	chart.timeDomain([ d3.time.hour.offset(maxDate, -1), maxDate ]);
          	break;
          case '3hr':
          	format = '%H:%M';
          	chart.timeDomain([ d3.time.hour.offset(maxDate, -3), maxDate ]);
          	break;

          case '6hr':
          	format = '%H:%M';
          	chart.timeDomain([ d3.time.hour.offset(maxDate, -6), maxDate ]);
          	break;

          case '1day':
          	format = '%H:%M';
          	chart.timeDomain([ d3.time.day.offset(maxDate, -1), maxDate ]);
          	break;

          case '1week':
          	format = '%m/%d';
          	chart.timeDomain([ d3.time.day.offset(maxDate, -7), maxDate ]);
          	break;
          default:
          	format = '%H:%M';

        }
        chart.tickFormat(format);
        chart.redraw(tasks);
    }

    changeTimeDomain('1week');

  },
  render() {
    const {columns, columnCounts} = this.props;

    const legend = columns.map((label) => {
      return (
        <LabelBadge key={label.name} label={label} extra={columnCounts[label.name]}/>
      );
    });
    return (
      <div className='-gantt-chart-and-legend'>
        <div ref='ganttWrapper' id='the-gantt-chart'/>
        <h3>Legend</h3>
        <p>Blue vertical line is Today</p>
        <LabelBadge key='completed' label={{name:'0 - Closed', color: '666666'}}/>
        {legend}
        <br/>{/* Add breaks to increase padding because I'm lazy and don't want to add CSS margins */}
        <br/>
      </div>
    );
  }

});

const RepoKanbanShell = React.createClass({
  componentWillMount() {
    // Needs to be called before `render()`
    IssueStore.startPolling();
  },
  componentWillUnmount() {
    IssueStore.stopPolling();
  },
  renderLoaded([allMilestones, cards]) {
    const {milestoneTitles} = getFilters().getState();

    let {data, columns, columnCounts} = filterByMilestoneAndKanbanColumn(cards);
    // COPYPASTA: Taken from repo-kanban
    columns = _.sortBy(columns, ({name}) => {
      if (name === UNCATEGORIZED_NAME) {
        // make sure Uncategorized is the left-most column
        return -1;
      } else {
        const result = /^(\d+)/.exec(name);
        return result && result[1] || name;<commit_msg>show closed counts<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import _ from 'underscore';

import {getFilters} from '../route-utils';
import IssueStore from '../issue-store';
import Client from '../github-client';
import {getCardColumn, UNCATEGORIZED_NAME} from '../helpers';
import Loadable from './loadable';
import LabelBadge from './label-badge';

import d3 from 'd3';
import gantt from '../gantt-chart';


const filterByMilestoneAndKanbanColumn = (cards) => {
  const data = {};
  const columns = {};
  const columnCounts = {}; // key is columnName
  const add = (card) => {
    if (card.issue.milestone) {
      const column = getCardColumn(card);
      const columnName = column.name;
      columns[columnName] = column;
      const msCounts = data[card.issue.milestone.title] || {};
      data[card.issue.milestone.title] = msCounts;
      msCounts[columnName] = msCounts[columnName] || 0;
      msCounts[columnName] += 1;

      columnCounts[columnName] = columnCounts[columnName] || 0;
      columnCounts[columnName] += 1;
    } else {
      // TODO: Should account for issues not in a milestone somehow
    }
  };

  cards.forEach((card) => {
    add(card);
  });
  return {data, columns: _.values(columns), columnCounts};
};


const GanttChart = React.createClass({
  componentDidMount() {
    this.renderChart();
  },
  componentDidUpdate() {
    this.renderChart();
  },
  renderChart() {
    const {milestones, data, columns} = this.props;
    const {ganttWrapper} = this.refs;
    const now = new Date();

    ReactDOM.findDOMNode(ganttWrapper).innerHTML = '';

    const tasks = milestones.map((milestone) => {
      const {createdAt, dueOn, title, state, closedIssues, openIssues} = milestone;
      const dueAt = dueOn ? new Date(dueOn) : null;
      let status;
      if (dueAt && dueAt.getTime() < now.getTime()) {
        status = 'milestone-status-overdue';
      } else {
        status = `milestone-status-${state}`;
      }
      const segments = [];
      if (closedIssues) {
        segments.push({count: closedIssues, color: '666666', title: 'Closed Issues'});
      }
      let accountedForCount = 0;
      _.each(columns, ({name, color}) => {
        if (data[milestone.title]) {
          const count = data[milestone.title][name] || 0;
          if (count) {
            accountedForCount += count;
            segments.push({count, color, title:name});
          }
        }
      });
      if (accountedForCount !== openIssues) {
        segments.push({count: openIssues - accountedForCount, color: 'ffffff', title: 'Other Open Issues'});
      }
      return {
        startDate: createdAt,
        endDate: dueAt || now,
        taskName: title,
        status: status,
        segments: segments
      };
    });

    const taskStatus = {
      'milestone-status-overdue': 'milestone-status-overdue',
        'milestone-status-open' : 'milestone-status-open',
        'milestone-status-closed' : 'milestone-status-closed'
    };

    const taskNames = tasks.map(({taskName}) => taskName).sort();

    tasks.sort(function(a, b) {
        return a.endDate - b.endDate;
    });
    const maxDate = tasks[tasks.length - 1].endDate;
    tasks.sort(function(a, b) {
        return a.startDate - b.startDate;
    });
    // const minDate = tasks[0].startDate;

    const format = '%H:%M';

    const chart = gantt(taskNames.length).taskTypes(taskNames).taskStatus(taskStatus).tickFormat(format).selector('#the-gantt-chart');
    chart(tasks);


    function changeTimeDomain(timeDomainString) {
        let format;
        switch (timeDomainString) {
          case '1hr':
          	format = '%H:%M:%S';
          	chart.timeDomain([ d3.time.hour.offset(maxDate, -1), maxDate ]);
          	break;
          case '3hr':
          	format = '%H:%M';
          	chart.timeDomain([ d3.time.hour.offset(maxDate, -3), maxDate ]);
          	break;

          case '6hr':
          	format = '%H:%M';
          	chart.timeDomain([ d3.time.hour.offset(maxDate, -6), maxDate ]);
          	break;

          case '1day':
          	format = '%H:%M';
          	chart.timeDomain([ d3.time.day.offset(maxDate, -1), maxDate ]);
          	break;

          case '1week':
          	format = '%m/%d';
          	chart.timeDomain([ d3.time.day.offset(maxDate, -7), maxDate ]);
          	break;
          default:
          	format = '%H:%M';

        }
        chart.tickFormat(format);
        chart.redraw(tasks);
    }

    changeTimeDomain('1week');

  },
  render() {
    const {columns, columnCounts, milestones} = this.props;

    const legend = columns.map((label) => {
      return (
        <LabelBadge key={label.name} label={label} extra={columnCounts[label.name]}/>
      );
    });
    let closedCount = 0;
    milestones.forEach((milestone) => {
      closedCount += milestone.closedIssues;
    });
    return (
      <div className='-gantt-chart-and-legend'>
        <div ref='ganttWrapper' id='the-gantt-chart'/>
        <h3>Legend</h3>
        <p>Blue vertical line is Today</p>
        <LabelBadge key='completed' label={{name:'0 - Closed', color: '666666'}} extra={closedCount}/>
        {legend}
        <br/>{/* Add breaks to increase padding because I'm lazy and don't want to add CSS margins */}
        <br/>
      </div>
    );
  }

});

const RepoKanbanShell = React.createClass({
  componentWillMount() {
    // Needs to be called before `render()`
    IssueStore.startPolling();
  },
  componentWillUnmount() {
    IssueStore.stopPolling();
  },
  renderLoaded([allMilestones, cards]) {
    const {milestoneTitles} = getFilters().getState();

    let {data, columns, columnCounts} = filterByMilestoneAndKanbanColumn(cards);
    // COPYPASTA: Taken from repo-kanban
    columns = _.sortBy(columns, ({name}) => {
      if (name === UNCATEGORIZED_NAME) {
        // make sure Uncategorized is the left-most column
        return -1;
      } else {
        const result = /^(\d+)/.exec(name);
        return result && result[1] || name;
{'fraction_non_alphanumeric': 0.15131856095711518, 'fraction_numerical': 0.007330019378212149, 'mean_word_length': 2.4850264239577218, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 42, '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': '3841405', 'n_tokens_mistral': 3835, 'n_tokens_neox': 3727, 'n_words': 805}