From f91d7f08a59927e056c758291a56230a12de2cc7 Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 00:56:01 +0530 Subject: [PATCH 01/11] create new discussions --- .../PENDING_REVIEW_NOTIFICATION_TEMPLATE.md | 6 +- src/github_services.py | 237 ++++++++---------- src/main.py | 49 +--- 3 files changed, 108 insertions(+), 184 deletions(-) diff --git a/.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md b/.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md index 69060e6..897fe6a 100644 --- a/.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md +++ b/.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md @@ -1,8 +1,4 @@ -Hi {{ username }}, - -It looks like you're assigned to this PR, but haven't taken any action for at least 2 days: +The following PRs are blocked on your review: {{ pr_list }} -Please review and unassign yourself from the pending PRs as soon as possible. - To avoid these messages in the future, please bookmark [this link](https://github.com/pulls/assigned) and check it daily. Thanks! \ No newline at end of file diff --git a/src/github_services.py b/src/github_services.py index 59f2c27..19b2ea3 100644 --- a/src/github_services.py +++ b/src/github_services.py @@ -203,30 +203,17 @@ def get_pull_request_dict_with_timestamp( assignee['created_at'] = parser.parse(event['created_at']) return pr_dict - @check_token -def _get_discussion_data( +def _get_repository_id( org_name: str, repo_name: str, - discussion_category: str, - discussion_title: str, -) -> Tuple[str, int]: - """Fetch discussion data from api and return corresponding discussion id and - discussion number. - """ +) -> str: + """Fetch repository id from given org and repo and return the id.""" - # The following query is written in GraphQL and is being used to fetch the category - # ids and titles from the GitHub discussions. To learn more, check this out - # https://docs.github.com/en/graphql. query = """ query ($org_name: String!, $repository: String!) { repository(owner: $org_name, name: $repository) { - discussionCategories(first: 10) { - nodes { - id - name - } - } + id } } """ @@ -244,32 +231,33 @@ def _get_discussion_data( ) data = response.json() - category_id = None - discussion_categories = ( - data['data']['repository']['discussionCategories']['nodes']) - - for category in discussion_categories: - if category['name'] == discussion_category: - category_id = category['id'] - break + repository_id = ( + data['data']['repository']['id']) - if category_id is None: + if repository_id is None: raise builtins.BaseException( - f'{discussion_category} category is missing in GitHub Discussion.') + f'{org_name}/{repo_name} doesn\'t exist.') - # The following query is written in GraphQL and is being used to fetch discussions - # from a particular GitHub discussion category. This helps to find out the discussion - # where we want to comment. To learn more, check this out - # https://docs.github.com/en/graphql. + return repository_id +@check_token +def _get_category_id( + org_name: str, + repo_name: str, + discussion_category: str +) -> str: + """Fetch discussion category id from given category name and return the id.""" + + # The following query is written in GraphQL and is being used to fetch the category + # ids and titles from the GitHub discussions. To learn more, check this out + # https://docs.github.com/en/graphql. query = """ - query ($org_name: String!, $repository: String!, $category_id: ID!) { + query ($org_name: String!, $repository: String!) { repository(owner: $org_name, name: $repository) { - discussions(categoryId: $category_id, last:10) { + discussionCategories(first: 10) { nodes { id - title - number + name } } } @@ -278,8 +266,7 @@ def _get_discussion_data( variables = { 'org_name': org_name, - 'repository': repo_name, - 'category_id': category_id + 'repository': repo_name } response = requests.post( @@ -289,52 +276,49 @@ def _get_discussion_data( timeout=TIMEOUT_SECS ) data = response.json() - discussion_id = None - discussions = data['data']['repository']['discussions']['nodes'] + category_id = None + discussion_categories = ( + data['data']['repository']['discussionCategories']['nodes']) - for discussion in discussions: - if discussion['title'] == discussion_title: - discussion_id = discussion['id'] - discussion_number = discussion['number'] + for category in discussion_categories: + if category['name'] == discussion_category: + category_id = category['id'] break - if discussion_id is None: + if category_id is None: raise builtins.BaseException( - f'Discussion with title {discussion_title} not found, please create a ' - 'discussion with that title.') - - return discussion_id, discussion_number + f'{discussion_category} category is missing in GitHub Discussion.') + return category_id -def _get_past_time(days: int=60) -> str: - """Returns the subtraction of current time and the arg passed in days.""" - return ( - datetime.datetime.now( - datetime.timezone.utc) - datetime.timedelta(days=days)).strftime( - '%Y-%m-%dT%H:%M:%SZ') -def _get_old_comment_ids( +@check_token +def _get_discussion_ids( org_name: str, repo_name: str, - discussion_number: int + discussion_category: str, ) -> List[str]: - """Return the old comment ids.""" + """Fetch discussion data from api and return corresponding discussion id and + discussion number. + """ + + category_id = _get_category_id(org_name, repo_name, discussion_category) + + # The following query is written in GraphQL and is being used to fetch discussions + # from a particular GitHub discussion category. This helps to find out the discussion + # where we want to comment. To learn more, check this out + # https://docs.github.com/en/graphql. - # The following query is written in GraphQL and is being used to fetch the oldest 50 - # comments in a existing GitHub discussions. Assuming that, this workflow will run - # twice every week, we will may not have more than 50 comments to delete. To learn - # more, check this out https://docs.github.com/en/graphql. query = """ - query ($org_name: String!, $repository: String!, $discussion_number: Int!) { + query ($org_name: String!, $repository: String!, $category_id: ID!) { repository(owner: $org_name, name: $repository) { - discussion(number: $discussion_number) { - comments(first: 50) { - nodes { - id - createdAt - } + discussions(categoryId: $category_id, last:10) { + nodes { + id + title + number } } } @@ -344,7 +328,7 @@ def _get_old_comment_ids( variables = { 'org_name': org_name, 'repository': repo_name, - 'discussion_number': discussion_number + 'category_id': category_id } response = requests.post( @@ -353,44 +337,35 @@ def _get_old_comment_ids( headers=_get_request_headers(), timeout=TIMEOUT_SECS ) - - response.raise_for_status() data = response.json() - comment_ids: List[str] = [] - - discussion_comments = ( - data['data']['repository']['discussion']['comments']['nodes'] - ) - - # Delete comments posted before this time. - delete_comments_before_in_days = _get_past_time(DELETE_COMMENTS_BEFORE_IN_DAYS) + discussions = data['data']['repository']['discussions']['nodes'] + discussion_ids = [ + discussion['id'] for discussion in discussions if discussion['id'] is not None + ] - for comment in discussion_comments: - if comment['createdAt'] < delete_comments_before_in_days: - comment_ids.append(comment['id']) - else: - break - return comment_ids + if not discussion_ids: + logging.info('No existing discussions found') + return discussion_ids -def _delete_comment(comment_id: str) -> None: +def _delete_discussion(discussion_id: str) -> None: """Delete the GitHub Discussion comment related to the comment id.""" query = """ - mutation deleteComment($comment_id: ID!) { - deleteDiscussionComment(input: {id: $comment_id}) { - clientMutationId - comment { - bodyText + mutation deleteComment($id: ID!) { + deleteDiscussion(input: {id: $discussion_id}) { + clientMutationId, + discussion { + title } } } """ variables = { - 'comment_id': comment_id + 'discussion_id': discussion_id } response = requests.post( @@ -401,19 +376,36 @@ def _delete_comment(comment_id: str) -> None: ) response.raise_for_status() +@check_token +def delete_discussions( + org_name: str, + repo_name: str, + discussion_category: str, +) -> None: + """Delete all existing discussions""" -def _post_comment(discussion_id: str, message: str) -> None: - """Post the given message in an existing discussion.""" + discussion_ids = _get_discussion_ids( + org_name, repo_name, discussion_category) - # The following code is written in GraphQL and is being used to perform a mutation - # operation. More specifically, we are using it to comment in GitHub discussion to - # let reviewers know about some of their pending tasks. To learn more, check this out: - # https://docs.github.com/en/graphql. + for discussion_id in discussion_ids: + _delete_discussion(discussion_id) + +@check_token +def create_discussion( + org_name: str, + repo_name: str, + discussion_category: str, + discussion_title: str, + discussion_body: str +): + """Create new discussion with given title and body.""" + + category_id = _get_category_id(org_name, repo_name, discussion_category) + repo_id = _get_repository_id(org_name, repo_name) query = """ - mutation post_comment($discussion_id: ID!, $comment: String!) { - addDiscussionComment(input: {discussionId: $discussion_id, body: $comment}) { - clientMutationId - comment { + mutation createDiscussion($repo_id: ID!, $category_id: ID!, $title: String!, $body: String!) { + createDiscussion(input: {repositoryId: $repo_id, categoryId: $category_id, title: $title, body: $body}) { + discussion { id } } @@ -421,8 +413,10 @@ def _post_comment(discussion_id: str, message: str) -> None: """ variables = { - 'discussion_id': discussion_id, - 'comment': message + 'repo_id': repo_id, + 'category_id': category_id, + 'title': discussion_title, + 'body': discussion_body } response = requests.post( @@ -431,38 +425,5 @@ def _post_comment(discussion_id: str, message: str) -> None: headers=_get_request_headers(), timeout=TIMEOUT_SECS ) - response.raise_for_status() - - -@check_token -def delete_discussion_comments( - org_name: str, - repo_name: str, - discussion_category: str, - discussion_title: str -) -> None: - """Delete old comments from GitHub Discussion.""" - - _, discussion_number = _get_discussion_data( - org_name, repo_name, discussion_category, discussion_title) - - comment_ids = _get_old_comment_ids(org_name, repo_name, discussion_number) - - for comment_id in comment_ids: - _delete_comment(comment_id) - - -@check_token -def add_discussion_comments( - org_name: str, - repo_name: str, - discussion_category: str, - discussion_title: str, - message: str -) -> None: - """Add comments in an existing GitHub discussion.""" - - discussion_id, _ = _get_discussion_data( - org_name, repo_name, discussion_category, discussion_title) - _post_comment(discussion_id, message) + response.raise_for_status() diff --git a/src/main.py b/src/main.py index e1eada3..77e3a74 100644 --- a/src/main.py +++ b/src/main.py @@ -58,12 +58,11 @@ TEMPLATE_PATH = '.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md' -def generate_message(username: str, pr_list: str, template_path: str=TEMPLATE_PATH) -> str: +def generate_message(pr_list: str, template_path: str=TEMPLATE_PATH) -> str: """Generates message using the template provided in PENDING_REVIEW_NOTIFICATION_TEMPLATE.md. Args: - username: str. Reviewer username. pr_list: str. List of PRs not reviewed within the maximum waiting time. template_path: str. The template file path. @@ -79,44 +78,10 @@ def generate_message(username: str, pr_list: str, template_path: str=TEMPLATE_PA with open(template_path, 'r', encoding='UTF-8') as file: message = file.read() - message = re.sub(r'\{\{ *username *\}\}', '@' + username, message) message = re.sub(r'\{\{ *pr_list *\}\}', pr_list, message) return message - -def send_notification( - username: str, - pull_requests: List[github_domain.PullRequest], - org_name: str, - repo_name: str, - discussion_category: str, - discussion_title: str -) -> None: - """Sends notification on github-discussion. - - Args: - username: str. GitHub username of the reviewer. - pull_requests: List. List of pending PRs. - org_name: str. The GitHub org name. - repo_name: str. The GitHub repo name. - discussion_category: str. Category name of the discussion. - discussion_title: str. Discussion title. - """ - pr_list_messages: List[str] = [] - for pull_request in pull_requests: - assignee = pull_request.get_assignee(username) - assert assignee is not None - pr_list_messages.append( - f'- [#{pull_request.pr_number}]({pull_request.url}) [Waiting for the ' - f'last {assignee.get_waiting_time()}]') - - message = generate_message(username, '\n'.join(pr_list_messages), TEMPLATE_PATH) - - github_services.add_discussion_comments( - org_name, repo_name, discussion_category, discussion_title, message) - - def main(args: Optional[List[str]]=None) -> None: """The main function to execute the workflow. @@ -148,12 +113,14 @@ def main(args: Optional[List[str]]=None) -> None: reviewer_to_assigned_prs = github_services.get_prs_assigned_to_reviewers( org_name, repo_name, max_wait_hours) - github_services.delete_discussion_comments( - org_name, repo_name, discussion_category, discussion_title) + github_services.delete_discussions( + org_name, repo_name, discussion_category) - for reviewer_name, prs in reviewer_to_assigned_prs.items(): - send_notification( - reviewer_name, prs, org_name, repo_name, discussion_category, discussion_title) + for reviewer_name, pr_list in reviewer_to_assigned_prs.items(): + discussion_title = f"Pending Reviews: @{reviewer_name}" + discussion_body = generate_message(pr_list) + github_services.create_discussion( + org_name, repo_name, discussion_category, discussion_title, discussion_body) if __name__ == '__main__': From 450f1b1e2148e202f3cbeb1a847f4154a3151d13 Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 00:59:34 +0530 Subject: [PATCH 02/11] revert after testing is done --- src/main.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index 77e3a74..2453eb4 100644 --- a/src/main.py +++ b/src/main.py @@ -93,7 +93,9 @@ def main(args: Optional[List[str]]=None) -> None: """ parsed_args = PARSER.parse_args(args=args) - org_name, repo_name = parsed_args.repo.split('/') + # org_name, repo_name = parsed_args.repo.split('/') + org_name = "oppia" + repo_name = "oppia" discussion_category = parsed_args.category discussion_title = parsed_args.title max_wait_hours = parsed_args.max_wait_hours @@ -110,14 +112,18 @@ def main(args: Optional[List[str]]=None) -> None: github_services.init_service(parsed_args.token) + reviewer_to_assigned_prs = github_services.get_prs_assigned_to_reviewers( org_name, repo_name, max_wait_hours) + org_name ="SD-13" + repo_name = "test-pending-review-notifier" github_services.delete_discussions( org_name, repo_name, discussion_category) for reviewer_name, pr_list in reviewer_to_assigned_prs.items(): - discussion_title = f"Pending Reviews: @{reviewer_name}" + # discussion_title = f"Pending Reviews: @{reviewer_name}" + discussion_title = "test-123" discussion_body = generate_message(pr_list) github_services.create_discussion( org_name, repo_name, discussion_category, discussion_title, discussion_body) From 69d0b8de381d86e377ed74e8ac5548cb24ba09c9 Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 01:24:15 +0530 Subject: [PATCH 03/11] pass PRs as string to the template --- src/main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index 2453eb4..fd5e657 100644 --- a/src/main.py +++ b/src/main.py @@ -97,7 +97,6 @@ def main(args: Optional[List[str]]=None) -> None: org_name = "oppia" repo_name = "oppia" discussion_category = parsed_args.category - discussion_title = parsed_args.title max_wait_hours = parsed_args.max_wait_hours # Raise error if any of the required arguments are not provided. @@ -124,7 +123,7 @@ def main(args: Optional[List[str]]=None) -> None: for reviewer_name, pr_list in reviewer_to_assigned_prs.items(): # discussion_title = f"Pending Reviews: @{reviewer_name}" discussion_title = "test-123" - discussion_body = generate_message(pr_list) + discussion_body = generate_message('\n'.join(pr_list), TEMPLATE_PATH) github_services.create_discussion( org_name, repo_name, discussion_category, discussion_title, discussion_body) From a2ebd9a9cac357e21a5ea01316ee100481918923 Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 01:31:14 +0530 Subject: [PATCH 04/11] trying to fix: TypeError: sequence item 0: expected str instance, PullRequest found --- src/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index fd5e657..5c7c922 100644 --- a/src/main.py +++ b/src/main.py @@ -123,7 +123,8 @@ def main(args: Optional[List[str]]=None) -> None: for reviewer_name, pr_list in reviewer_to_assigned_prs.items(): # discussion_title = f"Pending Reviews: @{reviewer_name}" discussion_title = "test-123" - discussion_body = generate_message('\n'.join(pr_list), TEMPLATE_PATH) + PRs = '\n'.join(pr_list) + discussion_body = generate_message(PRs, TEMPLATE_PATH) github_services.create_discussion( org_name, repo_name, discussion_category, discussion_title, discussion_body) From 37a42e9301130ace0ffa9741276cc0e0069e31cd Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 01:36:04 +0530 Subject: [PATCH 05/11] converting PRs to str --- src/main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index 5c7c922..b944f96 100644 --- a/src/main.py +++ b/src/main.py @@ -123,8 +123,7 @@ def main(args: Optional[List[str]]=None) -> None: for reviewer_name, pr_list in reviewer_to_assigned_prs.items(): # discussion_title = f"Pending Reviews: @{reviewer_name}" discussion_title = "test-123" - PRs = '\n'.join(pr_list) - discussion_body = generate_message(PRs, TEMPLATE_PATH) + discussion_body = generate_message('\n'.join(str(pr) for pr in pr_list), TEMPLATE_PATH) github_services.create_discussion( org_name, repo_name, discussion_category, discussion_title, discussion_body) From b7fe720909b8b81c99332e7cb0ce0a83b813b566 Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 01:43:38 +0530 Subject: [PATCH 06/11] debug deletion --- src/github_services.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/github_services.py b/src/github_services.py index 19b2ea3..a4dbf56 100644 --- a/src/github_services.py +++ b/src/github_services.py @@ -358,7 +358,7 @@ def _delete_discussion(discussion_id: str) -> None: deleteDiscussion(input: {id: $discussion_id}) { clientMutationId, discussion { - title + title } } } @@ -375,6 +375,8 @@ def _delete_discussion(discussion_id: str) -> None: timeout=TIMEOUT_SECS ) response.raise_for_status() + print() + print(response.json) @check_token def delete_discussions( @@ -387,6 +389,7 @@ def delete_discussions( discussion_ids = _get_discussion_ids( org_name, repo_name, discussion_category) + print(discussion_ids) for discussion_id in discussion_ids: _delete_discussion(discussion_id) From 0bc1a639c3bde05c24cc1a8760ed5844e2519491 Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 01:51:02 +0530 Subject: [PATCH 07/11] correct variable name in deletion query --- src/github_services.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/github_services.py b/src/github_services.py index a4dbf56..b3ef818 100644 --- a/src/github_services.py +++ b/src/github_services.py @@ -354,7 +354,7 @@ def _delete_discussion(discussion_id: str) -> None: """Delete the GitHub Discussion comment related to the comment id.""" query = """ - mutation deleteComment($id: ID!) { + mutation deleteDiscussion($discussion_id: ID!) { deleteDiscussion(input: {id: $discussion_id}) { clientMutationId, discussion { @@ -375,8 +375,7 @@ def _delete_discussion(discussion_id: str) -> None: timeout=TIMEOUT_SECS ) response.raise_for_status() - print() - print(response.json) + @check_token def delete_discussions( @@ -389,7 +388,6 @@ def delete_discussions( discussion_ids = _get_discussion_ids( org_name, repo_name, discussion_category) - print(discussion_ids) for discussion_id in discussion_ids: _delete_discussion(discussion_id) From 11a4cba33162d78433adb196c60744393d4323fa Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 13:39:32 +0530 Subject: [PATCH 08/11] test title --- src/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index b944f96..4b51b42 100644 --- a/src/main.py +++ b/src/main.py @@ -121,8 +121,8 @@ def main(args: Optional[List[str]]=None) -> None: org_name, repo_name, discussion_category) for reviewer_name, pr_list in reviewer_to_assigned_prs.items(): - # discussion_title = f"Pending Reviews: @{reviewer_name}" - discussion_title = "test-123" + discussion_title = f"Pending Reviews: {reviewer_name}" + # discussion_title = "test-123" discussion_body = generate_message('\n'.join(str(pr) for pr in pr_list), TEMPLATE_PATH) github_services.create_discussion( org_name, repo_name, discussion_category, discussion_title, discussion_body) From 593f427c57d9a6373d66e305c73925144d745cf8 Mon Sep 17 00:00:00 2001 From: Sujay Date: Wed, 20 Nov 2024 13:45:43 +0530 Subject: [PATCH 09/11] revert testing tweaks --- src/main.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main.py b/src/main.py index 4b51b42..b6efddd 100644 --- a/src/main.py +++ b/src/main.py @@ -93,9 +93,7 @@ def main(args: Optional[List[str]]=None) -> None: """ parsed_args = PARSER.parse_args(args=args) - # org_name, repo_name = parsed_args.repo.split('/') - org_name = "oppia" - repo_name = "oppia" + org_name, repo_name = parsed_args.repo.split('/') discussion_category = parsed_args.category max_wait_hours = parsed_args.max_wait_hours @@ -111,18 +109,14 @@ def main(args: Optional[List[str]]=None) -> None: github_services.init_service(parsed_args.token) - reviewer_to_assigned_prs = github_services.get_prs_assigned_to_reviewers( org_name, repo_name, max_wait_hours) - org_name ="SD-13" - repo_name = "test-pending-review-notifier" github_services.delete_discussions( org_name, repo_name, discussion_category) for reviewer_name, pr_list in reviewer_to_assigned_prs.items(): - discussion_title = f"Pending Reviews: {reviewer_name}" - # discussion_title = "test-123" + discussion_title = f"Pending Reviews: @{reviewer_name}" discussion_body = generate_message('\n'.join(str(pr) for pr in pr_list), TEMPLATE_PATH) github_services.create_discussion( org_name, repo_name, discussion_category, discussion_title, discussion_body) From c2d2ecc343d7615b432ad55d78f08e1213ca6815 Mon Sep 17 00:00:00 2001 From: Sujay Date: Thu, 21 Nov 2024 23:51:25 +0530 Subject: [PATCH 10/11] requested changes and mypy fixes --- .../PENDING_REVIEW_NOTIFICATION_TEMPLATE.md | 6 ++++- src/github_services.py | 11 +++++---- src/main.py | 23 ++++++++++++++----- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md b/.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md index 897fe6a..27c656a 100644 --- a/.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md +++ b/.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md @@ -1,4 +1,8 @@ -The following PRs are blocked on your review: +Hi {{ username }}, + +The following PRs are currently blocked on your review: {{ pr_list }} +Please review and unassign yourself from the pending PRs as soon as possible, then mark this discussion thread as 'Done'. + To avoid these messages in the future, please bookmark [this link](https://github.com/pulls/assigned) and check it daily. Thanks! \ No newline at end of file diff --git a/src/github_services.py b/src/github_services.py index b3ef818..a09138a 100644 --- a/src/github_services.py +++ b/src/github_services.py @@ -231,7 +231,7 @@ def _get_repository_id( ) data = response.json() - repository_id = ( + repository_id: str = ( data['data']['repository']['id']) if repository_id is None: @@ -277,7 +277,7 @@ def _get_category_id( ) data = response.json() - category_id = None + category_id: Optional[str] = None discussion_categories = ( data['data']['repository']['discussionCategories']['nodes']) @@ -290,6 +290,7 @@ def _get_category_id( raise builtins.BaseException( f'{discussion_category} category is missing in GitHub Discussion.') + assert category_id is not None return category_id @@ -383,7 +384,7 @@ def delete_discussions( repo_name: str, discussion_category: str, ) -> None: - """Delete all existing discussions""" + """Delete all existing discussions in the given discussion category.""" discussion_ids = _get_discussion_ids( org_name, repo_name, discussion_category) @@ -398,8 +399,8 @@ def create_discussion( discussion_category: str, discussion_title: str, discussion_body: str -): - """Create new discussion with given title and body.""" +) -> None: + """Create a new discussion with the given title and body in the given discussion category.""" category_id = _get_category_id(org_name, repo_name, discussion_category) repo_id = _get_repository_id(org_name, repo_name) diff --git a/src/main.py b/src/main.py index b6efddd..3b14796 100644 --- a/src/main.py +++ b/src/main.py @@ -22,7 +22,7 @@ import os import re -from typing import List, Optional +from typing import DefaultDict, List, Optional from src import github_domain from src import github_services @@ -58,12 +58,13 @@ TEMPLATE_PATH = '.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md' -def generate_message(pr_list: str, template_path: str=TEMPLATE_PATH) -> str: +def generate_message(username: str, pull_requests: List[github_domain.PullRequest], template_path: str=TEMPLATE_PATH) -> str: """Generates message using the template provided in PENDING_REVIEW_NOTIFICATION_TEMPLATE.md. Args: - pr_list: str. List of PRs not reviewed within the maximum waiting time. + username: str. Reviewer username. + pr_list: List[github_domain.PullRequest]. List of PullRequest not reviewed within the maximum waiting time. template_path: str. The template file path. Returns: @@ -72,13 +73,23 @@ def generate_message(pr_list: str, template_path: str=TEMPLATE_PATH) -> str: Raises: Exception. Template file is missing in the given path. """ + pr_list_messages: List[str] = [] + for pull_request in pull_requests: + assignee = pull_request.get_assignee(username) + assert assignee is not None + pr_list_messages.append( + f'- [#{pull_request.pr_number}]({pull_request.url}) [Waiting for the ' + f'last {assignee.get_waiting_time()}]') + + if not os.path.exists(template_path): raise builtins.BaseException(f'Please add a template file at: {template_path}') message = '' with open(template_path, 'r', encoding='UTF-8') as file: message = file.read() - message = re.sub(r'\{\{ *pr_list *\}\}', pr_list, message) + message = re.sub(r'\{\{ *username *\}\}', '@' + username, message) + message = re.sub(r'\{\{ *pr_list *\}\}', '\n'.join(pr_list_messages), message) return message @@ -109,7 +120,7 @@ def main(args: Optional[List[str]]=None) -> None: github_services.init_service(parsed_args.token) - reviewer_to_assigned_prs = github_services.get_prs_assigned_to_reviewers( + reviewer_to_assigned_prs: DefaultDict[str, List[github_domain.PullRequest]] = github_services.get_prs_assigned_to_reviewers( org_name, repo_name, max_wait_hours) github_services.delete_discussions( @@ -117,7 +128,7 @@ def main(args: Optional[List[str]]=None) -> None: for reviewer_name, pr_list in reviewer_to_assigned_prs.items(): discussion_title = f"Pending Reviews: @{reviewer_name}" - discussion_body = generate_message('\n'.join(str(pr) for pr in pr_list), TEMPLATE_PATH) + discussion_body = generate_message(reviewer_name, pr_list, TEMPLATE_PATH) github_services.create_discussion( org_name, repo_name, discussion_category, discussion_title, discussion_body) From 6c794b4efb297a5bf0216425a11f97a0e7aef9d7 Mon Sep 17 00:00:00 2001 From: Sujay Date: Fri, 22 Nov 2024 00:07:11 +0530 Subject: [PATCH 11/11] fix linter --- src/github_services.py | 2 +- src/main.py | 14 ++++++++++---- tests/github_services_test.py | 9 +++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/github_services.py b/src/github_services.py index a09138a..ed5219c 100644 --- a/src/github_services.py +++ b/src/github_services.py @@ -21,7 +21,7 @@ import datetime import logging -from typing import Any, Callable, DefaultDict, Dict, List, Optional, Tuple, Union +from typing import Any, Callable, DefaultDict, Dict, List, Optional, Union from dateutil import parser import requests from src import github_domain diff --git a/src/main.py b/src/main.py index 3b14796..91f6362 100644 --- a/src/main.py +++ b/src/main.py @@ -58,13 +58,18 @@ TEMPLATE_PATH = '.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md' -def generate_message(username: str, pull_requests: List[github_domain.PullRequest], template_path: str=TEMPLATE_PATH) -> str: +def generate_message( + username: str, + pull_requests: List[github_domain.PullRequest], + template_path: str=TEMPLATE_PATH +) -> str: """Generates message using the template provided in PENDING_REVIEW_NOTIFICATION_TEMPLATE.md. Args: username: str. Reviewer username. - pr_list: List[github_domain.PullRequest]. List of PullRequest not reviewed within the maximum waiting time. + pr_list: List[github_domain.PullRequest]. List of PullRequest objects not reviewed within + the maximum waiting time. template_path: str. The template file path. Returns: @@ -120,8 +125,9 @@ def main(args: Optional[List[str]]=None) -> None: github_services.init_service(parsed_args.token) - reviewer_to_assigned_prs: DefaultDict[str, List[github_domain.PullRequest]] = github_services.get_prs_assigned_to_reviewers( - org_name, repo_name, max_wait_hours) + reviewer_to_assigned_prs: DefaultDict[str, List[github_domain.PullRequest]] = ( + github_services.get_prs_assigned_to_reviewers(org_name, repo_name, max_wait_hours) + ) github_services.delete_discussions( org_name, repo_name, discussion_category) diff --git a/tests/github_services_test.py b/tests/github_services_test.py index ec7a26d..2e596eb 100644 --- a/tests/github_services_test.py +++ b/tests/github_services_test.py @@ -291,6 +291,7 @@ def test_get_prs_assigned_to_reviewers(self) -> None: self.assertEqual(mock_request.call_count, 6) + def test_get_discussion_data(self) -> None: """Test _get_discussion_data.""" @@ -464,3 +465,11 @@ def test_add_discussion_comments(self) -> None: self.assertTrue(mock_response_2.assert_called) self.assertTrue(mock_response_3.assert_called) self.assertEqual(mock_post.call_count, 3) + + +_get_repository_id +_get_category_id +_get_discussion_ids +_delete_discussion +delete_discussions +create_discussion