Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLA Test - Brian #15

Open
wants to merge 17 commits into
base: events_to_js
Choose a base branch
from
9 changes: 7 additions & 2 deletions gitmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def verify_env_var(var):
utc_zone = tz.gettz('UTC')

app = Flask(__name__)
VERSION = 2 #hack for the cache buster
VERSION = 3 #hack for the cache buster
app.config['DEBUG'] = True
app.secret_key = os.environ['SESSION_SECRET']

Expand Down Expand Up @@ -131,7 +131,12 @@ def milestones(orgname):

def get_milestones(repo):
r = make_request(repo['milestones_url'].split('{')[0])
out = r.json();

if r.status_code == 200:
out = r.json();
else:
out = []

for ms in out:
ms['repo'] = repo
return out
Expand Down
15 changes: 14 additions & 1 deletion static/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,18 @@ body {
font-size: 12px;
}

.milestone-list-item .badge.tag {
.badge.tag {
margin-right: 2px;
}

.badge.tag.filter {
cursor: pointer;
}

.badge.tag.filter.active {
background-color: #428bca;
}

.milestone-list-item .details {
margin: 10px 0 5px 0;
}
Expand Down Expand Up @@ -128,6 +136,11 @@ body {
margin-bottom: 10px;
}

.tag-header {
margin-bottom:3px;
text-align: right;
}

.org-avatar {
display: inline-block;
width: 24px;
Expand Down
112 changes: 70 additions & 42 deletions static/js/orgCtrl.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var app = angular.module('gitmap', ['ui.bootstrap'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/:orgname', { templateUrl: '/static/partials/org.html', controller: OrgCtrl })
.when('/:orgname', { templateUrl: '/static/partials/org.html', controller: OrgCtrl, reloadOnSearch: false })
.otherwise({ redirectTo: '/' });
}])
.config(['$locationProvider', function($locationProvider) { $locationProvider.html5Mode(true).hashPrefix('!'); }])
Expand Down Expand Up @@ -34,7 +34,7 @@ var app = angular.module('gitmap', ['ui.bootstrap'])
if(nodes) {
var nodeData = scope.nodes;
var selector = elem[0];

var width = 150,
height = 30,
padding = .2;
Expand All @@ -45,13 +45,13 @@ var app = angular.module('gitmap', ['ui.bootstrap'])
.padding([padding, padding])
.rows(1)
.cols(7);

var svg = d3.select(selector).append("svg")
.attr({
width: width,
height: height
});

var renderRect = function(rectSelector, classNames) {
rectSelector.append("rect")
.attr("class", classNames)
Expand All @@ -63,13 +63,13 @@ var app = angular.module('gitmap', ['ui.bootstrap'])
.attr("data-toggle", "tooltip")
.attr("data-delay", 200)
.attr("data-container", "body")
.attr("title", function(d) {
.attr("title", function(d) {
if(d.date.startOf('day').valueOf() === moment().startOf('day').valueOf())
return "today";
return d.date.startOf('day').from(moment().startOf('day'));
return d.date.startOf('day').from(moment().startOf('day'));
});
};

renderRect(svg.selectAll(".rect").data(rectGrid(nodeData)).enter(), "rect activity");
_(svg.selectAll(".rect")).each(function(x) {
$(x).tooltip();
Expand All @@ -81,7 +81,7 @@ var app = angular.module('gitmap', ['ui.bootstrap'])
}
});

function OrgCtrl($scope, $http, $routeParams) {
function OrgCtrl($scope, $http, $routeParams, $location) {
$scope.milestones = [];
$scope.milestonesLoaded = false;
$scope.orgname = $routeParams.orgname;
Expand All @@ -94,15 +94,23 @@ function OrgCtrl($scope, $http, $routeParams) {
_past7.push(moment(_current).milliseconds(0).seconds(0).minutes(0).hours(0));
}
_past7.reverse(); //descending left-to-right

$http({
method: 'GET',
url: "/api/org/" + $scope.orgname + "/milestones.json"
}).success(function(data, status) {
$scope.milestones = data;
$scope.org_avatar_url = data[0].repo.owner.avatar_url;
$scope.milestonesLoaded = true;
$scope.getMilestoneEvents();
_($scope.milestones).each(function(ms) {
ms.tags = parseBracketTags(ms.title);
ms.fullTitle = ms.title;
ms.title = stripBracketTags(ms.title);
$scope.allTags = _($scope.allTags).union(ms.tags);
});
if (data[0]) {
$scope.org_avatar_url = data[0].repo.owner.avatar_url;
$scope.getMilestoneEvents();
}
});

$scope.getMilestoneEvents = function() {
Expand All @@ -113,10 +121,9 @@ function OrgCtrl($scope, $http, $routeParams) {
_(repos).each(function(repo) {
getAllPages("/api/q?q=/repos/" + $scope.orgname + "/" + repo + "/issues/events")
.then(function(d) {
console.log(d);
parseEventsData(d, repo);
});
});
});
};

var getAllPages = function(url, all_data) {
Expand All @@ -125,9 +132,9 @@ function OrgCtrl($scope, $http, $routeParams) {

return $http.get(url).then(function(response) {
all_data = all_data.concat(response.data);

var next_url;

if (response.headers()['link']) {
var links = parseLinkHeader(response.headers()['link']);
if(links.rels.next)
Expand All @@ -151,31 +158,31 @@ function OrgCtrl($scope, $http, $routeParams) {
function(milestone) {
return milestone.repo.name == repo && milestone.number == issue.milestone.number;
});
if(!milestone.issues)

if(!milestone.issues)
milestone.issues = [];

milestone.issues.push(issue);

});

_($scope.milestones).chain()
.filter(function(m) { return m.repo.name == repo; })
.each(function(m) {
.each(function(m) {
var active_days = extractDays(m.issues, 'updated_at');
m.past7_activity = _.map(_past7, function(momObj) {
var dateIfActive = _.find(active_days, function(activeDay) {
return activeDay.valueOf() == momObj.valueOf()
});

if(dateIfActive)
return {date: momObj, value: true}
else
return {date: momObj, value: false}
});
});
});



};

//copied for safe-keeping
Expand All @@ -190,38 +197,37 @@ function OrgCtrl($scope, $http, $routeParams) {

if(milestone) { //milestone could already be closed
//and therefore not in our list
if(!milestone.eventsByIssue)
if(!milestone.eventsByIssue)
milestone.eventsByIssue = [];

//we use event.issue.number here because it is
//unique per-issue
if(!milestone.eventsByIssue[event.issue.number])
milestone.eventsByIssue[event.issue.number] = [];
milestone.eventsByIssue[event.issue.number].push(event);
}
}
});

_($scope.milestones).chain()
.filter(function(m) { return m.repo.name == repo; })
.each(function(m) {
.each(function(m) {
m.event_summaries = summarizeEvents(m.eventsByIssue);
var active_days = extractDays(_.flatten(m.eventsByIssue), 'created_at');
m.past7_activity = _.map(_past7, function(momObj) {
var dateIfActive = _.find(active_days, function(activeDay) {
return activeDay.valueOf() == momObj.valueOf()
});

if(dateIfActive)
return {date: momObj, value: true}
else
return {date: momObj, value: false}
});
});
});
};


var summarizeEvents = function(eventsByIssue) {
return _(eventsByIssue).chain().map(function(issueEvents) {
var summarizeEvents = function(eventsByIssue) {
return _(eventsByIssue).chain().map(function(issueEvents) {
var out = {};
out.title = _(issueEvents).first().issue.title;
out.url = _(issueEvents).first().issue.html_url;
Expand All @@ -238,25 +244,20 @@ function OrgCtrl($scope, $http, $routeParams) {
};

var extractDays = function(collection, date_field) {
var days = _(collection).chain().map(function(e) {
var days = _(collection).chain().map(function(e) {
return moment(e[date_field]).milliseconds(0)
.seconds(0).minutes(0).hours(0);
}).uniq(function(m) { return m.valueOf(); }).value();
return days;
};

$scope.progress = function(milestone) {
return !milestone.open_issues ? milestone.open_issues :
(milestone.closed_issues / (milestone.closed_issues + milestone.open_issues)) * 100;
};

$scope.stripBracketTags = function(string) {
var stripBracketTags = function(string) {
if(!string) return;
out = string.slice(string.lastIndexOf("]")+1).trim(" ");
return out;
};

$scope.parseBracketTags = function(string) {
var parseBracketTags = function(string) {
var regex = /[\[](.+?)[\]]/g
var out = [];
while(res = regex.exec(string)) {
Expand All @@ -265,11 +266,38 @@ function OrgCtrl($scope, $http, $routeParams) {
return out;
};

$scope.progress = function(milestone) {
return !milestone.open_issues ? milestone.open_issues :
(milestone.closed_issues / (milestone.closed_issues + milestone.open_issues)) * 100;
};

$scope.allTags = [];

$scope.tagsFilter = function (obj) {
return $scope.filteredTags.length ? _.intersection(obj.tags, $scope.filteredTags).length : true;
};

$scope.toggleTagFilter = function(t) {
if (_($scope.filteredTags).contains(t)) {
$scope.filteredTags = _($scope.filteredTags).without(t);
} else {
$scope.filteredTags.push(t);
}
};

$scope.filteredTags = [];

if ($routeParams.tags !== undefined)
_.map($routeParams.tags.split(','), $scope.toggleTagFilter);

$scope.$watch('filteredTags', function (tags) {
$location.search('tags', tags.join(','));
}, true);

$scope.toggleDetails = function(ms) {
ms.showDetails = !ms.showDetails;
};

$scope.testData = [{ "value": true }, { "value": false }, { "value": true },
{ "value": true }, { "value": false }, { "value": true }, { "value": true }];
}

22 changes: 16 additions & 6 deletions static/partials/org.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
<activity-grid nodes="milestone.past7_activity" />
</div>
<div class="title">
<span class="badge tag" ng-repeat="tag in parseBracketTags(milestone.title)">
<span class="badge tag" ng-repeat="tag in milestone.tags">
{{tag}}
</span>
<a href="{{ milestone.repo.html_url }}/issues?milestone={{ milestone.number }}">{{ stripBracketTags(milestone.title) }}</a>
<a href="{{ milestone.repo.html_url }}/issues?milestone={{ milestone.number }}" target="_blank">{{ milestone.title }}</a>
</div>
<div ng-hide="true" class="details">
<p class="ms-description-short">{{ milestone.description }}</p>
Expand All @@ -31,17 +31,27 @@
</div>
<span class="org-title">{{ orgname }} Roadmap</span>
</div>
<ul id="milestones-upcoming" class="list-group milestones scheduled">
<div class="loading" ng-show="!milestonesLoaded">Loading...</div>
<div ng-show="milestonesLoaded && !milestones.length">
We are trying to build a roadmap out of your repository
milestones, but there are none!
</div>
<div class="tag-header" ng-show="milestonesLoaded && milestones.length">
Tags:
<span class="badge tag filter" ng-repeat="tag in allTags" ng-click="toggleTagFilter(tag)" ng-class="{'active': filteredTags.indexOf(tag) != -1}">
{{tag}}
</span>
</div>
<ul id="milestones-upcoming" ng-show="milestonesLoaded && milestones.length" class="list-group milestones scheduled">
<li class="list-group-item header small">
<div class="days-left small">days left</div>
<div class="milestone-progress small">progress</div>
<div class="milestone-activity small">7d activity</div>
</li>
<div class="loading" ng-show="!milestonesLoaded">Loading...</div>
<li ng-show="milestonesLoaded" ng-click="toggleDetails(milestone)" class="list-group-item milestone-list-item" ng-repeat="milestone in milestones | filter:{due_on:'!!'} | orderBy:'due_on'">
<li ng-show="milestonesLoaded" ng-click="toggleDetails(milestone)" class="list-group-item milestone-list-item" ng-repeat="milestone in milestones | filter:{due_on:'!!'} | filter:tagsFilter | orderBy:'due_on'">
<div ng-include src="'template-ms-short.html'"></div>
</li>
<li ng-show="milestonesLoaded" href="" class="list-group-item milestone-list-item" ng-repeat="milestone in milestones | filter:{due_on:'!'} | orderBy:'due_on'">
<li ng-show="milestonesLoaded" href="" class="list-group-item milestone-list-item" ng-repeat="milestone in milestones | filter:{due_on:'!'} | filter:tagsFilter | orderBy:'due_on'">
<div ng-include src="'template-ms-short.html'"></div>
</li>
</ul>
2 changes: 2 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ <h1>gitmap.net</h1>
<h3>roadmaps for github organizations</h3>
<br />
<a href="/auth" type="button" class="btn btn-primary btn-large">login with github</a>
<br />
<p> we don't modify or store any of your github data </p>
</div>
</div>
</div>
Expand Down