File size: 3,106 Bytes
02a8414
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a9438f
 
 
 
 
 
 
 
 
 
02a8414
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a9438f
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
from __future__ import annotations

from sqlalchemy.orm import Session, joinedload

from app.models import Admin, Submission, Task
from app.services.presence import is_online
from app.web import local_now


def online_admins(db: Session) -> list[Admin]:
    now = local_now()
    admins = db.query(Admin).filter(Admin.is_active.is_(True)).order_by(Admin.id.asc()).all()
    return [admin for admin in admins if is_online(admin.last_seen_at, now)]


def rebalance_pending_reviews(db: Session, activity_id: int | None = None) -> list[Submission]:
    pending_query = (
        db.query(Submission)
        .options(
            joinedload(Submission.user),
            joinedload(Submission.group),
            joinedload(Submission.task).joinedload(Task.activity),
            joinedload(Submission.assigned_admin),
            joinedload(Submission.reviewed_by),
        )
        .filter(Submission.status == "pending")
        .order_by(Submission.created_at.asc(), Submission.id.asc())
    )
    if activity_id is not None:
        pending_query = pending_query.join(Submission.task).filter(Task.activity_id == activity_id)

    pending_submissions = pending_query.all()
    admins = online_admins(db)
    if not admins:
        changed = False
        for submission in pending_submissions:
            if submission.assigned_admin_id is None and submission.assigned_at is None:
                continue
            submission.assigned_admin_id = None
            submission.assigned_at = None
            changed = True
        if changed:
            db.commit()
            pending_submissions = pending_query.all()
        return pending_submissions

    now = local_now()
    active_ids = {admin.id for admin in admins}
    buckets: dict[int, list[Submission]] = {admin.id: [] for admin in admins}
    changed = False

    for submission in pending_submissions:
        if submission.assigned_admin_id in active_ids:
            buckets[submission.assigned_admin_id].append(submission)

    for submission in pending_submissions:
        if submission.assigned_admin_id in active_ids:
            continue
        target_admin = min(admins, key=lambda item: (len(buckets[item.id]), item.id))
        submission.assigned_admin_id = target_admin.id
        submission.assigned_at = now
        buckets[target_admin.id].append(submission)
        changed = True

    while True:
        source_admin = max(admins, key=lambda item: (len(buckets[item.id]), -item.id))
        target_admin = min(admins, key=lambda item: (len(buckets[item.id]), item.id))
        if len(buckets[source_admin.id]) - len(buckets[target_admin.id]) <= 1:
            break

        buckets[source_admin.id].sort(key=lambda item: (item.created_at, item.id))
        moved_submission = buckets[source_admin.id].pop()
        moved_submission.assigned_admin_id = target_admin.id
        moved_submission.assigned_at = now
        buckets[target_admin.id].append(moved_submission)
        changed = True

    if changed:
        db.commit()
        pending_submissions = pending_query.all()
    return pending_submissions