Skip to content
This repository was archived by the owner on Sep 27, 2019. It is now read-only.

Commit ab16393

Browse files
committed
Expanded PR czar status report. It can now automatically pull down the schedule and use that as the send-to email addresss
1 parent f6edb95 commit ab16393

File tree

1 file changed

+91
-18
lines changed

1 file changed

+91
-18
lines changed

script/github/github-pr-status.py

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import urllib
1414
import json
1515
import logging
16+
import requests
17+
import csv
18+
from StringIO import StringIO
1619
from datetime import datetime
1720
from pprint import pprint
1821

@@ -40,9 +43,9 @@
4043
GITHUB_USER = 'cmu-db'
4144
GITHUB_REPO = 'peloton'
4245

43-
CACHE_FILEPATH = "/tmp/%d.github-cache"
46+
CACHE_FILEPATH = "/tmp/github-cache/%s.cache"
4447

45-
EMAIL_FROM = "[email protected]"
48+
EMAIL_FROM = "[email protected]"
4649

4750
# ==============================================
4851
# pr_format
@@ -54,13 +57,59 @@ def pr_format(pr):
5457
return (ret)
5558
## DEF
5659

60+
# ==============================================
61+
# get_current_czar
62+
# ==============================================
63+
def get_current_czar(url, cache=False):
64+
65+
data = None
66+
cache_file = CACHE_FILEPATH % "schedule"
67+
if cache and os.path.exists(cache_file):
68+
LOG.debug("Cached Schedule '%s'" % cache_file)
69+
with open(cache_file, "r") as fd:
70+
data = fd.read()
71+
else:
72+
LOG.debug("Downloading schedule from %s" % url)
73+
response = requests.get(url)
74+
assert response.status_code == 200, \
75+
"Failed to download schedule '%s'" % url
76+
data = response.content
77+
LOG.debug("Creating schedule cached file '%s'" % cache_file)
78+
with open(cache_file, "w") as fd:
79+
fd.write(data)
80+
## IF
81+
82+
# The CSV file should have two columns:
83+
# (1) The date "Month Year"
84+
# (2) Email address
85+
current_date = datetime.now().strftime("%B %Y")
86+
current_czar = None
87+
first = True
88+
for row in csv.reader(StringIO(data)):
89+
if first:
90+
first = False
91+
continue
92+
if row[0].strip() == current_date:
93+
current_czar = row[1].strip()
94+
break
95+
## FOR
96+
if current_czar is None:
97+
raise Exception("Failed to find current assignment for '%s'" % current_date)
98+
99+
LOG.debug("Current Czar ('%s'): %s" % (current_date, current_czar))
100+
return (current_czar)
101+
## DEF
102+
103+
57104
# ==============================================
58105
# main
59106
# ==============================================
60107
if __name__ == '__main__':
61108
aparser = argparse.ArgumentParser()
62109
aparser.add_argument('token', type=str, help='Github API Token')
63-
aparser.add_argument('--send', type=str, help='Send status report to given email')
110+
aparser.add_argument('schedule', type=str, help='Schedule CSV URL')
111+
aparser.add_argument('--send', action='store_true', help='Send status report to current czar')
112+
aparser.add_argument('--send-to', type=str, help='Send status report to given email')
64113
aparser.add_argument('--gmail-user', type=str, help='Gmail username')
65114
aparser.add_argument('--gmail-pass', type=str,help='Gmail password')
66115
aparser.add_argument("--cache", action='store_true', help="Enable caching of raw Github requests (for development only)")
@@ -72,6 +121,20 @@ def pr_format(pr):
72121
if args['debug']:
73122
LOG.setLevel(logging.DEBUG)
74123

124+
if args['cache']:
125+
cache_dir = os.path.dirname(CACHE_FILEPATH)
126+
if not os.path.exists(cache_dir):
127+
LOG.debug("Creating cache directory '%s'" % cache_dir)
128+
os.makedirs(cache_dir)
129+
## IF
130+
131+
132+
## ----------------------------------------------
133+
134+
135+
# Download the schedule CSV and figure out who is the czar this month
136+
current_czar = get_current_czar(args['schedule'], cache=args['cache'])
137+
75138
## ----------------------------------------------
76139

77140
gh = Github(token=args['token'])
@@ -104,7 +167,7 @@ def pr_format(pr):
104167

105168

106169
# Get review comments for this issue
107-
cache_reviews = CACHE_FILEPATH % pr.number
170+
cache_reviews = CACHE_FILEPATH % str(pr.number)
108171
data = None
109172
if 'cache' in args and args['cache'] and os.path.exists(cache_reviews):
110173
LOG.debug("CACHED REVIEW COMMENTS '%s'" % cache_reviews)
@@ -133,7 +196,7 @@ def pr_format(pr):
133196
all_recieved_reviews = { }
134197
all_reviewers = { }
135198
for pr, labels, reviews in open_pulls.values():
136-
LOG.debug("Pull Request #%d - LABELS: %s", pr.number, labels)
199+
LOG.debug("PR #%d - LABELS: %s", pr.number, labels)
137200

