Refactor `LearningMentor` model to flat `AgentParticipantRelation` model
This commit is contained in:
parent
3f02fd254a
commit
cdfb9d2c5b
|
|
@ -8,9 +8,9 @@ import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||||
const courseSession = useCurrentCourseSession();
|
const courseSession = useCurrentCourseSession();
|
||||||
const { isLoading, summary, fetchData } = useLearningMentees(courseSession.value.id);
|
const { isLoading, summary, fetchData } = useLearningMentees(courseSession.value.id);
|
||||||
|
|
||||||
const removeMyMentee = async (menteeId: string) => {
|
const removeMyMentee = async (relationId: string) => {
|
||||||
await useCSRFFetch(
|
await useCSRFFetch(
|
||||||
`/api/mentor/${courseSession.value.id}/mentors/${summary.value?.mentor_id}/remove/${menteeId}`
|
`/api/mentor/${courseSession.value.id}/mentors/${relationId}/delete`
|
||||||
).delete();
|
).delete();
|
||||||
fetchData();
|
fetchData();
|
||||||
};
|
};
|
||||||
|
|
@ -28,25 +28,31 @@ const noMenteesText = computed(() =>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h2 class="heading-2 py-6">{{ $t("a.Personen, die du begleitest") }}</h2>
|
<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
|
<div
|
||||||
v-for="participant in summary?.participants ?? []"
|
v-for="relation in summary?.participant_relations ?? []"
|
||||||
:key="participant.id"
|
:key="relation.id"
|
||||||
data-cy="lm-my-mentee-list-item"
|
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"
|
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">
|
<div class="flex items-center space-x-2">
|
||||||
<img
|
<img
|
||||||
:alt="participant.last_name"
|
:alt="relation.participant_user.last_name"
|
||||||
class="h-11 w-11 rounded-full"
|
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>
|
||||||
<div class="text-bold">
|
<div class="text-bold">
|
||||||
{{ participant.first_name }}
|
{{ relation.participant_user.first_name }}
|
||||||
{{ participant.last_name }}
|
{{ relation.participant_user.last_name }}
|
||||||
</div>
|
</div>
|
||||||
{{ participant.email }}
|
{{ relation.participant_user.email }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-x-5">
|
<div class="space-x-5">
|
||||||
|
|
@ -55,7 +61,7 @@ const noMenteesText = computed(() =>
|
||||||
:to="{
|
:to="{
|
||||||
name: 'profileLearningPath',
|
name: 'profileLearningPath',
|
||||||
params: {
|
params: {
|
||||||
userId: participant.id,
|
userId: relation.participant_user.id,
|
||||||
courseSlug: courseSession.course.slug,
|
courseSlug: courseSession.course.slug,
|
||||||
},
|
},
|
||||||
}"
|
}"
|
||||||
|
|
@ -66,7 +72,7 @@ const noMenteesText = computed(() =>
|
||||||
<button
|
<button
|
||||||
class="underline"
|
class="underline"
|
||||||
data-cy="lm-my-mentee-remove"
|
data-cy="lm-my-mentee-remove"
|
||||||
@click="removeMyMentee(participant.id)"
|
@click="removeMyMentee(relation.id)"
|
||||||
>
|
>
|
||||||
{{ $t("a.Entfernen") }}
|
{{ $t("a.Entfernen") }}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,9 @@ const removeInvitation = async (invitationId: string) => {
|
||||||
await refreshInvitations();
|
await refreshInvitations();
|
||||||
};
|
};
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const removeMyMentor = async (relationId: string) => {
|
||||||
|
|
||||||
const removeMyMentor = async (mentorId: string) => {
|
|
||||||
await useCSRFFetch(
|
await useCSRFFetch(
|
||||||
`/api/mentor/${courseSession.value.id}/mentors/${mentorId}/remove/${userStore.id}`
|
`/api/mentor/${courseSession.value.id}/mentors/${relationId}/delete`
|
||||||
).delete();
|
).delete();
|
||||||
await refreshMentors();
|
await refreshMentors();
|
||||||
};
|
};
|
||||||
|
|
@ -138,16 +136,16 @@ const noLearningMentors = computed(() =>
|
||||||
>
|
>
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<img
|
<img
|
||||||
:alt="learningMentor.mentor.last_name"
|
:alt="learningMentor.agent.last_name"
|
||||||
class="h-11 w-11 rounded-full"
|
class="h-11 w-11 rounded-full"
|
||||||
:src="learningMentor.mentor.avatar_url"
|
:src="learningMentor.agent.avatar_url"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-bold">
|
<div class="text-bold">
|
||||||
{{ learningMentor.mentor.first_name }}
|
{{ learningMentor.agent.first_name }}
|
||||||
{{ learningMentor.mentor.last_name }}
|
{{ learningMentor.agent.last_name }}
|
||||||
</div>
|
</div>
|
||||||
{{ learningMentor.mentor.email }}
|
{{ learningMentor.agent.email }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,9 @@ const onSubmit = async () => {
|
||||||
<option
|
<option
|
||||||
v-for="learningMentor in learningMentors"
|
v-for="learningMentor in learningMentors"
|
||||||
:key="learningMentor.id"
|
: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>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ import type {
|
||||||
CourseSessionDetail,
|
CourseSessionDetail,
|
||||||
DashboardPersonsPageMode,
|
DashboardPersonsPageMode,
|
||||||
LearningContentWithCompletion,
|
LearningContentWithCompletion,
|
||||||
LearningMentor,
|
AgentParticipantRelation,
|
||||||
LearningPathType,
|
LearningPathType,
|
||||||
LearningUnitPerformanceCriteria,
|
LearningUnitPerformanceCriteria,
|
||||||
PerformanceCriteria,
|
PerformanceCriteria,
|
||||||
|
|
@ -489,7 +489,7 @@ export function useFileUpload() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useMyLearningMentors() {
|
export function useMyLearningMentors() {
|
||||||
const learningMentors = ref<LearningMentor[]>([]);
|
const learningMentors = ref<AgentParticipantRelation[]>([]);
|
||||||
const currentCourseSessionId = useCurrentCourseSession().value.id;
|
const currentCourseSessionId = useCurrentCourseSession().value.id;
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Assignment, Participant } from "@/services/learningMentees";
|
import type { Assignment, UserShort } from "@/services/learningMentees";
|
||||||
import { useLearningMentees } from "@/services/learningMentees";
|
import { useLearningMentees } from "@/services/learningMentees";
|
||||||
import { computed, onMounted, type Ref } from "vue";
|
import { computed, onMounted, type Ref } from "vue";
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCurrentCourseSession } from "@/composables";
|
||||||
|
|
@ -11,13 +11,16 @@ const props = defineProps<{
|
||||||
|
|
||||||
const courseSession = useCurrentCourseSession();
|
const courseSession = useCurrentCourseSession();
|
||||||
const learningMentees = useLearningMentees(courseSession.value.id);
|
const learningMentees = useLearningMentees(courseSession.value.id);
|
||||||
const participants = computed(() => learningMentees.summary.value?.participants);
|
|
||||||
const praxisAssignment: Ref<Assignment | null> = computed(() =>
|
const praxisAssignment: Ref<Assignment | null> = computed(() =>
|
||||||
learningMentees.getAssignmentById(props.praxisAssignmentId)
|
learningMentees.getAssignmentById(props.praxisAssignmentId)
|
||||||
);
|
);
|
||||||
|
|
||||||
const getParticipantById = (id: string): Participant | null => {
|
const getParticipantById = (id: string): UserShort | undefined => {
|
||||||
return participants.value?.find((participant) => participant.id === id) || null;
|
return (learningMentees.summary.value?.participant_relations ?? [])
|
||||||
|
.map((rel) => {
|
||||||
|
return rel.participant_user;
|
||||||
|
})
|
||||||
|
.find((user) => user.id === id);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Assignment, Participant } from "@/services/learningMentees";
|
import type { Assignment, UserShort } from "@/services/learningMentees";
|
||||||
import { useLearningMentees } from "@/services/learningMentees";
|
import { useLearningMentees } from "@/services/learningMentees";
|
||||||
import { computed, type Ref } from "vue";
|
import { computed, type Ref } from "vue";
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCurrentCourseSession } from "@/composables";
|
||||||
|
|
@ -15,14 +15,12 @@ const selfEvaluationFeedback: Ref<Assignment | null> = computed(() =>
|
||||||
learningMentees.getAssignmentById(props.learningUnitId)
|
learningMentees.getAssignmentById(props.learningUnitId)
|
||||||
);
|
);
|
||||||
|
|
||||||
const getParticipantById = (id: string): Participant | null => {
|
const getParticipantById = (id: string): UserShort | undefined => {
|
||||||
if (learningMentees.summary.value?.participants) {
|
return (learningMentees.summary.value?.participant_relations ?? [])
|
||||||
const found = learningMentees.summary.value.participants.find(
|
.map((rel) => {
|
||||||
(item) => item.id === id
|
return rel.participant_user;
|
||||||
);
|
})
|
||||||
return found || null;
|
.find((user) => user.id === id);
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ const isMentorsLoading = computed(() => learningMentors.loading.value);
|
||||||
|
|
||||||
const mentors = computed(() => {
|
const mentors = computed(() => {
|
||||||
return learningMentors.learningMentors.value.map((mentor) => ({
|
return learningMentors.learningMentors.value.map((mentor) => ({
|
||||||
id: mentor.mentor.id,
|
id: mentor.agent.id,
|
||||||
name: `${mentor.mentor.first_name} ${mentor.mentor.last_name}`,
|
name: `${mentor.agent.first_name} ${mentor.agent.last_name}`,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { itGet } from "@/fetchHelpers";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import { ref, watchEffect } from "vue";
|
import { ref, watchEffect } from "vue";
|
||||||
|
|
||||||
export interface Participant {
|
export interface UserShort {
|
||||||
id: string;
|
id: string;
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
|
|
@ -12,6 +12,14 @@ export interface Participant {
|
||||||
language: string;
|
language: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AgentParticipantRelation {
|
||||||
|
id: string;
|
||||||
|
role: "LEARNING_MENTOR";
|
||||||
|
course_session_id: number;
|
||||||
|
agent: UserShort;
|
||||||
|
participant_user: UserShort;
|
||||||
|
}
|
||||||
|
|
||||||
interface Circle {
|
interface Circle {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -40,9 +48,8 @@ export interface Assignment {
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Summary {
|
export interface LearningMentorSummary {
|
||||||
mentor_id: string;
|
participant_relations: AgentParticipantRelation[];
|
||||||
participants: Participant[];
|
|
||||||
circles: Circle[];
|
circles: Circle[];
|
||||||
assignments: Assignment[];
|
assignments: Assignment[];
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +58,7 @@ export const useLearningMentees = (
|
||||||
courseSessionId: string | Ref<string> | (() => string)
|
courseSessionId: string | Ref<string> | (() => string)
|
||||||
) => {
|
) => {
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const summary: Ref<Summary | null> = ref(null);
|
const summary: Ref<LearningMentorSummary | null> = ref(null);
|
||||||
const error = ref(null);
|
const error = ref(null);
|
||||||
|
|
||||||
const getAssignmentById = (id: string): Assignment | null => {
|
const getAssignmentById = (id: string): Assignment | null => {
|
||||||
|
|
|
||||||
|
|
@ -468,15 +468,16 @@ export interface ExpertSessionUser extends CourseSessionUser {
|
||||||
role: "EXPERT";
|
role: "EXPERT";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Mentor {
|
export interface Agent {
|
||||||
id: number;
|
id: number;
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LearningMentor {
|
export interface AgentParticipantRelation {
|
||||||
id: number;
|
id: number;
|
||||||
mentor: Mentor;
|
role: "LEARNING_MENTOR";
|
||||||
|
agent: Agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CourseSessionDetail = CourseSessionObjectType;
|
export type CourseSessionDetail = CourseSessionObjectType;
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@ def send_learning_mentor_invitation():
|
||||||
recipient_email="daniel.egger+sendgrid@gmail.com",
|
recipient_email="daniel.egger+sendgrid@gmail.com",
|
||||||
template=EmailTemplate.LEARNING_MENTOR_INVITATION,
|
template=EmailTemplate.LEARNING_MENTOR_INVITATION,
|
||||||
template_data={
|
template_data={
|
||||||
"inviter_name": f"Daniel Egger",
|
"inviter_name": "Daniel Egger",
|
||||||
"inviter_email": "daniel.egger@example.com",
|
"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",
|
template_language="de",
|
||||||
fail_silently=True,
|
fail_silently=True,
|
||||||
|
|
|
||||||
|
|
@ -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.models import CourseSessionAttendanceCourse
|
||||||
from vbv_lernwelt.course_session.services.attendance import AttendanceUserStatus
|
from vbv_lernwelt.course_session.services.attendance import AttendanceUserStatus
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
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 (
|
from vbv_lernwelt.learnpath.models import (
|
||||||
LearningContentAttendanceCourse,
|
LearningContentAttendanceCourse,
|
||||||
LearningContentFeedbackUK,
|
LearningContentFeedbackUK,
|
||||||
|
|
@ -144,7 +147,8 @@ def command(
|
||||||
SelfEvaluationFeedback.objects.all().delete()
|
SelfEvaluationFeedback.objects.all().delete()
|
||||||
CourseCompletionFeedback.objects.all().delete()
|
CourseCompletionFeedback.objects.all().delete()
|
||||||
|
|
||||||
LearningMentor.objects.all().delete()
|
AgentParticipantRelation.objects.all().delete()
|
||||||
|
# LearningMentor.objects.all().delete()
|
||||||
MentorInvitation.objects.all().delete()
|
MentorInvitation.objects.all().delete()
|
||||||
User.objects.all().update(organisation=Organisation.objects.first())
|
User.objects.all().update(organisation=Organisation.objects.first())
|
||||||
User.objects.all().update(language="de")
|
User.objects.all().update(language="de")
|
||||||
|
|
@ -414,48 +418,40 @@ def command(
|
||||||
|
|
||||||
if create_learning_mentor:
|
if create_learning_mentor:
|
||||||
cs_bern = CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID)
|
cs_bern = CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID)
|
||||||
|
AgentParticipantRelation.objects.create(
|
||||||
uk_mentor = LearningMentor.objects.create(
|
agent=User.objects.get(id=TEST_MENTOR1_USER_ID),
|
||||||
mentor=User.objects.get(id=TEST_MENTOR1_USER_ID),
|
participant=CourseSessionUser.objects.get(
|
||||||
course_session=cs_bern,
|
user__id=TEST_STUDENT1_USER_ID, course_session=cs_bern
|
||||||
)
|
),
|
||||||
uk_mentor.participants.add(
|
role="LEARNING_MENTOR",
|
||||||
CourseSessionUser.objects.get(
|
|
||||||
user__id=TEST_STUDENT1_USER_ID,
|
|
||||||
course_session=cs_bern,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
vv_course = Course.objects.get(id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
|
vv_course = Course.objects.get(id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
|
||||||
vv_course_session = CourseSession.objects.get(course=vv_course)
|
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(
|
AgentParticipantRelation.objects.create(
|
||||||
CourseSessionUser.objects.get(
|
agent=User.objects.get(id=TEST_MENTOR1_USER_ID),
|
||||||
user__id=TEST_STUDENT1_VV_USER_ID, course_session=vv_course_session
|
participant=CourseSessionUser.objects.get(
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
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(
|
|
||||||
user__id=TEST_STUDENT1_VV_USER_ID,
|
user__id=TEST_STUDENT1_VV_USER_ID,
|
||||||
course_session=vv_course_session,
|
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)
|
course = Course.objects.get(id=COURSE_TEST_ID)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,10 @@ from vbv_lernwelt.course_session.models import (
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
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.learnpath.models import Circle
|
||||||
from vbv_lernwelt.notify.models import Notification
|
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"])
|
cs = CourseSession.objects.get(import_id=data["ID"])
|
||||||
|
|
||||||
members, trainer, regionenleiter = get_or_create_users_uk()
|
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_to_course_session(cs, members)
|
||||||
add_trainers_to_course_session(cs, [trainer], uk_circle_keys, language)
|
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)
|
create_or_update_assignment_course_session(cs)
|
||||||
members, member_with_mentor, mentor = get_or_create_users_vv()
|
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_to_course_session(cs, members + [member_with_mentor])
|
||||||
add_mentor_to_course_session(cs, [(mentor, 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:
|
if cs:
|
||||||
CourseCompletion.objects.filter(course_session=cs).delete()
|
CourseCompletion.objects.filter(course_session=cs).delete()
|
||||||
Notification.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()
|
CourseSessionEdoniqTest.objects.filter(course_session=cs).delete()
|
||||||
CourseSessionUser.objects.filter(course_session=cs).delete()
|
CourseSessionUser.objects.filter(course_session=cs).delete()
|
||||||
learning_mentor_ids = (
|
|
||||||
LearningMentor.objects.filter(participants__course_session=cs)
|
AgentParticipantRelation.objects.filter(course_session=cs).delete()
|
||||||
.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()
|
|
||||||
else:
|
else:
|
||||||
logger.info("no_course_session_found", import_id=cs.import_id)
|
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]]
|
course_session: CourseSession, mentor_mentee_pairs: list[tuple[User, User]]
|
||||||
):
|
):
|
||||||
for mentor, mentee in mentor_mentee_pairs:
|
for mentor, mentee in mentor_mentee_pairs:
|
||||||
lm = LearningMentor.objects.create(
|
AgentParticipantRelation.objects.create(
|
||||||
course_session=course_session,
|
agent=mentor,
|
||||||
mentor=mentor,
|
participant=CourseSessionUser.objects.get(
|
||||||
)
|
user__id=mentee.id, course_session=course_session
|
||||||
lm.participants.add(
|
),
|
||||||
CourseSessionUser.objects.get(
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
user__id=mentee.id,
|
|
||||||
course_session=course_session,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,20 @@ class UserSerializer(serializers.ModelSerializer):
|
||||||
return instance
|
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 CypressUserSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,10 @@ from vbv_lernwelt.course_session.models import (
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||||
from vbv_lernwelt.duedate.models import DueDate
|
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 (
|
from vbv_lernwelt.learnpath.models import (
|
||||||
Circle,
|
Circle,
|
||||||
LearningContentAssignment,
|
LearningContentAssignment,
|
||||||
|
|
@ -101,13 +104,13 @@ def create_course_session(
|
||||||
|
|
||||||
|
|
||||||
def add_learning_mentor(
|
def add_learning_mentor(
|
||||||
course_session: CourseSession, mentor: User, mentee: CourseSessionUser
|
mentor: User, mentee: CourseSessionUser
|
||||||
) -> LearningMentor:
|
) -> AgentParticipantRelation:
|
||||||
learning_mentor = LearningMentor.objects.create(
|
return AgentParticipantRelation.objects.create(
|
||||||
course_session=course_session, mentor=mentor
|
agent=mentor,
|
||||||
|
participant=mentee,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
)
|
)
|
||||||
learning_mentor.participants.add(mentee)
|
|
||||||
return learning_mentor
|
|
||||||
|
|
||||||
|
|
||||||
def add_course_session_user(
|
def add_course_session_user(
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,7 @@ from vbv_lernwelt.competence.create_vv_new_competence_profile import (
|
||||||
create_vv_new_competence_profile,
|
create_vv_new_competence_profile,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.competence.models import PerformanceCriteria
|
from vbv_lernwelt.competence.models import PerformanceCriteria
|
||||||
from vbv_lernwelt.core.constants import (
|
from vbv_lernwelt.core.constants import TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID
|
||||||
TEST_MENTOR1_USER_ID,
|
|
||||||
TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID,
|
|
||||||
)
|
|
||||||
from vbv_lernwelt.core.create_default_users import default_users
|
from vbv_lernwelt.core.create_default_users import default_users
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.consts import (
|
from vbv_lernwelt.course.consts import (
|
||||||
|
|
@ -95,7 +92,6 @@ from vbv_lernwelt.importer.services import (
|
||||||
import_students_from_excel,
|
import_students_from_excel,
|
||||||
import_trainers_from_excel_for_training,
|
import_trainers_from_excel_for_training,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
|
||||||
from vbv_lernwelt.learnpath.create_vv_new_learning_path import (
|
from vbv_lernwelt.learnpath.create_vv_new_learning_path import (
|
||||||
create_vv_motorfahrzeug_pruefung_learning_path,
|
create_vv_motorfahrzeug_pruefung_learning_path,
|
||||||
create_vv_new_learning_path,
|
create_vv_new_learning_path,
|
||||||
|
|
@ -243,6 +239,11 @@ def create_versicherungsvermittlerin_course(
|
||||||
course_session=cs,
|
course_session=cs,
|
||||||
user=User.objects.get(username="student-vv@eiger-versicherungen.ch"),
|
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(
|
CourseSessionUser.objects.create(
|
||||||
course_session=cs,
|
course_session=cs,
|
||||||
|
|
@ -262,30 +263,6 @@ def create_versicherungsvermittlerin_course(
|
||||||
role=CourseSessionUser.Role.EXPERT,
|
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:
|
for admin_email in ADMIN_EMAILS:
|
||||||
CourseSessionUser.objects.create(
|
CourseSessionUser.objects.create(
|
||||||
course_session=cs,
|
course_session=cs,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -27,7 +27,7 @@ from vbv_lernwelt.iam.permissions import (
|
||||||
has_course_access_by_page_request,
|
has_course_access_by_page_request,
|
||||||
is_circle_expert,
|
is_circle_expert,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
from vbv_lernwelt.learning_mentor.models import AgentParticipantRelation
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
@ -155,9 +155,10 @@ def get_course_sessions(request):
|
||||||
|
|
||||||
# enrich with mentor course sessions
|
# enrich with mentor course sessions
|
||||||
mentor_course_sessions = CourseSession.objects.filter(
|
mentor_course_sessions = CourseSession.objects.filter(
|
||||||
id__in=LearningMentor.objects.filter(mentor=request.user).values_list(
|
id__in=[
|
||||||
"course_session", flat=True
|
rel.participant.course_session_id
|
||||||
)
|
for rel in AgentParticipantRelation.objects.filter(agent=request.user)
|
||||||
|
]
|
||||||
).prefetch_related("course")
|
).prefetch_related("course")
|
||||||
|
|
||||||
all_to_serialize = (
|
all_to_serialize = (
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,10 @@ from vbv_lernwelt.iam.permissions import (
|
||||||
can_view_course_session_group_statistics,
|
can_view_course_session_group_statistics,
|
||||||
can_view_course_session_progress,
|
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
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -95,12 +98,15 @@ class DashboardQuery(graphene.ObjectType):
|
||||||
mentees_ids = set()
|
mentees_ids = set()
|
||||||
course_session_ids = set()
|
course_session_ids = set()
|
||||||
|
|
||||||
mentees = CourseSessionUser.objects.filter(
|
relations_qs = AgentParticipantRelation.objects.filter(
|
||||||
participants__mentor=user, course_session__course=course
|
agent=user,
|
||||||
).values_list("user", "course_session")
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
for user_id, course_session_id in mentees:
|
participant__course_session__course=course,
|
||||||
mentees_ids.add(user_id)
|
)
|
||||||
course_session_ids.add(course_session_id)
|
|
||||||
|
for relation in relations_qs:
|
||||||
|
mentees_ids.add(relation.participant.user_id)
|
||||||
|
course_session_ids.add(relation.participant.course_session_id)
|
||||||
|
|
||||||
return CourseStatisticsType(
|
return CourseStatisticsType(
|
||||||
_id=f"mentor:{course.id}", # noqa
|
_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(
|
def get_learning_mentor_dashboards(
|
||||||
user: User, exclude_course_ids: Set[int]
|
user: User, exclude_course_ids: Set[int]
|
||||||
) -> Tuple[List[Dict[str, str]], Set[int]]:
|
) -> Tuple[List[Dict[str, str]], Set[int]]:
|
||||||
learning_mentor = LearningMentor.objects.filter(mentor=user).exclude(
|
learning_mentor_relation_qs = AgentParticipantRelation.objects.filter(
|
||||||
course_session__course__id__in=exclude_course_ids
|
agent=user, role=AgentParticipantRoleType.LEARNING_MENTOR.value
|
||||||
)
|
).exclude(participant__course_session__course__id__in=exclude_course_ids)
|
||||||
|
|
||||||
dashboards = []
|
dashboards = []
|
||||||
course_ids = set()
|
course_ids = set()
|
||||||
|
|
||||||
for mentor in learning_mentor:
|
for rel in learning_mentor_relation_qs:
|
||||||
course = mentor.course_session.course
|
course = rel.participant.course_session.course
|
||||||
course_ids.add(course.id)
|
|
||||||
if course.id in UK_COURSE_IDS:
|
if course.id in UK_COURSE_IDS:
|
||||||
dashboard_type = DashboardType.PRAXISBILDNER_DASHBOARD
|
dashboard_type = DashboardType.PRAXISBILDNER_DASHBOARD
|
||||||
else:
|
else:
|
||||||
dashboard_type = DashboardType.MENTOR_DASHBOARD
|
dashboard_type = DashboardType.MENTOR_DASHBOARD
|
||||||
dashboards.append(
|
|
||||||
{
|
if course.id not in course_ids:
|
||||||
"id": str(course.id),
|
course_ids.add(course.id)
|
||||||
"name": course.title,
|
dashboards.append(
|
||||||
"slug": course.slug,
|
{
|
||||||
"dashboard_type": dashboard_type,
|
"id": str(course.id),
|
||||||
"course_configuration": course.configuration,
|
"name": course.title,
|
||||||
}
|
"slug": course.slug,
|
||||||
)
|
"dashboard_type": dashboard_type,
|
||||||
|
"course_configuration": course.configuration,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return dashboards, course_ids
|
return dashboards, course_ids
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,22 +110,22 @@ class DashboardTestCase(GraphQLTestCase):
|
||||||
|
|
||||||
self.client.force_login(member)
|
self.client.force_login(member)
|
||||||
|
|
||||||
query = f"""query($course_id: ID!) {{
|
query = """query($course_id: ID!) {
|
||||||
course_progress(course_id: $course_id) {{
|
course_progress(course_id: $course_id) {
|
||||||
course_id
|
course_id
|
||||||
session_to_continue_id
|
session_to_continue_id
|
||||||
competence {{
|
competence {
|
||||||
total_count
|
total_count
|
||||||
success_count
|
success_count
|
||||||
fail_count
|
fail_count
|
||||||
}}
|
}
|
||||||
assignment {{
|
assignment {
|
||||||
total_count
|
total_count
|
||||||
points_max_count
|
points_max_count
|
||||||
points_achieved_count
|
points_achieved_count
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
variables = {"course_id": str(course.id)}
|
variables = {"course_id": str(course.id)}
|
||||||
|
|
@ -268,7 +268,7 @@ class DashboardTestCase(GraphQLTestCase):
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
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)
|
self.client.force_login(mentor)
|
||||||
|
|
||||||
|
|
@ -287,6 +287,7 @@ class DashboardTestCase(GraphQLTestCase):
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
self.assertResponseNoErrors(response)
|
self.assertResponseNoErrors(response)
|
||||||
|
print(response.json())
|
||||||
|
|
||||||
self.assertEqual(len(response.json()["data"]["dashboard_config"]), 1)
|
self.assertEqual(len(response.json()["data"]["dashboard_config"]), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
@ -314,7 +315,7 @@ class DashboardTestCase(GraphQLTestCase):
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
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
|
# WHEN
|
||||||
self.client.force_login(mentor_and_member)
|
self.client.force_login(mentor_and_member)
|
||||||
|
|
@ -350,11 +351,11 @@ class DashboardTestCase(GraphQLTestCase):
|
||||||
|
|
||||||
self.client.force_login(disallowed_user)
|
self.client.force_login(disallowed_user)
|
||||||
|
|
||||||
query = f"""query($course_id: ID!) {{
|
query = """query($course_id: ID!) {
|
||||||
course_statistics(course_id: $course_id) {{
|
course_statistics(course_id: $course_id) {
|
||||||
course_id
|
course_id
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"course_id": str(course.id)}
|
variables = {"course_id": str(course.id)}
|
||||||
|
|
||||||
|
|
@ -384,13 +385,13 @@ class DashboardTestCase(GraphQLTestCase):
|
||||||
|
|
||||||
self.client.force_login(supervisor)
|
self.client.force_login(supervisor)
|
||||||
|
|
||||||
query = f"""query($course_id: ID!) {{
|
query = """query($course_id: ID!) {
|
||||||
course_statistics(course_id: $course_id) {{
|
course_statistics(course_id: $course_id) {
|
||||||
course_id
|
course_id
|
||||||
course_title
|
course_title
|
||||||
course_slug
|
course_slug
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"course_id": str(course_2.id)}
|
variables = {"course_id": str(course_2.id)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.creators.test_utils import add_course_session_user, create_user
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
from vbv_lernwelt.dashboard.tests.test_views import BaseMentorAssignmentTestCase
|
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):
|
class MentorStatisticsTestCase(BaseMentorAssignmentTestCase, GraphQLTestCase):
|
||||||
|
|
@ -19,15 +19,13 @@ class MentorStatisticsTestCase(BaseMentorAssignmentTestCase, GraphQLTestCase):
|
||||||
self.course.configuration.save()
|
self.course.configuration.save()
|
||||||
|
|
||||||
self.mentor = create_user("mentor")
|
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)]
|
self.participants = [create_user(f"participant{i}") for i in range(4)]
|
||||||
|
|
||||||
def test_assignment_statistics(self):
|
def test_assignment_statistics(self):
|
||||||
# WHEN
|
# WHEN
|
||||||
has_lb = [True, True, True, False]
|
has_lb = [True, True, True, False]
|
||||||
has_passed = [True, False, True, False]
|
has_passed = [True, False, True, False]
|
||||||
|
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
csu = add_course_session_user(
|
csu = add_course_session_user(
|
||||||
self.course_session,
|
self.course_session,
|
||||||
|
|
@ -35,7 +33,9 @@ class MentorStatisticsTestCase(BaseMentorAssignmentTestCase, GraphQLTestCase):
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
if has_lb[i]:
|
if has_lb[i]:
|
||||||
self.lm.participants.add(csu)
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
|
||||||
|
)
|
||||||
|
|
||||||
AssignmentCompletion.objects.create(
|
AssignmentCompletion.objects.create(
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
|
|
@ -47,22 +47,22 @@ class MentorStatisticsTestCase(BaseMentorAssignmentTestCase, GraphQLTestCase):
|
||||||
)
|
)
|
||||||
# THEN
|
# THEN
|
||||||
# WHEN
|
# WHEN
|
||||||
query = f"""query ($courseId: ID!) {{
|
query = """query ($courseId: ID!) {
|
||||||
mentor_course_statistics(course_id: $courseId) {{
|
mentor_course_statistics(course_id: $courseId) {
|
||||||
course_session_selection_ids
|
course_session_selection_ids
|
||||||
user_selection_ids
|
user_selection_ids
|
||||||
assignments {{
|
assignments {
|
||||||
_id
|
_id
|
||||||
summary {{
|
summary {
|
||||||
_id
|
_id
|
||||||
completed_count
|
completed_count
|
||||||
average_passed
|
average_passed
|
||||||
total_passed
|
total_passed
|
||||||
total_failed
|
total_failed
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
}}
|
}
|
||||||
}}"""
|
}"""
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
variables = {"courseId": str(self.course.id)}
|
variables = {"courseId": str(self.course.id)}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ from vbv_lernwelt.dashboard.views import (
|
||||||
get_course_config,
|
get_course_config,
|
||||||
get_course_sessions_with_roles_for_user,
|
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.learnpath.models import Circle, LearningUnit
|
||||||
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
||||||
|
|
||||||
|
|
@ -98,14 +98,16 @@ class GetCourseSessionsForUserTestCase(TestCase):
|
||||||
|
|
||||||
def test_learning_mentor_get_sessions(self):
|
def test_learning_mentor_get_sessions(self):
|
||||||
mentor = create_user("mentor")
|
mentor = create_user("mentor")
|
||||||
LearningMentor.objects.create(mentor=mentor, course_session=self.course_session)
|
|
||||||
|
|
||||||
participant = create_user("participant")
|
participant = create_user("participant")
|
||||||
add_course_session_user(
|
csu = add_course_session_user(
|
||||||
self.course_session,
|
self.course_session,
|
||||||
participant,
|
participant,
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=mentor, participant=csu, role="LEARNING_MENTOR"
|
||||||
|
)
|
||||||
|
|
||||||
sessions = get_course_sessions_with_roles_for_user(mentor)
|
sessions = get_course_sessions_with_roles_for_user(mentor)
|
||||||
|
|
||||||
|
|
@ -187,7 +189,16 @@ class GetDashboardConfig(TestCase):
|
||||||
def test_mentor_uk_get_config(self):
|
def test_mentor_uk_get_config(self):
|
||||||
# GIVEN
|
# GIVEN
|
||||||
mentor = create_user("mentor")
|
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.is_uk = True
|
||||||
self.course.configuration.save()
|
self.course.configuration.save()
|
||||||
|
|
@ -205,7 +216,15 @@ class GetDashboardConfig(TestCase):
|
||||||
def test_mentor_vv_get_config(self):
|
def test_mentor_vv_get_config(self):
|
||||||
# GIVEN
|
# GIVEN
|
||||||
mentor = create_user("mentor")
|
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.is_vv = True
|
||||||
self.course.configuration.save()
|
self.course.configuration.save()
|
||||||
|
|
@ -228,7 +247,16 @@ class GetDashboardConfig(TestCase):
|
||||||
mentor,
|
mentor,
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
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.is_vv = True
|
||||||
self.course.configuration.save()
|
self.course.configuration.save()
|
||||||
|
|
@ -264,9 +292,6 @@ class GetMenteeCountTestCase(TestCase):
|
||||||
participants_with_mentor = [create_user(f"participant{i}") for i in range(2)]
|
participants_with_mentor = [create_user(f"participant{i}") for i in range(2)]
|
||||||
participant = create_user("participant")
|
participant = create_user("participant")
|
||||||
mentor = create_user("mentor")
|
mentor = create_user("mentor")
|
||||||
lm = LearningMentor.objects.create(
|
|
||||||
mentor=mentor, course_session=self.course_session
|
|
||||||
)
|
|
||||||
|
|
||||||
# WHEN
|
# WHEN
|
||||||
for p in participants_with_mentor:
|
for p in participants_with_mentor:
|
||||||
|
|
@ -275,7 +300,9 @@ class GetMenteeCountTestCase(TestCase):
|
||||||
p,
|
p,
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
lm.participants.add(csu)
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=mentor, participant=csu, role="LEARNING_MENTOR"
|
||||||
|
)
|
||||||
|
|
||||||
add_course_session_user(
|
add_course_session_user(
|
||||||
self.course_session,
|
self.course_session,
|
||||||
|
|
@ -305,9 +332,6 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
|
||||||
self.course.configuration.save()
|
self.course.configuration.save()
|
||||||
|
|
||||||
self.mentor = create_user("mentor")
|
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)]
|
self.participants = [create_user(f"participant{i}") for i in range(2)]
|
||||||
|
|
||||||
def create_and_test_count(
|
def create_and_test_count(
|
||||||
|
|
@ -337,7 +361,10 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
|
||||||
self.participants[0],
|
self.participants[0],
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
self.lm.participants.add(csu)
|
|
||||||
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
|
||||||
|
)
|
||||||
|
|
||||||
add_course_session_user(
|
add_course_session_user(
|
||||||
self.course_session,
|
self.course_session,
|
||||||
|
|
@ -367,7 +394,9 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
|
||||||
self.participants[0],
|
self.participants[0],
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
self.lm.participants.add(csu)
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
|
||||||
|
)
|
||||||
|
|
||||||
add_course_session_user(
|
add_course_session_user(
|
||||||
self.course_session,
|
self.course_session,
|
||||||
|
|
@ -389,8 +418,9 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
|
||||||
self.participants[0],
|
self.participants[0],
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
self.lm.participants.add(csu)
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
|
||||||
|
)
|
||||||
SelfEvaluationFeedback.objects.create(
|
SelfEvaluationFeedback.objects.create(
|
||||||
feedback_submitted=False,
|
feedback_submitted=False,
|
||||||
feedback_requester_user=self.participants[0],
|
feedback_requester_user=self.participants[0],
|
||||||
|
|
@ -411,8 +441,9 @@ class GetMentorOpenTasksTestCase(BaseMentorAssignmentTestCase):
|
||||||
self.participants[0],
|
self.participants[0],
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
self.lm.participants.add(csu)
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=self.mentor, participant=csu, role="LEARNING_MENTOR"
|
||||||
|
)
|
||||||
SelfEvaluationFeedback.objects.create(
|
SelfEvaluationFeedback.objects.create(
|
||||||
feedback_submitted=True,
|
feedback_submitted=True,
|
||||||
feedback_requester_user=self.participants[0],
|
feedback_requester_user=self.participants[0],
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,10 @@ from vbv_lernwelt.feedback.export import (
|
||||||
export_feedback_with_circle_restriction,
|
export_feedback_with_circle_restriction,
|
||||||
FEEDBACK_EXPORT_FILE_NAME,
|
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.learnpath.models import Circle
|
||||||
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
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
|
result_course_sessions[cs.id] = cs
|
||||||
|
|
||||||
# enrich with mentor course sessions
|
# enrich with mentor course sessions
|
||||||
lm_qs = LearningMentor.objects.filter(mentor=user).prefetch_related(
|
lm_qs = AgentParticipantRelation.objects.filter(agent=user).prefetch_related(
|
||||||
"course_session", "course_session__course"
|
"participant__course_session", "participant__course_session__course"
|
||||||
)
|
)
|
||||||
for lm in lm_qs:
|
for lm in lm_qs:
|
||||||
cs = lm.course_session
|
cs = lm.participant.course_session
|
||||||
cs.roles = set()
|
cs.roles = set()
|
||||||
cs = result_course_sessions.get(cs.id, cs)
|
cs = result_course_sessions.get(cs.id, cs)
|
||||||
|
|
||||||
cs.roles.add("LEARNING_MENTOR")
|
cs.roles.add(lm.role)
|
||||||
result_course_sessions[cs.id] = cs
|
result_course_sessions[cs.id] = cs
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
@ -197,46 +200,46 @@ def _create_person_list_with_roles(user):
|
||||||
# add persons where request.user is mentor
|
# add persons where request.user is mentor
|
||||||
for cs in course_sessions:
|
for cs in course_sessions:
|
||||||
if "LEARNING_MENTOR" in cs.roles:
|
if "LEARNING_MENTOR" in cs.roles:
|
||||||
lm = LearningMentor.objects.filter(
|
for relation in AgentParticipantRelation.objects.filter(
|
||||||
mentor=user, course_session=cs.id
|
agent=user, participant__course_session_id=cs.id
|
||||||
).first()
|
):
|
||||||
|
|
||||||
for participant in lm.participants.all():
|
|
||||||
course_session_entry = _create_course_session_dict(
|
course_session_entry = _create_course_session_dict(
|
||||||
cs,
|
cs,
|
||||||
"LEARNING_MENTOR",
|
"LEARNING_MENTOR",
|
||||||
"LEARNING_MENTEE",
|
"LEARNING_MENTEE",
|
||||||
)
|
)
|
||||||
|
participant_user = relation.participant.user
|
||||||
|
|
||||||
if participant.user.id not in result_persons:
|
if participant_user.id not in result_persons:
|
||||||
person_data = create_user_dict(participant.user)
|
person_data = create_user_dict(participant_user)
|
||||||
person_data["course_sessions"] = [course_session_entry]
|
person_data["course_sessions"] = [course_session_entry]
|
||||||
result_persons[participant.user.id] = person_data
|
result_persons[participant_user] = person_data
|
||||||
else:
|
else:
|
||||||
# user is already in result_persons
|
# user is already in result_persons
|
||||||
result_persons[participant.user.id]["course_sessions"].append(
|
result_persons[participant_user]["course_sessions"].append(
|
||||||
course_session_entry
|
course_session_entry
|
||||||
)
|
)
|
||||||
|
|
||||||
# add persons where request.user is mentee
|
# add persons where request.user is mentee
|
||||||
mentor_relation_qs = LearningMentor.objects.filter(
|
mentor_relation_qs = AgentParticipantRelation.objects.filter(
|
||||||
participants__user=user
|
participant__user=user,
|
||||||
).prefetch_related("mentor", "course_session")
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
|
).prefetch_related("agent")
|
||||||
for mentor_relation in mentor_relation_qs:
|
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(
|
course_session_entry = _create_course_session_dict(
|
||||||
cs,
|
cs,
|
||||||
"LEARNING_MENTEE",
|
"LEARNING_MENTEE",
|
||||||
"LEARNING_MENTOR",
|
"LEARNING_MENTOR",
|
||||||
)
|
)
|
||||||
|
|
||||||
if mentor_relation.mentor.id not in result_persons:
|
if mentor_relation.agent.id not in result_persons:
|
||||||
person_data = create_user_dict(mentor_relation.mentor)
|
person_data = create_user_dict(mentor_relation.agent)
|
||||||
person_data["course_sessions"] = [course_session_entry]
|
person_data["course_sessions"] = [course_session_entry]
|
||||||
result_persons[mentor_relation.mentor.id] = person_data
|
result_persons[mentor_relation.agent.id] = person_data
|
||||||
else:
|
else:
|
||||||
# user is already in result_persons
|
# 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
|
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:
|
def _get_mentee_count(course_id: str, mentor: User) -> int:
|
||||||
return CourseSessionUser.objects.filter(
|
return AgentParticipantRelation.objects.filter(
|
||||||
participants__mentor=mentor, course_session__course__id=course_id
|
agent=mentor,
|
||||||
|
participant__course_session__course_id=course_id,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
).count()
|
).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)
|
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:
|
if course_configuration.is_vv:
|
||||||
open_assigment_count = AssignmentCompletion.objects.filter(
|
open_assigment_count = AssignmentCompletion.objects.filter(
|
||||||
course_session__course__id=course_id,
|
course_session__course__id=course_id,
|
||||||
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
|
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
|
||||||
evaluation_user=mentor, # noqa
|
evaluation_user=mentor, # noqa
|
||||||
assignment_user__coursesessionuser__participants__mentor=mentor,
|
assignment_user_id__in=learning_meentee_ids,
|
||||||
).count()
|
).count()
|
||||||
|
|
||||||
open_feedback_qs = SelfEvaluationFeedback.objects.filter(
|
open_feedback_qs = SelfEvaluationFeedback.objects.filter(
|
||||||
feedback_provider_user=mentor, # noqa
|
feedback_provider_user=mentor, # noqa
|
||||||
feedback_requester_user__coursesessionuser__participants__mentor=mentor,
|
feedback_requester_user_id__in=learning_meentee_ids,
|
||||||
feedback_submitted=False,
|
feedback_submitted=False,
|
||||||
)
|
)
|
||||||
# filter open feedbacks for course_id (-> not possible with queryset)
|
# filter open feedbacks for course_id (-> not possible with queryset)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
|
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
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
|
from vbv_lernwelt.learnpath.models import LearningSequence
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -22,8 +25,8 @@ def has_course_access(user, course_id):
|
||||||
).exists():
|
).exists():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if LearningMentor.objects.filter(
|
if AgentParticipantRelation.objects.filter(
|
||||||
course_session__course_id=course_id, mentor=user
|
agent=user, participant__course_session__course_id=course_id
|
||||||
).exists():
|
).exists():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -72,8 +75,9 @@ def is_course_session_learning_mentor(mentor: User, course_session_id: int):
|
||||||
if course_session is None:
|
if course_session is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return LearningMentor.objects.filter(
|
return AgentParticipantRelation.objects.filter(
|
||||||
mentor=mentor, course_session=course_session
|
agent=mentor,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -87,9 +91,11 @@ def is_learning_mentor_for_user(
|
||||||
if csu is None:
|
if csu is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return LearningMentor.objects.filter(
|
return AgentParticipantRelation.objects.filter(
|
||||||
course_session_id=course_session_id, mentor=mentor, participants=csu
|
agent=mentor,
|
||||||
).exists()
|
participant=csu,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def is_course_session_supervisor(user, course_session_id: int):
|
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():
|
).exists():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if LearningMentor.objects.filter(
|
if AgentParticipantRelation.objects.filter(
|
||||||
course_session__course=course, mentor=user
|
agent=user, participant__course_session__course=course
|
||||||
).exists():
|
).exists():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from vbv_lernwelt.course.creators.test_utils import (
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
from vbv_lernwelt.iam.permissions import course_session_permissions
|
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):
|
class ActionTestCase(TestCase):
|
||||||
|
|
@ -21,14 +21,16 @@ class ActionTestCase(TestCase):
|
||||||
def test_course_session_permissions(self):
|
def test_course_session_permissions(self):
|
||||||
# GIVEN
|
# GIVEN
|
||||||
lm = create_user("mentor")
|
lm = create_user("mentor")
|
||||||
LearningMentor.objects.create(mentor=lm, course_session=self.course_session)
|
|
||||||
|
|
||||||
participant = create_user("participant")
|
participant = create_user("participant")
|
||||||
add_course_session_user(
|
csu = add_course_session_user(
|
||||||
self.course_session,
|
self.course_session,
|
||||||
participant,
|
participant,
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=lm, participant=csu, role="LEARNING_MENTOR"
|
||||||
|
)
|
||||||
|
|
||||||
trainer = create_user("trainer")
|
trainer = create_user("trainer")
|
||||||
add_course_session_user(
|
add_course_session_user(
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from vbv_lernwelt.course.creators.test_utils import (
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
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.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):
|
class RoleTestCase(TestCase):
|
||||||
|
|
@ -48,9 +48,14 @@ class RoleTestCase(TestCase):
|
||||||
|
|
||||||
def test_has_role_mentor(self):
|
def test_has_role_mentor(self):
|
||||||
# GIVEN
|
# GIVEN
|
||||||
LearningMentor.objects.create(
|
participant = create_user("participant")
|
||||||
mentor=self.user,
|
csu = add_course_session_user(
|
||||||
course_session=self.course_session,
|
self.course_session,
|
||||||
|
participant,
|
||||||
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
|
)
|
||||||
|
AgentParticipantRelation.objects.create(
|
||||||
|
agent=self.user, participant=csu, role="LEARNING_MENTOR"
|
||||||
)
|
)
|
||||||
|
|
||||||
# WHEN
|
# WHEN
|
||||||
|
|
@ -73,14 +78,10 @@ class RoleTestCase(TestCase):
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor = LearningMentor.objects.create(
|
AgentParticipantRelation.objects.create(
|
||||||
mentor=mentor,
|
agent=mentor, participant=member_csu, role="LEARNING_MENTOR"
|
||||||
course_session=course_session,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor.participants.add(member_csu)
|
|
||||||
learning_mentor.save()
|
|
||||||
|
|
||||||
# WHEN
|
# WHEN
|
||||||
is_mentor = is_learning_mentor_for_user(
|
is_mentor = is_learning_mentor_for_user(
|
||||||
mentor=mentor,
|
mentor=mentor,
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ class CreateOrUpdateCourseSessionTestCase(TestCase):
|
||||||
attendance_course.due_date.end.isoformat(), "2023-06-06T13:00:00+00:00"
|
attendance_course.due_date.end.isoformat(), "2023-06-06T13:00:00+00:00"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
f"E64, HKV Aarau, Bahnhofstrasse 460, 5001, Aarau",
|
"E64, HKV Aarau, Bahnhofstrasse 460, 5001, Aarau",
|
||||||
attendance_course.location,
|
attendance_course.location,
|
||||||
)
|
)
|
||||||
self.assertEqual("", attendance_course.trainer)
|
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"
|
attendance_course.due_date.end.isoformat(), "2023-06-06T15:00:00+00:00"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
f"E666, HKV Aarau2, Bahnhofstrasse 460, 5002, Aarau",
|
"E666, HKV Aarau2, Bahnhofstrasse 460, 5002, Aarau",
|
||||||
attendance_course.location,
|
attendance_course.location,
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,29 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.learning_mentor.models import (
|
||||||
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
|
AgentParticipantRelation,
|
||||||
|
MentorInvitation,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(LearningMentor)
|
@admin.register(AgentParticipantRelation)
|
||||||
class LearningMentorAdmin(admin.ModelAdmin):
|
class TrainerParticipantRelationAdmin(admin.ModelAdmin):
|
||||||
def participant_count(self, obj):
|
list_display = ["agent", "participant", "role"]
|
||||||
return obj.participants.count()
|
|
||||||
|
|
||||||
participant_count.short_description = "Participants"
|
search_fields = [
|
||||||
|
"agent__email",
|
||||||
list_display = ["mentor", "course_session", "participant_count"]
|
"agent__first_name",
|
||||||
|
"agent__last_name",
|
||||||
search_fields = ["mentor__email"]
|
"participant__user__email",
|
||||||
|
"participant__user__first_name",
|
||||||
raw_id_fields = [
|
"participant__user__last_name",
|
||||||
"mentor",
|
|
||||||
"course_session",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
raw_id_fields = [
|
||||||
if db_field.name == "participants":
|
"agent",
|
||||||
if request.resolver_match.kwargs.get("object_id"):
|
"participant",
|
||||||
object_id = str(request.resolver_match.kwargs.get("object_id"))
|
# "course_session",
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(MentorInvitation)
|
@admin.register(MentorInvitation)
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,8 @@ class LearningMentorConfig(AppConfig):
|
||||||
name = "vbv_lernwelt.learning_mentor"
|
name = "vbv_lernwelt.learning_mentor"
|
||||||
|
|
||||||
def ready(self):
|
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
|
||||||
|
|
|
||||||
|
|
@ -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")},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -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)]
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django_extensions.db.models import TimeStampedModel
|
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()
|
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):
|
class MentorInvitation(TimeStampedModel):
|
||||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
email = models.EmailField()
|
email = models.EmailField()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from vbv_lernwelt.core.serializers import UserSerializer
|
from vbv_lernwelt.core.serializers import UserShortSerializer
|
||||||
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
|
from vbv_lernwelt.learning_mentor.models import (
|
||||||
|
AgentParticipantRelation,
|
||||||
|
MentorInvitation,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MentorAssignmentCompletionSerializer(serializers.Serializer):
|
class MentorAssignmentCompletionSerializer(serializers.Serializer):
|
||||||
|
|
@ -39,10 +42,24 @@ class InvitationSerializer(serializers.ModelSerializer):
|
||||||
return invitation
|
return invitation
|
||||||
|
|
||||||
|
|
||||||
class MentorSerializer(serializers.ModelSerializer):
|
class AgentParticipantRelationSerializer(serializers.ModelSerializer):
|
||||||
mentor = UserSerializer(read_only=True)
|
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:
|
class Meta:
|
||||||
model = LearningMentor
|
model = AgentParticipantRelation
|
||||||
fields = ["id", "mentor"]
|
fields = [
|
||||||
|
"id",
|
||||||
|
"role",
|
||||||
|
"course_session_id",
|
||||||
|
"agent",
|
||||||
|
"participant_user",
|
||||||
|
]
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
|
||||||
|
|
@ -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.")
|
|
||||||
|
|
@ -11,7 +11,11 @@ from vbv_lernwelt.course.creators.test_utils import (
|
||||||
create_user,
|
create_user,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
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
|
from vbv_lernwelt.notify.email.email_services import EmailTemplate
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -254,13 +258,14 @@ class LearningMentorInvitationTest(APITestCase):
|
||||||
# THEN
|
# THEN
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertFalse(MentorInvitation.objects.filter(id=invitation.id).exists())
|
self.assertFalse(MentorInvitation.objects.filter(id=invitation.id).exists())
|
||||||
self.assertTrue(
|
|
||||||
LearningMentor.objects.filter(
|
relation_qs = AgentParticipantRelation.objects.all()
|
||||||
mentor=invitee,
|
self.assertEqual(relation_qs.count(), 1)
|
||||||
course_session=self.course_session,
|
relation = relation_qs.first()
|
||||||
participants=participant_cs_user,
|
self.assertEqual(relation.agent, invitee)
|
||||||
).exists()
|
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"]
|
user = response.data["user"]
|
||||||
self.assertEqual(user["id"], str(self.participant.id))
|
self.assertEqual(user["id"], str(self.participant.id))
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ from vbv_lernwelt.course.creators.test_utils import (
|
||||||
create_user,
|
create_user,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
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
|
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -71,63 +74,56 @@ class LearningMentorAPITest(APITestCase):
|
||||||
response = self.client.get(self.url)
|
response = self.client.get(self.url)
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
|
print(response.data)
|
||||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
def test_api_no_participants(self) -> None:
|
def test_api_no_participants(self) -> None:
|
||||||
# GIVEN
|
# GIVEN
|
||||||
self.client.force_login(self.mentor)
|
self.client.force_login(self.mentor)
|
||||||
LearningMentor.objects.create(
|
|
||||||
mentor=self.mentor, course_session=self.course_session
|
|
||||||
)
|
|
||||||
|
|
||||||
# WHEN
|
# WHEN
|
||||||
response = self.client.get(self.url)
|
response = self.client.get(self.url)
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
print(response.data)
|
||||||
self.assertEqual(response.data["participants"], [])
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||||
self.assertEqual(response.data["assignments"], [])
|
|
||||||
|
|
||||||
def test_api_participants(self) -> None:
|
def test_api_participants(self) -> None:
|
||||||
# GIVEN
|
# GIVEN
|
||||||
participants = [self.participant_1, self.participant_2, self.participant_3]
|
participants = [self.participant_1, self.participant_2, self.participant_3]
|
||||||
self.client.force_login(self.mentor)
|
|
||||||
mentor = LearningMentor.objects.create(
|
for participant in participants:
|
||||||
mentor=self.mentor, course_session=self.course_session
|
AgentParticipantRelation.objects.create(
|
||||||
)
|
agent=self.mentor,
|
||||||
mentor.participants.set(participants)
|
participant=participant,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
|
)
|
||||||
|
|
||||||
# WHEN
|
# WHEN
|
||||||
|
self.client.force_login(self.mentor)
|
||||||
response = self.client.get(self.url)
|
response = self.client.get(self.url)
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
|
print(response.data)
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
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 = [
|
rel1 = AgentParticipantRelation.objects.get(participant=self.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")
|
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(rel1.participant.user.email, "participant_1@example.com")
|
||||||
response.data["mentor_id"],
|
self.assertEqual(rel1.participant.user.first_name, "Test")
|
||||||
mentor.id,
|
self.assertEqual(rel1.participant.user.last_name, "Participant_1")
|
||||||
)
|
|
||||||
|
|
||||||
def test_api_self_evaluation_feedback(self) -> None:
|
def test_api_self_evaluation_feedback(self) -> None:
|
||||||
# GIVEN
|
# GIVEN
|
||||||
participants = [self.participant_1, self.participant_2, self.participant_3]
|
participants = [self.participant_1, self.participant_2, self.participant_3]
|
||||||
self.client.force_login(self.mentor)
|
|
||||||
|
|
||||||
mentor = LearningMentor.objects.create(
|
for participant in participants:
|
||||||
mentor=self.mentor, course_session=self.course_session
|
AgentParticipantRelation.objects.create(
|
||||||
)
|
agent=self.mentor,
|
||||||
|
participant=participant,
|
||||||
mentor.participants.set(participants)
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
|
)
|
||||||
|
|
||||||
learning_unit = create_learning_unit(
|
learning_unit = create_learning_unit(
|
||||||
circle=self.circle,
|
circle=self.circle,
|
||||||
|
|
@ -154,6 +150,7 @@ class LearningMentorAPITest(APITestCase):
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
# WHEN
|
# WHEN
|
||||||
|
self.client.force_login(self.mentor)
|
||||||
response = self.client.get(self.url)
|
response = self.client.get(self.url)
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
|
|
@ -194,7 +191,6 @@ class LearningMentorAPITest(APITestCase):
|
||||||
|
|
||||||
def test_api_praxis_assignments(self) -> None:
|
def test_api_praxis_assignments(self) -> None:
|
||||||
# GIVEN
|
# GIVEN
|
||||||
self.client.force_login(self.mentor)
|
|
||||||
|
|
||||||
assignment = create_assignment(
|
assignment = create_assignment(
|
||||||
course=self.course, assignment_type=AssignmentType.PRAXIS_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
|
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]
|
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(
|
AssignmentCompletion.objects.create(
|
||||||
assignment_user=self.participant_1.user,
|
assignment_user=self.participant_1.user,
|
||||||
|
|
@ -230,6 +226,7 @@ class LearningMentorAPITest(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# WHEN
|
# WHEN
|
||||||
|
self.client.force_login(self.mentor)
|
||||||
response = self.client.get(self.url)
|
response = self.client.get(self.url)
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
|
|
@ -267,12 +264,12 @@ class LearningMentorAPITest(APITestCase):
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor = LearningMentor.objects.create(
|
db_relation = AgentParticipantRelation.objects.create(
|
||||||
mentor=self.mentor, course_session=self.course_session
|
agent=self.mentor,
|
||||||
|
participant=participant_cs_user,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor.participants.add(participant_cs_user)
|
|
||||||
|
|
||||||
list_url = reverse(
|
list_url = reverse(
|
||||||
"list_user_mentors", kwargs={"course_session_id": self.course_session.id}
|
"list_user_mentors", kwargs={"course_session_id": self.course_session.id}
|
||||||
)
|
)
|
||||||
|
|
@ -281,12 +278,13 @@ class LearningMentorAPITest(APITestCase):
|
||||||
response = self.client.get(list_url)
|
response = self.client.get(list_url)
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
|
print(response.data)
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
mentor = response.data[0]
|
mentor_relation = response.data[0]
|
||||||
self.assertEqual(mentor["id"], learning_mentor.id)
|
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["email"], self.mentor.email)
|
||||||
self.assertEqual(mentor_user["id"], str(self.mentor.id))
|
self.assertEqual(mentor_user["id"], str(self.mentor.id))
|
||||||
|
|
||||||
|
|
@ -300,18 +298,17 @@ class LearningMentorAPITest(APITestCase):
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor = LearningMentor.objects.create(
|
relation = AgentParticipantRelation.objects.create(
|
||||||
mentor=self.mentor, course_session=self.course_session
|
agent=self.mentor,
|
||||||
|
participant=participant_cs_user,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor.participants.add(participant_cs_user)
|
|
||||||
|
|
||||||
remove_url = reverse(
|
remove_url = reverse(
|
||||||
"remove_participant_from_mentor",
|
"delete_agent_participant_relation",
|
||||||
kwargs={
|
kwargs={
|
||||||
"course_session_id": self.course_session.id,
|
"course_session_id": self.course_session.id,
|
||||||
"mentor_id": learning_mentor.id,
|
"relation_id": relation.id,
|
||||||
"participant_user_id": participant_cs_user.user.id,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -320,9 +317,7 @@ class LearningMentorAPITest(APITestCase):
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
self.assertFalse(
|
self.assertEqual(AgentParticipantRelation.objects.count(), 0)
|
||||||
LearningMentor.objects.filter(participants=participant_cs_user).exists()
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_remove_myself_from_mentor(self) -> None:
|
def test_remove_myself_from_mentor(self) -> None:
|
||||||
# GIVEN
|
# GIVEN
|
||||||
|
|
@ -335,18 +330,17 @@ class LearningMentorAPITest(APITestCase):
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
role=CourseSessionUser.Role.MEMBER,
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor = LearningMentor.objects.create(
|
relation = AgentParticipantRelation.objects.create(
|
||||||
mentor=self.mentor, course_session=self.course_session
|
agent=self.mentor,
|
||||||
|
participant=participant_cs_user,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor.participants.add(participant_cs_user)
|
|
||||||
|
|
||||||
remove_url = reverse(
|
remove_url = reverse(
|
||||||
"remove_participant_from_mentor",
|
"delete_agent_participant_relation",
|
||||||
kwargs={
|
kwargs={
|
||||||
"course_session_id": self.course_session.id,
|
"course_session_id": self.course_session.id,
|
||||||
"mentor_id": learning_mentor.id,
|
"relation_id": relation.id,
|
||||||
"participant_user_id": participant_cs_user.user.id,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -355,26 +349,54 @@ class LearningMentorAPITest(APITestCase):
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
self.assertFalse(
|
self.assertEqual(AgentParticipantRelation.objects.count(), 0)
|
||||||
LearningMentor.objects.filter(participants=participant_cs_user).exists()
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_mentor_multiple_courses(self) -> None:
|
def test_mentor_multiple_courses(self) -> None:
|
||||||
# GIVEN
|
# GIVEN
|
||||||
course_a, _ = create_course("Course A")
|
course_a, _ = create_course("Course A")
|
||||||
course_session_a = create_course_session(course=course_a, title="Test 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_b, _ = create_course("Course B")
|
||||||
course_session_b = create_course_session(course=course_b, title="Test 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
|
# WHEN
|
||||||
LearningMentor.objects.create(
|
relation_a = AgentParticipantRelation.objects.create(
|
||||||
mentor=self.mentor, course_session=course_session_a
|
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(
|
self.client.force_login(self.mentor)
|
||||||
mentor=self.mentor, course_session=course_session_b
|
|
||||||
|
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
|
response_b = self.client.get(
|
||||||
self.assertEqual(LearningMentor.objects.count(), 2)
|
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)
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ urlpatterns = [
|
||||||
path("summary", views.mentor_summary, name="mentor_summary"),
|
path("summary", views.mentor_summary, name="mentor_summary"),
|
||||||
path("mentors", views.list_user_mentors, name="list_user_mentors"),
|
path("mentors", views.list_user_mentors, name="list_user_mentors"),
|
||||||
path(
|
path(
|
||||||
"mentors/<int:mentor_id>/remove/<uuid:participant_user_id>",
|
"mentors/<uuid:relation_id>/delete",
|
||||||
views.remove_participant_from_mentor,
|
views.delete_agent_participant_relation,
|
||||||
name="remove_participant_from_mentor",
|
name="delete_agent_participant_relation",
|
||||||
),
|
),
|
||||||
path("invitations", views.list_invitations, name="list_invitations"),
|
path("invitations", views.list_invitations, name="list_invitations"),
|
||||||
path("invitations/create", views.create_invitation, name="create_invitation"),
|
path("invitations/create", views.create_invitation, name="create_invitation"),
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,15 @@ from vbv_lernwelt.learning_mentor.content.praxis_assignment import (
|
||||||
from vbv_lernwelt.learning_mentor.content.self_evaluation_feedback import (
|
from vbv_lernwelt.learning_mentor.content.self_evaluation_feedback import (
|
||||||
get_self_feedback_evaluation,
|
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 (
|
from vbv_lernwelt.learning_mentor.serializers import (
|
||||||
|
AgentParticipantRelationSerializer,
|
||||||
InvitationSerializer,
|
InvitationSerializer,
|
||||||
MentorAssignmentStatusSerializer,
|
MentorAssignmentStatusSerializer,
|
||||||
MentorSerializer,
|
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.learnpath.models import Circle
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
from vbv_lernwelt.notify.email.email_services import EmailTemplate, send_email
|
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):
|
def mentor_summary(request, course_session_id: int):
|
||||||
course_session = CourseSession.objects.get(id=course_session_id)
|
course_session = CourseSession.objects.get(id=course_session_id)
|
||||||
|
|
||||||
mentor = get_object_or_404(
|
participant_qs = AgentParticipantRelation.objects.filter(
|
||||||
LearningMentor, mentor=request.user, course_session=course_session
|
agent=request.user,
|
||||||
|
participant__course_session=course_session,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
participants = mentor.participants.filter(course_session=course_session)
|
if participant_qs.count() == 0:
|
||||||
users = [p.user for p in participants]
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
users = [p.participant.user for p in participant_qs]
|
||||||
|
|
||||||
assignments = []
|
assignments = []
|
||||||
circle_ids = set()
|
circle_ids = set()
|
||||||
|
|
@ -71,8 +79,9 @@ def mentor_summary(request, course_session_id: int):
|
||||||
)
|
)
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
"mentor_id": mentor.id,
|
"participant_relations": AgentParticipantRelationSerializer(
|
||||||
"participants": [UserSerializer(user).data for user in users],
|
participant_qs, many=True
|
||||||
|
).data,
|
||||||
"circles": list(
|
"circles": list(
|
||||||
Circle.objects.filter(id__in=circle_ids).values("id", "title")
|
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
|
CourseSessionUser, user=request.user, course_session=course_session
|
||||||
)
|
)
|
||||||
|
|
||||||
mentors = LearningMentor.objects.filter(
|
mentors = AgentParticipantRelation.objects.filter(
|
||||||
course_session=course_session, participants=course_session_user
|
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"])
|
@api_view(["DELETE"])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
def remove_participant_from_mentor(
|
def delete_agent_participant_relation(
|
||||||
request, course_session_id: int, mentor_id: int, participant_user_id: uuid.UUID
|
request, course_session_id: int, relation_id: uuid.UUID
|
||||||
):
|
):
|
||||||
requester_user = request.user
|
requester_user = request.user
|
||||||
mentor = get_object_or_404(
|
relation = get_object_or_404(AgentParticipantRelation, id=relation_id)
|
||||||
LearningMentor, id=mentor_id, course_session_id=course_session_id
|
|
||||||
)
|
|
||||||
|
|
||||||
is_requester_mentor = requester_user.id == mentor.mentor_id
|
is_requester_mentor = requester_user.id == relation.agent.id
|
||||||
is_requester_participant = mentor.participants.filter(
|
is_requester_participant = requester_user.id == relation.participant.user.id
|
||||||
user=requester_user, course_session_id=course_session_id
|
|
||||||
).exists()
|
|
||||||
|
|
||||||
if not is_requester_mentor and not is_requester_participant:
|
if not is_requester_mentor and not is_requester_participant:
|
||||||
return Response(
|
return Response(
|
||||||
|
|
@ -204,12 +210,7 @@ def remove_participant_from_mentor(
|
||||||
status=status.HTTP_403_FORBIDDEN,
|
status=status.HTTP_403_FORBIDDEN,
|
||||||
)
|
)
|
||||||
|
|
||||||
course_session = get_object_or_404(CourseSession, id=course_session_id)
|
relation.delete()
|
||||||
course_session_user = get_object_or_404(
|
|
||||||
CourseSessionUser, user=participant_user_id, course_session=course_session
|
|
||||||
)
|
|
||||||
|
|
||||||
mentor.participants.remove(course_session_user)
|
|
||||||
|
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
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,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
mentor, _ = LearningMentor.objects.get_or_create(
|
AgentParticipantRelation.objects.get_or_create(
|
||||||
mentor=request.user, course_session=course_session
|
agent=request.user,
|
||||||
|
participant=invitation.participant,
|
||||||
|
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
mentor.participants.add(invitation.participant)
|
|
||||||
invitation.delete()
|
invitation.delete()
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import imghdr
|
||||||
from wsgiref.util import FileWrapper
|
from wsgiref.util import FileWrapper
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpResponse, StreamingHttpResponse
|
from django.http import HttpResponse, StreamingHttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
@ -21,6 +22,16 @@ def user_image(request, image_id):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rendition = image.get_rendition(filter_spec)
|
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:
|
except SourceImageIOError:
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
"Source image file not found", content_type="text/plain", status=410
|
"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",
|
content_type="text/plain",
|
||||||
status=400,
|
status=400,
|
||||||
)
|
)
|
||||||
|
except Exception:
|
||||||
rendition.file.open("rb")
|
if settings.APP_ENVIRONMENT.startswith("local"):
|
||||||
image_format = imghdr.what(rendition.file)
|
# do not spam the console
|
||||||
|
logger.warning("Error while serving image")
|
||||||
return StreamingHttpResponse(
|
else:
|
||||||
FileWrapper(rendition.file),
|
logger.exception(
|
||||||
content_type=f"image/{image_format}" if image_format else "binary/octet-stream",
|
"Error while serving image", exc_info=True, label="s3_mediafiles"
|
||||||
)
|
)
|
||||||
|
return HttpResponse(
|
||||||
|
"Error while serving image", content_type="text/plain", status=400
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ from vbv_lernwelt.course.creators.test_utils import (
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSessionUser
|
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSessionUser
|
||||||
from vbv_lernwelt.course.services import mark_course_completion
|
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 (
|
from vbv_lernwelt.self_evaluation_feedback.models import (
|
||||||
CourseCompletionFeedback,
|
CourseCompletionFeedback,
|
||||||
SelfEvaluationFeedback,
|
SelfEvaluationFeedback,
|
||||||
|
|
@ -51,13 +51,10 @@ class SelfEvaluationFeedbackAPI(APITestCase):
|
||||||
title="Test Circle", course_page=self.course_page
|
title="Test Circle", course_page=self.course_page
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor = LearningMentor.objects.create(
|
AgentParticipantRelation.objects.create(
|
||||||
mentor=self.mentor,
|
agent=self.mentor, participant=member_csu, role="LEARNING_MENTOR"
|
||||||
course_session=self.course_session,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
learning_mentor.participants.add(member_csu)
|
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"vbv_lernwelt.notify.services.NotificationService.send_self_evaluation_feedback_request_feedback_notification"
|
"vbv_lernwelt.notify.services.NotificationService.send_self_evaluation_feedback_request_feedback_notification"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.core.serializers import UserSerializer
|
from vbv_lernwelt.core.serializers import UserSerializer
|
||||||
from vbv_lernwelt.course.models import CourseCompletion, CourseSession
|
from vbv_lernwelt.course.models import CourseCompletion, CourseSession
|
||||||
from vbv_lernwelt.iam.permissions import can_view_course_completions
|
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.learnpath.models import Circle, LearningUnit
|
||||||
from vbv_lernwelt.notify.services import NotificationService
|
from vbv_lernwelt.notify.services import NotificationService
|
||||||
from vbv_lernwelt.self_evaluation_feedback.models import (
|
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)
|
learning_unit = get_object_or_404(LearningUnit, id=learning_unit_id)
|
||||||
feedback_provider_user = get_object_or_404(User, id=feedback_provider_user_id)
|
feedback_provider_user = get_object_or_404(User, id=feedback_provider_user_id)
|
||||||
|
|
||||||
if not LearningMentor.objects.filter(
|
if not AgentParticipantRelation.objects.filter(
|
||||||
course_session__course=learning_unit.get_course(),
|
agent=feedback_provider_user,
|
||||||
mentor=feedback_provider_user,
|
participant__user=request.user,
|
||||||
participants__user=request.user,
|
|
||||||
).exists():
|
).exists():
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -5,7 +5,10 @@ from keycloak.exceptions import KeycloakDeleteError, KeycloakPostError
|
||||||
|
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
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.models import SsoSyncError, SsoUser
|
||||||
from vbv_lernwelt.sso.role_sync.services import (
|
from vbv_lernwelt.sso.role_sync.services import (
|
||||||
create_and_update_user,
|
create_and_update_user,
|
||||||
|
|
@ -20,7 +23,7 @@ def create_sso_user_from_admin(user: User, request):
|
||||||
create_and_update_user(user) # noqa
|
create_and_update_user(user) # noqa
|
||||||
user.save()
|
user.save()
|
||||||
messages.add_message(
|
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:
|
except KeycloakPostError as e:
|
||||||
messages.add_message(
|
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):
|
def sync_sso_roles_from_admin(user: User, request):
|
||||||
course_roles = [
|
course_roles = {
|
||||||
(csu.course_session.course.slug, csu.role)
|
(csu.course_session.course.slug, csu.role)
|
||||||
for csu in CourseSessionUser.objects.filter(user=user)
|
for csu in CourseSessionUser.objects.filter(user=user)
|
||||||
]
|
}
|
||||||
|
|
||||||
course_roles += [
|
course_roles += {
|
||||||
(lm.course_session.course.slug, "LEARNING_MENTOR")
|
(relation.participant.course_session.course.slug, "LEARNING_MENTOR")
|
||||||
for lm in LearningMentor.objects.filter(mentor=user)
|
for relation in AgentParticipantRelation.objects.filter(
|
||||||
]
|
agent=user, role=AgentParticipantRoleType.LEARNING_MENTOR.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
for csg in CourseSessionGroup.objects.filter(supervisor=user):
|
for csg in CourseSessionGroup.objects.filter(supervisor=user):
|
||||||
for course_session in csg.course_session.all():
|
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:
|
try:
|
||||||
sync_roles_for_user(user, course_roles)
|
sync_roles_for_user(user, course_roles)
|
||||||
messages.add_message(
|
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:
|
except KeycloakDeleteError as e:
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Set, Tuple
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from django.conf import settings
|
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__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
CourseRolesType = List[Tuple[str, str]]
|
CourseRolesType = Set[Tuple[str, str]]
|
||||||
KeyCloakRolesType = List[Dict[str, str]]
|
KeyCloakRolesType = List[Dict[str, str]]
|
||||||
|
|
||||||
keycloak_admin = None # Needed for pytest
|
keycloak_admin = None # Needed for pytest
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@ from keycloak.exceptions import KeycloakDeleteError, KeycloakError, KeycloakPost
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
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 (
|
from vbv_lernwelt.sso.role_sync.services import (
|
||||||
add_roles_to_user,
|
add_roles_to_user,
|
||||||
remove_roles_from_user,
|
remove_roles_from_user,
|
||||||
|
|
@ -78,21 +81,39 @@ def update_sso_roles_in_csg(sender, instance, action, reverse, model, pk_set, **
|
||||||
|
|
||||||
|
|
||||||
# LearningMentor
|
# LearningMentor
|
||||||
@receiver(post_delete, sender=LearningMentor, dispatch_uid="delete_sso_roles_in_lm")
|
@receiver(
|
||||||
def remove_sso_roles_in_lm(sender, instance: LearningMentor, **kwargs):
|
post_delete, sender=AgentParticipantRelation, dispatch_uid="delete_sso_roles_in_lm"
|
||||||
if not LearningMentor.objects.filter(
|
)
|
||||||
mentor=instance.mentor, course_session__course=instance.course_session.course
|
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():
|
).exists():
|
||||||
_remove_sso_role(
|
_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")
|
@receiver(
|
||||||
def update_sso_roles_in_lm(sender, instance: LearningMentor, **kwargs):
|
pre_save, sender=AgentParticipantRelation, dispatch_uid="update_sso_roles_in_lm"
|
||||||
if not instance.pk:
|
)
|
||||||
|
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(
|
_add_sso_role(
|
||||||
instance.mentor, instance.course_session.course.slug, "LEARNING_MENTOR"
|
instance.agent,
|
||||||
|
instance.participant.course_session.course.slug,
|
||||||
|
"LEARNING_MENTOR",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ from vbv_lernwelt.course.creators.test_utils import (
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.models import Course, CourseSessionUser
|
from vbv_lernwelt.course.models import Course, CourseSessionUser
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
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
|
from vbv_lernwelt.sso.signals import update_sso_roles_in_cs
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -217,10 +217,16 @@ class LearningMentorTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.course, self.course_page = create_course("Test Course")
|
self.course, self.course_page = create_course("Test Course")
|
||||||
self.course_session = create_course_session(course=self.course, title="Test VV")
|
self.course_session = create_course_session(course=self.course, title="Test VV")
|
||||||
|
|
||||||
self.user = create_user("mentor")
|
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")
|
@patch("vbv_lernwelt.sso.signals.remove_roles_from_user")
|
||||||
|
|
@ -229,7 +235,7 @@ class LearningMentorTests(TestCase):
|
||||||
):
|
):
|
||||||
mock_remove_roles_from_user.return_value = None
|
mock_remove_roles_from_user.return_value = None
|
||||||
|
|
||||||
self.mentor.delete()
|
self.relation.delete()
|
||||||
|
|
||||||
self.assertEqual(mock_remove_roles_from_user.call_count, 1)
|
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")
|
@patch("vbv_lernwelt.sso.signals.add_roles_to_user")
|
||||||
def test_add_roles_for_learning_mentor_on_create(self, mock_add_roles_from_user):
|
def test_add_roles_for_learning_mentor_on_create(self, mock_add_roles_from_user):
|
||||||
mock_add_roles_from_user.return_value = None
|
mock_add_roles_from_user.return_value = None
|
||||||
self.mentor.delete()
|
self.relation.delete()
|
||||||
|
|
||||||
LearningMentor.objects.create(
|
AgentParticipantRelation.objects.create(
|
||||||
mentor=self.user, course_session=self.course_session
|
agent=self.user, participant=self.csu, role="LEARNING_MENTOR"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(mock_add_roles_from_user.call_count, 1)
|
self.assertEqual(mock_add_roles_from_user.call_count, 1)
|
||||||
|
|
@ -263,5 +269,8 @@ class LearningMentorTests(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_add_roles_from_user.reset_mock()
|
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)
|
self.assertEqual(mock_add_roles_from_user.call_count, 0)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue