feat: praxis assignments API
This commit is contained in:
parent
56ecb72751
commit
a48ac35e62
|
|
@ -0,0 +1,115 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from vbv_lernwelt.assignment.models import (
|
||||||
|
Assignment,
|
||||||
|
AssignmentCompletion,
|
||||||
|
AssignmentCompletionStatus,
|
||||||
|
AssignmentType,
|
||||||
|
)
|
||||||
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.course.models import CourseSession
|
||||||
|
from vbv_lernwelt.course_session.models import CourseSessionAssignment
|
||||||
|
from vbv_lernwelt.learning_mentor.entities import (
|
||||||
|
CompletionStatus,
|
||||||
|
PraxisAssignmentCompletion,
|
||||||
|
PraxisAssignmentStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_assignment_completions(
|
||||||
|
course_session: CourseSession, assignment: Assignment, participants: List[User]
|
||||||
|
) -> List[PraxisAssignmentCompletion]:
|
||||||
|
evaluation_results = AssignmentCompletion.objects.filter(
|
||||||
|
assignment_user__in=participants,
|
||||||
|
course_session=course_session,
|
||||||
|
assignment=assignment,
|
||||||
|
).values("completion_status", "assignment_user__last_name", "assignment_user")
|
||||||
|
|
||||||
|
user_status_map = {}
|
||||||
|
for result in evaluation_results:
|
||||||
|
completion_status = result["completion_status"]
|
||||||
|
|
||||||
|
if completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED.value:
|
||||||
|
status = CompletionStatus.EVALUATED
|
||||||
|
elif completion_status in [
|
||||||
|
AssignmentCompletionStatus.SUBMITTED.value,
|
||||||
|
AssignmentCompletionStatus.EVALUATION_IN_PROGRESS.value,
|
||||||
|
]:
|
||||||
|
status = CompletionStatus.SUBMITTED
|
||||||
|
else:
|
||||||
|
status = CompletionStatus.UNKNOWN
|
||||||
|
|
||||||
|
user_status_map[result["assignment_user"]] = (
|
||||||
|
status,
|
||||||
|
result["assignment_user__last_name"],
|
||||||
|
)
|
||||||
|
|
||||||
|
status_priority = {
|
||||||
|
CompletionStatus.SUBMITTED: 1,
|
||||||
|
CompletionStatus.EVALUATED: 2,
|
||||||
|
CompletionStatus.UNKNOWN: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
sorted_participants = sorted(
|
||||||
|
participants,
|
||||||
|
key=lambda u: (
|
||||||
|
status_priority.get(
|
||||||
|
user_status_map.get(u.id, (CompletionStatus.UNKNOWN, ""))[0]
|
||||||
|
),
|
||||||
|
user_status_map.get(u.id, ("", u.last_name))[1],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
PraxisAssignmentCompletion(
|
||||||
|
status=user_status_map.get(
|
||||||
|
user.id, (CompletionStatus.UNKNOWN, user.last_name)
|
||||||
|
)[0],
|
||||||
|
user_id=user.id,
|
||||||
|
last_name=user.last_name,
|
||||||
|
)
|
||||||
|
for user in sorted_participants
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_praxis_assignments(
|
||||||
|
course_session: CourseSession, participants: List[User]
|
||||||
|
) -> List[PraxisAssignmentStatus]:
|
||||||
|
records = []
|
||||||
|
|
||||||
|
if not participants:
|
||||||
|
return records
|
||||||
|
|
||||||
|
for course_session_assignment in CourseSessionAssignment.objects.filter(
|
||||||
|
course_session=course_session,
|
||||||
|
learning_content__content_assignment__assignment_type__in=[
|
||||||
|
AssignmentType.PRAXIS_ASSIGNMENT.value,
|
||||||
|
],
|
||||||
|
):
|
||||||
|
learning_content = course_session_assignment.learning_content
|
||||||
|
|
||||||
|
completions = get_assignment_completions(
|
||||||
|
course_session=course_session,
|
||||||
|
assignment=learning_content.content_assignment,
|
||||||
|
participants=participants,
|
||||||
|
)
|
||||||
|
|
||||||
|
submitted_count = len(
|
||||||
|
[
|
||||||
|
completion
|
||||||
|
for completion in completions
|
||||||
|
if completion.status == CompletionStatus.SUBMITTED
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
records.append(
|
||||||
|
PraxisAssignmentStatus(
|
||||||
|
id=course_session_assignment.id,
|
||||||
|
title=learning_content.content_assignment.title,
|
||||||
|
circle_id=learning_content.get_circle().id,
|
||||||
|
pending_evaluations=submitted_count,
|
||||||
|
completions=completions,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class CompletionStatus(str, Enum):
|
||||||
|
UNKNOWN = "UNKNOWN"
|
||||||
|
SUBMITTED = "SUBMITTED"
|
||||||
|
EVALUATED = "EVALUATED"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PraxisAssignmentCompletion:
|
||||||
|
status: CompletionStatus
|
||||||
|
user_id: str
|
||||||
|
last_name: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PraxisAssignmentStatus:
|
||||||
|
id: str
|
||||||
|
title: str
|
||||||
|
circle_id: str
|
||||||
|
pending_evaluations: int
|
||||||
|
completions: List[PraxisAssignmentCompletion]
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
class PraxisAssignmentCompletionSerializer(serializers.Serializer):
|
||||||
|
status = serializers.SerializerMethodField()
|
||||||
|
user_id = serializers.CharField()
|
||||||
|
last_name = serializers.CharField()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_status(obj):
|
||||||
|
return obj.status.value
|
||||||
|
|
||||||
|
|
||||||
|
class PraxisAssignmentStatusSerializer(serializers.Serializer):
|
||||||
|
id = serializers.CharField()
|
||||||
|
title = serializers.CharField()
|
||||||
|
circle_id = serializers.CharField()
|
||||||
|
pending_evaluations = serializers.IntegerField()
|
||||||
|
completions = PraxisAssignmentCompletionSerializer(many=True)
|
||||||
|
|
@ -2,10 +2,19 @@ from django.urls import reverse
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
|
from vbv_lernwelt.assignment.models import (
|
||||||
|
AssignmentCompletion,
|
||||||
|
AssignmentCompletionStatus,
|
||||||
|
AssignmentType,
|
||||||
|
)
|
||||||
from vbv_lernwelt.course.creators.test_utils import (
|
from vbv_lernwelt.course.creators.test_utils import (
|
||||||
add_course_session_user,
|
add_course_session_user,
|
||||||
|
create_assignment,
|
||||||
|
create_assignment_learning_content,
|
||||||
|
create_circle,
|
||||||
create_course,
|
create_course,
|
||||||
create_course_session,
|
create_course_session,
|
||||||
|
create_course_session_assignment,
|
||||||
create_user,
|
create_user,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
|
|
@ -14,8 +23,20 @@ from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||||
|
|
||||||
class LearningMentorAPITest(APITestCase):
|
class LearningMentorAPITest(APITestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
course, course_page = create_course("Test Course")
|
self.course, self.course_page = create_course("Test Course")
|
||||||
self.course_session = create_course_session(course=course, title="Test VV")
|
self.course_session = create_course_session(course=self.course, title="Test VV")
|
||||||
|
|
||||||
|
circle, _ = create_circle(title="Circle", course_page=self.course_page)
|
||||||
|
|
||||||
|
self.assignment = create_assignment(
|
||||||
|
course=self.course, assignment_type=AssignmentType.PRAXIS_ASSIGNMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
lca = create_assignment_learning_content(circle, self.assignment)
|
||||||
|
create_course_session_assignment(
|
||||||
|
course_session=self.course_session, learning_content_assignment=lca
|
||||||
|
)
|
||||||
|
|
||||||
self.mentor = create_user("mentor")
|
self.mentor = create_user("mentor")
|
||||||
self.participant_1 = add_course_session_user(
|
self.participant_1 = add_course_session_user(
|
||||||
self.course_session,
|
self.course_session,
|
||||||
|
|
@ -87,3 +108,48 @@ class LearningMentorAPITest(APITestCase):
|
||||||
self.assertEqual(participant_1["email"], "participant_1@example.com")
|
self.assertEqual(participant_1["email"], "participant_1@example.com")
|
||||||
self.assertEqual(participant_1["first_name"], "Test")
|
self.assertEqual(participant_1["first_name"], "Test")
|
||||||
self.assertEqual(participant_1["last_name"], "Participant_1")
|
self.assertEqual(participant_1["last_name"], "Participant_1")
|
||||||
|
|
||||||
|
def test_api_praxis_assignments(self) -> None:
|
||||||
|
# GIVEN
|
||||||
|
participants = [self.participant_1, self.participant_2, self.participant_3]
|
||||||
|
self.client.force_login(self.mentor)
|
||||||
|
|
||||||
|
mentor = LearningMentor.objects.create(
|
||||||
|
mentor=self.mentor,
|
||||||
|
course=self.course_session.course,
|
||||||
|
)
|
||||||
|
mentor.participants.set(participants)
|
||||||
|
|
||||||
|
AssignmentCompletion.objects.create(
|
||||||
|
assignment_user=self.participant_1.user,
|
||||||
|
course_session=self.course_session,
|
||||||
|
assignment=self.assignment,
|
||||||
|
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
AssignmentCompletion.objects.create(
|
||||||
|
assignment_user=self.participant_3.user,
|
||||||
|
course_session=self.course_session,
|
||||||
|
assignment=self.assignment,
|
||||||
|
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
# WHEN
|
||||||
|
response = self.client.get(self.url)
|
||||||
|
|
||||||
|
# THEN
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data["praxis_assignments"]), 1)
|
||||||
|
|
||||||
|
assignment = response.data["praxis_assignments"][0]
|
||||||
|
self.assertEqual(assignment["pending_evaluations"], 1)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
assignment["completions"][0]["last_name"], self.participant_3.user.last_name
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
assignment["completions"][1]["last_name"], self.participant_1.user.last_name
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
assignment["completions"][2]["last_name"], self.participant_2.user.last_name
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ from vbv_lernwelt.course.creators.test_utils import (
|
||||||
create_course_session_assignment,
|
create_course_session_assignment,
|
||||||
create_user,
|
create_user,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.learning_mentor.views import (
|
from vbv_lernwelt.learning_mentor.content.praxis_assignment import (
|
||||||
CompletionStatus,
|
|
||||||
get_assignment_completions,
|
get_assignment_completions,
|
||||||
get_praxis_assignments,
|
get_praxis_assignments,
|
||||||
)
|
)
|
||||||
|
from vbv_lernwelt.learning_mentor.entities import CompletionStatus
|
||||||
|
|
||||||
|
|
||||||
class AttendanceServicesTestCase(TestCase):
|
class AttendanceServicesTestCase(TestCase):
|
||||||
|
|
|
||||||
|
|
@ -1,140 +1,14 @@
|
||||||
from dataclasses import dataclass
|
|
||||||
from enum import Enum
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
from rest_framework.generics import get_object_or_404
|
from rest_framework.generics import get_object_or_404
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from vbv_lernwelt.assignment.models import (
|
|
||||||
Assignment,
|
|
||||||
AssignmentCompletion,
|
|
||||||
AssignmentCompletionStatus,
|
|
||||||
AssignmentType,
|
|
||||||
)
|
|
||||||
from vbv_lernwelt.core.models import User
|
|
||||||
from vbv_lernwelt.core.serializers import UserSerializer
|
from vbv_lernwelt.core.serializers import UserSerializer
|
||||||
from vbv_lernwelt.course.models import CourseSession
|
from vbv_lernwelt.course.models import CourseSession
|
||||||
from vbv_lernwelt.course_session.models import CourseSessionAssignment
|
from vbv_lernwelt.learning_mentor.content.praxis_assignment import (
|
||||||
|
get_praxis_assignments,
|
||||||
|
)
|
||||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||||
|
from vbv_lernwelt.learning_mentor.serializers import PraxisAssignmentStatusSerializer
|
||||||
|
|
||||||
class CompletionStatus(str, Enum):
|
|
||||||
UNKNOWN = "UNKNOWN"
|
|
||||||
SUBMITTED = "SUBMITTED"
|
|
||||||
EVALUATED = "EVALUATED"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PraxisAssignmentCompletion:
|
|
||||||
status: CompletionStatus
|
|
||||||
user_id: str
|
|
||||||
last_name: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PraxisAssignmentStatus:
|
|
||||||
id: str
|
|
||||||
title: str
|
|
||||||
circle_id: str
|
|
||||||
pending_evaluations: int
|
|
||||||
completions: List[PraxisAssignmentCompletion]
|
|
||||||
|
|
||||||
|
|
||||||
def get_assignment_completions(
|
|
||||||
course_session: CourseSession, assignment: Assignment, participants: List[User]
|
|
||||||
) -> List[PraxisAssignmentCompletion]:
|
|
||||||
evaluation_results = AssignmentCompletion.objects.filter(
|
|
||||||
assignment_user__in=participants,
|
|
||||||
course_session=course_session,
|
|
||||||
assignment=assignment,
|
|
||||||
).values("completion_status", "assignment_user__last_name", "assignment_user")
|
|
||||||
|
|
||||||
user_status_map = {}
|
|
||||||
for result in evaluation_results:
|
|
||||||
completion_status = result["completion_status"]
|
|
||||||
|
|
||||||
if completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED.value:
|
|
||||||
status = CompletionStatus.EVALUATED
|
|
||||||
elif completion_status in [
|
|
||||||
AssignmentCompletionStatus.SUBMITTED.value,
|
|
||||||
AssignmentCompletionStatus.EVALUATION_IN_PROGRESS.value,
|
|
||||||
]:
|
|
||||||
status = CompletionStatus.SUBMITTED
|
|
||||||
else:
|
|
||||||
status = CompletionStatus.UNKNOWN
|
|
||||||
|
|
||||||
user_status_map[result["assignment_user"]] = (
|
|
||||||
status,
|
|
||||||
result["assignment_user__last_name"],
|
|
||||||
)
|
|
||||||
|
|
||||||
status_priority = {
|
|
||||||
CompletionStatus.SUBMITTED: 1,
|
|
||||||
CompletionStatus.EVALUATED: 2,
|
|
||||||
CompletionStatus.UNKNOWN: 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
sorted_participants = sorted(
|
|
||||||
participants,
|
|
||||||
key=lambda u: (
|
|
||||||
status_priority.get(
|
|
||||||
user_status_map.get(u.id, (CompletionStatus.UNKNOWN, ""))[0]
|
|
||||||
),
|
|
||||||
user_status_map.get(u.id, ("", u.last_name))[1],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return [
|
|
||||||
PraxisAssignmentCompletion(
|
|
||||||
status=user_status_map.get(
|
|
||||||
user.id, (CompletionStatus.UNKNOWN, user.last_name)
|
|
||||||
)[0],
|
|
||||||
user_id=user.id,
|
|
||||||
last_name=user.last_name,
|
|
||||||
)
|
|
||||||
for user in sorted_participants
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def get_praxis_assignments(
|
|
||||||
course_session: CourseSession, participants: List[User]
|
|
||||||
) -> List[PraxisAssignmentStatus]:
|
|
||||||
records = []
|
|
||||||
|
|
||||||
for course_session_assignment in CourseSessionAssignment.objects.filter(
|
|
||||||
course_session=course_session,
|
|
||||||
learning_content__content_assignment__assignment_type__in=[
|
|
||||||
AssignmentType.PRAXIS_ASSIGNMENT.value,
|
|
||||||
],
|
|
||||||
):
|
|
||||||
learning_content = course_session_assignment.learning_content
|
|
||||||
|
|
||||||
completions = get_assignment_completions(
|
|
||||||
course_session=course_session,
|
|
||||||
assignment=learning_content.content_assignment,
|
|
||||||
participants=participants,
|
|
||||||
)
|
|
||||||
|
|
||||||
submitted_count = len(
|
|
||||||
[
|
|
||||||
completion
|
|
||||||
for completion in completions
|
|
||||||
if completion.status == CompletionStatus.SUBMITTED
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
records.append(
|
|
||||||
PraxisAssignmentStatus(
|
|
||||||
id=course_session_assignment.id,
|
|
||||||
title=learning_content.content_assignment.title,
|
|
||||||
circle_id=learning_content.get_circle().id,
|
|
||||||
pending_evaluations=submitted_count,
|
|
||||||
completions=completions,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return records
|
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
|
|
@ -150,12 +24,15 @@ def mentor_summary(request, course_session_id: int):
|
||||||
)
|
)
|
||||||
|
|
||||||
participants = mentor.participants.filter(course_session=course_session)
|
participants = mentor.participants.filter(course_session=course_session)
|
||||||
|
users = [p.user for p in participants]
|
||||||
|
|
||||||
|
praxis_assignments = get_praxis_assignments(course_session, users)
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
"participants": [UserSerializer(p.user).data for p in participants],
|
"participants": [UserSerializer(user).data for user in users],
|
||||||
"praxis_assignments": get_praxis_assignments(
|
"praxis_assignments": PraxisAssignmentStatusSerializer(
|
||||||
course_session, participants
|
praxis_assignments, many=True
|
||||||
),
|
).data,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue