Skip to content

Commit 3678d20

Browse files
committed
Some cleanup around stats. Things are well except for the time series graphs.
- Legacy-Id: 16035
1 parent 334767f commit 3678d20

4 files changed

Lines changed: 43 additions & 139 deletions

File tree

ietf/group/views.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
current_unavailable_periods_for_reviewers,
9999
email_reviewer_availability_change,
100100
reviewer_rotation_list,
101-
latest_review_requests_for_reviewers,
101+
latest_review_assignments_for_reviewers,
102102
augment_review_requests_with_events,
103103
get_default_filter_re,
104104
days_needed_to_fulfill_min_interval_for_reviewers,
@@ -107,7 +107,7 @@
107107

108108

109109

110-
from ietf.name.models import ReviewRequestStateName
110+
from ietf.name.models import ReviewAssignmentStateName
111111
from ietf.utils.mail import send_mail_text, parse_preformatted
112112

113113
from ietf.ietfauth.utils import user_is_person
@@ -1361,8 +1361,8 @@ def reviewer_overview(request, acronym, group_type=None):
13611361

13621362
today = datetime.date.today()
13631363

1364-
req_data_for_reviewers = latest_review_requests_for_reviewers(group)
1365-
review_state_by_slug = { n.slug: n for n in ReviewRequestStateName.objects.all() }
1364+
req_data_for_reviewers = latest_review_assignments_for_reviewers(group)
1365+
assignment_state_by_slug = { n.slug: n for n in ReviewAssignmentStateName.objects.all() }
13661366

13671367
days_needed = days_needed_to_fulfill_min_interval_for_reviewers(group)
13681368

@@ -1383,14 +1383,15 @@ def reviewer_overview(request, acronym, group_type=None):
13831383
person.busy = person.id in days_needed
13841384

13851385

1386+
# TODO - What is this MAX_CLOSED_REQS trying to accomplish?
13861387
MAX_CLOSED_REQS = 10
13871388
req_data = req_data_for_reviewers.get(person.pk, [])
1388-
open_reqs = sum(1 for d in req_data if d.state in ["requested", "accepted"])
1389+
open_reqs = sum(1 for d in req_data if d.state in ["assigned", "accepted"])
13891390
latest_reqs = []
13901391
for d in req_data:
1391-
if d.state in ["requested", "accepted"] or len(latest_reqs) < MAX_CLOSED_REQS + open_reqs:
1392-
latest_reqs.append((d.req_pk, d.doc, d.reviewed_rev, d.assigned_time, d.deadline,
1393-
review_state_by_slug.get(d.state),
1392+
if d.state in ["assigned", "accepted"] or len(latest_reqs) < MAX_CLOSED_REQS + open_reqs:
1393+
latest_reqs.append((d.assignment_pk, d.doc, d.reviewed_rev, d.assigned_time, d.deadline,
1394+
assignment_state_by_slug.get(d.state),
13941395
int(math.ceil(d.assignment_to_closure_days)) if d.assignment_to_closure_days is not None else None))
13951396
person.latest_reqs = latest_reqs
13961397

ietf/review/utils.py

Lines changed: 9 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,6 @@ def days_needed_to_fulfill_min_interval_for_reviewers(team):
176176
"late_days",
177177
"request_to_assignment_days", "assignment_to_closure_days", "request_to_closure_days"])
178178

179-
# TODO - see if this becomes dead
180-
ReviewRequestData = namedtuple("ReviewRequestData", [
181-
"req_pk", "doc", "doc_pages", "req_time", "state", "assigned_time", "deadline", "reviewed_rev", "result", "team", "reviewer",
182-
"late_days",
183-
"request_to_assignment_days", "assignment_to_closure_days", "request_to_closure_days"])
184179

185180
def extract_review_assignment_data(teams=None, reviewers=None, time_from=None, time_to=None, ordering=[]):
186181
"""Yield data on each review assignment, sorted by (*ordering, assigned_on)
@@ -208,7 +203,7 @@ def extract_review_assignment_data(teams=None, reviewers=None, time_from=None, t
208203
"reviewer__person", "assigned_on", "completed_on"
209204
)
210205

211-
event_qs = event_qs.order_by(*[o.replace("reviewer", "reviewer__person") for o in ordering] + ["assigned_on", "pk", "completed_on"])
206+
event_qs = event_qs.order_by(*[o.replace("reviewer", "reviewer__person").replace("team","review_request__team") for o in ordering] + ["assigned_on", "pk", "completed_on"])
212207

213208
def positive_days(time_from, time_to):
214209
if time_from is None or time_to is None:
@@ -242,83 +237,19 @@ def positive_days(time_from, time_to):
242237

243238
yield d
244239

245-
# TODO - see if this is dead code
246-
def extract_review_request_data(teams=None, reviewers=None, time_from=None, time_to=None, ordering=[]):
247-
"""Yield data on each review request, sorted by (*ordering, time)
248-
for easy use with itertools.groupby. Valid entries in *ordering are "team" and "reviewer"."""
249-
250-
filters = Q()
251-
252-
if teams:
253-
filters &= Q(team__in=teams)
254-
255-
if reviewers:
256-
filters &= Q(reviewer__person__in=reviewers)
257-
258-
if time_from:
259-
filters &= Q(time__gte=time_from)
260-
261-
if time_to:
262-
filters &= Q(time__lte=time_to)
263-
264-
# we may be dealing with a big bunch of data, so treat it carefully
265-
event_qs = ReviewRequest.objects.filter(filters)
266-
267-
# left outer join with RequestRequestDocEvent for request/assign/close time
268-
event_qs = event_qs.values_list(
269-
"pk", "doc", "doc__pages", "time", "state", "deadline", "reviewassignment__reviewed_rev", "reviewassignment__result", "team",
270-
"reviewassignment__reviewer__person", "reviewrequestdocevent__time", "reviewrequestdocevent__type"
271-
)
272-
273-
event_qs = event_qs.order_by(*[o.replace("reviewer", "reviewassignment__reviewer__person") for o in ordering] + ["time", "pk", "-reviewrequestdocevent__time"])
274-
275-
def positive_days(time_from, time_to):
276-
if time_from is None or time_to is None:
277-
return None
278-
279-
delta = time_to - time_from
280-
seconds = delta.total_seconds()
281-
if seconds > 0:
282-
return seconds / float(24 * 60 * 60)
283-
else:
284-
return 0.0
285-
286-
for _, events in itertools.groupby(event_qs.iterator(), lambda t: t[0]):
287-
requested_time = assigned_time = closed_time = None
288-
289-
for e in events:
290-
req_pk, doc, doc_pages, req_time, state, deadline, reviewed_rev, result, team, reviewer, event_time, event_type = e
291-
292-
if event_type == "requested_review" and requested_time is None:
293-
requested_time = event_time
294-
elif event_type == "assigned_review_request" and assigned_time is None:
295-
assigned_time = event_time
296-
elif event_type == "closed_review_request" and closed_time is None:
297-
closed_time = event_time
298-
299-
late_days = positive_days(datetime.datetime.combine(deadline, datetime.time.max), closed_time)
300-
request_to_assignment_days = positive_days(requested_time, assigned_time)
301-
assignment_to_closure_days = positive_days(assigned_time, closed_time)
302-
request_to_closure_days = positive_days(requested_time, closed_time)
303-
304-
d = ReviewRequestData(req_pk, doc, doc_pages, req_time, state, assigned_time, deadline, reviewed_rev, result, team, reviewer,
305-
late_days, request_to_assignment_days, assignment_to_closure_days,
306-
request_to_closure_days)
307240

308-
yield d
309-
310-
def aggregate_raw_period_review_request_stats(review_request_data, count=None):
241+
def aggregate_raw_period_review_assignment_stats(review_assignment_data, count=None):
311242
"""Take a sequence of review request data from
312-
extract_review_request_data and aggregate them."""
243+
extract_review_assignment_data and aggregate them."""
313244

