feat: praxis assignments
This commit is contained in:
parent
278d659905
commit
669dfdd7c1
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 3.2.20 on 2023-12-01 10:11
|
# Generated by Django 3.2.20 on 2023-12-06 09:32
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
@ -9,6 +9,7 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
("course", "0005_course_enable_circle_documents"),
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -24,7 +25,7 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"mentor",
|
"mentor",
|
||||||
models.ForeignKey(
|
models.OneToOneField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
on_delete=django.db.models.deletion.SET_NULL,
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
|
@ -34,7 +35,7 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"students",
|
"students",
|
||||||
models.ManyToManyField(
|
models.ManyToManyField(
|
||||||
related_name="students", to=settings.AUTH_USER_MODEL
|
related_name="students", to="course.CourseSessionUser"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
|
|
||||||
|
|
||||||
class LearningMentor(models.Model):
|
class LearningMentor(models.Model):
|
||||||
mentor_email = models.EmailField(primary_key=True)
|
mentor_email = models.EmailField(primary_key=True)
|
||||||
mentor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
mentor = models.OneToOneField(
|
||||||
|
User, on_delete=models.SET_NULL, null=True, blank=True
|
||||||
|
)
|
||||||
|
|
||||||
students = models.ManyToManyField(User, related_name="students")
|
students = models.ManyToManyField(CourseSessionUser, related_name="students")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.mentor_email
|
return self.mentor_email
|
||||||
|
|
||||||
|
@property
|
||||||
|
def course_sessions(self):
|
||||||
|
return self.students.values_list("course_session", flat=True).distinct()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from vbv_lernwelt.assignment.models import (
|
||||||
|
AssignmentCompletion,
|
||||||
|
AssignmentCompletionStatus,
|
||||||
|
AssignmentType,
|
||||||
|
)
|
||||||
|
from vbv_lernwelt.course.creators.test_utils import (
|
||||||
|
create_assignment,
|
||||||
|
create_circle,
|
||||||
|
create_course,
|
||||||
|
create_course_session,
|
||||||
|
create_user,
|
||||||
|
)
|
||||||
|
from vbv_lernwelt.learning_mentor.views import (
|
||||||
|
CompletionStatus,
|
||||||
|
get_assignment_completions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AttendanceServicesTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user1 = create_user("Alpha")
|
||||||
|
self.user2 = create_user("Beta")
|
||||||
|
self.user3 = create_user("Kappa")
|
||||||
|
self.user4 = create_user("Gamma")
|
||||||
|
|
||||||
|
self.course, self.course_page = create_course("Test Course")
|
||||||
|
self.course_session = create_course_session(course=self.course, title=":)")
|
||||||
|
self.circle, _ = create_circle(title="Circle", course_page=self.course_page)
|
||||||
|
|
||||||
|
self.assignment = create_assignment(
|
||||||
|
course=self.course, assignment_type=AssignmentType.CASEWORK
|
||||||
|
)
|
||||||
|
|
||||||
|
AssignmentCompletion.objects.create(
|
||||||
|
assignment_user=self.user1,
|
||||||
|
course_session=self.course_session,
|
||||||
|
assignment=self.assignment,
|
||||||
|
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
|
||||||
|
)
|
||||||
|
AssignmentCompletion.objects.create(
|
||||||
|
assignment_user=self.user2,
|
||||||
|
course_session=self.course_session,
|
||||||
|
assignment=self.assignment,
|
||||||
|
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
|
||||||
|
)
|
||||||
|
AssignmentCompletion.objects.create(
|
||||||
|
assignment_user=self.user3,
|
||||||
|
course_session=self.course_session,
|
||||||
|
assignment=self.assignment,
|
||||||
|
completion_status=AssignmentCompletionStatus.IN_PROGRESS.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_assignments(self):
|
||||||
|
# GIVEN
|
||||||
|
participants = [self.user1, self.user2, self.user3, self.user4]
|
||||||
|
|
||||||
|
# WHEN
|
||||||
|
results = get_assignment_completions(
|
||||||
|
self.course_session, self.assignment, participants
|
||||||
|
)
|
||||||
|
|
||||||
|
# THEN
|
||||||
|
expected_order = ["Beta", "Alpha", "Gamma", "Kappa"]
|
||||||
|
expected_statuses = {
|
||||||
|
"Alpha": CompletionStatus.EVALUATED, # user1
|
||||||
|
"Beta": CompletionStatus.SUBMITTED, # user2
|
||||||
|
"Gamma": CompletionStatus.PENDING, # user4 (no AssignmentCompletion)
|
||||||
|
"Kappa": CompletionStatus.PENDING, # user3 (IN_PROGRESS should be PENDING)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assertEqual(len(results), len(participants))
|
||||||
|
for i, result in enumerate(results):
|
||||||
|
self.assertEqual(result.last_name, expected_order[i])
|
||||||
|
self.assertEqual(result.status, expected_statuses[result.last_name])
|
||||||
|
|
@ -1,38 +1,127 @@
|
||||||
|
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.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.course.models import CourseSession
|
from vbv_lernwelt.course.models import CourseSession
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
from vbv_lernwelt.course_session.models import CourseSessionAssignment
|
||||||
from vbv_lernwelt.feedback.utils import feedback_users
|
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||||
from vbv_lernwelt.learnpath.models import Circle
|
|
||||||
|
|
||||||
|
|
||||||
def get_practical_assignment():
|
class CompletionStatus(str, Enum):
|
||||||
pass
|
PENDING = "PENDING"
|
||||||
|
SUBMITTED = "SUBMITTED"
|
||||||
|
EVALUATED = "EVALUATED"
|
||||||
|
|
||||||
|
|
||||||
def get_feedbacks(course_session: CourseSession):
|
@dataclass
|
||||||
circle_feedbacks = []
|
class PraxisAssignmentCompletion:
|
||||||
|
status: CompletionStatus
|
||||||
|
user_id: str
|
||||||
|
last_name: str
|
||||||
|
|
||||||
fbs = FeedbackResponse.objects.filter(
|
|
||||||
submitted=True,
|
@dataclass
|
||||||
|
class PraxisAssignmentStatus:
|
||||||
|
id: str
|
||||||
|
title: str
|
||||||
|
circle_id: str
|
||||||
|
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,
|
course_session=course_session,
|
||||||
feedback_user__in=feedback_users(course_session.id),
|
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.PENDING
|
||||||
|
|
||||||
|
user_status_map[result["assignment_user"]] = (
|
||||||
|
status,
|
||||||
|
result["assignment_user__last_name"],
|
||||||
|
)
|
||||||
|
|
||||||
|
status_priority = {
|
||||||
|
CompletionStatus.SUBMITTED: 1,
|
||||||
|
CompletionStatus.EVALUATED: 2,
|
||||||
|
CompletionStatus.PENDING: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
sorted_participants = sorted(
|
||||||
|
participants,
|
||||||
|
key=lambda u: (
|
||||||
|
status_priority.get(
|
||||||
|
user_status_map.get(u.id, (CompletionStatus.PENDING, ""))[0]
|
||||||
|
),
|
||||||
|
user_status_map.get(u.id, ("", u.last_name))[1],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
PraxisAssignmentCompletion(
|
||||||
|
status=user_status_map.get(
|
||||||
|
user.id, (CompletionStatus.PENDING, user.last_name)
|
||||||
|
)[0],
|
||||||
|
user_id=user.id,
|
||||||
|
last_name=user.last_name,
|
||||||
|
)
|
||||||
|
for user in sorted_participants
|
||||||
|
]
|
||||||
|
|
||||||
def get_all_feedbacks(course_session: CourseSession):
|
|
||||||
circle_feedbacks = []
|
|
||||||
|
|
||||||
circles = (
|
def get_praxis_assignments(
|
||||||
course_session.course.get_learning_path()
|
course_session: CourseSession, participants: List[User]
|
||||||
.get_descendants()
|
) -> List[PraxisAssignmentStatus]:
|
||||||
.live()
|
records = []
|
||||||
.specific()
|
|
||||||
.exact_type(Circle)
|
|
||||||
)
|
|
||||||
|
|
||||||
print(circles)
|
for course_session_assignment in CourseSessionAssignment.objects.filter(
|
||||||
|
course_session=course_session,
|
||||||
|
learning_content__content_assignment__assignment_type__in=[
|
||||||
|
# TODO: Switch to PRAXIS_ASSIGNMENT
|
||||||
|
AssignmentType.CASEWORK.value,
|
||||||
|
],
|
||||||
|
):
|
||||||
|
learning_content = course_session_assignment.learning_content
|
||||||
|
records.append(
|
||||||
|
PraxisAssignmentStatus(
|
||||||
|
id=course_session_assignment.id,
|
||||||
|
title=learning_content.content_assignment.title,
|
||||||
|
circle_id=learning_content.get_circle().id,
|
||||||
|
completions=get_assignment_completions(
|
||||||
|
course_session=course_session,
|
||||||
|
assignment=learning_content.content_assignment,
|
||||||
|
participants=participants,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
|
|
@ -43,6 +132,13 @@ def mentor_summary(request, course_session_id: int):
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
course_session = CourseSession.objects.get(id=course_session_id)
|
course_session = CourseSession.objects.get(id=course_session_id)
|
||||||
|
|
||||||
get_all_feedbacks(course_session)
|
participants = LearningMentor.objects.filter(mentor=request.user)
|
||||||
|
|
||||||
return Response({})
|
return Response(
|
||||||
|
{
|
||||||
|
"participants": [UserSerializer(s).data for s in participants],
|
||||||
|
"praxis_assignments": get_praxis_assignments(
|
||||||
|
course_session, participants
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue