diff --git a/README.md b/README.md index a060adc..3438f67 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ The metrics that are measured are: | Time to answer | (Discussions only) The time between when a discussion is created and when it is answered. | | Time in label | The time between when a label has a specific label applied to an issue/pull request/discussion and when it is removed. This requires the LABELS_TO_MEASURE env variable to be set. | -*For pull requests, these metrics exclude the time the PR was in draft mode. +*For pull requests, these metrics exclude the time the PR was in draft mode. +*For Issue and pull requests, issue/pull request author's own comments and comments by bots are excluded. This action was developed by the GitHub OSPO for our own use and developed in a way that we could open source it that it might be useful to you as well! If you want to know more about how we use it, reach out in an issue in this repository. diff --git a/test_time_to_first_response.py b/test_time_to_first_response.py index af7177b..dc8c6b5 100644 --- a/test_time_to_first_response.py +++ b/test_time_to_first_response.py @@ -280,6 +280,36 @@ def test_measure_time_to_first_response_ignore_issue_owners_comment(self): # Check the results self.assertEqual(result, expected_result) + def test_measure_time_to_first_response_ignore_bot(self): + """Test that measure_time_to_first_response ignore bot's comment.""" + # Set up the mock GitHub issues + mock_issue1 = MagicMock() + mock_issue1.comments = 2 + mock_issue1.created_at = "2023-01-01T00:00:00Z" + + # Set up the mock GitHub issue comments + mock_comment1 = MagicMock() + mock_comment1.user.type = "Bot" + mock_comment1.created_at = datetime.fromisoformat("2023-01-02T00:00:00Z") + mock_issue1.issue.comments.return_value = [mock_comment1] + + # Set up the mock GitHub pull request comments + mock_pr_comment1 = MagicMock() + mock_pr_comment1.user.type = "Bot" + mock_pr_comment1.submitted_at = datetime.fromisoformat("2023-01-03T00:00:00Z") + mock_pr_comment2 = MagicMock() + mock_pr_comment2.user.type = "User" + mock_pr_comment2.submitted_at = datetime.fromisoformat("2023-01-04T00:00:00Z") # first response + mock_pull_request = MagicMock() + mock_pull_request.reviews.return_value = [mock_pr_comment1, mock_pr_comment2] + + # Call the function + result = measure_time_to_first_response(mock_issue1, None, mock_pull_request, None) + expected_result = timedelta(days=3) + + # Check the results + self.assertEqual(result, expected_result) + class TestGetAverageTimeToFirstResponse(unittest.TestCase): """Test the get_average_time_to_first_response function.""" diff --git a/time_to_first_response.py b/time_to_first_response.py index b59702d..ab84975 100644 --- a/time_to_first_response.py +++ b/time_to_first_response.py @@ -57,11 +57,7 @@ def measure_time_to_first_response( number=20, sort="created", direction="asc" ) # type: ignore for comment in comments: - if comment.user.login in ignore_users: - continue - if comment.user.login == issue.issue.user.login: - continue - if ready_for_review_at and comment.created_at < ready_for_review_at: + if ignore_comment(issue.issue.user, comment.user, ignore_users, comment.created_at, ready_for_review_at): continue first_comment_time = comment.created_at break @@ -71,11 +67,8 @@ def measure_time_to_first_response( if pull_request: review_comments = pull_request.reviews(number=50) # type: ignore for review_comment in review_comments: - if review_comment.user.login in ignore_users: - continue - if review_comment.user.login == issue.issue.user.login: - continue - if ready_for_review_at and review_comment.submitted_at < ready_for_review_at: + if ignore_comment(issue.issue.user, review_comment.user, ignore_users, + review_comment.submitted_at, ready_for_review_at): continue first_review_comment_time = review_comment.submitted_at break @@ -109,6 +102,25 @@ def measure_time_to_first_response( return None +def ignore_comment( + issue_user: github3.users.User, + comment_user: github3.users.User, + ignore_users: List[str], + comment_created_at: datetime, + ready_for_review_at: Union[datetime, None], +) -> bool: + """Check if a comment should be ignored.""" + return ( + # ignore comments by IGNORE_USERS + comment_user.login in ignore_users + # ignore comments by bots + or comment_user.type == "Bot" + # ignore comments by the issue creator + or comment_user.login == issue_user.login + # ignore comments created before the issue was ready for review + or (ready_for_review_at and comment_created_at < ready_for_review_at)) + + def get_average_time_to_first_response( issues: List[IssueWithMetrics], ) -> Union[timedelta, None]: