TWtablero/tablero

View on GitHub
app/js/component/data/issues_exporter.js

Summary

Maintainability
C
7 hrs
Test Coverage
/*
 * Copyright 2014 Thoughtworks Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define([
    'flight/lib/component',
    'component/mixins/with_auth_token_from_hash',
  ],
  function (defineComponent, withAuthTokemFromHash) {
    'use strict';
    return defineComponent(issuesExporter, withAuthTokemFromHash);

    function issuesExporter() {
      this.defaultAttrs({
        customColumns: ['1 - Ready', '2 - Development', '3 - Quality Assurance']
      });

      var issuesToExport = [];

      var csvLink = '';

      this.storeColumns = function (ev, columns) {
        var columns = columns.columns;

        this.attr.customColumns = _.map(columns, function (column) {
          return (parseInt(column['order']) + 1) + ' - ' + column['column'];
        });

      };


      this.cleanLabel = function (label) {
        return label.trim().
        replace(/^[^a-zA-Z]+/g, '').
        replace(/[^a-zA-Z[0-9]\s]/g, '').
        replace(/\s/g, '_').
        toLowerCase();
      };

      this.mountExportCsvLink = function (ev, data) {
        var repositoriesUrls = this.getRepositoriesUrlsFromIssues(data.issues);
        issuesToExport = data.issues;
        this.getEventsFromProjects(repositoriesUrls);
      };

      this.clearLink = function () {
        csvLink = '';
      };

      this.showExportingFeedbackLink = function () {
        $('#export_csv').hide();
        $('#export_csv').after('<a id="exporting_csv" class="btn btn-success btn-xs right not-active">EXPORTING<img src="/img/loading-dots.gif" /></a>');
      };

      this.showExportCsvLink = function () {
        $('#exporting_csv').remove();
        $('#export_csv').show();
      };

      this.linkToCsv = function (data) {
        var issuesCsv = this.issuesToCsv(data);
        if (csvLink === '') {
          issuesCsv.splice(0, 0, this.csvHeader());
        }

        csvLink += encodeURIComponent(issuesCsv.join('\n') + '\n');
        return 'data:text/csv;charset=utf8,' + csvLink;
      };

      this.validIssuesToExport = function (issues) {
        return _.filter(issues, function (issue) {
          return issue.projectName !== undefined;
        });
      };

      this.issuesToCsv = function (issues) {
        var issuesCsv = _.map(this.validIssuesToExport(issues), function (issue) {
          var data = ['\"' + issue.projectName + "\"",
            issue.number,
            "\"" + issue.title + "\"",
            issue.state,
            issue.kanbanState,
            "\"" + _.map(issue.labels, function (label) {
              return label.name;
            }) + "\"",
            issue.created_at,
            issue.closed_at,
            daysBetween(issue.created_at, issue.closed_at),
          ];
          _.each(this.attr.customColumns, function (column) {
            data.push(issue[this.cleanLabel(column) + "_at"]);
          }, this);
          return data.join(';');
        }, this);

        return issuesCsv;
      };

      function daysBetween(earlierDate, lateDate) {
        if ((earlierDate && lateDate) && (lateDate > earlierDate)) {
          var leadTime,
            millisecondsInDay = 86400000;

          leadTime = (new Date(lateDate) - new Date(earlierDate)) / millisecondsInDay;
          leadTime = parseInt(leadTime);

          return leadTime;
        } else {
          return "";
        }
      };

      this.csvHeader = function () {
        var header = ["Source", "Github ID", "Title", "Status", "Kanban State", "Tags", "Create at", "Closed at", "Lead Time"]
        _.each(this.attr.customColumns, function (label) {
          header.push(this.cleanLabel(label) + " at");
        }, this);
        return header.join(';');
      };

      this.getEventsFromProjects = function (projectsUrl) {
        var events = [];
        var self = this;

        function getEvents(url) {
          $.get(url, function (data, textStatus, request) {
            events = events.concat(data);

            try {
              var header = request.getResponseHeader('Link');
              var next = header.match(/<.*?>; rel="next"/)[0].match(/https.*[0-9]+/)[0];
              if (next) {
                getEvents(next);
              }
            } catch (err) {}
          });
        }

        $.each(projectsUrl, function (index, val) {
          getEvents(val + 'issues/events?per_page=100&access_token=' + this.getCurrentAuthToken());
        }.bind(self));

        $(document).one('ajaxStop', function () {
          self.createCsvUri(self.addEventDateForIssues(issuesToExport, events));
        });
      };

      this.groupEventsByIssuesId = function (events) {
        return _.groupBy(events, function (event) {
          return event.issue.id;
        });
      };

      this.excludeNonLabeledEvents = function (mappedEvents) {
        return _.object(_.map(mappedEvents, function (issueEvents, key) {
          return [key, _.filter(issueEvents, function (event) {
            return event.event == 'labeled';
          })]
        }));
      };

      this.getIssueEventsByLabel = function (labeledEvents, label) {
        return _.object(_.map(labeledEvents, function (issueEvents, key) {
          return [key, _.filter(issueEvents, function (event) {
            return event.label.name == label;
          })]
        }));
      };

      this.getEarliestIssueEvents = function (labeledEvents) {
        return _.object(_.map(labeledEvents, function (events, key) {
          return [key, _.first(_.sortBy(events, function (e) {
            return e.created_at;
          }))]
        }));
      };

      this.mergeEventsWithIssues = function (issues, events, key) {
        return _.each(issues, function (issue) {
          if (events[issue.id]) {
            issue[key] = events[issue.id].created_at;
          }
        });
      };

      this.getRepositoriesUrlsFromIssues = function (issues) {
        return _.uniq(_.pluck(issues, 'repoUrl'));
      };

      this.addEventDateForIssues = function (issues, events) {
        var groupedEventsByIssueId = {},
          labeledEvents = {},
          labelEvents = {},
          earlierstIssuesEvent = {},
          issuesWithEventDate = issues,

          groupedEventsByIssueId = this.groupEventsByIssuesId(events);
        labeledEvents = this.excludeNonLabeledEvents(groupedEventsByIssueId);
        _.each(this.attr.customColumns, function (label) {
          labelEvents = this.getIssueEventsByLabel(labeledEvents, label);
          earlierstIssuesEvent = this.getEarliestIssueEvents(labelEvents);
          issuesWithEventDate = this.mergeEventsWithIssues(issuesWithEventDate, earlierstIssuesEvent, this.cleanLabel(label) + "_at")
        }, this);

        return issuesWithEventDate;
      };

      this.createCsvUri = function (issuesWithEventDate) {
        var uri = this.linkToCsv(issuesWithEventDate);

        var downloadLink = document.createElement("a");
        downloadLink.href = uri;
        downloadLink.download = "data.csv";

        document.body.appendChild(downloadLink);
        downloadLink.click();
        document.body.removeChild(downloadLink);
        issuesToExport = [];
        this.trigger('data:issues:clearExportCsvLink');

      };

      this.after('initialize', function () {
        this.on('data:got:columns', this.storeColumns);
        this.on('data:issues:mountExportCsvLink', this.mountExportCsvLink);
        this.on('data:issues:mountExportCsvLink', this.showExportingFeedbackLink);
        this.on('data:issues:clearExportCsvLink', this.clearLink);
        this.on('data:issues:clearExportCsvLink', this.showExportCsvLink);

      });
    }
  }
);