Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def __init__(
self.assignee = assignee
self.assignees = assignees or []
self.time_to_first_response = time_to_first_response
self.time_to_first_review = None
self.time_to_close = time_to_close
self.time_to_answer = time_to_answer
self.time_in_draft = time_in_draft
Expand Down
6 changes: 6 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ 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_time_to_first_review (bool): If true, the time to first review 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
Expand Down Expand Up @@ -79,6 +80,7 @@ def __init__(
hide_time_to_answer: bool,
hide_time_to_close: bool,
hide_time_to_first_response: bool,
hide_time_to_first_review: bool,
hide_created_at: bool,
hide_status: bool,
ignore_user: List[str],
Expand Down Expand Up @@ -114,6 +116,7 @@ def __init__(
self.hide_time_to_answer = hide_time_to_answer
self.hide_time_to_close = hide_time_to_close
self.hide_time_to_first_response = hide_time_to_first_response
self.hide_time_to_first_review = hide_time_to_first_review
self.hide_created_at = hide_created_at
self.hide_status = hide_status
self.enable_mentor_count = enable_mentor_count
Expand Down Expand Up @@ -148,6 +151,7 @@ def __repr__(self):
f"{self.hide_time_to_answer}, "
f"{self.hide_time_to_close}, "
f"{self.hide_time_to_first_response}, "
f"{self.hide_time_to_first_review}, "
f"{self.hide_created_at}, "
f"{self.hide_status}, "
f"{self.ignore_users}, "
Expand Down Expand Up @@ -269,6 +273,7 @@ def get_env_vars(test: bool = False) -> EnvVars:
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)
hide_time_to_first_response = get_bool_env_var("HIDE_TIME_TO_FIRST_RESPONSE", False)
hide_time_to_first_review = get_bool_env_var("HIDE_TIME_TO_FIRST_REVIEW", False)
hide_created_at = get_bool_env_var("HIDE_CREATED_AT", True)
hide_status = get_bool_env_var("HIDE_STATUS", True)
hide_pr_statistics = get_bool_env_var("HIDE_PR_STATISTICS", True)
Expand All @@ -293,6 +298,7 @@ def get_env_vars(test: bool = False) -> EnvVars:
hide_time_to_answer,
hide_time_to_close,
hide_time_to_first_response,
hide_time_to_first_review,
hide_created_at,
hide_status,
ignore_users_list,
Expand Down
17 changes: 16 additions & 1 deletion issue_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
get_stats_time_to_first_response,
measure_time_to_first_response,
)
from time_to_first_review import (
get_stats_time_to_first_review,
measure_time_to_first_review,
)
from time_to_merge import measure_time_to_merge
from time_to_ready_for_review import get_time_to_ready_for_review

Expand Down Expand Up @@ -159,7 +163,13 @@ def get_per_issue_metrics(
issue_with_metrics.pr_comment_count = count_pr_comments(
issue, pull_request, ignore_users
)

if not env_vars.hide_time_to_first_review and pull_request:
issue_with_metrics.time_to_first_review = measure_time_to_first_review(
issue,
pull_request,
ready_for_review_at,
ignore_users,
)
if env_vars.hide_time_to_first_response is False:
issue_with_metrics.time_to_first_response = (
measure_time_to_first_response(
Expand Down Expand Up @@ -305,6 +315,7 @@ def main(): # pragma: no cover
write_to_markdown(
issues_with_metrics=None,
average_time_to_first_response=None,
average_time_to_first_review=None,
average_time_to_close=None,
average_time_to_answer=None,
average_time_in_draft=None,
Expand Down Expand Up @@ -333,6 +344,7 @@ def main(): # pragma: no cover
write_to_markdown(
issues_with_metrics=None,
average_time_to_first_response=None,
average_time_to_first_review=None,
average_time_to_close=None,
average_time_to_answer=None,
average_time_in_draft=None,
Expand Down Expand Up @@ -365,6 +377,7 @@ def main(): # pragma: no cover
)

stats_time_to_first_response = get_stats_time_to_first_response(issues_with_metrics)
stats_time_to_first_review = get_stats_time_to_first_review(issues_with_metrics)
stats_time_to_close = None
if num_issues_closed > 0:
stats_time_to_close = get_stats_time_to_close(issues_with_metrics)
Expand All @@ -385,6 +398,7 @@ def main(): # pragma: no cover
write_to_json(
issues_with_metrics=issues_with_metrics,
stats_time_to_first_response=stats_time_to_first_response,
stats_time_to_first_review=stats_time_to_first_review,
stats_time_to_close=stats_time_to_close,
stats_time_to_answer=stats_time_to_answer,
stats_time_in_draft=stats_time_in_draft,
Expand All @@ -400,6 +414,7 @@ def main(): # pragma: no cover
write_to_markdown(
issues_with_metrics=issues_with_metrics,
average_time_to_first_response=stats_time_to_first_response,
average_time_to_first_review=stats_time_to_first_review,
average_time_to_close=stats_time_to_close,
average_time_to_answer=stats_time_to_answer,
average_time_in_draft=stats_time_in_draft,
Expand Down
15 changes: 15 additions & 0 deletions json_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
write_to_json(
issues_with_metrics: Union[List[IssueWithMetrics], None],
stats_time_to_first_response: Union[dict[str, timedelta], None],
stats_time_to_first_review: Union[dict[str, timedelta], None],
stats_time_to_close: Union[dict[str, timedelta], None],
stats_time_to_answer: Union[dict[str, timedelta], None],
stats_time_in_draft: Union[dict[str, timedelta], None],
Expand All @@ -29,6 +30,7 @@
def write_to_json(
issues_with_metrics: Union[List[IssueWithMetrics], None],
stats_time_to_first_response: Union[dict[str, timedelta], None],
stats_time_to_first_review: Union[dict[str, timedelta], None],
stats_time_to_close: Union[dict[str, timedelta], None],
stats_time_to_answer: Union[dict[str, timedelta], None],
stats_time_in_draft: Union[dict[str, timedelta], None],
Expand Down Expand Up @@ -104,6 +106,15 @@ def write_to_json(
med_time_to_first_response = stats_time_to_first_response["med"]
p90_time_to_first_response = stats_time_to_first_response["90p"]

# time to first review
average_time_to_first_review = None
med_time_to_first_review = None
p90_time_to_first_review = None
if stats_time_to_first_review is not None:
average_time_to_first_review = stats_time_to_first_review["avg"]
med_time_to_first_review = stats_time_to_first_review["med"]
p90_time_to_first_review = stats_time_to_first_review["90p"]

# time to close
average_time_to_close = None
med_time_to_close = None
Expand Down Expand Up @@ -155,16 +166,19 @@ def write_to_json(
# Create a dictionary with the metrics
metrics: dict[str, Any] = {
"average_time_to_first_response": str(average_time_to_first_response),
"average_time_to_first_review": str(average_time_to_first_review),
"average_time_to_close": str(average_time_to_close),
"average_time_to_answer": str(average_time_to_answer),
"average_time_in_draft": str(average_time_in_draft),
"average_time_in_labels": average_time_in_labels,
"median_time_to_first_response": str(med_time_to_first_response),
"median_time_to_first_review": str(med_time_to_first_review),
"median_time_to_close": str(med_time_to_close),
"median_time_to_answer": str(med_time_to_answer),
"median_time_in_draft": str(med_time_in_draft),
"median_time_in_labels": med_time_in_labels,
"90_percentile_time_to_first_response": str(p90_time_to_first_response),
"90_percentile_time_to_first_review": str(p90_time_to_first_review),
"90_percentile_time_to_close": str(p90_time_to_close),
"90_percentile_time_to_answer": str(p90_time_to_answer),
"90_percentile_time_in_draft": str(p90_time_in_draft),
Expand Down Expand Up @@ -193,6 +207,7 @@ def write_to_json(
"assignee": issue.assignee,
"assignees": issue.assignees,
"time_to_first_response": str(issue.time_to_first_response),
"time_to_first_review": str(issue.time_to_first_review),
"time_to_close": str(issue.time_to_close),
"time_to_answer": str(issue.time_to_answer),
"time_in_draft": str(issue.time_in_draft),
Expand Down
21 changes: 21 additions & 0 deletions markdown_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ def get_non_hidden_columns(labels) -> List[str]:
if not hide_time_to_first_response:
columns.append("Time to first response")

hide_time_to_first_review = env_vars.hide_time_to_first_review
if not hide_time_to_first_review:
columns.append("Time to first review")

hide_time_to_close = env_vars.hide_time_to_close
if not hide_time_to_close:
columns.append("Time to close")
Expand Down Expand Up @@ -129,6 +133,7 @@ def sort_issues(
valid_fields = {
"time_to_close",
"time_to_first_response",
"time_to_first_review",
"time_to_answer",
"time_in_draft",
"created_at",
Expand Down Expand Up @@ -200,6 +205,7 @@ def group_issues(
def write_to_markdown(
issues_with_metrics: Union[List[IssueWithMetrics], None],
average_time_to_first_response: Union[dict[str, timedelta], None],
average_time_to_first_review: Union[dict[str, timedelta], None],
average_time_to_close: Union[dict[str, timedelta], None],
average_time_to_answer: Union[dict[str, timedelta], None],
average_time_in_draft: Union[dict[str, timedelta], None],
Expand Down Expand Up @@ -268,6 +274,7 @@ def write_to_markdown(
write_overall_metrics_tables(
issues_with_metrics,
average_time_to_first_response,
average_time_to_first_review,
average_time_to_close,
average_time_to_answer,
average_time_in_draft,
Expand Down Expand Up @@ -345,6 +352,8 @@ def write_to_markdown(
)
if "Time to first response" in columns:
file.write(f" {issue.time_to_first_response} |")
if "Time to first review" in columns:
file.write(f" {issue.time_to_first_review} |")
if "Time to close" in columns:
file.write(f" {issue.time_to_close} |")
if "Time to answer" in columns:
Expand Down Expand Up @@ -374,6 +383,7 @@ def write_to_markdown(
def write_overall_metrics_tables(
issues_with_metrics,
stats_time_to_first_response,
stats_time_to_first_review,
stats_time_to_close,
stats_time_to_answer,
average_time_in_draft,
Expand All @@ -397,6 +407,7 @@ def write_overall_metrics_tables(
column in columns
for column in [
"Time to first response",
"Time to first review",
"Time to close",
"Time to answer",
"Time in draft",
Expand All @@ -417,6 +428,16 @@ def write_overall_metrics_tables(
)
else:
file.write("| Time to first response | None | None | None |\n")
if "Time to first review" in columns:
if stats_time_to_first_review is not None:
file.write(
f"| Time to first review "
f"| {stats_time_to_first_review['avg']} "
f"| {stats_time_to_first_review['med']} "
f"| {stats_time_to_first_review['90p']} |\n"
)
else:
file.write("| Time to first review | None | None | None |\n")
if "Time to close" in columns:
if stats_time_to_close is not None:
file.write(
Expand Down
2 changes: 2 additions & 0 deletions test_assignee_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def test_assignee_in_markdown_output(self):
try:
write_to_markdown(
issues_with_metrics=issues_with_metrics,
average_time_to_first_review=None,
average_time_to_first_response={
"avg": timedelta(hours=3),
"med": timedelta(hours=3),
Expand Down Expand Up @@ -132,6 +133,7 @@ def test_assignee_in_json_output(self):
try:
json_output = write_to_json(
issues_with_metrics=issues_with_metrics,
stats_time_to_first_review=None,
stats_time_to_first_response={
"avg": timedelta(hours=3),
"med": timedelta(hours=3),
Expand Down
5 changes: 3 additions & 2 deletions test_column_order_fix.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def test_status_and_created_at_columns_alignment(self):
write_to_markdown(
issues_with_metrics=issues_with_metrics,
average_time_to_first_response=None,
average_time_to_first_review=None,
average_time_to_close=None,
average_time_to_answer=None,
average_time_in_draft=None,
Expand All @@ -80,7 +81,7 @@ def test_status_and_created_at_columns_alignment(self):
# The table should have the columns in the correct order
# and the data should be properly aligned
expected_header = (
"| Title | URL | Assignee | Author | Time to first response | "
"| Title | URL | Assignee | Author | Time to first response | Time to first review | "
"Time to close | Time to answer | Created At | Status |"
)
self.assertIn(expected_header, content)
Expand All @@ -92,7 +93,7 @@ def test_status_and_created_at_columns_alignment(self):
"| Test Issue | https://github.com/user/repo/issues/1 | "
"[assignee1](https://github.com/assignee1) | "
"[testuser](https://github.com/testuser) | 1 day, 0:00:00 | "
"2 days, 0:00:00 | 3 days, 0:00:00 | 2023-01-01T00:00:00Z | open |"
"None | 2 days, 0:00:00 | 3 days, 0:00:00 | 2023-01-01T00:00:00Z | open |"
)
self.assertIn(expected_row, content)

Expand Down
4 changes: 4 additions & 0 deletions test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def test_get_env_vars_with_github_app(self):
hide_time_to_answer=False,
hide_time_to_close=False,
hide_time_to_first_response=False,
hide_time_to_first_review=False,
hide_created_at=True,
hide_status=True,
ignore_user=[],
Expand Down Expand Up @@ -187,6 +188,7 @@ def test_get_env_vars_with_token(self):
hide_time_to_answer=False,
hide_time_to_close=False,
hide_time_to_first_response=False,
hide_time_to_first_review=False,
hide_created_at=True,
hide_status=True,
ignore_user=[],
Expand Down Expand Up @@ -292,6 +294,7 @@ def test_get_env_vars_optional_values(self):
hide_time_to_answer=True,
hide_time_to_close=True,
hide_time_to_first_response=True,
hide_time_to_first_review=False,
hide_created_at=True,
hide_status=True,
ignore_user=[],
Expand Down Expand Up @@ -339,6 +342,7 @@ def test_get_env_vars_optionals_are_defaulted(self):
hide_time_to_answer=False,
hide_time_to_close=False,
hide_time_to_first_response=False,
hide_time_to_first_review=False,
hide_created_at=True,
hide_status=True,
ignore_user=[],
Expand Down
Loading
Loading