From d87d4e6673c51c210e077e446fe4c0e9e259bb7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 19:07:38 +0000 Subject: [PATCH 01/11] chore(deps): bump the dependencies group with 2 updates Bumps the dependencies group with 2 updates: [flake8](https://github.com/pycqa/flake8) and [pytest](https://github.com/pytest-dev/pytest). Updates `flake8` from 7.2.0 to 7.3.0 - [Commits](https://github.com/pycqa/flake8/compare/7.2.0...7.3.0) Updates `pytest` from 8.4.0 to 8.4.1 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.4.0...8.4.1) --- updated-dependencies: - dependency-name: flake8 dependency-version: 7.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: pytest dependency-version: 8.4.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies ... Signed-off-by: dependabot[bot] --- requirements-test.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-test.txt b/requirements-test.txt index b69e850..3b294cb 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,9 +1,9 @@ black==25.1.0 -flake8==7.2.0 +flake8==7.3.0 mypy==1.16.1 mypy-extensions==1.1.0 pylint==3.3.7 -pytest==8.4.0 +pytest==8.4.1 pytest-cov==6.2.1 types-pytz==2025.2.0.20250516 types-requests==2.32.4.20250611 From 06a4304ebc5f4b5ca08e6af2cd533257451a1dcb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 20:38:30 +0000 Subject: [PATCH 02/11] chore(deps): bump python-dotenv in the dependencies group --- updated-dependencies: - dependency-name: python-dotenv dependency-version: 1.1.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index efe5a96..2d48a84 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ github3.py==4.0.1 numpy==2.2.4 -python-dotenv==1.1.0 +python-dotenv==1.1.1 pytz==2025.2 requests==2.32.4 From 4dad97e7abb2bbcc0dd66359f3d76c85a393bcc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 20:43:41 +0000 Subject: [PATCH 03/11] chore(deps): bump github/codeql-action in the dependencies group Bumps the dependencies group with 1 update: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 3.29.0 to 3.29.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/ce28f5bb42b7a9f2c824e633a3f6ee835bab6858...181d5eefc20863364f96762470ba6f862bdef56b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.29.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index b1ce6af..047161a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -42,6 +42,6 @@ jobs: path: results.sarif retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.24.9 + uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.24.9 with: sarif_file: results.sarif From cd8e18d66f5a327ccef171f4633825be8ea1c602 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:22:21 +0000 Subject: [PATCH 04/11] chore(deps): bump python from `f2fdaec` to `6544e0e` Bumps python from `f2fdaec` to `6544e0e`. --- updated-dependencies: - dependency-name: python dependency-version: 3.13-slim dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f7112a5..26a5817 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ #checkov:skip=CKV_DOCKER_2 #checkov:skip=CKV_DOCKER_3 -FROM python:3.13-slim@sha256:f2fdaec50160418e0c2867ba3e254755edd067171725886d5d303fd7057bbf81 +FROM python:3.13-slim@sha256:6544e0e002b40ae0f59bc3618b07c1e48064c4faed3a15ae2fbd2e8f663e8283 LABEL com.github.actions.name="issue-metrics" \ com.github.actions.description="Gather metrics on issues/prs/discussions such as time to first response, count of issues opened, closed, etc." \ com.github.actions.icon="check-square" \ From fb669c9330d041c418139860d16458494f378b42 Mon Sep 17 00:00:00 2001 From: Zack Koppert Date: Thu, 17 Jul 2025 23:20:40 +0000 Subject: [PATCH 05/11] feat: add status column feature with configs to hide it Signed-off-by: Zack Koppert --- README.md | 5 +++-- config.py | 6 ++++++ markdown_writer.py | 8 ++++++++ test_assignee_functionality.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e63018c..12e498b 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ This action can be configured to authenticate with GitHub App Installation or Pe ##### GitHub App Installation | field | required | default | description | -| ---------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ---------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GH_APP_ID` | True | `""` | GitHub Application ID. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. | | `GH_APP_INSTALLATION_ID` | True | `""` | GitHub Application Installation ID. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. | | `GH_APP_PRIVATE_KEY` | True | `""` | GitHub Application Private Key. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. | @@ -154,7 +154,8 @@ This action can be configured to authenticate with GitHub App Installation or Pe | `HIDE_TIME_TO_ANSWER` | False | False | If set to `true`, the time to answer a discussion will not be displayed in the generated Markdown file. | | `HIDE_TIME_TO_CLOSE` | False | False | If set to `true`, the time to close will not be displayed in the generated Markdown file. | | `HIDE_TIME_TO_FIRST_RESPONSE` | False | False | If set to `true`, the time to first response will not be displayed in the generated Markdown file. | -| `HIDE_CREATED_AT` | False | True | If set to `true`, the creation timestmap will not be displayed in the generated Markdown file. | +| `HIDE_STATUS` | False | True | If set to `true`, the status column will not be shown | +| `HIDE_CREATED_AT` | False | True | If set to `true`, the creation timestamp will not be displayed in the generated Markdown file. | | `DRAFT_PR_TRACKING` | False | False | If set to `true`, draft PRs will be included in the metrics as a new column and in the summary stats. | | `IGNORE_USERS` | False | False | A comma separated list of users to ignore when calculating metrics. (ie. `IGNORE_USERS: 'user1,user2'`). To ignore bots, append `[bot]` to the user (ie. `IGNORE_USERS: 'github-actions[bot]'`) Users in this list will also have their authored issues and pull requests removed from the Markdown table. | | `ENABLE_MENTOR_COUNT` | False | False | If set to 'TRUE' count number of comments users left on discussions, issues and PRs and display number of active mentors | diff --git a/config.py b/config.py index 55768dc..baa0097 100644 --- a/config.py +++ b/config.py @@ -39,6 +39,8 @@ class EnvVars: hide_time_to_close (bool): If true, the time to close metric is hidden in the output hide_time_to_first_response (bool): If true, the time to first response metric is hidden in the output + hide_created_at (bool): If true, the created at timestamp is hidden in the output + hide_status (bool): If true, the status column is hidden in the output ignore_users (List[str]): List of usernames to ignore when calculating metrics labels_to_measure (List[str]): List of labels to measure how much time the label is applied enable_mentor_count (bool): If set to TRUE, compute number of mentors @@ -73,6 +75,7 @@ def __init__( hide_time_to_close: bool, hide_time_to_first_response: bool, hide_created_at: bool, + hide_status: bool, # New attribute ignore_user: List[str], labels_to_measure: List[str], enable_mentor_count: bool, @@ -102,6 +105,7 @@ def __init__( self.hide_time_to_close = hide_time_to_close self.hide_time_to_first_response = hide_time_to_first_response self.hide_created_at = hide_created_at + self.hide_status = hide_status # Initialize the new attribute self.enable_mentor_count = enable_mentor_count self.min_mentor_comments = min_mentor_comments self.max_comments_eval = max_comments_eval @@ -238,6 +242,7 @@ def get_env_vars(test: bool = False) -> EnvVars: hide_time_to_close = get_bool_env_var("HIDE_TIME_TO_CLOSE", False) hide_time_to_first_response = get_bool_env_var("HIDE_TIME_TO_FIRST_RESPONSE", False) hide_created_at = get_bool_env_var("HIDE_CREATED_AT", True) + hide_status = get_bool_env_var("HIDE_STATUS", True) # New attribute enable_mentor_count = get_bool_env_var("ENABLE_MENTOR_COUNT", False) min_mentor_comments = os.getenv("MIN_MENTOR_COMMENTS", "10") max_comments_eval = os.getenv("MAX_COMMENTS_EVAL", "20") @@ -259,6 +264,7 @@ def get_env_vars(test: bool = False) -> EnvVars: hide_time_to_close, hide_time_to_first_response, hide_created_at, + hide_status, ignore_users_list, labels_to_measure_list, enable_mentor_count, diff --git a/markdown_writer.py b/markdown_writer.py index efaf0ac..e83fa23 100644 --- a/markdown_writer.py +++ b/markdown_writer.py @@ -75,6 +75,10 @@ def get_non_hidden_columns(labels) -> List[str]: if not hide_time_to_answer: columns.append("Time to answer") + hide_status = env_vars.hide_status # Check the new attribute + if not hide_status: + columns.append("Status") # Add the 'status' column + enable_time_in_draft = env_vars.draft_pr_tracking if enable_time_in_draft: columns.append("Time in draft") @@ -232,6 +236,8 @@ def write_to_markdown( file.write(f" {issue.label_metrics[label]} |") if "Created At" in columns: file.write(f" {issue.created_at} |") + if "Status" in columns: # Check if 'Status' column is to be displayed + file.write(f" {issue.status} |") # Write the status of the issue file.write("\n") file.write( "\n_This report was generated with the \ @@ -324,6 +330,8 @@ def write_overall_metrics_tables( f"| {stats_time_in_labels['med'][label]} " f"| {stats_time_in_labels['90p'][label]} |\n" ) + if "Status" in columns: # Add logic for the 'status' column + file.write("| Status | | | |\n") file.write("\n") # Write count stats to a separate table diff --git a/test_assignee_functionality.py b/test_assignee_functionality.py index 1c12a9b..890fc62 100644 --- a/test_assignee_functionality.py +++ b/test_assignee_functionality.py @@ -75,6 +75,34 @@ def test_get_non_hidden_columns_hides_both_assignee_and_author(self): self.assertNotIn("Assignee", columns) self.assertNotIn("Author", columns) + @patch.dict( + os.environ, + { + "GH_TOKEN": "test_token", + "SEARCH_QUERY": "is:issue is:open repo:user/repo", + "HIDE_STATUS": "false", + }, + clear=True, + ) + def test_get_non_hidden_columns_includes_status_by_default(self): + """Test that status column is included by default.""" + columns = get_non_hidden_columns(labels=None) + self.assertIn("Status", columns) + + @patch.dict( + os.environ, + { + "GH_TOKEN": "test_token", + "SEARCH_QUERY": "is:issue is:open repo:user/repo", + "HIDE_STATUS": "true", + }, + clear=True, + ) + def test_get_non_hidden_columns_hides_status_when_env_set(self): + """Test that status column is hidden when HIDE_STATUS is true.""" + columns = get_non_hidden_columns(labels=None) + self.assertNotIn("Status", columns) + def test_assignee_column_position(self): """Test that assignee column appears before author column.""" with patch.dict( From 155da571f82b5998414aae45ba10d8b603931111 Mon Sep 17 00:00:00 2001 From: Zack Koppert Date: Thu, 17 Jul 2025 16:28:31 -0700 Subject: [PATCH 06/11] fix: add hide_status to the env class and fix tests Signed-off-by: Zack Koppert --- config.py | 7 ++++--- test_config.py | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/config.py b/config.py index baa0097..475aa34 100644 --- a/config.py +++ b/config.py @@ -75,7 +75,7 @@ def __init__( hide_time_to_close: bool, hide_time_to_first_response: bool, hide_created_at: bool, - hide_status: bool, # New attribute + hide_status: bool, ignore_user: List[str], labels_to_measure: List[str], enable_mentor_count: bool, @@ -105,7 +105,7 @@ def __init__( self.hide_time_to_close = hide_time_to_close self.hide_time_to_first_response = hide_time_to_first_response self.hide_created_at = hide_created_at - self.hide_status = hide_status # Initialize the new attribute + self.hide_status = hide_status self.enable_mentor_count = enable_mentor_count self.min_mentor_comments = min_mentor_comments self.max_comments_eval = max_comments_eval @@ -134,6 +134,7 @@ def __repr__(self): f"{self.hide_time_to_close}," f"{self.hide_time_to_first_response}," f"{self.hide_created_at}," + f"{self.hide_status}," f"{self.ignore_users}," f"{self.labels_to_measure}," f"{self.enable_mentor_count}," @@ -242,7 +243,7 @@ def get_env_vars(test: bool = False) -> EnvVars: hide_time_to_close = get_bool_env_var("HIDE_TIME_TO_CLOSE", False) hide_time_to_first_response = get_bool_env_var("HIDE_TIME_TO_FIRST_RESPONSE", False) hide_created_at = get_bool_env_var("HIDE_CREATED_AT", True) - hide_status = get_bool_env_var("HIDE_STATUS", True) # New attribute + hide_status = get_bool_env_var("HIDE_STATUS", True) enable_mentor_count = get_bool_env_var("ENABLE_MENTOR_COUNT", False) min_mentor_comments = os.getenv("MIN_MENTOR_COMMENTS", "10") max_comments_eval = os.getenv("MAX_COMMENTS_EVAL", "20") diff --git a/test_config.py b/test_config.py index 537d157..49435fa 100644 --- a/test_config.py +++ b/test_config.py @@ -131,6 +131,7 @@ def test_get_env_vars_with_github_app(self): hide_time_to_close=False, hide_time_to_first_response=False, hide_created_at=True, + hide_status=True, ignore_user=[], labels_to_measure=[], enable_mentor_count=False, @@ -186,6 +187,7 @@ def test_get_env_vars_with_token(self): hide_time_to_close=False, hide_time_to_first_response=False, hide_created_at=True, + hide_status=True, ignore_user=[], labels_to_measure=[], enable_mentor_count=False, @@ -276,6 +278,7 @@ def test_get_env_vars_optional_values(self): hide_time_to_close=True, hide_time_to_first_response=True, hide_created_at=True, + hide_status=True, ignore_user=[], labels_to_measure=["waiting-for-review", "waiting-for-manager"], enable_mentor_count=False, @@ -320,6 +323,7 @@ def test_get_env_vars_optionals_are_defaulted(self): hide_time_to_close=False, hide_time_to_first_response=False, hide_created_at=True, + hide_status=True, ignore_user=[], labels_to_measure=[], enable_mentor_count=False, From 38ca2915327c8ae6a9cc2a1e06d7acd883df950c Mon Sep 17 00:00:00 2001 From: Zack Koppert Date: Thu, 17 Jul 2025 17:02:33 -0700 Subject: [PATCH 07/11] feat: connect status up to actual api call Signed-off-by: Zack Koppert --- classes.py | 3 +++ issue_metrics.py | 4 ++++ markdown_writer.py | 8 ++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/classes.py b/classes.py index 20ab9b3..d24f430 100644 --- a/classes.py +++ b/classes.py @@ -24,6 +24,7 @@ class IssueWithMetrics: label_metrics (dict, optional): A dictionary containing the label metrics mentor_activity (dict, optional): A dictionary containing active mentors created_at (datetime, optional): The time the issue was created. + status (str, optional): The status of the issue, e.g., "open", "closed as completed", """ # pylint: disable=too-many-instance-attributes @@ -42,6 +43,7 @@ def __init__( created_at=None, assignee=None, assignees=None, + status=None, ): self.title = title self.html_url = html_url @@ -55,3 +57,4 @@ def __init__( self.label_metrics = labels_metrics self.mentor_activity = mentor_activity self.created_at = created_at + self.status = status diff --git a/issue_metrics.py b/issue_metrics.py index e7f5982..6664535 100644 --- a/issue_metrics.py +++ b/issue_metrics.py @@ -175,8 +175,12 @@ def get_per_issue_metrics( issue_with_metrics.time_to_close = measure_time_to_close( issue, None ) + if env_vars.hide_status is False: + issue_with_metrics.status = f"{issue.issue.state} as {issue.issue.state_reason}" # type: ignore elif issue.state == "open": # type: ignore num_issues_open += 1 + if env_vars.hide_status is False: + issue_with_metrics.status = f"{issue.issue.state}" # type: ignore if not env_vars.hide_created_at: if isinstance(issue, github3.search.IssueSearchResult): # type: ignore issue_with_metrics.created_at = issue.issue.created_at # type: ignore diff --git a/markdown_writer.py b/markdown_writer.py index e83fa23..7a88a8a 100644 --- a/markdown_writer.py +++ b/markdown_writer.py @@ -75,9 +75,9 @@ def get_non_hidden_columns(labels) -> List[str]: if not hide_time_to_answer: columns.append("Time to answer") - hide_status = env_vars.hide_status # Check the new attribute + hide_status = env_vars.hide_status if not hide_status: - columns.append("Status") # Add the 'status' column + columns.append("Status") enable_time_in_draft = env_vars.draft_pr_tracking if enable_time_in_draft: @@ -236,8 +236,8 @@ def write_to_markdown( file.write(f" {issue.label_metrics[label]} |") if "Created At" in columns: file.write(f" {issue.created_at} |") - if "Status" in columns: # Check if 'Status' column is to be displayed - file.write(f" {issue.status} |") # Write the status of the issue + if "Status" in columns: + file.write(f" {issue.status} |") file.write("\n") file.write( "\n_This report was generated with the \ From f6ab3d4ff6eb802a7e66ddbfe62040594377ccb5 Mon Sep 17 00:00:00 2001 From: Zack Koppert Date: Thu, 17 Jul 2025 18:33:58 -0700 Subject: [PATCH 08/11] test: fix tests and add test for hide status is true Signed-off-by: Zack Koppert --- test_markdown_writer.py | 140 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 11 deletions(-) diff --git a/test_markdown_writer.py b/test_markdown_writer.py index bf3612c..0fbcad3 100644 --- a/test_markdown_writer.py +++ b/test_markdown_writer.py @@ -23,6 +23,7 @@ "GH_TOKEN": "test_token", "DRAFT_PR_TRACKING": "True", "HIDE_CREATED_AT": "False", + "HIDE_STATUS": "False", }, ) class TestWriteToMarkdown(unittest.TestCase): @@ -128,20 +129,21 @@ def test_write_to_markdown(self): "| Time to answer | 4 days, 0:00:00 | 4 days, 0:00:00 | 4 days, 0:00:00 |\n" "| Time in draft | 1 day, 0:00:00 | 1 day, 0:00:00 | 1 day, 0:00:00 |\n" "| Time spent in bug | 1 day, 12:00:00 | 1 day, 12:00:00 | 1 day, 12:00:00 |\n" + "| Status | | | |\n" "\n" "| Metric | Count |\n" "| --- | ---: |\n" "| Number of items that remain open | 2 |\n" "| Number of items closed | 1 |\n" "| Total number of items created | 2 |\n\n" - "| Title | URL | Assignee | Author | Time to first response | Time to close |" - " Time to answer | Time in draft | Time spent in bug | Created At |\n" - "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n" + "| Title | URL | Assignee | Author | Time to first response | Time to close | " + "Time to answer | Status | Time in draft | Time spent in bug | Created At |\n" + "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n" "| Issue 1 | https://github.com/user/repo/issues/1 | [charlie](https://github.com/charlie) | " "[alice](https://github.com/alice) | 1 day, 0:00:00 | 2 days, 0:00:00 | 3 days, 0:00:00 | " - "1 day, 0:00:00 | 4 days, 0:00:00 | -5 days, 0:00:00 |\n" + "1 day, 0:00:00 | 4 days, 0:00:00 | -5 days, 0:00:00 | None |\n" "| Issue 2 | https://github.com/user/repo/issues/2 | None | [bob](https://github.com/bob) | 3 days, 0:00:00 | " - "4 days, 0:00:00 | 5 days, 0:00:00 | 1 day, 0:00:00 | 2 days, 0:00:00 | -5 days, 0:00:00 |\n\n" + "4 days, 0:00:00 | 5 days, 0:00:00 | 1 day, 0:00:00 | 2 days, 0:00:00 | -5 days, 0:00:00 | None |\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" ) @@ -182,6 +184,7 @@ def test_write_to_markdown_with_vertical_bar_in_title(self): time_to_first_response=timedelta(days=3), time_to_close=timedelta(days=4), time_to_answer=timedelta(days=5), + time_in_draft=None, labels_metrics={"bug": timedelta(days=2)}, ), ] @@ -243,21 +246,22 @@ def test_write_to_markdown_with_vertical_bar_in_title(self): "| Time to answer | 4 days, 0:00:00 | 4 days, 0:00:00 | 4 days, 0:00:00 |\n" "| Time in draft | 1 day, 0:00:00 | 1 day, 0:00:00 | 1 day, 0:00:00 |\n" "| Time spent in bug | 1 day, 12:00:00 | 1 day, 12:00:00 | 1 day, 12:00:00 |\n" + "| Status | | | |\n" "\n" "| Metric | Count |\n" "| --- | ---: |\n" "| Number of items that remain open | 2 |\n" "| Number of items closed | 1 |\n" "| Total number of items created | 2 |\n\n" - "| Title | URL | Assignee | Author | Time to first response | Time to close |" - " Time to answer | Time in draft | Time spent in bug | Created At |\n" - "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n" + "| Title | URL | Assignee | Author | Time to first response | Time to close | " + "Time to answer | Status | Time in draft | Time spent in bug | Created At |\n" + "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n" "| Issue 1 | https://github.com/user/repo/issues/1 | [charlie](https://github.com/charlie) | " "[alice](https://github.com/alice) | 1 day, 0:00:00 | 2 days, 0:00:00 | 3 days, 0:00:00 | " - "1 day, 0:00:00 | 1 day, 0:00:00 | -5 days, 0:00:00 |\n" + "1 day, 0:00:00 | 1 day, 0:00:00 | -5 days, 0:00:00 | None |\n" "| feat| Issue 2 | https://github.com/user/repo/issues/2 | None | " "[bob](https://github.com/bob) | 3 days, 0:00:00 | " - "4 days, 0:00:00 | 5 days, 0:00:00 | None | 2 days, 0:00:00 | -5 days, 0:00:00 |\n\n" + "4 days, 0:00:00 | 5 days, 0:00:00 | None | 2 days, 0:00:00 | -5 days, 0:00:00 | None |\n\n" "_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_\n" ) self.assertEqual(content, expected_content) @@ -308,6 +312,7 @@ def test_write_to_markdown_no_issues(self): "HIDE_LABEL_METRICS": "True", "NON_MENTIONING_LINKS": "True", "GH_ENTERPRISE_URL": "https://ghe.com", + "HIDE_STATUS": "False", }, ) class TestWriteToMarkdownWithEnv(unittest.TestCase): @@ -400,7 +405,116 @@ def test_writes_markdown_file_with_non_hidden_columns_only(self): "| Number of items that remain open | 2 |\n" "| Number of most active mentors | 5 |\n" "| Total number of items created | 2 |\n\n" - "| Title | URL | Assignee | Author | Created At |\n" + "| Title | URL | Assignee | Author | Status | Created At |\n" + "| --- | --- | --- | --- | --- | --- |\n" + "| Issue 1 | https://www.ghe.com/user/repo/issues/1 | [charlie](https://ghe.com/charlie) | " + "[alice](https://ghe.com/alice) | -5 days, 0:00:00 | None |\n" + "| Issue 2 | https://www.ghe.com/user/repo/issues/2 | None | [bob](https://ghe.com/bob) | -5 days, 0:00:00 | None |\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") + + @patch.dict( + os.environ, + { + "SEARCH_QUERY": "is:open repo:user/repo", + "GH_TOKEN": "test_token", + "HIDE_CREATED_AT": "False", + "HIDE_TIME_TO_FIRST_RESPONSE": "True", + "HIDE_TIME_TO_CLOSE": "True", + "HIDE_TIME_TO_ANSWER": "True", + "HIDE_LABEL_METRICS": "True", + "NON_MENTIONING_LINKS": "True", + "GH_ENTERPRISE_URL": "https://ghe.com", + "HIDE_STATUS": "True", # Status column should be hidden + }, + ) + def test_writes_markdown_file_with_hidden_status_column(self): + """ + Test that write_to_markdown writes the correct markdown file + when HIDE_STATUS is set to True, ensuring the Status column + is not present in the output. + """ + # Create mock data + issues_with_metrics = [ + IssueWithMetrics( + title="Issue 1", + html_url="https://ghe.com/user/repo/issues/1", + author="alice", + assignee="charlie", + assignees=["charlie"], + created_at=timedelta(days=-5), + time_to_first_response=timedelta(minutes=10), + time_to_close=timedelta(days=1), + time_to_answer=timedelta(hours=2), + time_in_draft=timedelta(days=1), + labels_metrics={ + "label1": timedelta(days=1), + }, + ), + IssueWithMetrics( + title="Issue 2", + html_url="https://ghe.com/user/repo/issues/2", + author="bob", + assignee=None, + assignees=[], + created_at=timedelta(days=-5), + time_to_first_response=timedelta(minutes=20), + time_to_close=timedelta(days=2), + time_to_answer=timedelta(hours=4), + labels_metrics={ + "label1": timedelta(days=1), + }, + ), + ] + average_time_to_first_response = timedelta(minutes=15) + average_time_to_close = timedelta(days=1.5) + average_time_to_answer = timedelta(hours=3) + average_time_in_draft = timedelta(days=1) + average_time_in_labels = { + "label1": timedelta(days=1), + } + num_issues_opened = 2 + num_issues_closed = 2 + num_mentor_count = 5 + ghe = "https://ghe.com" + + # Call the function + write_to_markdown( + issues_with_metrics=issues_with_metrics, + average_time_to_first_response=average_time_to_first_response, + average_time_to_close=average_time_to_close, + average_time_to_answer=average_time_to_answer, + average_time_in_labels=average_time_in_labels, + average_time_in_draft=average_time_in_draft, + num_issues_opened=num_issues_opened, + num_issues_closed=num_issues_closed, + num_mentor_count=num_mentor_count, + labels=["label1"], + search_query="repo:user/repo is:issue", + hide_label_metrics=True, + hide_items_closed_count=True, + enable_mentor_count=True, + non_mentioning_links=True, + report_title="Issue Metrics", + output_file="issue_metrics.md", + ghe=ghe, + ) + + # Check that the function writes the correct markdown file + with open("issue_metrics.md", "r", encoding="utf-8") as file: + content = file.read() + + expected_content = ( + "# Issue Metrics\n\n" + "| Metric | Count |\n" + "| --- | ---: |\n" + "| Number of items that remain open | 2 |\n" + "| Number of most active mentors | 5 |\n" + "| Total number of items created | 2 |\n\n" + "| Title | URL | Assignee | Author | Created At |\n" # Status column should be missing "| --- | --- | --- | --- | --- |\n" "| Issue 1 | https://www.ghe.com/user/repo/issues/1 | [charlie](https://ghe.com/charlie) | " "[alice](https://ghe.com/alice) | -5 days, 0:00:00 |\n" @@ -410,3 +524,7 @@ def test_writes_markdown_file_with_non_hidden_columns_only(self): ) self.assertEqual(content, expected_content) os.remove("issue_metrics.md") + + +if __name__ == "__main__": + unittest.main() From 354444a7d5846ba085332bb3e1f94a6ec49c05c0 Mon Sep 17 00:00:00 2001 From: Zack Koppert Date: Thu, 17 Jul 2025 18:41:11 -0700 Subject: [PATCH 09/11] docs: fix formatting Signed-off-by: Zack Koppert --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 12e498b..596bfc3 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ This action can be configured to authenticate with GitHub App Installation or Pe ##### GitHub App Installation | field | required | default | description | -| ---------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ---------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GH_APP_ID` | True | `""` | GitHub Application ID. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. | | `GH_APP_INSTALLATION_ID` | True | `""` | GitHub Application Installation ID. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. | | `GH_APP_PRIVATE_KEY` | True | `""` | GitHub Application Private Key. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. | @@ -154,7 +154,7 @@ This action can be configured to authenticate with GitHub App Installation or Pe | `HIDE_TIME_TO_ANSWER` | False | False | If set to `true`, the time to answer a discussion will not be displayed in the generated Markdown file. | | `HIDE_TIME_TO_CLOSE` | False | False | If set to `true`, the time to close will not be displayed in the generated Markdown file. | | `HIDE_TIME_TO_FIRST_RESPONSE` | False | False | If set to `true`, the time to first response will not be displayed in the generated Markdown file. | -| `HIDE_STATUS` | False | True | If set to `true`, the status column will not be shown | +| `HIDE_STATUS` | False | True | If set to `true`, the status column will not be shown | | `HIDE_CREATED_AT` | False | True | If set to `true`, the creation timestamp will not be displayed in the generated Markdown file. | | `DRAFT_PR_TRACKING` | False | False | If set to `true`, draft PRs will be included in the metrics as a new column and in the summary stats. | | `IGNORE_USERS` | False | False | A comma separated list of users to ignore when calculating metrics. (ie. `IGNORE_USERS: 'user1,user2'`). To ignore bots, append `[bot]` to the user (ie. `IGNORE_USERS: 'github-actions[bot]'`) Users in this list will also have their authored issues and pull requests removed from the Markdown table. | From 117b899213b701849ea2ad27032d93ba08d59dad Mon Sep 17 00:00:00 2001 From: Zack Koppert Date: Thu, 17 Jul 2025 21:00:16 -0700 Subject: [PATCH 10/11] fix: pythonic logic Co-authored-by: Jason Meridth <35014+jmeridth@users.noreply.github.com> Signed-off-by: Zack Koppert --- issue_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/issue_metrics.py b/issue_metrics.py index 6664535..53df4fa 100644 --- a/issue_metrics.py +++ b/issue_metrics.py @@ -175,7 +175,7 @@ def get_per_issue_metrics( issue_with_metrics.time_to_close = measure_time_to_close( issue, None ) - if env_vars.hide_status is False: + if not env_vars.hide_status: issue_with_metrics.status = f"{issue.issue.state} as {issue.issue.state_reason}" # type: ignore elif issue.state == "open": # type: ignore num_issues_open += 1 From 543e100b8d0ab9112849b4a09dfe4b0a0a5badfc Mon Sep 17 00:00:00 2001 From: Zack Koppert Date: Thu, 17 Jul 2025 21:01:46 -0700 Subject: [PATCH 11/11] fix: pythonic logic Co-authored-by: Jason Meridth <35014+jmeridth@users.noreply.github.com> Signed-off-by: Zack Koppert --- issue_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/issue_metrics.py b/issue_metrics.py index 53df4fa..0c86912 100644 --- a/issue_metrics.py +++ b/issue_metrics.py @@ -179,7 +179,7 @@ def get_per_issue_metrics( issue_with_metrics.status = f"{issue.issue.state} as {issue.issue.state_reason}" # type: ignore elif issue.state == "open": # type: ignore num_issues_open += 1 - if env_vars.hide_status is False: + if not env_vars.hide_status: issue_with_metrics.status = f"{issue.issue.state}" # type: ignore if not env_vars.hide_created_at: if isinstance(issue, github3.search.IssueSearchResult): # type: ignore pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy