Skip to content

Commit 4860492

Browse files
authored
Merge pull request #610 from github/copilot/fix-draft-pr-metrics-issue
2 parents 3858628 + 9e50a55 commit 4860492

File tree

3 files changed

+58
-21
lines changed

3 files changed

+58
-21
lines changed

issue_metrics.py

100644100755
File mode changed.

test_time_in_draft.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_time_in_draft_with_ready_for_review(self):
2828
"""
2929
self.issue.issue.events.return_value = [
3030
MagicMock(
31-
event="converted_to_draft",
31+
event="convert_to_draft",
3232
created_at=datetime(2021, 1, 1, tzinfo=pytz.utc),
3333
),
3434
MagicMock(
@@ -46,7 +46,7 @@ def test_time_in_draft_without_ready_for_review(self):
4646
"""
4747
self.issue.issue.events.return_value = [
4848
MagicMock(
49-
event="converted_to_draft",
49+
event="convert_to_draft",
5050
created_at=datetime(2021, 1, 1, tzinfo=pytz.utc),
5151
),
5252
]
@@ -63,15 +63,15 @@ def test_time_in_draft_multiple_intervals(self):
6363
"""
6464
self.issue.issue.events.return_value = [
6565
MagicMock(
66-
event="converted_to_draft",
66+
event="convert_to_draft",
6767
created_at=datetime(2021, 1, 1, tzinfo=pytz.utc),
6868
),
6969
MagicMock(
7070
event="ready_for_review",
7171
created_at=datetime(2021, 1, 3, tzinfo=pytz.utc),
7272
),
7373
MagicMock(
74-
event="converted_to_draft",
74+
event="convert_to_draft",
7575
created_at=datetime(2021, 1, 5, tzinfo=pytz.utc),
7676
),
7777
MagicMock(
@@ -89,7 +89,7 @@ def test_time_in_draft_ongoing_draft(self):
8989
"""
9090
self.issue.issue.events.return_value = [
9191
MagicMock(
92-
event="converted_to_draft",
92+
event="convert_to_draft",
9393
created_at=datetime(2021, 1, 1, tzinfo=pytz.utc),
9494
),
9595
]
@@ -117,7 +117,7 @@ def test_time_in_draft_without_ready_for_review_and_closed(self):
117117
"""
118118
self.issue.issue.events.return_value = [
119119
MagicMock(
120-
event="converted_to_draft",
120+
event="convert_to_draft",
121121
created_at=datetime(2021, 1, 1, tzinfo=pytz.utc),
122122
),
123123
]
@@ -133,9 +133,9 @@ def test_time_in_draft_initially_created_as_draft(self):
133133
Test measure_time_in_draft with a PR initially created as draft.
134134
"""
135135
# Set up issue created_at time
136-
self.issue.issue.created_at = "2021-01-01T00:00:00Z"
136+
self.issue.issue.created_at = datetime(2021, 1, 1, tzinfo=pytz.utc)
137137

