-
Notifications
You must be signed in to change notification settings - Fork 91
Expand file tree
/
Copy pathtime_in_draft.py
More file actions
134 lines (111 loc) · 4.88 KB
/
time_in_draft.py
File metadata and controls
134 lines (111 loc) · 4.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""
This module contains a function that measures the time a pull request has been in draft state.
"""
from datetime import datetime, timedelta
from typing import List, Union
import github3
import numpy
import pytz
from classes import IssueWithMetrics
def measure_time_in_draft(
issue: github3.issues.Issue,
pull_request: Union[github3.pulls.PullRequest, None] = None,
) -> Union[timedelta, None]:
"""If a pull request has had time in the draft state, return the cumulative amount of time it was in draft.
args:
issue (github3.issues.Issue): A GitHub issue which has been pre-qualified as a pull request.
pull_request (github3.pulls.PullRequest, optional): The pull request object.
returns:
Union[timedelta, None]: Total time the pull request has spent in draft state.
"""
events = list(issue.issue.events())
draft_start = None
total_draft_time = timedelta(0)
# Check if PR was initially created as draft
pr_created_at = None
try:
if pull_request is None:
pull_request = issue.issue.pull_request()
pr_created_at = issue.issue.created_at
# Look for ready_for_review events to determine if PR was initially draft
ready_for_review_events = []
convert_to_draft_events = []
for event in events:
if event.event == "ready_for_review":
ready_for_review_events.append(event)
elif event.event == "convert_to_draft":
convert_to_draft_events.append(event)
# If there are ready_for_review events, check if PR was initially draft
if ready_for_review_events:
first_ready_event = min(ready_for_review_events, key=lambda x: x.created_at)
prior_draft_events = [
e
for e in convert_to_draft_events
if e.created_at < first_ready_event.created_at
]
if not prior_draft_events:
# PR was initially created as draft, calculate time from creation to first ready_for_review
total_draft_time += first_ready_event.created_at - pr_created_at
# If there are no ready_for_review events but the PR is currently draft, it might be initially draft and still open
elif not ready_for_review_events and not convert_to_draft_events:
# Check if PR is currently draft and open
if (
hasattr(pull_request, "draft")
and pull_request.draft
and issue.issue.state == "open"
):
# PR was initially created as draft and is still draft
draft_start = pr_created_at
except (AttributeError, ValueError, TypeError):
# If we can't get PR info, fall back to original logic
pass
for event in events:
if event.event == "convert_to_draft":
draft_start = event.created_at
elif event.event == "ready_for_review" and draft_start:
# Calculate draft time for this interval
total_draft_time += event.created_at - draft_start
draft_start = None
# If the PR is currently in draft state, calculate the time in draft up to now
if draft_start and issue.issue.state == "open":
total_draft_time += datetime.now(pytz.utc) - draft_start
# Round to the nearest second
return (
timedelta(seconds=round(total_draft_time.total_seconds()))
if total_draft_time > timedelta(0)
else None
)
def get_stats_time_in_draft(
issues_with_metrics: List[IssueWithMetrics],
) -> Union[dict[str, timedelta], None]:
"""
Calculate stats describing the time in draft for a list of issues.
"""
# Filter out issues with no time in draft
issues_with_time_to_draft = [
issue for issue in issues_with_metrics if issue.time_in_draft is not None
]
# Calculate the total time in draft for all issues
draft_times = []
if issues_with_time_to_draft:
for issue in issues_with_time_to_draft:
if issue.time_in_draft:
draft_times.append(issue.time_in_draft.total_seconds())
# Calculate stats describing time in draft
num_issues_with_time_in_draft = len(issues_with_time_to_draft)
if num_issues_with_time_in_draft > 0:
average_time_in_draft = numpy.round(numpy.average(draft_times))
med_time_in_draft = numpy.round(numpy.median(draft_times))
ninety_percentile_time_in_draft = numpy.round(
numpy.percentile(draft_times, 90, axis=0)
)
else:
return None
stats = {
"avg": timedelta(seconds=average_time_in_draft),
"med": timedelta(seconds=med_time_in_draft),
"90p": timedelta(seconds=ninety_percentile_time_in_draft),
}
# Print the average time in draft converting seconds to a readable time format
print(f"Average time in draft: {timedelta(seconds=average_time_in_draft)}")
return stats