Repository: github/issue-metrics (python)

Issue:
Ability to hide `Number of items closed` Metric in the overview table
### Is your feature request related to a problem?

`Number of items closed` as shown in the screenshot does not have any meaningful value when running the action against Discussions. Discussions are normally marked as answered rather than closed.

![Image](https://github.com/github/issue-metrics/assets/6935431/f703f710-96de-4584-9b5e-8f8ccb7cbdd0)



### Describe the solution you'd like

I would like to have the ability to configure `HIDE_ITEMS_CLOSED_COUNT` env variable such that it is not reported in the markdown output.

### Describe alternatives you've considered

Just dealing with it 🤷 

### Additional context

_No response_

Pull request:
feat: Add option to hide Number of items closed metric
# Pull Request
<!-- 
PR title needs to be prefixed with a conventional commit type
(build,chore,ci,docs,feat,fix,perf,refactor,revert,style,test)

It should also be brief and descriptive for a good changelog entry

examples: "feat: add new logger" or "fix: remove unused imports"
-->
fixes #309 

## Proposed Changes
<!-- Describe what the changes are and link to a GitHub Issue if one exists -->
This pull request introduces a new environment variable `HIDE_ITEMS_CLOSED_COUNT` to control whether the number of items closed metric should be displayed in the generated Markdown file.

## Readiness Checklist

### Author/Contributor

- [x] If documentation is needed for this change, has that been included in this pull request
- [x] run `make lint` and fix any issues that you have introduced
- [x] run `make test` and ensure you have test coverage for the lines you are introducing
- [x] If publishing new data to the public (scorecards, security scan results, code quality results, live dashboards, etc.), please request review from `@jeffrey-luszcz`

### Reviewer

- [x] Label as either `fix`, `documentation`, `enhancement`, `infrastructure`, `maintenance`, or `breaking`


Patch:
diff --git a/.env-example b/.env-example
index f50b62c..c905ee3 100644
--- a/.env-example
+++ b/.env-example
@@ -4,6 +4,7 @@ GH_APP_PRIVATE_KEY=""
 GH_ENTERPRISE_URL = ""
 GH_TOKEN = ""
 HIDE_AUTHOR = "false"
+HIDE_ITEMS_CLOSED_COUNT="false"
 HIDE_LABEL_METRICS = "false"
 HIDE_TIME_TO_ANSWER = "false"
 HIDE_TIME_TO_CLOSE = "false"
diff --git a/README.md b/README.md
index 456a781..241fc77 100644
--- a/README.md
+++ b/README.md
@@ -143,6 +143,7 @@ This action can be configured to authenticate with GitHub App Installation or Pe
 |-------------------------------|----------|---------|-------------|
 | `GH_ENTERPRISE_URL`           | False    | `""`    | URL of GitHub Enterprise instance to use for auth instead of github.com                                                                 |
 | `HIDE_AUTHOR`                 | False    | False   | If set to `true`, the author will not be displayed in the generated Markdown file. |
+| `HIDE_ITEMS_CLOSED_COUNT`     | False    | False   | If set to `true`, the number of items closed metric will not be displayed in the generated Markdown file. |
 | `HIDE_LABEL_METRICS`          | False | False      | If set to `true`, the time in label metrics will not be displayed in the generated Markdown file. |
 | `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. |
diff --git a/config.py b/config.py
index d6c2c75..5186d44 100644
--- a/config.py
+++ b/config.py
@@ -28,6 +28,7 @@ class EnvVars:
         gh_token (str | None): GitHub personal access token (PAT) for API authentication
         ghe (str): The GitHub Enterprise URL to use for authentication
         hide_author (bool): If true, the author's information is hidden in the output
+        hide_items_closed_count (bool): If true, the number of items closed metric is hidden in the output
         hide_label_metrics (bool): If true, the label metrics are hidden in the output
         hide_time_to_answer (bool): If true, the time to answer discussions is hidden in the output
         hide_time_to_close (bool): If true, the time to close metric is hidden in the output
@@ -49,6 +50,7 @@ class EnvVars:
         gh_token: str | None,
         ghe: str | None,
         hide_author: bool,
+        hide_items_closed_count: bool,
         hide_label_metrics: bool,
         hide_time_to_answer: bool,
         hide_time_to_close: bool,
@@ -69,6 +71,7 @@ class EnvVars:
         self.ignore_users = ignore_user
         self.labels_to_measure = labels_to_measure
         self.hide_author = hide_author
+        self.hide_items_closed_count = hide_items_closed_count
         self.hide_label_metrics = hide_label_metrics
         self.hide_time_to_answer = hide_time_to_answer
         self.hide_time_to_close = hide_time_to_close
@@ -88,6 +91,7 @@ class EnvVars:
             f"{self.gh_token},"
             f"{self.ghe},"
             f"{self.hide_author},"
+            f"{self.hide_items_closed_count}),"
             f"{self.hide_label_metrics},"
             f"{self.hide_time_to_answer},"
             f"{self.hide_time_to_close},"
@@ -98,7 +102,7 @@ class EnvVars:
             f"{self.min_mentor_comments},"
             f"{self.max_comments_eval},"
             f"{self.heavily_involved_cutoff},"
-            f"{self.search_query})"
+            f"{self.search_query}"
         )
 
 
@@ -182,6 +186,7 @@ def get_env_vars(test: bool = False) -> EnvVars:
 
     # Hidden columns
     hide_author = get_bool_env_var("HIDE_AUTHOR", False)
+    hide_items_closed_count = get_bool_env_var("HIDE_ITEMS_CLOSED_COUNT", False)
     hide_label_metrics = get_bool_env_var("HIDE_LABEL_METRICS", False)
     hide_time_to_answer = get_bool_env_var("HIDE_TIME_TO_ANSWER", False)
     hide_time_to_close = get_bool_env_var("HIDE_TIME_TO_CLOSE", False)
@@ -198,6 +203,7 @@ def get_env_vars(test: bool = False) -> EnvVars:
         gh_token,
         ghe,
         hide_author,
+        hide_items_closed_count,
         hide_label_metrics,
         hide_time_to_answer,
         hide_time_to_close,
diff --git a/issue_metrics.py b/issue_metrics.py
index dfe57f2..3d62d30 100644
--- a/issue_metrics.py
+++ b/issue_metrics.py
@@ -305,6 +305,7 @@ def main():
     search_query = env_vars.search_query
     token = env_vars.gh_token
     ignore_users = env_vars.ignore_users
+    hide_items_closed_count = env_vars.hide_items_closed_count
 
     gh_app_id = env_vars.gh_app_id
     gh_app_installation_id = env_vars.gh_app_installation_id
@@ -412,6 +413,7 @@ def main():
         num_mentor_count,
         labels,
         search_query,
+        hide_items_closed_count,
     )
 
     max_char_count = 65535
diff --git a/markdown_writer.py b/markdown_writer.py
index 0b587ae..5e3e300 100644
--- a/markdown_writer.py
+++ b/markdown_writer.py
@@ -84,6 +84,7 @@ def write_to_markdown(
     labels=None,
     search_query=None,
     hide_label_metrics=False,
+    hide_items_closed_count=False,
 ) -> None:
     """Write the issues with metrics to a markdown file.
 
@@ -102,6 +103,7 @@ def write_to_markdown(
         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.
         hide_label_metrics (bool): Represents whether the user has chosen to hide label metrics in the output
+        hide_items_closed_count (bool): Represents whether the user has chosen to hide the number of items closed
 
     Returns:
         None.
@@ -135,6 +137,7 @@ def write_to_markdown(
             columns,
             file,
             hide_label_metrics,
+            hide_items_closed_count,
         )
 
         # Write second table with individual issue/pr/discussion metrics
@@ -193,6 +196,7 @@ def write_overall_metrics_tables(
     columns,
     file,
     hide_label_metrics,
+    hide_items_closed_count=False,
 ):
     """Write the overall metrics tables to the markdown file."""
     if (
@@ -250,6 +254,7 @@ def write_overall_metrics_tables(
     file.write("| Metric | Count |\n")
     file.write("| --- | ---: |\n")
     file.write(f"| Number of items that remain open | {num_issues_opened} |\n")
-    file.write(f"| Number of items closed | {num_issues_closed} |\n")
+    if not hide_items_closed_count:
+        file.write(f"| Number of items closed | {num_issues_closed} |\n")
     file.write(f"| Number of most active mentors | {num_mentor_count} |\n")
     file.write(f"| Total number of items created | {len(issues_with_metrics)} |\n\n")


Test patch:
diff --git a/test_config.py b/test_config.py
index d2f1bd1..6c2abef 100644
--- a/test_config.py
+++ b/test_config.py
@@ -72,6 +72,7 @@ class TestGetEnvVars(unittest.TestCase):
             "GH_TOKEN",
             "GHE",
             "HIDE_AUTHOR",
+            "HIDE_ITEMS_CLOSED_COUNT",
             "HIDE_LABEL_METRICS",
             "HIDE_TIME_TO_ANSWER",
             "HIDE_TIME_TO_CLOSE",
@@ -93,6 +94,7 @@ class TestGetEnvVars(unittest.TestCase):
             "GH_TOKEN": "",
             "GH_ENTERPRISE_URL": "",
             "HIDE_AUTHOR": "",
+            "HIDE_ITEMS_CLOSED_COUNT": "false",
             "HIDE_LABEL_METRICS": "",
             "HIDE_TIME_TO_ANSWER": "",
             "HIDE_TIME_TO_CLOSE": "",
@@ -116,6 +118,7 @@ class TestGetEnvVars(unittest.TestCase):
             False,
             False,
             False,
+            False,
             [],
             [],
             False,
@@ -136,6 +139,7 @@ class TestGetEnvVars(unittest.TestCase):
             "GH_ENTERPRISE_URL": "",
             "GH_TOKEN": TOKEN,
             "HIDE_AUTHOR": "",
+            "HIDE_ITEMS_CLOSED_COUNT": "false",
             "HIDE_LABEL_METRICS": "",
             "HIDE_TIME_TO_ANSWER": "",
             "HIDE_TIME_TO_CLOSE": "",
@@ -159,6 +163,7 @@ class TestGetEnvVars(unittest.TestCase):
             False,
             False,
             False,
+            False,
             [],
             [],
             False,
@@ -170,6 +175,41 @@ class TestGetEnvVars(unittest.TestCase):
         result = get_env_vars(True)
         self.assertEqual(str(result), str(expected_result))
 
+    @patch.dict(
+        os.environ,
+        {
+            "GH_APP_ID": "",
+            "GH_APP_INSTALLATION_ID": "",
+            "GH_APP_PRIVATE_KEY": "",
+            "GH_TOKEN": "",
+            "SEARCH_QUERY": SEARCH_QUERY,
+            "HIDE_ITEMS_CLOSED_COUNT": "false",
+        },
+        clear=True,
+    )
+    def test_get_env_vars_missing_token(self):
+        """Test that an error is raised if the TOKEN environment variables is not set"""
+        with self.assertRaises(ValueError):
+            get_env_vars(True)
+
+    @patch.dict(
+        os.environ,
+        {
+            "GH_APP_ID": "",
+            "GH_APP_INSTALLATION_ID": "",
+            "GH_APP_PRIVATE_KEY": "",
+            "GH_TOKEN": TOKEN,
+            "SEARCH_QUERY": "",
+            "HIDE_ITEMS_CLOSED_COUNT": "false",
+        },
+        clear=True,
+    )
+    def test_get_env_vars_missing_query(self):
+        """Test that an error is raised if the SEARCH_QUERY environment variable is not set."""
+
+        with self.assertRaises(ValueError):
+            get_env_vars(True)
+
     @patch.dict(
         os.environ,
         {
@@ -179,6 +219,7 @@ class TestGetEnvVars(unittest.TestCase):
             "GH_TOKEN": TOKEN,
             "GH_ENTERPRISE_URL": "",
             "HIDE_AUTHOR": "true",
+            "HIDE_ITEMS_CLOSED_COUNT": "true",
             "HIDE_LABEL_METRICS": "true",
             "HIDE_TIME_TO_ANSWER": "true",
             "HIDE_TIME_TO_CLOSE": "true",
@@ -201,6 +242,7 @@ class TestGetEnvVars(unittest.TestCase):
             True,
             True,
             True,
+            True,
             [],
             ["waiting-for-review", "waiting-for-manager"],
             False,
@@ -218,32 +260,35 @@ class TestGetEnvVars(unittest.TestCase):
             "GH_APP_ID": "",
             "GH_APP_INSTALLATION_ID": "",
             "GH_APP_PRIVATE_KEY": "",
-            "GH_TOKEN": "",
+            "GH_TOKEN": "TOKEN",
             "SEARCH_QUERY": SEARCH_QUERY,
         },
         clear=True,
     )
-    def test_get_env_vars_missing_token(self):
-        """Test that an error is raised if the TOKEN environment variables is not set"""
-        with self.assertRaises(ValueError):
-            get_env_vars(True)
-
-    @patch.dict(
-        os.environ,
-        {
-            "GH_APP_ID": "",
-            "GH_APP_INSTALLATION_ID": "",
-            "GH_APP_PRIVATE_KEY": "",
-            "GH_TOKEN": TOKEN,
-            "SEARCH_QUERY": "",
-        },
-        clear=True,
-    )
-    def test_get_env_vars_missing_query(self):
-        """Test that an error is raised if the SEARCH_QUERY environment variable is not set."""
-
-        with self.assertRaises(ValueError):
-            get_env_vars(True)
+    def test_get_env_vars_optionals_are_defaulted(self):
+        """Test that optional values are set to their default values if not provided"""
+        expected_result = EnvVars(
+            None,
+            None,
+            b"",
+            "TOKEN",
+            "",
+            False,
+            False,
+            False,
+            False,
+            False,
+            False,
+            [],
+            [],
+            False,
+            "10",
+            "20",
+            "3",
+            SEARCH_QUERY,
+        )
+        result = get_env_vars(True)
+        self.assertEqual(str(result), str(expected_result))
 
 
 if __name__ == "__main__":
diff --git a/test_markdown_writer.py b/test_markdown_writer.py
index 3ebb692..4328f70 100644
--- a/test_markdown_writer.py
+++ b/test_markdown_writer.py
@@ -313,6 +313,7 @@ class TestWriteToMarkdownWithEnv(unittest.TestCase):
             labels=["label1"],
             search_query="repo:user/repo is:issue",
             hide_label_metrics=True,
+            hide_items_closed_count=True,
         )
 
         # Check that the function writes the correct markdown file
@@ -323,7 +324,6 @@ class TestWriteToMarkdownWithEnv(unittest.TestCase):
             "| Metric | Count |\n"
             "| --- | ---: |\n"
             "| Number of items that remain open | 2 |\n"
-            "| Number of items closed | 2 |\n"
             "| Number of most active mentors | 5 |\n"
             "| Total number of items created | 2 |\n\n"
             "| Title | URL | Author |\n"