138-
# Mock events with only ready_for_review (no converted_to_draft)
138+
# Mock events with only ready_for_review (no convert_to_draft)
139139
self.issue.issue.events.return_value = [
140140
MagicMock(
141141
event="ready_for_review",
@@ -159,7 +159,7 @@ def test_time_in_draft_initially_created_as_draft_still_open(self):
159159
Test measure_time_in_draft with a PR initially created as draft and still in draft.
160160
"""
161161
# Set up issue created_at time
162-
self.issue.issue.created_at = "2021-01-01T00:00:00Z"
162+
self.issue.issue.created_at = datetime(2021, 1, 1, tzinfo=pytz.utc)
163163

164164
# Mock events with no ready_for_review events (still draft)
165165
self.issue.issue.events.return_value = []
@@ -192,7 +192,7 @@ def test_time_in_draft_with_attribute_error_scenario(self):
192192
issue_search_result.issue.state = "open"
193193
issue_search_result.issue.events.return_value = [
194194
MagicMock(
195-
event="converted_to_draft",
195+
event="convert_to_draft",
196196
created_at=datetime(2021, 1, 1, tzinfo=pytz.utc),
197197
),
198198
]
@@ -204,6 +204,40 @@ def test_time_in_draft_with_attribute_error_scenario(self):
204204
expected = timedelta(days=3)
205205
self.assertEqual(result, expected, "The time in draft should be 3 days.")
206206

207+
def test_time_in_draft_with_iterator_events(self):
208+
"""
209+
Test measure_time_in_draft with events() returning an iterator instead of a list.
210+
This test ensures the function works correctly when events() returns an iterator
211+
(as it does in the real GitHub API), which can only be consumed once.
212+
"""
213+
# Set up issue created_at time
214+
self.issue.issue.created_at = datetime(2021, 1, 1, tzinfo=pytz.utc)
215+
216+
# Create an iterator of events (simulating real GitHub API behavior)
217+
def events_iterator():
218+
return iter(
219+
[
220+
MagicMock(
221+
event="convert_to_draft",
222+
created_at=datetime(2021, 1, 1, tzinfo=pytz.utc),
223+
),
224+
MagicMock(
225+
event="ready_for_review",
226+
created_at=datetime(2021, 1, 3, tzinfo=pytz.utc),
227+
),
228+
]
229+
)
230+
231+
self.issue.issue.events = events_iterator
232+
233+
result = measure_time_in_draft(self.issue)
234+
expected = timedelta(days=2)
235+
self.assertEqual(
236+
result,
237+
expected,
238+
"The time in draft should be 2 days when events() returns an iterator.",
239+
)
240+
207241

208242
class TestGetStatsTimeInDraft(unittest.TestCase):
209243
"""

time_in_draft.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def measure_time_in_draft(
2424
returns:
2525
Union[timedelta, None]: Total time the pull request has spent in draft state.
2626
"""
27-
events = issue.issue.events()
27+
events = list(issue.issue.events())
2828
draft_start = None
2929
total_draft_time = timedelta(0)
3030

@@ -35,25 +35,23 @@ def measure_time_in_draft(
3535
if pull_request is None:
3636
pull_request = issue.issue.pull_request()
3737

38-
pr_created_at = datetime.fromisoformat(
39-
issue.issue.created_at.replace("Z", "+00:00")
40-
)
38+
pr_created_at = issue.issue.created_at
4139

4240
# Look for ready_for_review events to determine if PR was initially draft
4341
ready_for_review_events = []
44-
converted_to_draft_events = []
42+
convert_to_draft_events = []
4543
for event in events:
4644
if event.event == "ready_for_review":
4745
ready_for_review_events.append(event)
48-
elif event.event == "converted_to_draft":
49-
converted_to_draft_events.append(event)
46+
elif event.event == "convert_to_draft":
47+
convert_to_draft_events.append(event)
5048

5149
# If there are ready_for_review events, check if PR was initially draft
5250
if ready_for_review_events:
5351
first_ready_event = min(ready_for_review_events, key=lambda x: x.created_at)
5452
prior_draft_events = [
5553
e
56-
for e in converted_to_draft_events
54+
for e in convert_to_draft_events
5755
if e.created_at < first_ready_event.created_at
5856
]
5957

@@ -62,7 +60,7 @@ def measure_time_in_draft(
6260
total_draft_time += first_ready_event.created_at - pr_created_at
6361

6462
# If there are no ready_for_review events but the PR is currently draft, it might be initially draft and still open
65-
elif not ready_for_review_events and not converted_to_draft_events:
63+
elif not ready_for_review_events and not convert_to_draft_events:
6664
# Check if PR is currently draft and open
6765
if (
6866
hasattr(pull_request, "draft")
@@ -77,7 +75,7 @@ def measure_time_in_draft(
7775
pass
7876

7977
for event in events:
80-
if event.event == "converted_to_draft":
78+
if event.event == "convert_to_draft":
8179
draft_start = event.created_at
8280
elif event.event == "ready_for_review" and draft_start:
8381
# Calculate draft time for this interval
@@ -88,7 +86,12 @@ def measure_time_in_draft(
8886
if draft_start and issue.issue.state == "open":
8987
total_draft_time += datetime.now(pytz.utc) - draft_start
9088

91-
return total_draft_time if total_draft_time > timedelta(0) else None
89+
# Round to the nearest second
90+
return (
91+
timedelta(seconds=round(total_draft_time.total_seconds()))
92+
if total_draft_time > timedelta(0)
93+
else None
94+
)
9295

9396

9497
def get_stats_time_in_draft(

0 commit comments

Comments
 (0)