wip: self evaluation mentor api
This commit is contained in:
parent
3f9742550f
commit
a7922743fd
|
|
@ -20,6 +20,7 @@ import type {
|
||||||
CourseSession,
|
CourseSession,
|
||||||
CourseSessionDetail,
|
CourseSessionDetail,
|
||||||
LearningContentWithCompletion,
|
LearningContentWithCompletion,
|
||||||
|
LearningMentor,
|
||||||
LearningPathType,
|
LearningPathType,
|
||||||
LearningUnitPerformanceCriteria,
|
LearningUnitPerformanceCriteria,
|
||||||
PerformanceCriteria,
|
PerformanceCriteria,
|
||||||
|
|
@ -466,7 +467,7 @@ export function useFileUpload() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useLearningMentors() {
|
export function useLearningMentors() {
|
||||||
const learningMentors = ref([]);
|
const learningMentors = ref<LearningMentor[]>([]);
|
||||||
const currentCourseSessionId = useCurrentCourseSession().value.id;
|
const currentCourseSessionId = useCurrentCourseSession().value.id;
|
||||||
|
|
||||||
const fetchMentors = async () => {
|
const fetchMentors = async () => {
|
||||||
|
|
|
||||||
|
|
@ -456,6 +456,17 @@ export interface ExpertSessionUser extends CourseSessionUser {
|
||||||
role: "EXPERT";
|
role: "EXPERT";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Mentor {
|
||||||
|
id: number;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LearningMentor {
|
||||||
|
id: number;
|
||||||
|
mentor: Mentor;
|
||||||
|
}
|
||||||
|
|
||||||
export type CourseSessionDetail = CourseSessionObjectType;
|
export type CourseSessionDetail = CourseSessionObjectType;
|
||||||
|
|
||||||
// document upload
|
// document upload
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,7 @@ LOCAL_APPS = [
|
||||||
"vbv_lernwelt.course_session_group",
|
"vbv_lernwelt.course_session_group",
|
||||||
"vbv_lernwelt.shop",
|
"vbv_lernwelt.shop",
|
||||||
"vbv_lernwelt.learning_mentor",
|
"vbv_lernwelt.learning_mentor",
|
||||||
|
"vbv_lernwelt.self_evaluation_feedback",
|
||||||
]
|
]
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
||||||
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,9 @@ urlpatterns = [
|
||||||
|
|
||||||
path("api/mentor/<signed_int:course_session_id>/", include("vbv_lernwelt.learning_mentor.urls")),
|
path("api/mentor/<signed_int:course_session_id>/", include("vbv_lernwelt.learning_mentor.urls")),
|
||||||
|
|
||||||
|
# self evaluation feedback
|
||||||
|
path("api/self-evaluation-feedback/", include("vbv_lernwelt.self_evaluation_feedback.urls")),
|
||||||
|
|
||||||
# assignment
|
# assignment
|
||||||
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/",
|
||||||
|
|
|
||||||
|
|
@ -268,10 +268,21 @@ def create_course_session_edoniq_test(
|
||||||
return cset
|
return cset
|
||||||
|
|
||||||
|
|
||||||
|
def create_learning_unit(circle: Circle, course: Course):
|
||||||
|
cat, _ = CourseCategory.objects.get_or_create(
|
||||||
|
course=course, title="Course Category"
|
||||||
|
)
|
||||||
|
|
||||||
|
return LearningUnitFactory(
|
||||||
|
title="Learning Unit", parent=circle, course_category=cat
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_performance_criteria_page(
|
def create_performance_criteria_page(
|
||||||
course: Course,
|
course: Course,
|
||||||
course_page: CoursePage,
|
course_page: CoursePage,
|
||||||
circle: Circle,
|
circle: Circle,
|
||||||
|
learning_unit: LearningUnitFactory | None = None,
|
||||||
) -> PerformanceCriteria:
|
) -> PerformanceCriteria:
|
||||||
competence_navi_page = CompetenceNaviPageFactory(
|
competence_navi_page = CompetenceNaviPageFactory(
|
||||||
title="Competence Navi",
|
title="Competence Navi",
|
||||||
|
|
@ -290,17 +301,14 @@ def create_performance_criteria_page(
|
||||||
items=[("item", "Action Competence Item")],
|
items=[("item", "Action Competence Item")],
|
||||||
)
|
)
|
||||||
|
|
||||||
cat, _ = CourseCategory.objects.get_or_create(
|
if not learning_unit:
|
||||||
course=course, title="Course Category"
|
learning_unit = create_learning_unit(circle=circle, course=course)
|
||||||
)
|
|
||||||
|
|
||||||
lu = LearningUnitFactory(title="Learning Unit", parent=circle, course_category=cat)
|
|
||||||
|
|
||||||
return PerformanceCriteriaFactory(
|
return PerformanceCriteriaFactory(
|
||||||
parent=action_competence,
|
parent=action_competence,
|
||||||
competence_id="X1.1",
|
competence_id="X1.1",
|
||||||
title="Performance Criteria",
|
title="Performance Criteria",
|
||||||
learning_unit=lu,
|
learning_unit=learning_unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -329,25 +329,3 @@ class CircleDocument(models.Model):
|
||||||
self.file.upload_finished_at = None
|
self.file.upload_finished_at = None
|
||||||
self.file.save()
|
self.file.save()
|
||||||
return super().delete(*args, **kwargs)
|
return super().delete(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Model something like this:
|
|
||||||
# class LearningUnitCompletionFeedback(models.Model):
|
|
||||||
# assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
||||||
# feedback_user = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
||||||
# feedback_submitted = models.BooleanField(default=False)
|
|
||||||
# learning_unit = models.ForeignKey(
|
|
||||||
# "learnpath.LearningUnit", on_delete=models.CASCADE
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# class CourseCompletionFeedback(models.Model):
|
|
||||||
# learning_unit_completion_feedback = models.ForeignKey(
|
|
||||||
# LearningUnitCompletionFeedback, on_delete=models.CASCADE
|
|
||||||
# )
|
|
||||||
# course_completion = models.ForeignKey(CourseCompletion, on_delete=models.CASCADE)
|
|
||||||
# feedback_status = models.CharField(
|
|
||||||
# max_length=255,
|
|
||||||
# choices=[(status, status.value) for status in CourseCompletionStatus],
|
|
||||||
# default=CourseCompletionStatus.UNKNOWN.value,
|
|
||||||
# )
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class SelfEvaluationFeedbackConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "vbv_lernwelt.self_evaluation_feedback"
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
# Generated by Django 3.2.20 on 2024-01-21 18:42
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import vbv_lernwelt.course.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("course", "0006_auto_20231221_1411"),
|
||||||
|
("learnpath", "0014_alter_learningunit_feedback_user"),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="SelfEvaluationFeedback",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("feedback_submitted", models.BooleanField(default=False)),
|
||||||
|
(
|
||||||
|
"feedback_provider_user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="feedback_provider_user",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"feedback_requester_user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="feedback_requester_user",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"learning_unit",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="learnpath.learningunit",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="CourseCompletionFeedback",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"provider_evaluation_feedback",
|
||||||
|
models.CharField(
|
||||||
|
choices=[
|
||||||
|
(
|
||||||
|
vbv_lernwelt.course.models.CourseCompletionStatus[
|
||||||
|
"SUCCESS"
|
||||||
|
],
|
||||||
|
"SUCCESS",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
vbv_lernwelt.course.models.CourseCompletionStatus[
|
||||||
|
"FAIL"
|
||||||
|
],
|
||||||
|
"FAIL",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
vbv_lernwelt.course.models.CourseCompletionStatus[
|
||||||
|
"UNKNOWN"
|
||||||
|
],
|
||||||
|
"UNKNOWN",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
default="UNKNOWN",
|
||||||
|
max_length=255,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"feedback",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="self_evaluation_feedback.selfevaluationfeedback",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"requester_evaluation",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="course.coursecompletion",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.admin import User
|
||||||
|
from vbv_lernwelt.course.models import CourseCompletion, CourseCompletionStatus
|
||||||
|
|
||||||
|
|
||||||
|
class SelfEvaluationFeedback(models.Model):
|
||||||
|
feedback_submitted = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
feedback_requester_user = models.ForeignKey(
|
||||||
|
User, on_delete=models.CASCADE, related_name="feedback_requester_user"
|
||||||
|
)
|
||||||
|
|
||||||
|
feedback_provider_user = models.ForeignKey(
|
||||||
|
User, on_delete=models.CASCADE, related_name="feedback_provider_user"
|
||||||
|
)
|
||||||
|
|
||||||
|
learning_unit = models.ForeignKey(
|
||||||
|
"learnpath.LearningUnit", on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CourseCompletionFeedback(models.Model):
|
||||||
|
feedback = models.ForeignKey(SelfEvaluationFeedback, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
# the course completion has to be evaluated by the feedback provider
|
||||||
|
requester_evaluation = models.ForeignKey(CourseCompletion, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
provider_evaluation_feedback = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
choices=[(status, status.value) for status in CourseCompletionStatus],
|
||||||
|
default=CourseCompletionStatus.UNKNOWN.value,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from vbv_lernwelt.competence.models import PerformanceCriteria
|
||||||
|
from vbv_lernwelt.core.serializers import UserSerializer
|
||||||
|
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
||||||
|
|
||||||
|
|
||||||
|
class SelfEvaluationFeedbackSerializer(serializers.ModelSerializer):
|
||||||
|
criteria = serializers.SerializerMethodField()
|
||||||
|
feedback_requester_user = UserSerializer(read_only=True)
|
||||||
|
feedback_provider_user = UserSerializer(read_only=True)
|
||||||
|
learning_unit_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
read_only=True, source="learning_unit"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SelfEvaluationFeedback
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"learning_unit_id",
|
||||||
|
"feedback_submitted",
|
||||||
|
"feedback_requester_user",
|
||||||
|
"feedback_provider_user",
|
||||||
|
"criteria",
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_criteria(self, obj):
|
||||||
|
performance_criteria: List[
|
||||||
|
PerformanceCriteria
|
||||||
|
] = obj.learning_unit.performancecriteria_set.all()
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"id": criteria.id,
|
||||||
|
"title": criteria.title,
|
||||||
|
}
|
||||||
|
for criteria in performance_criteria
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
|
from vbv_lernwelt.course.creators.test_utils import (
|
||||||
|
add_course_session_user,
|
||||||
|
create_circle,
|
||||||
|
create_course,
|
||||||
|
create_course_session,
|
||||||
|
create_learning_unit,
|
||||||
|
create_performance_criteria_page,
|
||||||
|
create_user,
|
||||||
|
)
|
||||||
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
|
from vbv_lernwelt.course.services import mark_course_completion
|
||||||
|
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||||
|
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
||||||
|
|
||||||
|
|
||||||
|
def create_self_evaluation_feedback(
|
||||||
|
learning_unit, feedback_requester_user, feedback_provider_user
|
||||||
|
):
|
||||||
|
return SelfEvaluationFeedback.objects.create(
|
||||||
|
learning_unit=learning_unit,
|
||||||
|
feedback_requester_user=feedback_requester_user,
|
||||||
|
feedback_provider_user=feedback_provider_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SelfEvaluationFeedbackAPI(APITestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.member = create_user("member")
|
||||||
|
self.mentor = create_user("mentor")
|
||||||
|
|
||||||
|
self.course, self.course_page = create_course("Test Course")
|
||||||
|
self.course_session = create_course_session(
|
||||||
|
course=self.course, title="Test Bern 2022 a"
|
||||||
|
)
|
||||||
|
|
||||||
|
member_csu = add_course_session_user(
|
||||||
|
course_session=self.course_session,
|
||||||
|
user=self.member,
|
||||||
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.circle, _ = create_circle(
|
||||||
|
title="Test Circle", course_page=self.course_page
|
||||||
|
)
|
||||||
|
|
||||||
|
learning_mentor = LearningMentor.objects.create(
|
||||||
|
mentor=self.mentor,
|
||||||
|
course=self.course_session.course,
|
||||||
|
)
|
||||||
|
|
||||||
|
learning_mentor.participants.add(member_csu)
|
||||||
|
|
||||||
|
self.client.force_login(self.member)
|
||||||
|
|
||||||
|
def test_start_self_evaluation_feedback(self):
|
||||||
|
# GIVEN
|
||||||
|
learning_unit = create_learning_unit(course=self.course, circle=self.circle)
|
||||||
|
|
||||||
|
pc = create_performance_criteria_page(
|
||||||
|
course=self.course,
|
||||||
|
course_page=self.course_page,
|
||||||
|
circle=self.circle,
|
||||||
|
learning_unit=learning_unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
mark_course_completion(
|
||||||
|
page=pc,
|
||||||
|
user=self.member,
|
||||||
|
course_session=self.course_session,
|
||||||
|
completion_status="SUCCESS",
|
||||||
|
)
|
||||||
|
|
||||||
|
# WHEN
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("start_self_evaluation_feedback"),
|
||||||
|
{
|
||||||
|
"learning_unit_id": learning_unit.id,
|
||||||
|
"feedback_provider_user_id": self.mentor.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# THEN
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.data["success"], True)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
SelfEvaluationFeedback.objects.count(),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
# just get the first one
|
||||||
|
f = SelfEvaluationFeedback.objects.first()
|
||||||
|
|
||||||
|
self.assertEqual(f.feedback_requester_user, self.member)
|
||||||
|
self.assertEqual(f.feedback_provider_user, self.mentor)
|
||||||
|
self.assertEqual(f.learning_unit, learning_unit)
|
||||||
|
|
||||||
|
def test_start_self_evaluation_feedback_not_allowed_user(self):
|
||||||
|
# GIVEN
|
||||||
|
learning_unit = create_learning_unit(course=self.course, circle=self.circle)
|
||||||
|
not_a_mentor = create_user("not_a_mentor")
|
||||||
|
|
||||||
|
# WHEN
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("start_self_evaluation_feedback"),
|
||||||
|
{
|
||||||
|
"learning_unit_id": learning_unit.id,
|
||||||
|
"feedback_provider_user_id": not_a_mentor.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# THEN
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from .views import start_self_evaluation_feedback
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path(
|
||||||
|
"start-feedback",
|
||||||
|
start_self_evaluation_feedback,
|
||||||
|
name="start_self_evaluation_feedback",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from rest_framework.decorators import api_view, permission_classes
|
||||||
|
from rest_framework.exceptions import PermissionDenied
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||||
|
from vbv_lernwelt.learnpath.models import LearningUnit
|
||||||
|
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
||||||
|
|
||||||
|
|
||||||
|
@api_view(["POST"])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
def start_self_evaluation_feedback(request):
|
||||||
|
learning_unit_id = request.data.get("learning_unit_id")
|
||||||
|
feedback_provider_user_id = request.data.get("feedback_provider_user_id")
|
||||||
|
|
||||||
|
learning_unit = get_object_or_404(LearningUnit, id=learning_unit_id)
|
||||||
|
feedback_provider_user = get_object_or_404(User, id=feedback_provider_user_id)
|
||||||
|
|
||||||
|
if not LearningMentor.objects.filter(
|
||||||
|
course=learning_unit.get_course(),
|
||||||
|
mentor=feedback_provider_user,
|
||||||
|
participants__user=request.user,
|
||||||
|
).exists():
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
SelfEvaluationFeedback.objects.create(
|
||||||
|
feedback_requester_user=request.user,
|
||||||
|
feedback_provider_user=feedback_provider_user,
|
||||||
|
learning_unit=learning_unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: Create notification for feedback_provider_user
|
||||||
|
|
||||||
|
return Response({"success": True})
|
||||||
|
|
||||||
|
|
||||||
|
@api_view(["GET"])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
def list_self_evaluation_feedback(request):
|
||||||
|
feedbacks = SelfEvaluationFeedback.objects.filter(
|
||||||
|
feedback_provider_user=request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO continue here
|
||||||
|
|
||||||
|
return Response({"success": True})
|
||||||
Loading…
Reference in New Issue