forked from github/issue-metrics
-
Notifications
You must be signed in to change notification settings - Fork 0
/
labels.py
135 lines (114 loc) · 4.89 KB
/
labels.py
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
""" Functions for calculating time spent in labels. """
from datetime import datetime, timedelta
from typing import List
import github3
import numpy
import pytz
from classes import IssueWithMetrics
def get_label_events(
issue: github3.issues.Issue, labels: List[str] # type: ignore
) -> List[github3.issues.event]: # type: ignore
"""
Get the label events for a given issue if the label is of interest.
Args:
issue (github3.issues.Issue): A GitHub issue.
labels (List[str]): A list of labels of interest.
Returns:
List[github3.issues.event]: A list of label events for the given issue.
"""
label_events = []
for event in issue.issue.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: github3.issues.Issue, labels: List[str]) -> dict:
"""
Calculate the time spent with the given labels on a given issue.
Args:
issue (github3.issues.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)
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:
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 - datetime.fromisoformat(issue.created_at)
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 - datetime.fromisoformat(issue.created_at)
for label in labels:
# if the label is still on there, add the time from the last event to now
if label in labeled and label not in unlabeled:
# if the issue is closed, add the time from the issue creation to the closed_at time
if issue.state == "closed":
label_metrics[label] += datetime.fromisoformat(
issue.closed_at
) - datetime.fromisoformat(issue.created_at)
else:
# if the issue is open, add the time from the issue creation to now
label_metrics[label] += datetime.now(pytz.utc) - datetime.fromisoformat(
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