314245
state_dict = defaultdict(int)
315246
late_state_dict = defaultdict(int)
316247
result_dict = defaultdict(int)
317248
assignment_to_closure_days_list = []
318249
assignment_to_closure_days_count = 0
319250

320-
for (req_pk, doc, doc_pages, req_time, state, assigned_time, deadline, reviewed_rev, result, team, reviewer,
321-
late_days, request_to_assignment_days, assignment_to_closure_days, request_to_closure_days) in review_request_data:
251+
for (assignment_pk, doc, doc_pages, req_time, state, assigned_time, deadline, reviewed_rev, result, team, reviewer,
252+
late_days, request_to_assignment_days, assignment_to_closure_days, request_to_closure_days) in review_assignment_data:
322253
if count == "pages":
323254
c = doc_pages
324255
else:
@@ -337,19 +268,19 @@ def aggregate_raw_period_review_request_stats(review_request_data, count=None):
337268

338269
return state_dict, late_state_dict, result_dict, assignment_to_closure_days_list, assignment_to_closure_days_count
339270

340-
def sum_period_review_request_stats(raw_aggregation):
271+
def sum_period_review_assignment_stats(raw_aggregation):
341272
"""Compute statistics from aggregated review request data for one aggregation point."""
342273
state_dict, late_state_dict, result_dict, assignment_to_closure_days_list, assignment_to_closure_days_count = raw_aggregation
343274

344275
res = {}
345276
res["state"] = state_dict
346277
res["result"] = result_dict
347278

348-
res["open"] = sum(state_dict.get(s, 0) for s in ("requested", "accepted"))
279+
res["open"] = sum(state_dict.get(s, 0) for s in ("assigned", "accepted"))
349280
res["completed"] = sum(state_dict.get(s, 0) for s in ("completed", "part-completed"))
350281
res["not_completed"] = sum(state_dict.get(s, 0) for s in state_dict if s in ("rejected", "withdrawn", "overtaken", "no-response"))
351282

352-
res["open_late"] = sum(late_state_dict.get(s, 0) for s in ("requested", "accepted"))
283+
res["open_late"] = sum(late_state_dict.get(s, 0) for s in ("assigned", "accepted"))
353284
res["open_in_time"] = res["open"] - res["open_late"]
354285
res["completed_late"] = sum(late_state_dict.get(s, 0) for s in ("completed", "part-completed"))
355286
res["completed_in_time"] = res["completed"] - res["completed_late"]
@@ -358,7 +289,7 @@ def sum_period_review_request_stats(raw_aggregation):
358289

359290
return res
360291

361-
def sum_raw_review_request_aggregations(raw_aggregations):
292+
def sum_raw_review_assignment_aggregations(raw_aggregations):
362293
"""Collapse a sequence of aggregations into one aggregation."""
363294
state_dict = defaultdict(int)
364295
late_state_dict = defaultdict(int)
@@ -397,36 +328,6 @@ def latest_review_assignments_for_reviewers(team, days_back=365):
397328

398329
return assignment_data_for_reviewers
399330

400-
# TODO - see if this is dead code
401-
def latest_review_requests_for_reviewers(team, days_back=365):
402-
"""Collect and return stats for reviewers on latest requests, in
403-
extract_review_request_data format."""
404-
405-
extracted_data = extract_review_request_data(
406-
teams=[team],
407-
time_from=datetime.date.today() - datetime.timedelta(days=days_back),
408-
ordering=["reviewer"],
409-
)
410-
411-
req_data_for_reviewers = {
412-
reviewer: list(reversed(list(req_data_items)))
413-
for reviewer, req_data_items in itertools.groupby(extracted_data, key=lambda data: data.reviewer)
414-
}
415-
416-
return req_data_for_reviewers
417-
418-
def make_new_review_request_from_existing(review_req):
419-
obj = ReviewRequest()
420-
obj.time = review_req.time
421-
obj.type = review_req.type
422-
obj.doc = review_req.doc
423-
obj.team = review_req.team
424-
obj.deadline = review_req.deadline
425-
obj.requested_rev = review_req.requested_rev
426-
obj.requested_by = review_req.requested_by
427-
obj.state = ReviewRequestStateName.objects.get(slug="requested")
428-
return obj
429-
430331
def email_review_assignment_change(request, review_assignment, subject, msg, by, notify_secretary, notify_reviewer, notify_requested_by):
431332

