+
{{ courseName }}
-
{{ data.ui_config.role_key }}
+
{{ courseConfig.role_key }}
-
MENTOR_TASKS_WIDGET
-
MENTOR_PERSON_WIDGET
-
MENTOR_COMPETENCE_WIDGET
+
MENTOR_TASKS_WIDGET
+
MENTOR_PERSON_WIDGET
+
MENTOR_COMPETENCE_WIDGET
diff --git a/client/src/pages/dashboard/DashboardPage.vue b/client/src/pages/dashboard/DashboardPage.vue
index 8d57ac50..40e65e95 100644
--- a/client/src/pages/dashboard/DashboardPage.vue
+++ b/client/src/pages/dashboard/DashboardPage.vue
@@ -1,6 +1,6 @@
@@ -54,8 +64,8 @@ onMounted(dashboardStore.loadDashboardDetails);
diff --git a/client/src/services/dashboard.ts b/client/src/services/dashboard.ts
index 2beaf0a3..6e4c636e 100644
--- a/client/src/services/dashboard.ts
+++ b/client/src/services/dashboard.ts
@@ -20,6 +20,21 @@ export type DashboardPersonRoleType =
| "LEARNING_MENTOR"
| "LEARNING_MENTEE";
+export type DashboardRoleKeyType =
+ | "Supervisor"
+ | "Expert"
+ | "Member"
+ | "MentorUK"
+ | "MentorVV";
+
+export type WidgetType =
+ | "ProgressWidget"
+ | "CompetenceWidget"
+ | "MentorTasksWidget"
+ | "MentorPersonWidget"
+ | "MentorCompetenceWidget"
+ | "CompetenceCertificateWidget";
+
export type DashboardPersonCourseSessionType = {
id: number;
session_title: string;
@@ -40,6 +55,19 @@ export type DashboardPersonType = {
course_sessions: DashboardPersonCourseSessionType[];
};
+export type DashboardConfigType = {
+ course_id: string;
+ course_slug: string;
+ course_title: string;
+ role_key: DashboardRoleKeyType;
+ is_uk: boolean;
+ is_vv: boolean;
+ is_mentor: boolean;
+ widgets: WidgetType[];
+ has_preview: boolean;
+ session_to_continue_id: string;
+};
+
export const fetchStatisticData = async (
courseId: string
): Promise => {
@@ -113,3 +141,7 @@ export const fetchCourseData = async (
export async function fetchDashboardPersons() {
return await itGetCached("/api/dashboard/persons/");
}
+
+export async function fetchDashboardConfigv2() {
+ return await itGetCached("/api/dashboard/config/");
+}
diff --git a/server/config/urls.py b/server/config/urls.py
index b18dc421..b1a3ba78 100644
--- a/server/config/urls.py
+++ b/server/config/urls.py
@@ -39,7 +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.dashboard.views import get_dashboard_config, get_dashboard_persons
from vbv_lernwelt.edoniq_test.views import (
export_students,
export_students_and_trainers,
@@ -116,9 +116,10 @@ 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"),
+ path(r"api/dashboard/config/", get_dashboard_config, name="get_dashboard_config"),
# course
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
diff --git a/server/vbv_lernwelt/dashboard/graphql/queries.py b/server/vbv_lernwelt/dashboard/graphql/queries.py
index 74b75d55..f760fa5a 100644
--- a/server/vbv_lernwelt/dashboard/graphql/queries.py
+++ b/server/vbv_lernwelt/dashboard/graphql/queries.py
@@ -75,8 +75,9 @@ class DashboardQuery(graphene.ObjectType):
mentees_ids = set()
course_session_ids = set()
- mentees = CourseSessionUser.objects.filter(participants__mentor=user,
- course_session__course=course).values_list("user", "course_session")
+ mentees = CourseSessionUser.objects.filter(
+ participants__mentor=user, course_session__course=course
+ ).values_list("user", "course_session")
for user_id, course_session_id in mentees:
mentees_ids.add(user_id)
course_session_ids.add(course_session_id)
diff --git a/server/vbv_lernwelt/dashboard/graphql/types/assignment.py b/server/vbv_lernwelt/dashboard/graphql/types/assignment.py
index df456052..ad747b22 100644
--- a/server/vbv_lernwelt/dashboard/graphql/types/assignment.py
+++ b/server/vbv_lernwelt/dashboard/graphql/types/assignment.py
@@ -74,8 +74,9 @@ def create_assignment_summary(course_id, metrics) -> AssignmentStatisticsSummary
def get_assignment_completion_metrics(
- course_session: CourseSession, assignment: vbv_lernwelt.assignment.models.Assignment,
- user_selection_ids: List[str] | None
+ course_session: CourseSession,
+ assignment: vbv_lernwelt.assignment.models.Assignment,
+ user_selection_ids: List[str] | None,
) -> AssignmentCompletionMetricsType:
course_session_users = CourseSessionUser.objects.filter(
course_session=course_session,
@@ -112,7 +113,7 @@ def get_assignment_completion_metrics(
def create_record(
course_session_assignment: CourseSessionAssignment | CourseSessionEdoniqTest,
- user_selection_ids: List[str] | None
+ user_selection_ids: List[str] | None,
) -> AssignmentStatisticsRecordType:
if isinstance(course_session_assignment, CourseSessionAssignment):
due_date = course_session_assignment.submission_deadline
@@ -160,14 +161,18 @@ def assignments(
],
learning_content__content_assignment__competence_certificate__isnull=False,
):
- record = create_record(course_session_assignment=csa, user_selection_ids=user_selection_ids)
+ record = create_record(
+ course_session_assignment=csa, user_selection_ids=user_selection_ids
+ )
records.append(record)
for cset in CourseSessionEdoniqTest.objects.filter(
course_session=course_session,
learning_content__content_assignment__competence_certificate__isnull=False,
):
- record = create_record(course_session_assignment=cset, user_selection_ids=user_selection_ids)
+ record = create_record(
+ course_session_assignment=cset, user_selection_ids=user_selection_ids
+ )
records.append(record)
return AssignmentsStatisticsType(
diff --git a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py
index 38da733a..3fa68f93 100644
--- a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py
+++ b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py
@@ -241,8 +241,11 @@ class CourseStatisticsType(graphene.ObjectType):
)
def resolve_competences(root, info) -> CompetencesStatisticsType:
- user_selection_ids = [str(user) for user in
- root.user_selection_ids] if root.user_selection_ids else None # noqa
+ user_selection_ids = (
+ [str(user) for user in root.user_selection_ids]
+ if root.user_selection_ids
+ else None
+ ) # noqa
records, success_total, fail_total = competences(
course_slug=str(root.course_slug),
course_session_selection_ids=[
@@ -261,8 +264,11 @@ class CourseStatisticsType(graphene.ObjectType):
)
def resolve_assignments(root, info) -> AssignmentsStatisticsType:
- user_selection_ids = [str(user) for user in
- root.user_selection_ids] if root.user_selection_ids else None # noqa
+ user_selection_ids = (
+ [str(user) for user in root.user_selection_ids]
+ if root.user_selection_ids
+ else None
+ ) # noqa
return assignments(
course_id=root.course_id,
course_session_selection_ids=root.course_session_selection_ids,
@@ -381,7 +387,11 @@ def get_ui_config_for_course(course: Course, user: User) -> UIConfigType:
# mentors
if course.id in mentor_course_ids:
if not role_key:
- role_key = RoleKeyType.MENTOR_UK if course.id in UK_COURSE_IDS else RoleKeyType.MENTOR_VV
+ role_key = (
+ RoleKeyType.MENTOR_UK
+ if course.id in UK_COURSE_IDS
+ else RoleKeyType.MENTOR_VV
+ )
has_preview = True
widgets.append(WidgetType.MENTOR_TASKS_WIDGET)
widgets.append(WidgetType.MENTOR_PERSON_WIDGET)
diff --git a/server/vbv_lernwelt/dashboard/views.py b/server/vbv_lernwelt/dashboard/views.py
index 0f44b479..7f3a7eba 100644
--- a/server/vbv_lernwelt/dashboard/views.py
+++ b/server/vbv_lernwelt/dashboard/views.py
@@ -1,4 +1,4 @@
-from dataclasses import dataclass
+from dataclasses import asdict, dataclass
from typing import List, Set
from rest_framework.decorators import api_view
@@ -9,6 +9,7 @@ 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.dashboard.graphql.types.dashboard import RoleKeyType, WidgetType
from vbv_lernwelt.learning_mentor.models import LearningMentor
@@ -25,6 +26,20 @@ class CourseSessionWithRoles:
raise NotImplementedError("This proxy object cannot be saved.")
+@dataclass(frozen=True)
+class CourseConfig:
+ course_id: int
+ course_slug: str
+ course_title: str
+ role_key: str
+ is_uk: bool
+ is_vv: bool
+ is_mentor: bool
+ widgets: List[str]
+ has_preview: bool
+ session_to_continue_id: str | None
+
+
def get_course_sessions_with_roles_for_user(user: User) -> List[CourseSessionWithRoles]:
result_course_sessions = {}
@@ -67,6 +82,18 @@ def get_course_sessions_with_roles_for_user(user: User) -> List[CourseSessionWit
]
+def has_cs_role(roles: Set[str]) -> bool:
+ return bool(roles & {"SUPERVISOR", "EXPERT", "MEMBER"})
+
+
+def user_role(roles: Set[str]) -> str:
+ return (
+ "SUPERVISOR"
+ if "SUPERVISOR" in roles
+ else ("EXPERT" if "EXPERT" in roles else "MEMBER")
+ )
+
+
@api_view(["GET"])
def get_dashboard_persons(request):
try:
@@ -74,19 +101,11 @@ def get_dashboard_persons(request):
result_persons = {}
for cs in course_sessions:
- if {
- "SUPERVISOR",
- "EXPERT",
- "MEMBER",
- } & cs.roles and cs.course.configuration.is_uk:
+ if has_cs_role(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")
- )
+ my_role = user_role(cs.roles)
for csu in course_session_users:
result_persons[csu.user.id] = {
"user_id": csu.user.id,
@@ -183,3 +202,123 @@ def get_dashboard_persons(request):
except Exception as e:
logger.error(e, exc_info=True)
return Response({"error": str(e)}, status=404)
+
+
+def get_widgets_for_course(
+ role_key: RoleKeyType, is_uk: bool, is_vv: bool, is_mentor: bool
+) -> List[str]:
+ widgets = []
+
+ if role_key == RoleKeyType.MEMBER:
+ widgets.append(WidgetType.PROGRESS_WIDGET.value)
+ widgets.append(WidgetType.COMPETENCE_WIDGET.value)
+ if is_uk:
+ widgets.append(WidgetType.COMPETENCE_CERTIFICATE_WIDGET.value)
+
+ if is_mentor:
+ widgets.append(WidgetType.MENTOR_PERSON_WIDGET.value)
+ if is_vv:
+ widgets.append(WidgetType.MENTOR_TASKS_WIDGET.value)
+ if is_uk:
+ widgets.append(WidgetType.MENTOR_COMPETENCE_WIDGET.value)
+
+ return widgets
+
+
+def get_role_and_mentor_key(
+ course_sessions: List[CourseSessionWithRoles], is_uk: bool, is_vv: bool
+) -> tuple[RoleKeyType, bool]:
+ roles = set()
+ role = None
+ for cs in course_sessions:
+ roles.update(cs.roles)
+
+ if "SUPERVISOR" in roles:
+ role = RoleKeyType.SUPERVISOR
+ elif "EXPERT" in roles:
+ role = RoleKeyType.TRAINER
+ elif "MEMBER" in roles:
+ role = RoleKeyType.MEMBER
+ elif "LEARNING_MENTOR" in roles:
+ if is_uk:
+ role = RoleKeyType.MENTOR_UK
+ elif is_vv:
+ role = RoleKeyType.MENTOR_VV
+
+ is_mentor = "LEARNING_MENTOR" in roles
+ return role, is_mentor
+
+
+def sort_course_sessions_by_course(
+ course_sessions: List[CourseSessionWithRoles],
+) -> dict:
+ course_sessions_by_course = {}
+ for cs in course_sessions:
+ if cs.course.id not in course_sessions_by_course:
+ course_sessions_by_course[cs.course.id] = []
+ course_sessions_by_course[cs.course.id].append(cs)
+ return course_sessions_by_course
+
+
+def has_preview(role_key: RoleKeyType) -> bool:
+ return role_key in [RoleKeyType.MENTOR_UK, RoleKeyType.MENTOR_VV]
+
+
+def get_newest_cs(
+ course_sessions: List[CourseSessionWithRoles],
+) -> CourseSessionWithRoles | None:
+ newest: CourseSessionWithRoles | None = None
+
+ for cs in course_sessions:
+ generation_newest = newest.generation if newest else None
+ if generation_newest is None or cs.generation > generation_newest:
+ newest = cs
+
+ return newest
+
+
+def get_course_config(
+ course_sessions: List[CourseSessionWithRoles],
+) -> List[CourseConfig]:
+ course_configs = []
+ cs_by_course = sort_course_sessions_by_course(course_sessions)
+ for _id, cs_in_course in cs_by_course.items():
+ is_uk = cs_in_course[0].course.configuration.is_uk
+ is_vv = cs_in_course[0].course.configuration.is_vv
+ role_key, is_mentor = get_role_and_mentor_key(cs_in_course, is_uk, is_vv)
+ session_to_continue = get_newest_cs(cs_in_course)
+ course_configs.append(
+ CourseConfig(
+ course_id=cs_in_course[0].course.id,
+ course_slug=cs_in_course[0].course.slug,
+ course_title=cs_in_course[0].course.title,
+ role_key=role_key.value,
+ is_uk=is_uk,
+ is_vv=is_vv,
+ is_mentor=is_mentor,
+ widgets=get_widgets_for_course(role_key, is_uk, is_vv, is_mentor),
+ has_preview=has_preview(role_key),
+ session_to_continue_id=str(session_to_continue.id)
+ if session_to_continue
+ else None,
+ )
+ )
+
+ return course_configs
+
+
+@api_view(["GET"])
+def get_dashboard_config(request):
+ try:
+ course_sessions = get_course_sessions_with_roles_for_user(request.user) # noqa
+ course_configs = get_course_config(course_sessions)
+
+ return Response(
+ status=200,
+ data=[asdict(cc) for cc in course_configs],
+ )
+ except PermissionDenied as e:
+ raise e
+ except Exception as e:
+ logger.error(e, exc_info=True)
+ return Response({"error": str(e)}, status=404)