Refactor `LearningMentor` model to flat `AgentParticipantRelation` model

This commit is contained in:
Daniel Egger 2024-07-21 15:44:58 +02:00
parent 3f02fd254a
commit cdfb9d2c5b
45 changed files with 761 additions and 483 deletions

View File

@ -8,9 +8,9 @@ import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
const courseSession = useCurrentCourseSession();
const { isLoading, summary, fetchData } = useLearningMentees(courseSession.value.id);
const removeMyMentee = async (menteeId: string) => {
const removeMyMentee = async (relationId: string) => {
await useCSRFFetch(
`/api/mentor/${courseSession.value.id}/mentors/${summary.value?.mentor_id}/remove/${menteeId}`
`/api/mentor/${courseSession.value.id}/mentors/${relationId}/delete`
).delete();
fetchData();
};
@ -28,25 +28,31 @@ const noMenteesText = computed(() =>
</div>
<div v-else>
<h2 class="heading-2 py-6">{{ $t("a.Personen, die du begleitest") }}</h2>
<div v-if="(summary?.participants?.length ?? 0) > 0" class="bg-white px-4 py-2">
<div
v-if="(summary?.participant_relations.length ?? 0) > 0"
class="bg-white px-4 py-2"
>
<div
v-for="participant in summary?.participants ?? []"
:key="participant.id"
v-for="relation in summary?.participant_relations ?? []"
:key="relation.id"
data-cy="lm-my-mentee-list-item"
class="flex flex-col items-start justify-between gap-4 border-b py-2 last:border-b-0 md:flex-row md:items-center md:gap-16"
>
<div class="flex items-center space-x-2">
<img
:alt="participant.last_name"
:alt="relation.participant_user.last_name"
class="h-11 w-11 rounded-full"
:src="participant.avatar_url || '/static/avatars/myvbv-default-avatar.png'"
:src="
relation.participant_user.avatar_url ||
'/static/avatars/myvbv-default-avatar.png'
"
/>
<div>
<div class="text-bold">
{{ participant.first_name }}
{{ participant.last_name }}
{{ relation.participant_user.first_name }}
{{ relation.participant_user.last_name }}
</div>
{{ participant.email }}
{{ relation.participant_user.email }}
</div>
</div>
<div class="space-x-5">
@ -55,7 +61,7 @@ const noMenteesText = computed(() =>
:to="{
name: 'profileLearningPath',
params: {
userId: participant.id,
userId: relation.participant_user.id,
courseSlug: courseSession.course.slug,
},
}"
@ -66,7 +72,7 @@ const noMenteesText = computed(() =>
<button
class="underline"
data-cy="lm-my-mentee-remove"
@click="removeMyMentee(participant.id)"
@click="removeMyMentee(relation.id)"
>
{{ $t("a.Entfernen") }}
</button>

View File

@ -51,11 +51,9 @@ const removeInvitation = async (invitationId: string) => {
await refreshInvitations();
};
const userStore = useUserStore();
const removeMyMentor = async (mentorId: string) => {
const removeMyMentor = async (relationId: string) => {
await useCSRFFetch(
`/api/mentor/${courseSession.value.id}/mentors/${mentorId}/remove/${userStore.id}`
`/api/mentor/${courseSession.value.id}/mentors/${relationId}/delete`
).delete();
await refreshMentors();
};
@ -138,16 +136,16 @@ const noLearningMentors = computed(() =>
>
<div class="flex items-center space-x-2">
<img
:alt="learningMentor.mentor.last_name"
:alt="learningMentor.agent.last_name"
class="h-11 w-11 rounded-full"
:src="learningMentor.mentor.avatar_url"
:src="learningMentor.agent.avatar_url"
/>
<div>
<div class="text-bold">
{{ learningMentor.mentor.first_name }}
{{ learningMentor.mentor.last_name }}
{{ learningMentor.agent.first_name }}
{{ learningMentor.agent.last_name }}
</div>
{{ learningMentor.mentor.email }}
{{ learningMentor.agent.email }}
</div>
</div>
<button

View File

@ -74,9 +74,9 @@ const onSubmit = async () => {
<option
v-for="learningMentor in learningMentors"
:key="learningMentor.id"
:value="learningMentor.mentor.id"
:value="learningMentor.agent.id"
>
{{ learningMentor.mentor.first_name }} {{ learningMentor.mentor.last_name }}
{{ learningMentor.agent.first_name }} {{ learningMentor.agent.last_name }}
</option>
</select>
</div>

View File

@ -39,7 +39,7 @@ import type {
CourseSessionDetail,
DashboardPersonsPageMode,
LearningContentWithCompletion,
LearningMentor,
AgentParticipantRelation,
LearningPathType,
LearningUnitPerformanceCriteria,
PerformanceCriteria,
@ -489,7 +489,7 @@ export function useFileUpload() {
}
export function useMyLearningMentors() {
const learningMentors = ref<LearningMentor[]>([]);
const learningMentors = ref<AgentParticipantRelation[]>([]);
const currentCourseSessionId = useCurrentCourseSession().value.id;
const loading = ref(false);

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import type { Assignment, Participant } from "@/services/learningMentees";
import type { Assignment, UserShort } from "@/services/learningMentees";
import { useLearningMentees } from "@/services/learningMentees";
import { computed, onMounted, type Ref } from "vue";
import { useCurrentCourseSession } from "@/composables";
@ -11,13 +11,16 @@ const props = defineProps<{
const courseSession = useCurrentCourseSession();
const learningMentees = useLearningMentees(courseSession.value.id);
const participants = computed(() => learningMentees.summary.value?.participants);
const praxisAssignment: Ref<Assignment | null> = computed(() =>
learningMentees.getAssignmentById(props.praxisAssignmentId)
);
const getParticipantById = (id: string): Participant | null => {
return participants.value?.find((participant) => participant.id === id) || null;
const getParticipantById = (id: string): UserShort | undefined => {
return (learningMentees.summary.value?.participant_relations ?? [])
.map((rel) => {
return rel.participant_user;
})
.find((user) => user.id === id);
};
onMounted(() => {

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import type { Assignment, Participant } from "@/services/learningMentees";
import type { Assignment, UserShort } from "@/services/learningMentees";
import { useLearningMentees } from "@/services/learningMentees";
import { computed, type Ref } from "vue";
import { useCurrentCourseSession } from "@/composables";
@ -15,14 +15,12 @@ const selfEvaluationFeedback: Ref<Assignment | null> = computed(() =>
learningMentees.getAssignmentById(props.learningUnitId)
);
const getParticipantById = (id: string): Participant | null => {
if (learningMentees.summary.value?.participants) {
const found = learningMentees.summary.value.participants.find(
(item) => item.id === id
);
return found || null;
}
return null;
const getParticipantById = (id: string): UserShort | undefined => {
return (learningMentees.summary.value?.participant_relations ?? [])
.map((rel) => {
return rel.participant_user;
})
.find((user) => user.id === id);
};
</script>

View File

@ -29,8 +29,8 @@ const isMentorsLoading = computed(() => learningMentors.loading.value);
const mentors = computed(() => {
return learningMentors.learningMentors.value.map((mentor) => ({
id: mentor.mentor.id,
name: `${mentor.mentor.first_name} ${mentor.mentor.last_name}`,
id: mentor.agent.id,
name: `${mentor.agent.first_name} ${mentor.agent.last_name}`,
}));
});

View File

@ -2,7 +2,7 @@ import { itGet } from "@/fetchHelpers";
import type { Ref } from "vue";
import { ref, watchEffect } from "vue";
export interface Participant {
export interface UserShort {
id: string;
first_name: string;
last_name: string;
@ -12,6 +12,14 @@ export interface Participant {
language: string;
}
export interface AgentParticipantRelation {
id: string;
role: "LEARNING_MENTOR";
course_session_id: number;
agent: UserShort;
participant_user: UserShort;
}
interface Circle {
id: number;
title: string;
@ -40,9 +48,8 @@ export interface Assignment {
type: string;
}
export interface Summary {
mentor_id: string;
participants: Participant[];
export interface LearningMentorSummary {
participant_relations: AgentParticipantRelation[];
circles: Circle[];
assignments: Assignment[];
}
@ -51,7 +58,7 @@ export const useLearningMentees = (
courseSessionId: string | Ref<string> | (() => string)
) => {
const isLoading = ref(false);
const summary: Ref<Summary | null> = ref(null);
const summary: Ref<LearningMentorSummary | null> = ref(null);
const error = ref(null);
const getAssignmentById = (id: string): Assignment | null => {

View File

@ -468,15 +468,16 @@ export interface ExpertSessionUser extends CourseSessionUser {
role: "EXPERT";
}
export interface Mentor {
export interface Agent {
id: number;
first_name: string;
last_name: string;
}
export interface LearningMentor {
export interface AgentParticipantRelation {
id: number;
mentor: Mentor;
role: "LEARNING_MENTOR";
agent: Agent;
}
export type CourseSessionDetail = CourseSessionObjectType;

View File

@ -39,9 +39,9 @@ def send_learning_mentor_invitation():
recipient_email="daniel.egger+sendgrid@gmail.com",
template=EmailTemplate.LEARNING_MENTOR_INVITATION,
template_data={
"inviter_name": f"Daniel Egger",
"inviter_name": "Daniel Egger",
"inviter_email": "daniel.egger@example.com",
"target_url": f"https://stage.vbv-afa.ch/foobar",
"target_url": "https://stage.vbv-afa.ch/foobar",
},
template_language="de",
fail_silently=True,

View File

@ -43,7 +43,10 @@ from vbv_lernwelt.course.services import mark_course_completion
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
from vbv_lernwelt.course_session.services.attendance import AttendanceUserStatus
from vbv_lernwelt.feedback.models import FeedbackResponse
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
MentorInvitation,
)
from vbv_lernwelt.learnpath.models import (
LearningContentAttendanceCourse,
LearningContentFeedbackUK,
@ -144,7 +147,8 @@ def command(
SelfEvaluationFeedback.objects.all().delete()
CourseCompletionFeedback.objects.all().delete()
LearningMentor.objects.all().delete()
AgentParticipantRelation.objects.all().delete()
# LearningMentor.objects.all().delete()
MentorInvitation.objects.all().delete()
User.objects.all().update(organisation=Organisation.objects.first())
User.objects.all().update(language="de")
@ -414,48 +418,40 @@ def command(
if create_learning_mentor:
cs_bern = CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID)
uk_mentor = LearningMentor.objects.create(
mentor=User.objects.get(id=TEST_MENTOR1_USER_ID),
course_session=cs_bern,
)
uk_mentor.participants.add(
CourseSessionUser.objects.get(
user__id=TEST_STUDENT1_USER_ID,
course_session=cs_bern,
)
AgentParticipantRelation.objects.create(
agent=User.objects.get(id=TEST_MENTOR1_USER_ID),
participant=CourseSessionUser.objects.get(
user__id=TEST_STUDENT1_USER_ID, course_session=cs_bern
),
role="LEARNING_MENTOR",
)
vv_course = Course.objects.get(id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
vv_course_session = CourseSession.objects.get(course=vv_course)
vv_mentor = LearningMentor.objects.create(
mentor=User.objects.get(id=TEST_MENTOR1_USER_ID),
course_session=vv_course_session,
)
vv_mentor.participants.add(
CourseSessionUser.objects.get(
user__id=TEST_STUDENT1_VV_USER_ID, course_session=vv_course_session
)
)
vv_mentor.participants.add(
CourseSessionUser.objects.get(
user__id=TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID,
course_session=vv_course_session,
)
)
vv_student_and_mentor = LearningMentor.objects.create(
mentor=User.objects.get(id=TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID),
course_session=vv_course_session,
)
vv_student_and_mentor.participants.add(
CourseSessionUser.objects.get(
AgentParticipantRelation.objects.create(
agent=User.objects.get(id=TEST_MENTOR1_USER_ID),
participant=CourseSessionUser.objects.get(
user__id=TEST_STUDENT1_VV_USER_ID,
course_session=vv_course_session,
)
),
role="LEARNING_MENTOR",
)
AgentParticipantRelation.objects.create(
agent=User.objects.get(id=TEST_MENTOR1_USER_ID),
participant=CourseSessionUser.objects.get(
user__id=TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID,
course_session=vv_course_session,
),
role="LEARNING_MENTOR",
)
AgentParticipantRelation.objects.create(
agent=User.objects.get(id=TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID),
participant=CourseSessionUser.objects.get(
user__id=TEST_STUDENT1_VV_USER_ID,
course_session=vv_course_session,
),
role="LEARNING_MENTOR",
)
course = Course.objects.get(id=COURSE_TEST_ID)

View File

@ -24,7 +24,10 @@ from vbv_lernwelt.course_session.models import (
)
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.feedback.models import FeedbackResponse
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
)
from vbv_lernwelt.learnpath.models import Circle
from vbv_lernwelt.notify.models import Notification
@ -71,7 +74,7 @@ def create_or_update_uk(language="de"):
cs = CourseSession.objects.get(import_id=data["ID"])
members, trainer, regionenleiter = get_or_create_users_uk()
delete_cs_data(cs, members + [trainer, regionenleiter])
delete_cs_data(cs)
add_to_course_session(cs, members)
add_trainers_to_course_session(cs, [trainer], uk_circle_keys, language)
@ -89,13 +92,13 @@ def create_or_update_vv(language="de"):
create_or_update_assignment_course_session(cs)
members, member_with_mentor, mentor = get_or_create_users_vv()
delete_cs_data(cs, members + [member_with_mentor, mentor])
delete_cs_data(cs)
add_to_course_session(cs, members + [member_with_mentor])
add_mentor_to_course_session(cs, [(mentor, member_with_mentor)])
def delete_cs_data(cs: CourseSession, users: list[User]):
def delete_cs_data(cs: CourseSession):
if cs:
CourseCompletion.objects.filter(course_session=cs).delete()
Notification.objects.filter(course_session=cs).delete()
@ -105,16 +108,8 @@ def delete_cs_data(cs: CourseSession, users: list[User]):
)
CourseSessionEdoniqTest.objects.filter(course_session=cs).delete()
CourseSessionUser.objects.filter(course_session=cs).delete()
learning_mentor_ids = (
LearningMentor.objects.filter(participants__course_session=cs)
.values_list("id", flat=True)
.distinct()
| LearningMentor.objects.filter(mentor__in=users)
.values_list("id", flat=True)
.distinct()
)
# cannot call delete on distinct objects
LearningMentor.objects.filter(id__in=list(learning_mentor_ids)).delete()
AgentParticipantRelation.objects.filter(course_session=cs).delete()
else:
logger.info("no_course_session_found", import_id=cs.import_id)
@ -138,15 +133,12 @@ def add_mentor_to_course_session(
course_session: CourseSession, mentor_mentee_pairs: list[tuple[User, User]]
):
for mentor, mentee in mentor_mentee_pairs:
lm = LearningMentor.objects.create(
course_session=course_session,
mentor=mentor,
)
lm.participants.add(
CourseSessionUser.objects.get(
user__id=mentee.id,
course_session=course_session,
)
AgentParticipantRelation.objects.create(
agent=mentor,
participant=CourseSessionUser.objects.get(
user__id=mentee.id, course_session=course_session
),
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)

View File

@ -131,6 +131,20 @@ class UserSerializer(serializers.ModelSerializer):
return instance
class UserShortSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
"id",
"first_name",
"last_name",
"email",
"username",
"avatar_url",
"language",
]
class CypressUserSerializer(serializers.ModelSerializer):
class Meta:
model = User

View File

@ -41,7 +41,10 @@ from vbv_lernwelt.course_session.models import (
)
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.duedate.models import DueDate
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
)
from vbv_lernwelt.learnpath.models import (
Circle,
LearningContentAssignment,
@ -101,13 +104,13 @@ def create_course_session(
def add_learning_mentor(
course_session: CourseSession, mentor: User, mentee: CourseSessionUser
) -> LearningMentor:
learning_mentor = LearningMentor.objects.create(
course_session=course_session, mentor=mentor
mentor: User, mentee: CourseSessionUser
) -> AgentParticipantRelation:
return AgentParticipantRelation.objects.create(
agent=mentor,
participant=mentee,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
learning_mentor.participants.add(mentee)
return learning_mentor
def add_course_session_user(

View File

@ -45,10 +45,7 @@ from vbv_lernwelt.competence.create_vv_new_competence_profile import (
create_vv_new_competence_profile,
)
from vbv_lernwelt.competence.models import PerformanceCriteria
from vbv_lernwelt.core.constants import (
TEST_MENTOR1_USER_ID,
TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID,
)
from vbv_lernwelt.core.constants import TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID
from vbv_lernwelt.core.create_default_users import default_users
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.consts import (
@ -95,7 +92,6 @@ from vbv_lernwelt.importer.services import (
import_students_from_excel,
import_trainers_from_excel_for_training,
)
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learnpath.create_vv_new_learning_path import (
create_vv_motorfahrzeug_pruefung_learning_path,
create_vv_new_learning_path,
@ -243,6 +239,11 @@ def create_versicherungsvermittlerin_course(
course_session=cs,
user=User.objects.get(username="student-vv@eiger-versicherungen.ch"),
)
mentor_and_student_2_learning_csu = CourseSessionUser.objects.create(
course_session=cs,
user=User.objects.get(id=TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID),
role=CourseSessionUser.Role.MEMBER,
)
CourseSessionUser.objects.create(
course_session=cs,
@ -262,30 +263,6 @@ def create_versicherungsvermittlerin_course(
role=CourseSessionUser.Role.EXPERT,
)
mentor_and_student_2_learning_csu = CourseSessionUser.objects.create(
course_session=cs,
user=User.objects.get(id=TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID),
role=CourseSessionUser.Role.MEMBER,
)
# TEST_MENTOR1_USER_ID is only mentor
just_mentor = LearningMentor.objects.create(
mentor=User.objects.get(id=TEST_MENTOR1_USER_ID),
course_session=cs,
)
just_mentor.participants.add(student_1_csu)
just_mentor.participants.add(mentor_and_student_2_learning_csu)
# TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID is both student and mentor
mentor_and_student_learning_mentor = LearningMentor.objects.create(
mentor=User.objects.get(id=TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID),
course_session=cs,
)
mentor_and_student_learning_mentor.participants.add(student_1_csu)
for admin_email in ADMIN_EMAILS:
CourseSessionUser.objects.create(
course_session=cs,

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.25 on 2024-07-17 14:53
from django.db import migrations, models
import vbv_lernwelt.course.models
class Migration(migrations.Migration):
dependencies = [
("course", "0008_auto_20240403_1132"),
]
operations = [
migrations.AlterField(
model_name="coursecompletion",
name="completion_status",
field=models.CharField(
choices=[
("SUCCESS", "Success"),
("FAIL", "Fail"),
("UNKNOWN", "Unknown"),
],
default=vbv_lernwelt.course.models.CourseCompletionStatus["UNKNOWN"],
max_length=255,
),
),
]

View File

@ -27,7 +27,7 @@ from vbv_lernwelt.iam.permissions import (
has_course_access_by_page_request,
is_circle_expert,
)
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
logger = structlog.get_logger(__name__)
@ -155,9 +155,10 @@ def get_course_sessions(request):
# enrich with mentor course sessions
mentor_course_sessions = CourseSession.objects.filter(
id__in=LearningMentor.objects.filter(mentor=request.user).values_list(
"course_session", flat=True
)
id__in=[
rel.participant.course_session_id
for rel in AgentParticipantRelation.objects.filter(agent=request.user)
]
).prefetch_related("course")
all_to_serialize = (

View File

@ -25,7 +25,10 @@ from vbv_lernwelt.iam.permissions import (
can_view_course_session_group_statistics,
can_view_course_session_progress,
)
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
)
from vbv_lernwelt.learnpath.models import Circle
@ -95,12 +98,15 @@ class DashboardQuery(graphene.ObjectType):
mentees_ids = set()
course_session_ids = set()
mentees = CourseSessionUser.objects.filter(
participants__mentor=user, course_session__course=course
).values_list("user", "course_session")
for user_id, course_session_id in mentees:
mentees_ids.add(user_id)
course_session_ids.add(course_session_id)
relations_qs = AgentParticipantRelation.objects.filter(
agent=user,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
participant__course_session__course=course,
)
for relation in relations_qs:
mentees_ids.add(relation.participant.user_id)
course_session_ids.add(relation.participant.course_session_id)
return CourseStatisticsType(
_id=f"mentor:{course.id}", # noqa
@ -249,29 +255,31 @@ def get_user_statistics_dashboards(user: User) -> Tuple[List[Dict[str, str]], Se
def get_learning_mentor_dashboards(
user: User, exclude_course_ids: Set[int]
) -> Tuple[List[Dict[str, str]], Set[int]]:
learning_mentor = LearningMentor.objects.filter(mentor=user).exclude(
course_session__course__id__in=exclude_course_ids
)
learning_mentor_relation_qs = AgentParticipantRelation.objects.filter(
agent=user, role=AgentParticipantRoleType.LEARNING_MENTOR.value
).exclude(participant__course_session__course__id__in=exclude_course_ids)
dashboards = []
course_ids = set()
for mentor in learning_mentor:
course = mentor.course_session.course
course_ids.add(course.id)
for rel in learning_mentor_relation_qs:
course = rel.participant.course_session.course
if course.id in UK_COURSE_IDS:
dashboard_type = DashboardType.PRAXISBILDNER_DASHBOARD
else:
dashboard_type = DashboardType.MENTOR_DASHBOARD
dashboards.append(
{
"id": str(course.id),
"name": course.title,
"slug": course.slug,
"dashboard_type": dashboard_type,
"course_configuration": course.configuration,
}
)
if course.id not in course_ids:
course_ids.add(course.id)
dashboards.append(
{
"id": str(course.id),
"name": course.title,
"slug": course.slug,
"dashboard_type": dashboard_type,
"course_configuration": course.configuration,
}
)
return dashboards, course_ids

View File

@ -110,22 +110,22 @@ class DashboardTestCase(GraphQLTestCase):
self.client.force_login(member)
query = f"""query($course_id: ID!) {{
course_progress(course_id: $course_id) {{
query = """query($course_id: ID!) {
course_progress(course_id: $course_id) {
course_id
session_to_continue_id
competence {{
competence {
total_count
success_count
fail_count
}}
assignment {{
}
assignment {
total_count
points_max_count
points_achieved_count
}}
}}
}}
}
}
}
"""
variables = {"course_id": str(course.id)}
@ -268,7 +268,7 @@ class DashboardTestCase(GraphQLTestCase):
role=CourseSessionUser.Role.MEMBER,
)
add_learning_mentor(course_session=cs_1, mentor=mentor, mentee=csu)
add_learning_mentor(mentor=mentor, mentee=csu)
self.client.force_login(mentor)
@ -287,6 +287,7 @@ class DashboardTestCase(GraphQLTestCase):
# THEN
self.assertResponseNoErrors(response)
print(response.json())
self.assertEqual(len(response.json()["data"]["dashboard_config"]), 1)
self.assertEqual(
@ -314,7 +315,7 @@ class DashboardTestCase(GraphQLTestCase):
role=CourseSessionUser.Role.MEMBER,
)
add_learning_mentor(course_session=cs, mentor=mentor_and_member, mentee=mentee)
add_learning_mentor(mentor=mentor_and_member, mentee=mentee)
# WHEN
self.client.force_login(mentor_and_member)
@ -350,11 +351,11 @@ class DashboardTestCase(GraphQLTestCase):
self.client.force_login(disallowed_user)
query = f"""query($course_id: ID!) {{
course_statistics(course_id: $course_id) {{
query = """query($course_id: ID!) {
course_statistics(course_id: $course_id) {
course_id
}}
}}
}
}
"""
variables = {"course_id": str(course.id)}
@ -384,13 +385,13 @@ class DashboardTestCase(GraphQLTestCase):
self.client.force_login(supervisor)
query = f"""query($course_id: ID!) {{
course_statistics(course_id: $course_id) {{
query = """query($course_id: ID!) {
course_statistics(course_id: $course_id) {
course_id
course_title
course_slug
}}
}}
}
}
"""
variables = {"course_id": str(course_2.id)}

View File

@ -7,7 +7,7 @@ from vbv_lernwelt.assignment.models import (
from vbv_lernwelt.course.creators.test_utils import add_course_session_user, create_user
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.dashboard.tests.test_views import BaseMentorAssignmentTestCase
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
class MentorStatisticsTestCase(BaseMentorAssignmentTestCase, GraphQLTestCase):
@ -19,15 +19,13 @@ class MentorStatisticsTestCase(BaseMentorAssignmentTestCase, GraphQLTestCase):
self.course.configuration.save()
self.mentor = create_user("mentor")
self.lm = LearningMentor.objects.create(
mentor=self.mentor, course_session=self.course_session
)
self.participants = [create_user(f"participant{i}") for i in range(4)]
def test_assignment_statistics(self):
# WHEN
has_lb = [True, True, True, False]
has_passed = [True, False, True, False]
for i in range(4):
csu = add_course_session_user(
self.course_session,
@ -35,7 +33,9 @@ class MentorStatisticsTestCase(BaseMentorAssignmentTestCase, GraphQLTestCase):
role=CourseSessionUser.Role.MEMBER,
)
if has_lb[i]:
self.lm.participants.add(csu)
AgentParticipantRelation.objects.create(
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
)
AssignmentCompletion.objects.create(
course_session=self.course_session,
@ -47,22 +47,22 @@ class MentorStatisticsTestCase(BaseMentorAssignmentTestCase, GraphQLTestCase):
)
# THEN
# WHEN
query = f"""query ($courseId: ID!) {{
mentor_course_statistics(course_id: $courseId) {{
query = """query ($courseId: ID!) {
mentor_course_statistics(course_id: $courseId) {
course_session_selection_ids
user_selection_ids
assignments {{
assignments {
_id
summary {{
summary {
_id
completed_count
average_passed
total_passed
total_failed
}}
}}
}}
}}"""
}
}
}
}"""
# THEN
variables = {"courseId": str(self.course.id)}

View File

@ -37,7 +37,7 @@ from vbv_lernwelt.dashboard.views import (
get_course_config,
get_course_sessions_with_roles_for_user,
)
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
from vbv_lernwelt.learnpath.models import Circle, LearningUnit
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
@ -98,14 +98,16 @@ class GetCourseSessionsForUserTestCase(TestCase):
def test_learning_mentor_get_sessions(self):
mentor = create_user("mentor")
LearningMentor.objects.create(mentor=mentor, course_session=self.course_session)
participant = create_user("participant")
add_course_session_user(
csu = add_course_session_user(
self.course_session,
participant,
role=CourseSessionUser.Role.MEMBER,
)
AgentParticipantRelation.objects.create(
agent=mentor, participant=csu, role="LEARNING_MENTOR"
)
sessions = get_course_sessions_with_roles_for_user(mentor)
@ -187,7 +189,16 @@ class GetDashboardConfig(TestCase):
def test_mentor_uk_get_config(self):
# GIVEN
mentor = create_user("mentor")
LearningMentor.objects.create(mentor=mentor, course_session=self.course_session)
participant = create_user("participant")
csu = add_course_session_user(
self.course_session,
participant,
role=CourseSessionUser.Role.MEMBER,
)
AgentParticipantRelation.objects.create(
agent=mentor, participant=csu, role="LEARNING_MENTOR"
)
self.course.configuration.is_uk = True
self.course.configuration.save()
@ -205,7 +216,15 @@ class GetDashboardConfig(TestCase):
def test_mentor_vv_get_config(self):
# GIVEN
mentor = create_user("mentor")
LearningMentor.objects.create(mentor=mentor, course_session=self.course_session)
participant = create_user("participant")
csu = add_course_session_user(
self.course_session,
participant,
role=CourseSessionUser.Role.MEMBER,
)
AgentParticipantRelation.objects.create(
agent=mentor, participant=csu, role="LEARNING_MENTOR"
)
self.course.configuration.is_vv = True
self.course.configuration.save()
@ -228,7 +247,16 @@ class GetDashboardConfig(TestCase):
mentor,
role=CourseSessionUser.Role.MEMBER,
)
LearningMentor.objects.create(mentor=mentor, course_session=self.course_session)
participant = create_user("participant")
csu = add_course_session_user(
self.course_session,
participant,
role=CourseSessionUser.Role.MEMBER,
)
AgentParticipantRelation.objects.create(
agent=mentor, participant=csu, role="LEARNING_MENTOR"
)
self.course.configuration.is_vv = True
self.course.configuration.save()
@ -264,9 +292,6 @@ class GetMenteeCountTestCase(TestCase):
participants_with_mentor = [create_user(f"participant{i}") for i in range(2)]
participant = create_user("participant")
mentor = create_user("mentor")
lm = LearningMentor.objects.create(
mentor=mentor, course_session=self.course_session
)
# WHEN
for p in participants_with_mentor:
@ -275,7 +300,9 @@ class GetMenteeCountTestCase(TestCase):
p,
role=CourseSessionUser.Role.MEMBER,
)
lm.participants.add(csu)
AgentParticipantRelation.objects.create(
agent=mentor, participant=csu, role="LEARNING_MENTOR"
)
add_course_session_user(
self.course_session,
@ -305,9 +332,6 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
self.course.configuration.save()
self.mentor = create_user("mentor")
self.lm = LearningMentor.objects.create(
mentor=self.mentor, course_session=self.course_session
)
self.participants = [create_user(f"participant{i}") for i in range(2)]
def create_and_test_count(
@ -337,7 +361,10 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
self.participants[0],
role=CourseSessionUser.Role.MEMBER,
)
self.lm.participants.add(csu)
AgentParticipantRelation.objects.create(
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
)
add_course_session_user(
self.course_session,
@ -367,7 +394,9 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
self.participants[0],
role=CourseSessionUser.Role.MEMBER,
)
self.lm.participants.add(csu)
AgentParticipantRelation.objects.create(
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
)
add_course_session_user(
self.course_session,
@ -389,8 +418,9 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
self.participants[0],
role=CourseSessionUser.Role.MEMBER,
)
self.lm.participants.add(csu)
AgentParticipantRelation.objects.create(
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
)
SelfEvaluationFeedback.objects.create(
feedback_submitted=False,
feedback_requester_user=self.participants[0],
@ -411,8 +441,9 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
self.participants[0],
role=CourseSessionUser.Role.MEMBER,
)
self.lm.participants.add(csu)
AgentParticipantRelation.objects.create(
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
)
SelfEvaluationFeedback.objects.create(
feedback_submitted=True,
feedback_requester_user=self.participants[0],

View File

@ -42,7 +42,10 @@ from vbv_lernwelt.feedback.export import (
export_feedback_with_circle_restriction,
FEEDBACK_EXPORT_FILE_NAME,
)
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
)
from vbv_lernwelt.learnpath.models import Circle
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
@ -118,15 +121,15 @@ def get_course_sessions_with_roles_for_user(user: User) -> List[CourseSessionWit
result_course_sessions[cs.id] = cs
# enrich with mentor course sessions
lm_qs = LearningMentor.objects.filter(mentor=user).prefetch_related(
"course_session", "course_session__course"
lm_qs = AgentParticipantRelation.objects.filter(agent=user).prefetch_related(
"participant__course_session", "participant__course_session__course"
)
for lm in lm_qs:
cs = lm.course_session
cs = lm.participant.course_session
cs.roles = set()
cs = result_course_sessions.get(cs.id, cs)
cs.roles.add("LEARNING_MENTOR")
cs.roles.add(lm.role)
result_course_sessions[cs.id] = cs
return [
@ -197,46 +200,46 @@ def _create_person_list_with_roles(user):
# add persons where request.user is mentor
for cs in course_sessions:
if "LEARNING_MENTOR" in cs.roles:
lm = LearningMentor.objects.filter(
mentor=user, course_session=cs.id
).first()
for participant in lm.participants.all():
for relation in AgentParticipantRelation.objects.filter(
agent=user, participant__course_session_id=cs.id
):
course_session_entry = _create_course_session_dict(
cs,
"LEARNING_MENTOR",
"LEARNING_MENTEE",
)
participant_user = relation.participant.user
if participant.user.id not in result_persons:
person_data = create_user_dict(participant.user)
if participant_user.id not in result_persons:
person_data = create_user_dict(participant_user)
person_data["course_sessions"] = [course_session_entry]
result_persons[participant.user.id] = person_data
result_persons[participant_user] = person_data
else:
# user is already in result_persons
result_persons[participant.user.id]["course_sessions"].append(
result_persons[participant_user]["course_sessions"].append(
course_session_entry
)
# add persons where request.user is mentee
mentor_relation_qs = LearningMentor.objects.filter(
participants__user=user
).prefetch_related("mentor", "course_session")
mentor_relation_qs = AgentParticipantRelation.objects.filter(
participant__user=user,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
).prefetch_related("agent")
for mentor_relation in mentor_relation_qs:
cs = mentor_relation.course_session
cs = mentor_relation.participant.course_session
course_session_entry = _create_course_session_dict(
cs,
"LEARNING_MENTEE",
"LEARNING_MENTOR",
)
if mentor_relation.mentor.id not in result_persons:
person_data = create_user_dict(mentor_relation.mentor)
if mentor_relation.agent.id not in result_persons:
person_data = create_user_dict(mentor_relation.agent)
person_data["course_sessions"] = [course_session_entry]
result_persons[mentor_relation.mentor.id] = person_data
result_persons[mentor_relation.agent.id] = person_data
else:
# user is already in result_persons
result_persons[mentor_relation.mentor.id]["course_sessions"].append(
result_persons[mentor_relation.agent.id]["course_sessions"].append(
course_session_entry
)
@ -495,8 +498,10 @@ def get_mentee_count(request, course_id: str):
def _get_mentee_count(course_id: str, mentor: User) -> int:
return CourseSessionUser.objects.filter(
participants__mentor=mentor, course_session__course__id=course_id
return AgentParticipantRelation.objects.filter(
agent=mentor,
participant__course_session__course_id=course_id,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
).count()
@ -524,17 +529,26 @@ def _get_mentor_open_tasks_count(course_id: str, mentor: User) -> int:
course_configuration = CourseConfiguration.objects.get(course_id=course_id)
learning_meentee_ids = [
str(relation.participant.user_id)
for relation in AgentParticipantRelation.objects.filter(
agent=mentor,
participant__course_session__course_id=course_id,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
).prefetch_related("participant")
]
if course_configuration.is_vv:
open_assigment_count = AssignmentCompletion.objects.filter(
course_session__course__id=course_id,
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
evaluation_user=mentor, # noqa
assignment_user__coursesessionuser__participants__mentor=mentor,
assignment_user_id__in=learning_meentee_ids,
).count()
open_feedback_qs = SelfEvaluationFeedback.objects.filter(
feedback_provider_user=mentor, # noqa
feedback_requester_user__coursesessionuser__participants__mentor=mentor,
feedback_requester_user_id__in=learning_meentee_ids,
feedback_submitted=False,
)
# filter open feedbacks for course_id (-> not possible with queryset)

View File

@ -1,7 +1,10 @@
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
)
from vbv_lernwelt.learnpath.models import LearningSequence
@ -22,8 +25,8 @@ def has_course_access(user, course_id):
).exists():
return True
if LearningMentor.objects.filter(
course_session__course_id=course_id, mentor=user
if AgentParticipantRelation.objects.filter(
agent=user, participant__course_session__course_id=course_id
).exists():
return True
@ -72,8 +75,9 @@ def is_course_session_learning_mentor(mentor: User, course_session_id: int):
if course_session is None:
return False
return LearningMentor.objects.filter(
mentor=mentor, course_session=course_session
return AgentParticipantRelation.objects.filter(
agent=mentor,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
).exists()
@ -87,9 +91,11 @@ def is_learning_mentor_for_user(
if csu is None:
return False
return LearningMentor.objects.filter(
course_session_id=course_session_id, mentor=mentor, participants=csu
).exists()
return AgentParticipantRelation.objects.filter(
agent=mentor,
participant=csu,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
def is_course_session_supervisor(user, course_session_id: int):
@ -222,8 +228,8 @@ def has_role_in_course(user: User, course: Course) -> bool:
).exists():
return True
if LearningMentor.objects.filter(
course_session__course=course, mentor=user
if AgentParticipantRelation.objects.filter(
agent=user, participant__course_session__course=course
).exists():
return True

View File

@ -8,7 +8,7 @@ from vbv_lernwelt.course.creators.test_utils import (
)
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.iam.permissions import course_session_permissions
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
class ActionTestCase(TestCase):
@ -21,14 +21,16 @@ class ActionTestCase(TestCase):
def test_course_session_permissions(self):
# GIVEN
lm = create_user("mentor")
LearningMentor.objects.create(mentor=lm, course_session=self.course_session)
participant = create_user("participant")
add_course_session_user(
csu = add_course_session_user(
self.course_session,
participant,
role=CourseSessionUser.Role.MEMBER,
)
AgentParticipantRelation.objects.create(
agent=lm, participant=csu, role="LEARNING_MENTOR"
)
trainer = create_user("trainer")
add_course_session_user(

View File

@ -9,7 +9,7 @@ from vbv_lernwelt.course.creators.test_utils import (
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.iam.permissions import has_role_in_course, is_learning_mentor_for_user
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
class RoleTestCase(TestCase):
@ -48,9 +48,14 @@ class RoleTestCase(TestCase):
def test_has_role_mentor(self):
# GIVEN
LearningMentor.objects.create(
mentor=self.user,
course_session=self.course_session,
participant = create_user("participant")
csu = add_course_session_user(
self.course_session,
participant,
role=CourseSessionUser.Role.MEMBER,
)
AgentParticipantRelation.objects.create(
agent=self.user, participant=csu, role="LEARNING_MENTOR"
)
# WHEN
@ -73,14 +78,10 @@ class RoleTestCase(TestCase):
role=CourseSessionUser.Role.MEMBER,
)
learning_mentor = LearningMentor.objects.create(
mentor=mentor,
course_session=course_session,
AgentParticipantRelation.objects.create(
agent=mentor, participant=member_csu, role="LEARNING_MENTOR"
)
learning_mentor.participants.add(member_csu)
learning_mentor.save()
# WHEN
is_mentor = is_learning_mentor_for_user(
mentor=mentor,

View File

@ -101,7 +101,7 @@ class CreateOrUpdateCourseSessionTestCase(TestCase):
attendance_course.due_date.end.isoformat(), "2023-06-06T13:00:00+00:00"
)
self.assertEqual(
f"E64, HKV Aarau, Bahnhofstrasse 460, 5001, Aarau",
"E64, HKV Aarau, Bahnhofstrasse 460, 5001, Aarau",
attendance_course.location,
)
self.assertEqual("", attendance_course.trainer)
@ -183,7 +183,7 @@ class CreateOrUpdateCourseSessionTestCase(TestCase):
attendance_course.due_date.end.isoformat(), "2023-06-06T15:00:00+00:00"
)
self.assertEqual(
f"E666, HKV Aarau2, Bahnhofstrasse 460, 5002, Aarau",
"E666, HKV Aarau2, Bahnhofstrasse 460, 5002, Aarau",
attendance_course.location,
)
self.assertEqual(

View File

@ -1,34 +1,29 @@
from django.contrib import admin
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
MentorInvitation,
)
@admin.register(LearningMentor)
class LearningMentorAdmin(admin.ModelAdmin):
def participant_count(self, obj):
return obj.participants.count()
@admin.register(AgentParticipantRelation)
class TrainerParticipantRelationAdmin(admin.ModelAdmin):
list_display = ["agent", "participant", "role"]
participant_count.short_description = "Participants"
list_display = ["mentor", "course_session", "participant_count"]
search_fields = ["mentor__email"]
raw_id_fields = [
"mentor",
"course_session",
search_fields = [
"agent__email",
"agent__first_name",
"agent__last_name",
"participant__user__email",
"participant__user__first_name",
"participant__user__last_name",
]
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == "participants":
if request.resolver_match.kwargs.get("object_id"):
object_id = str(request.resolver_match.kwargs.get("object_id"))
lm = LearningMentor.objects.get(id=object_id)
kwargs["queryset"] = CourseSessionUser.objects.filter(
course_session=lm.course_session
)
return super().formfield_for_manytomany(db_field, request, **kwargs)
raw_id_fields = [
"agent",
"participant",
# "course_session",
]
@admin.register(MentorInvitation)

View File

@ -6,4 +6,8 @@ class LearningMentorConfig(AppConfig):
name = "vbv_lernwelt.learning_mentor"
def ready(self):
import vbv_lernwelt.learning_mentor.signals # noqa F401
try:
# pylint: disable=unused-import,import-outside-toplevel
import vbv_lernwelt.learning_mentor.signals # noqa F401
except ImportError:
pass

View File

@ -0,0 +1,57 @@
# Generated by Django 3.2.20 on 2024-07-18 13:33
import uuid
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("course", "0009_alter_coursecompletion_completion_status"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("learning_mentor", "0007_mentorinvitation_target_url"),
]
operations = [
migrations.CreateModel(
name="AgentParticipantRelation",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"role",
models.CharField(
choices=[("LEARNING_MENTOR", "LEARNING_MENTOR")],
default="LEARNING_MENTOR",
max_length=255,
),
),
(
"agent",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
(
"participant",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="course.coursesessionuser",
),
),
],
options={
"unique_together": {("agent", "participant")},
},
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.20 on 2024-07-18 13:33
from django.db import migrations
from django.db.migrations import RunPython
def refactor_mentor_to_agent_participant(apps=None, schema_editor=None):
LearningMentor = apps.get_model("learning_mentor", "LearningMentor")
AgentParticipantRelation = apps.get_model(
"learning_mentor", "AgentParticipantRelation"
)
for mentor in LearningMentor.objects.all():
for participant in mentor.participants.all():
AgentParticipantRelation.objects.create(
agent=mentor.mentor,
participant=participant,
)
class Migration(migrations.Migration):
dependencies = [
("learning_mentor", "0008_agentparticipantrelation"),
]
operations = [RunPython(refactor_mentor_to_agent_participant)]

View File

@ -1,4 +1,5 @@
import uuid
from enum import Enum
from django.db import models
from django_extensions.db.models import TimeStampedModel
@ -30,6 +31,26 @@ class LearningMentor(models.Model):
return self.participants.values_list("course_session", flat=True).distinct()
class AgentParticipantRoleType(Enum):
LEARNING_MENTOR = "LEARNING_MENTOR" # Lernbegleiter
class AgentParticipantRelation(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
agent = models.ForeignKey(User, on_delete=models.CASCADE)
participant = models.ForeignKey(CourseSessionUser, on_delete=models.CASCADE)
role = models.CharField(
max_length=255,
choices=[(t.value, t.value) for t in AgentParticipantRoleType],
default=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
class Meta:
unique_together = [("agent", "participant")]
class MentorInvitation(TimeStampedModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField()

View File

@ -1,7 +1,10 @@
from rest_framework import serializers
from vbv_lernwelt.core.serializers import UserSerializer
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
from vbv_lernwelt.core.serializers import UserShortSerializer
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
MentorInvitation,
)
class MentorAssignmentCompletionSerializer(serializers.Serializer):
@ -39,10 +42,24 @@ class InvitationSerializer(serializers.ModelSerializer):
return invitation
class MentorSerializer(serializers.ModelSerializer):
mentor = UserSerializer(read_only=True)
class AgentParticipantRelationSerializer(serializers.ModelSerializer):
agent = UserShortSerializer(read_only=True)
participant_user = serializers.SerializerMethodField()
course_session_id = serializers.SerializerMethodField()
def get_participant_user(self, obj):
return UserShortSerializer(obj.participant.user).data
def get_course_session_id(self, obj):
return obj.participant.course_session_id
class Meta:
model = LearningMentor
fields = ["id", "mentor"]
model = AgentParticipantRelation
fields = [
"id",
"role",
"course_session_id",
"agent",
"participant_user",
]
read_only_fields = ["id"]

View File

@ -1,18 +0,0 @@
from django.core.exceptions import ValidationError
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from .models import LearningMentor
@receiver(m2m_changed, sender=LearningMentor.participants.through)
def validate_student(sender, instance, action, reverse, model, pk_set, **kwargs):
if action == "pre_add":
participants = model.objects.filter(pk__in=pk_set)
for participant in participants:
if participant.course_session != instance.course_session:
raise ValidationError(
"Participant (CourseSessionUser) does not match the course for this mentor."
)
if participant.user == instance.mentor:
raise ValidationError("You cannot mentor yourself.")

View File

@ -11,7 +11,11 @@ from vbv_lernwelt.course.creators.test_utils import (
create_user,
)
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
MentorInvitation,
)
from vbv_lernwelt.notify.email.email_services import EmailTemplate
@ -254,13 +258,14 @@ class LearningMentorInvitationTest(APITestCase):
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertFalse(MentorInvitation.objects.filter(id=invitation.id).exists())
self.assertTrue(
LearningMentor.objects.filter(
mentor=invitee,
course_session=self.course_session,
participants=participant_cs_user,
).exists()
)
relation_qs = AgentParticipantRelation.objects.all()
self.assertEqual(relation_qs.count(), 1)
relation = relation_qs.first()
self.assertEqual(relation.agent, invitee)
self.assertEqual(relation.participant, participant_cs_user)
self.assertEqual(relation.participant.course_session, self.course_session)
self.assertEqual(relation.role, AgentParticipantRoleType.LEARNING_MENTOR.value)
user = response.data["user"]
self.assertEqual(user["id"], str(self.participant.id))

View File

@ -22,7 +22,10 @@ from vbv_lernwelt.course.creators.test_utils import (
create_user,
)
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
)
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
@ -71,63 +74,56 @@ class LearningMentorAPITest(APITestCase):
response = self.client.get(self.url)
# THEN
print(response.data)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_api_no_participants(self) -> None:
# GIVEN
self.client.force_login(self.mentor)
LearningMentor.objects.create(
mentor=self.mentor, course_session=self.course_session
)
# WHEN
response = self.client.get(self.url)
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["participants"], [])
self.assertEqual(response.data["assignments"], [])
print(response.data)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_api_participants(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_session=self.course_session
)
mentor.participants.set(participants)
for participant in participants:
AgentParticipantRelation.objects.create(
agent=self.mentor,
participant=participant,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
# WHEN
self.client.force_login(self.mentor)
response = self.client.get(self.url)
# THEN
print(response.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["participants"]), len(participants))
self.assertEqual(len(response.data["participant_relations"]), len(participants))
participant_1 = [
p
for p in response.data["participants"]
if p["id"] == str(self.participant_1.user.id)
][0]
self.assertEqual(participant_1["email"], "participant_1@example.com")
self.assertEqual(participant_1["first_name"], "Test")
self.assertEqual(participant_1["last_name"], "Participant_1")
rel1 = AgentParticipantRelation.objects.get(participant=self.participant_1)
self.assertEqual(
response.data["mentor_id"],
mentor.id,
)
self.assertEqual(rel1.participant.user.email, "participant_1@example.com")
self.assertEqual(rel1.participant.user.first_name, "Test")
self.assertEqual(rel1.participant.user.last_name, "Participant_1")
def test_api_self_evaluation_feedback(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_session=self.course_session
)
mentor.participants.set(participants)
for participant in participants:
AgentParticipantRelation.objects.create(
agent=self.mentor,
participant=participant,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
learning_unit = create_learning_unit(
circle=self.circle,
@ -154,6 +150,7 @@ class LearningMentorAPITest(APITestCase):
# ...
# WHEN
self.client.force_login(self.mentor)
response = self.client.get(self.url)
# THEN
@ -194,7 +191,6 @@ class LearningMentorAPITest(APITestCase):
def test_api_praxis_assignments(self) -> None:
# GIVEN
self.client.force_login(self.mentor)
assignment = create_assignment(
course=self.course, assignment_type=AssignmentType.PRAXIS_ASSIGNMENT
@ -205,13 +201,13 @@ class LearningMentorAPITest(APITestCase):
course_session=self.course_session, learning_content_assignment=lca
)
mentor = LearningMentor.objects.create(
mentor=self.mentor,
course_session=self.course_session,
)
participants = [self.participant_1, self.participant_2, self.participant_3]
mentor.participants.set(participants)
for participant in participants:
AgentParticipantRelation.objects.create(
agent=self.mentor,
participant=participant,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
AssignmentCompletion.objects.create(
assignment_user=self.participant_1.user,
@ -230,6 +226,7 @@ class LearningMentorAPITest(APITestCase):
)
# WHEN
self.client.force_login(self.mentor)
response = self.client.get(self.url)
# THEN
@ -267,12 +264,12 @@ class LearningMentorAPITest(APITestCase):
role=CourseSessionUser.Role.MEMBER,
)
learning_mentor = LearningMentor.objects.create(
mentor=self.mentor, course_session=self.course_session
db_relation = AgentParticipantRelation.objects.create(
agent=self.mentor,
participant=participant_cs_user,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
learning_mentor.participants.add(participant_cs_user)
list_url = reverse(
"list_user_mentors", kwargs={"course_session_id": self.course_session.id}
)
@ -281,12 +278,13 @@ class LearningMentorAPITest(APITestCase):
response = self.client.get(list_url)
# THEN
print(response.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
mentor = response.data[0]
self.assertEqual(mentor["id"], learning_mentor.id)
mentor_relation = response.data[0]
self.assertEqual(mentor_relation["id"], str(db_relation.id))
mentor_user = mentor["mentor"]
mentor_user = mentor_relation["agent"]
self.assertEqual(mentor_user["email"], self.mentor.email)
self.assertEqual(mentor_user["id"], str(self.mentor.id))
@ -300,18 +298,17 @@ class LearningMentorAPITest(APITestCase):
role=CourseSessionUser.Role.MEMBER,
)
learning_mentor = LearningMentor.objects.create(
mentor=self.mentor, course_session=self.course_session
relation = AgentParticipantRelation.objects.create(
agent=self.mentor,
participant=participant_cs_user,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
learning_mentor.participants.add(participant_cs_user)
remove_url = reverse(
"remove_participant_from_mentor",
"delete_agent_participant_relation",
kwargs={
"course_session_id": self.course_session.id,
"mentor_id": learning_mentor.id,
"participant_user_id": participant_cs_user.user.id,
"relation_id": relation.id,
},
)
@ -320,9 +317,7 @@ class LearningMentorAPITest(APITestCase):
# THEN
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertFalse(
LearningMentor.objects.filter(participants=participant_cs_user).exists()
)
self.assertEqual(AgentParticipantRelation.objects.count(), 0)
def test_remove_myself_from_mentor(self) -> None:
# GIVEN
@ -335,18 +330,17 @@ class LearningMentorAPITest(APITestCase):
role=CourseSessionUser.Role.MEMBER,
)
learning_mentor = LearningMentor.objects.create(
mentor=self.mentor, course_session=self.course_session
relation = AgentParticipantRelation.objects.create(
agent=self.mentor,
participant=participant_cs_user,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
learning_mentor.participants.add(participant_cs_user)
remove_url = reverse(
"remove_participant_from_mentor",
"delete_agent_participant_relation",
kwargs={
"course_session_id": self.course_session.id,
"mentor_id": learning_mentor.id,
"participant_user_id": participant_cs_user.user.id,
"relation_id": relation.id,
},
)
@ -355,26 +349,54 @@ class LearningMentorAPITest(APITestCase):
# THEN
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertFalse(
LearningMentor.objects.filter(participants=participant_cs_user).exists()
)
self.assertEqual(AgentParticipantRelation.objects.count(), 0)
def test_mentor_multiple_courses(self) -> None:
# GIVEN
course_a, _ = create_course("Course A")
course_session_a = create_course_session(course=course_a, title="Test A")
participant_a = create_user("participant_a")
participant_course_session_a = add_course_session_user(
course_session_a,
participant_a,
role=CourseSessionUser.Role.MEMBER,
)
course_b, _ = create_course("Course B")
course_session_b = create_course_session(course=course_b, title="Test B")
participant_b = create_user("participant_b")
participant_course_session_b = add_course_session_user(
course_session_b,
participant_b,
role=CourseSessionUser.Role.MEMBER,
)
# WHEN
LearningMentor.objects.create(
mentor=self.mentor, course_session=course_session_a
relation_a = AgentParticipantRelation.objects.create(
agent=self.mentor,
participant=participant_course_session_a,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
relation_b = AgentParticipantRelation.objects.create(
agent=self.mentor,
participant=participant_course_session_b,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
LearningMentor.objects.create(
mentor=self.mentor, course_session=course_session_b
self.client.force_login(self.mentor)
response_a = self.client.get(
reverse("mentor_summary", kwargs={"course_session_id": course_session_a.id})
)
self.assertEqual(len(response_a.data["participant_relations"]), 1)
self.assertEqual(
response_a.data["participant_relations"][0]["id"], str(relation_a.id)
)
# THEN
self.assertEqual(LearningMentor.objects.count(), 2)
response_b = self.client.get(
reverse("mentor_summary", kwargs={"course_session_id": course_session_b.id})
)
self.assertEqual(len(response_b.data["participant_relations"]), 1)
self.assertEqual(
response_b.data["participant_relations"][0]["id"], str(relation_b.id)
)

View File

@ -6,9 +6,9 @@ urlpatterns = [
path("summary", views.mentor_summary, name="mentor_summary"),
path("mentors", views.list_user_mentors, name="list_user_mentors"),
path(
"mentors/<int:mentor_id>/remove/<uuid:participant_user_id>",
views.remove_participant_from_mentor,
name="remove_participant_from_mentor",
"mentors/<uuid:relation_id>/delete",
views.delete_agent_participant_relation,
name="delete_agent_participant_relation",
),
path("invitations", views.list_invitations, name="list_invitations"),
path("invitations/create", views.create_invitation, name="create_invitation"),

View File

@ -16,11 +16,15 @@ from vbv_lernwelt.learning_mentor.content.praxis_assignment import (
from vbv_lernwelt.learning_mentor.content.self_evaluation_feedback import (
get_self_feedback_evaluation,
)
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
MentorInvitation,
)
from vbv_lernwelt.learning_mentor.serializers import (
AgentParticipantRelationSerializer,
InvitationSerializer,
MentorAssignmentStatusSerializer,
MentorSerializer,
)
from vbv_lernwelt.learnpath.models import Circle
from vbv_lernwelt.notify.email.email_services import EmailTemplate, send_email
@ -31,12 +35,16 @@ from vbv_lernwelt.notify.email.email_services import EmailTemplate, send_email
def mentor_summary(request, course_session_id: int):
course_session = CourseSession.objects.get(id=course_session_id)
mentor = get_object_or_404(
LearningMentor, mentor=request.user, course_session=course_session
participant_qs = AgentParticipantRelation.objects.filter(
agent=request.user,
participant__course_session=course_session,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
participants = mentor.participants.filter(course_session=course_session)
users = [p.user for p in participants]
if participant_qs.count() == 0:
return Response(status=status.HTTP_404_NOT_FOUND)
users = [p.participant.user for p in participant_qs]
assignments = []
circle_ids = set()
@ -71,8 +79,9 @@ def mentor_summary(request, course_session_id: int):
)
return Response(
{
"mentor_id": mentor.id,
"participants": [UserSerializer(user).data for user in users],
"participant_relations": AgentParticipantRelationSerializer(
participant_qs, many=True
).data,
"circles": list(
Circle.objects.filter(id__in=circle_ids).values("id", "title")
),
@ -176,27 +185,24 @@ def list_user_mentors(request, course_session_id: int):
CourseSessionUser, user=request.user, course_session=course_session
)
mentors = LearningMentor.objects.filter(
course_session=course_session, participants=course_session_user
mentors = AgentParticipantRelation.objects.filter(
participant=course_session_user,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
return Response(MentorSerializer(mentors, many=True).data)
return Response(AgentParticipantRelationSerializer(mentors, many=True).data)
@api_view(["DELETE"])
@permission_classes([IsAuthenticated])
def remove_participant_from_mentor(
request, course_session_id: int, mentor_id: int, participant_user_id: uuid.UUID
def delete_agent_participant_relation(
request, course_session_id: int, relation_id: uuid.UUID
):
requester_user = request.user
mentor = get_object_or_404(
LearningMentor, id=mentor_id, course_session_id=course_session_id
)
relation = get_object_or_404(AgentParticipantRelation, id=relation_id)
is_requester_mentor = requester_user.id == mentor.mentor_id
is_requester_participant = mentor.participants.filter(
user=requester_user, course_session_id=course_session_id
).exists()
is_requester_mentor = requester_user.id == relation.agent.id
is_requester_participant = requester_user.id == relation.participant.user.id
if not is_requester_mentor and not is_requester_participant:
return Response(
@ -204,12 +210,7 @@ def remove_participant_from_mentor(
status=status.HTTP_403_FORBIDDEN,
)
course_session = get_object_or_404(CourseSession, id=course_session_id)
course_session_user = get_object_or_404(
CourseSessionUser, user=participant_user_id, course_session=course_session
)
mentor.participants.remove(course_session_user)
relation.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
@ -228,11 +229,12 @@ def accept_invitation(request, course_session_id: int):
status=status.HTTP_400_BAD_REQUEST,
)
mentor, _ = LearningMentor.objects.get_or_create(
mentor=request.user, course_session=course_session
AgentParticipantRelation.objects.get_or_create(
agent=request.user,
participant=invitation.participant,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
mentor.participants.add(invitation.participant)
invitation.delete()
return Response(

View File

@ -2,6 +2,7 @@ import imghdr
from wsgiref.util import FileWrapper
import structlog
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, StreamingHttpResponse
from django.shortcuts import get_object_or_404
@ -21,6 +22,16 @@ def user_image(request, image_id):
try:
rendition = image.get_rendition(filter_spec)
rendition.file.open("rb")
image_format = imghdr.what(rendition.file)
return StreamingHttpResponse(
FileWrapper(rendition.file),
content_type=(
f"image/{image_format}" if image_format else "binary/octet-stream"
),
)
except SourceImageIOError:
return HttpResponse(
"Source image file not found", content_type="text/plain", status=410
@ -31,11 +42,14 @@ def user_image(request, image_id):
content_type="text/plain",
status=400,
)
rendition.file.open("rb")
image_format = imghdr.what(rendition.file)
return StreamingHttpResponse(
FileWrapper(rendition.file),
content_type=f"image/{image_format}" if image_format else "binary/octet-stream",
)
except Exception:
if settings.APP_ENVIRONMENT.startswith("local"):
# do not spam the console
logger.warning("Error while serving image")
else:
logger.exception(
"Error while serving image", exc_info=True, label="s3_mediafiles"
)
return HttpResponse(
"Error while serving image", content_type="text/plain", status=400
)

View File

@ -14,7 +14,7 @@ from vbv_lernwelt.course.creators.test_utils import (
)
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSessionUser
from vbv_lernwelt.course.services import mark_course_completion
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
from vbv_lernwelt.self_evaluation_feedback.models import (
CourseCompletionFeedback,
SelfEvaluationFeedback,
@ -51,13 +51,10 @@ class SelfEvaluationFeedbackAPI(APITestCase):
title="Test Circle", course_page=self.course_page
)
learning_mentor = LearningMentor.objects.create(
mentor=self.mentor,
course_session=self.course_session,
AgentParticipantRelation.objects.create(
agent=self.mentor, participant=member_csu, role="LEARNING_MENTOR"
)
learning_mentor.participants.add(member_csu)
@patch(
"vbv_lernwelt.notify.services.NotificationService.send_self_evaluation_feedback_request_feedback_notification"
)

View File

@ -11,7 +11,7 @@ from vbv_lernwelt.core.models import User
from vbv_lernwelt.core.serializers import UserSerializer
from vbv_lernwelt.course.models import CourseCompletion, CourseSession
from vbv_lernwelt.iam.permissions import can_view_course_completions
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
from vbv_lernwelt.learnpath.models import Circle, LearningUnit
from vbv_lernwelt.notify.services import NotificationService
from vbv_lernwelt.self_evaluation_feedback.models import (
@ -38,10 +38,9 @@ def start_self_evaluation_feedback(request, learning_unit_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_session__course=learning_unit.get_course(),
mentor=feedback_provider_user,
participants__user=request.user,
if not AgentParticipantRelation.objects.filter(
agent=feedback_provider_user,
participant__user=request.user,
).exists():
raise PermissionDenied()

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.25 on 2024-07-17 14:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("shop", "0015_cembra_fields"),
]
operations = [
migrations.AlterField(
model_name="checkoutinformation",
name="refno2",
field=models.CharField(max_length=255),
),
]

View File

@ -5,7 +5,10 @@ from keycloak.exceptions import KeycloakDeleteError, KeycloakPostError
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
)
from vbv_lernwelt.sso.models import SsoSyncError, SsoUser
from vbv_lernwelt.sso.role_sync.services import (
create_and_update_user,
@ -20,7 +23,7 @@ def create_sso_user_from_admin(user: User, request):
create_and_update_user(user) # noqa
user.save()
messages.add_message(
request, messages.SUCCESS, f"Der Bentuzer wurde in Keycloak erstellt."
request, messages.SUCCESS, "Der Bentuzer wurde in Keycloak erstellt."
)
except KeycloakPostError as e:
messages.add_message(
@ -31,24 +34,26 @@ def create_sso_user_from_admin(user: User, request):
def sync_sso_roles_from_admin(user: User, request):
course_roles = [
course_roles = {
(csu.course_session.course.slug, csu.role)
for csu in CourseSessionUser.objects.filter(user=user)
]
}
course_roles += [
(lm.course_session.course.slug, "LEARNING_MENTOR")
for lm in LearningMentor.objects.filter(mentor=user)
]
course_roles += {
(relation.participant.course_session.course.slug, "LEARNING_MENTOR")
for relation in AgentParticipantRelation.objects.filter(
agent=user, role=AgentParticipantRoleType.LEARNING_MENTOR.value
)
}
for csg in CourseSessionGroup.objects.filter(supervisor=user):
for course_session in csg.course_session.all():
course_roles.append((course_session.course.slug, "SUPERVISOR"))
course_roles.add((course_session.course.slug, "SUPERVISOR"))
try:
sync_roles_for_user(user, course_roles)
messages.add_message(
request, messages.SUCCESS, f"Die Daten wurden mit Keycloak synchronisiert."
request, messages.SUCCESS, "Die Daten wurden mit Keycloak synchronisiert."
)
except KeycloakDeleteError as e:
messages.add_message(

View File

@ -1,5 +1,5 @@
import unicodedata
from typing import Dict, List, Tuple
from typing import Dict, List, Set, Tuple
import structlog
from django.conf import settings
@ -12,7 +12,7 @@ from vbv_lernwelt.sso.role_sync.roles import ROLE_IDS, SSO_ROLES
logger = structlog.get_logger(__name__)
CourseRolesType = List[Tuple[str, str]]
CourseRolesType = Set[Tuple[str, str]]
KeyCloakRolesType = List[Dict[str, str]]
keycloak_admin = None # Needed for pytest

View File

@ -6,7 +6,10 @@ from keycloak.exceptions import KeycloakDeleteError, KeycloakError, KeycloakPost
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import (
AgentParticipantRelation,
AgentParticipantRoleType,
)
from vbv_lernwelt.sso.role_sync.services import (
add_roles_to_user,
remove_roles_from_user,
@ -78,21 +81,39 @@ def update_sso_roles_in_csg(sender, instance, action, reverse, model, pk_set, **
# LearningMentor
@receiver(post_delete, sender=LearningMentor, dispatch_uid="delete_sso_roles_in_lm")
def remove_sso_roles_in_lm(sender, instance: LearningMentor, **kwargs):
if not LearningMentor.objects.filter(
mentor=instance.mentor, course_session__course=instance.course_session.course
@receiver(
post_delete, sender=AgentParticipantRelation, dispatch_uid="delete_sso_roles_in_lm"
)
def remove_sso_roles_in_lm(sender, instance: AgentParticipantRelation, **kwargs):
if not AgentParticipantRelation.objects.filter(
agent=instance.agent,
participant__course_session__course=instance.participant.course_session.course,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
).exists():
_remove_sso_role(
instance.mentor, instance.course_session.course.slug, "LEARNING_MENTOR"
instance.agent,
instance.participant.course_session.course.slug,
"LEARNING_MENTOR",
)
@receiver(pre_save, sender=LearningMentor, dispatch_uid="update_sso_roles_in_lm")
def update_sso_roles_in_lm(sender, instance: LearningMentor, **kwargs):
if not instance.pk:
@receiver(
pre_save, sender=AgentParticipantRelation, dispatch_uid="update_sso_roles_in_lm"
)
def update_sso_roles_in_lm(sender, instance: AgentParticipantRelation, **kwargs):
if (
instance.role == AgentParticipantRoleType.LEARNING_MENTOR.value
and AgentParticipantRelation.objects.filter(
agent=instance.agent,
participant__course_session__course=instance.participant.course_session.course,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
).count()
== 0
):
_add_sso_role(
instance.mentor, instance.course_session.course.slug, "LEARNING_MENTOR"
instance.agent,
instance.participant.course_session.course.slug,
"LEARNING_MENTOR",
)

View File

@ -24,7 +24,7 @@ from vbv_lernwelt.course.creators.test_utils import (
)
from vbv_lernwelt.course.models import Course, CourseSessionUser
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
from vbv_lernwelt.sso.signals import update_sso_roles_in_cs
@ -217,10 +217,16 @@ class LearningMentorTests(TestCase):
def setUp(self):
self.course, self.course_page = create_course("Test Course")
self.course_session = create_course_session(course=self.course, title="Test VV")
self.user = create_user("mentor")
self.mentor = LearningMentor.objects.create(
mentor=self.user, course_session=self.course_session
participant = create_user("participant")
self.csu = add_course_session_user(
self.course_session,
participant,
role=CourseSessionUser.Role.MEMBER,
)
self.relation = AgentParticipantRelation.objects.create(
agent=self.user, participant=self.csu, role="LEARNING_MENTOR"
)
@patch("vbv_lernwelt.sso.signals.remove_roles_from_user")
@ -229,7 +235,7 @@ class LearningMentorTests(TestCase):
):
mock_remove_roles_from_user.return_value = None
self.mentor.delete()
self.relation.delete()
self.assertEqual(mock_remove_roles_from_user.call_count, 1)
@ -240,10 +246,10 @@ class LearningMentorTests(TestCase):
@patch("vbv_lernwelt.sso.signals.add_roles_to_user")
def test_add_roles_for_learning_mentor_on_create(self, mock_add_roles_from_user):
mock_add_roles_from_user.return_value = None
self.mentor.delete()
self.relation.delete()
LearningMentor.objects.create(
mentor=self.user, course_session=self.course_session
AgentParticipantRelation.objects.create(
agent=self.user, participant=self.csu, role="LEARNING_MENTOR"
)
self.assertEqual(mock_add_roles_from_user.call_count, 1)
@ -263,5 +269,8 @@ class LearningMentorTests(TestCase):
)
mock_add_roles_from_user.reset_mock()
self.mentor.participants.set([participant_1])
AgentParticipantRelation.objects.create(
agent=self.user, participant=participant_1, role="LEARNING_MENTOR"
)
self.assertEqual(mock_add_roles_from_user.call_count, 0)