diff --git a/Makefile b/Makefile index 3158f29..c1a51df 100644 --- a/Makefile +++ b/Makefile @@ -11,5 +11,5 @@ lint: # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + flake8 . --count --exit-zero --max-complexity=15 --max-line-length=127 --statistics pylint --rcfile=.pylintrc --fail-under=9.0 *.py \ No newline at end of file diff --git a/issue_metrics.py b/issue_metrics.py index 370b777..0b4145e 100644 --- a/issue_metrics.py +++ b/issue_metrics.py @@ -13,7 +13,8 @@ Searches for issues in a GitHub repository that match the given search query. auth_to_github() -> github3.GitHub: Connect to GitHub API with token authentication. get_per_issue_metrics(issues: Union[List[dict], List[github3.issues.Issue]], - discussions: bool = False), labels: Union[List[str], None] = None, ignore_users: List[str] = [] -> tuple[List, int, int]: + discussions: bool = False), labels: Union[List[str], None] = None, + ignore_users: List[str] = [] -> tuple[List, int, int]: Calculate the metrics for each issue in a list of GitHub issues. get_owner(search_query: str) -> Union[str, None]]: Get the owner from the search query. @@ -123,10 +124,12 @@ def auth_to_github() -> github3.GitHub: if token := os.getenv("GH_TOKEN"): if not os.getenv("GITHUB_SERVER_URL"): github_connection = github3.login(token=token) - elif os.getenv("GITHUB_SERVER_URL") == 'https://github.com': + elif os.getenv("GITHUB_SERVER_URL") == "https://github.com": github_connection = github3.login(token=token) else: - github_connection = github3.GitHubEnterprise(os.getenv("GITHUB_SERVER_URL"),token=token) + github_connection = github3.GitHubEnterprise( + os.getenv("GITHUB_SERVER_URL"), token=token + ) else: raise ValueError("GH_TOKEN environment variable not set") @@ -137,7 +140,7 @@ def get_per_issue_metrics( issues: Union[List[dict], List[github3.search.IssueSearchResult]], # type: ignore discussions: bool = False, labels: Union[List[str], None] = None, - ignore_users: List[str] = [], + ignore_users: List[str] = None, ) -> tuple[List, int, int]: """ Calculate the metrics for each issue/pr/discussion in a list provided. @@ -159,6 +162,8 @@ def get_per_issue_metrics( issues_with_metrics = [] num_issues_open = 0 num_issues_closed = 0 + if ignore_users is None: + ignore_users = [] for issue in issues: if discussions: @@ -320,6 +325,7 @@ def main(): average_time_in_labels, num_issues_open, num_issues_closed, + search_query, ) write_to_markdown( issues_with_metrics, @@ -330,6 +336,7 @@ def main(): num_issues_open, num_issues_closed, labels, + search_query, ) diff --git a/json_writer.py b/json_writer.py index b540c9f..81dafeb 100644 --- a/json_writer.py +++ b/json_writer.py @@ -8,6 +8,7 @@ average_time_to_answer: timedelta, num_issues_opened: int, num_issues_closed: int, + search_query: str, ) -> str: Write the issues with metrics to a json file. @@ -30,6 +31,7 @@ def write_to_json( average_time_in_labels: Union[dict, None], num_issues_opened: Union[int, None], num_issues_closed: Union[int, None], + search_query: str, ) -> str: """ Write the issues with metrics to a JSON file called issue_metrics.json. @@ -63,6 +65,7 @@ def write_to_json( } }, ], + "search_query": "is:issue is:open repo:owner/repo" } """ @@ -107,6 +110,9 @@ def write_to_json( # Add the issues to the metrics dictionary metrics["issues"] = issues + # Add the search query to the metrics dictionary + metrics["search_query"] = search_query + # add output to github action output # pylint: disable=unspecified-encoding metrics_json = json.dumps(metrics) diff --git a/labels.py b/labels.py index ad6fa7c..6af1d5b 100644 --- a/labels.py +++ b/labels.py @@ -29,7 +29,7 @@ def get_label_events( return label_events -def get_label_metrics(issue: github3.issues.Issue, labels: List[str]) -> dict: # type: ignore +def get_label_metrics(issue: github3.issues.Issue, labels: List[str]) -> dict: """ Calculate the time spent with the given labels on a given issue. diff --git a/markdown_writer.py b/markdown_writer.py index 3bc20f5..7e23593 100644 --- a/markdown_writer.py +++ b/markdown_writer.py @@ -73,6 +73,7 @@ def write_to_markdown( num_issues_opened: Union[int, None], num_issues_closed: Union[int, None], labels=None, + search_query=None, ) -> None: """Write the issues with metrics to a markdown file. @@ -88,6 +89,7 @@ def write_to_markdown( num_issues_opened (int): The Number of items that remain opened. num_issues_closed (int): The number of issues that were closed. labels (List[str]): A list of the labels that are used in the issues. + search_query (str): The search query used to find the issues. Returns: None. @@ -154,6 +156,8 @@ def write_to_markdown( file.write( "\n_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_\n" ) + if search_query: + file.write(f"Search query used to find these items: `{search_query}`\n") print("Wrote issue metrics to issue_metrics.md") diff --git a/test_json_writer.py b/test_json_writer.py index e2951b7..e68fa42 100644 --- a/test_json_writer.py +++ b/test_json_writer.py @@ -64,6 +64,7 @@ def test_write_to_json(self): "label_metrics": {}, }, ], + "search_query": "is:issue repo:owner/repo", } # Call the function and check the output @@ -78,6 +79,7 @@ def test_write_to_json(self): }, num_issues_opened=num_issues_opened, num_issues_closed=num_issues_closed, + search_query="is:issue repo:owner/repo", ), json.dumps(expected_output), ) diff --git a/test_markdown_writer.py b/test_markdown_writer.py index 3dc4780..40cb85d 100644 --- a/test_markdown_writer.py +++ b/test_markdown_writer.py @@ -63,6 +63,7 @@ def test_write_to_markdown(self): num_issues_opened=num_issues_opened, num_issues_closed=num_issues_closed, labels=["bug"], + search_query="is:issue is:open label:bug", ) # Check that the function writes the correct markdown file @@ -87,6 +88,7 @@ def test_write_to_markdown(self): "| Issue 2 | https://github.com/user/repo/issues/2 | 3 days, 0:00:00 | " "4 days, 0:00:00 | 5 days, 0:00:00 | 2 days, 0:00:00 |\n\n" "_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_\n" + "Search query used to find these items: `is:issue is:open label:bug`\n" ) self.assertEqual(content, expected_content) os.remove("issue_metrics.md") @@ -94,10 +96,10 @@ def test_write_to_markdown(self): def test_write_to_markdown_with_vertical_bar_in_title(self): """Test that write_to_markdown writes the correct markdown file when the title contains a vertical bar. - This test creates a list of mock GitHub issues (one of which contains a vertical bar in the title) with time to first response - attributes, calls write_to_markdown with the list and the average time to - first response, time to close and checks that the function writes the correct - markdown file. + This test creates a list of mock GitHub issues (one of which contains a vertical + bar in the title) with time to first response attributes, calls write_to_markdown + with the list and the average time to first response, time to close and checks + that the function writes the correct markdown file. """ # Create mock data @@ -243,6 +245,7 @@ def test_writes_markdown_file_with_non_hidden_columns_only(self): num_issues_opened=num_issues_opened, num_issues_closed=num_issues_closed, labels=["label1"], + search_query="repo:user/repo is:issue", ) # Check that the function writes the correct markdown file @@ -260,6 +263,7 @@ def test_writes_markdown_file_with_non_hidden_columns_only(self): "| Issue 1 | https://github.com/user/repo/issues/1 |\n" "| Issue 2 | https://github.com/user/repo/issues/2 |\n\n" "_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_\n" + "Search query used to find these items: `repo:user/repo is:issue`\n" ) self.assertEqual(content, expected_content) os.remove("issue_metrics.md") diff --git a/time_to_first_response.py b/time_to_first_response.py index a5295ca..204b209 100644 --- a/time_to_first_response.py +++ b/time_to_first_response.py @@ -27,7 +27,7 @@ def measure_time_to_first_response( issue: Union[github3.issues.Issue, None], # type: ignore discussion: Union[dict, None], - ignore_users: List[str] = [], + ignore_users: List[str] = None, ) -> Union[timedelta, None]: """Measure the time to first response for a single issue or a discussion. @@ -44,6 +44,8 @@ def measure_time_to_first_response( first_comment_time = None earliest_response = None issue_time = None + if ignore_users is None: + ignore_users = [] # Get the first comment time if issue: