|
7 | 7 | from django.shortcuts import render, get_object_or_404, redirect |
8 | 8 | from django.contrib import messages |
9 | 9 | from django.contrib.auth.decorators import user_passes_test |
| 10 | +from django.utils import timezone |
10 | 11 |
|
11 | 12 | from courses.models import ( |
12 | 13 | Course, |
|
23 | 24 | ReviewCriteria, |
24 | 25 | ProjectEvaluationScore, |
25 | 26 | Enrollment, |
| 27 | + LeaderboardComplaint, |
26 | 28 | ) |
27 | 29 | from courses.scoring import ( |
28 | 30 | score_homework_submissions, |
@@ -109,13 +111,18 @@ def course_admin(request, course_slug): |
109 | 111 | "disabled_lip": enrollments.filter(disable_learning_in_public=True).count(), |
110 | 112 | "zero_score": enrollments.filter(total_score=0).count(), |
111 | 113 | "hidden_leaderboard": enrollments.filter(display_on_leaderboard=False).count(), |
| 114 | + "open_complaints": LeaderboardComplaint.objects.filter( |
| 115 | + enrollment__course=course, |
| 116 | + resolved=False, |
| 117 | + ).count(), |
112 | 118 | } |
113 | 119 | needs_attention_count = ( |
114 | 120 | len(homework_needing_score) |
115 | 121 | + len(projects_needing_reviews) |
116 | 122 | + len(projects_needing_score) |
117 | 123 | + support_metrics["disabled_lip"] |
118 | 124 | + support_metrics["hidden_leaderboard"] |
| 125 | + + support_metrics["open_complaints"] |
119 | 126 | ) |
120 | 127 | project_action_count = len(projects_needing_reviews) + len(projects_needing_score) |
121 | 128 |
|
@@ -620,6 +627,76 @@ def enrollments_list(request, course_slug): |
620 | 627 | return render(request, "cadmin/enrollments.html", context) |
621 | 628 |
|
622 | 629 |
|
| 630 | +@staff_required |
| 631 | +def leaderboard_complaints(request, course_slug): |
| 632 | + course = get_object_or_404(Course, slug=course_slug) |
| 633 | + |
| 634 | + enrollments = ( |
| 635 | + Enrollment.objects.filter(course=course) |
| 636 | + .select_related("student") |
| 637 | + .annotate( |
| 638 | + open_complaints=Count( |
| 639 | + "complaints", |
| 640 | + filter=Q(complaints__resolved=False), |
| 641 | + ), |
| 642 | + total_complaints=Count("complaints"), |
| 643 | + ) |
| 644 | + .filter(total_complaints__gt=0) |
| 645 | + .order_by("-open_complaints", "-total_complaints", "position_on_leaderboard") |
| 646 | + ) |
| 647 | + |
| 648 | + complaints_by_enrollment = defaultdict(list) |
| 649 | + complaints = ( |
| 650 | + LeaderboardComplaint.objects.filter(enrollment__course=course) |
| 651 | + .select_related("enrollment", "reporter", "resolved_by") |
| 652 | + .order_by("resolved", "-created_at") |
| 653 | + ) |
| 654 | + for complaint in complaints: |
| 655 | + complaints_by_enrollment[complaint.enrollment_id].append(complaint) |
| 656 | + |
| 657 | + enrollment_rows = [] |
| 658 | + for enrollment in enrollments: |
| 659 | + enrollment_rows.append( |
| 660 | + { |
| 661 | + "enrollment": enrollment, |
| 662 | + "complaints": complaints_by_enrollment[enrollment.id], |
| 663 | + } |
| 664 | + ) |
| 665 | + |
| 666 | + context = { |
| 667 | + "course": course, |
| 668 | + "enrollment_rows": enrollment_rows, |
| 669 | + "open_complaints_count": LeaderboardComplaint.objects.filter( |
| 670 | + enrollment__course=course, |
| 671 | + resolved=False, |
| 672 | + ).count(), |
| 673 | + "total_complaints_count": LeaderboardComplaint.objects.filter( |
| 674 | + enrollment__course=course, |
| 675 | + ).count(), |
| 676 | + } |
| 677 | + return render(request, "cadmin/leaderboard_complaints.html", context) |
| 678 | + |
| 679 | + |
| 680 | +@staff_required |
| 681 | +def leaderboard_complaint_resolve(request, course_slug, complaint_id): |
| 682 | + if request.method != "POST": |
| 683 | + return redirect("cadmin_leaderboard_complaints", course_slug=course_slug) |
| 684 | + |
| 685 | + course = get_object_or_404(Course, slug=course_slug) |
| 686 | + complaint = get_object_or_404( |
| 687 | + LeaderboardComplaint, |
| 688 | + id=complaint_id, |
| 689 | + enrollment__course=course, |
| 690 | + ) |
| 691 | + complaint.resolved = True |
| 692 | + complaint.resolved_at = timezone.now() |
| 693 | + complaint.resolved_by = request.user |
| 694 | + complaint.save(update_fields=["resolved", "resolved_at", "resolved_by"]) |
| 695 | + |
| 696 | + messages.success(request, "Flag marked as resolved.") |
| 697 | + return redirect("cadmin_leaderboard_complaints", course_slug=course_slug) |
| 698 | + |
| 699 | + |
623 | 700 | @staff_required |
624 | 701 | def enrollment_edit(request, course_slug, enrollment_id): |
625 | 702 | """Edit an enrollment - mainly to disable learning in public""" |
|
0 commit comments