fix
This commit is contained in:
@@ -21,7 +21,7 @@ from django.db.models import (
|
|||||||
Subquery,
|
Subquery,
|
||||||
)
|
)
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from account.decorators import admin_required
|
from account.decorators import admin_required, super_required
|
||||||
from prompt.models import Conversation, Message
|
from prompt.models import Conversation, Message
|
||||||
from .classifier import classify_conversation_messages
|
from .classifier import classify_conversation_messages
|
||||||
|
|
||||||
@@ -517,7 +517,7 @@ def get_task_stats(request, task_id: int, classname: Optional[str] = None):
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/gradebook/", response=GradebookOut)
|
@router.get("/gradebook/", response=GradebookOut)
|
||||||
@admin_required
|
@super_required
|
||||||
def get_gradebook(
|
def get_gradebook(
|
||||||
request,
|
request,
|
||||||
classname: str = "",
|
classname: str = "",
|
||||||
@@ -536,7 +536,7 @@ def get_gradebook(
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/gradebook/export/")
|
@router.get("/gradebook/export/")
|
||||||
@admin_required
|
@super_required
|
||||||
def export_gradebook(
|
def export_gradebook(
|
||||||
request,
|
request,
|
||||||
classname: str = "",
|
classname: str = "",
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def _csv_number(value):
|
|||||||
|
|
||||||
def _classes():
|
def _classes():
|
||||||
return list(
|
return list(
|
||||||
User.objects.filter(role=RoleChoices.NORMAL)
|
User.objects.filter(role__in=(RoleChoices.NORMAL, RoleChoices.ADMIN))
|
||||||
.exclude(classname="")
|
.exclude(classname="")
|
||||||
.values_list("classname", flat=True)
|
.values_list("classname", flat=True)
|
||||||
.distinct()
|
.distinct()
|
||||||
@@ -81,7 +81,9 @@ def build_gradebook(filters: GradebookFilters):
|
|||||||
|
|
||||||
classes = _classes()
|
classes = _classes()
|
||||||
class_students = list(
|
class_students = list(
|
||||||
User.objects.filter(role=RoleChoices.NORMAL, classname=classname)
|
User.objects.filter(
|
||||||
|
role__in=(RoleChoices.NORMAL, RoleChoices.ADMIN), classname=classname
|
||||||
|
)
|
||||||
.order_by("username", "id")
|
.order_by("username", "id")
|
||||||
.only("id", "username", "classname")
|
.only("id", "username", "classname")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ class ShowcaseManagementApiTest(TestCase):
|
|||||||
|
|
||||||
class GradebookApiTest(TestCase):
|
class GradebookApiTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self.super = _make_user("grade-super", role=RoleChoices.SUPER)
|
||||||
self.admin = _make_user("grade-admin", role=RoleChoices.ADMIN)
|
self.admin = _make_user("grade-admin", role=RoleChoices.ADMIN)
|
||||||
self.normal = _make_user("grade-normal", classname="blocked")
|
self.normal = _make_user("grade-normal", classname="blocked")
|
||||||
|
|
||||||
@@ -289,7 +290,7 @@ class GradebookApiTest(TestCase):
|
|||||||
return submission
|
return submission
|
||||||
|
|
||||||
def test_gradebook_requires_classname(self):
|
def test_gradebook_requires_classname(self):
|
||||||
self.client.force_login(self.admin)
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
resp = self.client.get("/api/submission/gradebook/")
|
resp = self.client.get("/api/submission/gradebook/")
|
||||||
|
|
||||||
@@ -307,6 +308,34 @@ class GradebookApiTest(TestCase):
|
|||||||
self.assertIn(resp.status_code, (302, 403))
|
self.assertIn(resp.status_code, (302, 403))
|
||||||
self.assertIn(export_resp.status_code, (302, 403))
|
self.assertIn(export_resp.status_code, (302, 403))
|
||||||
|
|
||||||
|
def test_admin_user_cannot_access_gradebook(self):
|
||||||
|
self.client.force_login(self.admin)
|
||||||
|
|
||||||
|
resp = self.client.get("/api/submission/gradebook/?classname=10A")
|
||||||
|
export_resp = self.client.get(
|
||||||
|
"/api/submission/gradebook/export/?classname=10A"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn(resp.status_code, (302, 403))
|
||||||
|
self.assertIn(export_resp.status_code, (302, 403))
|
||||||
|
|
||||||
|
def test_admin_user_is_counted_in_class_gradebook(self):
|
||||||
|
self._student("alice")
|
||||||
|
admin_in_class = _make_user(
|
||||||
|
"grade-admin-2", role=RoleChoices.ADMIN, classname="10A"
|
||||||
|
)
|
||||||
|
task = _make_task(title="Admin Visible", task_type="challenge", display=9)
|
||||||
|
self._submit(admin_in_class, task, 5.0)
|
||||||
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
|
resp = self.client.get("/api/submission/gradebook/?classname=10A")
|
||||||
|
|
||||||
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
data = resp.json()
|
||||||
|
self.assertEqual(data["student_count"], 2)
|
||||||
|
usernames = [row["username"] for row in data["rows"]]
|
||||||
|
self.assertIn("grade-admin-2", usernames)
|
||||||
|
|
||||||
def test_coverage_includes_tutorial_and_challenge_without_public_requirement(self):
|
def test_coverage_includes_tutorial_and_challenge_without_public_requirement(self):
|
||||||
students = [
|
students = [
|
||||||
self._student("alice"),
|
self._student("alice"),
|
||||||
@@ -337,7 +366,7 @@ class GradebookApiTest(TestCase):
|
|||||||
self._submit(students[0], challenge, 3.0)
|
self._submit(students[0], challenge, 3.0)
|
||||||
self._submit(students[1], challenge, 4.0)
|
self._submit(students[1], challenge, 4.0)
|
||||||
self._submit(students[0], low_coverage, 5.0)
|
self._submit(students[0], low_coverage, 5.0)
|
||||||
self.client.force_login(self.admin)
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
resp = self.client.get("/api/submission/gradebook/?classname=10A")
|
resp = self.client.get("/api/submission/gradebook/?classname=10A")
|
||||||
|
|
||||||
@@ -390,7 +419,7 @@ class GradebookApiTest(TestCase):
|
|||||||
old_best = self._submit(alice, task, 4.5, created=older)
|
old_best = self._submit(alice, task, 4.5, created=older)
|
||||||
new_best = self._submit(alice, task, 4.5, created=newer)
|
new_best = self._submit(alice, task, 4.5, created=newer)
|
||||||
self._submit(bob, task, 3.0)
|
self._submit(bob, task, 3.0)
|
||||||
self.client.force_login(self.admin)
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
resp = self.client.get("/api/submission/gradebook/?classname=10A")
|
resp = self.client.get("/api/submission/gradebook/?classname=10A")
|
||||||
|
|
||||||
@@ -412,7 +441,7 @@ class GradebookApiTest(TestCase):
|
|||||||
self._submit(bob, tutorial, 5.0)
|
self._submit(bob, tutorial, 5.0)
|
||||||
self._submit(alice, challenge, 5.0)
|
self._submit(alice, challenge, 5.0)
|
||||||
self._submit(bob, challenge, 1.0)
|
self._submit(bob, challenge, 1.0)
|
||||||
self.client.force_login(self.admin)
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
resp = self.client.get(
|
resp = self.client.get(
|
||||||
"/api/submission/gradebook/?classname=10A&task_type=tutorial&username=alice"
|
"/api/submission/gradebook/?classname=10A&task_type=tutorial&username=alice"
|
||||||
@@ -427,7 +456,7 @@ class GradebookApiTest(TestCase):
|
|||||||
|
|
||||||
def test_missing_class_returns_empty_table_with_class_options(self):
|
def test_missing_class_returns_empty_table_with_class_options(self):
|
||||||
self._student("alice")
|
self._student("alice")
|
||||||
self.client.force_login(self.admin)
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
resp = self.client.get("/api/submission/gradebook/?classname=missing")
|
resp = self.client.get("/api/submission/gradebook/?classname=missing")
|
||||||
|
|
||||||
@@ -447,7 +476,7 @@ class GradebookApiTest(TestCase):
|
|||||||
]
|
]
|
||||||
optional = _make_task(title="Low Coverage", task_type="challenge", display=8)
|
optional = _make_task(title="Low Coverage", task_type="challenge", display=8)
|
||||||
self._submit(students[0], optional, 5.0)
|
self._submit(students[0], optional, 5.0)
|
||||||
self.client.force_login(self.admin)
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
resp = self.client.get("/api/submission/gradebook/?classname=10A")
|
resp = self.client.get("/api/submission/gradebook/?classname=10A")
|
||||||
|
|
||||||
@@ -481,7 +510,7 @@ class GradebookApiTest(TestCase):
|
|||||||
for i in range(1, 21):
|
for i in range(1, 21):
|
||||||
student = self._student(f"s{i:02d}", classname="10B")
|
student = self._student(f"s{i:02d}", classname="10B")
|
||||||
self._submit(student, task, float(21 - i))
|
self._submit(student, task, float(21 - i))
|
||||||
self.client.force_login(self.admin)
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
resp = self.client.get("/api/submission/gradebook/?classname=10B")
|
resp = self.client.get("/api/submission/gradebook/?classname=10B")
|
||||||
|
|
||||||
@@ -504,7 +533,7 @@ class GradebookApiTest(TestCase):
|
|||||||
self._submit(alice, tutorial, 4.0)
|
self._submit(alice, tutorial, 4.0)
|
||||||
self._submit(bob, tutorial, 2.0)
|
self._submit(bob, tutorial, 2.0)
|
||||||
self._submit(alice, challenge, 5.0)
|
self._submit(alice, challenge, 5.0)
|
||||||
self.client.force_login(self.admin)
|
self.client.force_login(self.super)
|
||||||
|
|
||||||
resp = self.client.get(
|
resp = self.client.get(
|
||||||
"/api/submission/gradebook/export/?classname=10A&task_type=tutorial"
|
"/api/submission/gradebook/export/?classname=10A&task_type=tutorial"
|
||||||
|
|||||||
Reference in New Issue
Block a user