forked from openedx/repo-tools
/
pulls.py
125 lines (104 loc) · 4.21 KB
/
pulls.py
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
import operator
import pprint
from helpers import paginated_get, requests
import jreport
from urlobject import URLObject
import yaml
class JPullRequest(jreport.JObj):
def __init__(self, issue_data, org_fn=None):
super(JPullRequest, self).__init__(issue_data)
self['labels'] = [self.short_label(l['name']) for l in self['labels']]
if org_fn:
self['org'] = org_fn(self)
# A pull request is external if marked as such, or if the author's
# organization is not edX.
if "osc" in self['labels']:
self['intext'] = "external"
elif self['org'] == "edX":
self['intext'] = "internal"
else:
self['intext'] = "external"
def load_pull_details(self, pulls=None):
"""Get pull request details also.
`pulls` is a dictionary of pull requests, to perhaps avoid making
another request.
"""
pull_request = None
if pulls:
pull_request = pulls.get(self['number'])
if not pull_request:
pull_request = requests.get(self['pull_request.url']).json()
self['pull'] = pull_request
if self['state'] == 'open':
self['combinedstate'] = 'open'
self['combinedstatecolor'] = 'green'
elif self['pull.merged_at']:
self['combinedstate'] = 'merged'
self['combinedstatecolor'] = 'blue'
else:
self['combinedstate'] = 'closed'
self['combinedstatecolor'] = 'red'
def short_label(self, lname):
if lname == "open-source-contribution":
return "osc"
if lname.startswith("waiting on "):
return lname[len("waiting on "):]
return lname
@classmethod
def from_json(cls, issues_data, org_fn=None):
for issue_data in issues_data:
issue = cls(issue_data, org_fn)
pr_url = issue.get('pull_request', {}).get('url')
if not pr_url:
continue
yield issue
def get_pulls(owner_repo, labels=None, state="open", since=None, org=False, pull_details=None):
"""
Get a bunch of pull requests (actually issues).
`pull_details` indicates how much information you want from the associated
pull request document. None means just issue information is enough. "list"
means the information available when listing pull requests is enough. "all"
means you need all the details. See the GitHub API docs for the difference:
https://developer.github.com/v3/pulls/
"""
url = URLObject("https://api.github.com/repos/{}/issues".format(owner_repo))
if labels:
url = url.set_query_param('labels', ",".join(labels))
if since:
url = url.set_query_param('since', since.isoformat())
if state:
url = url.set_query_param('state', state)
url = url.set_query_param('sort', 'updated')
org_fn = None
if org:
try:
with open("people.yaml") as fpeople:
people = yaml.load(fpeople)
def_org = "other"
except IOError:
people = {}
def_org = "---"
def org_fn(issue):
user_info = people.get(issue["user.login"])
if not user_info:
user_info = {"institution": "unsigned"}
return user_info.get("institution", def_org)
issues = JPullRequest.from_json(paginated_get(url), org_fn)
if org:
issues = sorted(issues, key=operator.itemgetter("org"))
pulls = None
if pull_details == "list":
issues = list(issues)
if issues:
# Request a bunch of pull details up front, for joining to. We can't
# ask for exactly the ones we need, so make a guess.
limit = int(len(issues) * 1.5)
pull_url = URLObject("https://api.github.com/repos/{}/pulls".format(owner_repo))
if state:
pull_url = pull_url.set_query_param('state', state)
pulls = { pr['number']: pr for pr in paginated_get(pull_url, limit=limit) }
for issue in issues:
if pull_details:
issue.load_pull_details(pulls=pulls)
issue['id'] = "{}.{}".format(owner_repo, issue['number'])
yield issue