Add grading api endpoint

This commit is contained in:
Daniel Egger 2023-04-25 09:14:45 +02:00
parent 9580d79559
commit baf5801b6a
5 changed files with 236 additions and 23 deletions

View File

@ -8,11 +8,15 @@ from django.urls import include, path, re_path
from django.views import defaults as default_views
from grapple import urls as grapple_urls
from ratelimit.exceptions import Ratelimited
from wagtail import urls as wagtail_urls
from wagtail.admin import urls as wagtailadmin_urls
from wagtail.documents import urls as wagtaildocs_urls
from vbv_lernwelt.assignment.views import (
request_assignment_completion,
request_assignment_completion_for_user,
update_assignment_input,
upsert_user_assignment_completion,
grade_assignment_completion,
)
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
from vbv_lernwelt.core.views import (
@ -43,9 +47,6 @@ from vbv_lernwelt.feedback.views import (
get_feedback_for_circle,
)
from vbv_lernwelt.notify.views import email_notification_settings
from wagtail import urls as wagtail_urls
from wagtail.admin import urls as wagtailadmin_urls
from wagtail.documents import urls as wagtaildocs_urls
def raise_example_error(request):
@ -99,8 +100,10 @@ urlpatterns = [
name="request_course_completion_for_user"),
# assignment
path(r"api/assignment/update/", update_assignment_input,
name="update_assignment_input"),
path(r"api/assignment/upsert/", upsert_user_assignment_completion,
name="upsert_user_assignment_completion"),
path(r"api/assignment/grade/", grade_assignment_completion,
name="grade_assignment_completion"),
path(r"api/assignment/<int:assignment_id>/<int:course_session_id>/",
request_assignment_completion,
name="request_assignment_completion"),

View File

@ -36,7 +36,7 @@ def update_assignment_completion(
it can also contain "trainer_input" when the trainer has entered grading data
{
"<user_text_input:uuid>": {
"trainer_data": {"points": 4, "text": "Gute Antwort"}
"expert_data": {"points": 4, "text": "Gute Antwort"}
},
}
:param copy_task_data: if true, the task data will be copied to the completion data

View File

@ -1,8 +1,13 @@
import json
from django.utils import timezone
from rest_framework.test import APITestCase
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletion
from vbv_lernwelt.assignment.models import (
Assignment,
AssignmentCompletion,
AssignmentCompletionAuditLog,
)
from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.core.models import User
from vbv_lernwelt.core.utils import find_first
@ -11,7 +16,7 @@ from vbv_lernwelt.course.creators.test_course import create_test_course
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
class AssignmentApiStudentTestCase(APITestCase):
class AssignmentApiTestCase(APITestCase):
def setUp(self) -> None:
create_default_users()
create_test_course(include_vv=False)
@ -23,15 +28,20 @@ class AssignmentApiStudentTestCase(APITestCase):
course_id=COURSE_TEST_ID,
title="Test Lehrgang Session",
)
self.user = User.objects.get(username="student")
csu = CourseSessionUser.objects.create(
self.student = User.objects.get(username="student")
self.student_csu = CourseSessionUser.objects.create(
course_session=self.cs,
user=self.user,
user=self.student,
)
def test_can_updateAssignmentCompletion_asStudent(self):
self.expert = User.objects.get(username="admin")
self.expert_csu = CourseSessionUser.objects.create(
course_session=self.cs, user=self.expert, role="EXPERT"
)
def test_student_can_upsertAssignmentCompletion(self):
self.client.login(username="student", password="test")
url = f"/api/assignment/update/"
url = f"/api/assignment/upsert/"
user_text_input = find_first(
self.assignment_subtasks, pred=lambda x: x["type"] == "user_text_input"
@ -53,7 +63,7 @@ class AssignmentApiStudentTestCase(APITestCase):
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(response_json["assignment_user"], self.user.id)
self.assertEqual(response_json["assignment_user"], self.student.id)
self.assertEqual(response_json["assignment"], self.assignment.id)
self.assertEqual(response_json["completion_status"], "in_progress")
self.assertDictEqual(
@ -64,7 +74,7 @@ class AssignmentApiStudentTestCase(APITestCase):
)
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.user,
assignment_user=self.student,
course_session_id=self.cs.id,
assignment_id=self.assignment.id,
)
@ -107,7 +117,7 @@ class AssignmentApiStudentTestCase(APITestCase):
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(response_json["assignment_user"], self.user.id)
self.assertEqual(response_json["assignment_user"], self.student.id)
self.assertEqual(response_json["assignment"], self.assignment.id)
self.assertEqual(response_json["completion_status"], "submitted")
self.assertDictEqual(
@ -134,3 +144,153 @@ class AssignmentApiStudentTestCase(APITestCase):
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 404)
self.assertTrue("Cannot update completion status" in str(response_json))
def test_expert_can_gradeAssignmentCompletion(self):
# setup AssignmentCompletion
subtasks = self.assignment.filter_user_subtasks(
subtask_types=["user_text_input"]
)
user_text_input = find_first(
subtasks,
pred=lambda x: (value := x.get("value"))
and value.get("text", "").startswith(
"Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest?"
),
)
ac = AssignmentCompletion.objects.create(
assignment_user=self.student,
assignment=self.assignment,
course_session=self.cs,
completion_status="submitted",
submitted_at=timezone.now(),
completion_data={
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
},
},
)
# make api call
self.client.login(username="admin", password="test")
url = f"/api/assignment/grade/"
response = self.client.post(
url,
{
"assignment_id": self.assignment.id,
"assignment_user_id": self.student.id,
"course_session_id": self.cs.id,
"completion_status": "grading_in_progress",
"completion_data": {
user_text_input["id"]: {
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
},
},
format="json",
)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(response_json["assignment_user"], self.student.id)
self.assertEqual(response_json["assignment"], self.assignment.id)
self.assertEqual(response_json["completion_status"], "grading_in_progress")
self.assertDictEqual(
response_json["completion_data"],
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.cs.id,
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "grading_in_progress")
self.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
# finish grading
response = self.client.post(
url,
{
"assignment_id": self.assignment.id,
"assignment_user_id": self.student.id,
"course_session_id": self.cs.id,
"completion_status": "graded",
"completion_data": {
user_text_input["id"]: {
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
},
},
format="json",
)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(response_json["assignment_user"], self.student.id)
self.assertEqual(response_json["assignment"], self.assignment.id)
self.assertEqual(response_json["completion_status"], "graded")
self.assertDictEqual(
response_json["completion_data"],
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.cs.id,
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "graded")
self.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
# `graded` will create a new AssignmentCompletionAuditLog
acl = AssignmentCompletionAuditLog.objects.get(
assignment_user=self.student,
course_session_id=self.cs.id,
assignment_id=self.assignment.id,
completion_status="graded",
)
self.maxDiff = None
self.assertDictEqual(
acl.completion_data,
{
user_text_input["id"]: {
"id": user_text_input["id"],
"type": "user_text_input",
"value": {
"text": "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest? Begründe deine Empfehlung",
},
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)

View File

@ -326,7 +326,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
course_session=self.course_session,
completion_data={
user_text_input["id"]: {
"trainer_data": {"points": 1, "comment": "Gut gemacht!"}
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
},
completion_status="grading_in_progress",
@ -342,7 +342,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
self.assertEqual(ac.completion_status, "grading_in_progress")
user_input = ac.completion_data[user_text_input["id"]]
self.assertDictEqual(
user_input["trainer_data"], {"points": 1, "comment": "Gut gemacht!"}
user_input["expert_data"], {"points": 1, "comment": "Gut gemacht!"}
)
self.assertEqual(
user_input["user_data"]["text"], "Ich würde nichts weiteres empfehlen."
@ -380,7 +380,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
completion_status="grading_in_progress",
completion_data={
user_text_input["id"]: {
"trainer_data": {"points": 1, "comment": "Gut gemacht!"}
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
},
)

View File

@ -8,6 +8,7 @@ from wagtail.models import Page
from vbv_lernwelt.assignment.models import AssignmentCompletion
from vbv_lernwelt.assignment.serializers import AssignmentCompletionSerializer
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseSession
from vbv_lernwelt.course.permissions import (
has_course_access,
@ -64,7 +65,7 @@ def request_assignment_completion_for_user(
@api_view(["POST"])
def update_assignment_input(request):
def upsert_user_assignment_completion(request):
try:
assignment_id = request.data.get("assignment_id")
course_session_id = request.data.get("course_session_id")
@ -88,11 +89,11 @@ def update_assignment_input(request):
)
logger.debug(
"store_assignment_input successful",
"upsert_user_assignment_completion successful",
label="assignment_api",
assignment_id=assignment.id,
assignment_title=assignment.title,
user_id=request.user.id,
assignment_user_id=request.user.id,
course_session_id=course_session_id,
completion_status=completion_status,
)
@ -103,3 +104,52 @@ def update_assignment_input(request):
except Exception as e:
logger.error(e, exc_info=True)
return Response({"error": str(e)}, status=404)
@api_view(["POST"])
def grade_assignment_completion(request):
try:
assignment_id = request.data.get("assignment_id")
assignment_user_id = request.data.get("assignment_user_id")
course_session_id = request.data.get("course_session_id")
completion_status = request.data.get("completion_status", "grading_in_progress")
completion_data = request.data.get("completion_data", {})
assignment_page = Page.objects.get(id=assignment_id)
assignment_user = User.objects.get(id=assignment_user_id)
if not has_course_access_by_page_request(request, assignment_page):
raise PermissionDenied()
if not is_course_session_expert(request.user, course_session_id):
raise PermissionDenied()
assignment = assignment_page.specific
ac = update_assignment_completion(
assignment_user=assignment_user,
assignment=assignment,
course_session=CourseSession.objects.get(id=course_session_id),
completion_data=completion_data,
completion_status=completion_status,
copy_task_data=False,
grading_user=request.user,
)
logger.debug(
"grade_assignment_completion successful",
label="assignment_api",
assignment_id=assignment.id,
assignment_title=assignment.title,
assignment_user_id=assignment_user_id,
course_session_id=course_session_id,
completion_status=completion_status,
grading_user_id=request.user.id,
)
return Response(status=200, data=AssignmentCompletionSerializer(ac).data)
except PermissionDenied as e:
raise e
except Exception as e:
logger.error(e, exc_info=True)
return Response({"error": str(e)}, status=404)