add punchcard graphic

Graphing code adapted from https://stackoverflow.com/a/14850998
This commit is contained in:
Frédéric Péters 2017-10-14 17:20:51 +02:00
parent cb4a7ef263
commit 7c3fdf5007
1 changed files with 92 additions and 0 deletions

View File

@ -0,0 +1,92 @@
import matplotlib.pyplot as plt
import numpy as np
from django.core.management.base import BaseCommand
from django.utils.dateparse import parse_date
from eodb.events.models import Commit
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('--username', metavar='USERNAME')
parser.add_argument('--datemin', metavar='DATEMIN')
parser.add_argument('--datemax', metavar='DATEMAX')
parser.add_argument('--committime', action='store_true',
help='use commit datetime instead of author datetime')
def handle(self, *args, **options):
infos = {}
for i in range(7):
for j in range(24):
infos[(i, j)] = 0
filters = {'author_email__endswith': '@entrouvert.com'}
title = 'Git activity'
if options.get('username'):
filters['author_email__startswith'] = options['username'] + '@'
title += ' for %s' % options['username']
datetime_var = 'author_datetime'
if options.get('committime'):
datetime_var = 'commit_datetime'
if options.get('datemin'):
filters[datetime_var + '__gte'] = parse_date(options['datemin'])
title += ' from %s' % options['datemin']
if options.get('datemax'):
filters[datetime_var + '__lt'] = parse_date(options['datemax'])
title += ' until %s' % options['datemax']
for commit in Commit.objects.filter(**filters):
coords = (getattr(commit, datetime_var).weekday(), getattr(commit, datetime_var).hour)
infos[coords] = infos[coords] + 1
draw_punchcard(infos)
plt.title(title)
plt.show()
# https://stackoverflow.com/a/14850998
def draw_punchcard(infos,
ax1=range(7),
ax2=range(24),
ax1_ticks=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
ax2_ticks=range(24),
ax1_label='Day',
ax2_label='Hour'):
# - infos: Dictionary of quantities to display.
# They are indexed by key of type (val1,val2) with
# val1 included in ax1 and val2 included in ax2.
# build the array which contains the values
data = np.zeros((len(ax1),len(ax2)))
for key in infos:
data[key[0],key[1]] = infos[key]
data = data/float(np.max(data))
# shape ratio
r = float(data.shape[1])/data.shape[0]
# Draw the punchcard (create one circle per element)
# Ugly normalisation allows to obtain perfect circles instead of ovals....
for y in range(data.shape[0]):
for x in range(data.shape[1]):
circle = plt.Circle((x/float(data.shape[1]-1)*(data.shape[0]-1),y/r),
data[y][x]/float(data.shape[1])*data.shape[0]/2)
plt.gca().add_artist(circle)
plt.ylim(0-0.5, data.shape[0]-0.5)
plt.xlim(0, data.shape[0])
plt.yticks(np.arange(0,len(ax1)/r-.1,1/r), ax1_ticks)
xt = np.linspace(0, len(ax1)-1, len(ax2))
plt.xticks(xt, ax2_ticks)
plt.xlabel(ax2_label)
plt.ylabel(ax1_label)
plt.gca().invert_yaxis()
# make sure the axes are equal, and resize the canvas to fit the plot
plt.axis('equal')
plt.axis([-1, 7, 7/r, -1])
scale = 1 #0.5
plt.gcf().set_size_inches(data.shape[1]*scale,data.shape[0]*scale, forward=True)