Clean up assignment graphql
* Remove old assignment api REST code which is unused * Refactor handling AssignmentCompletionStatus enum * Add full test for GraphQL assignment completion mutation * Hide wagtail INFO logs on local dev
This commit is contained in:
parent
926ecb0ae0
commit
9860a59cef
|
|
@ -184,7 +184,7 @@ scalar JSONString
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
send_feedback(input: SendFeedbackInput!): SendFeedbackPayload
|
send_feedback(input: SendFeedbackInput!): SendFeedbackPayload
|
||||||
upsert_assignment_completion(assignment_id: ID!, assignment_user_id: ID, completion_data_string: String, completion_status: String, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float): AssignmentCompletionMutation
|
upsert_assignment_completion(assignment_id: ID!, assignment_user_id: ID, completion_data_string: String, completion_status: AssignmentCompletionStatus, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float): AssignmentCompletionMutation
|
||||||
}
|
}
|
||||||
|
|
||||||
type SendFeedbackPayload {
|
type SendFeedbackPayload {
|
||||||
|
|
@ -222,4 +222,12 @@ input SendFeedbackInput {
|
||||||
|
|
||||||
type AssignmentCompletionMutation {
|
type AssignmentCompletionMutation {
|
||||||
assignment_completion: AssignmentCompletionType
|
assignment_completion: AssignmentCompletionType
|
||||||
|
}
|
||||||
|
|
||||||
|
"""An enumeration."""
|
||||||
|
enum AssignmentCompletionStatus {
|
||||||
|
IN_PROGRESS
|
||||||
|
SUBMITTED
|
||||||
|
EVALUATION_IN_PROGRESS
|
||||||
|
EVALUATION_SUBMITTED
|
||||||
}
|
}
|
||||||
|
|
@ -372,6 +372,11 @@ if IT_DJANGO_LOGGING_CONF == "IT_DJANGO_LOGGING_CONF_CONSOLE_COLOR":
|
||||||
"level": "INFO",
|
"level": "INFO",
|
||||||
"propagate": False,
|
"propagate": False,
|
||||||
},
|
},
|
||||||
|
"wagtail": {
|
||||||
|
"handlers": ["default"],
|
||||||
|
"level": "WARNING" if IT_LOCAL_HIDE_DJANGO_SERVER_LOGS else "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
"vbv_lernwelt": {
|
"vbv_lernwelt": {
|
||||||
"handlers": ["default"],
|
"handlers": ["default"],
|
||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||||
from graphene_django.views import GraphQLView
|
from graphene_django.views import GraphQLView
|
||||||
from ratelimit.exceptions import Ratelimited
|
from ratelimit.exceptions import Ratelimited
|
||||||
|
|
||||||
from vbv_lernwelt.assignment.views import (
|
from vbv_lernwelt.assignment.views import request_assignment_completion_status
|
||||||
evaluate_assignment_completion,
|
|
||||||
request_assignment_completion,
|
|
||||||
request_assignment_completion_for_user,
|
|
||||||
request_assignment_completion_status,
|
|
||||||
upsert_user_assignment_completion,
|
|
||||||
)
|
|
||||||
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
|
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
|
||||||
from vbv_lernwelt.core.schema import schema
|
from vbv_lernwelt.core.schema import schema
|
||||||
from vbv_lernwelt.core.views import (
|
from vbv_lernwelt.core.views import (
|
||||||
|
|
@ -120,21 +114,10 @@ urlpatterns = [
|
||||||
name="request_course_completion_for_user"),
|
name="request_course_completion_for_user"),
|
||||||
|
|
||||||
# assignment
|
# assignment
|
||||||
path(r"api/assignment/upsert/", upsert_user_assignment_completion,
|
|
||||||
name="upsert_user_assignment_completion"),
|
|
||||||
path(r"api/assignment/evaluate/", evaluate_assignment_completion,
|
|
||||||
name="evaluate_assignment_completion"),
|
|
||||||
path(r"api/assignment/<signed_int:assignment_id>/<signed_int:course_session_id>/",
|
|
||||||
request_assignment_completion,
|
|
||||||
name="request_assignment_completion"),
|
|
||||||
path(
|
path(
|
||||||
r"api/assignment/<signed_int:assignment_id>/<signed_int:course_session_id>/status/",
|
r"api/assignment/<signed_int:assignment_id>/<signed_int:course_session_id>/status/",
|
||||||
request_assignment_completion_status,
|
request_assignment_completion_status,
|
||||||
name="request_assignment_completion_status"),
|
name="request_assignment_completion_status"),
|
||||||
path(
|
|
||||||
r"api/assignment/<signed_int:assignment_id>/<signed_int:course_session_id>/<signed_int:user_id>/",
|
|
||||||
request_assignment_completion_for_user,
|
|
||||||
name="request_assignment_completion_for_user"),
|
|
||||||
|
|
||||||
# documents
|
# documents
|
||||||
path(r'api/core/document/start/', document_upload_start,
|
path(r'api/core/document/start/', document_upload_start,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import structlog
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
|
|
||||||
from vbv_lernwelt.assignment.graphql.types import AssignmentCompletionType
|
from vbv_lernwelt.assignment.graphql.types import AssignmentCompletionType
|
||||||
from vbv_lernwelt.assignment.models import Assignment
|
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus
|
||||||
from vbv_lernwelt.assignment.services import update_assignment_completion
|
from vbv_lernwelt.assignment.services import update_assignment_completion
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.models import CourseSession
|
from vbv_lernwelt.course.models import CourseSession
|
||||||
|
|
@ -17,12 +17,14 @@ logger = structlog.get_logger(__name__)
|
||||||
class AssignmentCompletionMutation(graphene.Mutation):
|
class AssignmentCompletionMutation(graphene.Mutation):
|
||||||
assignment_completion = graphene.Field(AssignmentCompletionType)
|
assignment_completion = graphene.Field(AssignmentCompletionType)
|
||||||
|
|
||||||
class Input:
|
class Arguments:
|
||||||
assignment_id = graphene.ID(required=True)
|
assignment_id = graphene.ID(required=True)
|
||||||
course_session_id = graphene.ID(required=True)
|
course_session_id = graphene.ID(required=True)
|
||||||
assignment_user_id = graphene.ID()
|
assignment_user_id = graphene.ID()
|
||||||
|
|
||||||
completion_status = graphene.String()
|
completion_status = graphene.Argument(
|
||||||
|
graphene.Enum.from_enum(AssignmentCompletionStatus)
|
||||||
|
)
|
||||||
completion_data_string = graphene.String()
|
completion_data_string = graphene.String()
|
||||||
|
|
||||||
evaluation_grade = graphene.Float()
|
evaluation_grade = graphene.Float()
|
||||||
|
|
@ -36,7 +38,7 @@ class AssignmentCompletionMutation(graphene.Mutation):
|
||||||
assignment_id,
|
assignment_id,
|
||||||
course_session_id,
|
course_session_id,
|
||||||
assignment_user_id=None,
|
assignment_user_id=None,
|
||||||
completion_status="IN_PROGRESS",
|
completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS,
|
||||||
completion_data_string="{}",
|
completion_data_string="{}",
|
||||||
evaluation_grade=None,
|
evaluation_grade=None,
|
||||||
evaluation_points=None,
|
evaluation_points=None,
|
||||||
|
|
@ -63,7 +65,10 @@ class AssignmentCompletionMutation(graphene.Mutation):
|
||||||
|
|
||||||
evaluation_data = {}
|
evaluation_data = {}
|
||||||
|
|
||||||
if completion_status in ["EVALUATION_SUBMITTED", "EVALUATION_IN_PROGRESS"]:
|
if completion_status in [
|
||||||
|
AssignmentCompletionStatus.EVALUATION_SUBMITTED,
|
||||||
|
AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
|
||||||
|
]:
|
||||||
if not is_course_session_expert(info.context.user, course_session_id):
|
if not is_course_session_expert(info.context.user, course_session_id):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Generated by Django 3.2.13 on 2023-06-28 14:12
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("assignment", "0004_assignment_assignment_type"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assignmentcompletionauditlog",
|
||||||
|
name="completion_status",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("IN_PROGRESS", "IN_PROGRESS"),
|
||||||
|
("SUBMITTED", "SUBMITTED"),
|
||||||
|
("EVALUATION_IN_PROGRESS", "EVALUATION_IN_PROGRESS"),
|
||||||
|
("EVALUATION_SUBMITTED", "EVALUATION_SUBMITTED"),
|
||||||
|
],
|
||||||
|
default="IN_PROGRESS",
|
||||||
|
max_length=255,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Generated by Django 3.2.13 on 2023-06-28 14:16
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("learnpath", "0007_learningunit_title_hidden"),
|
||||||
|
("assignment", "0005_alter_assignmentcompletionauditlog_completion_status"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveConstraint(
|
||||||
|
model_name="assignmentcompletion",
|
||||||
|
name="assignment_completion_unique_user_assignment_course_session",
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assignmentcompletion",
|
||||||
|
name="circle",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="learnpath.circle",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name="assignmentcompletion",
|
||||||
|
constraint=models.UniqueConstraint(
|
||||||
|
fields=("assignment_user", "assignment", "course_session", "circle"),
|
||||||
|
name="assignment_completion_unique_user_assignment_course_session",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -107,14 +107,10 @@ class EvaluationTaskBlock(blocks.StructBlock):
|
||||||
label = "Beurteilungskriterium"
|
label = "Beurteilungskriterium"
|
||||||
|
|
||||||
|
|
||||||
AssignmentType = Enum(
|
class AssignmentType(Enum):
|
||||||
"AssignmentType",
|
CASEWORK = "CASEWORK" # Geleitete Fallarbeit
|
||||||
[
|
PREP_ASSIGNMENT = "PREP_ASSIGNMENT" # Vorbereitungsauftrag
|
||||||
"CASEWORK", # Geleitete Fallarbeit
|
REFLECTION = "REFLECTION" # Reflexion
|
||||||
"PREP_ASSIGNMENT", # Vorbereitungsauftrag
|
|
||||||
"REFLECTION", # Reflexion
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Assignment(CourseBasePage):
|
class Assignment(CourseBasePage):
|
||||||
|
|
@ -130,8 +126,8 @@ class Assignment(CourseBasePage):
|
||||||
|
|
||||||
assignment_type = models.CharField(
|
assignment_type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=[(tag.name, tag.name) for tag in AssignmentType],
|
choices=[(tag.value, tag.value) for tag in AssignmentType],
|
||||||
default=AssignmentType.CASEWORK.name,
|
default=AssignmentType.CASEWORK.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
intro_text = RichTextField(
|
intro_text = RichTextField(
|
||||||
|
|
@ -242,14 +238,17 @@ class Assignment(CourseBasePage):
|
||||||
return self.filter_user_subtasks() + self.get_evaluation_tasks()
|
return self.filter_user_subtasks() + self.get_evaluation_tasks()
|
||||||
|
|
||||||
|
|
||||||
AssignmentCompletionStatus = Enum(
|
class AssignmentCompletionStatus(Enum):
|
||||||
"AssignmentCompletionStatus",
|
IN_PROGRESS = "IN_PROGRESS"
|
||||||
["IN_PROGRESS", "SUBMITTED", "EVALUATION_IN_PROGRESS", "EVALUATION_SUBMITTED"],
|
SUBMITTED = "SUBMITTED"
|
||||||
)
|
EVALUATION_IN_PROGRESS = "EVALUATION_IN_PROGRESS"
|
||||||
|
EVALUATION_SUBMITTED = "EVALUATION_SUBMITTED"
|
||||||
|
|
||||||
|
|
||||||
def is_valid_assignment_completion_status(status):
|
def is_valid_assignment_completion_status(
|
||||||
return status in AssignmentCompletionStatus.__members__
|
completion_status: AssignmentCompletionStatus,
|
||||||
|
):
|
||||||
|
return completion_status.value in AssignmentCompletionStatus.__members__
|
||||||
|
|
||||||
|
|
||||||
class AssignmentCompletion(models.Model):
|
class AssignmentCompletion(models.Model):
|
||||||
|
|
@ -271,11 +270,18 @@ class AssignmentCompletion(models.Model):
|
||||||
assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
|
assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
|
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
|
||||||
course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)
|
course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)
|
||||||
|
circle = models.ForeignKey(
|
||||||
|
"learnpath.Circle",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
|
||||||
completion_status = models.CharField(
|
completion_status = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
choices=[(acs.name, acs.name) for acs in AssignmentCompletionStatus],
|
choices=[(acs.value, acs.value) for acs in AssignmentCompletionStatus],
|
||||||
default="IN_PROGRESS",
|
default=AssignmentCompletionStatus.IN_PROGRESS.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
completion_data = models.JSONField(default=dict)
|
completion_data = models.JSONField(default=dict)
|
||||||
|
|
@ -284,7 +290,7 @@ class AssignmentCompletion(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
UniqueConstraint(
|
UniqueConstraint(
|
||||||
fields=["assignment_user", "assignment", "course_session"],
|
fields=["assignment_user", "assignment", "course_session", "circle"],
|
||||||
name="assignment_completion_unique_user_assignment_course_session",
|
name="assignment_completion_unique_user_assignment_course_session",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
import structlog
|
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Type
|
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
@ -21,7 +20,7 @@ def update_assignment_completion(
|
||||||
assignment: Assignment,
|
assignment: Assignment,
|
||||||
course_session: CourseSession,
|
course_session: CourseSession,
|
||||||
completion_data=None,
|
completion_data=None,
|
||||||
completion_status: Type[AssignmentCompletionStatus] = "IN_PROGRESS",
|
completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS,
|
||||||
evaluation_user: User | None = None,
|
evaluation_user: User | None = None,
|
||||||
evaluation_grade: float | None = None,
|
evaluation_grade: float | None = None,
|
||||||
evaluation_points: float | None = None,
|
evaluation_points: float | None = None,
|
||||||
|
|
@ -58,12 +57,14 @@ def update_assignment_completion(
|
||||||
|
|
||||||
if not is_valid_assignment_completion_status(completion_status):
|
if not is_valid_assignment_completion_status(completion_status):
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
{"completion_status": f"Invalid completion status {completion_status}"}
|
{
|
||||||
|
"completion_status": f"Invalid completion status {completion_status.value}"
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if validate_completion_status_change:
|
if validate_completion_status_change:
|
||||||
# TODO: check time?
|
# TODO: check time?
|
||||||
if completion_status == "SUBMITTED":
|
if completion_status == AssignmentCompletionStatus.SUBMITTED:
|
||||||
if ac.completion_status in [
|
if ac.completion_status in [
|
||||||
"SUBMITTED",
|
"SUBMITTED",
|
||||||
"EVALUATION_IN_PROGRESS",
|
"EVALUATION_IN_PROGRESS",
|
||||||
|
|
@ -74,7 +75,7 @@ def update_assignment_completion(
|
||||||
"completion_status": f"Cannot update completion status from {ac.completion_status} to SUBMITTED"
|
"completion_status": f"Cannot update completion status from {ac.completion_status} to SUBMITTED"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
elif completion_status == "EVALUATION_SUBMITTED":
|
elif completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED:
|
||||||
if ac.completion_status == "EVALUATION_SUBMITTED":
|
if ac.completion_status == "EVALUATION_SUBMITTED":
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
{
|
{
|
||||||
|
|
@ -82,14 +83,20 @@ def update_assignment_completion(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if completion_status == "IN_PROGRESS" and ac.completion_status != "IN_PROGRESS":
|
if (
|
||||||
|
completion_status == AssignmentCompletionStatus.IN_PROGRESS
|
||||||
|
and ac.completion_status != "IN_PROGRESS"
|
||||||
|
):
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
{
|
{
|
||||||
"completion_status": f"Cannot set completion status to IN_PROGRESS when it is {ac.completion_status}"
|
"completion_status": f"Cannot set completion status to IN_PROGRESS when it is {ac.completion_status}"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if completion_status in ["EVALUATION_SUBMITTED", "EVALUATION_IN_PROGRESS"]:
|
if completion_status in [
|
||||||
|
AssignmentCompletionStatus.EVALUATION_SUBMITTED,
|
||||||
|
AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
|
||||||
|
]:
|
||||||
if evaluation_user is None:
|
if evaluation_user is None:
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
{
|
{
|
||||||
|
|
@ -98,7 +105,7 @@ def update_assignment_completion(
|
||||||
)
|
)
|
||||||
ac.evaluation_user = evaluation_user
|
ac.evaluation_user = evaluation_user
|
||||||
|
|
||||||
if completion_status == "EVALUATION_SUBMITTED":
|
if completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED:
|
||||||
if evaluation_grade is None:
|
if evaluation_grade is None:
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
{
|
{
|
||||||
|
|
@ -115,12 +122,12 @@ def update_assignment_completion(
|
||||||
ac.evaluation_grade = evaluation_grade
|
ac.evaluation_grade = evaluation_grade
|
||||||
ac.evaluation_points = evaluation_points
|
ac.evaluation_points = evaluation_points
|
||||||
|
|
||||||
if completion_status == "SUBMITTED":
|
if completion_status == AssignmentCompletionStatus.SUBMITTED:
|
||||||
ac.submitted_at = timezone.now()
|
ac.submitted_at = timezone.now()
|
||||||
elif completion_status == "EVALUATION_SUBMITTED":
|
elif completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED:
|
||||||
ac.evaluation_submitted_at = timezone.now()
|
ac.evaluation_submitted_at = timezone.now()
|
||||||
|
|
||||||
ac.completion_status = completion_status
|
ac.completion_status = completion_status.value
|
||||||
|
|
||||||
# TODO: make more validation of the provided input -> maybe with graphql
|
# TODO: make more validation of the provided input -> maybe with graphql
|
||||||
completion_data = _remove_unknown_entries(assignment, completion_data)
|
completion_data = _remove_unknown_entries(assignment, completion_data)
|
||||||
|
|
@ -140,13 +147,16 @@ def update_assignment_completion(
|
||||||
|
|
||||||
ac.save()
|
ac.save()
|
||||||
|
|
||||||
if completion_status in ["EVALUATION_SUBMITTED", "SUBMITTED"]:
|
if completion_status in [
|
||||||
|
AssignmentCompletionStatus.EVALUATION_SUBMITTED,
|
||||||
|
AssignmentCompletionStatus.SUBMITTED,
|
||||||
|
]:
|
||||||
acl = AssignmentCompletionAuditLog.objects.create(
|
acl = AssignmentCompletionAuditLog.objects.create(
|
||||||
assignment_user=assignment_user,
|
assignment_user=assignment_user,
|
||||||
assignment=assignment,
|
assignment=assignment,
|
||||||
course_session=course_session,
|
course_session=course_session,
|
||||||
evaluation_user=evaluation_user,
|
evaluation_user=evaluation_user,
|
||||||
completion_status=completion_status,
|
completion_status=completion_status.value,
|
||||||
assignment_user_email=assignment_user.email,
|
assignment_user_email=assignment_user.email,
|
||||||
assignment_slug=assignment.slug,
|
assignment_slug=assignment.slug,
|
||||||
completion_data=deepcopy(ac.completion_data),
|
completion_data=deepcopy(ac.completion_data),
|
||||||
|
|
|
||||||
|
|
@ -1,299 +0,0 @@
|
||||||
import json
|
|
||||||
|
|
||||||
from django.utils import timezone
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from vbv_lernwelt.assignment.creators.create_assignments import (
|
|
||||||
create_uk_fahrzeug_casework,
|
|
||||||
)
|
|
||||||
from vbv_lernwelt.assignment.models import (
|
|
||||||
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
|
|
||||||
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
|
||||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
|
||||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
|
||||||
|
|
||||||
|
|
||||||
class AssignmentApiTestCase(APITestCase):
|
|
||||||
def setUp(self) -> None:
|
|
||||||
create_default_users()
|
|
||||||
create_test_course(include_vv=False)
|
|
||||||
self.assignment = create_uk_fahrzeug_casework(course_id=COURSE_TEST_ID)
|
|
||||||
self.assignment_subtasks = self.assignment.filter_user_subtasks()
|
|
||||||
|
|
||||||
self.cs = CourseSession.objects.create(
|
|
||||||
course_id=COURSE_TEST_ID,
|
|
||||||
title="Test Lehrgang Session",
|
|
||||||
)
|
|
||||||
self.student = User.objects.get(username="student")
|
|
||||||
self.student_csu = CourseSessionUser.objects.create(
|
|
||||||
course_session=self.cs,
|
|
||||||
user=self.student,
|
|
||||||
)
|
|
||||||
|
|
||||||
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/upsert/"
|
|
||||||
|
|
||||||
user_text_input = find_first(
|
|
||||||
self.assignment_subtasks, pred=lambda x: x["type"] == "user_text_input"
|
|
||||||
)
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
"assignment_id": self.assignment.id,
|
|
||||||
"course_session_id": self.cs.id,
|
|
||||||
"completion_status": "IN_PROGRESS",
|
|
||||||
"completion_data": {
|
|
||||||
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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"], "IN_PROGRESS")
|
|
||||||
self.assertDictEqual(
|
|
||||||
response_json["completion_data"],
|
|
||||||
{
|
|
||||||
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
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, "IN_PROGRESS")
|
|
||||||
self.assertDictEqual(
|
|
||||||
db_entry.completion_data,
|
|
||||||
{
|
|
||||||
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# read data via request api
|
|
||||||
response = self.client.get(
|
|
||||||
f"/api/assignment/{self.assignment.id}/{self.cs.id}/",
|
|
||||||
format="json",
|
|
||||||
)
|
|
||||||
response_json = response.json()
|
|
||||||
print(json.dumps(response.json(), indent=2))
|
|
||||||
self.assertDictEqual(
|
|
||||||
response_json["completion_data"],
|
|
||||||
{
|
|
||||||
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# submit the assignment
|
|
||||||
response = self.client.post(
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
"assignment_id": self.assignment.id,
|
|
||||||
"course_session_id": self.cs.id,
|
|
||||||
"completion_status": "SUBMITTED",
|
|
||||||
"completion_data": {
|
|
||||||
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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"], "SUBMITTED")
|
|
||||||
self.assertDictEqual(
|
|
||||||
response_json["completion_data"],
|
|
||||||
{
|
|
||||||
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# second submit will fail
|
|
||||||
response = self.client.post(
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
"assignment_id": self.assignment.id,
|
|
||||||
"course_session_id": self.cs.id,
|
|
||||||
"completion_status": "SUBMITTED",
|
|
||||||
"completion_data": {
|
|
||||||
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
format="json",
|
|
||||||
)
|
|
||||||
response_json = response.json()
|
|
||||||
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/evaluate/"
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
"assignment_id": self.assignment.id,
|
|
||||||
"assignment_user_id": self.student.id,
|
|
||||||
"course_session_id": self.cs.id,
|
|
||||||
"completion_status": "EVALUATION_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"], "EVALUATION_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, "EVALUATION_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": "EVALUATION_SUBMITTED",
|
|
||||||
"completion_data": {
|
|
||||||
user_text_input["id"]: {
|
|
||||||
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"evaluation_grade": 4.5,
|
|
||||||
"evaluation_points": 16,
|
|
||||||
},
|
|
||||||
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"], "EVALUATION_SUBMITTED")
|
|
||||||
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, "EVALUATION_SUBMITTED")
|
|
||||||
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!"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# `EVALUATION_SUBMITTED` 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="EVALUATION_SUBMITTED",
|
|
||||||
)
|
|
||||||
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!"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
@ -0,0 +1,368 @@
|
||||||
|
import json
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
from django.utils import timezone
|
||||||
|
from graphene_django.utils import GraphQLTestCase
|
||||||
|
|
||||||
|
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
|
||||||
|
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||||
|
from vbv_lernwelt.course.models import CourseSession
|
||||||
|
|
||||||
|
|
||||||
|
class AttendanceCourseUserMutationTestCase(GraphQLTestCase):
|
||||||
|
GRAPHQL_URL = "/server/graphql/"
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
create_default_users()
|
||||||
|
create_test_course(include_vv=False, with_sessions=True)
|
||||||
|
self.course_session = CourseSession.objects.get(title="Test Bern 2022 a")
|
||||||
|
self.trainer = User.objects.get(username="test-trainer1@example.com")
|
||||||
|
self.student = User.objects.get(username="test-student1@example.com")
|
||||||
|
|
||||||
|
self.assignment = Assignment.objects.get(
|
||||||
|
slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"
|
||||||
|
)
|
||||||
|
self.assignment_subtasks = self.assignment.filter_user_subtasks()
|
||||||
|
|
||||||
|
# self.client.force_login(self.trainer)
|
||||||
|
|
||||||
|
def test_student_can_upsertAssignmentCompletion(self):
|
||||||
|
self.client.force_login(self.student)
|
||||||
|
user_text_input = find_first(
|
||||||
|
self.assignment_subtasks, pred=lambda x: x["type"] == "user_text_input"
|
||||||
|
)
|
||||||
|
|
||||||
|
completion_data_string = json.dumps(
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
|
||||||
|
}
|
||||||
|
).replace('"', '\\"')
|
||||||
|
|
||||||
|
query = f"""
|
||||||
|
mutation {{
|
||||||
|
upsert_assignment_completion(
|
||||||
|
assignment_id: {self.assignment.id}
|
||||||
|
course_session_id: {self.course_session.id}
|
||||||
|
completion_status: IN_PROGRESS
|
||||||
|
completion_data_string: "{completion_data_string}"
|
||||||
|
) {{
|
||||||
|
assignment_completion {{
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
completion_data
|
||||||
|
assignment_user {{ id }}
|
||||||
|
assignment {{ id }}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.query(query)
|
||||||
|
self.assertResponseNoErrors(response)
|
||||||
|
|
||||||
|
data = json.loads(response.content)["data"]["upsert_assignment_completion"][
|
||||||
|
"assignment_completion"
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(data["assignment_user"]["id"], str(self.student.id))
|
||||||
|
self.assertEqual(data["assignment"]["id"], str(self.assignment.id))
|
||||||
|
self.assertEqual(data["completion_status"], "IN_PROGRESS")
|
||||||
|
self.assertDictEqual(
|
||||||
|
data["completion_data"],
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# check DB data
|
||||||
|
db_entry = AssignmentCompletion.objects.get(
|
||||||
|
assignment_user=self.student,
|
||||||
|
course_session_id=self.course_session.id,
|
||||||
|
assignment_id=self.assignment.id,
|
||||||
|
)
|
||||||
|
self.assertEqual(db_entry.completion_status, "IN_PROGRESS")
|
||||||
|
self.assertDictEqual(
|
||||||
|
db_entry.completion_data,
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# submit the response
|
||||||
|
completion_data_string = json.dumps(
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
|
||||||
|
}
|
||||||
|
).replace('"', '\\"')
|
||||||
|
|
||||||
|
query = f"""
|
||||||
|
mutation {{
|
||||||
|
upsert_assignment_completion(
|
||||||
|
assignment_id: {self.assignment.id}
|
||||||
|
course_session_id: {self.course_session.id}
|
||||||
|
completion_status: SUBMITTED
|
||||||
|
completion_data_string: "{completion_data_string}"
|
||||||
|
) {{
|
||||||
|
assignment_completion {{
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
completion_data
|
||||||
|
assignment_user {{ id }}
|
||||||
|
assignment {{ id }}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.query(query)
|
||||||
|
self.assertResponseNoErrors(response)
|
||||||
|
|
||||||
|
data = json.loads(response.content)["data"]["upsert_assignment_completion"][
|
||||||
|
"assignment_completion"
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(data["assignment_user"]["id"], str(self.student.id))
|
||||||
|
self.assertEqual(data["assignment"]["id"], str(self.assignment.id))
|
||||||
|
self.assertEqual(data["completion_status"], "SUBMITTED")
|
||||||
|
self.assertDictEqual(
|
||||||
|
data["completion_data"],
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# check DB data
|
||||||
|
db_entry = AssignmentCompletion.objects.get(
|
||||||
|
assignment_user=self.student,
|
||||||
|
course_session_id=self.course_session.id,
|
||||||
|
assignment_id=self.assignment.id,
|
||||||
|
)
|
||||||
|
self.assertEqual(db_entry.completion_status, "SUBMITTED")
|
||||||
|
self.assertEqual(db_entry.submitted_at.date(), date.today())
|
||||||
|
self.assertDictEqual(
|
||||||
|
db_entry.completion_data,
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# second submit will fail
|
||||||
|
completion_data_string = json.dumps(
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {"user_data": {"text": "Hallo via API 3"}},
|
||||||
|
}
|
||||||
|
).replace('"', '\\"')
|
||||||
|
|
||||||
|
query = f"""
|
||||||
|
mutation {{
|
||||||
|
upsert_assignment_completion(
|
||||||
|
assignment_id: {self.assignment.id}
|
||||||
|
course_session_id: {self.course_session.id}
|
||||||
|
completion_status: SUBMITTED
|
||||||
|
completion_data_string: "{completion_data_string}"
|
||||||
|
) {{
|
||||||
|
assignment_completion {{
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
completion_data
|
||||||
|
assignment_user {{ id }}
|
||||||
|
assignment {{ id }}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.query(query)
|
||||||
|
self.assertResponseHasErrors(response)
|
||||||
|
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.course_session,
|
||||||
|
completion_status="SUBMITTED",
|
||||||
|
submitted_at=timezone.now(),
|
||||||
|
completion_data={
|
||||||
|
user_text_input["id"]: {
|
||||||
|
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.client.force_login(self.trainer)
|
||||||
|
|
||||||
|
completion_data_string = json.dumps(
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {
|
||||||
|
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
).replace('"', '\\"')
|
||||||
|
|
||||||
|
query = f"""
|
||||||
|
mutation {{
|
||||||
|
upsert_assignment_completion(
|
||||||
|
assignment_id: {self.assignment.id}
|
||||||
|
assignment_user_id: {self.student.id}
|
||||||
|
course_session_id: {self.course_session.id}
|
||||||
|
completion_status: EVALUATION_IN_PROGRESS
|
||||||
|
completion_data_string: "{completion_data_string}"
|
||||||
|
) {{
|
||||||
|
assignment_completion {{
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
completion_data
|
||||||
|
assignment_user {{ id }}
|
||||||
|
assignment {{ id }}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.query(query)
|
||||||
|
self.assertResponseNoErrors(response)
|
||||||
|
|
||||||
|
data = json.loads(response.content)["data"]["upsert_assignment_completion"][
|
||||||
|
"assignment_completion"
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(data["assignment_user"]["id"], str(self.student.id))
|
||||||
|
self.assertEqual(data["assignment"]["id"], str(self.assignment.id))
|
||||||
|
self.assertEqual(data["completion_status"], "EVALUATION_IN_PROGRESS")
|
||||||
|
self.assertDictEqual(
|
||||||
|
data["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.course_session.id,
|
||||||
|
assignment_id=self.assignment.id,
|
||||||
|
)
|
||||||
|
self.assertEqual(db_entry.completion_status, "EVALUATION_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
|
||||||
|
completion_data_string = json.dumps(
|
||||||
|
{
|
||||||
|
user_text_input["id"]: {
|
||||||
|
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
).replace('"', '\\"')
|
||||||
|
|
||||||
|
query = f"""
|
||||||
|
mutation {{
|
||||||
|
upsert_assignment_completion(
|
||||||
|
assignment_id: {self.assignment.id}
|
||||||
|
assignment_user_id: {self.student.id}
|
||||||
|
course_session_id: {self.course_session.id}
|
||||||
|
completion_status: EVALUATION_SUBMITTED
|
||||||
|
completion_data_string: "{completion_data_string}"
|
||||||
|
evaluation_grade: 4.5,
|
||||||
|
evaluation_points: 16,
|
||||||
|
) {{
|
||||||
|
assignment_completion {{
|
||||||
|
id
|
||||||
|
completion_status
|
||||||
|
completion_data
|
||||||
|
assignment_user {{ id }}
|
||||||
|
assignment {{ id }}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.query(query)
|
||||||
|
self.assertResponseNoErrors(response)
|
||||||
|
|
||||||
|
data = json.loads(response.content)["data"]["upsert_assignment_completion"][
|
||||||
|
"assignment_completion"
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(data["assignment_user"]["id"], str(self.student.id))
|
||||||
|
self.assertEqual(data["assignment"]["id"], str(self.assignment.id))
|
||||||
|
self.assertEqual(data["completion_status"], "EVALUATION_SUBMITTED")
|
||||||
|
self.assertDictEqual(
|
||||||
|
data["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.course_session.id,
|
||||||
|
assignment_id=self.assignment.id,
|
||||||
|
)
|
||||||
|
self.assertEqual(db_entry.completion_status, "EVALUATION_SUBMITTED")
|
||||||
|
self.assertEqual(db_entry.evaluation_grade, 4.5)
|
||||||
|
self.assertEqual(db_entry.evaluation_points, 16)
|
||||||
|
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!"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# `EVALUATION_SUBMITTED` will create a new AssignmentCompletionAuditLog
|
||||||
|
acl = AssignmentCompletionAuditLog.objects.get(
|
||||||
|
assignment_user=self.student,
|
||||||
|
course_session_id=self.course_session.id,
|
||||||
|
assignment_id=self.assignment.id,
|
||||||
|
completion_status="EVALUATION_SUBMITTED",
|
||||||
|
)
|
||||||
|
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!"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
@ -8,6 +8,7 @@ from vbv_lernwelt.assignment.models import (
|
||||||
Assignment,
|
Assignment,
|
||||||
AssignmentCompletion,
|
AssignmentCompletion,
|
||||||
AssignmentCompletionAuditLog,
|
AssignmentCompletionAuditLog,
|
||||||
|
AssignmentCompletionStatus,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.assignment.services import update_assignment_completion
|
from vbv_lernwelt.assignment.services import update_assignment_completion
|
||||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
|
|
@ -161,7 +162,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="SUBMITTED",
|
completion_status=AssignmentCompletionStatus.SUBMITTED,
|
||||||
)
|
)
|
||||||
|
|
||||||
ac = AssignmentCompletion.objects.get(
|
ac = AssignmentCompletion.objects.get(
|
||||||
|
|
@ -216,7 +217,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
submitted_at=timezone.now(),
|
submitted_at=timezone.now(),
|
||||||
completion_status="SUBMITTED",
|
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
|
||||||
completion_data={
|
completion_data={
|
||||||
user_text_input0["id"]: {
|
user_text_input0["id"]: {
|
||||||
"user_data": {"text": "Am Anfang war das Wort... 0"}
|
"user_data": {"text": "Am Anfang war das Wort... 0"}
|
||||||
|
|
@ -232,7 +233,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="SUBMITTED",
|
completion_status=AssignmentCompletionStatus.SUBMITTED,
|
||||||
)
|
)
|
||||||
|
|
||||||
# can submit twice with flag
|
# can submit twice with flag
|
||||||
|
|
@ -240,7 +241,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="SUBMITTED",
|
completion_status=AssignmentCompletionStatus.SUBMITTED,
|
||||||
validate_completion_status_change=False,
|
validate_completion_status_change=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -280,7 +281,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="SUBMITTED",
|
completion_status=AssignmentCompletionStatus.SUBMITTED,
|
||||||
copy_task_data=True,
|
copy_task_data=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -306,7 +307,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="SUBMITTED",
|
completion_status=AssignmentCompletionStatus.SUBMITTED,
|
||||||
)
|
)
|
||||||
|
|
||||||
evaluation_task = self.assignment.get_evaluation_tasks()[0]
|
evaluation_task = self.assignment.get_evaluation_tasks()[0]
|
||||||
|
|
@ -320,7 +321,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
"expert_data": {"points": 2, "text": "Gut gemacht!"}
|
"expert_data": {"points": 2, "text": "Gut gemacht!"}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
completion_status="EVALUATION_IN_PROGRESS",
|
completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
|
||||||
evaluation_user=self.trainer,
|
evaluation_user=self.trainer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -352,7 +353,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="SUBMITTED",
|
completion_status=AssignmentCompletionStatus.SUBMITTED,
|
||||||
completion_data={
|
completion_data={
|
||||||
user_text_input["id"]: {
|
user_text_input["id"]: {
|
||||||
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
|
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
|
||||||
|
|
@ -369,7 +370,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
|
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
completion_status="EVALUATION_IN_PROGRESS",
|
completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
|
||||||
evaluation_user=self.trainer,
|
evaluation_user=self.trainer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -404,7 +405,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="IN_PROGRESS",
|
completion_status=AssignmentCompletionStatus.IN_PROGRESS.value,
|
||||||
completion_data={
|
completion_data={
|
||||||
user_text_input["id"]: {
|
user_text_input["id"]: {
|
||||||
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
|
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
|
||||||
|
|
@ -417,7 +418,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="EVALUATION_IN_PROGRESS",
|
completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
|
||||||
completion_data={
|
completion_data={
|
||||||
user_text_input["id"]: {
|
user_text_input["id"]: {
|
||||||
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
|
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
|
||||||
|
|
@ -445,7 +446,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment_user=self.user,
|
assignment_user=self.user,
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_status="SUBMITTED",
|
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
|
||||||
completion_data={
|
completion_data={
|
||||||
user_text_input["id"]: {
|
user_text_input["id"]: {
|
||||||
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
|
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
|
||||||
|
|
@ -464,7 +465,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
"expert_data": {"points": 2, "text": "Gut gemacht!"}
|
"expert_data": {"points": 2, "text": "Gut gemacht!"}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
completion_status="EVALUATION_IN_PROGRESS",
|
completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
|
||||||
evaluation_user=self.trainer,
|
evaluation_user=self.trainer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -475,7 +476,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_data={},
|
completion_data={},
|
||||||
completion_status="EVALUATION_SUBMITTED",
|
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED,
|
||||||
evaluation_user=self.trainer,
|
evaluation_user=self.trainer,
|
||||||
evaluation_grade=None,
|
evaluation_grade=None,
|
||||||
evaluation_points=None,
|
evaluation_points=None,
|
||||||
|
|
@ -486,7 +487,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
|
||||||
assignment=self.assignment,
|
assignment=self.assignment,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
completion_data={},
|
completion_data={},
|
||||||
completion_status="EVALUATION_SUBMITTED",
|
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED,
|
||||||
evaluation_user=self.trainer,
|
evaluation_user=self.trainer,
|
||||||
evaluation_grade=4.5,
|
evaluation_grade=4.5,
|
||||||
evaluation_points=16,
|
evaluation_points=16,
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,14 @@
|
||||||
import structlog
|
import structlog
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.generics import get_object_or_404
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from wagtail.models import Page
|
|
||||||
|
|
||||||
from vbv_lernwelt.assignment.models import AssignmentCompletion
|
from vbv_lernwelt.assignment.models import AssignmentCompletion
|
||||||
from vbv_lernwelt.assignment.serializers import AssignmentCompletionSerializer
|
from vbv_lernwelt.course.permissions import is_course_session_expert
|
||||||
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,
|
|
||||||
has_course_access_by_page_request,
|
|
||||||
is_course_session_expert,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _request_assignment_completion(
|
|
||||||
assignment_id, course_session_id, assignment_user_id
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
response_data = AssignmentCompletionSerializer(
|
|
||||||
AssignmentCompletion.objects.get(
|
|
||||||
assignment_user_id=assignment_user_id,
|
|
||||||
assignment_id=assignment_id,
|
|
||||||
course_session_id=course_session_id,
|
|
||||||
),
|
|
||||||
).data
|
|
||||||
|
|
||||||
return Response(status=200, data=response_data)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(e)
|
|
||||||
return Response({"error": str(e)}, status=404)
|
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
|
||||||
def request_assignment_completion(request, assignment_id, course_session_id):
|
|
||||||
course_id = get_object_or_404(CourseSession, id=course_session_id).course_id
|
|
||||||
if has_course_access(request.user, course_id):
|
|
||||||
return _request_assignment_completion(
|
|
||||||
assignment_id=assignment_id,
|
|
||||||
course_session_id=course_session_id,
|
|
||||||
assignment_user_id=request.user.id,
|
|
||||||
)
|
|
||||||
raise PermissionDenied()
|
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
|
||||||
def request_assignment_completion_for_user(
|
|
||||||
request, assignment_id, course_session_id, user_id
|
|
||||||
):
|
|
||||||
if request.user.id == user_id or is_course_session_expert(
|
|
||||||
request.user, course_session_id
|
|
||||||
):
|
|
||||||
return _request_assignment_completion(
|
|
||||||
assignment_id=assignment_id,
|
|
||||||
course_session_id=course_session_id,
|
|
||||||
assignment_user_id=user_id,
|
|
||||||
)
|
|
||||||
raise PermissionDenied()
|
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
def request_assignment_completion_status(request, assignment_id, course_session_id):
|
def request_assignment_completion_status(request, assignment_id, course_session_id):
|
||||||
# TODO quickfix before GraphQL...
|
# TODO quickfix before GraphQL...
|
||||||
|
|
@ -75,102 +20,3 @@ def request_assignment_completion_status(request, assignment_id, course_session_
|
||||||
return Response(status=200, data=qs)
|
return Response(status=200, data=qs)
|
||||||
|
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
|
||||||
@api_view(["POST"])
|
|
||||||
def upsert_user_assignment_completion(request):
|
|
||||||
try:
|
|
||||||
assignment_id = request.data.get("assignment_id")
|
|
||||||
course_session_id = request.data.get("course_session_id")
|
|
||||||
completion_status = request.data.get("completion_status", "IN_PROGRESS")
|
|
||||||
completion_data = request.data.get("completion_data", {})
|
|
||||||
|
|
||||||
assignment_page = Page.objects.get(id=assignment_id)
|
|
||||||
|
|
||||||
if not has_course_access_by_page_request(request, assignment_page):
|
|
||||||
raise PermissionDenied()
|
|
||||||
|
|
||||||
assignment = assignment_page.specific
|
|
||||||
|
|
||||||
ac = update_assignment_completion(
|
|
||||||
assignment_user=request.user,
|
|
||||||
assignment=assignment,
|
|
||||||
course_session=CourseSession.objects.get(id=course_session_id),
|
|
||||||
completion_data=completion_data,
|
|
||||||
completion_status=completion_status,
|
|
||||||
copy_task_data=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
"upsert_user_assignment_completion successful",
|
|
||||||
label="assignment_api",
|
|
||||||
assignment_id=assignment.id,
|
|
||||||
assignment_title=assignment.title,
|
|
||||||
assignment_user_id=request.user.id,
|
|
||||||
course_session_id=course_session_id,
|
|
||||||
completion_status=completion_status,
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
@api_view(["POST"])
|
|
||||||
def evaluate_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", "EVALUATION_IN_PROGRESS"
|
|
||||||
)
|
|
||||||
completion_data = request.data.get("completion_data", {})
|
|
||||||
evaluation_grade = request.data.get("evaluation_grade", None)
|
|
||||||
evaluation_points = request.data.get("evaluation_grade", None)
|
|
||||||
|
|
||||||
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,
|
|
||||||
evaluation_user=request.user,
|
|
||||||
evaluation_grade=evaluation_grade,
|
|
||||||
evaluation_points=evaluation_points,
|
|
||||||
)
|
|
||||||
|
|
||||||
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,
|
|
||||||
evaluation_user_id=request.user.id,
|
|
||||||
evaluation_grade=evaluation_grade,
|
|
||||||
evaluation_points=evaluation_points,
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import traceback
|
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from graphene import ResolveInfo
|
from graphene import ResolveInfo
|
||||||
|
|
||||||
|
|
@ -12,5 +10,7 @@ class GrapheneErrorLoggingMiddleware(object):
|
||||||
try:
|
try:
|
||||||
return next(root, info, **args)
|
return next(root, info, **args)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error(traceback.format_exc())
|
logger.error(
|
||||||
|
"GraphQL error", label="graphql_error", info=info, exc_info=error
|
||||||
|
)
|
||||||
raise error
|
raise error
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue