|
8 | 8 | Functions:
|
9 | 9 | get_env_vars() -> EnvVars: Get the environment variables for use
|
10 | 10 | in the script.
|
11 |
| - search_issues(search_query: str, github_connection: github3.GitHub, owners_and_repositories: List[dict]) |
12 |
| - -> github3.structs.SearchIterator: |
13 |
| - Searches for issues in a GitHub repository that match the given search query. |
14 | 11 | get_per_issue_metrics(issues: Union[List[dict], List[github3.issues.Issue]],
|
15 | 12 | discussions: bool = False), labels: Union[List[str], None] = None,
|
16 | 13 | ignore_users: List[str] = [] -> tuple[List, int, int]:
|
|
21 | 18 | """
|
22 | 19 |
|
23 | 20 | import shutil
|
24 |
| -import sys |
25 |
| -from time import sleep |
26 | 21 | from typing import List, Union
|
27 | 22 |
|
28 | 23 | import github3
|
|
36 | 31 | from markdown_helpers import markdown_too_large_for_issue_body, split_markdown_file
|
37 | 32 | from markdown_writer import write_to_markdown
|
38 | 33 | from most_active_mentors import count_comments_per_user, get_mentor_count
|
| 34 | +from search import get_owners_and_repositories, search_issues |
39 | 35 | from time_to_answer import get_stats_time_to_answer, measure_time_to_answer
|
40 | 36 | from time_to_close import get_stats_time_to_close, measure_time_to_close
|
41 | 37 | from time_to_first_response import (
|
|
46 | 42 | from time_to_ready_for_review import get_time_to_ready_for_review
|
47 | 43 |
|
48 | 44 |
|
49 |
| -def search_issues( |
50 |
| - search_query: str, |
51 |
| - github_connection: github3.GitHub, |
52 |
| - owners_and_repositories: List[dict], |
53 |
| - rate_limit_bypass: bool = False, |
54 |
| -) -> List[github3.search.IssueSearchResult]: # type: ignore |
55 |
| - """ |
56 |
| - Searches for issues/prs/discussions in a GitHub repository that match |
57 |
| - the given search query and handles errors related to GitHub API responses. |
58 |
| -
|
59 |
| - Args: |
60 |
| - search_query (str): The search query to use for finding issues/prs/discussions. |
61 |
| - github_connection (github3.GitHub): A connection to the GitHub API. |
62 |
| - owners_and_repositories (List[dict]): A list of dictionaries containing |
63 |
| - the owner and repository names. |
64 |
| -
|
65 |
| - Returns: |
66 |
| - List[github3.search.IssueSearchResult]: A list of issues that match the search query. |
67 |
| - """ |
68 |
| - |
69 |
| - # Rate Limit Handling: API only allows 30 requests per minute |
70 |
| - def wait_for_api_refresh( |
71 |
| - iterator: github3.structs.SearchIterator, rate_limit_bypass: bool = False |
72 |
| - ): |
73 |
| - # If the rate limit bypass is enabled, don't wait for the API to refresh |
74 |
| - if rate_limit_bypass: |
75 |
| - return |
76 |
| - |
77 |
| - max_retries = 5 |
78 |
| - retry_count = 0 |
79 |
| - sleep_time = 70 |
80 |
| - |
81 |
| - while iterator.ratelimit_remaining < 5: |
82 |
| - if retry_count >= max_retries: |
83 |
| - raise RuntimeError("Exceeded maximum retries for API rate limit") |
84 |
| - |
85 |
| - print( |
86 |
| - f"GitHub API Rate Limit Low, waiting {sleep_time} seconds to refresh." |
87 |
| - ) |
88 |
| - sleep(sleep_time) |
89 |
| - |
90 |
| - # Exponentially increase the sleep time for the next retry |
91 |
| - sleep_time *= 2 |
92 |
| - retry_count += 1 |
93 |
| - |
94 |
| - issues_per_page = 100 |
95 |
| - |
96 |
| - print("Searching for issues...") |
97 |
| - issues_iterator = github_connection.search_issues( |
98 |
| - search_query, per_page=issues_per_page |
99 |
| - ) |
100 |
| - wait_for_api_refresh(issues_iterator, rate_limit_bypass) |
101 |
| - |
102 |
| - issues = [] |
103 |
| - repos_and_owners_string = "" |
104 |
| - for item in owners_and_repositories: |
105 |
| - repos_and_owners_string += ( |
106 |
| - f"{item.get('owner', '')}/{item.get('repository', '')} " |
107 |
| - ) |
108 |
| - |
109 |
| - # Print the issue titles |
110 |
| - try: |
111 |
| - for idx, issue in enumerate(issues_iterator, 1): |
112 |
| - print(issue.title) # type: ignore |
113 |
| - issues.append(issue) |
114 |
| - |
115 |
| - # requests are sent once per page of issues |
116 |
| - if idx % issues_per_page == 0: |
117 |
| - wait_for_api_refresh(issues_iterator, rate_limit_bypass) |
118 |
| - |
119 |
| - except github3.exceptions.ForbiddenError: |
120 |
| - print( |
121 |
| - f"You do not have permission to view a repository from: '{repos_and_owners_string}'; Check your API Token." |
122 |
| - ) |
123 |
| - sys.exit(1) |
124 |
| - except github3.exceptions.NotFoundError: |
125 |
| - print( |
126 |
| - f"The repository could not be found; Check the repository owner and names: '{repos_and_owners_string}" |
127 |
| - ) |
128 |
| - sys.exit(1) |
129 |
| - except github3.exceptions.ConnectionError: |
130 |
| - print( |
131 |
| - "There was a connection error; Check your internet connection or API Token." |
132 |
| - ) |
133 |
| - sys.exit(1) |
134 |
| - except github3.exceptions.AuthenticationFailed: |
135 |
| - print("Authentication failed; Check your API Token.") |
136 |
| - sys.exit(1) |
137 |
| - except github3.exceptions.UnprocessableEntity: |
138 |
| - print("The search query is invalid; Check the search query.") |
139 |
| - sys.exit(1) |
140 |
| - |
141 |
| - return issues |
142 |
| - |
143 |
| - |
144 | 45 | def get_per_issue_metrics(
|
145 | 46 | issues: Union[List[dict], List[github3.search.IssueSearchResult]], # type: ignore
|
146 | 47 | env_vars: EnvVars,
|
@@ -264,37 +165,6 @@ def get_per_issue_metrics(
|
264 | 165 | return issues_with_metrics, num_issues_open, num_issues_closed
|
265 | 166 |
|
266 | 167 |
|
267 |
| -def get_owners_and_repositories( |
268 |
| - search_query: str, |
269 |
| -) -> List[dict]: |
270 |
| - """Get the owners and repositories from the search query. |
271 |
| -
|
272 |
| - Args: |
273 |
| - search_query (str): The search query used to search for issues. |
274 |
| -
|
275 |
| - Returns: |
276 |
| - List[dict]: A list of dictionaries of owners and repositories. |
277 |
| -
|
278 |
| - """ |
279 |
| - search_query_split = search_query.split(" ") |
280 |
| - results_list = [] |
281 |
| - for item in search_query_split: |
282 |
| - result = {} |
283 |
| - if "repo:" in item and "/" in item: |
284 |
| - result["owner"] = item.split(":")[1].split("/")[0] |
285 |
| - result["repository"] = item.split(":")[1].split("/")[1] |
286 |
| - if "org:" in item or "owner:" in item or "user:" in item: |
287 |
| - result["owner"] = item.split(":")[1] |
288 |
| - if "user:" in item: |
289 |
| - result["owner"] = item.split(":")[1] |
290 |
| - if "owner:" in item: |
291 |
| - result["owner"] = item.split(":")[1] |
292 |
| - if result: |
293 |
| - results_list.append(result) |
294 |
| - |
295 |
| - return results_list |
296 |
| - |
297 |
| - |
298 | 168 | def main(): # pragma: no cover
|
299 | 169 | """Run the issue-metrics script.
|
300 | 170 |
|
|
0 commit comments