Skip to content

Commit 17c6bd1

Browse files
authored
Merge pull request #612 from github/copilot/add-env-var-for-report
2 parents 7129e00 + 0ba0832 commit 17c6bd1

File tree

5 files changed

+190
-55
lines changed

5 files changed

+190
-55
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ This action can be configured to authenticate with GitHub App Installation or Pe
157157
| `HIDE_STATUS` | False | True | If set to `true`, the status column will not be shown |
158158
| `HIDE_CREATED_AT` | False | True | If set to `true`, the creation timestamp will not be displayed in the generated Markdown file. |
159159
| `HIDE_PR_STATISTICS` | False | True | If set to `true`, PR comment statistics (mean, median, 90th percentile, and individual PR comment counts) will not be displayed in the generated Markdown file. |
160+
| `HIDE_ITEMS_LIST` | False | False | If set to `true`, the individual items list table of individual pull requests, issues, and discussions will not be displayed in the generated Markdown file. Only the summary metrics will be shown. |
160161
| `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. |
161162
| `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. |
162163
| `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 |

config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class EnvVars:
5858
draft_pr_tracking (bool): If set to TRUE, track PR time in draft state
5959
in addition to other metrics
6060
hide_pr_statistics (bool): If set to TRUE, hide PR comment statistics in the output
61+
hide_items_list (bool): If set to TRUE, hide the list of individual items in the report
6162
"""
6263

6364
def __init__(
@@ -90,6 +91,7 @@ def __init__(
9091
rate_limit_bypass: bool = False,
9192
draft_pr_tracking: bool = False,
9293
hide_pr_statistics: bool = True,
94+
hide_items_list: bool = False,
9395
):
9496
self.gh_app_id = gh_app_id
9597
self.gh_app_installation_id = gh_app_installation_id
@@ -119,6 +121,7 @@ def __init__(
119121
self.rate_limit_bypass = rate_limit_bypass
120122
self.draft_pr_tracking = draft_pr_tracking
121123
self.hide_pr_statistics = hide_pr_statistics
124+
self.hide_items_list = hide_items_list
122125

123126
def __repr__(self):
124127
return (
@@ -151,6 +154,7 @@ def __repr__(self):
151154
f"{self.rate_limit_bypass}"
152155
f"{self.draft_pr_tracking}"
153156
f"{self.hide_pr_statistics}"
157+
f"{self.hide_items_list}"
154158
)
155159

156160

@@ -249,6 +253,7 @@ def get_env_vars(test: bool = False) -> EnvVars:
249253
hide_created_at = get_bool_env_var("HIDE_CREATED_AT", True)
250254
hide_status = get_bool_env_var("HIDE_STATUS", True)
251255
hide_pr_statistics = get_bool_env_var("HIDE_PR_STATISTICS", True)
256+
hide_items_list = get_bool_env_var("HIDE_ITEMS_LIST", False)
252257
enable_mentor_count = get_bool_env_var("ENABLE_MENTOR_COUNT", False)
253258
min_mentor_comments = os.getenv("MIN_MENTOR_COMMENTS", "10")
254259
max_comments_eval = os.getenv("MAX_COMMENTS_EVAL", "20")
@@ -284,4 +289,5 @@ def get_env_vars(test: bool = False) -> EnvVars:
284289
rate_limit_bypass,
285290
draft_pr_tracking,
286291
hide_pr_statistics,
292+
hide_items_list,
287293
)

markdown_writer.py

Lines changed: 60 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -189,68 +189,73 @@ def write_to_markdown(
189189
)
190190

191191
# Write second table with individual issue/pr/discussion metrics
192-
# First write the header
193-
file.write("|")
194-
for column in columns:
195-
file.write(f" {column} |")
196-
file.write("\n")
192+
# Skip this table if hide_items_list is True
193+
if not env_vars.hide_items_list:
194+
# First write the header
195+
file.write("|")
196+
for column in columns:
197+
file.write(f" {column} |")
198+
file.write("\n")
197199

198-
# Then write the column dividers
199-
file.write("|")
200-
for _ in columns:
201-
file.write(" --- |")
202-
file.write("\n")
200+
# Then write the column dividers
201+
file.write("|")
202+
for _ in columns:
203+
file.write(" --- |")
204+
file.write("\n")
203205

204-
# Then write the issues/pr/discussions row by row
205-
for issue in issues_with_metrics:
206-
# Replace the vertical bar with the HTML entity
207-
issue.title = issue.title.replace("|", "|")
208-
# Replace any whitespace
209-
issue.title = issue.title.strip()
206+
# Then write the issues/pr/discussions row by row
207+
for issue in issues_with_metrics:
208+
# Replace the vertical bar with the HTML entity
209+
issue.title = issue.title.replace("|", "|")
210+
# Replace any whitespace
211+
issue.title = issue.title.strip()
210212

211-
endpoint = ghe.removeprefix("https://") if ghe else "github.com"
212-
if non_mentioning_links:
213-
file.write(
214-
f"| {issue.title} | "
215-
f"{issue.html_url}".replace(
216-
f"https://{endpoint}", f"https://www.{endpoint}"
213+
endpoint = ghe.removeprefix("https://") if ghe else "github.com"
214+
if non_mentioning_links:
215+
file.write(
216+
f"| {issue.title} | "
217+
f"{issue.html_url}".replace(
218+
f"https://{endpoint}", f"https://www.{endpoint}"
219+
)
220+
+ " |"
217221
)
218-
+ " |"
219-
)
220-
else:
221-
file.write(f"| {issue.title} | {issue.html_url} |")
222-
if "Assignee" in columns:
223-
if issue.assignees:
224-
assignee_links = [
225-
f"[{assignee}](https://{endpoint}/{assignee})"
226-
for assignee in issue.assignees
227-
]
228-
file.write(f" {', '.join(assignee_links)} |")
229222
else:
230-
file.write(" None |")
231-
if "Author" in columns:
232-
file.write(f" [{issue.author}](https://{endpoint}/{issue.author}) |")
233-
if "Time to first response" in columns:
234-
file.write(f" {issue.time_to_first_response} |")
235-
if "Time to close" in columns:
236-
file.write(f" {issue.time_to_close} |")
237-
if "Time to answer" in columns:
238-
file.write(f" {issue.time_to_answer} |")
239-
if "Time in draft" in columns:
240-
file.write(f" {issue.time_in_draft} |")
241-
if labels and issue.label_metrics:
242-
for label in labels:
243-
if f"Time spent in {label}" in columns:
244-
file.write(f" {issue.label_metrics[label]} |")
245-
if "Created At" in columns:
246-
file.write(f" {issue.created_at} |")
247-
if "Status" in columns:
248-
file.write(f" {issue.status} |")
249-
if "PR Comments" in columns:
250-
file.write(f" {issue.pr_comment_count or 'N/A'} |")
223+
file.write(f"| {issue.title} | {issue.html_url} |")
224+
if "Assignee" in columns:
225+
if issue.assignees:
226+
assignee_links = [
227+
f"[{assignee}](https://{endpoint}/{assignee})"
228+
for assignee in issue.assignees
229+
]
230+
file.write(f" {', '.join(assignee_links)} |")
231+
else:
232+
file.write(" None |")
233+
if "Author" in columns:
234+
file.write(
235+
f" [{issue.author}](https://{endpoint}/{issue.author}) |"
236+
)
237+
if "Time to first response" in columns:
238+
file.write(f" {issue.time_to_first_response} |")
239+
if "Time to close" in columns:
240+
file.write(f" {issue.time_to_close} |")
241+
if "Time to answer" in columns:
242+
file.write(f" {issue.time_to_answer} |")
243+
if "Time in draft" in columns:
244+
file.write(f" {issue.time_in_draft} |")
245+
if labels and issue.label_metrics:
246+
for label in labels:
247+
if f"Time spent in {label}" in columns:
248+
file.write(f" {issue.label_metrics[label]} |")
249+
if "Created At" in columns:
250+
file.write(f" {issue.created_at} |")
251+
if "Status" in columns:
252+
file.write(f" {issue.status} |")
253+
if "PR Comments" in columns:
254+
file.write(f" {issue.pr_comment_count or 'N/A'} |")
255+
file.write("\n")
251256
file.write("\n")
252257
file.write(
253-
"\n_This report was generated with the \
258+
"_This report was generated with the \
254259
[Issue Metrics Action](https://github.com/github/issue-metrics)_\n"
255260
)
256261
if search_query:

test_config.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,20 @@ def test_get_env_vars_missing_query(self):
238238
with self.assertRaises(ValueError):
239239
get_env_vars(True)
240240

241+
@patch.dict(
242+
os.environ,
243+
{
244+
"GH_TOKEN": TOKEN,
245+
"SEARCH_QUERY": SEARCH_QUERY,
246+
"HIDE_ITEMS_LIST": "true",
247+
},
248+
clear=True,
249+
)
250+
def test_get_env_vars_hide_items_list(self):
251+
"""Test that HIDE_ITEMS_LIST environment variable is properly read."""
252+
result = get_env_vars(True)
253+
self.assertTrue(result.hide_items_list)
254+
241255
@patch.dict(
242256
os.environ,
243257
{
@@ -293,6 +307,7 @@ def test_get_env_vars_optional_values(self):
293307
rate_limit_bypass=True,
294308
draft_pr_tracking=True,
295309
hide_pr_statistics=True,
310+
hide_items_list=False,
296311
)
297312
result = get_env_vars(True)
298313
self.assertEqual(str(result), str(expected_result))
@@ -339,6 +354,7 @@ def test_get_env_vars_optionals_are_defaulted(self):
339354
rate_limit_bypass=False,
340355
draft_pr_tracking=False,
341356
hide_pr_statistics=True,
357+
hide_items_list=False,
342358
)
343359
result = get_env_vars(True)
344360
self.assertEqual(str(result), str(expected_result))

test_markdown_writer.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,113 @@ def test_writes_markdown_file_with_hidden_status_column(self):
531531
self.assertEqual(content, expected_content)
532532
os.remove("issue_metrics.md")
533533

534+
@patch.dict(
535+
os.environ,
536+
{
537+
"SEARCH_QUERY": "is:open repo:user/repo",
538+
"GH_TOKEN": "test_token",
539+
"HIDE_CREATED_AT": "False",
540+
"HIDE_TIME_TO_FIRST_RESPONSE": "True",
541+
"HIDE_TIME_TO_CLOSE": "True",
542+
"HIDE_TIME_TO_ANSWER": "True",
543+
"HIDE_LABEL_METRICS": "True",
544+
"NON_MENTIONING_LINKS": "True",
545+
"GH_ENTERPRISE_URL": "https://ghe.com",
546+
"HIDE_STATUS": "True", # Status column should be hidden
547+
"HIDE_ITEMS_LIST": "True", # Hide the items list table
548+
},
549+
)
550+
def test_writes_markdown_file_with_hidden_items_list(self):
551+
"""
552+
Test that write_to_markdown writes the correct markdown file
553+
when HIDE_ITEMS_LIST is set to True, ensuring the individual
554+
items table is not present in the output.
555+
"""
556+
# Create mock data
557+
issues_with_metrics = [
558+
IssueWithMetrics(
559+
title="Issue 1",
560+
html_url="https://ghe.com/user/repo/issues/1",
561+
author="alice",
562+
assignee="charlie",
563+
assignees=["charlie"],
564+
created_at=timedelta(days=-5),
565+
time_to_first_response=timedelta(minutes=10),
566+
time_to_close=timedelta(days=1),
567+
time_to_answer=timedelta(hours=2),
568+
time_in_draft=timedelta(days=1),
569+
labels_metrics={
570+
"label1": timedelta(days=1),
571+
},
572+
),
573+
IssueWithMetrics(
574+
title="Issue 2",
575+
html_url="https://ghe.com/user/repo/issues/2",
576+
author="bob",
577+
assignee=None,
578+
assignees=[],
579+
created_at=timedelta(days=-5),
580+
time_to_first_response=timedelta(minutes=20),
581+
time_to_close=timedelta(days=2),
582+
time_to_answer=timedelta(hours=4),
583+
labels_metrics={
584+
"label1": timedelta(days=1),
585+
},
586+
),
587+
]
588+
average_time_to_first_response = timedelta(minutes=15)
589+
average_time_to_close = timedelta(days=1.5)
590+
average_time_to_answer = timedelta(hours=3)
591+
average_time_in_draft = timedelta(days=1)
592+
average_time_in_labels = {
593+
"label1": timedelta(days=1),
594+
}
595+
num_issues_opened = 2
596+
num_issues_closed = 2
597+
num_mentor_count = 5
598+
ghe = "https://ghe.com"
599+
600+
# Call the function
601+
write_to_markdown(
602+
issues_with_metrics=issues_with_metrics,
603+
average_time_to_first_response=average_time_to_first_response,
604+
average_time_to_close=average_time_to_close,
605+
average_time_to_answer=average_time_to_answer,
606+
average_time_in_draft=average_time_in_draft,
607+
average_time_in_labels=average_time_in_labels,
608+
stats_pr_comments=None,
609+
num_issues_opened=num_issues_opened,
610+
num_issues_closed=num_issues_closed,
611+
num_mentor_count=num_mentor_count,
612+
labels=["label1"],
613+
search_query="repo:user/repo is:issue",
614+
hide_label_metrics=True,
615+
hide_items_closed_count=True,
616+
enable_mentor_count=True,
617+
non_mentioning_links=True,
618+
report_title="Issue Metrics",
619+
output_file="issue_metrics.md",
620+
ghe=ghe,
621+
)
622+
623+
# Check that the function writes the correct markdown file
624+
with open("issue_metrics.md", "r", encoding="utf-8") as file:
625+
content = file.read()
626+
627+
# Expected content should not include the individual items table
628+
expected_content = (
629+
"# Issue Metrics\n\n"
630+
"| Metric | Count |\n"
631+
"| --- | ---: |\n"
632+
"| Number of items that remain open | 2 |\n"
633+
"| Number of most active mentors | 5 |\n"
634+
"| Total number of items created | 2 |\n\n"
635+
"_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_\n"
636+
"Search query used to find these items: `repo:user/repo is:issue`\n"
637+
)
638+
self.assertEqual(content, expected_content)
639+
os.remove("issue_metrics.md")
640+
534641

535642
if __name__ == "__main__":
536643
unittest.main()

0 commit comments

Comments
 (0)