Skip to content

Commit

Permalink
Merge pull request #40 from github/hiding
Browse files Browse the repository at this point in the history
feat: Configurable column hiding
  • Loading branch information
zkoppert authored Jun 29, 2023
2 parents 6bd1c13 + 0d43573 commit f8c1690
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 34 deletions.
3 changes: 3 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
GH_TOKEN = " "
SEARCH_QUERY = "repo:owner/repo is:open is:issue"
HIDE_TIME_TO_FIRST_RESPONSE = False
HIDE_TIME_TO_CLOSE = False
HIDE_TIME_TO_ANSWER = False
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ Below are the allowed configuration options:

| field | required | default | description |
|-----------------------|----------|---------|-------------|
| `GH_TOKEN` | true | | The GitHub Token used to scan the repository. Must have read access to all repository you are interested in scanning. |
| `SEARCH_QUERY` | true | | The query by which you can filter issues/prs which must contain a `repo:` entry or an `org:` entry. For discussions, include `type:discussions` in the query. |
| `GH_TOKEN` | True | | The GitHub Token used to scan the repository. Must have read access to all repository you are interested in scanning. |
| `SEARCH_QUERY` | True | | The query by which you can filter issues/prs which must contain a `repo:` entry or an `org:` entry. For discussions, include `type:discussions` in the query. |
| `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_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_ANSWER` | False | False | If set to true, the time to answer a discussion will not be displayed in the generated markdown file. |

### Example workflows

Expand Down Expand Up @@ -196,6 +199,7 @@ jobs:
## Example issue_metrics.md output
Here is the output with no hidden columns:
```markdown
# Issue Metrics

Expand All @@ -209,13 +213,31 @@ jobs:
| Total number of items created | 3 |

| Title | URL | Time to first response | Time to close | Time to answer |
| --- | --- | ---: | ---: | ---: |
| --- | --- | --- | --- | --- |
| Discussion Title 1 | https://github.com/user/repo/discussions/1 | 0:00:41 | 6 days, 7:08:52 | 1 day |
| Pull Request Title 2 | https://github.com/user/repo/pulls/2 | 0:05:26 | None | None |
| Issue Title 3 | https://github.com/user/repo/issues/3 | 2:26:07 | None | None |

```

Here is the output with all hidable columns hidden:
```markdown
# Issue Metrics

| Metric | Value |
| --- | ---: |
| Number of items that remain open | 2 |
| Number of items closed | 1 |
| Total number of items created | 3 |

| Title | URL |
| --- | --- |
| Discussion Title 1 | https://github.com/user/repo/discussions/1 |
| Pull Request Title 2 | https://github.com/user/repo/pulls/2 |
| Issue Title 3 | https://github.com/user/repo/issues/3 | 2:26:07 |

```

## Local usage without Docker

1. Copy `.env-example` to `.env`
Expand Down
113 changes: 83 additions & 30 deletions markdown_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,49 @@
file: file object = None
) -> None:
Write the issues with metrics to a markdown file.
get_non_hidden_columns(
average_time_to_first_response: timedelta,
average_time_to_close: timedelta,
average_time_to_answer: timedelta
) -> List[str]:
Get the columns that are not hidden.
"""

import os
from datetime import timedelta
from typing import List, Union

from classes import IssueWithMetrics


def get_non_hidden_columns() -> List[str]:
"""
Get a list of the columns that are not hidden.
Args:
None
Returns:
List[str]: A list of the columns that are not hidden.
"""
columns = ["Title", "URL"]
# Find the number of columns and which are to be hidden
hide_time_to_first_response = os.getenv("HIDE_TIME_TO_FIRST_RESPONSE")
if not hide_time_to_first_response:
columns.append("Time to first response")

hide_time_to_close = os.getenv("HIDE_TIME_TO_CLOSE")
if not hide_time_to_close:
columns.append("Time to close")

hide_time_to_answer = os.getenv("HIDE_TIME_TO_ANSWER")
if not hide_time_to_answer:
columns.append("Time to answer")

return columns


def write_to_markdown(
issues_with_metrics: Union[List[IssueWithMetrics], None],
average_time_to_first_response: Union[timedelta, None],
Expand All @@ -50,40 +85,58 @@ def write_to_markdown(
None.
"""
columns = get_non_hidden_columns()

# If all the metrics are None, then there are no issues
if not issues_with_metrics or len(issues_with_metrics) == 0:
with file or open("issue_metrics.md", "w", encoding="utf-8") as file:
file.write("no issues found for the given search criteria\n\n")
else:
# Sort the issues by time to first response
issues_with_metrics.sort(
key=lambda x: x.time_to_first_response or timedelta.max
)
with file or open("issue_metrics.md", "w", encoding="utf-8") as file:
file.write("# Issue Metrics\n\n")
file.write("| Metric | Value |\n")
file.write("| --- | ---: |\n")
return

# Sort the issues by time to first response
issues_with_metrics.sort(key=lambda x: x.time_to_first_response or timedelta.max)
with file or open("issue_metrics.md", "w", encoding="utf-8") as file:
file.write("# Issue Metrics\n\n")

# Write first table with overall metrics
file.write("| Metric | Value |\n")
file.write("| --- | ---: |\n")
if "Time to first response" in columns:
file.write(
f"| Average time to first response | {average_time_to_first_response} |\n"
)
if "Time to close" in columns:
file.write(f"| Average time to close | {average_time_to_close} |\n")
if "Time to answer" in columns:
file.write(f"| Average time to answer | {average_time_to_answer} |\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")
file.write(
f"| Total number of items created | {len(issues_with_metrics)} |\n\n"
)
file.write(
"| Title | URL | Time to first response | Time to close | Time to answer |\n"
)
file.write("| --- | --- | ---: | ---: | ---: |\n")
for issue in issues_with_metrics:
file.write(
f"| "
f"{issue.title} | "
f"{issue.html_url} | "
f"{issue.time_to_first_response} |"
f" {issue.time_to_close} |"
f" {issue.time_to_answer} |"
f"\n"
)
print("Wrote issue metrics to issue_metrics.md")
file.write(f"| Number of items that remain open | {num_issues_opened} |\n")
file.write(f"| Number of items closed | {num_issues_closed} |\n")
file.write(
f"| Total number of items created | {len(issues_with_metrics)} |\n\n"
)

# Write second table with individual issue/pr/discussion metrics
# First write the header
file.write("|")
for column in columns:
file.write(f" {column} |")
file.write("\n")

# Then write the column dividers
file.write("|")
for _ in columns:
file.write(" --- |")
file.write("\n")

# Then write the issues/pr/discussions row by row
for issue in issues_with_metrics:
file.write(f"| " f"{issue.title} | " f"{issue.html_url} |")
if "Time to first response" in columns:
file.write(f" {issue.time_to_first_response} |")
if "Time to close" in columns:
file.write(f" {issue.time_to_close} |")
if "Time to answer" in columns:
file.write(f" {issue.time_to_answer} |")
file.write("\n")

print("Wrote issue metrics to issue_metrics.md")
75 changes: 74 additions & 1 deletion test_markdown_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def test_write_to_markdown(self):
"| Number of items closed | 1 |\n"
"| Total number of items created | 2 |\n\n"
"| Title | URL | Time to first response | Time to close | Time to answer |\n"
"| --- | --- | ---: | ---: | ---: |\n"
"| --- | --- | --- | --- | --- |\n"
"| Issue 1 | https://github.com/user/repo/issues/1 | 1 day, 0:00:00 | "
"2 days, 0:00:00 | 3 days, 0:00:00 |\n"
"| Issue 2 | https://github.com/user/repo/issues/2 | 3 days, 0:00:00 | "
Expand All @@ -93,3 +93,76 @@ def test_write_to_markdown_no_issues(self):
"issue_metrics.md", "w", encoding="utf-8"
)
mock_open_file().write.assert_called_once_with(expected_output)


class TestWriteToMarkdownWithEnv(unittest.TestCase):
"""Test the write_to_markdown function with the HIDE* environment variables set."""

def setUp(self):
# Set the HIDE* environment variables to True
os.environ["HIDE_TIME_TO_FIRST_RESPONSE"] = "True"
os.environ["HIDE_TIME_TO_CLOSE"] = "True"
os.environ["HIDE_TIME_TO_ANSWER"] = "True"

def tearDown(self):
# Unset the HIDE* environment variables
os.environ.pop("HIDE_TIME_TO_FIRST_RESPONSE")
os.environ.pop("HIDE_TIME_TO_CLOSE")
os.environ.pop("HIDE_TIME_TO_ANSWER")

def test_writes_markdown_file_with_non_hidden_columns_only(self):
"""
Test that write_to_markdown writes the correct
markdown file with non-hidden columns only.
"""

# Create mock data
issues_with_metrics = [
IssueWithMetrics(
title="Issue 1",
html_url="https://github.com/user/repo/issues/1",
time_to_first_response=timedelta(minutes=10),
time_to_close=timedelta(days=1),
time_to_answer=timedelta(hours=2),
),
IssueWithMetrics(
title="Issue 2",
html_url="https://github.com/user/repo/issues/2",
time_to_first_response=timedelta(minutes=20),
time_to_close=timedelta(days=2),
time_to_answer=timedelta(hours=4),
),
]
average_time_to_first_response = timedelta(minutes=15)
average_time_to_close = timedelta(days=1.5)
average_time_to_answer = timedelta(hours=3)
num_issues_opened = 2
num_issues_closed = 1

# Call the function
write_to_markdown(
issues_with_metrics,
average_time_to_first_response,
average_time_to_close,
average_time_to_answer,
num_issues_opened,
num_issues_closed,
)

# 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 | Value |\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 |\n"
"| --- | --- |\n"
"| Issue 1 | https://github.com/user/repo/issues/1 |\n"
"| Issue 2 | https://github.com/user/repo/issues/2 |\n"
)
self.assertEqual(content, expected_content)
os.remove("issue_metrics.md")

0 comments on commit f8c1690

Please sign in to comment.