Skip to content

Instantly share code, notes, and snippets.

@bilderbuchi
Last active December 20, 2015 08:19
Show Gist options
  • Save bilderbuchi/6099727 to your computer and use it in GitHub Desktop.
Save bilderbuchi/6099727 to your computer and use it in GitHub Desktop.
Issue tracker raw code. not very userfriendly.
"""
A set of small helper functions for interacting with Github repositories
(primarily openFrameworks) via PyGithub.
"""
import webbrowser, os, inspect
from github import Github
def get_repo(user='openframeworks', repo='openFrameworks',
token='Github_token.txt', timeout=20):
"""Return Github authenticated repo, ready for use"""
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
with open(os.path.join(currentdir,token)) as token_file:
my_token = token_file.readline().rstrip('\n')
G = Github(my_token, timeout=timeout)
return G.get_user(user).get_repo(repo)
def open_in_browser(list_of_urls):
"""Offer to open a list of URLs in the browser."""
if list_of_urls:
answer = raw_input('Press "y" to open all issues in the browser, other key to quit:\n')
if answer.lower() == 'y':
print('Opening...')
for u in list_of_urls:
webbrowser.open(u)
print('Done')
#!/usr/bin/python
"""Main script for openFrameworks Github issues visualization."""
from github import Github
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import legend
import numpy as np
import datetime, dateutil
import times # module for saner time management. See http://nvie.com/posts/introducing-times/
import pickle
from itertools import chain
from sys import exit
# hack for relative import of module
# you can probably drop this if github_tools.py is in the same directory as this
# script (but it's not in my setup)
import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir)
import github_tools
# TODO:
# possibly use a database instead of pickling: https://github.com/pudo/dataset
# issue open duration should not be in recarray
#link plots
# (opendate, 1), (closedate, -1); cat; sort_by_date; cumsum(col2); plot
# same for PR
# use PyGithub's new "since" for getting fresh commits only
#
# other plots:
# % issues without label
# issue longest silent
# longest open issue
# most issues closed/opened per week/7day window
# % without any comment
# most bugs squashed
#Github/PyGithub objects are Capitalized!
###############################################################################
#CONFIGURATION
fetch=True # fetch data from github or load from json-serialized file
#fetch=False
mpl.rc('axes',grid=True, axisbelow=True)
mpl.rc('grid',linestyle='-', color='lightgray')
mpl.rc('figure', dpi=90)
#-------------------------------------------------------------------------------------
datefmt = "%Y-%m-%dT%H:%M:%SZ" #2012-05-19T00:28:09Z
event_datefmt = "%Y-%m-%d"
tmp=[['2008-09-04', '2008-09-09'],['2011-01-10', '2011-01-14'],['2012-02-20', '2012-02-27'],['2013-08-08', '2013-08-14']]
OFEventTitles=['OFLab Linz', 'DevCon Pittsburgh', 'DevCon Detroit', 'DevCon Yamaguchi']
OFEvents=[[datetime.datetime.strptime(x,event_datefmt) for x in y] for y in tmp]
###############################################################################
# Load all we need from Github. Create raw list data
if fetch:
print 'Fetching fresh data from Github'
Repo = github_tools.get_repo()
print 'OF has ' + str(Repo.open_issues) + ' open issues.'
print('\nGetting issues')
Open_Issues=Repo.get_issues(state='open')
Closed_Issues=Repo.get_issues(state='closed')
print 'Issues received'
print('\nGetting tags')
Tags=Repo.get_tags()
print('Tags received')
print('\nGetting commits')
Commits=Repo.get_commits()
# TODO: find a better/faster way to query this. probably via local git repo
print('Commits received')
# ---------------------------------------------------------------------------
print '\nCreating issues recarray'
# unicode strings as objects, otherwise string length is 0!
issues_dtype=[('number', int), ('state', object), ('created_at', object), ('closed_at', object), ('duration_open', object)]
# create new, empty recarray:
issues_recarray=np.recarray((0,),dtype=issues_dtype)
for i in chain(Closed_Issues,Open_Issues):
issues_recarray.resize(issues_recarray.shape[0]+1)
if i.closed_at:
closedate=i.closed_at
else:
closedate=datetime.datetime.now()
entry=(i.number, i.state, i.created_at, i.closed_at, closedate-i.created_at)
issues_recarray[-1]=entry
issues_recarray.sort(order='number')
print '%s issues on record' % issues_recarray.shape[0]
print '\nCreating commits recarray'
commits_dtype=[('sha', object), ('commit_date', object), ('author_date', object)]
commits_recarray=np.recarray((0,), dtype=commits_dtype)
for c in Commits:
commits_recarray.resize(commits_recarray.shape[0]+1)
commits_recarray[-1]=(c.sha, c.commit.committer.date, c.commit.author.date)
print '%s commits received' % commits_recarray.shape[0]
print('\nCreating tags recarray')
tags_dtype=[('date', object), ('name', object)]
tags_recarray=np.recarray((0,), dtype=tags_dtype)
for t in Tags:
tags_recarray.resize(tags_recarray.shape[0]+1)
tags_recarray[-1]=(t.commit.commit.committer.date, t.name)
print '%s tags received' % tags_recarray.shape[0]
# ---------------------------------------------------------------------------
print "\nSaving data"
with open('issues_recarray.pickle', 'wb') as fp:
pickle.dump(issues_recarray, fp)
with open('commits_recarray.pickle', 'wb') as fp:
pickle.dump(commits_recarray, fp)
with open('tags_recarray.pickle', 'wb') as fp:
pickle.dump(tags_recarray, fp)
print "Data saved!\n"
###############################################################################
# Load lists from file
else:
print '\nLoading existing data from files'
with open('issues_recarray.pickle', 'rb') as fp:
issues_recarray = pickle.load(fp)
with open('commits_recarray.pickle', 'rb') as fp:
commits_recarray = pickle.load(fp)
with open('tags_recarray.pickle', 'rb') as fp:
tags_recarray = pickle.load(fp)
print 'Files loaded!\n'
###############################################################################
# Process objects.
open_issue_count=np.vstack((issues_recarray.created_at, np.ones((len(issues_recarray.created_at))) ))
open_issue_count=np.hstack(( open_issue_count, np.vstack((issues_recarray.closed_at[issues_recarray.state == 'closed'], -1*np.ones((len(issues_recarray.closed_at[issues_recarray.state == 'closed']))))) )).T
open_issue_count=open_issue_count[open_issue_count[:,0].argsort()]
open_issue_count=np.column_stack((open_issue_count, np.cumsum(open_issue_count[:,1], axis=0)))
#alternatively, use np.newaxis to get a proper 2D array
#print str(np.shape(open_issue_count))
xbegin = min([min(commits_recarray.author_date), min(issues_recarray.created_at)])
xend = datetime.datetime.now()
print "Data range: %s days\n" % str((xend-xbegin).days)
bin_edges=[xbegin] + dateutil.rrule.rrule(dateutil.rrule.WEEKLY, dtstart = xbegin,byweekday=dateutil.rrule.MO).between(xbegin, xend, inc=False) + [xend]
bin_edges=mpl.dates.date2num(bin_edges)
#TODO: Calculate average, stddev, max of time-to-fix, open time.
###############################################################################
#Plot figure
fig = plt.figure(figsize=(380/25.4, 200/25.4))
ax = fig.add_subplot(211)
plt.title('OF issue tracker statistics - created ' + str(xend.date()))
#
for t in xrange(tags_recarray.shape[0]):
ax.axvline(tags_recarray.date[t],color='y',alpha=0.5)
plt.annotate(tags_recarray.name[t], xy=(tags_recarray.date[t], 0.90) ,xycoords=("data", "axes fraction"), ha='right', va='top')
for e in xrange(len(OFEventTitles)):
ax.axvspan(OFEvents[e][0],OFEvents[e][1],color='y',alpha=0.5)
plt.annotate(OFEventTitles[e], xy=(OFEvents[e][0], 0.97) ,xycoords=("data", "axes fraction"), ha='right', va='top')
ax.plot(open_issue_count[:,0],open_issue_count[:,2],label='open issues', color='k', alpha=0.8)
ax.hist([mpl.dates.date2num(issues_recarray.created_at), mpl.dates.date2num(issues_recarray.closed_at[issues_recarray.state == 'closed'])], histtype='barstacked',bins=bin_edges,label=['created issues','closed issues'], color=['red', 'green'],alpha=0.8)
ax.legend(loc='center left')
locator=mpl.dates.AutoDateLocator(maxticks=15)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(mpl.dates.AutoDateFormatter(locator))
ax.xaxis.grid(False)
ax.set_xlim(left=xbegin)
ax.tick_params(axis='x', direction='out')
# ---------------------------------------------------------------------------
ax2 = fig.add_subplot(212, sharex=ax)
plt.title('OF commit statistics')
for t in xrange(tags_recarray.shape[0]):
ax2.axvline(tags_recarray.date[t],color='y',alpha=0.5)
plt.annotate(tags_recarray.name[t], xy=(tags_recarray.date[t], 0.90) ,xycoords=("data", "axes fraction"), ha='right', va='top')
for e in xrange(len(OFEventTitles)):
ax2.axvspan(OFEvents[e][0],OFEvents[e][1],color='y',alpha=0.5)
plt.annotate(OFEventTitles[e], xy=(OFEvents[e][0], 0.97) ,xycoords=("data", "axes fraction"), ha='right', va='top')
ax2.hist(mpl.dates.date2num(commits_recarray.author_date),bins=bin_edges,label='master commits authored',color='blue',alpha=0.5)
ax2.legend(loc='best')
ax2.xaxis.set_major_locator(locator)
ax2.xaxis.set_major_formatter(mpl.dates.AutoDateFormatter(locator))
ax2.xaxis.grid(False)
ax2.set_xlim(left=xbegin)
ax2.tick_params(axis='x', direction='out')
fig.autofmt_xdate()
plt.tight_layout()
plt.show()
fig.savefig('./autosave/OF_repo_viz_'+str(xend.date())+'.png')
print '\nFinished!'
###############################################################################
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment