-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathwork.py
More file actions
executable file
·143 lines (118 loc) · 4.92 KB
/
work.py
File metadata and controls
executable file
·143 lines (118 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/python2.7
"""Tool to generate a graph of commits per day.
The tool reads the repository information of one or more Mercurial repositories
and builds a chart with commit activity per day. This is similar to the commit
chart in github, but generated locally. It creates an SVG file, and launches a
file viewer to display it.
Notice that the mercurial module is not supported in Python3, so we have to use
Python 2.7
"""
__author__ = 'jt@javiertordable.com (Javier Tordable)'
from mercurial import hg, changelog, ui
from svgwrite import Drawing
import argparse
from datetime import date, timedelta
import math
import subprocess
import sys
import os
# Chart file configuration.
CHART_FILE_NAME = 'work.svg'
CHART_FILE_PATH = os.getcwd()
CHART_VIEWER = 'eog'
# Visualization style.
NUM_DAYS_TO_SHOW = 365
DAY_BOX_SIZE = 11
DAY_BOX_SEPARATION = 2
DISTANCE_BETWEEN_BOXES = DAY_BOX_SIZE + DAY_BOX_SEPARATION
MARGIN = 6
# Box colors, sorted from weaker to stronger.
BOX_COLORS = ['#eeeeee', '#d6e685', '#8cc665', '#44a340', '#1e6823']
def create_empty_chart(full_file_name):
"""Creates a chart of the proper dimensions with a white background."""
num_days_in_week = 7
num_weeks = math.ceil(NUM_DAYS_TO_SHOW / 7.0)
if date.today().weekday() + 1 < NUM_DAYS_TO_SHOW % 7:
# We need to draw NUM_DAYS_TO_SHOW % 7 extra days, but on the last week
# we have only space for date.today().weekday() + 1 days.
num_weeks += 1
width = 2 * MARGIN + num_weeks * DAY_BOX_SIZE + \
(num_weeks - 1) * DAY_BOX_SEPARATION
height = 2 * MARGIN + num_days_in_week * DAY_BOX_SIZE + \
(num_days_in_week - 1) * DAY_BOX_SEPARATION
chart = Drawing(full_file_name, size=(width, height))
chart.add(chart.rect(insert=(0, 0), size=(width, height), fill='white'))
return chart
def get_box_color(count):
"""Returns the box color that corresponds to the given count."""
if count < 1:
return BOX_COLORS[0]
elif count == 1:
return BOX_COLORS[1]
elif 2 <= count <= 3:
return BOX_COLORS[2]
elif 4 <= count <= 5:
return BOX_COLORS[3]
else: # 6 <= count.
return BOX_COLORS[4]
def draw_daily_boxes(chart, start_date, cl_counts):
"""Draws the boxes for CL counts for each day."""
first_day_to_show = start_date.weekday()
last_day_to_show = first_day_to_show + NUM_DAYS_TO_SHOW
for day_index in range(first_day_to_show, last_day_to_show):
# Boxes are stacked first by column and then by row.
x = MARGIN + (day_index // 7) * DISTANCE_BETWEEN_BOXES
y = MARGIN + (day_index % 7) * DISTANCE_BETWEEN_BOXES
# Compute the real date from the day index.
day = start_date + timedelta(days=(day_index - first_day_to_show))
if day in cl_counts:
color = get_box_color(cl_counts[day])
else:
color = get_box_color(0)
chart.add(chart.rect(insert=(x,y),
size=(DAY_BOX_SIZE, DAY_BOX_SIZE),
fill=color))
def extract_cl_counts(repository_path, cl_counts):
"""Reads the repository changelog and extracts CL counts per day."""
repository = hg.repository(ui.ui(), repository_path)
changelog = repository.changelog
for cl_index in changelog:
cl_id = changelog.lookup(cl_index)
cl = changelog.read(cl_id)
# The timestamp seems to be the 3rd field in the CL.
# It's given in a tuple. The UNIX timestap is the first field.
timestamp = cl[2][0]
cl_date = date.fromtimestamp(timestamp)
if cl_date in cl_counts:
cl_counts[cl_date] = cl_counts[cl_date] + 1
else:
cl_counts[cl_date] = 1
def view_chart(full_file_name):
"""Launch the image viewer to open the SVG file."""
# Don't print initialization errors.
subprocess.call([CHART_VIEWER, full_file_name], stderr=open(os.devnull))
def main():
parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__)
parser.add_argument('path', nargs='*', default=None,
help='Root directory for the Mercurial repository.')
args = parser.parse_args()
# Get the changelog data from each repository.
cl_counts = {}
if args.path:
for repository_path in args.path:
extract_cl_counts(repository_path, cl_counts)
else:
# Assume that the current path has a repository.
extract_cl_counts(os.getcwd(), cl_counts)
# Draw the chart and save it in a file.
full_file_name = os.path.join(CHART_FILE_PATH, CHART_FILE_NAME)
chart = create_empty_chart(full_file_name)
start_date = date.today() - timedelta(days=(NUM_DAYS_TO_SHOW - 1))
draw_daily_boxes(chart, start_date, cl_counts)
chart.save()
# Open the image file and print total count.
view_chart(full_file_name)
print('Changes as of: ' + str(date.today()) + ': ' + \
str(sum(cl_counts.itervalues())))
if __name__ == '__main__':
main()