Add dashboard persons code for Berufsbildner

This commit is contained in:
Daniel Egger 2024-07-22 17:06:53 +02:00
parent 4b2fcc09ec
commit 61ee85668e
23 changed files with 215 additions and 108 deletions

View File

@ -16,6 +16,7 @@ const props = defineProps<{
courseSession: CourseSession;
learningContent: LearningContentAssignment | LearningContentEdoniqTest;
showTitle: boolean;
userSelectionIds?: string[];
}>();
log.debug("AssignmentSubmissionProgress created", stringifyParse(props));
@ -35,7 +36,8 @@ onMounted(async () => {
await loadAssignmentCompletionStatusData(
props.learningContent.content_assignment.id,
props.courseSession.id,
props.learningContent.id
props.learningContent.id,
props.userSelectionIds
);
state.submissionProgressStatusCount = {

View File

@ -7,6 +7,7 @@ const props = defineProps<{
assignmentsCompleted: number;
avgPassed: number;
courseSlug: string;
detailsLink?: string;
}>();
const progress = computed(() => {
@ -20,7 +21,7 @@ const progress = computed(() => {
<template>
<BaseBox
:details-link="`/statistic/${courseSlug}/assignment`"
:details-link="props.detailsLink || `/statistic/${courseSlug}/assignment`"
data-cy="dashboard.stats.assignments"
>
<template #title>{{ $t("a.Kompetenznachweis-Elemente") }}</template>

View File

@ -1,10 +1,10 @@
<script setup lang="ts">
import { computed, onMounted, type Ref, ref } from "vue";
import { computed, onMounted, ref } from "vue";
import {
type DashboardRoleKeyType,
fetchMentorCompetenceSummary,
} from "@/services/dashboard";
import type { AssignmentsStatisticsType, BaseStatisticsType } from "@/gql/graphql";
import type { BaseStatisticsType } from "@/gql/graphql";
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
import AssignmentSummaryBox from "@/components/dashboard/AssignmentSummaryBox.vue";
@ -43,6 +43,7 @@ onMounted(async () => {
:assignments-completed="assignmentStats.summary.completed_count"
:avg-passed="assignmentStats.summary.average_passed"
:course-slug="props.courseSlug"
:details-link="`/statistic/berufsbildner/${props.courseSlug}/assignment`"
/>
</div>
</div>

View File

@ -131,7 +131,7 @@ export function useCourseSessionDetailQuery(courSessionId?: string) {
function filterMembers(userSelectionIds: string[] | null = null) {
return (courseSessionDetail.value?.users ?? []).filter((u) => {
if (userSelectionIds) {
if (userSelectionIds && userSelectionIds.length > 0) {
return userSelectionIds.includes(u.user_id) && u.role === "MEMBER";
}
return u.role === "MEMBER";
@ -495,7 +495,7 @@ export function getVvRoleDisplay(role: DashboardPersonRoleType) {
switch (role) {
case "LEARNING_MENTOR":
return t("a.Lernbegleitung");
case "LEARNING_MENTEE":
case "PARTICIPANT":
return t("a.Teilnehmer");
case "EXPERT":
return t("a.Experte");
@ -512,7 +512,7 @@ export function getUkRoleDisplay(role: DashboardPersonRoleType) {
switch (role) {
case "LEARNING_MENTOR":
return t("a.Praxisbildner");
case "LEARNING_MENTEE":
case "PARTICIPANT":
return t("a.Teilnehmer");
case "EXPERT":
return t("a.Trainer");
@ -579,7 +579,7 @@ export function useDashboardPersonsDueDates(
return refDate >= dayjs().startOf("day");
});
// attach `LEARNING_MENTEE` to due dates for `LEARNING_MENTOR` persons
// attach `PARTICIPANT` to due dates for `LEARNING_MENTOR` persons
currentDueDates.value.forEach((dueDate) => {
if (dueDate.course_session.my_role === "LEARNING_MENTOR") {
dueDate.persons = dashboardPersons.value.filter((person) => {
@ -589,7 +589,7 @@ export function useDashboardPersonsDueDates(
.includes(dueDate.course_session.id)
) {
return person.course_sessions.some(
(cs) => cs.user_role === "LEARNING_MENTEE"
(cs) => cs.user_role === "PARTICIPANT"
);
}
});

View File

@ -26,7 +26,7 @@ const documents = {
"\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n": types.DashboardProgressDocument,
"\n query dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n }\n }\n": types.DashboardCourseDataDocument,
"\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n competence_certificate_title\n competence_certificate_id\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_evaluation_percent\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n": types.CourseStatisticsDocument,
"\n query mentorCourseStatistics($courseId: ID!, $agentRole: String!) {\n mentor_course_statistics(course_id: $courseId, agent_role: $agentRole) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n competence_certificate_id\n competence_certificate_title\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_evaluation_percent\n average_passed\n }\n }\n }\n }\n }\n": types.MentorCourseStatisticsDocument,
"\n query mentorCourseStatistics($courseId: ID!, $agentRole: String!) {\n mentor_course_statistics(course_id: $courseId, agent_role: $agentRole) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n competence_certificate_id\n competence_certificate_title\n details_url\n learning_content_id\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_evaluation_percent\n average_passed\n }\n }\n }\n }\n }\n": types.MentorCourseStatisticsDocument,
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $learningContentType: String!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n learning_content_type: $learningContentType\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument,
};
@ -99,7 +99,7 @@ export function graphql(source: "\n query courseStatistics($courseId: ID!) {\n
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query mentorCourseStatistics($courseId: ID!, $agentRole: String!) {\n mentor_course_statistics(course_id: $courseId, agent_role: $agentRole) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n competence_certificate_id\n competence_certificate_title\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_evaluation_percent\n average_passed\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query mentorCourseStatistics($courseId: ID!, $agentRole: String!) {\n mentor_course_statistics(course_id: $courseId, agent_role: $agentRole) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n competence_certificate_id\n competence_certificate_title\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_evaluation_percent\n average_passed\n }\n }\n }\n }\n }\n"];
export function graphql(source: "\n query mentorCourseStatistics($courseId: ID!, $agentRole: String!) {\n mentor_course_statistics(course_id: $courseId, agent_role: $agentRole) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n competence_certificate_id\n competence_certificate_title\n details_url\n learning_content_id\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_evaluation_percent\n average_passed\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query mentorCourseStatistics($courseId: ID!, $agentRole: String!) {\n mentor_course_statistics(course_id: $courseId, agent_role: $agentRole) {\n _id\n course_id\n course_title\n course_slug\n course_session_selection_ids\n user_selection_ids\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n total_passed\n total_failed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n competence_certificate_id\n competence_certificate_title\n details_url\n learning_content_id\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_evaluation_percent\n average_passed\n }\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/

File diff suppressed because one or more lines are too long

View File

@ -59,6 +59,7 @@ type AssignmentStatisticsRecordType {
competence_certificate_id: ID
competence_certificate_title: String
metrics: AssignmentCompletionMetricsType!
learning_content_id: ID!
details_url: String!
}

View File

@ -547,6 +547,7 @@ export const DASHBOARD_MENTOR_COMPETENCE_SUMMARY = graphql(`
competence_certificate_id
competence_certificate_title
details_url
learning_content_id
deadline
metrics {
_id

View File

@ -19,10 +19,13 @@ import { useTranslation } from "i18next-vue";
const { t } = useTranslation();
const props = defineProps<{
export interface Props {
courseSession: CourseSession;
learningContent: LearningContentAssignment | LearningContentEdoniqTest;
}>();
userSelectionIds?: string[];
}
const props = defineProps<Props>();
log.debug("AssignmentDetails created", stringifyParse(props));
@ -45,11 +48,13 @@ const isPraxisAssignment = computed(() => {
});
onMounted(async () => {
log.debug("AssignmentDetails mounted", props.learningContent, props.userSelectionIds);
const { gradedUsers, assignmentSubmittedUsers } =
await loadAssignmentCompletionStatusData(
props.learningContent.content_assignment.id,
props.courseSession.id,
props.learningContent.id
props.learningContent.id,
props.userSelectionIds ?? []
);
state.gradedUsers = gradedUsers;
state.assignmentSubmittedUsers = assignmentSubmittedUsers;
@ -111,13 +116,14 @@ function findUserPointsHtml(userId: string) {
:course-session="courseSession"
:learning-content="learningContent"
:show-title="false"
:user-selection-ids="props.userSelectionIds"
/>
</div>
<div v-if="courseSessionDetailResult.filterMembers().length" class="mt-6">
<ul>
<ItPersonRow
v-for="csu in courseSessionDetailResult.filterMembers()"
v-for="csu in courseSessionDetailResult.filterMembers(props.userSelectionIds)"
:key="csu.user_id"
:name="`${csu.first_name} ${csu.last_name}`"
:avatar-url="csu.avatar_url"

View File

@ -5,6 +5,7 @@ import * as log from "loglevel";
import { computed, onMounted } from "vue";
import type { LearningContentAssignment, LearningContentEdoniqTest } from "@/types";
import { useExpertCockpitPageData } from "@/pages/cockpit/cockpitPage/composables";
import { getPreviousRoute } from "@/router/history";
const props = defineProps<{
courseSlug: string;
@ -25,16 +26,22 @@ const lpQueryResult = useCourseData(props.courseSlug);
const learningContentAssignment = computed(() => {
return lpQueryResult.findLearningContent(props.assignmentId);
});
const previousRoute = getPreviousRoute();
const backRoute = computed(() => {
if (previousRoute?.path.endsWith("/assignment")) {
return previousRoute;
}
return `/course/${props.courseSlug}/cockpit`;
});
</script>
<template>
<div v-if="!loading" class="bg-gray-200">
<div class="container-large">
<nav class="py-4 pb-4">
<router-link
class="btn-text inline-flex items-center pl-0"
:to="`/course/${props.courseSlug}/cockpit`"
>
<router-link class="btn-text inline-flex items-center pl-0" :to="backRoute">
<it-icon-arrow-left />
<span>{{ $t("general.back") }}</span>
</router-link>

View File

@ -365,7 +365,7 @@ watch(selectedRegion, () => {
(['SUPERVISOR', 'EXPERT'].includes(cs.my_role) &&
cs.user_role === 'MEMBER') ||
(cs.my_role === 'LEARNING_MENTOR' &&
cs.user_role === 'LEARNING_MENTEE')
cs.user_role === 'PARTICIPANT')
"
>
<router-link

View File

@ -0,0 +1,59 @@
<script setup lang="ts">
import { useCourseData, useCurrentCourseSession } from "@/composables";
import AssignmentDetails from "@/pages/cockpit/assignmentsPage/AssignmentDetails.vue";
import * as log from "loglevel";
import { computed } from "vue";
import type { LearningContentAssignment, LearningContentEdoniqTest } from "@/types";
import { useExpertCockpitPageData } from "@/pages/cockpit/cockpitPage/composables";
import { getPreviousRoute } from "@/router/history";
const props = defineProps<{
courseSlug: string;
assignmentId: string;
agentRole: string;
participantUserIds: string[];
}>();
log.debug("AgentAssignmentDetail created", props.courseSlug, props.agentRole);
const courseSession = useCurrentCourseSession();
const { loading } = useExpertCockpitPageData(
props.courseSlug,
props.participantUserIds
);
const lpQueryResult = useCourseData(props.courseSlug);
const learningContentAssignment = computed(() => {
return lpQueryResult.findLearningContent(props.assignmentId);
});
</script>
<template>
<div v-if="!loading" class="bg-gray-200">
<div class="container-large">
<nav class="py-4 pb-4">
<router-link
class="btn-text inline-flex items-center pl-0"
:to="`/statistic/${props.agentRole}/${props.courseSlug}/assignment`"
>
<it-icon-arrow-left />
<span>{{ $t("general.back") }}</span>
</router-link>
</nav>
<main>
<div class="bg-white p-6">
<!-- prettier-ignore -->
<AssignmentDetails
v-if="learningContentAssignment"
:course-session="courseSession"
:learning-content="learningContentAssignment as (LearningContentAssignment | LearningContentEdoniqTest)"
:user-selection-ids="participantUserIds"
/>
</div>
</main>
</div>
</div>
</template>
<style scoped></style>

View File

@ -1,56 +1,45 @@
<script setup lang="ts">
import { useCourseData, useCurrentCourseSession } from "@/composables";
import AssignmentDetails from "@/pages/cockpit/assignmentsPage/AssignmentDetails.vue";
import { useCurrentCourseSession } from "@/composables";
import * as log from "loglevel";
import { computed, onMounted } from "vue";
import type { LearningContentAssignment, LearningContentEdoniqTest } from "@/types";
import { useExpertCockpitPageData } from "@/pages/cockpit/cockpitPage/composables";
import { onMounted, ref } from "vue";
import { fetchDashboardPersons } from "@/services/dashboard";
import AgentAssignmentDetail from "@/pages/dashboard/agentAssignment/AgentAssignmentDetail.vue";
const props = defineProps<{
courseSlug: string;
assignmentId: string;
agentRole: string;
}>();
log.debug("AgentAssignmentDetailPage created", props.courseSlug);
log.debug("AgentAssignmentDetailPage created", props.courseSlug, props.agentRole);
const courseSession = useCurrentCourseSession();
const { loading } = useExpertCockpitPageData(props.courseSlug);
const loading = ref(true);
const participantUserIds = ref<string[]>([]);
onMounted(async () => {
log.debug("AgentAssignmentDetailPage mounted");
});
log.debug("AgentAssignmentDetailPage mounted", courseSession);
const lpQueryResult = useCourseData(props.courseSlug);
const learningContentAssignment = computed(() => {
return lpQueryResult.findLearningContent(props.assignmentId);
const personData = await fetchDashboardPersons("default");
const participants = personData?.filter((p) => {
return p.course_sessions.find(
(cs) => cs.id === courseSession.value.id && cs.my_role === "BERUFSBILDNER"
);
});
participantUserIds.value = participants?.map((p) => p.user_id);
loading.value = false;
});
</script>
<template>
<div v-if="!loading" class="bg-gray-200">
<div class="container-large">
<nav class="py-4 pb-4">
<router-link
class="btn-text inline-flex items-center pl-0"
:to="`/course/${props.courseSlug}/cockpit`"
>
<it-icon-arrow-left />
<span>{{ $t("general.back") }}</span>
</router-link>
</nav>
<main>
<div class="bg-white p-6">
<!-- prettier-ignore -->
<AssignmentDetails
v-if="learningContentAssignment"
:course-session="courseSession"
:learning-content="learningContentAssignment as (LearningContentAssignment | LearningContentEdoniqTest)"
/>
</div>
</main>
</div>
</div>
<AgentAssignmentDetail
v-if="participantUserIds.length"
:assignment-id="props.assignmentId"
:agent-role="props.agentRole"
:course-slug="props.courseSlug"
:participant-user-ids="participantUserIds"
/>
</template>
<style scoped></style>

View File

@ -72,6 +72,7 @@ onMounted(async () => {
:course-statistics="agentAssignmentData"
:course-session-name="courseSessionName"
:circle-meta="circleMeta"
:detail-base-url="`/statistic/${props.agentRole}/${props.courseSlug}/assignment/`"
></AssignmentList>
</div>
</div>

View File

@ -19,6 +19,7 @@ const props = defineProps<{
courseStatistics: BaseStatisticsType;
courseSessionName: (sessionId: string) => string;
circleMeta: (circleId: string) => StatisticsCircleDataType;
detailBaseUrl?: string;
}>();
const statisticFilter: Ref<typeof StatisticFilterList | null> = ref(null);
@ -51,6 +52,14 @@ async function exportData() {
const filteredItems = statisticFilter.value.getFilteredItems();
await exportDataAsXls(filteredItems, exportCompetenceElements, userStore.language);
}
const itemDetailUrl = (item: AssignmentStatisticsRecordType) => {
if (props.detailBaseUrl) {
return `${props.detailBaseUrl}${item.learning_content_id}?courseSessionId=${item.course_session_id}`;
}
return item.details_url;
};
</script>
<template>
@ -114,8 +123,7 @@ async function exportData() {
></ItProgress>
<router-link
class="underline"
target="_blank"
:to="(item as AssignmentStatisticsRecordType).details_url"
:to="itemDetailUrl(item as AssignmentStatisticsRecordType)"
>
{{ $t("a.Details anschauen") }}
</router-link>

View File

@ -1,5 +1,5 @@
import { useCourseSessionDetailQuery } from "@/composables";
import { itGet } from "@/fetchHelpers";
import { itGet, itPost } from "@/fetchHelpers";
import type {
Assignment,
AssignmentCompletion,
@ -19,12 +19,16 @@ export interface GradedUser {
export async function loadAssignmentCompletionStatusData(
assignmentId: string,
courseSessionId: string,
learningContentId: string
learningContentId: string,
userSelectionIds: string[] | undefined = undefined
) {
const courseSessionDetailResult = useCourseSessionDetailQuery();
const assignmentCompletionData = (await itGet(
`/api/assignment/${assignmentId}/${courseSessionId}/status/`
const assignmentCompletionData = (await itPost(
`/api/assignment/${assignmentId}/${courseSessionId}/status/`,
{
user_selection_ids: userSelectionIds ?? [],
}
)) as UserAssignmentCompletionStatus[];
const members = courseSessionDetailResult.filterMembers();

View File

@ -26,7 +26,8 @@ export type DashboardPersonRoleType =
| "EXPERT"
| "MEMBER"
| "LEARNING_MENTOR"
| "LEARNING_MENTEE";
| "BERUFSBILDNER"
| "PARTICIPANT";
export type DashboardRoleKeyType =
| "Supervisor"

View File

@ -101,7 +101,7 @@ class AssignmentCompletionMutation(graphene.Mutation):
):
if not can_evaluate_assignments(
evaluation_user=info.context.user,
assignment_user_id=assignment_user_id,
assignment_user_ids=[assignment_user_id],
course_session_id=course_session_id,
):
raise PermissionDenied()

View File

@ -139,7 +139,7 @@ def resolve_assignment_completion(
if str(assignment_user_id) == str(info.context.user.id) or can_evaluate_assignments(
evaluation_user=info.context.user,
assignment_user_id=assignment_user_id,
assignment_user_ids=[assignment_user_id],
course_session_id=course_session_id,
):
course_id = CourseSession.objects.get(id=course_session_id).course_id

View File

@ -9,14 +9,23 @@ from vbv_lernwelt.iam.permissions import can_evaluate_assignments
logger = structlog.get_logger(__name__)
@api_view(["GET"])
@api_view(["GET", "POST"])
def request_assignment_completion_status(request, assignment_id, course_session_id):
# TODO quickfix before GraphQL...
if can_evaluate_assignments(request.user, course_session_id):
user_selection_ids = request.data.get("user_selection_ids", [])
if can_evaluate_assignments(
request.user, course_session_id, assignment_user_ids=user_selection_ids
):
qs = AssignmentCompletion.objects.filter(
course_session_id=course_session_id,
assignment_id=assignment_id,
).values(
)
if len(user_selection_ids) > 0:
qs = qs.filter(assignment_user_id__in=user_selection_ids)
values = qs.values(
"id",
"assignment_user_id",
"completion_status",
@ -28,7 +37,7 @@ def request_assignment_completion_status(request, assignment_id, course_session_
)
# Convert the learning_content_page_id to a string
data = list(qs) # Evaluate the queryset
data = list(values) # Evaluate the queryset
for item in data:
if item["evaluation_points"] is not None:
# only `evaluation_points_final` is relevant for the frontend

View File

@ -41,6 +41,7 @@ class AssignmentStatisticsRecordType(graphene.ObjectType):
competence_certificate_id = graphene.ID()
competence_certificate_title = graphene.String()
metrics = graphene.Field(AssignmentCompletionMetricsType, required=True)
learning_content_id = graphene.ID(required=True)
details_url = graphene.String(required=True)
@ -101,13 +102,14 @@ def get_assignment_completion_metrics(
user_selection_ids: List[str] | None,
urql_id_postfix: str = "",
) -> AssignmentCompletionMetricsType:
csu_qs = CourseSessionUser.objects.filter(
course_session_id=course_session.id, role=CourseSessionUser.Role.MEMBER
)
if user_selection_ids:
course_session_users = user_selection_ids
else:
course_session_users = CourseSessionUser.objects.filter(
course_session=course_session,
role=CourseSessionUser.Role.MEMBER,
).values_list("user", flat=True)
csu_qs = csu_qs.filter(user_id__in=user_selection_ids)
course_session_users = csu_qs.values_list("user", flat=True)
assignment_completions = AssignmentCompletion.objects.filter(
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
@ -165,17 +167,16 @@ def create_record(
return AssignmentStatisticsRecordType(
# make sure it's unique, across all types of assignments!
_id=f"{course_session_assignment._meta.model_name}#{course_session_assignment.id}@{urql_id_postfix}",
# noqa
_id=f"{course_session_assignment._meta.model_name}#{course_session_assignment.id}@{urql_id_postfix}", # noqa
course_session_id=str(course_session_assignment.course_session.id), # noqa
circle_id=learning_content.get_circle().id, # noqa
course_session_assignment_id=str(course_session_assignment.id), # noqa
generation=course_session_assignment.course_session.generation, # noqa
assignment_type_translation_key=due_date.assignment_type_translation_key,
assignment_type_translation_key=due_date.assignment_type_translation_key, # noqa
competence_certificate_id=str(competence_certificate.id), # noqa
competence_certificate_title=competence_certificate.title, # noqa
# noqa
assignment_title=learning_content.content_assignment.title, # noqa
learning_content_id=str(learning_content.id), # noqa
metrics=get_assignment_completion_metrics( # noqa
course_session=course_session_assignment.course_session, # noqa
assignment=learning_content.content_assignment, # noqa

View File

@ -200,28 +200,34 @@ def _create_person_list_with_roles(user):
# add persons where request.user is mentor
for cs in course_sessions:
def _add_agent_relation(my_role, user_role):
course_session_entry = _create_course_session_dict(cs, my_role, user_role)
participant_user = relation.participant.user
if participant_user.id not in result_persons:
person_data = create_user_dict(participant_user)
person_data["course_sessions"] = [course_session_entry]
result_persons[participant_user] = person_data
else:
# user is already in result_persons
result_persons[participant_user]["course_sessions"].append(
course_session_entry
)
if "LEARNING_MENTOR" in cs.roles:
for relation in AgentParticipantRelation.objects.filter(
agent=user, participant__course_session_id=cs.id
agent=user, participant__course_session_id=cs.id, role="LEARNING_MENTOR"
):
course_session_entry = _create_course_session_dict(
cs,
"LEARNING_MENTOR",
"LEARNING_MENTEE",
)
participant_user = relation.participant.user
_add_agent_relation("LEARNING_MENTOR", "PARTICIPANT")
if participant_user.id not in result_persons:
person_data = create_user_dict(participant_user)
person_data["course_sessions"] = [course_session_entry]
result_persons[participant_user] = person_data
else:
# user is already in result_persons
result_persons[participant_user]["course_sessions"].append(
course_session_entry
)
if "BERUFSBILDNER" in cs.roles:
for relation in AgentParticipantRelation.objects.filter(
agent=user, participant__course_session_id=cs.id, role="BERUFSBILDNER"
):
_add_agent_relation("BERUFSBILDNER", "PARTICIPANT")
# add persons where request.user is mentee
# add persons where request.user is lerning mentee
mentor_relation_qs = AgentParticipantRelation.objects.filter(
participant__user=user,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
@ -230,7 +236,7 @@ def _create_person_list_with_roles(user):
cs = mentor_relation.participant.course_session
course_session_entry = _create_course_session_dict(
cs,
"LEARNING_MENTEE",
"PARTICIPANT",
"LEARNING_MENTOR",
)

View File

@ -97,7 +97,6 @@ def is_agent_for_user(
qs = AgentParticipantRelation.objects.filter(
agent=agent,
participant=csu,
role=AgentParticipantRoleType.LEARNING_MENTOR.value,
)
if roles and len(roles) > 0:
@ -159,7 +158,9 @@ def is_course_session_member(user, course_session_id: int | None = None):
def can_evaluate_assignments(
evaluation_user: User, course_session_id: int, assignment_user_id: str | None = None
evaluation_user: User,
course_session_id: int,
assignment_user_ids: list[str] | None = None,
):
if evaluation_user.is_superuser:
return True
@ -167,20 +168,28 @@ def can_evaluate_assignments(
is_supervisor = CourseSessionGroup.objects.filter(
supervisor=evaluation_user, course_session__id=course_session_id
).exists()
if is_supervisor:
return True
is_expert = CourseSessionUser.objects.filter(
course_session_id=course_session_id,
user=evaluation_user,
role=CourseSessionUser.Role.EXPERT,
).exists()
if is_expert:
return True
is_mentor = is_learning_mentor_for_user(
mentor=evaluation_user,
participant_user_id=assignment_user_id,
course_session_id=course_session_id,
)
return is_supervisor or is_expert or is_mentor
if assignment_user_ids and len(assignment_user_ids) > 0:
is_agent_array = []
for assignment_user_id in assignment_user_ids:
is_agent_array.append(
is_agent_for_user(
agent=evaluation_user,
participant_user_id=assignment_user_id,
course_session_id=course_session_id,
)
)
return all(is_agent_array)
def course_sessions_for_user_qs(user):