WIP: Add Praxisbildner switch, move code to type
This commit is contained in:
parent
6edb5be093
commit
2f77bf7734
|
|
@ -0,0 +1,41 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, Ref } from "vue";
|
||||
import { ProgressDashboardAssignmentType } from "@/gql/graphql";
|
||||
import { fetchProgressData } from "@/services/dashboard";
|
||||
import AssignmentProgressSummaryBox from "@/components/dashboard/AssignmentProgressSummaryBox.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
courseId: string;
|
||||
courseSlug: string;
|
||||
sessionToContinueId: string;
|
||||
}>();
|
||||
|
||||
const DEFAULT_ASSIGNMENT = {
|
||||
points_achieved_count: 0,
|
||||
points_max_count: 0,
|
||||
total_count: 0,
|
||||
};
|
||||
const assignment: Ref<ProgressDashboardAssignmentType> = ref(DEFAULT_ASSIGNMENT);
|
||||
|
||||
const competenceCertificateUrl = computed(() => {
|
||||
return `/course/${props.courseSlug}/competence/certificates?courseSessionId=${props.sessionToContinueId}`;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await fetchProgressData(props.courseId);
|
||||
assignment.value = data?.assignment;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="assignment" class="mb-14 space-y-8">
|
||||
<div class="flex flex-col space-y-7 bg-white p-6">
|
||||
<AssignmentProgressSummaryBox
|
||||
:total-assignments="assignment.total_count"
|
||||
:achieved-points-count="assignment.points_achieved_count"
|
||||
:max-points-count="assignment.points_max_count"
|
||||
:details-link="competenceCertificateUrl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -18,8 +18,8 @@ const competenceCriteriaUrl = computed(() => {
|
|||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const some = await fetchProgressData(props.courseId);
|
||||
competence.value = some.competence;
|
||||
const data = await fetchProgressData(props.courseId);
|
||||
competence.value = data.competence;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import type { CourseProgressType } from "@/gql/graphql";
|
||||
import type { CourseProgressType, WidgetType } from "@/gql/graphql";
|
||||
import { DashboardConfigType } from "@/gql/graphql";
|
||||
import { fetchCourseData } from "@/services/dashboard";
|
||||
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
||||
import CompetenceSummary from "@/components/dashboard/CompetenceSummary.vue";
|
||||
import AssignmentSummary from "@/components/dashboard/AssignmentSummary.vue";
|
||||
|
||||
const mentorWidgets = [
|
||||
"MENTOR_TASKS_WIDGET",
|
||||
"MENTOR_PERSON_WIDGET",
|
||||
"MENTOR_COMPETENCE_WIDGET",
|
||||
];
|
||||
const progressWidgets = ["COMPETENCE_WIDGET", "COMPETENCE_CERTIFICATE_WIDGET"];
|
||||
|
||||
const props = defineProps<{
|
||||
courseConfig: DashboardConfigType;
|
||||
|
|
@ -16,6 +24,17 @@ const data = ref<CourseProgressType | null>(null);
|
|||
|
||||
const courseSlug = computed(() => props.courseConfig?.slug);
|
||||
const courseName = computed(() => props.courseConfig?.name);
|
||||
const numberOfMentorWidgets = computed(() => {
|
||||
return data.value?.widgets.filter((widget) => mentorWidgets.includes(widget)).length;
|
||||
});
|
||||
const numberOfProgressWidgets = computed(() => {
|
||||
return data.value?.widgets.filter((widget) => progressWidgets.includes(widget))
|
||||
.length;
|
||||
});
|
||||
|
||||
function hasWidget(widget: WidgetType) {
|
||||
return data.value?.widgets.includes(widget);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
data.value = await fetchCourseData(props.courseConfig.id);
|
||||
|
|
@ -27,23 +46,31 @@ onMounted(async () => {
|
|||
<div v-if="!isLoading && courseConfig" class="mb-14 space-y-8">
|
||||
<div class="flex flex-col space-y-7 bg-white p-6">
|
||||
<h3>{{ courseName }}</h3>
|
||||
<div v-for="widget in data.widgets" :key="widget.id">
|
||||
{{ widget }}
|
||||
<LearningPathDiagram
|
||||
v-if="
|
||||
widget === 'PROGRESS_WIDGET' && data.session_to_continue_id && courseSlug
|
||||
"
|
||||
:key="courseSlug"
|
||||
:course-slug="courseSlug"
|
||||
:course-session-id="data.session_to_continue_id"
|
||||
diagram-type="horizontal"
|
||||
></LearningPathDiagram>
|
||||
<LearningPathDiagram
|
||||
v-if="hasWidget('PROGRESS_WIDGET') && data.session_to_continue_id && courseSlug"
|
||||
:key="courseSlug"
|
||||
:course-slug="courseSlug"
|
||||
:course-session-id="data.session_to_continue_id"
|
||||
diagram-type="horizontal"
|
||||
></LearningPathDiagram>
|
||||
<div v-if="numberOfProgressWidgets" class="flex flex-col flex-wrap">
|
||||
<CompetenceSummary
|
||||
v-else-if="widget === 'COMPETENCE_WIDGET'"
|
||||
v-if="hasWidget('COMPETENCE_WIDGET')"
|
||||
:course-slug="courseSlug"
|
||||
:session-to-continue-id="data.session_to_continue_id"
|
||||
:course-id="courseConfig.id"
|
||||
></CompetenceSummary>
|
||||
<AssignmentSummary
|
||||
v-if="hasWidget('COMPETENCE_CERTIFICATE_WIDGET')"
|
||||
:course-slug="courseSlug"
|
||||
:session-to-continue-id="data.session_to_continue_id"
|
||||
:course-id="courseConfig.id"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="numberOfMentorWidgets > 0" class="flex flex-col flex-wrap">
|
||||
<div v-if="hasWidget('MENTOR_TASKS_WIDGET')">MENTOR_TASKS_WIDGET</div>
|
||||
<div v-if="hasWidget('MENTOR_PERSON_WIDGET')">MENTOR_PERSON_WIDGET</div>
|
||||
<div v-if="hasWidget('MENTOR_COMPETENCE_WIDGET')">MENTOR_COMPETENCE_WIDGET</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -466,6 +466,7 @@ export type DashboardConfigType = {
|
|||
|
||||
export type DashboardType =
|
||||
| 'MENTOR_DASHBOARD'
|
||||
| 'PRAXISBILDNER_DASHBOARD'
|
||||
| 'PROGRESS_DASHBOARD'
|
||||
| 'SIMPLE_DASHBOARD'
|
||||
| 'STATISTICS_DASHBOARD';
|
||||
|
|
@ -908,7 +909,6 @@ export type Query = {
|
|||
course_session_attendance_course?: Maybe<CourseSessionAttendanceCourseObjectType>;
|
||||
course_statistics?: Maybe<CourseStatisticsType>;
|
||||
dashboard_config: Array<DashboardConfigType>;
|
||||
dashboard_data?: Maybe<DashboardConfigType>;
|
||||
learning_content_assignment?: Maybe<LearningContentAssignmentObjectType>;
|
||||
learning_content_attendance_course?: Maybe<LearningContentAttendanceCourseObjectType>;
|
||||
learning_content_document_list?: Maybe<LearningContentDocumentListObjectType>;
|
||||
|
|
@ -980,11 +980,6 @@ export type QueryCourseStatisticsArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type QueryDashboardDataArgs = {
|
||||
course_id: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryLearningPathArgs = {
|
||||
course_id?: InputMaybe<Scalars['ID']['input']>;
|
||||
course_slug?: InputMaybe<Scalars['String']['input']>;
|
||||
|
|
@ -1056,7 +1051,8 @@ export type UserObjectType = {
|
|||
export type WidgetType =
|
||||
| 'COMPETENCE_CERTIFICATE_WIDGET'
|
||||
| 'COMPETENCE_WIDGET'
|
||||
| 'MENTEE_WIDGET'
|
||||
| 'MENTOR_COMPETENCE_WIDGET'
|
||||
| 'MENTOR_PERSON_WIDGET'
|
||||
| 'MENTOR_TASKS_WIDGET'
|
||||
| 'PROGRESS_WIDGET';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ type Query {
|
|||
course_statistics(course_id: ID!): CourseStatisticsType
|
||||
course_progress(course_id: ID!): CourseProgressType
|
||||
dashboard_config: [DashboardConfigType!]!
|
||||
dashboard_data(course_id: ID!): DashboardConfigType
|
||||
learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType
|
||||
course_session_attendance_course(id: ID!, assignment_user_id: ID): CourseSessionAttendanceCourseObjectType
|
||||
course(id: ID, slug: String): CourseObjectType
|
||||
|
|
@ -200,8 +199,9 @@ type ProgressDashboardAssignmentType {
|
|||
enum WidgetType {
|
||||
PROGRESS_WIDGET
|
||||
COMPETENCE_WIDGET
|
||||
MENTEE_WIDGET
|
||||
MENTOR_TASKS_WIDGET
|
||||
MENTOR_PERSON_WIDGET
|
||||
MENTOR_COMPETENCE_WIDGET
|
||||
COMPETENCE_CERTIFICATE_WIDGET
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +218,7 @@ enum DashboardType {
|
|||
PROGRESS_DASHBOARD
|
||||
SIMPLE_DASHBOARD
|
||||
MENTOR_DASHBOARD
|
||||
PRAXISBILDNER_DASHBOARD
|
||||
}
|
||||
|
||||
type CourseConfigurationObjectType {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ const boards: Record<DashboardType, DashboardPage> = {
|
|||
SIMPLE_DASHBOARD: { main: SimpleCoursePage, aside: SimpleDates },
|
||||
STATISTICS_DASHBOARD: { main: StatisticPage, aside: CourseDetailDates },
|
||||
MENTOR_DASHBOARD: { main: MentorPage, aside: SimpleDates },
|
||||
PRAXISBILDNER_DASHBOARD: { main: CoursePanel, aside: SimpleDates },
|
||||
};
|
||||
|
||||
onMounted(dashboardStore.loadDashboardDetails);
|
||||
|
|
@ -57,8 +58,17 @@ onMounted(dashboardStore.loadDashboardDetails);
|
|||
<CoursePanel :course-config="config" />
|
||||
</li>
|
||||
</ul>
|
||||
<!-- keep until we unify the dashboard -->
|
||||
<CoursePanel
|
||||
v-if="
|
||||
dashboardStore.currentDashboardConfig.dashboard_type ===
|
||||
'PRAXISBILDNER_DASHBOARD'
|
||||
"
|
||||
course-config="dashboardStore.currentDashboardConfig"
|
||||
/>
|
||||
<component
|
||||
:is="boards[dashboardStore.currentDashboardConfig.dashboard_type].main"
|
||||
v-else
|
||||
></component>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ from vbv_lernwelt.dashboard.graphql.types.dashboard import (
|
|||
CourseStatisticsType,
|
||||
DashboardConfigType,
|
||||
DashboardType,
|
||||
get_widgets_for_course,
|
||||
ProgressDashboardAssignmentType,
|
||||
ProgressDashboardCompetenceType,
|
||||
WidgetType,
|
||||
)
|
||||
from vbv_lernwelt.iam.permissions import (
|
||||
can_view_course_session,
|
||||
|
|
@ -40,10 +40,6 @@ class DashboardQuery(graphene.ObjectType):
|
|||
graphene.NonNull(DashboardConfigType), required=True
|
||||
)
|
||||
|
||||
dashboard_data = graphene.Field(
|
||||
DashboardConfigType, course_id=graphene.ID(required=True)
|
||||
)
|
||||
|
||||
def resolve_course_statistics(root, info, course_id: str): # noqa
|
||||
user = info.context.user
|
||||
course = Course.objects.get(id=course_id)
|
||||
|
|
@ -114,6 +110,8 @@ class DashboardQuery(graphene.ObjectType):
|
|||
|
||||
user = info.context.user
|
||||
course = Course.objects.get(id=course_id)
|
||||
setattr(info.context, "course", course)
|
||||
return CourseProgressType()
|
||||
|
||||
newest: CourseSession | None = None
|
||||
course_session_for_user: List[str] = []
|
||||
|
|
@ -169,12 +167,9 @@ class DashboardQuery(graphene.ObjectType):
|
|||
points_max_count=int(points_max_count), # noqa
|
||||
points_achieved_count=int(points_achieved_count), # noqa
|
||||
),
|
||||
widgets=get_widget_for_course(course_id, user), # noqa
|
||||
widgets=get_widgets_for_course(course, user), # noqa
|
||||
)
|
||||
|
||||
def resolve_dashboard_data(root, info, course_id: str):
|
||||
return get_widget_for_course(course_id, info.context.user)
|
||||
|
||||
|
||||
def get_user_statistics_dashboards(user: User) -> Tuple[List[Dict[str, str]], Set[int]]:
|
||||
course_ids = set()
|
||||
|
|
@ -272,42 +267,3 @@ def get_user_course_session_dashboards(
|
|||
)
|
||||
|
||||
return dashboards, course_ids
|
||||
|
||||
|
||||
def get_widget_for_course(course_id: str, user: User) -> List[WidgetType]:
|
||||
widgets = []
|
||||
|
||||
course_sessions = CourseSession.objects.filter(course__id=course_id).prefetch_related(
|
||||
"course",
|
||||
"course__configuration",
|
||||
)
|
||||
roles_by_course: Dict[Course, Set[DashboardType]] = {}
|
||||
|
||||
learning_mentors = LearningMentor.objects.filter(mentor=user).values_list(
|
||||
"course_session__course__id", "id"
|
||||
)
|
||||
mentor_course_ids = set([mentor[0] for mentor in learning_mentors])
|
||||
|
||||
# duplicate code
|
||||
for course_session in course_sessions:
|
||||
if can_view_course_session(user=user, course_session=course_session):
|
||||
role = CourseSessionUser.objects.get(
|
||||
course_session=course_session, user=user
|
||||
).role
|
||||
roles_by_course.setdefault(course_session.course, set())
|
||||
roles_by_course[course_session.course].add(role)
|
||||
|
||||
for course, roles in roles_by_course.items():
|
||||
if len(roles) == 1:
|
||||
course_role = roles.pop()
|
||||
|
||||
# test widgets
|
||||
if course_role == CourseSessionUser.Role.MEMBER:
|
||||
widgets.append(WidgetType.PROGRESS_WIDGET)
|
||||
widgets.append(WidgetType.COMPETENCE_WIDGET)
|
||||
if course.configuration.enable_competence_certificates:
|
||||
widgets.append(WidgetType.COMPETENCE_CERTIFICATE_WIDGET)
|
||||
if course.id in mentor_course_ids:
|
||||
widgets.append(WidgetType.MENTOR_TASKS_WIDGET)
|
||||
# add KN if has KN
|
||||
return widgets
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
import graphene
|
||||
from graphene import Enum
|
||||
|
||||
from vbv_lernwelt.assignment.models import (
|
||||
AssignmentCompletion,
|
||||
AssignmentCompletionStatus,
|
||||
)
|
||||
from vbv_lernwelt.core.models import User
|
||||
from vbv_lernwelt.course.graphql.types import CourseConfigurationObjectType
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.dashboard.graphql.types.assignment import (
|
||||
assignments,
|
||||
AssignmentsStatisticsType,
|
||||
|
|
@ -20,6 +27,11 @@ from vbv_lernwelt.dashboard.graphql.types.feedback import (
|
|||
feedback_responses,
|
||||
FeedbackStatisticsResponsesType,
|
||||
)
|
||||
from vbv_lernwelt.iam.permissions import (
|
||||
can_view_course_session,
|
||||
can_view_course_session_progress,
|
||||
)
|
||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||
from vbv_lernwelt.learnpath.models import Circle
|
||||
|
||||
|
||||
|
|
@ -54,13 +66,15 @@ class DashboardType(Enum):
|
|||
PROGRESS_DASHBOARD = "ProgressDashboard"
|
||||
SIMPLE_DASHBOARD = "SimpleDashboard"
|
||||
MENTOR_DASHBOARD = "MentorDashboard"
|
||||
PRAXISBILDNER_DASHBOARD = "PraxisbildnerDashboard"
|
||||
|
||||
|
||||
class WidgetType(Enum):
|
||||
PROGRESS_WIDGET = "ProgressWidget"
|
||||
COMPETENCE_WIDGET = "CompetenceWidget"
|
||||
MENTEE_WIDGET = "MenteeWidget"
|
||||
MENTOR_TASKS_WIDGET = "MentorTasksWidget"
|
||||
MENTOR_PERSON_WIDGET = "MentorPersonWidget"
|
||||
MENTOR_COMPETENCE_WIDGET = "MentorCompetenceWidget"
|
||||
COMPETENCE_CERTIFICATE_WIDGET = "CompetenceCertificateWidget"
|
||||
|
||||
|
||||
|
|
@ -99,6 +113,86 @@ class CourseProgressType(graphene.ObjectType):
|
|||
assignment = graphene.Field(ProgressDashboardAssignmentType, required=False)
|
||||
widgets = graphene.List(graphene.NonNull(WidgetType), required=True)
|
||||
|
||||
def resolve__id(root, info):
|
||||
return info.context.course.id
|
||||
|
||||
def resolve_course_id(root, info):
|
||||
return info.context.course.id
|
||||
|
||||
def resolve_session_to_continue_id(root, info):
|
||||
newest, _course_session_for_user = root._get_newest_cs_and_cs_for_user(
|
||||
info, info.context.course.id, info.context.user
|
||||
)
|
||||
return newest.id if newest else None
|
||||
|
||||
def resolve_assignment(root, info):
|
||||
evaluation_results = AssignmentCompletion.objects.filter(
|
||||
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
|
||||
assignment_user=info.context.user,
|
||||
course_session__course=info.context.course,
|
||||
).values("evaluation_max_points", "evaluation_points")
|
||||
|
||||
evaluation_results = list(evaluation_results)
|
||||
points_max_count = sum(
|
||||
[result.get("evaluation_max_points", 0) for result in evaluation_results]
|
||||
)
|
||||
points_achieved_count = sum(
|
||||
[result.get("evaluation_points", 0) for result in evaluation_results]
|
||||
)
|
||||
return ProgressDashboardAssignmentType( # noqa
|
||||
_id=info.context.course.id, # noqa
|
||||
total_count=len(evaluation_results), # noqa
|
||||
points_max_count=int(points_max_count), # noqa
|
||||
points_achieved_count=int(points_achieved_count), # noqa
|
||||
)
|
||||
|
||||
def resolve_competence(root, info):
|
||||
newest, course_session_for_user = root._get_newest_cs_and_cs_for_user(
|
||||
info, info.context.course.id, info.context.user
|
||||
)
|
||||
_, success_total, fail_total = competences(
|
||||
course_slug=str(info.context.course.slug),
|
||||
course_session_selection_ids=course_session_for_user,
|
||||
user_selection_ids=[str(info.context.user.id)],
|
||||
)
|
||||
|
||||
return ProgressDashboardCompetenceType( # noqa
|
||||
_id=info.context.course.id, # noqa
|
||||
total_count=success_total + fail_total, # noqa
|
||||
success_count=success_total, # noqa
|
||||
fail_count=fail_total, # noqa
|
||||
)
|
||||
|
||||
def resolve_widgets(root, info):
|
||||
return get_widgets_for_course(info.context.course, info.context.user)
|
||||
|
||||
def _get_newest_cs_and_cs_for_user(
|
||||
root, info, course_id: str, user: User
|
||||
) -> Tuple[CourseSession, List[str]]:
|
||||
newest: CourseSession | None = getattr(info.context, "newest", None)
|
||||
course_session_for_user: List[str] = getattr(
|
||||
info.context, "course_session_for_user", []
|
||||
)
|
||||
|
||||
if newest is not None and course_session_for_user:
|
||||
return newest, course_session_for_user
|
||||
|
||||
for course_session in CourseSession.objects.filter(course_id=course_id):
|
||||
if can_view_course_session_progress(
|
||||
user=user, course_session=course_session
|
||||
):
|
||||
course_session_for_user.append(course_session)
|
||||
generation_newest = newest.generation if newest else None
|
||||
if (
|
||||
generation_newest is None
|
||||
or course_session.generation > generation_newest
|
||||
):
|
||||
newest = course_session
|
||||
# cache for use in other resolvers
|
||||
setattr(info.context, "newest", newest)
|
||||
setattr(info.context, "course_session_for_user", course_session_for_user)
|
||||
return newest, course_session_for_user
|
||||
|
||||
|
||||
class CourseStatisticsType(graphene.ObjectType):
|
||||
_id = graphene.ID(required=True)
|
||||
|
|
@ -224,3 +318,48 @@ class CourseStatisticsType(graphene.ObjectType):
|
|||
generations=list(generations), # noqa
|
||||
circles=circle_data, # noqa
|
||||
)
|
||||
|
||||
|
||||
def get_widgets_for_course(course: Course, user: User) -> List[WidgetType]:
|
||||
widgets = []
|
||||
|
||||
course_sessions = CourseSession.objects.filter(
|
||||
course__id=course.id, coursesessionuser__user=user
|
||||
).prefetch_related(
|
||||
"course",
|
||||
"course__configuration",
|
||||
)
|
||||
|
||||
roles_by_course: Dict[Course, Set[DashboardType]] = {}
|
||||
|
||||
learning_mentors = LearningMentor.objects.filter(
|
||||
mentor=user, course_session__course__id=course.id
|
||||
).values_list("course_session__course__id", "id")
|
||||
mentor_course_ids = set([mentor[0] for mentor in learning_mentors])
|
||||
|
||||
# duplicate code
|
||||
for course_session in course_sessions:
|
||||
if can_view_course_session(user=user, course_session=course_session):
|
||||
role = CourseSessionUser.objects.get(
|
||||
course_session=course_session, user=user
|
||||
).role
|
||||
roles_by_course.setdefault(course_session.course, set())
|
||||
roles_by_course[course_session.course].add(role)
|
||||
|
||||
for course, roles in roles_by_course.items():
|
||||
if len(roles) == 1:
|
||||
course_role = roles.pop()
|
||||
|
||||
# members
|
||||
if course_role == CourseSessionUser.Role.MEMBER:
|
||||
widgets.append(WidgetType.PROGRESS_WIDGET)
|
||||
widgets.append(WidgetType.COMPETENCE_WIDGET)
|
||||
if course.configuration.enable_competence_certificates:
|
||||
widgets.append(WidgetType.COMPETENCE_CERTIFICATE_WIDGET)
|
||||
# mentors
|
||||
if course.id in mentor_course_ids:
|
||||
widgets.append(WidgetType.MENTOR_TASKS_WIDGET)
|
||||
widgets.append(WidgetType.MENTOR_PERSON_WIDGET)
|
||||
if course.configuration.enable_competence_certificates:
|
||||
widgets.append(WidgetType.MENTOR_COMPETENCE_WIDGET)
|
||||
return widgets
|
||||
|
|
|
|||
Loading…
Reference in New Issue