138201
# Step 1: Get the list of 'ready_for_review' PRs that do not have
139202
# an assigned reviewer
@@ -171,11 +234,11 @@ def pr_format(pr):
171234
status['ReviewMissing'].append(pr.number)
172235

173236

174-
all_reviewers[pr.number] = reviewers
175-
all_recieved_reviews[pr.number] = recieved_reviews
237+
all_reviewers[pr.number] = map(str, reviewers)
238+
all_recieved_reviews[pr.number] = map(str, recieved_reviews)
176239

177-
LOG.debug("REVIEWERS: %s", ",".join(reviewers))
178-
LOG.debug("RECEIVED REVIEWS: %s", ",".join(recieved_reviews))
240+
LOG.debug("PR #%d - REVIEWERS: %s", pr.number, ",".join(reviewers))
241+
LOG.debug("PR #%d - RECEIVED REVIEWS: %s", pr.number, ",".join(recieved_reviews))
179242
# IF
180243

181244
# Step 4: Mark any PRs without labels
@@ -190,7 +253,7 @@ def pr_format(pr):
190253
## NO LABELS
191254
content += "*NO LABELS*\n\n"
192255
if len(status['NoLabels']) == 0:
193-
content += "**NONE**\n\n"
256+
content += "*NONE*\n\n"
194257
else:
195258
for pr_num in sorted(status['NoLabels']):
196259
content += pr_format(open_pulls[pr_num][0]) + "\n\n"
@@ -199,7 +262,7 @@ def pr_format(pr):
199262
## READY TO MERGE
200263
content += "*READY TO MERGE*\n\n"
201264
if len(status['ReadyToMerge']) == 0:
202-
content += "**NONE**\n\n"
265+
content += "*NONE*\n\n"
203266
else:
204267
for pr_num in sorted(status['ReadyToMerge']):
205268
content += pr_format(open_pulls[pr_num][0]) + "\n\n"
@@ -208,29 +271,39 @@ def pr_format(pr):
208271
## READY FOR REVIEW, NO ASSIGNMENT
209272
content += "*READY FOR REVIEW WITHOUT ASSIGNMENT*\n\n"
210273
if len(status['NeedReviewers']) == 0:
211-
content += "**NONE**\n\n"
274+
content += "*NONE*\n\n"
212275
else:
213276
for pr_num in sorted(status['NeedReviewers']):
214277
content += pr_format(open_pulls[pr_num][0]) + "\n\n"
215278
content += linebreak
216279

217-
218280
## MISSING REVIEWS
219281
content += "*WAITING FOR REVIEW*\n\n"
220282
if len(status['ReviewMissing']) == 0:
221-
content += "**NONE**\n\n"
283+
content += "*NONE*\n\n"
222284
else:
223285
for pr_num in sorted(status['ReviewMissing']):
224-
content += pr_format(open_pulls[pr_num][0]) + "\n\n"
225-
content += " Assigned Reviewers: %s\n" % (list(all_recieved_reviews[pr_num]))
286+
if len(all_reviewers[pr_num]) == 0:
287+
all_reviewers[pr_num] = [ '*NONE*' ]
288+
if len(all_recieved_reviews[pr_num]) == 0:
289+
all_recieved_reviews[pr_num] = [ '*NONE*' ]
290+
291+
content += pr_format(open_pulls[pr_num][0]) + "\n"
292+
content += " Assigned Reviewers: %s\n" % (",".join(all_reviewers[pr_num]))
293+
content += " Reviews Provided: %s\n\n" % (",".join(all_recieved_reviews[pr_num]))
226294
#content += linebreak
227295

228296

229297
if "send" in args and args["send"]:
298+
send_to = current_czar
299+
if "send_to" in args and args["send_to"]:
300+
send_to = args["send_to"]
301+
302+
LOG.debug("Sending status report to '%s'" % send_to)
230303
msg = MIMEText(content)
231304
msg['Subject'] = "%s PR Status Report (%s)" % (GITHUB_REPO.title(), datetime.now().strftime("%Y-%m-%d"))
232305
msg['From'] = EMAIL_FROM
233-
msg['To'] = args["send"]
306+
msg['To'] = send_to
234307
msg['Reply-To'] = "[email protected]"
235308

236309
server_ssl = smtplib.SMTP_SSL("smtp.gmail.com", 465)
@@ -241,7 +314,7 @@ def pr_format(pr):
241314
#server_ssl.quit()
242315
server_ssl.close()
243316

244-
LOG.info("Status email sent to '%s'" % args["send"])
317+
LOG.info("Status email sent to '%s'" % send_to)
245318
else:
246319
print content
247320
## IF

0 commit comments

Comments
 (0)