<commit_before>define(function(require) {
    'use strict';

    require('lib');

    var tmplOverview = require('text!templates/start.html');
    var tmplLoading = require('text!templates/loading.html');
    var tmplHeader = require('text!templates/header.html');
    var tmplSelect = require('text!templates/projectSelect.html');
    var tmplToken = require('text!templates/getToken.html');
    var tmplComplete = require('text!templates/complete.html');
    var report = require('report');


	var _token = null;
	var project_id = null;
    var project_name = "";
    var search_str = false;
	var stories = [];
	var columns = [];
	var token = null;
    var epics = null;
    var members;

    function request( url, options ) {
        
        var params = {
                data        : "",
                headers     : {},
                type        : "POST",
                contentType : "application/x-www-form-urlencoded",
                processData : true,
                dataType    : 'json'
            };

        $.extend(params, options);

        url = "https://www.pivotaltracker.com/services/v5" + url;

        params.headers["Content-Type"] = params.contentType;
        params.headers["X-TrackerToken"] = token;

        return $.ajax(url, params);
    }

    function getProject() {
    	return $('#project').val()
    }

    function loadProjects () {

		request("/projects", {
			type: "GET"
		}).done(function( result ) {
			var tmplProject = _.template(tmplSelect);
			$('#main').html(tmplProject({project: result}));
			loading.hide();
		}).fail(function( status, e ) {
			window.localStorage.removeItem('pivotal-api-token');
			token = null;
			getToken('Unable to connect. Token seems invalid');
	    });
    }

    function loadMembersAndEpics() {

        $.when(
            request("/projects/" + project_id + "/memberships", { type: "GET" }),
            request("/projects/" + project_id + "/epics", { type: "GET" })
        ).then(function(result1, result2){
            
            members = _.indexBy(_.map(result1[0], function(m) { 
                return m.person 
            }), 'id');
            
            epics = _.indexBy(_.map(result2[0], function(e) {
                return {
                    name: e.name, 
                    label: e.label.name, 
                    label_id: e.label.id,
                    id: e.id,
                    url: e.url
                }; 
            }), 'label_id');

            loadStories();
        });
    }

    function getName(id) {
    	if (members[id]) return members[id].name;
    	else return id;
    }

    function checkColumns(story) {
    	var record = _.keys(story);
    	var diff = _.difference(record, columns);
    	for (var i=0; i<diff.length; i++) columns.push(diff[i]);
    }

    function flattenStories(result) {
    	var match;
    	for (var i=0; i<result.length; i++) {

            // Set Epic Name
            var epic_name = '';
            var label_ids = _.pluck(result[i].labels, 'id');
            for (var p = 0; p < label_ids.length; p++) {
                if (epics.hasOwnProperty(label_ids[p])) {
                    epic_name = epics[label_ids[p]].name.replace(/[",]/g,' ');
                }
            }
            result[i].epic = epic_name;

    		var labels = _.pluck(result[i].labels, 'name');

            // release-.. and sprint-.. are special labels, used to mark the associated sprint
    		for (var k=0; k<labels.length; k++) {
    			if (match = labels[k].match(/^(release|sprint)[- ]?([^\s-]+)/i)) {
    				result[i][match[1].toLowerCase()] = match[2];
    			}
    		}

            // Add labels, appended by space
    		result[i].labels = labels.join(' ');

            // Convert member ids to member names
    		if (result[i]['owned_by_id']) result[i]['owner'] = getName(result[i]['owned_by_id']);
    		result[i]['owners'] = _.map(result[i]['owner_ids'], function(id) { return getName(id); }).join(',');
    		result[i]['requested_by'] = getName(result[i]['requested_by_id']);
    		delete result[i]['owner_ids'];

            result[i]['done'] = '';

            // Extract data from description - custom field hack
    		var description = result[i].description || '';
    		var lines = description.split(/[\n\r]+/);
    		for (var k=0; k < lines.length; k++) {
    			var matches = lines[k].match(/^\^(.+)[\:=](.+)$/);
    			if (matches) {
    				result[i][matches[1].trim().toLowerCase()] = matches[2].trim();
    			}
    		}<commit_msg>Allowing adding comments to custom variables<commit_after>define(function(require) {
    'use strict';

    require('lib');

    var tmplOverview = require('text!templates/start.html');
    var tmplLoading = require('text!templates/loading.html');
    var tmplHeader = require('text!templates/header.html');
    var tmplSelect = require('text!templates/projectSelect.html');
    var tmplToken = require('text!templates/getToken.html');
    var tmplComplete = require('text!templates/complete.html');
    var report = require('report');


	var _token = null;
	var project_id = null;
    var project_name = "";
    var search_str = false;
	var stories = [];
	var columns = [];
	var token = null;
    var epics = null;
    var members;

    function request( url, options ) {
        
        var params = {
                data        : "",
                headers     : {},
                type        : "POST",
                contentType : "application/x-www-form-urlencoded",
                processData : true,
                dataType    : 'json'
            };

        $.extend(params, options);

        url = "https://www.pivotaltracker.com/services/v5" + url;

        params.headers["Content-Type"] = params.contentType;
        params.headers["X-TrackerToken"] = token;

        return $.ajax(url, params);
    }

    function getProject() {
    	return $('#project').val()
    }

    function loadProjects () {

		request("/projects", {
			type: "GET"
		}).done(function( result ) {
			var tmplProject = _.template(tmplSelect);
			$('#main').html(tmplProject({project: result}));
			loading.hide();
		}).fail(function( status, e ) {
			window.localStorage.removeItem('pivotal-api-token');
			token = null;
			getToken('Unable to connect. Token seems invalid');
	    });
    }

    function loadMembersAndEpics() {

        $.when(
            request("/projects/" + project_id + "/memberships", { type: "GET" }),
            request("/projects/" + project_id + "/epics", { type: "GET" })
        ).then(function(result1, result2){
            
            members = _.indexBy(_.map(result1[0], function(m) { 
                return m.person 
            }), 'id');
            
            epics = _.indexBy(_.map(result2[0], function(e) {
                return {
                    name: e.name, 
                    label: e.label.name, 
                    label_id: e.label.id,
                    id: e.id,
                    url: e.url
                }; 
            }), 'label_id');

            loadStories();
        });
    }

    function getName(id) {
    	if (members[id]) return members[id].name;
    	else return id;
    }

    function checkColumns(story) {
    	var record = _.keys(story);
    	var diff = _.difference(record, columns);
    	for (var i=0; i<diff.length; i++) columns.push(diff[i]);
    }

    function flattenStories(result) {
    	var match;
    	for (var i=0; i<result.length; i++) {

            // Set Epic Name
            var epic_name = '';
            var label_ids = _.pluck(result[i].labels, 'id');
            for (var p = 0; p < label_ids.length; p++) {
                if (epics.hasOwnProperty(label_ids[p])) {
                    epic_name = epics[label_ids[p]].name.replace(/[",]/g,' ');
                }
            }
            result[i].epic = epic_name;

    		var labels = _.pluck(result[i].labels, 'name');

            // release-.. and sprint-.. are special labels, used to mark the associated sprint
    		for (var k=0; k<labels.length; k++) {
    			if (match = labels[k].match(/^(release|sprint)[- ]?([^\s-]+)/i)) {
    				result[i][match[1].toLowerCase()] = match[2];
    			}
    		}

            // Add labels, appended by space
    		result[i].labels = labels.join(' ');

            // Convert member ids to member names
    		if (result[i]['owned_by_id']) result[i]['owner'] = getName(result[i]['owned_by_id']);
    		result[i]['owners'] = _.map(result[i]['owner_ids'], function(id) { return getName(id); }).join(',');
    		result[i]['requested_by'] = getName(result[i]['requested_by_id']);
    		delete result[i]['owner_ids'];

            result[i]['done'] = '';

            // Extract data from description - custom field hack
    		var description = result[i].description || '';
    		var lines = description.split(/[\n\r]+/);
    		for (var k=0; k < lines.length; k++) {
    			var matches = lines[k].match(/^\^(.+)[\:=](.+)$/);
    			if (matches) {
    				result[i][matches[1].trim().toLowerCase()] = matches[2].replace(/#.*/,"").trim();
    			}
    		}
{'fraction_non_alphanumeric': 0.1660957011824511, 'fraction_numerical': 0.003536302353851254, 'mean_word_length': 1.9907468605419696, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, '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': '7098508', 'n_tokens_mistral': 2910, 'n_tokens_neox': 2754, 'n_words': 561}