VBV-673: Praxisbildner Übersicht KN
This commit is contained in:
parent
45be3ae296
commit
f4be4e2418
|
|
@ -157,6 +157,7 @@ function hasActionButton(): boolean {
|
|||
<MentorOpenTasksCount
|
||||
v-if="hasWidget('MentorTasksWidget')"
|
||||
:course-id="courseConfig.course_id"
|
||||
:course-slug="courseSlug"
|
||||
/>
|
||||
<MentorMenteeCount
|
||||
v-if="hasWidget('MentorPersonWidget')"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import BaseBox from "@/components/dashboard/BaseBox.vue";
|
|||
|
||||
const props = defineProps<{
|
||||
courseId: string;
|
||||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
const openTaskCount: Ref<number> = ref(0);
|
||||
|
|
@ -18,7 +19,10 @@ onMounted(async () => {
|
|||
|
||||
<template>
|
||||
<div class="w-[325px]">
|
||||
<BaseBox :details-link="'foo'" data-cy="dashboard.mentor.competenceSummary">
|
||||
<BaseBox
|
||||
:details-link="`/course/${props.courseSlug}/learning-mentor/tasks`"
|
||||
data-cy="dashboard.mentor.competenceSummary"
|
||||
>
|
||||
<template #title>{{ $t("Zu erledigen") }}</template>
|
||||
<template #content>
|
||||
<div class="flex flex-row space-x-3 bg-white pb-6">
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import type {
|
|||
CourseCompletionStatus,
|
||||
CourseSession,
|
||||
CourseSessionDetail,
|
||||
DashboardPersonsPageMode,
|
||||
LearningContentWithCompletion,
|
||||
LearningMentor,
|
||||
LearningPathType,
|
||||
|
|
@ -542,7 +543,7 @@ export function getUkRoleDisplay(role: DashboardPersonRoleType) {
|
|||
}
|
||||
}
|
||||
|
||||
export function useDashboardPersons() {
|
||||
export function useDashboardPersons(mode: DashboardPersonsPageMode = "default") {
|
||||
const dashboardPersons = ref<DashboardPersonType[]>([]);
|
||||
const dashboardDueDates = ref<DashboardDueDate[]>([]);
|
||||
const loading = ref(false);
|
||||
|
|
@ -554,7 +555,7 @@ export function useDashboardPersons() {
|
|||
loading.value = true;
|
||||
try {
|
||||
const [persons, dueDates] = await Promise.all([
|
||||
fetchDashboardPersons(),
|
||||
fetchDashboardPersons(mode),
|
||||
fetchDashboardDueDates(),
|
||||
]);
|
||||
dashboardPersons.value = persons;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,18 @@ import { useTranslation } from "i18next-vue";
|
|||
import _ from "lodash";
|
||||
import type { DashboardPersonCourseSessionType } from "@/services/dashboard";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
import type { DashboardPersonsPageMode } from "@/types";
|
||||
|
||||
log.debug("DashboardPersonsPage created");
|
||||
|
||||
export interface Props {
|
||||
mode?: DashboardPersonsPageMode;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
mode: "default",
|
||||
});
|
||||
|
||||
const UNFILTERED = Number.MAX_SAFE_INTEGER.toString();
|
||||
|
||||
type MenuItem = {
|
||||
|
|
@ -20,7 +29,7 @@ type MenuItem = {
|
|||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { loading, dashboardPersons } = useDashboardPersons();
|
||||
const { loading, dashboardPersons } = useDashboardPersons(props.mode);
|
||||
|
||||
const courses = computed(() => {
|
||||
return [
|
||||
|
|
@ -323,15 +332,33 @@ watch(selectedRegion, () => {
|
|||
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 v-if="props.mode === 'default'" 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 v-if="props.mode === 'competenceMetrics'" class="md:w-1/3">
|
||||
<div
|
||||
v-if="cs.competence_metrics?.passed_count || 0 > 0"
|
||||
class="my-2 w-fit rounded-md bg-green-200 px-2.5 py-0.5"
|
||||
>
|
||||
{{ $t("a.Bestanden") }}:
|
||||
{{ cs.competence_metrics?.passed_count }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="props.mode === 'default'" class="md:w-1/4">
|
||||
{{ personRoleDisplayValue(cs) }}
|
||||
</div>
|
||||
<div v-else-if="props.mode === 'competenceMetrics'" class="md:w-1/3">
|
||||
<div
|
||||
v-if="cs.competence_metrics?.failed_count || 0 > 0"
|
||||
class="my-2 w-fit rounded-md bg-error-red-200 px-2.5 py-0.5"
|
||||
>
|
||||
{{ $t("a.Nicht Bestanden") }}:
|
||||
{{ cs.competence_metrics?.failed_count }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:w-1/4 md:text-right">
|
||||
<div
|
||||
v-if="
|
||||
|
|
|
|||
|
|
@ -64,6 +64,11 @@ const router = createRouter({
|
|||
path: "/dashboard/persons",
|
||||
component: () => import("@/pages/dashboard/DashboardPersonsPage.vue"),
|
||||
},
|
||||
{
|
||||
path: "/dashboard/persons-competence",
|
||||
component: () => import("@/pages/dashboard/DashboardPersonsPage.vue"),
|
||||
props: { mode: "competenceMetrics" },
|
||||
},
|
||||
{
|
||||
path: "/dashboard/due-dates",
|
||||
component: () => import("@/pages/dashboard/DashboardDueDatesPage.vue"),
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import type {
|
|||
CourseStatisticsType,
|
||||
DashboardConfigType,
|
||||
} from "@/gql/graphql";
|
||||
import type { DueDate } from "@/types";
|
||||
import type { DashboardPersonsPageMode, DueDate } from "@/types";
|
||||
|
||||
export type DashboardPersonRoleType =
|
||||
| "SUPERVISOR"
|
||||
|
|
@ -52,6 +52,10 @@ export type DashboardPersonCourseSessionType = {
|
|||
my_role_display: string;
|
||||
is_uk: boolean;
|
||||
is_vv: boolean;
|
||||
competence_metrics?: {
|
||||
passed_count: number;
|
||||
failed_count: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type DashboardPersonType = {
|
||||
|
|
@ -62,6 +66,10 @@ export type DashboardPersonType = {
|
|||
course_sessions: DashboardPersonCourseSessionType[];
|
||||
avatar_url: string;
|
||||
avatar_url_small: string;
|
||||
competence_metrics?: {
|
||||
passed_count: number;
|
||||
failed_count: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type DashboardCourseConfigType = {
|
||||
|
|
@ -153,8 +161,12 @@ export const fetchMentorCompetenceSummary = async (
|
|||
}
|
||||
};
|
||||
|
||||
export async function fetchDashboardPersons() {
|
||||
return await itGetCached<DashboardPersonType[]>("/api/dashboard/persons/");
|
||||
export async function fetchDashboardPersons(mode: DashboardPersonsPageMode) {
|
||||
let url = "/api/dashboard/persons/";
|
||||
if (mode === "competenceMetrics") {
|
||||
url += "?with_competence_metrics=true";
|
||||
}
|
||||
return await itGetCached<DashboardPersonType[]>(url);
|
||||
}
|
||||
|
||||
export async function fetchDashboardDueDates() {
|
||||
|
|
|
|||
|
|
@ -610,3 +610,5 @@ export type User = {
|
|||
course_session_experts: any[];
|
||||
language: string;
|
||||
};
|
||||
|
||||
export type DashboardPersonsPageMode = "default" | "competenceMetrics";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
from vbv_lernwelt.assignment.models import AssignmentType
|
||||
from vbv_lernwelt.course_session.models import (
|
||||
CourseSessionAssignment,
|
||||
CourseSessionEdoniqTest,
|
||||
)
|
||||
|
||||
|
||||
def query_competence_course_session_assignments(course_session_ids, circle_ids=None):
|
||||
if circle_ids is None:
|
||||
circle_ids = []
|
||||
|
||||
result = []
|
||||
|
||||
for csa in CourseSessionAssignment.objects.filter(
|
||||
course_session_id__in=course_session_ids,
|
||||
learning_content__content_assignment__assignment_type__in=[
|
||||
AssignmentType.CASEWORK.value,
|
||||
],
|
||||
learning_content__content_assignment__competence_certificate__isnull=False,
|
||||
):
|
||||
if circle_ids and csa.learning_content.get_circle().id not in circle_ids:
|
||||
continue
|
||||
result.append(csa)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def query_competence_course_session_edoniq_tests(course_session_ids, circle_ids=None):
|
||||
if circle_ids is None:
|
||||
circle_ids = []
|
||||
|
||||
result = []
|
||||
|
||||
for cset in CourseSessionEdoniqTest.objects.filter(
|
||||
course_session_id__in=course_session_ids,
|
||||
learning_content__content_assignment__competence_certificate__isnull=False,
|
||||
):
|
||||
if circle_ids and cset.learning_content.get_circle().id not in circle_ids:
|
||||
continue
|
||||
result.append(cset)
|
||||
|
||||
return result
|
||||
|
|
@ -9,6 +9,10 @@ from vbv_lernwelt.assignment.models import (
|
|||
AssignmentCompletionStatus,
|
||||
AssignmentType,
|
||||
)
|
||||
from vbv_lernwelt.competence.services import (
|
||||
query_competence_course_session_assignments,
|
||||
query_competence_course_session_edoniq_tests,
|
||||
)
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.course_session.models import (
|
||||
CourseSessionAssignment,
|
||||
|
|
@ -169,26 +173,15 @@ def assignments(
|
|||
records: List[AssignmentStatisticsRecordType] = []
|
||||
|
||||
for course_session in course_sessions:
|
||||
for csa in CourseSessionAssignment.objects.filter(
|
||||
course_session=course_session,
|
||||
learning_content__content_assignment__assignment_type__in=[
|
||||
AssignmentType.CASEWORK.value,
|
||||
],
|
||||
learning_content__content_assignment__competence_certificate__isnull=False,
|
||||
for csa in query_competence_course_session_assignments(
|
||||
[course_session.id], circle_ids
|
||||
):
|
||||
if circle_ids and csa.learning_content.get_circle().id not in circle_ids:
|
||||
continue
|
||||
record = create_record(
|
||||
course_session_assignment=csa, user_selection_ids=user_selection_ids
|
||||
)
|
||||
record = create_record(csa, user_selection_ids)
|
||||
records.append(record)
|
||||
|
||||
for cset in CourseSessionEdoniqTest.objects.filter(
|
||||
course_session=course_session,
|
||||
learning_content__content_assignment__competence_certificate__isnull=False,
|
||||
for cset in query_competence_course_session_edoniq_tests(
|
||||
[course_session.id], circle_ids
|
||||
):
|
||||
if circle_ids and cset.learning_content.get_circle().id not in circle_ids:
|
||||
continue
|
||||
record = create_record(
|
||||
course_session_assignment=cset, user_selection_ids=user_selection_ids
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ from vbv_lernwelt.assignment.models import (
|
|||
AssignmentCompletion,
|
||||
AssignmentCompletionStatus,
|
||||
)
|
||||
from vbv_lernwelt.competence.services import (
|
||||
query_competence_course_session_assignments,
|
||||
query_competence_course_session_edoniq_tests,
|
||||
)
|
||||
from vbv_lernwelt.core.models import User
|
||||
from vbv_lernwelt.course.models import (
|
||||
CourseConfiguration,
|
||||
|
|
@ -142,8 +146,7 @@ def _create_course_session_dict(course_session_object, my_role, user_role):
|
|||
}
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
def get_dashboard_persons(request):
|
||||
def _create_person_list_with_roles(user):
|
||||
def create_user_dict(user_object):
|
||||
return {
|
||||
"user_id": user_object.id,
|
||||
|
|
@ -154,8 +157,7 @@ def get_dashboard_persons(request):
|
|||
"avatar_url": user_object.avatar_url,
|
||||
}
|
||||
|
||||
try:
|
||||
course_sessions = get_course_sessions_with_roles_for_user(request.user)
|
||||
course_sessions = get_course_sessions_with_roles_for_user(user)
|
||||
|
||||
result_persons = {}
|
||||
for cs in course_sessions:
|
||||
|
|
@ -175,7 +177,7 @@ def get_dashboard_persons(request):
|
|||
for cs in course_sessions:
|
||||
if "LEARNING_MENTOR" in cs.roles:
|
||||
lm = LearningMentor.objects.filter(
|
||||
mentor=request.user, course_session=cs.id
|
||||
mentor=user, course_session=cs.id
|
||||
).first()
|
||||
|
||||
for participant in lm.participants.all():
|
||||
|
|
@ -197,7 +199,7 @@ def get_dashboard_persons(request):
|
|||
|
||||
# add persons where request.user is mentee
|
||||
mentor_relation_qs = LearningMentor.objects.filter(
|
||||
participants__user=request.user
|
||||
participants__user=user
|
||||
).prefetch_related("mentor", "course_session")
|
||||
for mentor_relation in mentor_relation_qs:
|
||||
cs = mentor_relation.course_session
|
||||
|
|
@ -217,9 +219,59 @@ def get_dashboard_persons(request):
|
|||
course_session_entry
|
||||
)
|
||||
|
||||
return result_persons.values()
|
||||
|
||||
|
||||
def _persons_list_add_competence_metrics(persons):
|
||||
course_session_ids = {cs["id"] for p in persons for cs in p["course_sessions"]}
|
||||
competence_assignments = query_competence_course_session_assignments(
|
||||
course_session_ids
|
||||
) + query_competence_course_session_edoniq_tests(course_session_ids)
|
||||
assignment_ids = {
|
||||
a.learning_content.content_assignment.id for a in competence_assignments
|
||||
}
|
||||
|
||||
for p in persons:
|
||||
passed_count = 0
|
||||
failed_count = 0
|
||||
for cs in p["course_sessions"]:
|
||||
evaluation_results = AssignmentCompletion.objects.filter(
|
||||
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
|
||||
assignment_user_id=p.get("user_id"),
|
||||
course_session=cs.get("id"),
|
||||
assignment_id__in=assignment_ids,
|
||||
)
|
||||
cs_passed_count = len(
|
||||
[ac for ac in evaluation_results if ac.evaluation_passed]
|
||||
)
|
||||
cs_failed_count = len(evaluation_results) - cs_passed_count
|
||||
cs["competence_metrics"] = {
|
||||
"passed_count": cs_passed_count,
|
||||
"failed_count": cs_failed_count,
|
||||
}
|
||||
passed_count += len(
|
||||
[ac for ac in evaluation_results if ac.evaluation_passed]
|
||||
)
|
||||
failed_count += len(evaluation_results) - passed_count
|
||||
p["competence_metrics"] = {
|
||||
"passed_count": passed_count,
|
||||
"failed_count": failed_count,
|
||||
}
|
||||
|
||||
return persons
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
def get_dashboard_persons(request):
|
||||
try:
|
||||
persons = list(_create_person_list_with_roles(request.user))
|
||||
|
||||
if request.GET.get("with_competence_metrics", "") == "true":
|
||||
persons = _persons_list_add_competence_metrics(persons)
|
||||
|
||||
return Response(
|
||||
status=200,
|
||||
data=list(result_persons.values()),
|
||||
data=list(persons),
|
||||
)
|
||||
except PermissionDenied as e:
|
||||
raise e
|
||||
|
|
|
|||
Loading…
Reference in New Issue