Merged in feature/bugfix-attendance-check-dropdown (pull request #200)
Feature/bugfix attendance check dropdown
This commit is contained in:
commit
2db7a5186f
|
|
@ -39,7 +39,8 @@ onMounted(async () => {
|
|||
const { gradedUsers, assignmentSubmittedUsers } =
|
||||
await loadAssignmentCompletionStatusData(
|
||||
props.learningContentAssignment.content_assignment_id,
|
||||
props.courseSession.id
|
||||
props.courseSession.id,
|
||||
props.learningContentAssignment.id
|
||||
);
|
||||
state.gradedUsers = gradedUsers;
|
||||
state.assignmentSubmittedUsers = assignmentSubmittedUsers;
|
||||
|
|
|
|||
|
|
@ -5,14 +5,15 @@ import ItPersonRow from "@/components/ui/ItPersonRow.vue";
|
|||
import { useCurrentCourseSession } from "@/composables";
|
||||
import type { AttendanceUserStatus } from "@/gql/graphql";
|
||||
import { ATTENDANCE_CHECK_MUTATION } from "@/graphql/mutations";
|
||||
import { ATTENDANCE_CHECK_QUERY } from "@/graphql/queries";
|
||||
import { useCockpitStore } from "@/stores/cockpit";
|
||||
import type { DropdownSelectable } from "@/types";
|
||||
import { useMutation, useQuery } from "@urql/vue";
|
||||
import { useMutation } from "@urql/vue";
|
||||
import dayjs from "dayjs";
|
||||
import log from "loglevel";
|
||||
import { computed, reactive, watch } from "vue";
|
||||
import { computed, onMounted, reactive, watch } from "vue";
|
||||
import { useTranslation } from "i18next-vue";
|
||||
import { ATTENDANCE_CHECK_QUERY } from "@/graphql/queries";
|
||||
import { graphqlClient } from "@/graphql/client";
|
||||
|
||||
const { t } = useTranslation();
|
||||
const cockpitStore = useCockpitStore();
|
||||
|
|
@ -28,17 +29,13 @@ const presenceCoursesDropdownOptions = computed(() => {
|
|||
(attendanceCourse) =>
|
||||
({
|
||||
id: attendanceCourse.id,
|
||||
name: `${t("Präsenzkurs")} ${dayjs(attendanceCourse.start).format(
|
||||
"DD.MM.YYYY"
|
||||
)}`,
|
||||
name: `${t("Präsenzkurs")} ${attendanceCourse.circle_title} ${dayjs(
|
||||
attendanceCourse.start
|
||||
).format("DD.MM.YYYY")}`,
|
||||
} as DropdownSelectable)
|
||||
);
|
||||
});
|
||||
|
||||
const attendanceCourseSelected = computed(
|
||||
() => state.attendanceCourseSelected.id != "-1"
|
||||
);
|
||||
|
||||
const state = reactive({
|
||||
userPresence: new Map<string, boolean>(),
|
||||
attendanceCourseSelected: presenceCoursesDropdownOptions.value[0],
|
||||
|
|
@ -46,13 +43,11 @@ const state = reactive({
|
|||
attendanceSaved: false,
|
||||
});
|
||||
|
||||
const attendanceQuery = useQuery({
|
||||
query: ATTENDANCE_CHECK_QUERY,
|
||||
pause: true,
|
||||
variables: {
|
||||
courseSessionId: state.attendanceCourseSelected.id.toString(),
|
||||
},
|
||||
});
|
||||
function resetState() {
|
||||
state.userPresence = new Map<string, boolean>();
|
||||
state.disclaimerConfirmed = false;
|
||||
state.attendanceSaved = false;
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
type UserPresence = {
|
||||
|
|
@ -79,9 +74,19 @@ const onSubmit = async () => {
|
|||
};
|
||||
|
||||
const loadAttendanceData = async () => {
|
||||
const res = await attendanceQuery.executeQuery();
|
||||
resetState();
|
||||
// with changing variables `useQuery` does not seem to work correctly
|
||||
const res = await graphqlClient.query(
|
||||
ATTENDANCE_CHECK_QUERY,
|
||||
{
|
||||
courseSessionId: state.attendanceCourseSelected.id.toString(),
|
||||
},
|
||||
{
|
||||
requestPolicy: "network-only",
|
||||
}
|
||||
);
|
||||
const attendanceUserList =
|
||||
res?.data?.value?.course_session_attendance_course?.attendance_user_list ?? [];
|
||||
res.data?.course_session_attendance_course?.attendance_user_list ?? [];
|
||||
for (const user of attendanceUserList) {
|
||||
if (!user) continue;
|
||||
state.userPresence.set(user.user_id.toString(), user.status === "PRESENT");
|
||||
|
|
@ -91,15 +96,23 @@ const loadAttendanceData = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
loadAttendanceData();
|
||||
watch(state.attendanceCourseSelected, () => {
|
||||
onMounted(() => {
|
||||
log.debug("AttendanceCheckPage mounted");
|
||||
loadAttendanceData();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => state.attendanceCourseSelected,
|
||||
() => {
|
||||
log.debug("attendanceCourseSelected changed", state.attendanceCourseSelected);
|
||||
loadAttendanceData();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-200">
|
||||
<div class="container-large">
|
||||
<div v-if="courseSession" class="container-large">
|
||||
<nav class="py-4 pb-4">
|
||||
<router-link
|
||||
class="btn-text inline-flex items-center pl-0"
|
||||
|
|
@ -117,7 +130,6 @@ watch(state.attendanceCourseSelected, () => {
|
|||
></ItDropdownSelect>
|
||||
<div v-if="!state.attendanceSaved" class="flex flex-row items-center">
|
||||
<ItCheckbox
|
||||
:disabled="!attendanceCourseSelected"
|
||||
:checkbox-item="{
|
||||
value: true,
|
||||
checked: state.disclaimerConfirmed,
|
||||
|
|
@ -158,7 +170,7 @@ watch(state.attendanceCourseSelected, () => {
|
|||
>
|
||||
<template #leading>
|
||||
<ItCheckbox
|
||||
:disabled="!attendanceCourseSelected || state.attendanceSaved"
|
||||
:disabled="state.attendanceSaved"
|
||||
:checkbox-item="{
|
||||
value: true,
|
||||
checked: state.userPresence.get(csu.user_id.toString()) as boolean,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ onMounted(async () => {
|
|||
const { assignmentSubmittedUsers, gradedUsers, total } =
|
||||
await loadAssignmentCompletionStatusData(
|
||||
props.learningContentAssignment.content_assignment_id,
|
||||
props.courseSession.id
|
||||
props.courseSession.id,
|
||||
props.learningContentAssignment.id
|
||||
);
|
||||
|
||||
state.submissionProgressStatusCount = {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ function setActiveClasses(translationKey: string) {
|
|||
</div>
|
||||
<div>
|
||||
<router-link
|
||||
:to="`/course/${props.courseSlug}/cockpit/attendanceCheck`"
|
||||
:to="`/course/${props.courseSlug}/cockpit/attendance`"
|
||||
class="btn-secondary min-w-min"
|
||||
>
|
||||
{{ $t("Anwesenheit prüfen") }}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ const router = createRouter({
|
|||
props: true,
|
||||
},
|
||||
{
|
||||
path: "attendanceCheck",
|
||||
path: "attendance",
|
||||
component: () =>
|
||||
import("@/pages/cockpit/attendanceCheckPage/AttendanceCheckPage.vue"),
|
||||
props: true,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ export function calcLearningContentAssignments(learningPath?: LearningPath) {
|
|||
|
||||
export async function loadAssignmentCompletionStatusData(
|
||||
assignmentId: number,
|
||||
courseSessionId: number
|
||||
courseSessionId: number,
|
||||
learningContentId: number
|
||||
) {
|
||||
const cockpitStore = useCockpitStore();
|
||||
|
||||
|
|
@ -46,7 +47,9 @@ export async function loadAssignmentCompletionStatusData(
|
|||
const assignmentSubmittedUsers: CourseSessionUser[] = [];
|
||||
for (const csu of courseSessionUsers) {
|
||||
const userAssignmentStatus = assignmentCompletionData.find(
|
||||
(s) => s.assignment_user_id === csu.user_id
|
||||
(s) =>
|
||||
s.assignment_user_id === csu.user_id &&
|
||||
s.learning_content_page_id === learningContentId
|
||||
);
|
||||
if (
|
||||
userAssignmentStatus?.completion_status === "SUBMITTED" ||
|
||||
|
|
|
|||
|
|
@ -425,6 +425,7 @@ export interface CourseSessionAttendanceCourse {
|
|||
location: string;
|
||||
trainer: string;
|
||||
due_date_id: number;
|
||||
circle_title: string;
|
||||
}
|
||||
|
||||
export interface CourseSessionAssignment {
|
||||
|
|
@ -568,6 +569,7 @@ export interface UserAssignmentCompletionStatus {
|
|||
assignment_user_id: string;
|
||||
completion_status: AssignmentCompletionStatus;
|
||||
evaluation_grade: number | null;
|
||||
learning_content_page_id: number;
|
||||
}
|
||||
|
||||
export type DueDate = {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,13 @@ def request_assignment_completion_status(request, assignment_id, course_session_
|
|||
qs = AssignmentCompletion.objects.filter(
|
||||
course_session_id=course_session_id,
|
||||
assignment_id=assignment_id,
|
||||
).values("id", "assignment_user_id", "completion_status", "evaluation_grade")
|
||||
).values(
|
||||
"id",
|
||||
"assignment_user_id",
|
||||
"completion_status",
|
||||
"evaluation_grade",
|
||||
"learning_content_page_id",
|
||||
)
|
||||
return Response(status=200, data=qs)
|
||||
|
||||
raise PermissionDenied()
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class CourseSessionAttendanceCourseAdmin(admin.ModelAdmin):
|
|||
"start_date",
|
||||
"end_date",
|
||||
"trainer",
|
||||
"location",
|
||||
]
|
||||
list_filter = ["course_session__course", "course_session"]
|
||||
|
||||
|
|
@ -38,12 +39,10 @@ class CourseSessionAttendanceCourseAdmin(admin.ModelAdmin):
|
|||
end_date.admin_order_field = "due_date__end"
|
||||
|
||||
def circle(self, obj):
|
||||
try:
|
||||
return obj.learning_content.get_ancestors().exact_type(Circle).first().title
|
||||
except Exception:
|
||||
# noop
|
||||
pass
|
||||
return None
|
||||
circle = obj.get_circle()
|
||||
if circle:
|
||||
return circle.title
|
||||
return ""
|
||||
|
||||
# Create a method that serves as a form field
|
||||
def circle_display(self, obj=None):
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from django_jsonform.models.fields import JSONField as JSONSchemaField
|
|||
|
||||
from vbv_lernwelt.assignment.models import AssignmentType
|
||||
from vbv_lernwelt.duedate.models import DueDate
|
||||
from vbv_lernwelt.learnpath.models import Circle
|
||||
|
||||
|
||||
class CourseSessionAttendanceCourse(models.Model):
|
||||
|
|
@ -73,6 +74,14 @@ class CourseSessionAttendanceCourse(models.Model):
|
|||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def get_circle(self):
|
||||
try:
|
||||
return self.learning_content.get_ancestors().exact_type(Circle).first()
|
||||
except Exception:
|
||||
# noop
|
||||
pass
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.course_session} - {self.learning_content}"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from vbv_lernwelt.course_session.models import (
|
|||
class CourseSessionAttendanceCourseSerializer(serializers.ModelSerializer):
|
||||
start = serializers.SerializerMethodField()
|
||||
end = serializers.SerializerMethodField()
|
||||
circle_title = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = CourseSessionAttendanceCourse
|
||||
|
|
@ -21,6 +22,7 @@ class CourseSessionAttendanceCourseSerializer(serializers.ModelSerializer):
|
|||
"trainer",
|
||||
"start",
|
||||
"end",
|
||||
"circle_title",
|
||||
]
|
||||
|
||||
def get_start(self, obj):
|
||||
|
|
@ -29,6 +31,12 @@ class CourseSessionAttendanceCourseSerializer(serializers.ModelSerializer):
|
|||
def get_end(self, obj):
|
||||
return obj.due_date.end
|
||||
|
||||
def get_circle_title(self, obj):
|
||||
circle = obj.get_circle()
|
||||
if circle:
|
||||
return circle.title
|
||||
return ""
|
||||
|
||||
|
||||
class CourseSessionAssignmentSerializer(serializers.ModelSerializer):
|
||||
submission_deadline_start = serializers.SerializerMethodField()
|
||||
|
|
|
|||
Loading…
Reference in New Issue