Skip to content

Commit e48b3d3

Browse files
committed
Add a script to make an Excel spreadsheet for estimation in Google Docs
1 parent 81e1101 commit e48b3d3

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

make-estimation-spreadsheet.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env python
2+
3+
from collections import namedtuple
4+
import doctest
5+
from optparse import OptionParser
6+
import os
7+
from os.path import dirname, join, realpath
8+
import re
9+
from xlwt import Workbook, Formula
10+
11+
from github import get_issues
12+
13+
# This script generates an Excel spreadsheet with one sheet per
14+
# developer and a consensus sheet, designed to be uploaded to Google
15+
# Spreadsheets for making estimates independently.
16+
17+
cwd = os.getcwd()
18+
repo_directory = realpath(join(dirname(__file__)))
19+
20+
SheetInfo = namedtuple('SheetInfo', ['name', 'developer', 'sheet'])
21+
22+
Issue = namedtuple('Issue', ['url', 'number', 'title'])
23+
24+
# Algorithm from: http://stackoverflow.com/a/182924/223092
25+
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
26+
def column_number_to_letters(column_number):
27+
dividend = column_number
28+
result = ''
29+
while dividend > 0:
30+
modulus = (dividend - 1) % len(alphabet)
31+
result = alphabet[modulus] + result
32+
dividend = (dividend - modulus) / len(alphabet)
33+
return result
34+
35+
def get_unestimated_open_issues(repo):
36+
37+
results = []
38+
row_index = 1
39+
40+
for number, title, body, issue in get_issues(repo):
41+
if ('pull_request' in issue) and issue['pull_request']['html_url']:
42+
continue
43+
difficulty_label_names = [i['name'] for i in issue['labels']
44+
if re.search(r'Difficulty ', i['name'])]
45+
if difficulty_label_names:
46+
continue
47+
row_index += 1
48+
results.append(Issue(issue['html_url'], number, title))
49+
50+
return results
51+
52+
def main(repo, developers):
53+
54+
basic_header = ['URL', 'Number', 'Title']
55+
56+
workbook = Workbook()
57+
sheets = []
58+
59+
for developer in developers:
60+
sheet_name = "{0} Estimates".format(developer)
61+
sheet = workbook.add_sheet(sheet_name)
62+
sheets.append(SheetInfo(sheet_name, developer, sheet))
63+
64+
consensus_sheet_name = "Consensus"
65+
consensus_sheet_info = SheetInfo(
66+
consensus_sheet_name,
67+
None,
68+
workbook.add_sheet(consensus_sheet_name)
69+
)
70+
sheets.append(consensus_sheet_info)
71+
72+
for sheet_info in sheets:
73+
sheet = sheet_info.sheet
74+
for column_index, column_header in enumerate(basic_header):
75+
sheet.write(0, column_index, column_header)
76+
if sheet_info.developer:
77+
sheet.write(0, len(basic_header), "{0} Estimate".format(sheet_info.developer))
78+
sheet.write(0, len(basic_header) + 1, "{0} Notes".format(sheet_info.developer))
79+
else:
80+
# If it's the consensus sheet, then add two columns for
81+
# each developer.
82+
column_index = len(basic_header)
83+
for developer in developers:
84+
sheet.write(0, column_index, "{0} Estimate".format(developer))
85+
sheet.write(0, column_index + 1, "{0} Notes".format(developer))
86+
column_index += 2
87+
sheet.write(0, column_index, "Consensus")
88+
89+
for i, issue in enumerate(get_unestimated_open_issues(repo)):
90+
row_index = i + 1
91+
for sheet_info in sheets:
92+
for column_index in range(len(basic_header)):
93+
sheet_info.sheet.write(row_index, column_index, issue[column_index])
94+
# In the Consensus sheet add a reference to each of the
95+
# developer sheets' estimate and notes columns.
96+
for i, sheet_info in enumerate(s for s in sheets if s.developer):
97+
fmt = "'{sheet}'!{column}{row}"
98+
for original_column_index in (len(basic_header) + i for i in range(2)):
99+
formula_text = fmt.format(
100+
sheet=sheet_info.name,
101+
column=column_number_to_letters(original_column_index + 1),
102+
row=row_index + 1)
103+
consensus_sheet_info.sheet.write(
104+
row_index,
105+
original_column_index + 2 * i,
106+
Formula(formula_text))
107+
108+
workbook.save('example.xls')
109+
110+
usage = """Usage: %prog [options] REPOSITORY DEVELOPER_A DEVELOPER_B ...
111+
112+
Repository should be username/repository from GitHub, e.g. mysociety/pombola"""
113+
parser = OptionParser(usage=usage)
114+
parser.add_option("-t", "--test",
115+
action="store_true", dest="test", default=False,
116+
help="Run doctests")
117+
118+
(options, args) = parser.parse_args()
119+
120+
if options.test:
121+
doctest.testmod()
122+
else:
123+
if len(args) < 2:
124+
parser.print_help()
125+
else:
126+
repository = args[0]
127+
developers = args[1:]
128+
main(repository, developers)

0 commit comments

Comments
 (0)