Repository navigation
Expand file tree
/
Copy pathlabels.py
More file actions
138 lines (114 loc) · 5.06 KB
/
Copy pathlabels.py
File metadata and controls
138 lines (114 loc) · 5.06 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
135
136
137
138
"""Functions for calculating time spent in labels."""
from datetime import datetime, timedelta
import numpy
import pytz
from github.Issue import Issue
from classes import IssueWithMetrics
def get_label_events(issue: Issue, labels: list[str]) -> list:
"""
Get the label events for a given issue if the label is of interest.
Args:
issue (Issue): A GitHub issue.
labels (List[str]): A list of labels of interest.
Returns:
list: A list of label events for the given issue.
"""
label_events = []
for event in issue.get_events():
if event.event in ("labeled", "unlabeled") and event.label.name in labels:
label_events.append(event)
return label_events
def get_label_metrics(issue: Issue, labels: list[str]) -> dict:
"""
Calculate the time spent with the given labels on a given issue.
Args:
issue (Issue): A GitHub issue.
labels (List[str]): A list of labels to measure time spent in.
Returns:
dict: A dictionary containing the time spent in each label or None.
"""
label_metrics: dict = {}
label_events = get_label_events(issue, labels)
label_last_event_type: dict = {}
for label in labels:
label_metrics[label] = None
# If the event is one of the labels we're looking for, add the time to the dictionary
unlabeled = {}
labeled = {}
if not label_events:
return label_metrics
# Calculate the time to add or subtract to the time spent in label based on the label events
for event in label_events:
# Skip labeling events that have occurred past issue close time
if issue.closed_at is not None and (event.created_at >= issue.closed_at):
continue
if event.event == "labeled":
labeled[event.label.name] = True
if event.label.name in labels:
if label_metrics[event.label.name] is None:
label_metrics[event.label.name] = timedelta(0)
label_metrics[event.label.name] -= event.created_at - issue.created_at
label_last_event_type[event.label.name] = "labeled"
elif event.event == "unlabeled":
unlabeled[event.label.name] = True
if event.label.name in labels:
if label_metrics[event.label.name] is None:
label_metrics[event.label.name] = timedelta(0)
label_metrics[event.label.name] += event.created_at - issue.created_at
label_last_event_type[event.label.name] = "unlabeled"
for label in labels:
if label in labeled:
# if the issue is closed, add the time from the issue creation to the closed_at time
if issue.state == "closed":
# Only add the final (closed_at - created_at) span if the label was still applied at closure.
if label_last_event_type.get(label) != "labeled":
continue
label_metrics[label] += issue.closed_at - issue.created_at
else:
# skip label if last labeling event is 'unlabeled' and issue is still open
if label_last_event_type[label] == "unlabeled":
continue
# if the issue is open, add the time from the issue creation to now
label_metrics[label] += datetime.now(pytz.utc) - issue.created_at
return label_metrics
def get_stats_time_in_labels(
issues_with_metrics: list[IssueWithMetrics],
labels: dict[str, timedelta],
) -> dict[str, dict[str, timedelta | None]]:
"""Calculate stats describing time spent in each label."""
time_in_labels = {}
for issue in issues_with_metrics:
if issue.label_metrics:
for label in issue.label_metrics:
if issue.label_metrics[label] is None:
continue
if label not in time_in_labels:
time_in_labels[label] = [issue.label_metrics[label].total_seconds()]
else:
time_in_labels[label].append(
issue.label_metrics[label].total_seconds()
)
average_time_in_labels: dict[str, timedelta | None] = {}
med_time_in_labels: dict[str, timedelta | None] = {}
ninety_percentile_in_labels: dict[str, timedelta | None] = {}
for label, time_list in time_in_labels.items():
average_time_in_labels[label] = timedelta(
seconds=numpy.round(numpy.average(time_list))
)
med_time_in_labels[label] = timedelta(
seconds=numpy.round(numpy.median(time_list))
)
ninety_percentile_in_labels[label] = timedelta(
seconds=numpy.round(numpy.percentile(time_list, 90, axis=0))
)
for label in labels:
if label not in average_time_in_labels:
average_time_in_labels[label] = None
med_time_in_labels[label] = None
ninety_percentile_in_labels[label] = None
stats = {
"avg": average_time_in_labels,
"med": med_time_in_labels,
"90p": ninety_percentile_in_labels,
}
return stats