| from __future__ import annotations | |
| from datetime import datetime | |
| from sqlalchemy.orm import Session, joinedload | |
| from app.models import Activity, Submission, Task, User | |
| def get_activity_with_group_context(db: Session, activity_id: int) -> Activity | None: | |
| return ( | |
| db.query(Activity) | |
| .options( | |
| joinedload(Activity.tasks) | |
| .joinedload(Task.submissions) | |
| .joinedload(Submission.user) | |
| .joinedload(User.group), | |
| joinedload(Activity.tasks) | |
| .joinedload(Task.submissions) | |
| .joinedload(Submission.group), | |
| joinedload(Activity.tasks) | |
| .joinedload(Task.submissions) | |
| .joinedload(Submission.reviewed_by), | |
| joinedload(Activity.tasks) | |
| .joinedload(Task.submissions) | |
| .joinedload(Submission.assigned_admin), | |
| ) | |
| .filter(Activity.id == activity_id) | |
| .first() | |
| ) | |
| def pick_primary_submission(submissions: list[Submission]) -> Submission | None: | |
| if not submissions: | |
| return None | |
| approved = [submission for submission in submissions if submission.status == "approved"] | |
| if approved: | |
| return min( | |
| approved, | |
| key=lambda item: ((item.approved_at or item.created_at or datetime.min), item.id), | |
| ) | |
| pending = [submission for submission in submissions if submission.status == "pending"] | |
| if pending: | |
| return max( | |
| pending, | |
| key=lambda item: ((item.created_at or datetime.min), item.id), | |
| ) | |
| rejected = [submission for submission in submissions if submission.status == "rejected"] | |
| if rejected: | |
| return max( | |
| rejected, | |
| key=lambda item: ((item.created_at or datetime.min), item.id), | |
| ) | |
| return max( | |
| submissions, | |
| key=lambda item: ((item.created_at or datetime.min), item.id), | |
| ) | |
| def build_group_submission_map(activity: Activity, group_id: int | None) -> dict[int, Submission]: | |
| if not activity or not group_id: | |
| return {} | |
| group_submissions: dict[int, Submission] = {} | |
| for task in activity.tasks: | |
| task_submissions = [submission for submission in task.submissions if submission.group_id == group_id] | |
| primary_submission = pick_primary_submission(task_submissions) | |
| if primary_submission: | |
| group_submissions[task.id] = primary_submission | |
| return group_submissions | |
| def build_group_progress(activity: Activity, group_id: int | None) -> dict: | |
| total_tasks = len(activity.tasks) if activity else 0 | |
| if not activity or not group_id: | |
| return { | |
| "total_tasks": total_tasks, | |
| "approved_count": 0, | |
| "pending_count": 0, | |
| "rejected_count": 0, | |
| "completion_ratio": 0, | |
| } | |
| submission_map = build_group_submission_map(activity, group_id) | |
| approved_count = sum(1 for submission in submission_map.values() if submission.status == "approved") | |
| pending_count = sum(1 for submission in submission_map.values() if submission.status == "pending") | |
| rejected_count = sum(1 for submission in submission_map.values() if submission.status == "rejected") | |
| return { | |
| "total_tasks": total_tasks, | |
| "approved_count": approved_count, | |
| "pending_count": pending_count, | |
| "rejected_count": rejected_count, | |
| "completion_ratio": 0 if total_tasks == 0 else approved_count / total_tasks, | |
| } | |