Repository navigation
Expand file tree
/
Copy pathdiscussions.py
More file actions
174 lines (149 loc) · 5.75 KB
/
Copy pathdiscussions.py
File metadata and controls
174 lines (149 loc) · 5.75 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
"""
This module provides functions for working with discussions in a GitHub repository.
Functions:
get_discussions(github_connection: Github, search_query: str, max_comments: int = 20) -> list:
Get a list of discussions in a GitHub repository that match the search query.
"""
from github import Github
COMMENTS_PAGE_SIZE = 100
# Fetches further pages of a single discussion's comments once the first page
# returned by the search query is exhausted.
COMMENTS_QUERY = """
query($id: ID!, $cursor: String, $pageSize: Int!) {
node(id: $id) {
... on Discussion {
comments(first: $pageSize, after: $cursor) {
nodes {
createdAt
author {
login
__typename
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
"""
def _fetch_remaining_comments(
github_connection: Github, discussion: dict, max_comments: int
):
"""Page a single discussion's comments until max_comments is satisfied.
The search query returns only the first page of each discussion's comments,
so without this the mentor counting branch silently stops at that page.
Args:
github_connection (Github): An authenticated PyGithub connection.
discussion (dict): A discussion node, updated in place.
max_comments (int): Maximum number of comments to collect.
"""
comments = discussion.get("comments")
if not comments:
return
nodes = comments.get("nodes", [])
page_info = comments.get("pageInfo") or {}
while len(nodes) < max_comments and page_info.get("hasNextPage"):
variables = {
"id": discussion["id"],
"cursor": page_info.get("endCursor"),
"pageSize": min(COMMENTS_PAGE_SIZE, max_comments - len(nodes)),
}
_, response_json = github_connection.requester.graphql_query(
COMMENTS_QUERY, variables
)
# The discussion may have been deleted or hidden between the search
# response and this follow-up query, leaving data.node null. Degrade
# gracefully to the comments already collected instead of aborting
# the whole run.
node = (response_json.get("data") or {}).get("node") or {}
page = node.get("comments")
if not page:
break
page_nodes = page.get("nodes") or []
if not page_nodes:
break
nodes.extend(page_nodes)
page_info = page.get("pageInfo") or {}
comments["pageInfo"] = page_info
def get_discussions(
github_connection: Github, search_query: str, max_comments: int = 20
):
"""Get a list of discussions in a GitHub repository that match the search query.
Args:
github_connection (Github): An authenticated PyGithub connection.
GitHub Enterprise routing is handled by the connection's base URL.
search_query (str): The search query to filter discussions by.
max_comments (int): Maximum number of comments to collect per discussion.
Values above the GraphQL page size trigger extra requests, so that
discussions match the issue and pull request branches, which keep
paginating until the limit is satisfied.
Returns:
list: A list of discussions in the repository that match the search query.
"""
# Construct the GraphQL query with pagination
query = """
query($query: String!, $cursor: String) {
search(query: $query, type: DISCUSSION, first: 100, after: $cursor) {
edges {
node {
... on Discussion {
id
title
url
createdAt
author {
login
__typename
}
comments(first: 100) {
nodes {
createdAt
author {
login
__typename
}
}
pageInfo {
hasNextPage
endCursor
}
}
answerChosenAt
closedAt
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
"""
# Remove the type:discussions filter from the search query
search_query = search_query.replace("type:discussions ", "")
# PyGithub's graphql_query reuses the connection's auth, base URL, and
# GitHub Enterprise /api/graphql handling, and raises GithubException on
# HTTP or GraphQL errors.
discussions = []
cursor = None
while True:
# Set the variables for the GraphQL query
variables = {"query": search_query, "cursor": cursor}
# Send the GraphQL request
_, response_json = github_connection.requester.graphql_query(query, variables)
data = response_json["data"]
# Extract the discussions from the current page
for edge in data["search"]["edges"]:
discussion = edge["node"]
_fetch_remaining_comments(github_connection, discussion, max_comments)
discussions.append(discussion)
# Check if there are more pages
page_info = data["search"]["pageInfo"]
if not page_info["hasNextPage"]:
break
cursor = page_info["endCursor"]
return discussions