-
Notifications
You must be signed in to change notification settings - Fork 45
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
activity: Make activity page with graphs on issues #27
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<!doctype html> | ||
<html> | ||
|
||
<head> | ||
<title>Community Activity</title> | ||
<style> | ||
canvas { | ||
-moz-user-select: none; | ||
-webkit-user-select: none; | ||
-ms-user-select: none; | ||
} | ||
</style> | ||
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> | ||
</head> | ||
|
||
<body> | ||
<h1>Community Activity</h1> | ||
<br> | ||
<br> | ||
<div style="width:75%;"> | ||
<canvas id="canvas"></canvas> | ||
</div> | ||
<select id="chartType"> | ||
<option value="Month">Year</option> | ||
<option value="Week">Month</option> | ||
<option value="Day">Week</option> | ||
</select> | ||
<br> | ||
<br> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.js"></script> | ||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> | ||
<script src="/static/charts.js"></script> | ||
<script> | ||
$('#chartType').on('change', function(){ | ||
updateChart($('#chartType').val()) | ||
}); | ||
updateChart($('#chartType').val()); | ||
</script> | ||
</body> | ||
|
||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import requests | ||
import json | ||
import datetime | ||
import os | ||
import calendar | ||
from dateutil import parser, relativedelta | ||
|
||
|
||
class Scraper(): | ||
""" | ||
This is the class responsible for scraping provided issues into a | ||
dictionary containing just statistical information of the data. | ||
""" | ||
|
||
""" | ||
Count of months/weeks/days respectively to be scraped in past. | ||
""" | ||
CONSTANTS = { | ||
'month_count': 12, | ||
'week_count': 4, | ||
'day_count': 7, | ||
} | ||
|
||
def __init__(self, content, date): | ||
""" | ||
Constructs a new ``Scraper`` | ||
|
||
:param content: Github API Parsed JSON issues | ||
:param date: The date to scrape data till. | ||
""" | ||
self.date = date | ||
self.content = content | ||
|
||
# Initialise data dicts | ||
self.data = { | ||
'year': { | ||
'labels': [], | ||
'closed': [0]*self.CONSTANTS['month_count'], | ||
'opened': [0]*self.CONSTANTS['month_count'], | ||
}, | ||
'month': { | ||
'labels': [], | ||
'closed': [0]*self.CONSTANTS['week_count'], | ||
'opened': [0]*self.CONSTANTS['week_count'], | ||
}, | ||
'week': { | ||
'labels': [], | ||
'closed': [0]*self.CONSTANTS['day_count'], | ||
'opened': [0]*self.CONSTANTS['day_count'], | ||
}, | ||
} | ||
|
||
# Process labels for each option | ||
for x in range(self.CONSTANTS['month_count']-1, -1, -1): | ||
self.data['year']['labels'].append(calendar.month_name[( | ||
self.date - relativedelta.relativedelta(months=x)).month]) | ||
|
||
for x in range(self.CONSTANTS['week_count']-1, -1, -1): | ||
day = self.date - relativedelta.relativedelta(weeks=x) | ||
strt = (day - datetime.timedelta(days=day.weekday())) | ||
fin = (day + datetime.timedelta(days=6-day.weekday())) | ||
self.data['month']['labels'].append( | ||
calendar.month_abbr[strt.month] + ' ' + str(strt.day) | ||
+ ' - ' | ||
+ calendar.month_abbr[fin.month] + ' ' + str(fin.day)) | ||
|
||
for x in range(self.CONSTANTS['day_count']-1, -1, -1): | ||
day_idx = (self.date - datetime.timedelta(days=x)).weekday() | ||
self.data['week']['labels'].append(calendar.day_name[day_idx]) | ||
|
||
def __diff_month(self, d): | ||
""" | ||
:param d: Date as datetime, self.date >= Date. | ||
|
||
:return: Difference in months(int) ignoring partially complete months. | ||
""" | ||
return (self.date.year - d.year) * 12 + self.date.month - d.month | ||
|
||
def __diff_week(self, d): | ||
""" | ||
:param d: Date as datetime, self.date >= Date. | ||
|
||
:return: Difference in weeks(int) ignoring partially complete weeks. | ||
""" | ||
monday1 = (self.date - datetime.timedelta(days=self.date.weekday())) | ||
monday2 = (d - datetime.timedelta(days=d.weekday())) | ||
return (monday1 - monday2).days // 7 | ||
|
||
def __diff_days(self, d): | ||
""" | ||
:param d: Date as datetime, self.date >= Date. | ||
|
||
:return: Difference in days(int) ignoring partially complete days. | ||
""" | ||
return (self.date-d).days | ||
|
||
def get_data(self): | ||
""" | ||
Get data | ||
|
||
:return: Data in form of dict containing year, month, week data. | ||
""" | ||
for issue in self.content: | ||
issue = issue['issue'] | ||
# Parse date, while ignoring the timestamp. | ||
dt = parser.parse(issue['createdAt'][:10]) | ||
|
||
mon = self.__diff_month(dt) | ||
if mon < self.CONSTANTS['month_count']: | ||
mon = self.CONSTANTS['month_count'] - mon | ||
self.data['year']['opened'][mon] += 1 | ||
if issue['state'] == 'closed': | ||
self.data['year']['closed'][mon] += 1 | ||
|
||
wk = self.__diff_week(dt) | ||
if wk < self.CONSTANTS['week_count']: | ||
wk = self.CONSTANTS['week_count'] - wk | ||
self.data['month']['opened'][wk] += 1 | ||
if issue['state'] == 'closed': | ||
self.data['month']['closed'][wk] += 1 | ||
|
||
dys = self.__diff_days(dt) | ||
if dys < self.CONSTANTS['day_count']: | ||
dys = self.CONSTANTS['day_count'] - dys | ||
self.data['week']['opened'][dys] += 1 | ||
if issue['state'] == 'closed': | ||
self.data['week']['closed'][dys] += 1 | ||
|
||
return self.data | ||
|
||
|
||
if __name__ == '__main__': | ||
|
||
org_name = open('org_name.txt').readline() | ||
|
||
# URL to grab all issues from | ||
issues_url = 'http://' + org_name + '.github.io/gh-board/issues.json' | ||
|
||
content = requests.get(issues_url) | ||
parsed_json = content.json() | ||
|
||
real_data = Scraper(parsed_json['issues'], datetime.datetime.today()) | ||
real_data = real_data.get_data() | ||
|
||
print(real_data) | ||
with open('static' + os.sep + 'activity-data.json', 'w') as fp: | ||
json.dump(real_data, fp) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/bin/bash | ||
|
||
regex="github.com/[a-z0-9A-Z]*" | ||
git config -l | grep -o "$regex" | while read -r line ; do | ||
echo "${line:11}" | xargs echo -n > org_name.txt | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we prefer two indents by default for shell. Maybe see what ShellCheckBear says ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems not, .ci/deploy.sh uses 4 as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that was a horribly rushed script I mostly copied from other sources and fixed up; without code review; naughty me. https://github.com/coala/coala-bears/blob/master/.ci/deps.sh We dont run code linting on them yet. I've created coala/coala#4952 for that. (I'll publish a backlog of new tasks tomorrow) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (this script is so small it doesnt matter; you are right we dont have a convention yet, so you dont need to update this patch to follow a non-existent coding standard) |
||
done |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* globals $, Chart */ | ||
var curChart; | ||
|
||
function setChart(labels, openedData, closedData, type) { | ||
var ctx = document.getElementById("canvas"); | ||
|
||
curChart = new Chart(ctx, { | ||
type: 'line', | ||
data: { | ||
labels: labels, | ||
datasets: [{ | ||
label: "Issues Opened", | ||
backgroundColor: "RGBA(33, 150, 243, 0.2)", | ||
borderColor: "RGBA(33, 150, 243, 1)", | ||
data: openedData, | ||
fill: true, | ||
}, { | ||
label: "Issues Closed", | ||
backgroundColor: "RGBA(244, 67, 54, 0.2)", | ||
borderColor: "RGBA(244, 67, 54, 1)", | ||
data: closedData, | ||
fill: true, | ||
}] | ||
}, | ||
options: { | ||
responsive: true, | ||
title: { | ||
display: true, | ||
text: 'Community Activity' | ||
}, | ||
tooltips: { | ||
mode: 'index', | ||
intersect: false, | ||
}, | ||
hover: { | ||
mode: 'nearest', | ||
intersect: true | ||
}, | ||
scales: { | ||
xAxes: [{ | ||
display: true, | ||
scaleLabel: { | ||
display: true, | ||
labelString: type | ||
} | ||
}], | ||
yAxes: [{ | ||
display: true, | ||
scaleLabel: { | ||
display: true, | ||
labelString: 'Number' | ||
} | ||
}] | ||
} | ||
} | ||
}); | ||
} | ||
|
||
function updateChart(type) { | ||
if(curChart){ curChart.destroy(); } | ||
|
||
$.getJSON("/static/activity-data.json", | ||
function(data) { | ||
var labels, openedData, closedData; | ||
if(type === "Month") { | ||
labels = data.year.labels; | ||
openedData = data.year.opened; | ||
closedData = data.year.closed; | ||
} | ||
else if(type === "Week") { | ||
labels = data.month.labels; | ||
openedData = data.month.opened; | ||
closedData = data.month.closed; | ||
} | ||
else { | ||
labels = data.week.labels; | ||
openedData = data.week.opened; | ||
closedData = data.week.closed; | ||
} | ||
setChart(labels, openedData, closedData, type); | ||
}) | ||
.fail(function(data, textStatus, error) { | ||
var err = textStatus + ", " + error; | ||
console.error("Request Failed: " + err); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return type