432333
system_email = Person.objects.get(name="(System)").formatted_email()

ietf/stats/tests.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from pyquery import PyQuery
55
from requests import Response
66

7+
import debug # pyflakes:ignore
8+
79
from django.urls import reverse as urlreverse
810
from django.contrib.auth.models import User
911

@@ -157,8 +159,8 @@ def test_known_country_list(self):
157159

158160
def test_review_stats(self):
159161
reviewer = PersonFactory()
160-
review_req = ReviewRequestFactory()
161-
ReviewAssignmentFactory(review_request=review_req, reviewer=reviewer.email_set.first())
162+
review_req = ReviewRequestFactory(state_id='assigned')
163+
ReviewAssignmentFactory(review_request=review_req, state_id='assigned', reviewer=reviewer.email_set.first())
162164
RoleFactory(group=review_req.team,name_id='reviewer',person=reviewer)
163165
ReviewerSettingsFactory(team=review_req.team, person=reviewer)
164166
PersonFactory(user__username='plain')

ietf/stats/views.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818

1919
import debug # pyflakes:ignore
2020

21-
from ietf.review.utils import (extract_review_request_data,
22-
aggregate_raw_period_review_request_stats,
23-
ReviewRequestData,
24-
sum_period_review_request_stats,
25-
sum_raw_review_request_aggregations)
21+
from ietf.review.utils import (extract_review_assignment_data,
22+
aggregate_raw_period_review_assignment_stats,
23+
ReviewAssignmentData,
24+
sum_period_review_assignment_stats,
25+
sum_raw_review_assignment_aggregations)
2626
from ietf.submit.models import Submission
2727
from ietf.group.models import Role, Group
2828
from ietf.person.models import Person
29-
from ietf.name.models import ReviewRequestStateName, ReviewResultName, CountryName, DocRelationshipName
29+
from ietf.name.models import ReviewResultName, CountryName, DocRelationshipName, ReviewAssignmentStateName
3030
from ietf.person.name import plain_name
3131
from ietf.doc.models import DocAlias, Document, State, DocEvent
3232
from ietf.meeting.models import Meeting
@@ -1091,7 +1091,7 @@ def parse_date(s):
10911091
query_reviewers = None
10921092

10931093
group_by_objs = { t.pk: t for t in query_teams }
1094-
group_by_index = ReviewRequestData._fields.index("team")
1094+
group_by_index = ReviewAssignmentData._fields.index("team")
10951095

10961096
elif level == "reviewer":
10971097
for t in teams:
@@ -1102,17 +1102,17 @@ def parse_date(s):
11021102
return HttpResponseRedirect(urlreverse(review_stats))
11031103

11041104
query_reviewers = list(Person.objects.filter(
1105-
email__reviewrequest__time__gte=from_time,
1106-
email__reviewrequest__time__lte=to_time,
1107-
email__reviewrequest__team=reviewers_for_team,
1105+
email__reviewassignment__review_request__time__gte=from_time,
1106+
email__reviewassignment__review_request__time__lte=to_time,
1107+
email__reviewassignment__review_request__team=reviewers_for_team,
11081108
**reviewer_filter_args.get(t.pk, {})
11091109
).distinct())
11101110
query_reviewers.sort(key=lambda p: p.last_name())
11111111

11121112
query_teams = [t]
11131113

11141114
group_by_objs = { r.pk: r for r in query_reviewers }
1115-
group_by_index = ReviewRequestData._fields.index("reviewer")
1115+
group_by_index = ReviewAssignmentData._fields.index("reviewer")
11161116

11171117
# now filter and aggregate the data
11181118
possible_teams = possible_completion_types = possible_results = possible_states = None
@@ -1136,9 +1136,9 @@ def add_if_exists_else_subtract(element, l):
11361136
)
11371137
query_teams = [t for t in query_teams if t.acronym in selected_teams]
11381138

1139-
extracted_data = extract_review_request_data(query_teams, query_reviewers, from_time, to_time)
1139+
extracted_data = extract_review_assignment_data(query_teams, query_reviewers, from_time, to_time)
11401140

1141-
req_time_index = ReviewRequestData._fields.index("req_time")
1141+
req_time_index = ReviewAssignmentData._fields.index("req_time")
11421142

11431143
def time_key_fn(t):
11441144
d = t[req_time_index].date()
@@ -1150,8 +1150,8 @@ def time_key_fn(t):
11501150
found_states = set()
11511151
aggrs = []
11521152
for d, request_data_items in itertools.groupby(extracted_data, key=time_key_fn):
1153-
raw_aggr = aggregate_raw_period_review_request_stats(request_data_items, count=count)
1154-
aggr = sum_period_review_request_stats(raw_aggr)
1153+
raw_aggr = aggregate_raw_period_review_assignment_stats(request_data_items, count=count)
1154+
aggr = sum_period_review_assignment_stats(raw_aggr)
11551155

11561156
aggrs.append((d, aggr))
11571157

@@ -1161,7 +1161,7 @@ def time_key_fn(t):
11611161
found_states.add(slug)
11621162

11631163
results = ReviewResultName.objects.filter(slug__in=found_results)
1164-
states = ReviewRequestStateName.objects.filter(slug__in=found_states)
1164+
states = ReviewAssignmentStateName.objects.filter(slug__in=found_states)
11651165

11661166
# choice
11671167

@@ -1210,18 +1210,18 @@ def time_key_fn(t):
12101210
}])
12111211

12121212
else: # tabular data
1213-
extracted_data = extract_review_request_data(query_teams, query_reviewers, from_time, to_time, ordering=[level])
1213+
extracted_data = extract_review_assignment_data(query_teams, query_reviewers, from_time, to_time, ordering=[level])
12141214

12151215
data = []
12161216

12171217
found_results = set()
12181218
found_states = set()
12191219
raw_aggrs = []
12201220
for group_pk, request_data_items in itertools.groupby(extracted_data, key=lambda t: t[group_by_index]):
1221-
raw_aggr = aggregate_raw_period_review_request_stats(request_data_items, count=count)
1221+
raw_aggr = aggregate_raw_period_review_assignment_stats(request_data_items, count=count)
12221222
raw_aggrs.append(raw_aggr)
12231223

1224-
aggr = sum_period_review_request_stats(raw_aggr)
1224+
aggr = sum_period_review_assignment_stats(raw_aggr)
12251225

12261226
# skip zero-valued rows
12271227
if aggr["open"] == 0 and aggr["completed"] == 0 and aggr["not_completed"] == 0:
@@ -1238,12 +1238,12 @@ def time_key_fn(t):
12381238

12391239
# add totals row
12401240
if len(raw_aggrs) > 1:
1241-
totals = sum_period_review_request_stats(sum_raw_review_request_aggregations(raw_aggrs))
1241+
totals = sum_period_review_assignment_stats(sum_raw_review_assignment_aggregations(raw_aggrs))
12421242
totals["obj"] = "Totals"
12431243
data.append(totals)
12441244

12451245
results = ReviewResultName.objects.filter(slug__in=found_results)
1246-
states = ReviewRequestStateName.objects.filter(slug__in=found_states)
1246+
states = ReviewAssignmentStateName.objects.filter(slug__in=found_states)
12471247

12481248
# massage states/results breakdowns for template rendering
12491249
for aggr in data:

0 commit comments

Comments
 (0)