Merge branch 'feature/dashboard-persons' into feature/dashboard
This commit is contained in:
commit
5ba319e524
|
|
@ -7,6 +7,8 @@ import {
|
|||
circleFlatLearningContents,
|
||||
circleFlatLearningUnits,
|
||||
} from "@/services/circle";
|
||||
import type { DashboardPersonType } from "@/services/dashboard";
|
||||
import { fetchDashboardPersons } from "@/services/dashboard";
|
||||
import { presignUpload, uploadFile } from "@/services/files";
|
||||
import { useCompletionStore } from "@/stores/completion";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
|
|
@ -487,3 +489,24 @@ export function useMyLearningMentors() {
|
|||
loading,
|
||||
};
|
||||
}
|
||||
|
||||
export function useDashboardPersons() {
|
||||
const dashboardPersons = ref<DashboardPersonType[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const fetchData = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
dashboardPersons.value = await fetchDashboardPersons();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(fetchData);
|
||||
|
||||
return {
|
||||
dashboardPersons,
|
||||
loading,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ export const itFetch = (url: RequestInfo, options: RequestInit) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const itPost = (url: RequestInfo, data: unknown, options: RequestInit = {}) => {
|
||||
export const itPost = <T>(
|
||||
url: RequestInfo,
|
||||
data: unknown,
|
||||
options: RequestInit = {}
|
||||
) => {
|
||||
options = Object.assign({}, options);
|
||||
|
||||
const headers = Object.assign(
|
||||
|
|
@ -56,11 +60,11 @@ export const itPost = (url: RequestInfo, data: unknown, options: RequestInit = {
|
|||
return response.json().catch(() => {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
});
|
||||
}) as Promise<T>;
|
||||
};
|
||||
|
||||
export const itGet = (url: RequestInfo) => {
|
||||
return itPost(url, {}, { method: "GET" });
|
||||
export const itGet = <T>(url: RequestInfo) => {
|
||||
return itPost<T>(url, {}, { method: "GET" });
|
||||
};
|
||||
|
||||
export const itDelete = (url: RequestInfo) => {
|
||||
|
|
@ -81,17 +85,17 @@ export function bustItGetCache(key?: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export const itGetCached = (
|
||||
export const itGetCached = <T>(
|
||||
url: RequestInfo,
|
||||
options = {
|
||||
reload: false,
|
||||
}
|
||||
): Promise<any> => {
|
||||
): Promise<T> => {
|
||||
if (!itGetPromiseCache.has(url.toString()) || options.reload) {
|
||||
itGetPromiseCache.set(url.toString(), itGet(url));
|
||||
itGetPromiseCache.set(url.toString(), itGet<T>(url));
|
||||
}
|
||||
|
||||
return itGetPromiseCache.get(url.toString()) as Promise<any>;
|
||||
return itGetPromiseCache.get(url.toString()) as Promise<T>;
|
||||
};
|
||||
|
||||
export const useCSRFFetch = createFetch({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
<script setup lang="ts">
|
||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||
import log from "loglevel";
|
||||
import { useDashboardPersons } from "@/composables";
|
||||
|
||||
log.debug("DashboardPersonsPage created");
|
||||
|
||||
const { loading: loadingPersons, dashboardPersons } = useDashboardPersons();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="loadingPersons" class="m-8 flex justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
<div v-else class="bg-gray-200">
|
||||
<div class="container-large">
|
||||
<div class="bg-white px-4 py-2">
|
||||
<div
|
||||
v-for="person in dashboardPersons"
|
||||
:key="person.user_id"
|
||||
data-cy="person"
|
||||
class="flex flex-col justify-between gap-4 border-b p-2 last:border-b-0 md:flex-row md:items-center md:justify-between md:gap-16"
|
||||
>
|
||||
<div class="w-full flex-auto md:w-1/3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<img
|
||||
:alt="person.last_name"
|
||||
class="h-11 w-11 rounded-full"
|
||||
:src="'/static/avatars/myvbv-default-avatar.png'"
|
||||
/>
|
||||
<div>
|
||||
<div class="text-bold">
|
||||
{{ person.first_name }}
|
||||
{{ person.last_name }}
|
||||
</div>
|
||||
<div class="text-gray-900">{{ person.email }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex-auto items-start md:w-2/3">
|
||||
<div
|
||||
v-for="cs in person.course_sessions"
|
||||
:key="cs.id"
|
||||
class="w-full border-b pb-2 pt-2 first:pt-0 last:border-b-0 last:pb-0"
|
||||
>
|
||||
<div class="flex flex-col md:flex-row md:items-center">
|
||||
<div class="md:w-1/2">
|
||||
<div class="text-gray-900">{{ cs.course_title }}</div>
|
||||
<div v-if="cs.is_uk">{{ cs.session_title }}</div>
|
||||
</div>
|
||||
<div class="md:w-1/4">
|
||||
<div>{{ cs.user_role }}</div>
|
||||
<div>my role: {{ cs.my_role }}</div>
|
||||
</div>
|
||||
<div class="md:w-1/4 md:text-right">
|
||||
<div
|
||||
v-if="
|
||||
(['SUPERVISOR', 'EXPERT'].includes(cs.my_role) &&
|
||||
cs.user_role === 'MEMBER') ||
|
||||
(cs.my_role === 'LEARNING_MENTOR' &&
|
||||
cs.user_role === 'LEARNING_MENTEE')
|
||||
"
|
||||
>
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'profileLearningPath',
|
||||
params: {
|
||||
userId: person.user_id,
|
||||
courseSlug: cs.course_slug,
|
||||
},
|
||||
query: { courseSessionId: cs.id },
|
||||
}"
|
||||
class="link w-full lg:text-right"
|
||||
>
|
||||
{{ $t("a.Profil anzeigen") }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <button-->
|
||||
<!-- class="underline"-->
|
||||
<!-- data-cy="lm-my-mentor-remove"-->
|
||||
<!-- @click="removeMyMentor(learningMentor.id)"-->
|
||||
<!-- >-->
|
||||
<!-- {{ $t("a.Entfernen") }}-->
|
||||
<!-- </button>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -91,7 +91,7 @@ export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalize
|
|||
return {
|
||||
path: to.path,
|
||||
query: restOfQuery,
|
||||
replace: true,
|
||||
// replace: true,
|
||||
};
|
||||
} else {
|
||||
// courseSessionId is invalid for current user -> redirect to home
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@ const router = createRouter({
|
|||
name: "home",
|
||||
component: DashboardPage,
|
||||
},
|
||||
{
|
||||
path: "/dashboard/persons",
|
||||
component: () => import("@/pages/dashboard/DashboardPersonsPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "/course/:courseSlug/media",
|
||||
props: true,
|
||||
|
|
|
|||
|
|
@ -6,12 +6,40 @@ import {
|
|||
DASHBOARD_COURSE_STATISTICS,
|
||||
} from "@/graphql/queries";
|
||||
|
||||
import { itGetCached } from "@/fetchHelpers";
|
||||
import type {
|
||||
CourseProgressType,
|
||||
CourseStatisticsType,
|
||||
DashboardConfigType,
|
||||
} from "@/gql/graphql";
|
||||
|
||||
export type DashboardPersonRoleType =
|
||||
| "SUPERVISOR"
|
||||
| "EXPERT"
|
||||
| "MEMBER"
|
||||
| "LEARNING_MENTOR"
|
||||
| "LEARNING_MENTEE";
|
||||
|
||||
export type DashboardPersonCourseSessionType = {
|
||||
id: number;
|
||||
session_title: string;
|
||||
course_id: number;
|
||||
course_title: string;
|
||||
course_slug: string;
|
||||
user_role: DashboardPersonRoleType;
|
||||
my_role: DashboardPersonRoleType;
|
||||
is_uk: boolean;
|
||||
is_vv: boolean;
|
||||
};
|
||||
|
||||
export type DashboardPersonType = {
|
||||
user_id: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email: string;
|
||||
course_sessions: DashboardPersonCourseSessionType[];
|
||||
};
|
||||
|
||||
export const fetchStatisticData = async (
|
||||
courseId: string
|
||||
): Promise<CourseStatisticsType | null> => {
|
||||
|
|
@ -48,6 +76,7 @@ export const fetchProgressData = async (
|
|||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchDashboardConfig = async (): Promise<DashboardConfigType[] | null> => {
|
||||
try {
|
||||
const res = await graphqlClient.query(DASHBOARD_CONFIG, {});
|
||||
|
|
@ -80,3 +109,7 @@ export const fetchCourseData = async (
|
|||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export async function fetchDashboardPersons() {
|
||||
return await itGetCached<DashboardPersonType[]>("/api/dashboard/persons/");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ from vbv_lernwelt.course.views import (
|
|||
request_course_completion_for_user,
|
||||
)
|
||||
from vbv_lernwelt.course_session.views import get_course_session_documents
|
||||
from vbv_lernwelt.dashboard.views import get_dashboard_persons
|
||||
from vbv_lernwelt.edoniq_test.views import (
|
||||
export_students,
|
||||
export_students_and_trainers,
|
||||
|
|
@ -115,6 +116,9 @@ urlpatterns = [
|
|||
# notify
|
||||
re_path(r"api/notify/email_notification_settings/$", email_notification_settings,
|
||||
name='email_notification_settings'),
|
||||
|
||||
# dashboard
|
||||
path(r"api/dashboard/persons/", get_dashboard_persons, name="get_dashboard_persons"),
|
||||
|
||||
# course
|
||||
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ from wagtail.blocks.list_block import ListBlock, ListValue
|
|||
from wagtail.rich_text import RichText
|
||||
|
||||
|
||||
def create_uk_fahrzeug_casework(course_id=COURSE_UK, competence_certificate=None):
|
||||
def create_uk_fahrzeug_casework(
|
||||
course_id=COURSE_UK, competence_certificate=None, with_documents=False
|
||||
):
|
||||
assignment_list_page = (
|
||||
CoursePage.objects.get(course_id=course_id)
|
||||
.get_children()
|
||||
|
|
@ -40,7 +42,6 @@ def create_uk_fahrzeug_casework(course_id=COURSE_UK, competence_certificate=None
|
|||
needs_expert_evaluation=True,
|
||||
competence_certificate=competence_certificate,
|
||||
effort_required="ca. 5 Stunden",
|
||||
solution_sample=ContentDocument.objects.get(title="Musterlösung Fahrzeug"),
|
||||
intro_text=replace_whitespace(
|
||||
"""
|
||||
<h3>Ausgangslage</h3>
|
||||
|
|
@ -70,6 +71,11 @@ def create_uk_fahrzeug_casework(course_id=COURSE_UK, competence_certificate=None
|
|||
evaluation_document_url="/static/media/assignments/UK_03_09_NACH_KN_Beurteilungsraster.pdf",
|
||||
evaluation_description="Diese geleitete Fallarbeit wird auf Grund des folgenden Beurteilungsintrument bewertet.",
|
||||
)
|
||||
if with_documents:
|
||||
assignment.solution_sample = ContentDocument.objects.get(
|
||||
title="Musterlösung Fahrzeug"
|
||||
)
|
||||
assignment.save()
|
||||
|
||||
assignment.evaluation_tasks = []
|
||||
assignment.evaluation_tasks.append(
|
||||
|
|
@ -3591,7 +3597,7 @@ def create_uk_reflection(course_id=COURSE_UK):
|
|||
assignment = AssignmentFactory(
|
||||
parent=assignment_list_page,
|
||||
assignment_type=AssignmentType.REFLECTION.name,
|
||||
title=f"Reflexion",
|
||||
title="Reflexion",
|
||||
effort_required="ca. 1 Stunde",
|
||||
intro_text=replace_whitespace(
|
||||
"""
|
||||
|
|
@ -3747,7 +3753,7 @@ def create_uk_fr_reflection(course_id=COURSE_UK_FR, circle_title="Véhicule"):
|
|||
assignment = AssignmentFactory(
|
||||
parent=assignment_list_page,
|
||||
assignment_type=AssignmentType.REFLECTION.name,
|
||||
title=f"Reflexion",
|
||||
title="Reflexion",
|
||||
effort_required="",
|
||||
intro_text=replace_whitespace(
|
||||
"""
|
||||
|
|
@ -3900,7 +3906,7 @@ def create_uk_it_reflection(course_id=COURSE_UK_FR, circle_title="Véhicule"):
|
|||
assignment = AssignmentFactory(
|
||||
parent=assignment_list_page,
|
||||
assignment_type=AssignmentType.REFLECTION.name,
|
||||
title=f"Riflessione",
|
||||
title="Riflessione",
|
||||
effort_required="",
|
||||
intro_text=replace_whitespace(
|
||||
"""
|
||||
|
|
@ -4053,7 +4059,7 @@ def create_vv_reflection(
|
|||
assignment = AssignmentFactory(
|
||||
parent=assignment_list_page,
|
||||
assignment_type=AssignmentType.REFLECTION.name,
|
||||
title=f"Reflexion",
|
||||
title="Reflexion",
|
||||
effort_required="ca. 1 Stunde",
|
||||
intro_text=replace_whitespace(
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ class CourseConfigurationAdmin(admin.ModelAdmin):
|
|||
"enable_circle_documents",
|
||||
"enable_learning_mentor",
|
||||
"enable_competence_certificates",
|
||||
"is_vv",
|
||||
"is_uk",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -97,12 +97,15 @@ from vbv_lernwelt.media_library.tests.media_library_factories import (
|
|||
)
|
||||
|
||||
|
||||
def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
||||
def create_test_course(
|
||||
include_uk=True, include_vv=True, with_sessions=False, with_documents=False
|
||||
):
|
||||
# create_locales_for_wagtail()
|
||||
create_default_collections()
|
||||
create_default_content_documents()
|
||||
if UserImage.objects.count() == 0 and ContentImage.objects.count() == 0:
|
||||
create_default_images()
|
||||
if with_documents:
|
||||
create_default_content_documents()
|
||||
if UserImage.objects.count() == 0 and ContentImage.objects.count() == 0:
|
||||
create_default_images()
|
||||
|
||||
course: Course = create_test_course_with_categories()
|
||||
course.configuration.enable_learning_mentor = False
|
||||
|
|
@ -118,7 +121,9 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
|||
|
||||
if include_uk:
|
||||
create_uk_fahrzeug_casework(
|
||||
course_id=COURSE_TEST_ID, competence_certificate=competence_certificate
|
||||
course_id=COURSE_TEST_ID,
|
||||
competence_certificate=competence_certificate,
|
||||
with_documents=with_documents,
|
||||
)
|
||||
create_uk_fahrzeug_prep_assignment(course_id=COURSE_TEST_ID)
|
||||
create_uk_condition_acceptance(course_id=COURSE_TEST_ID)
|
||||
|
|
@ -132,7 +137,9 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
|||
if include_vv:
|
||||
create_vv_gewinnen_casework(course_id=COURSE_TEST_ID)
|
||||
|
||||
create_test_learning_path(include_uk=include_uk, include_vv=include_vv)
|
||||
create_test_learning_path(
|
||||
include_uk=include_uk, include_vv=include_vv, with_documents=with_documents
|
||||
)
|
||||
create_test_media_library()
|
||||
|
||||
if with_sessions:
|
||||
|
|
@ -187,7 +194,7 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
|||
csa = CourseSessionAssignment.objects.create(
|
||||
course_session=cs_bern,
|
||||
learning_content=LearningContentAssignment.objects.get(
|
||||
slug=f"test-lehrgang-lp-circle-fahrzeug-lc-fahrzeug-mein-erstes-auto"
|
||||
slug="test-lehrgang-lp-circle-fahrzeug-lc-fahrzeug-mein-erstes-auto"
|
||||
),
|
||||
)
|
||||
next_monday = datetime.now() + relativedelta(weekday=MO(2))
|
||||
|
|
@ -215,7 +222,7 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
|||
_csa = CourseSessionAssignment.objects.create(
|
||||
course_session=cs_bern,
|
||||
learning_content=LearningContentAssignment.objects.get(
|
||||
slug=f"test-lehrgang-lp-circle-reisen-lc-mein-kundenstamm"
|
||||
slug="test-lehrgang-lp-circle-reisen-lc-mein-kundenstamm"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -418,20 +425,22 @@ def create_test_course_with_categories(apps=None, schema_editor=None):
|
|||
return course
|
||||
|
||||
|
||||
def create_test_learning_path(include_uk=True, include_vv=True):
|
||||
def create_test_learning_path(include_uk=True, include_vv=True, with_documents=False):
|
||||
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
|
||||
lp = LearningPathFactory(title="Test Lernpfad", parent=course_page)
|
||||
|
||||
if include_uk:
|
||||
TopicFactory(title="Circle ÜK", is_visible=False, parent=lp)
|
||||
create_test_uk_circle_fahrzeug(lp, title="Fahrzeug")
|
||||
create_test_uk_circle_fahrzeug(
|
||||
lp, title="Fahrzeug", with_documents=with_documents
|
||||
)
|
||||
|
||||
if include_vv:
|
||||
TopicFactory(title="Circle VV", is_visible=False, parent=lp)
|
||||
create_test_circle_reisen(lp)
|
||||
|
||||
|
||||
def create_test_uk_circle_fahrzeug(lp, title="Fahrzeug"):
|
||||
def create_test_uk_circle_fahrzeug(lp, title="Fahrzeug", with_documents=False):
|
||||
circle = CircleFactory(
|
||||
title=title,
|
||||
parent=lp,
|
||||
|
|
@ -470,21 +479,25 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
|
|||
),
|
||||
content_url=f"/course/{lp.get_course().slug}/media/handlungsfelder/{slugify(title)}",
|
||||
)
|
||||
LearningContentAssignmentFactory(
|
||||
title="Redlichkeitserklärung",
|
||||
parent=circle,
|
||||
content_assignment=Assignment.objects.get(
|
||||
slug__startswith="test-lehrgang-assignment-redlichkeits"
|
||||
(
|
||||
LearningContentAssignmentFactory(
|
||||
title="Redlichkeitserklärung",
|
||||
parent=circle,
|
||||
content_assignment=Assignment.objects.get(
|
||||
slug__startswith="test-lehrgang-assignment-redlichkeits"
|
||||
),
|
||||
),
|
||||
),
|
||||
LearningContentAssignmentFactory(
|
||||
title="Fahrzeug - Mein erstes Auto",
|
||||
assignment_type="PREP_ASSIGNMENT",
|
||||
parent=circle,
|
||||
content_assignment=Assignment.objects.get(
|
||||
slug__startswith="test-lehrgang-assignment-fahrzeug-mein-erstes-auto"
|
||||
)
|
||||
(
|
||||
LearningContentAssignmentFactory(
|
||||
title="Fahrzeug - Mein erstes Auto",
|
||||
assignment_type="PREP_ASSIGNMENT",
|
||||
parent=circle,
|
||||
content_assignment=Assignment.objects.get(
|
||||
slug__startswith="test-lehrgang-assignment-fahrzeug-mein-erstes-auto"
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
PerformanceCriteriaFactory(
|
||||
parent=ActionCompetence.objects.get(competence_id="X1"),
|
||||
|
|
@ -531,21 +544,24 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
|
|||
|
||||
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
||||
LearningUnitFactory(title="Transfer", parent=circle)
|
||||
LearningContentAssignmentFactory(
|
||||
title="Reflexion",
|
||||
assignment_type="REFLECTION",
|
||||
parent=circle,
|
||||
content_assignment=Assignment.objects.get(
|
||||
slug__startswith=f"test-lehrgang-assignment-reflexion"
|
||||
(
|
||||
LearningContentAssignmentFactory(
|
||||
title="Reflexion",
|
||||
assignment_type="REFLECTION",
|
||||
parent=circle,
|
||||
content_assignment=Assignment.objects.get(
|
||||
slug__startswith="test-lehrgang-assignment-reflexion"
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assignment = Assignment.objects.get(
|
||||
slug__startswith="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs"
|
||||
)
|
||||
assignment.solution_sample = ContentDocument.objects.get(
|
||||
title="Musterlösung Fahrzeug"
|
||||
)
|
||||
if with_documents:
|
||||
assignment.solution_sample = ContentDocument.objects.get(
|
||||
title="Musterlösung Fahrzeug"
|
||||
)
|
||||
assignment.save()
|
||||
LearningContentAssignmentFactory(
|
||||
title="Überprüfen einer Motorfahrzeug-Versicherungspolice",
|
||||
|
|
@ -574,9 +590,9 @@ def create_test_circle_reisen(lp):
|
|||
description="Willkommen im Lehrgang Versicherungsvermitler VBV",
|
||||
)
|
||||
LearningContentMediaLibraryFactory(
|
||||
title=f"Mediathek Reisen",
|
||||
title="Mediathek Reisen",
|
||||
parent=circle,
|
||||
content_url=f"/course/test-lehrgang/media/handlungsfelder/reisen",
|
||||
content_url="/course/test-lehrgang/media/handlungsfelder/reisen",
|
||||
)
|
||||
|
||||
LearningSequenceFactory(title="Analyse", parent=circle)
|
||||
|
|
@ -596,25 +612,27 @@ def create_test_circle_reisen(lp):
|
|||
content_url="https://s3.eu-central-1.amazonaws.com/myvbv-wbt.iterativ.ch/emma-und-ayla-campen-durch-amerika-analyse-xapi-FZoZOP9y/index.html",
|
||||
)
|
||||
|
||||
LearningContentAssignmentFactory(
|
||||
title="Mein Kundenstamm",
|
||||
assignment_type="PRAXIS_ASSIGNMENT",
|
||||
parent=circle,
|
||||
content_assignment=Assignment.objects.get(
|
||||
slug__startswith="test-lehrgang-assignment-mein-kundenstamm"
|
||||
(
|
||||
LearningContentAssignmentFactory(
|
||||
title="Mein Kundenstamm",
|
||||
assignment_type="PRAXIS_ASSIGNMENT",
|
||||
parent=circle,
|
||||
content_assignment=Assignment.objects.get(
|
||||
slug__startswith="test-lehrgang-assignment-mein-kundenstamm"
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
PerformanceCriteriaFactory(
|
||||
parent=ActionCompetence.objects.get(competence_id="Y1"),
|
||||
competence_id=f"Y1.1",
|
||||
title=f"Ich bin fähig zu Reisen eine Gesprächsführung zu machen",
|
||||
competence_id="Y1.1",
|
||||
title="Ich bin fähig zu Reisen eine Gesprächsführung zu machen",
|
||||
learning_unit=lu,
|
||||
)
|
||||
PerformanceCriteriaFactory(
|
||||
parent=ActionCompetence.objects.get(competence_id="Y2"),
|
||||
competence_id=f"Y2.1",
|
||||
title=f"Ich bin fähig zu Reisen eine Analyse zu machen",
|
||||
competence_id="Y2.1",
|
||||
title="Ich bin fähig zu Reisen eine Analyse zu machen",
|
||||
learning_unit=lu,
|
||||
)
|
||||
|
||||
|
|
@ -627,7 +645,7 @@ def create_test_circle_reisen(lp):
|
|||
parent=parent,
|
||||
)
|
||||
LearningContentPlaceholderFactory(
|
||||
title=f"Fachcheck Reisen",
|
||||
title="Fachcheck Reisen",
|
||||
parent=parent,
|
||||
)
|
||||
LearningContentKnowledgeAssessmentFactory(
|
||||
|
|
@ -757,9 +775,9 @@ def create_test_media_library():
|
|||
die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.
|
||||
""".strip(),
|
||||
body=RichText(
|
||||
f"<h2>Lernmedien</h2>"
|
||||
f"<h3>Allgemeines</h3>"
|
||||
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
|
||||
"<h2>Lernmedien</h2>"
|
||||
"<h3>Allgemeines</h3>"
|
||||
"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -778,9 +796,9 @@ def create_test_media_library():
|
|||
title=cat,
|
||||
parent=media_lib_allgemeines,
|
||||
body=RichText(
|
||||
f"<h2>Lernmedien</h2>"
|
||||
f"<h3>Allgemeines</h3>"
|
||||
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
|
||||
"<h2>Lernmedien</h2>"
|
||||
"<h3>Allgemeines</h3>"
|
||||
"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ def command(course):
|
|||
create_course_uk_it()
|
||||
|
||||
if COURSE_TEST_ID in course:
|
||||
create_test_course(with_sessions=True)
|
||||
create_test_course(with_sessions=True, with_documents=True)
|
||||
|
||||
if COURSE_UK_TRAINING in course:
|
||||
create_course_training_de()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
# Generated by Django 3.2.20 on 2024-04-03 09:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
TEST_COURSE_ID = -1
|
||||
|
||||
UK_COURSE_IDS = [
|
||||
-3, # uk-de
|
||||
-6, # uk-training-de
|
||||
-5, # uk-fr
|
||||
-7, # uk-training-fr
|
||||
-8, # uk-it
|
||||
-9, # uk-training-it
|
||||
]
|
||||
|
||||
|
||||
VV_COURSE_IDS = [
|
||||
-4, # vv-de
|
||||
-10, # vv-fr
|
||||
-11, # vv-it
|
||||
-12, # vv-prüfung
|
||||
]
|
||||
|
||||
|
||||
def forward_migration(apps, schema_editor):
|
||||
Course = apps.get_model("course", "Course")
|
||||
CourseConfiguration = apps.get_model("course", "CourseConfiguration")
|
||||
|
||||
for course in Course.objects.all():
|
||||
config, created = CourseConfiguration.objects.get_or_create(
|
||||
course=course,
|
||||
)
|
||||
|
||||
# -> disable unnecessary features
|
||||
if course.id in UK_COURSE_IDS:
|
||||
config.is_uk = True
|
||||
elif course.id in VV_COURSE_IDS:
|
||||
config.is_vv = True
|
||||
|
||||
config.save()
|
||||
|
||||
|
||||
def backward_migration(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("course", "0007_auto_20240226_1553"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="courseconfiguration",
|
||||
name="is_uk",
|
||||
field=models.BooleanField(default=False, verbose_name="ÜK-Lehrgang"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="courseconfiguration",
|
||||
name="is_vv",
|
||||
field=models.BooleanField(
|
||||
default=False, verbose_name="Versicherungsvermittler-Lehrgang"
|
||||
),
|
||||
),
|
||||
migrations.RunPython(forward_migration, backward_migration),
|
||||
]
|
||||
|
|
@ -149,7 +149,7 @@ class CourseBasePage(Page):
|
|||
def save(self, clean=True, user=None, log_action=False, **kwargs):
|
||||
slug_changed = False
|
||||
|
||||
if not self.id is None:
|
||||
if self.id is not None:
|
||||
old_record = Page.objects.get(id=self.id).specific
|
||||
if old_record.slug != self.slug:
|
||||
self.set_url_path(self.get_parent())
|
||||
|
|
@ -340,5 +340,8 @@ class CourseConfiguration(models.Model):
|
|||
_("Kompetenzweise ein/aus"), default=True
|
||||
)
|
||||
|
||||
is_vv = models.BooleanField(_("Versicherungsvermittler-Lehrgang"), default=False)
|
||||
is_uk = models.BooleanField(_("ÜK-Lehrgang"), default=False)
|
||||
|
||||
def __str__(self):
|
||||
return f"Course Configuration for '{self.course.title}'"
|
||||
|
|
|
|||
|
|
@ -73,16 +73,22 @@ class CourseSessionSerializer(serializers.ModelSerializer):
|
|||
course = serializers.SerializerMethodField()
|
||||
due_dates = serializers.SerializerMethodField()
|
||||
actions = serializers.SerializerMethodField()
|
||||
user_roles = serializers.SerializerMethodField()
|
||||
|
||||
def get_course(self, obj):
|
||||
return CourseSerializer(obj.course).data
|
||||
|
||||
def get_due_dates(self, obj):
|
||||
due_dates = DueDate.objects.filter(
|
||||
Q(start__isnull=False) | Q(end__isnull=False), course_session=obj
|
||||
Q(start__isnull=False) | Q(end__isnull=False), course_session_id=obj.id
|
||||
)
|
||||
return DueDateSerializer(due_dates, many=True).data
|
||||
|
||||
def get_user_roles(self, obj):
|
||||
if hasattr(obj, "roles"):
|
||||
return list(obj.roles)
|
||||
return []
|
||||
|
||||
class Meta:
|
||||
model = CourseSession
|
||||
fields = [
|
||||
|
|
@ -95,6 +101,7 @@ class CourseSessionSerializer(serializers.ModelSerializer):
|
|||
"end_date",
|
||||
"due_dates",
|
||||
"actions",
|
||||
"user_roles",
|
||||
]
|
||||
read_only_fields = ["actions"]
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from vbv_lernwelt.course.creators.test_utils import (
|
||||
add_course_session_group_supervisor,
|
||||
add_course_session_user,
|
||||
create_course,
|
||||
create_course_session,
|
||||
create_course_session_group,
|
||||
create_user,
|
||||
)
|
||||
from vbv_lernwelt.course.models import CourseSessionUser
|
||||
from vbv_lernwelt.dashboard.views import get_course_sessions_with_roles_for_user
|
||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||
|
||||
|
||||
class GetCourseSessionsForUserTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.course, _ = create_course("Test Course")
|
||||
self.course_session = create_course_session(
|
||||
course=self.course, title="Test Session"
|
||||
)
|
||||
|
||||
def test_participant_get_sessions(self):
|
||||
# participant gets all his sessions marked with role "MEMBER"
|
||||
participant = create_user("participant")
|
||||
add_course_session_user(
|
||||
self.course_session,
|
||||
participant,
|
||||
role=CourseSessionUser.Role.MEMBER,
|
||||
)
|
||||
|
||||
# WHEN
|
||||
sessions = get_course_sessions_with_roles_for_user(participant)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(len(sessions), 1)
|
||||
self.assertEqual(sessions[0].title, "Test Session")
|
||||
self.assertSetEqual(sessions[0].roles, {"MEMBER"})
|
||||
|
||||
def test_trainer_get_sessions(self):
|
||||
# GIVEN
|
||||
# trainer gets all his sessions marked with role "EXPERT"
|
||||
trainer = create_user("trainer")
|
||||
add_course_session_user(
|
||||
self.course_session,
|
||||
trainer,
|
||||
role=CourseSessionUser.Role.EXPERT,
|
||||
)
|
||||
|
||||
# WHEN
|
||||
sessions = get_course_sessions_with_roles_for_user(trainer)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(len(sessions), 1)
|
||||
self.assertEqual(sessions[0].title, "Test Session")
|
||||
self.assertSetEqual(sessions[0].roles, {"EXPERT"})
|
||||
|
||||
def test_supervisor_get_sessions(self):
|
||||
supervisor = create_user("supervisor")
|
||||
group = create_course_session_group(course_session=self.course_session)
|
||||
add_course_session_group_supervisor(group=group, user=supervisor)
|
||||
|
||||
sessions = get_course_sessions_with_roles_for_user(supervisor)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(len(sessions), 1)
|
||||
self.assertEqual(sessions[0].title, "Test Session")
|
||||
self.assertEqual(sessions[0].roles, {"SUPERVISOR"})
|
||||
|
||||
def test_learning_mentor_get_sessions(self):
|
||||
mentor = create_user("mentor")
|
||||
LearningMentor.objects.create(mentor=mentor, course_session=self.course_session)
|
||||
|
||||
participant = create_user("participant")
|
||||
add_course_session_user(
|
||||
self.course_session,
|
||||
participant,
|
||||
role=CourseSessionUser.Role.MEMBER,
|
||||
)
|
||||
|
||||
sessions = get_course_sessions_with_roles_for_user(mentor)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(len(sessions), 1)
|
||||
self.assertEqual(sessions[0].title, "Test Session")
|
||||
self.assertEqual(sessions[0].roles, {"LEARNING_MENTOR"})
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import List, Set
|
||||
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.response import Response
|
||||
|
||||
from vbv_lernwelt.core.models import User
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.course.views import logger
|
||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CourseSessionWithRoles:
|
||||
_original: CourseSession
|
||||
roles: Set[str]
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
# Delegate attribute access to the _original CourseSession object
|
||||
return getattr(self._original, name)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
raise NotImplementedError("This proxy object cannot be saved.")
|
||||
|
||||
|
||||
def get_course_sessions_with_roles_for_user(user: User) -> List[CourseSessionWithRoles]:
|
||||
result_course_sessions = {}
|
||||
|
||||
# participant/member/expert course sessions
|
||||
csu_qs = CourseSessionUser.objects.filter(user=user).prefetch_related(
|
||||
"course_session", "course_session__course"
|
||||
)
|
||||
for csu in csu_qs:
|
||||
cs = csu.course_session
|
||||
# member/expert is mutually exclusive...
|
||||
cs.roles = {csu.role}
|
||||
result_course_sessions[cs.id] = cs
|
||||
|
||||
# enrich with supervisor course sessions
|
||||
csg_qs = CourseSessionGroup.objects.filter(supervisor=user).prefetch_related(
|
||||
"course_session", "course_session__course"
|
||||
)
|
||||
for csg in csg_qs:
|
||||
for cs in csg.course_session.all():
|
||||
cs.roles = set()
|
||||
cs = result_course_sessions.get(cs.id, cs)
|
||||
|
||||
cs.roles.add("SUPERVISOR")
|
||||
result_course_sessions[cs.id] = cs
|
||||
|
||||
# enrich with mentor course sessions
|
||||
lm_qs = LearningMentor.objects.filter(mentor=user).prefetch_related(
|
||||
"course_session", "course_session__course"
|
||||
)
|
||||
for lm in lm_qs:
|
||||
cs = lm.course_session
|
||||
cs.roles = set()
|
||||
cs = result_course_sessions.get(cs.id, cs)
|
||||
|
||||
cs.roles.add("LEARNING_MENTOR")
|
||||
result_course_sessions[cs.id] = cs
|
||||
|
||||
return [
|
||||
CourseSessionWithRoles(cs, cs.roles) for cs in result_course_sessions.values()
|
||||
]
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
def get_dashboard_persons(request):
|
||||
try:
|
||||
course_sessions = get_course_sessions_with_roles_for_user(request.user)
|
||||
|
||||
result_persons = {}
|
||||
for cs in course_sessions:
|
||||
if {
|
||||
"SUPERVISOR",
|
||||
"EXPERT",
|
||||
"MEMBER",
|
||||
} & cs.roles and cs.course.configuration.is_uk:
|
||||
course_session_users = CourseSessionUser.objects.filter(
|
||||
course_session=cs.id
|
||||
)
|
||||
my_role = (
|
||||
"SUPERVISOR"
|
||||
if "SUPERVISOR" in cs.roles
|
||||
else ("EXPERT" if "EXPERT" in cs.roles else "MEMBER")
|
||||
)
|
||||
for csu in course_session_users:
|
||||
result_persons[csu.user.id] = {
|
||||
"user_id": csu.user.id,
|
||||
"first_name": csu.user.first_name,
|
||||
"last_name": csu.user.last_name,
|
||||
"email": csu.user.email,
|
||||
"course_sessions": [
|
||||
{
|
||||
"id": cs.id,
|
||||
"session_title": cs.title,
|
||||
"course_id": cs.course.id,
|
||||
"course_title": cs.course.title,
|
||||
"course_slug": cs.course.slug,
|
||||
"user_role": csu.role,
|
||||
"my_role": my_role,
|
||||
"is_uk": cs.course.configuration.is_uk,
|
||||
"is_vv": cs.course.configuration.is_vv,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
# add persons where request.user is mentor
|
||||
for cs in course_sessions:
|
||||
if "LEARNING_MENTOR" in cs.roles:
|
||||
lm = LearningMentor.objects.filter(
|
||||
mentor=request.user, course_session=cs.id
|
||||
).first()
|
||||
|
||||
for participant in lm.participants.all():
|
||||
course_session_entry = {
|
||||
"id": cs.id,
|
||||
"session_title": cs.title,
|
||||
"course_id": cs.course.id,
|
||||
"course_title": cs.course.title,
|
||||
"course_slug": cs.course.slug,
|
||||
"user_role": "LEARNING_MENTEE",
|
||||
"my_role": "LEARNING_MENTOR",
|
||||
"is_uk": cs.course.configuration.is_uk,
|
||||
"is_vv": cs.course.configuration.is_vv,
|
||||
}
|
||||
|
||||
if participant.user.id not in result_persons:
|
||||
result_persons[participant.user.id] = {
|
||||
"user_id": participant.user.id,
|
||||
"first_name": participant.user.first_name,
|
||||
"last_name": participant.user.last_name,
|
||||
"email": participant.user.email,
|
||||
"course_sessions": [course_session_entry],
|
||||
}
|
||||
else:
|
||||
# user is already in result_persons
|
||||
result_persons[participant.user.id]["course_sessions"].append(
|
||||
course_session_entry
|
||||
)
|
||||
|
||||
# add persons where request.user is mentee
|
||||
mentor_relation_qs = LearningMentor.objects.filter(
|
||||
participants__user=request.user
|
||||
).prefetch_related("mentor", "course_session")
|
||||
for mentor_relation in mentor_relation_qs:
|
||||
cs = mentor_relation.course_session
|
||||
course_session_entry = {
|
||||
"id": cs.id,
|
||||
"session_title": cs.title,
|
||||
"course_id": cs.course.id,
|
||||
"course_title": cs.course.title,
|
||||
"course_slug": cs.course.slug,
|
||||
"user_role": "LEARNING_MENTOR",
|
||||
"my_role": "LEARNING_MENTEE",
|
||||
"is_uk": cs.course.configuration.is_uk,
|
||||
"is_vv": cs.course.configuration.is_vv,
|
||||
}
|
||||
|
||||
if mentor_relation.mentor.id not in result_persons:
|
||||
result_persons[mentor_relation.mentor.id] = {
|
||||
"user_id": mentor_relation.mentor.id,
|
||||
"first_name": mentor_relation.mentor.first_name,
|
||||
"last_name": mentor_relation.mentor.last_name,
|
||||
"email": mentor_relation.mentor.email,
|
||||
"course_sessions": [course_session_entry],
|
||||
}
|
||||
else:
|
||||
# user is already in result_persons
|
||||
result_persons[mentor_relation.mentor.id]["course_sessions"].append(
|
||||
course_session_entry
|
||||
)
|
||||
|
||||
return Response(
|
||||
status=200,
|
||||
data=list(result_persons.values()),
|
||||
)
|
||||
except PermissionDenied as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
return Response({"error": str(e)}, status=404)
|
||||
|
|
@ -48,9 +48,9 @@ def has_course_session_preview(user, course_session_id: int):
|
|||
if is_course_session_member(user, course_session_id):
|
||||
return False
|
||||
|
||||
return is_learning_mentor(user, course_session_id) or is_course_session_expert(
|
||||
return is_course_session_learning_mentor(
|
||||
user, course_session_id
|
||||
)
|
||||
) or is_course_session_expert(user, course_session_id)
|
||||
|
||||
|
||||
def has_media_library(user, course_session_id: int):
|
||||
|
|
@ -66,7 +66,7 @@ def has_media_library(user, course_session_id: int):
|
|||
).exists()
|
||||
|
||||
|
||||
def is_learning_mentor(mentor: User, course_session_id: int):
|
||||
def is_course_session_learning_mentor(mentor: User, course_session_id: int):
|
||||
course_session = CourseSession.objects.get(id=course_session_id)
|
||||
|
||||
if course_session is None:
|
||||
|
|
@ -92,6 +92,15 @@ def is_learning_mentor_for_user(
|
|||
).exists()
|
||||
|
||||
|
||||
def is_course_session_supervisor(user, course_session_id: int):
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
return CourseSessionGroup.objects.filter(
|
||||
supervisor=user, course_session=course_session_id
|
||||
).exists()
|
||||
|
||||
|
||||
def is_course_session_expert(user, course_session_id: int):
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
|
@ -244,40 +253,6 @@ def has_appointments(user: User, course_session_id: int) -> bool:
|
|||
return CourseSessionUser.objects.filter(course_session=course_session_id).exists()
|
||||
|
||||
|
||||
def has_learning_mentor(user: User, course_session_id: int) -> bool:
|
||||
course_session = CourseSession.objects.get(id=course_session_id)
|
||||
|
||||
if course_session is None:
|
||||
return False
|
||||
|
||||
if not course_session.course.configuration.enable_learning_mentor:
|
||||
return False
|
||||
|
||||
if is_learning_mentor(user, course_session_id):
|
||||
return True
|
||||
|
||||
if is_course_session_member(user, course_session_id):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def can_edit_mentors(user: User, course_session_id: int) -> bool:
|
||||
if not has_learning_mentor(user, course_session_id):
|
||||
return False
|
||||
|
||||
# limit further, since has_learning_mentor is too broad
|
||||
return is_course_session_member(user, course_session_id)
|
||||
|
||||
|
||||
def can_guide_members(user: User, course_session_id: int) -> bool:
|
||||
if not has_learning_mentor(user, course_session_id):
|
||||
return False
|
||||
|
||||
# limit further, since has_learning_mentor is too broad
|
||||
return is_learning_mentor(user, course_session_id)
|
||||
|
||||
|
||||
def can_view_profile(user: User, profile_user: CourseSessionUser) -> bool:
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
|
@ -311,29 +286,40 @@ def can_view_course_completions(
|
|||
)
|
||||
|
||||
|
||||
def can_complete_learning_content(user: User, course_session_id: int) -> bool:
|
||||
return is_course_session_member(
|
||||
user, course_session_id
|
||||
) or is_course_session_expert(user, course_session_id)
|
||||
|
||||
|
||||
def course_session_permissions(user: User, course_session_id: int) -> list[str]:
|
||||
course_session = CourseSession.objects.get(id=course_session_id)
|
||||
|
||||
is_supervisor = is_course_session_supervisor(user, course_session_id)
|
||||
is_expert = is_course_session_expert(user, course_session_id)
|
||||
is_member = is_course_session_member(user, course_session_id)
|
||||
is_learning_mentor = is_course_session_learning_mentor(user, course_session_id)
|
||||
|
||||
course_has_learning_mentor = (
|
||||
course_session.course.configuration.enable_learning_mentor
|
||||
)
|
||||
has_learning_mentor = course_has_learning_mentor and (
|
||||
is_member or is_expert or is_learning_mentor
|
||||
)
|
||||
|
||||
return _action_list(
|
||||
{
|
||||
"learning-mentor": has_learning_mentor(user, course_session_id),
|
||||
"learning-mentor::edit-mentors": can_edit_mentors(user, course_session_id),
|
||||
"learning-mentor::guide-members": can_guide_members(
|
||||
user, course_session_id
|
||||
),
|
||||
# roles
|
||||
"is_supervisor": is_supervisor,
|
||||
"is_expert": is_expert,
|
||||
"is_member": is_member,
|
||||
"is_learning_mentor": is_learning_mentor,
|
||||
# actions
|
||||
"learning-mentor": has_learning_mentor,
|
||||
"learning-mentor::edit-mentors": has_learning_mentor and is_member,
|
||||
"learning-mentor::guide-members": course_has_learning_mentor
|
||||
and is_learning_mentor,
|
||||
"preview": has_course_session_preview(user, course_session_id),
|
||||
"media-library": has_media_library(user, course_session_id),
|
||||
"appointments": has_appointments(user, course_session_id),
|
||||
"expert-cockpit": is_course_session_expert(user, course_session_id),
|
||||
"learning-path": is_course_session_member(user, course_session_id),
|
||||
"competence-navi": is_course_session_member(user, course_session_id),
|
||||
"complete-learning-content": can_complete_learning_content(
|
||||
user, course_session_id
|
||||
),
|
||||
"media-library": is_supervisor or is_expert or is_member,
|
||||
"appointments": is_supervisor or is_expert or is_member,
|
||||
"expert-cockpit": is_expert,
|
||||
"learning-path": is_member,
|
||||
"competence-navi": is_member,
|
||||
"complete-learning-content": is_expert or is_member,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -48,15 +48,16 @@ class ActionTestCase(TestCase):
|
|||
self.assertEqual(
|
||||
mentor_actions,
|
||||
[
|
||||
"is_learning_mentor",
|
||||
"learning-mentor",
|
||||
"learning-mentor::guide-members",
|
||||
"preview",
|
||||
"appointments",
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
participant_actions,
|
||||
[
|
||||
"is_member",
|
||||
"learning-mentor",
|
||||
"learning-mentor::edit-mentors",
|
||||
"media-library",
|
||||
|
|
@ -69,6 +70,8 @@ class ActionTestCase(TestCase):
|
|||
self.assertEqual(
|
||||
trainer_actions,
|
||||
[
|
||||
"is_expert",
|
||||
"learning-mentor",
|
||||
"preview",
|
||||
"media-library",
|
||||
"appointments",
|
||||
|
|
|
|||
Loading…
Reference in New Issue