| 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
|
|
|
|
|