|
2 | 2 | from collections import defaultdict |
3 | 3 |
|
4 | 4 | from django.contrib.sites.models import Site |
| 5 | +from django.db import models |
5 | 6 |
|
6 | 7 | from ietf.group.models import Group, Role |
7 | 8 | from ietf.doc.models import Document, DocEvent, State, LastCallDocEvent |
8 | 9 | from ietf.iesg.models import TelechatDate |
9 | 10 | from ietf.person.models import Person |
10 | 11 | from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream |
11 | | -from ietf.review.models import ReviewRequest, ReviewRequestStateName, ReviewTypeName |
| 12 | +from ietf.review.models import ReviewRequest, ReviewRequestStateName, ReviewTypeName, Reviewer |
12 | 13 | from ietf.utils.mail import send_mail |
13 | 14 | from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs |
14 | 15 |
|
@@ -273,3 +274,60 @@ def extract_revision_ordered_review_requests_for_documents(queryset, names): |
273 | 274 | front = next_front |
274 | 275 |
|
275 | 276 | return res |
| 277 | + |
| 278 | +def construct_review_request_assignment_choices(possible_emails, team, review_req=None): |
| 279 | + possible_emails = list(possible_emails) |
| 280 | + |
| 281 | + reviewers = { r.person_id: r for r in Reviewer.objects.filter(team=team, person__in=[e.person_id for e in possible_emails]) } |
| 282 | + |
| 283 | + latest_assignment_for_reviewer = dict(ReviewRequest.objects.filter( |
| 284 | + reviewer__in=possible_emails, |
| 285 | + ).values_list("reviewer").annotate(models.Max("time"))) |
| 286 | + |
| 287 | + now = datetime.datetime.now() |
| 288 | + |
| 289 | + rankings = [] |
| 290 | + for e in possible_emails: |
| 291 | + reviewer = reviewers.get(e.person_id) |
| 292 | + if not reviewer: |
| 293 | + reviewer = Reviewer() |
| 294 | + |
| 295 | + days_past = None |
| 296 | + latest = latest_assignment_for_reviewer.get(e.pk) |
| 297 | + if latest is not None: |
| 298 | + days_past = (now - latest).days - reviewer.frequency |
| 299 | + |
| 300 | + # FIXME: |
| 301 | + # positive: (Perhaps do these separately? As initial values?) |
| 302 | + # has done review of previous rev |
| 303 | + # would like to review |
| 304 | + |
| 305 | + # blocks: |
| 306 | + # connections to doc + filter_re |
| 307 | + # has rejected same request/completed partial review |
| 308 | + # is unavailable_until |
| 309 | + |
| 310 | + if days_past is None: |
| 311 | + ready_for = "first time" |
| 312 | + else: |
| 313 | + d = int(round(days_past)) |
| 314 | + if d > 0: |
| 315 | + ready_for = "ready for {} {}".format(d, "day" if d == 1 else "days") |
| 316 | + else: |
| 317 | + d = -d |
| 318 | + ready_for = "frequency exceeded - ready in {} {}".format(d, "day" if d == 1 else "days") |
| 319 | + |
| 320 | + label = "{}: {}".format(e.person, ready_for) |
| 321 | + |
| 322 | + rank = (-100000 if days_past is None else -days_past,) |
| 323 | + |
| 324 | + rankings.append({ |
| 325 | + "email": e, |
| 326 | + "rank": rank, |
| 327 | + "label": label, |
| 328 | + }) |
| 329 | + |
| 330 | + rankings.sort(key=lambda r: r["rank"]) |
| 331 | + |
| 332 | + # FIXME: empty choices |
| 333 | + return [(r["email"].pk, r["label"]) for r in rankings] |
0 commit comments