diff --git a/server/vbv_lernwelt/learning_mentor/migrations/0001_initial.py b/server/vbv_lernwelt/learning_mentor/migrations/0001_initial.py index ef477003..7ca498da 100644 --- a/server/vbv_lernwelt/learning_mentor/migrations/0001_initial.py +++ b/server/vbv_lernwelt/learning_mentor/migrations/0001_initial.py @@ -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 from django.conf import settings @@ -9,6 +9,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ("course", "0005_course_enable_circle_documents"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -24,7 +25,7 @@ class Migration(migrations.Migration): ), ( "mentor", - models.ForeignKey( + models.OneToOneField( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, @@ -34,7 +35,7 @@ class Migration(migrations.Migration): ( "students", models.ManyToManyField( - related_name="students", to=settings.AUTH_USER_MODEL + related_name="students", to="course.CourseSessionUser" ), ), ], diff --git a/server/vbv_lernwelt/learning_mentor/models.py b/server/vbv_lernwelt/learning_mentor/models.py index 5b6c51ed..933ff893 100644 --- a/server/vbv_lernwelt/learning_mentor/models.py +++ b/server/vbv_lernwelt/learning_mentor/models.py @@ -1,13 +1,20 @@ from django.db import models from vbv_lernwelt.core.models import User +from vbv_lernwelt.course.models import CourseSessionUser class LearningMentor(models.Model): 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): return self.mentor_email + + @property + def course_sessions(self): + return self.students.values_list("course_session", flat=True).distinct() diff --git a/server/vbv_lernwelt/learning_mentor/tests/test_assignments.py b/server/vbv_lernwelt/learning_mentor/tests/test_assignments.py new file mode 100644 index 00000000..8694ff3b --- /dev/null +++ b/server/vbv_lernwelt/learning_mentor/tests/test_assignments.py @@ -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]) diff --git a/server/vbv_lernwelt/learning_mentor/views.py b/server/vbv_lernwelt/learning_mentor/views.py index 9ca77589..c55eecc2 100644 --- a/server/vbv_lernwelt/learning_mentor/views.py +++ b/server/vbv_lernwelt/learning_mentor/views.py @@ -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.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.feedback.models import FeedbackResponse -from vbv_lernwelt.feedback.utils import feedback_users -from vbv_lernwelt.learnpath.models import Circle +from vbv_lernwelt.course_session.models import CourseSessionAssignment +from vbv_lernwelt.learning_mentor.models import LearningMentor -def get_practical_assignment(): - pass +class CompletionStatus(str, Enum): + PENDING = "PENDING" + SUBMITTED = "SUBMITTED" + EVALUATED = "EVALUATED" -def get_feedbacks(course_session: CourseSession): - circle_feedbacks = [] +@dataclass +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, - 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 = ( - course_session.course.get_learning_path() - .get_descendants() - .live() - .specific() - .exact_type(Circle) - ) +def get_praxis_assignments( + course_session: CourseSession, participants: List[User] +) -> List[PraxisAssignmentStatus]: + records = [] - 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"]) @@ -43,6 +132,13 @@ def mentor_summary(request, course_session_id: int): if request.method == "GET": 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 + ), + } + )