Merged in feature/VBV-525-feedback-improvements (pull request #208)
Feature/VBV-525 feedback improvements Approved-by: Christian Cueni
This commit is contained in:
commit
05f111eada
|
|
@ -1,233 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import ItRadioGroup from "@/components/ui/ItRadioGroup.vue";
|
|
||||||
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
|
||||||
import { graphql } from "@/gql/";
|
|
||||||
import type { SendFeedbackInput } from "@/gql/graphql";
|
|
||||||
import FeedbackCompletition from "@/pages/learningPath/learningContentPage/feedback/FeedbackCompletition.vue";
|
|
||||||
import {
|
|
||||||
PERCENTAGES,
|
|
||||||
RATINGS,
|
|
||||||
YES_NO,
|
|
||||||
} from "@/pages/learningPath/learningContentPage/feedback/feedback.constants";
|
|
||||||
import LearningContentMultiLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentMultiLayout.vue";
|
|
||||||
import { useCircleStore } from "@/stores/circle";
|
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
||||||
import type { LearningContentFeedback } from "@/types";
|
|
||||||
import { useMutation } from "@urql/vue";
|
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
|
||||||
import log from "loglevel";
|
|
||||||
import { computed, onMounted, reactive, ref } from "vue";
|
|
||||||
import { useTranslation } from "i18next-vue";
|
|
||||||
|
|
||||||
const props = defineProps<{ page: LearningContentFeedback }>();
|
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
|
||||||
const circleStore = useCircleStore();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
log.debug("Feedback mounted");
|
|
||||||
});
|
|
||||||
|
|
||||||
const stepNo = useRouteQuery("step", "0", { transform: Number, mode: "push" });
|
|
||||||
|
|
||||||
const title = computed(
|
|
||||||
() => `«${circleStore.circle?.title}»: ${t("feedback.areYouSatisfied")}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const stepLabels = [
|
|
||||||
t("general.introduction"),
|
|
||||||
t("feedback.satisfactionLabel"),
|
|
||||||
t("feedback.goalAttainmentLabel"),
|
|
||||||
t("feedback.proficiencyLabel"),
|
|
||||||
t("feedback.preparationTaskClarityLabel"),
|
|
||||||
t("feedback.instructorCompetenceLabel"),
|
|
||||||
t("feedback.instructorRespectLabel"),
|
|
||||||
t("feedback.instructorOpenFeedbackLabel"),
|
|
||||||
t("feedback.recommendLabel"),
|
|
||||||
t("feedback.courseNegativeFeedbackLabel"),
|
|
||||||
t("feedback.coursePositiveFeedbackLabel"),
|
|
||||||
t("general.submission"),
|
|
||||||
];
|
|
||||||
|
|
||||||
const numSteps = stepLabels.length;
|
|
||||||
|
|
||||||
const sendFeedbackMutation = graphql(`
|
|
||||||
mutation SendFeedbackMutation($input: SendFeedbackInput!) {
|
|
||||||
send_feedback(input: $input) {
|
|
||||||
feedback_response {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
errors {
|
|
||||||
field
|
|
||||||
messages
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
const { executeMutation } = useMutation(sendFeedbackMutation);
|
|
||||||
|
|
||||||
const satisfaction = ref(null);
|
|
||||||
const goalAttainment = ref(null);
|
|
||||||
const proficiency = ref(null);
|
|
||||||
const preparationTaskClarity = ref(null);
|
|
||||||
const instructorCompetence = ref(null);
|
|
||||||
const instructorRespect = ref(null);
|
|
||||||
const instructorOpenFeedback = ref("");
|
|
||||||
const wouldRecommend = ref(null);
|
|
||||||
const courseNegativeFeedback = ref("");
|
|
||||||
const coursePositiveFeedback = ref("");
|
|
||||||
|
|
||||||
const mutationResult = ref<any>(null);
|
|
||||||
|
|
||||||
const previousStep = () => {
|
|
||||||
if (stepNo.value > 0) {
|
|
||||||
stepNo.value -= 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const nextStep = () => {
|
|
||||||
if (stepNo.value < numSteps) {
|
|
||||||
stepNo.value += 1;
|
|
||||||
}
|
|
||||||
log.info(`next step ${stepNo.value} of ${numSteps}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const sendFeedback = () => {
|
|
||||||
log.info("sending feedback");
|
|
||||||
const courseSession = courseSessionsStore.currentCourseSession;
|
|
||||||
if (!courseSession || !courseSession.id) {
|
|
||||||
log.error("no course session set");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const input: SendFeedbackInput = reactive({
|
|
||||||
data: {
|
|
||||||
preparation_task_clarity: preparationTaskClarity,
|
|
||||||
course_negative_feedback: courseNegativeFeedback,
|
|
||||||
course_positive_feedback: coursePositiveFeedback,
|
|
||||||
goal_attainment: goalAttainment,
|
|
||||||
instructor_competence: instructorCompetence,
|
|
||||||
instructor_respect: instructorRespect,
|
|
||||||
instructor_open_feedback: instructorOpenFeedback,
|
|
||||||
satisfaction,
|
|
||||||
proficiency,
|
|
||||||
would_recommend: wouldRecommend,
|
|
||||||
},
|
|
||||||
page: props.page.id,
|
|
||||||
course_session: courseSession.id,
|
|
||||||
});
|
|
||||||
const variables = reactive({
|
|
||||||
input,
|
|
||||||
});
|
|
||||||
log.debug(variables);
|
|
||||||
executeMutation(variables)
|
|
||||||
.then(({ data }) => {
|
|
||||||
log.debug(data);
|
|
||||||
mutationResult.value = data;
|
|
||||||
})
|
|
||||||
.catch((e) => log.error(e));
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<LearningContentMultiLayout
|
|
||||||
:title="title"
|
|
||||||
sub-title="Feedback"
|
|
||||||
:learning-content="page"
|
|
||||||
:show-start-button="stepNo === 0"
|
|
||||||
:show-next-button="stepNo > 0 && stepNo + 1 < numSteps"
|
|
||||||
:show-previous-button="stepNo > 0"
|
|
||||||
:show-exit-button="stepNo + 1 === numSteps"
|
|
||||||
:current-step="stepNo"
|
|
||||||
:steps-count="numSteps"
|
|
||||||
:start-badge-text="$t('general.introduction')"
|
|
||||||
:end-badge-text="$t('general.submission')"
|
|
||||||
:base-url="props.page.frontend_url"
|
|
||||||
close-button-variant="close"
|
|
||||||
@previous="previousStep()"
|
|
||||||
@next="nextStep()"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<p v-if="stepNo === 0" class="mt-10">
|
|
||||||
{{
|
|
||||||
$t("feedback.intro", {
|
|
||||||
name: `${courseSessionsStore.circleExperts[0]?.first_name} ${courseSessionsStore.circleExperts[0]?.last_name}`,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
<p v-if="stepNo > 0 && stepNo + 1 < numSteps" class="pb-2">
|
|
||||||
{{ stepLabels[stepNo] }}
|
|
||||||
</p>
|
|
||||||
<ItRadioGroup
|
|
||||||
v-if="stepNo === 1"
|
|
||||||
v-model="satisfaction"
|
|
||||||
class="mb-8"
|
|
||||||
:items="RATINGS"
|
|
||||||
/>
|
|
||||||
<ItRadioGroup
|
|
||||||
v-if="stepNo === 2"
|
|
||||||
v-model="goalAttainment"
|
|
||||||
class="mb-8"
|
|
||||||
:items="RATINGS"
|
|
||||||
/>
|
|
||||||
<ItRadioGroup
|
|
||||||
v-if="stepNo === 3"
|
|
||||||
v-model="proficiency"
|
|
||||||
class="mb-8"
|
|
||||||
:items="PERCENTAGES"
|
|
||||||
/>
|
|
||||||
<ItRadioGroup
|
|
||||||
v-if="stepNo === 4"
|
|
||||||
v-model="preparationTaskClarity"
|
|
||||||
class="mb-8"
|
|
||||||
:items="YES_NO"
|
|
||||||
/>
|
|
||||||
<ItRadioGroup
|
|
||||||
v-if="stepNo === 5"
|
|
||||||
v-model="instructorCompetence"
|
|
||||||
class="mb-8"
|
|
||||||
:items="RATINGS"
|
|
||||||
/>
|
|
||||||
<ItRadioGroup
|
|
||||||
v-if="stepNo === 6"
|
|
||||||
v-model="instructorRespect"
|
|
||||||
class="mb-8"
|
|
||||||
:items="RATINGS"
|
|
||||||
/>
|
|
||||||
<ItTextarea v-if="stepNo === 7" v-model="instructorOpenFeedback" class="mb-8" />
|
|
||||||
<ItRadioGroup
|
|
||||||
v-if="stepNo === 8"
|
|
||||||
v-model="wouldRecommend"
|
|
||||||
class="mb-8"
|
|
||||||
:items="YES_NO"
|
|
||||||
/>
|
|
||||||
<ItTextarea v-if="stepNo === 9" v-model="courseNegativeFeedback" class="mb-8" />
|
|
||||||
<ItTextarea v-if="stepNo === 10" v-model="coursePositiveFeedback" class="mb-8" />
|
|
||||||
<FeedbackCompletition
|
|
||||||
v-if="stepNo === 11"
|
|
||||||
:avatar-url="courseSessionsStore.circleExperts[0].avatar_url"
|
|
||||||
:title="
|
|
||||||
$t('feedback.completionTitle', {
|
|
||||||
name: `${courseSessionsStore.circleExperts[0].first_name} ${courseSessionsStore.circleExperts[0].last_name}`,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
:description="$t('feedback.completionDescription')"
|
|
||||||
:feedback-sent="mutationResult != null"
|
|
||||||
@send-feedback="sendFeedback"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</LearningContentMultiLayout>
|
|
||||||
<!--
|
|
||||||
<pre>
|
|
||||||
satisfaction {{ satisfaction }}
|
|
||||||
goalAttainment {{ goalAttainment }}
|
|
||||||
proficiency {{ proficiency }}
|
|
||||||
receivedMaterials {{ receivedMaterials }}
|
|
||||||
materialsRating {{ materialsRating }}
|
|
||||||
instructorCompetence {{ instructorCompetence }}
|
|
||||||
instructorRespect {{ instructorRespect }}
|
|
||||||
instructorOpenFeedback {{ instructorOpenFeedback }}
|
|
||||||
wouldRecommend {{ wouldRecommend }}
|
|
||||||
coursePositiveFeedback {{ coursePositiveFeedback }}
|
|
||||||
courseNegativeFeedback {{ courseNegativeFeedback }}
|
|
||||||
mutationResult: {{ mutationResult }}
|
|
||||||
</pre> -->
|
|
||||||
</template>
|
|
||||||
|
|
@ -26,7 +26,9 @@
|
||||||
class="h-8 bg-sky-500"
|
class="h-8 bg-sky-500"
|
||||||
:style="{ width: `${percentage * 100 * 0.8}%` }"
|
:style="{ width: `${percentage * 100 * 0.8}%` }"
|
||||||
></div>
|
></div>
|
||||||
<div class="text-sm">{{ (percentage * 100).toFixed(1) }}%</div>
|
<div class="text-sm" :data-cy="`percentage-value-${label}`">
|
||||||
|
{{ (percentage * 100).toFixed(1) }}%
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</QuestionSummary>
|
</QuestionSummary>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
as="template"
|
as="template"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
|
:data-cy="`radio-${item.value}`"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex-1 cursor-pointer py-10 text-center text-xl font-bold hover:border-gray-500 hover:bg-gray-200 ui-checked:bg-sky-500 ui-not-checked:border"
|
class="flex-1 cursor-pointer py-10 text-center text-xl font-bold hover:border-gray-500 hover:bg-gray-200 ui-checked:bg-sky-500 ui-not-checked:border"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<QuestionSummary :title="props.title" :text="props.text">
|
<QuestionSummary :title="props.title" :text="props.text">
|
||||||
<h5 class="mb-8 text-base">{{ answers.length }} {{ $t("feedback.answers") }}</h5>
|
<h5 class="mb-8 text-base">
|
||||||
|
{{ uniqueAnswers.length }} {{ $t("feedback.answers") }}
|
||||||
|
</h5>
|
||||||
<ol>
|
<ol>
|
||||||
<li v-for="answer of props.answers" :key="answer" class="mb-2 last:mb-0">
|
<li v-for="answer of uniqueAnswers" :key="answer" class="mb-2 last:mb-0">
|
||||||
<p>{{ answer }}</p>
|
<p>{{ answer }}</p>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
@ -11,12 +13,17 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import QuestionSummary from "@/components/ui/QuestionSummary.vue";
|
import QuestionSummary from "@/components/ui/QuestionSummary.vue";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
answers: string[];
|
answers: string[];
|
||||||
title: string;
|
title: string;
|
||||||
text: string;
|
text: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const uniqueAnswers = computed(() => {
|
||||||
|
return [...new Set(props.answers)];
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="postcss" scoped></style>
|
<style lang="postcss" scoped></style>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
<span
|
<span
|
||||||
class="col-start-2 row-span-2 inline-flex h-9 w-11 items-center justify-center rounded text-xl font-bold"
|
class="col-start-2 row-span-2 inline-flex h-9 w-11 items-center justify-center rounded text-xl font-bold"
|
||||||
:style="ratingValueStyle"
|
:style="ratingValueStyle"
|
||||||
|
data-cy="rating-scale-average"
|
||||||
>
|
>
|
||||||
{{ rating.toFixed(1) }}
|
{{ rating.toFixed(1) }}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -75,6 +76,7 @@ import QuestionSummary from "@/components/ui/QuestionSummary.vue";
|
||||||
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
|
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useTranslation } from "i18next-vue";
|
import { useTranslation } from "i18next-vue";
|
||||||
|
import log from "loglevel";
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|
@ -97,6 +99,8 @@ const props = defineProps<{
|
||||||
text: string;
|
text: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
log.debug("RatingScale created", props);
|
||||||
|
|
||||||
const rating = computed((): number => {
|
const rating = computed((): number => {
|
||||||
const sum = props.ratings.reduce((a, b) => a + b, 0);
|
const sum = props.ratings.reduce((a, b) => a + b, 0);
|
||||||
return sum / props.ratings.length;
|
return sum / props.ratings.length;
|
||||||
|
|
@ -182,8 +186,6 @@ const gradientStyle = {
|
||||||
const ratingValueStyle = {
|
const ratingValueStyle = {
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(props);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="postcss" scoped>
|
<style lang="postcss" scoped>
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,14 @@
|
||||||
:style="greenStyle"
|
:style="greenStyle"
|
||||||
></div>
|
></div>
|
||||||
<div class="self-center justify-self-center font-bold grid-in-left-label">
|
<div class="self-center justify-self-center font-bold grid-in-left-label">
|
||||||
<Popover class="relative">
|
<Popover class="relative" data-cy="popover-no">
|
||||||
<PopoverButton class="focus:outline-none">
|
<PopoverButton class="focus:outline-none">
|
||||||
{{ $t("general.no") }}
|
{{ $t("general.no") }}
|
||||||
</PopoverButton>
|
</PopoverButton>
|
||||||
<PopoverPanel
|
<PopoverPanel
|
||||||
class="absolute top-[-200%] z-10 w-[120px] border border-gray-500 bg-white p-1 text-left text-sm font-normal"
|
class="absolute top-[-200%] z-10 w-[120px] border border-gray-500 bg-white p-1 text-left text-sm font-normal"
|
||||||
>
|
>
|
||||||
<p>
|
<p data-cy="num-no">
|
||||||
{{
|
{{
|
||||||
`"${$t("general.no")}" ${numberOfRatings["no"]} ${$t(
|
`"${$t("general.no")}" ${numberOfRatings["no"]} ${$t(
|
||||||
"feedback.answers"
|
"feedback.answers"
|
||||||
|
|
@ -46,14 +46,14 @@
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div class="self-center justify-self-center font-bold grid-in-right-label">
|
<div class="self-center justify-self-center font-bold grid-in-right-label">
|
||||||
<Popover class="relative">
|
<Popover class="relative" data-cy="popover-yes">
|
||||||
<PopoverButton class="focus:outline-none">
|
<PopoverButton class="focus:outline-none">
|
||||||
{{ $t("general.yes") }}
|
{{ $t("general.yes") }}
|
||||||
</PopoverButton>
|
</PopoverButton>
|
||||||
<PopoverPanel
|
<PopoverPanel
|
||||||
class="absolute top-[-200%] z-10 w-[120px] border border-gray-500 bg-white p-1 text-left text-sm font-normal"
|
class="absolute top-[-200%] z-10 w-[120px] border border-gray-500 bg-white p-1 text-left text-sm font-normal"
|
||||||
>
|
>
|
||||||
<p>
|
<p data-cy="num-yes">
|
||||||
{{
|
{{
|
||||||
`"${$t("general.yes")}" ${numberOfRatings["yes"]} ${$t(
|
`"${$t("general.yes")}" ${numberOfRatings["yes"]} ${$t(
|
||||||
"feedback.answers"
|
"feedback.answers"
|
||||||
|
|
@ -71,6 +71,7 @@
|
||||||
import QuestionSummary from "@/components/ui/QuestionSummary.vue";
|
import QuestionSummary from "@/components/ui/QuestionSummary.vue";
|
||||||
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
|
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
ratings: boolean[];
|
ratings: boolean[];
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
|
||||||
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
||||||
*/
|
*/
|
||||||
const documents = {
|
const documents = {
|
||||||
"\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n send_feedback(input: $input) {\n feedback_response {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument,
|
|
||||||
"\n mutation AttendanceCheckMutation(\n $attendanceCourseId: ID!\n $attendanceUserList: [AttendanceUserInputType]!\n ) {\n update_course_session_attendance_course_users(\n id: $attendanceCourseId\n attendance_user_list: $attendanceUserList\n ) {\n course_session_attendance_course {\n id\n attendance_user_list {\n user_id\n first_name\n last_name\n email\n status\n }\n }\n }\n }\n": types.AttendanceCheckMutationDocument,
|
"\n mutation AttendanceCheckMutation(\n $attendanceCourseId: ID!\n $attendanceUserList: [AttendanceUserInputType]!\n ) {\n update_course_session_attendance_course_users(\n id: $attendanceCourseId\n attendance_user_list: $attendanceUserList\n ) {\n course_session_attendance_course {\n id\n attendance_user_list {\n user_id\n first_name\n last_name\n email\n status\n }\n }\n }\n }\n": types.AttendanceCheckMutationDocument,
|
||||||
"\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n": types.UpsertAssignmentCompletionDocument,
|
"\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n": types.UpsertAssignmentCompletionDocument,
|
||||||
"\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc,
|
"\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc,
|
||||||
|
|
@ -21,6 +20,7 @@ const documents = {
|
||||||
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
|
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
|
||||||
"\n query courseQuery($courseId: Int!) {\n course(id: $courseId) {\n id\n slug\n title\n category_name\n learning_path {\n id\n }\n }\n }\n": types.CourseQueryDocument,
|
"\n query courseQuery($courseId: Int!) {\n course(id: $courseId) {\n id\n slug\n title\n category_name\n learning_path {\n id\n }\n }\n }\n": types.CourseQueryDocument,
|
||||||
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
|
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
|
||||||
|
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\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,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,10 +37,6 @@ const documents = {
|
||||||
*/
|
*/
|
||||||
export function graphql(source: string): unknown;
|
export function graphql(source: string): unknown;
|
||||||
|
|
||||||
/**
|
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
||||||
*/
|
|
||||||
export function graphql(source: "\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n send_feedback(input: $input) {\n feedback_response {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n"): (typeof documents)["\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n send_feedback(input: $input) {\n feedback_response {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n"];
|
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
@ -69,6 +65,10 @@ export function graphql(source: "\n query courseQuery($courseId: Int!) {\n c
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* 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 competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"];
|
export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\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.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\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"): (typeof documents)["\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\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"];
|
||||||
|
|
||||||
export function graphql(source: string) {
|
export function graphql(source: string) {
|
||||||
return (documents as any)[source] ?? {};
|
return (documents as any)[source] ?? {};
|
||||||
|
|
|
||||||
|
|
@ -270,13 +270,11 @@ export type ErrorType = {
|
||||||
messages: Array<Scalars['String']['output']>;
|
messages: Array<Scalars['String']['output']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FeedbackResponse = Node & {
|
export type FeedbackResponseObjectType = {
|
||||||
__typename?: 'FeedbackResponse';
|
__typename?: 'FeedbackResponseObjectType';
|
||||||
circle: CircleObjectType;
|
|
||||||
created_at: Scalars['DateTime']['output'];
|
|
||||||
data?: Maybe<Scalars['GenericScalar']['output']>;
|
data?: Maybe<Scalars['GenericScalar']['output']>;
|
||||||
/** The ID of the object */
|
id: Scalars['UUID']['output'];
|
||||||
id: Scalars['ID']['output'];
|
submitted: Scalars['Boolean']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LearningContentAssignmentObjectType = LearningContentInterface & {
|
export type LearningContentAssignmentObjectType = LearningContentInterface & {
|
||||||
|
|
@ -537,14 +535,17 @@ export type LearnpathLearningContentAssignmentAssignmentTypeChoices =
|
||||||
|
|
||||||
export type Mutation = {
|
export type Mutation = {
|
||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
send_feedback?: Maybe<SendFeedbackPayload>;
|
send_feedback?: Maybe<SendFeedbackMutation>;
|
||||||
update_course_session_attendance_course_users?: Maybe<AttendanceCourseUserMutation>;
|
update_course_session_attendance_course_users?: Maybe<AttendanceCourseUserMutation>;
|
||||||
upsert_assignment_completion?: Maybe<AssignmentCompletionMutation>;
|
upsert_assignment_completion?: Maybe<AssignmentCompletionMutation>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationSendFeedbackArgs = {
|
export type MutationSendFeedbackArgs = {
|
||||||
input: SendFeedbackInput;
|
course_session_id: Scalars['ID']['input'];
|
||||||
|
data?: InputMaybe<Scalars['GenericScalar']['input']>;
|
||||||
|
learning_content_page_id: Scalars['ID']['input'];
|
||||||
|
submitted?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -566,12 +567,6 @@ export type MutationUpsertAssignmentCompletionArgs = {
|
||||||
learning_content_page_id?: InputMaybe<Scalars['ID']['input']>;
|
learning_content_page_id?: InputMaybe<Scalars['ID']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** An object with an ID */
|
|
||||||
export type Node = {
|
|
||||||
/** The ID of the object */
|
|
||||||
id: Scalars['ID']['output'];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Query = {
|
export type Query = {
|
||||||
__typename?: 'Query';
|
__typename?: 'Query';
|
||||||
assignment?: Maybe<AssignmentObjectType>;
|
assignment?: Maybe<AssignmentObjectType>;
|
||||||
|
|
@ -647,19 +642,11 @@ export type QueryLearningPathArgs = {
|
||||||
slug?: InputMaybe<Scalars['String']['input']>;
|
slug?: InputMaybe<Scalars['String']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SendFeedbackInput = {
|
export type SendFeedbackMutation = {
|
||||||
clientMutationId?: InputMaybe<Scalars['String']['input']>;
|
__typename?: 'SendFeedbackMutation';
|
||||||
course_session: Scalars['Int']['input'];
|
|
||||||
data?: InputMaybe<Scalars['GenericScalar']['input']>;
|
|
||||||
page: Scalars['Int']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SendFeedbackPayload = {
|
|
||||||
__typename?: 'SendFeedbackPayload';
|
|
||||||
clientMutationId?: Maybe<Scalars['String']['output']>;
|
|
||||||
/** May contain more than one error for same field. */
|
/** May contain more than one error for same field. */
|
||||||
errors?: Maybe<Array<Maybe<ErrorType>>>;
|
errors?: Maybe<Array<Maybe<ErrorType>>>;
|
||||||
feedback_response?: Maybe<FeedbackResponse>;
|
feedback_response?: Maybe<FeedbackResponseObjectType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TopicObjectType = CoursePageInterface & {
|
export type TopicObjectType = CoursePageInterface & {
|
||||||
|
|
@ -689,13 +676,6 @@ export type UserType = {
|
||||||
username: Scalars['String']['output'];
|
username: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SendFeedbackMutationMutationVariables = Exact<{
|
|
||||||
input: SendFeedbackInput;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type SendFeedbackMutationMutation = { __typename?: 'Mutation', send_feedback?: { __typename?: 'SendFeedbackPayload', feedback_response?: { __typename?: 'FeedbackResponse', id: string } | null, errors?: Array<{ __typename?: 'ErrorType', field: string, messages: Array<string> } | null> | null } | null };
|
|
||||||
|
|
||||||
export type AttendanceCheckMutationMutationVariables = Exact<{
|
export type AttendanceCheckMutationMutationVariables = Exact<{
|
||||||
attendanceCourseId: Scalars['ID']['input'];
|
attendanceCourseId: Scalars['ID']['input'];
|
||||||
attendanceUserList: Array<InputMaybe<AttendanceUserInputType>> | InputMaybe<AttendanceUserInputType>;
|
attendanceUserList: Array<InputMaybe<AttendanceUserInputType>> | InputMaybe<AttendanceUserInputType>;
|
||||||
|
|
@ -811,11 +791,21 @@ export type CompetenceCertificateQueryQuery = { __typename?: 'Query', competence
|
||||||
& { ' $fragmentRefs'?: { 'CoursePageFieldsCompetenceCertificateListObjectTypeFragment': CoursePageFieldsCompetenceCertificateListObjectTypeFragment } }
|
& { ' $fragmentRefs'?: { 'CoursePageFieldsCompetenceCertificateListObjectTypeFragment': CoursePageFieldsCompetenceCertificateListObjectTypeFragment } }
|
||||||
) | null };
|
) | null };
|
||||||
|
|
||||||
|
export type SendFeedbackMutationMutationVariables = Exact<{
|
||||||
|
courseSessionId: Scalars['ID']['input'];
|
||||||
|
learningContentId: Scalars['ID']['input'];
|
||||||
|
data: Scalars['GenericScalar']['input'];
|
||||||
|
submitted?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type SendFeedbackMutationMutation = { __typename?: 'Mutation', send_feedback?: { __typename?: 'SendFeedbackMutation', feedback_response?: { __typename?: 'FeedbackResponseObjectType', id: any, data?: any | null, submitted: boolean } | null, errors?: Array<{ __typename?: 'ErrorType', field: string, messages: Array<string> } | null> | null } | null };
|
||||||
|
|
||||||
export const CoursePageFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<CoursePageFieldsFragment, unknown>;
|
export const CoursePageFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<CoursePageFieldsFragment, unknown>;
|
||||||
export const SendFeedbackMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendFeedbackMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SendFeedbackInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"send_feedback"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feedback_response"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"messages"}}]}}]}}]}}]} as unknown as DocumentNode<SendFeedbackMutationMutation, SendFeedbackMutationMutationVariables>;
|
|
||||||
export const AttendanceCheckMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AttendanceCheckMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"attendanceCourseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"attendanceUserList"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AttendanceUserInputType"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update_course_session_attendance_course_users"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"attendanceCourseId"}}},{"kind":"Argument","name":{"kind":"Name","value":"attendance_user_list"},"value":{"kind":"Variable","name":{"kind":"Name","value":"attendanceUserList"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_session_attendance_course"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"attendance_user_list"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user_id"}},{"kind":"Field","name":{"kind":"Name","value":"first_name"}},{"kind":"Field","name":{"kind":"Name","value":"last_name"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]}}]} as unknown as DocumentNode<AttendanceCheckMutationMutation, AttendanceCheckMutationMutationVariables>;
|
export const AttendanceCheckMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AttendanceCheckMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"attendanceCourseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"attendanceUserList"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AttendanceUserInputType"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update_course_session_attendance_course_users"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"attendanceCourseId"}}},{"kind":"Argument","name":{"kind":"Name","value":"attendance_user_list"},"value":{"kind":"Variable","name":{"kind":"Name","value":"attendanceUserList"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_session_attendance_course"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"attendance_user_list"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user_id"}},{"kind":"Field","name":{"kind":"Name","value":"first_name"}},{"kind":"Field","name":{"kind":"Name","value":"last_name"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]}}]} as unknown as DocumentNode<AttendanceCheckMutationMutation, AttendanceCheckMutationMutationVariables>;
|
||||||
export const UpsertAssignmentCompletionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertAssignmentCompletion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AssignmentCompletionStatus"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"initializeCompletion"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsert_assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_data_string"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_grade"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_points"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}}},{"kind":"Argument","name":{"kind":"Name","value":"initialize_completion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"initializeCompletion"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}}]}}]} as unknown as DocumentNode<UpsertAssignmentCompletionMutation, UpsertAssignmentCompletionMutationVariables>;
|
export const UpsertAssignmentCompletionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertAssignmentCompletion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AssignmentCompletionStatus"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"initializeCompletion"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsert_assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_data_string"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_grade"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_points"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}}},{"kind":"Argument","name":{"kind":"Name","value":"initialize_completion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"initializeCompletion"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}}]}}]} as unknown as DocumentNode<UpsertAssignmentCompletionMutation, UpsertAssignmentCompletionMutationVariables>;
|
||||||
export const AttendanceCheckQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"attendanceCheckQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_session_attendance_course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"attendance_user_list"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user_id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]} as unknown as DocumentNode<AttendanceCheckQueryQuery, AttendanceCheckQueryQueryVariables>;
|
export const AttendanceCheckQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"attendanceCheckQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_session_attendance_course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"attendance_user_list"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user_id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]} as unknown as DocumentNode<AttendanceCheckQueryQuery, AttendanceCheckQueryQueryVariables>;
|
||||||
export const AssignmentCompletionQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"assignmentCompletionQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"max_points"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"effort_required"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_description"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_document_url"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_tasks"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"intro_text"}},{"kind":"Field","name":{"kind":"Name","value":"performance_objectives"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"tasks"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"translation_key"}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<AssignmentCompletionQueryQuery, AssignmentCompletionQueryQueryVariables>;
|
export const AssignmentCompletionQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"assignmentCompletionQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"max_points"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"effort_required"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_description"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_document_url"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_tasks"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"intro_text"}},{"kind":"Field","name":{"kind":"Name","value":"performance_objectives"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"tasks"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"translation_key"}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<AssignmentCompletionQueryQuery, AssignmentCompletionQueryQueryVariables>;
|
||||||
export const CourseQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"category_name"}},{"kind":"Field","name":{"kind":"Name","value":"learning_path"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<CourseQueryQuery, CourseQueryQueryVariables>;
|
export const CourseQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"category_name"}},{"kind":"Field","name":{"kind":"Name","value":"learning_path"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<CourseQueryQuery, CourseQueryQueryVariables>;
|
||||||
export const CompetenceCertificateQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"competenceCertificateQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_certificate_list"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificates"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"assignments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"max_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_content"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}},{"kind":"Field","name":{"kind":"Name","value":"circle"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<CompetenceCertificateQueryQuery, CompetenceCertificateQueryQueryVariables>;
|
export const CompetenceCertificateQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"competenceCertificateQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_certificate_list"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificates"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"assignments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"max_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_content"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}},{"kind":"Field","name":{"kind":"Name","value":"circle"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<CompetenceCertificateQueryQuery, CompetenceCertificateQueryQueryVariables>;
|
||||||
|
export const SendFeedbackMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendFeedbackMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GenericScalar"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"send_feedback"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"submitted"},"value":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feedback_response"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"submitted"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"messages"}}]}}]}}]}}]} as unknown as DocumentNode<SendFeedbackMutationMutation, SendFeedbackMutationMutationVariables>;
|
||||||
|
|
@ -546,31 +546,22 @@ type CompetenceCertificateListObjectType implements CoursePageInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
send_feedback(input: SendFeedbackInput!): SendFeedbackPayload
|
send_feedback(course_session_id: ID!, data: GenericScalar, learning_content_page_id: ID!, submitted: Boolean = false): SendFeedbackMutation
|
||||||
update_course_session_attendance_course_users(attendance_user_list: [AttendanceUserInputType]!, id: ID!): AttendanceCourseUserMutation
|
update_course_session_attendance_course_users(attendance_user_list: [AttendanceUserInputType]!, id: ID!): AttendanceCourseUserMutation
|
||||||
upsert_assignment_completion(assignment_id: ID!, assignment_user_id: UUID, completion_data_string: String, completion_status: AssignmentCompletionStatus, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float, initialize_completion: Boolean, learning_content_page_id: ID): AssignmentCompletionMutation
|
upsert_assignment_completion(assignment_id: ID!, assignment_user_id: UUID, completion_data_string: String, completion_status: AssignmentCompletionStatus, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float, initialize_completion: Boolean, learning_content_page_id: ID): AssignmentCompletionMutation
|
||||||
}
|
}
|
||||||
|
|
||||||
type SendFeedbackPayload {
|
type SendFeedbackMutation {
|
||||||
feedback_response: FeedbackResponse
|
feedback_response: FeedbackResponseObjectType
|
||||||
|
|
||||||
"""May contain more than one error for same field."""
|
"""May contain more than one error for same field."""
|
||||||
errors: [ErrorType]
|
errors: [ErrorType]
|
||||||
clientMutationId: String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FeedbackResponse implements Node {
|
type FeedbackResponseObjectType {
|
||||||
"""The ID of the object"""
|
id: UUID!
|
||||||
id: ID!
|
|
||||||
data: GenericScalar
|
data: GenericScalar
|
||||||
created_at: DateTime!
|
submitted: Boolean!
|
||||||
circle: CircleObjectType!
|
|
||||||
}
|
|
||||||
|
|
||||||
"""An object with an ID"""
|
|
||||||
interface Node {
|
|
||||||
"""The ID of the object"""
|
|
||||||
id: ID!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorType {
|
type ErrorType {
|
||||||
|
|
@ -578,13 +569,6 @@ type ErrorType {
|
||||||
messages: [String!]!
|
messages: [String!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
input SendFeedbackInput {
|
|
||||||
page: Int!
|
|
||||||
course_session: Int!
|
|
||||||
data: GenericScalar
|
|
||||||
clientMutationId: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type AttendanceCourseUserMutation {
|
type AttendanceCourseUserMutation {
|
||||||
course_session_attendance_course: CourseSessionAttendanceCourseType
|
course_session_attendance_course: CourseSessionAttendanceCourseType
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export const CoursePageInterface = "CoursePageInterface";
|
||||||
export const CourseSessionAttendanceCourseType = "CourseSessionAttendanceCourseType";
|
export const CourseSessionAttendanceCourseType = "CourseSessionAttendanceCourseType";
|
||||||
export const DateTime = "DateTime";
|
export const DateTime = "DateTime";
|
||||||
export const ErrorType = "ErrorType";
|
export const ErrorType = "ErrorType";
|
||||||
export const FeedbackResponse = "FeedbackResponse";
|
export const FeedbackResponseObjectType = "FeedbackResponseObjectType";
|
||||||
export const Float = "Float";
|
export const Float = "Float";
|
||||||
export const GenericScalar = "GenericScalar";
|
export const GenericScalar = "GenericScalar";
|
||||||
export const ID = "ID";
|
export const ID = "ID";
|
||||||
|
|
@ -41,10 +41,8 @@ export const LearningSequenceObjectType = "LearningSequenceObjectType";
|
||||||
export const LearningUnitObjectType = "LearningUnitObjectType";
|
export const LearningUnitObjectType = "LearningUnitObjectType";
|
||||||
export const LearnpathLearningContentAssignmentAssignmentTypeChoices = "LearnpathLearningContentAssignmentAssignmentTypeChoices";
|
export const LearnpathLearningContentAssignmentAssignmentTypeChoices = "LearnpathLearningContentAssignmentAssignmentTypeChoices";
|
||||||
export const Mutation = "Mutation";
|
export const Mutation = "Mutation";
|
||||||
export const Node = "Node";
|
|
||||||
export const Query = "Query";
|
export const Query = "Query";
|
||||||
export const SendFeedbackInput = "SendFeedbackInput";
|
export const SendFeedbackMutation = "SendFeedbackMutation";
|
||||||
export const SendFeedbackPayload = "SendFeedbackPayload";
|
|
||||||
export const String = "String";
|
export const String = "String";
|
||||||
export const TopicObjectType = "TopicObjectType";
|
export const TopicObjectType = "TopicObjectType";
|
||||||
export const UUID = "UUID";
|
export const UUID = "UUID";
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,20 @@
|
||||||
<span>{{ $t("general.back") }}</span>
|
<span>{{ $t("general.back") }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</nav>
|
</nav>
|
||||||
<main>
|
<main v-if="feedbackData">
|
||||||
<h1 class="mb-2">{{ $t("feedback.feedbackPageTitle") }}</h1>
|
<h1 class="mb-2">{{ $t("feedback.feedbackPageTitle") }}</h1>
|
||||||
<p class="mb-10">
|
<p class="mb-10">
|
||||||
<span class="font-bold">{{ feedbackData.amount }}</span>
|
<span class="font-bold" data-cy="feedback-data-amount">
|
||||||
|
{{ feedbackData.amount }}
|
||||||
|
</span>
|
||||||
{{ $t("feedback.feedbackPageInfo") }}
|
{{ $t("feedback.feedbackPageInfo") }}
|
||||||
</p>
|
</p>
|
||||||
<ol v-if="Object.keys(feedbackData).length > 0">
|
<ol v-if="feedbackData.amount > 0">
|
||||||
<li v-for="(question, i) in orderedQuestions" :key="i">
|
<li
|
||||||
|
v-for="(question, i) in orderedQuestions"
|
||||||
|
:key="i"
|
||||||
|
:data-cy="`question-${i + 1}`"
|
||||||
|
>
|
||||||
<RatingScale
|
<RatingScale
|
||||||
v-if="ratingKeys.includes(question.key)"
|
v-if="ratingKeys.includes(question.key)"
|
||||||
class="mb-8 bg-white"
|
class="mb-8 bg-white"
|
||||||
|
|
@ -67,7 +73,7 @@ import VerticalBarChart from "@/components/ui/VerticalBarChart.vue";
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCurrentCourseSession } from "@/composables";
|
||||||
import { itGet } from "@/fetchHelpers";
|
import { itGet } from "@/fetchHelpers";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
import { onMounted, reactive } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import { useTranslation } from "i18next-vue";
|
import { useTranslation } from "i18next-vue";
|
||||||
|
|
||||||
interface FeedbackData {
|
interface FeedbackData {
|
||||||
|
|
@ -144,14 +150,13 @@ const openKeys = [
|
||||||
"instructor_open_feedback",
|
"instructor_open_feedback",
|
||||||
];
|
];
|
||||||
|
|
||||||
const feedbackData = reactive<FeedbackData>({ amount: 0, questions: {} });
|
const feedbackData = ref<FeedbackData | undefined>(undefined);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug("FeedbackPage mounted");
|
log.debug("FeedbackPage mounted");
|
||||||
const data = await itGet(
|
feedbackData.value = await itGet(
|
||||||
`/api/core/feedback/${courseSession.value.id}/${props.circleId}`
|
`/api/core/feedback/${courseSession.value.id}/${props.circleId}`
|
||||||
);
|
);
|
||||||
Object.assign(feedbackData, data);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,11 @@ const getIconName = (lc: LearningContent) => {
|
||||||
<button class="btn-primary">
|
<button class="btn-primary">
|
||||||
<router-link
|
<router-link
|
||||||
:to="submittable.detailsLink"
|
:to="submittable.detailsLink"
|
||||||
:data-cy="`show-details-btn-${submittable.content.slug}`"
|
:data-cy="
|
||||||
|
isFeedback(submittable.content)
|
||||||
|
? `show-feedback-btn-${submittable.content.slug}`
|
||||||
|
: `show-details-btn-${submittable.content.slug}`
|
||||||
|
"
|
||||||
>
|
>
|
||||||
{{ submittable.showDetailsText }}
|
{{ submittable.showDetailsText }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import type { Component } from "vue";
|
||||||
import { computed, onUnmounted } from "vue";
|
import { computed, onUnmounted } from "vue";
|
||||||
import AssignmentBlock from "./blocks/AssignmentBlock.vue";
|
import AssignmentBlock from "./blocks/AssignmentBlock.vue";
|
||||||
import AttendanceCourseBlock from "./blocks/AttendanceCourseBlock.vue";
|
import AttendanceCourseBlock from "./blocks/AttendanceCourseBlock.vue";
|
||||||
import FeedbackBlock from "./blocks/FeedbackBlock.vue";
|
import FeedbackBlock from "./feedback/FeedbackBlock.vue";
|
||||||
import IframeBlock from "./blocks/IframeBlock.vue";
|
import IframeBlock from "./blocks/IframeBlock.vue";
|
||||||
import MediaLibraryBlock from "./blocks/MediaLibraryBlock.vue";
|
import MediaLibraryBlock from "./blocks/MediaLibraryBlock.vue";
|
||||||
import PlaceholderBlock from "./blocks/PlaceholderBlock.vue";
|
import PlaceholderBlock from "./blocks/PlaceholderBlock.vue";
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<template>
|
|
||||||
<FeedbackForm :page="content" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import FeedbackForm from "@/components/FeedbackForm.vue";
|
|
||||||
import type { LearningContentFeedback } from "@/types";
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
content: LearningContentFeedback;
|
|
||||||
}>();
|
|
||||||
</script>
|
|
||||||
|
|
@ -0,0 +1,280 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ItRadioGroup from "@/components/ui/ItRadioGroup.vue";
|
||||||
|
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
||||||
|
import { graphql } from "@/gql";
|
||||||
|
import FeedbackCompletition from "@/pages/learningPath/learningContentPage/feedback/FeedbackCompletition.vue";
|
||||||
|
import {
|
||||||
|
PERCENTAGES,
|
||||||
|
RATINGS,
|
||||||
|
YES_NO,
|
||||||
|
} from "@/pages/learningPath/learningContentPage/feedback/feedback.constants";
|
||||||
|
import LearningContentMultiLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentMultiLayout.vue";
|
||||||
|
import { useCircleStore } from "@/stores/circle";
|
||||||
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
|
import type { LearningContentFeedback } from "@/types";
|
||||||
|
import { useMutation } from "@urql/vue";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
|
import log from "loglevel";
|
||||||
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
|
import { useCurrentCourseSession } from "@/composables";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
content: LearningContentFeedback;
|
||||||
|
}>();
|
||||||
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
|
const courseSession = useCurrentCourseSession();
|
||||||
|
const circleStore = useCircleStore();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const stepNo = useRouteQuery("step", "0", { transform: Number, mode: "push" });
|
||||||
|
|
||||||
|
const title = computed(
|
||||||
|
() => `«${circleStore.circle?.title}»: ${t("feedback.areYouSatisfied")}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const stepLabels = [
|
||||||
|
t("general.introduction"),
|
||||||
|
t("feedback.satisfactionLabel"),
|
||||||
|
t("feedback.goalAttainmentLabel"),
|
||||||
|
t("feedback.proficiencyLabel"),
|
||||||
|
t("feedback.preparationTaskClarityLabel"),
|
||||||
|
t("feedback.instructorCompetenceLabel"),
|
||||||
|
t("feedback.instructorRespectLabel"),
|
||||||
|
t("feedback.instructorOpenFeedbackLabel"),
|
||||||
|
t("feedback.recommendLabel"),
|
||||||
|
t("feedback.courseNegativeFeedbackLabel"),
|
||||||
|
t("feedback.coursePositiveFeedbackLabel"),
|
||||||
|
t("general.submission"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const numSteps = stepLabels.length;
|
||||||
|
|
||||||
|
// noinspection GraphQLUnresolvedReference -> mute IntelliJ warning
|
||||||
|
const sendFeedbackMutation = graphql(`
|
||||||
|
mutation SendFeedbackMutation(
|
||||||
|
$courseSessionId: ID!
|
||||||
|
$learningContentId: ID!
|
||||||
|
$data: GenericScalar!
|
||||||
|
$submitted: Boolean
|
||||||
|
) {
|
||||||
|
send_feedback(
|
||||||
|
course_session_id: $courseSessionId
|
||||||
|
learning_content_page_id: $learningContentId
|
||||||
|
data: $data
|
||||||
|
submitted: $submitted
|
||||||
|
) {
|
||||||
|
feedback_response {
|
||||||
|
id
|
||||||
|
data
|
||||||
|
submitted
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
field
|
||||||
|
messages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const feedbackSubmitted = ref(false);
|
||||||
|
|
||||||
|
const { executeMutation } = useMutation(sendFeedbackMutation);
|
||||||
|
|
||||||
|
interface FeedbackData {
|
||||||
|
[key: string]: number | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const feedbackData: FeedbackData = reactive({
|
||||||
|
satisfaction: null,
|
||||||
|
goal_attainment: null,
|
||||||
|
proficiency: null,
|
||||||
|
preparation_task_clarity: null,
|
||||||
|
instructor_competence: null,
|
||||||
|
instructor_respect: null,
|
||||||
|
instructor_open_feedback: "",
|
||||||
|
would_recommend: null,
|
||||||
|
course_negative_feedback: "",
|
||||||
|
course_positive_feedback: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const questionData = [
|
||||||
|
{
|
||||||
|
modelKey: "satisfaction",
|
||||||
|
items: RATINGS,
|
||||||
|
component: ItRadioGroup,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "goal_attainment",
|
||||||
|
items: RATINGS,
|
||||||
|
component: ItRadioGroup,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "proficiency",
|
||||||
|
items: PERCENTAGES,
|
||||||
|
component: ItRadioGroup,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "preparation_task_clarity",
|
||||||
|
items: YES_NO,
|
||||||
|
component: ItRadioGroup,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "instructor_competence",
|
||||||
|
items: RATINGS,
|
||||||
|
component: ItRadioGroup,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "instructor_respect",
|
||||||
|
items: RATINGS,
|
||||||
|
component: ItRadioGroup,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "instructor_open_feedback",
|
||||||
|
component: ItTextarea,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "would_recommend",
|
||||||
|
items: YES_NO,
|
||||||
|
component: ItRadioGroup,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "course_negative_feedback",
|
||||||
|
component: ItTextarea,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "course_positive_feedback",
|
||||||
|
component: ItTextarea,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const previousStep = () => {
|
||||||
|
if (stepNo.value > 0) {
|
||||||
|
stepNo.value -= 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextStep = () => {
|
||||||
|
if (stepNo.value < numSteps && hasStepValidInput(stepNo.value)) {
|
||||||
|
stepNo.value += 1;
|
||||||
|
}
|
||||||
|
log.debug(`next step ${stepNo.value} of ${numSteps}`);
|
||||||
|
mutateFeedback(feedbackData);
|
||||||
|
};
|
||||||
|
|
||||||
|
function hasStepValidInput(stepNumber: number) {
|
||||||
|
const question = questionData[stepNumber - 1];
|
||||||
|
if (question) {
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
"instructor_open_feedback",
|
||||||
|
"course_negative_feedback",
|
||||||
|
"course_positive_feedback",
|
||||||
|
].includes(question.modelKey)
|
||||||
|
) {
|
||||||
|
// text response questions need to have a "truthy" value (not "" or null)
|
||||||
|
return feedbackData[question.modelKey];
|
||||||
|
} else {
|
||||||
|
// other responses need to have data, can be `0` or `false`
|
||||||
|
return feedbackData[question.modelKey] !== null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mutateFeedback(data: FeedbackData, submit = false) {
|
||||||
|
log.debug("mutate feedback", feedbackData);
|
||||||
|
return executeMutation({
|
||||||
|
courseSessionId: courseSession.value.id.toString(),
|
||||||
|
learningContentId: props.content.id.toString(),
|
||||||
|
data: data,
|
||||||
|
submitted: submit,
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
log.debug("feedback mutation result", result);
|
||||||
|
if (result.data?.send_feedback?.feedback_response?.data) {
|
||||||
|
const responseData = result.data.send_feedback.feedback_response.data;
|
||||||
|
if (!responseData.instructor_open_feedback) {
|
||||||
|
responseData.instructor_open_feedback = "";
|
||||||
|
}
|
||||||
|
if (!responseData.course_negative_feedback) {
|
||||||
|
responseData.course_negative_feedback = "";
|
||||||
|
}
|
||||||
|
if (!responseData.course_positive_feedback) {
|
||||||
|
responseData.course_positive_feedback = "";
|
||||||
|
}
|
||||||
|
Object.assign(feedbackData, responseData);
|
||||||
|
log.debug("feedback data", feedbackData);
|
||||||
|
feedbackSubmitted.value =
|
||||||
|
result.data?.send_feedback?.feedback_response?.submitted || false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => log.error(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
log.debug("Feedback mounted");
|
||||||
|
await mutateFeedback({});
|
||||||
|
if (feedbackSubmitted.value) {
|
||||||
|
stepNo.value = numSteps - 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LearningContentMultiLayout
|
||||||
|
:title="title"
|
||||||
|
sub-title="Feedback"
|
||||||
|
:learning-content="content"
|
||||||
|
:show-start-button="stepNo === 0"
|
||||||
|
:show-next-button="stepNo > 0 && stepNo + 1 < numSteps"
|
||||||
|
:disable-next-button="!hasStepValidInput(stepNo)"
|
||||||
|
:show-previous-button="stepNo > 0 && !feedbackSubmitted"
|
||||||
|
:show-exit-button="stepNo + 1 === numSteps"
|
||||||
|
:current-step="stepNo"
|
||||||
|
:steps-count="numSteps"
|
||||||
|
:start-badge-text="$t('general.introduction')"
|
||||||
|
:end-badge-text="$t('general.submission')"
|
||||||
|
:base-url="props.content.frontend_url"
|
||||||
|
close-button-variant="close"
|
||||||
|
@previous="previousStep()"
|
||||||
|
@next="nextStep()"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p v-if="stepNo === 0" class="mt-10">
|
||||||
|
{{
|
||||||
|
$t("feedback.intro", {
|
||||||
|
name: `${courseSessionsStore.circleExperts[0]?.first_name} ${courseSessionsStore.circleExperts[0]?.last_name}`,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<p v-if="stepNo > 0 && stepNo + 1 < numSteps" class="pb-2">
|
||||||
|
{{ stepLabels[stepNo] }}
|
||||||
|
</p>
|
||||||
|
<div v-for="(question, index) in questionData" :key="index">
|
||||||
|
<!-- eslint-disable -->
|
||||||
|
<!-- eslint does not like the dynamic v-model... -->
|
||||||
|
<component
|
||||||
|
:is="question.component"
|
||||||
|
v-if="index + 1 === stepNo"
|
||||||
|
v-model="feedbackData[question.modelKey] as any"
|
||||||
|
:items="question['items']"
|
||||||
|
:cy-key="question.modelKey"
|
||||||
|
/>
|
||||||
|
<!-- eslint-enable -->
|
||||||
|
</div>
|
||||||
|
<FeedbackCompletition
|
||||||
|
v-if="stepNo === 11"
|
||||||
|
:avatar-url="courseSessionsStore.circleExperts[0].avatar_url"
|
||||||
|
:title="
|
||||||
|
$t('feedback.completionTitle', {
|
||||||
|
name: `${courseSessionsStore.circleExperts[0].first_name} ${courseSessionsStore.circleExperts[0].last_name}`,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:description="$t('feedback.completionDescription')"
|
||||||
|
:feedback-sent="feedbackSubmitted"
|
||||||
|
@send-feedback="mutateFeedback(feedbackData, true)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</LearningContentMultiLayout>
|
||||||
|
</template>
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="hidden lg:mb-12 lg:block">{{ title }}</h1>
|
<h2 class="hidden lg:mb-12 lg:block">{{ title }}</h2>
|
||||||
<div
|
<div
|
||||||
class="b-0 flex flex-col lg:flex-row lg:items-center lg:border lg:border-gray-400 lg:p-8"
|
class="b-0 flex flex-col lg:flex-row lg:items-center lg:border lg:border-gray-400 lg:p-8"
|
||||||
>
|
>
|
||||||
<img :src="avatarUrl" class="mb-6 h-16 w-16 rounded-full lg:mr-12" />
|
<img :src="avatarUrl" class="mb-6 h-16 w-16 rounded-full lg:mr-12" />
|
||||||
<h1 class="mb-8 block lg:hidden">{{ title }}</h1>
|
<h2 class="mb-8 block lg:hidden">{{ title }}</h2>
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-6">{{ description }}</p>
|
<p class="mb-6">{{ description }}</p>
|
||||||
<button v-if="!feedbackSent" class="btn-primary" @click="$emit('sendFeedback')">
|
<button
|
||||||
|
v-if="!feedbackSent"
|
||||||
|
class="btn-primary"
|
||||||
|
data-cy="sendFeedbackButton"
|
||||||
|
@click="$emit('sendFeedback')"
|
||||||
|
>
|
||||||
{{ $t("feedback.sendFeedback") }}
|
{{ $t("feedback.sendFeedback") }}
|
||||||
</button>
|
</button>
|
||||||
<p v-else class="flex items-center bg-green-200 px-6 py-4">
|
<p v-else class="flex items-center bg-green-200 px-6 py-4">
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ const props = defineProps<{
|
||||||
showStartButton: boolean;
|
showStartButton: boolean;
|
||||||
showPreviousButton: boolean;
|
showPreviousButton: boolean;
|
||||||
showNextButton: boolean;
|
showNextButton: boolean;
|
||||||
|
disableNextButton: boolean;
|
||||||
showExitButton: boolean;
|
showExitButton: boolean;
|
||||||
closingButtonVariant: ClosingButtonVariant;
|
closingButtonVariant: ClosingButtonVariant;
|
||||||
}>();
|
}>();
|
||||||
|
|
@ -41,6 +42,7 @@ const closingButtonText = computed(() => {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="props.showNextButton"
|
v-if="props.showNextButton"
|
||||||
|
:disabled="props.disableNextButton"
|
||||||
class="btn-blue z-10 flex items-center"
|
class="btn-blue z-10 flex items-center"
|
||||||
data-cy="next-step"
|
data-cy="next-step"
|
||||||
@click="$emit('next')"
|
@click="$emit('next')"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ interface Props {
|
||||||
showStartButton: boolean;
|
showStartButton: boolean;
|
||||||
showPreviousButton: boolean;
|
showPreviousButton: boolean;
|
||||||
showNextButton: boolean;
|
showNextButton: boolean;
|
||||||
|
disableNextButton?: boolean;
|
||||||
showExitButton: boolean;
|
showExitButton: boolean;
|
||||||
currentStep: number;
|
currentStep: number;
|
||||||
stepsCount: number;
|
stepsCount: number;
|
||||||
|
|
@ -37,6 +38,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
closeButtonVariant: "mark_as_done",
|
closeButtonVariant: "mark_as_done",
|
||||||
baseUrl: undefined,
|
baseUrl: undefined,
|
||||||
stepQueryParam: undefined,
|
stepQueryParam: undefined,
|
||||||
|
disableNextButton: false,
|
||||||
beforeExitCallback: async () => Promise.resolve(),
|
beforeExitCallback: async () => Promise.resolve(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -103,6 +105,7 @@ const emit = defineEmits(["previous", "next", "exit"]);
|
||||||
:show-previous-button="props.showPreviousButton"
|
:show-previous-button="props.showPreviousButton"
|
||||||
:show-exit-button="props.showExitButton"
|
:show-exit-button="props.showExitButton"
|
||||||
:closing-button-variant="props.closeButtonVariant"
|
:closing-button-variant="props.closeButtonVariant"
|
||||||
|
:disable-next-button="props.disableNextButton"
|
||||||
@previous="emit('previous')"
|
@previous="emit('previous')"
|
||||||
@next="emit('next')"
|
@next="emit('next')"
|
||||||
@start="emit('next')"
|
@start="emit('next')"
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ const closingButtonVariant = computed(() => {
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<LearningContentFooter
|
<LearningContentFooter
|
||||||
:show-next-button="false"
|
:show-next-button="false"
|
||||||
|
:disable-next-button="false"
|
||||||
:show-previous-button="false"
|
:show-previous-button="false"
|
||||||
:show-exit-button="true"
|
:show-exit-button="true"
|
||||||
:show-start-button="false"
|
:show-start-button="false"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ const { cloudPlugin } = require("cypress-cloud/plugin");
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
projectId: "RVEZS1",
|
projectId: "RVEZS1",
|
||||||
watchForFileChanges: false,
|
watchForFileChanges: false,
|
||||||
video: false,
|
video: true,
|
||||||
viewportWidth: 1280,
|
viewportWidth: 1280,
|
||||||
viewportHeight: 720,
|
viewportHeight: 720,
|
||||||
retries: {
|
retries: {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
import { TEST_STUDENT1_USER_ID } from "../../consts";
|
||||||
|
import { login } from "../helpers";
|
||||||
|
|
||||||
|
describe("feedbackStudent.cy.js", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.manageCommand("cypress_reset");
|
||||||
|
login("test-student1@example.com", "test");
|
||||||
|
cy.visit("/course/test-lehrgang/learn/fahrzeug/feedback");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can open feedback page", () => {
|
||||||
|
cy.testLearningContentTitle("Wie zufrieden bist du?");
|
||||||
|
cy.testLearningContentSubtitle("Feedback");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can create feedback by giving answers to all steps", () => {
|
||||||
|
// initial wait for step 0 (or none with step==0) is required for pipelines
|
||||||
|
cy.url().should((url) => {
|
||||||
|
expect(url).to.match(/\/fahrzeug\/feedback(\?step=0)?$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// fill feedback form
|
||||||
|
// step 1
|
||||||
|
cy.url().should("include", "step=1");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="radio-4"]').click();
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 2
|
||||||
|
cy.url().should("include", "step=2");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
// the system should store after every step -> check stored data
|
||||||
|
cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then(
|
||||||
|
(ac) => {
|
||||||
|
expect(ac.submitted).to.be.false;
|
||||||
|
expect(ac.data.satisfaction).to.equal(4);
|
||||||
|
expect(ac.data.instructor_competence).to.equal(null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="radio-3"]').click();
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 3
|
||||||
|
cy.url().should("include", "step=3");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="radio-80"]').click();
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 4
|
||||||
|
cy.url().should("include", "step=4");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="radio-false"]').click();
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 5
|
||||||
|
cy.url().should("include", "step=5");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="radio-2"]').click();
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 6
|
||||||
|
cy.url().should("include", "step=6");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="radio-1"]').click();
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 7
|
||||||
|
cy.url().should("include", "step=7");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="it-textarea-instructor_open_feedback"]').type(
|
||||||
|
"Der Kursleiter ist eigentlich ganz nett."
|
||||||
|
);
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 8
|
||||||
|
cy.url().should("include", "step=8");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="radio-true"]').click();
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 9
|
||||||
|
cy.url().should("include", "step=9");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="it-textarea-course_negative_feedback"]').type(
|
||||||
|
"Ich bin unzufrieden mit einigen Sachen."
|
||||||
|
);
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 10
|
||||||
|
cy.url().should("include", "step=10");
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="it-textarea-course_positive_feedback"]').type(
|
||||||
|
"Ich bin zufrieden mit den meisten Dingen."
|
||||||
|
);
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
cy.url().should("include", "step=11");
|
||||||
|
cy.get('[data-cy="sendFeedbackButton"]').click();
|
||||||
|
cy.get('[data-cy="complete-and-continue"]').click({ force: true });
|
||||||
|
|
||||||
|
// marked complete in circle
|
||||||
|
cy.url().should((url) => {
|
||||||
|
expect(url).to.match(/\/fahrzeug#lu-transfer?$/);
|
||||||
|
});
|
||||||
|
cy.reload();
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-feedback-checkbox"]'
|
||||||
|
).should("have.class", "cy-checked");
|
||||||
|
|
||||||
|
// reopening page should get directly to last step
|
||||||
|
cy.visit("/course/test-lehrgang/learn/fahrzeug/feedback");
|
||||||
|
|
||||||
|
// check stored data
|
||||||
|
cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then(
|
||||||
|
(ac) => {
|
||||||
|
expect(ac.submitted).to.be.true;
|
||||||
|
expect(ac.data).to.deep.equal({
|
||||||
|
course_negative_feedback: "Ich bin unzufrieden mit einigen Sachen.",
|
||||||
|
course_positive_feedback: "Ich bin zufrieden mit den meisten Dingen.",
|
||||||
|
goal_attainment: 3,
|
||||||
|
instructor_competence: 2,
|
||||||
|
instructor_open_feedback: "Der Kursleiter ist eigentlich ganz nett.",
|
||||||
|
instructor_respect: 1,
|
||||||
|
preparation_task_clarity: false,
|
||||||
|
proficiency: 80,
|
||||||
|
satisfaction: 4,
|
||||||
|
would_recommend: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { login } from "../helpers";
|
||||||
|
|
||||||
|
describe("feedbackTrainer.cy.js", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit("/course/test-lehrgang/learn/fahrzeug/feedback");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can open feedback results page with empty results", () => {
|
||||||
|
cy.manageCommand("cypress_reset");
|
||||||
|
login("test-trainer1@example.com", "test");
|
||||||
|
cy.visit("/course/test-lehrgang/cockpit");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="show-feedback-btn-test-lehrgang-lp-circle-fahrzeug-lc-feedback"]'
|
||||||
|
).click();
|
||||||
|
|
||||||
|
cy.get('[data-cy="feedback-data-amount"]').should("contain", "0");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can open feedback results page with results", () => {
|
||||||
|
cy.manageCommand("cypress_reset --create-feedback-responses");
|
||||||
|
login("test-trainer1@example.com", "test");
|
||||||
|
cy.visit("/course/test-lehrgang/cockpit");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="show-feedback-btn-test-lehrgang-lp-circle-fahrzeug-lc-feedback"]'
|
||||||
|
).click();
|
||||||
|
|
||||||
|
cy.get('[data-cy="feedback-data-amount"]').should("contain", "3");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-1"]')
|
||||||
|
.find('[data-cy="rating-scale-average"]')
|
||||||
|
.should("contain", "3.3");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-2"]')
|
||||||
|
.find('[data-cy="rating-scale-average"]')
|
||||||
|
.should("contain", "3.0");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-3"]')
|
||||||
|
.find('[data-cy="percentage-value-40%"]')
|
||||||
|
.should("contain", "33.3");
|
||||||
|
cy.get('[data-cy="question-3"]')
|
||||||
|
.find('[data-cy="percentage-value-80%"]')
|
||||||
|
.should("contain", "33.3");
|
||||||
|
cy.get('[data-cy="question-3"]')
|
||||||
|
.find('[data-cy="percentage-value-100%"]')
|
||||||
|
.should("contain", "33.3");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-4"]')
|
||||||
|
.find('[data-cy="popover-yes"]')
|
||||||
|
.click()
|
||||||
|
.find('[data-cy="num-yes"]')
|
||||||
|
.should("contain", "3");
|
||||||
|
cy.get('[data-cy="question-4"]')
|
||||||
|
.find('[data-cy="popover-no"]')
|
||||||
|
.click()
|
||||||
|
.find('[data-cy="num-no"]')
|
||||||
|
.should("contain", "0");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-5"]')
|
||||||
|
.find('[data-cy="rating-scale-average"]')
|
||||||
|
.should("contain", "2.7");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-6"]')
|
||||||
|
.find('[data-cy="rating-scale-average"]')
|
||||||
|
.should("contain", "3.0");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-7"]')
|
||||||
|
.should("contain", "Super Kurs!")
|
||||||
|
.should("contain", "Super, bin begeistert")
|
||||||
|
.should("contain", "Ok, entspricht den Erwartungen");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-8"]')
|
||||||
|
.find('[data-cy="popover-yes"]')
|
||||||
|
.click()
|
||||||
|
.find('[data-cy="num-yes"]')
|
||||||
|
.should("contain", "2");
|
||||||
|
cy.get('[data-cy="question-8"]')
|
||||||
|
.find('[data-cy="popover-no"]')
|
||||||
|
.click()
|
||||||
|
.find('[data-cy="num-no"]')
|
||||||
|
.should("contain", "1");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-9"]')
|
||||||
|
.should("contain", "Nichts Schlechtes")
|
||||||
|
.should("contain", "Es wäre praktisch, Zugang zu einer FAQ zu haben.")
|
||||||
|
.should("contain", "Mehr Videos wären schön.");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-10"]')
|
||||||
|
.should("contain", "Nur Gutes.")
|
||||||
|
.should("contain", "Das Beispiel mit der Katze fand ich sehr gut")
|
||||||
|
.should("contain", "Die Präsentation war super");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -120,6 +120,7 @@ function loadObjectJson(
|
||||||
`.replace(/(?:\r\n|\r|\n)/g, "");
|
`.replace(/(?:\r\n|\r|\n)/g, "");
|
||||||
return cy.manageShellCommand(command).then((result) => {
|
return cy.manageShellCommand(command).then((result) => {
|
||||||
const objectJson = JSON.parse(result.stdout);
|
const objectJson = JSON.parse(result.stdout);
|
||||||
|
// console.log(command);
|
||||||
console.log(objectJson);
|
console.log(objectJson);
|
||||||
return objectJson;
|
return objectJson;
|
||||||
});
|
});
|
||||||
|
|
@ -135,6 +136,16 @@ Cypress.Commands.add("loadAssignmentCompletion", (key, value) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add("loadFeedbackResponse", (key, value) => {
|
||||||
|
return loadObjectJson(
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
"vbv_lernwelt.feedback.models.FeedbackResponse",
|
||||||
|
"vbv_lernwelt.feedback.serializers.CypressFeedbackResponseSerializer",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("makeSelfEvaluation", (answers) => {
|
Cypress.Commands.add("makeSelfEvaluation", (answers) => {
|
||||||
for (let i = 0; i < answers.length; i++) {
|
for (let i = 0; i < answers.length; i++) {
|
||||||
const answer = answers[i];
|
const answer = answers[i];
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ ADMIN_USER_ID = "872efd96-3bd7-4a1e-a239-2d72cad9f604"
|
||||||
TEST_TRAINER1_USER_ID = "b9e71f59-c44f-4290-b93a-9b3151e9a2fc"
|
TEST_TRAINER1_USER_ID = "b9e71f59-c44f-4290-b93a-9b3151e9a2fc"
|
||||||
TEST_STUDENT1_USER_ID = "65c73ad0-6d53-43a9-a4a4-64143f27b03a"
|
TEST_STUDENT1_USER_ID = "65c73ad0-6d53-43a9-a4a4-64143f27b03a"
|
||||||
TEST_STUDENT2_USER_ID = "19c40d94-15cc-4198-aaad-ef707c4b0900"
|
TEST_STUDENT2_USER_ID = "19c40d94-15cc-4198-aaad-ef707c4b0900"
|
||||||
|
TEST_STUDENT3_USER_ID = "bcf94dba-53bc-474b-a22d-e4af39aa042b"
|
||||||
|
|
||||||
TEST_COURSE_SESSION_BERN_ID = -1
|
TEST_COURSE_SESSION_BERN_ID = -1
|
||||||
TEST_COURSE_SESSION_ZURICH_ID = -2
|
TEST_COURSE_SESSION_ZURICH_ID = -2
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from vbv_lernwelt.core.constants import (
|
||||||
ADMIN_USER_ID,
|
ADMIN_USER_ID,
|
||||||
TEST_STUDENT1_USER_ID,
|
TEST_STUDENT1_USER_ID,
|
||||||
TEST_STUDENT2_USER_ID,
|
TEST_STUDENT2_USER_ID,
|
||||||
|
TEST_STUDENT3_USER_ID,
|
||||||
TEST_TRAINER1_USER_ID,
|
TEST_TRAINER1_USER_ID,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
|
|
@ -284,6 +285,13 @@ def create_default_users(user_model=User, group_model=Group, default_password=No
|
||||||
last_name="Student2",
|
last_name="Student2",
|
||||||
avatar_url="/static/avatars/uk1.lina.egger.jpg",
|
avatar_url="/static/avatars/uk1.lina.egger.jpg",
|
||||||
)
|
)
|
||||||
|
_create_student_user(
|
||||||
|
id=TEST_STUDENT3_USER_ID,
|
||||||
|
email="test-student3@example.com",
|
||||||
|
first_name="Test",
|
||||||
|
last_name="Student3",
|
||||||
|
avatar_url="/static/avatars/uk1.christian.koller.jpg",
|
||||||
|
)
|
||||||
_create_staff_user(
|
_create_staff_user(
|
||||||
email="matthias.wirth@vbv-afa.ch",
|
email="matthias.wirth@vbv-afa.ch",
|
||||||
first_name="Matthias",
|
first_name="Matthias",
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,20 @@ from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletion
|
||||||
from vbv_lernwelt.core.constants import (
|
from vbv_lernwelt.core.constants import (
|
||||||
TEST_COURSE_SESSION_BERN_ID,
|
TEST_COURSE_SESSION_BERN_ID,
|
||||||
TEST_STUDENT1_USER_ID,
|
TEST_STUDENT1_USER_ID,
|
||||||
|
TEST_STUDENT2_USER_ID,
|
||||||
|
TEST_STUDENT3_USER_ID,
|
||||||
TEST_TRAINER1_USER_ID,
|
TEST_TRAINER1_USER_ID,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.creators.test_course import (
|
from vbv_lernwelt.course.creators.test_course import (
|
||||||
create_edoniq_test_result_data,
|
create_edoniq_test_result_data,
|
||||||
|
create_feedback_response_data,
|
||||||
create_test_assignment_evaluation_data,
|
create_test_assignment_evaluation_data,
|
||||||
create_test_assignment_submitted_data,
|
create_test_assignment_submitted_data,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.models import CourseCompletion, CourseSession
|
from vbv_lernwelt.course.models import CourseCompletion, CourseSession
|
||||||
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
|
from vbv_lernwelt.learnpath.models import LearningContentFeedback
|
||||||
from vbv_lernwelt.notify.models import Notification
|
from vbv_lernwelt.notify.models import Notification
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,15 +37,22 @@ from vbv_lernwelt.notify.models import Notification
|
||||||
default=False,
|
default=False,
|
||||||
help="will create edoniq result data for test-student1@example.com",
|
help="will create edoniq result data for test-student1@example.com",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--create-feedback-responses/--no-create-feedback-responses",
|
||||||
|
default=False,
|
||||||
|
help="will create feedback response data",
|
||||||
|
)
|
||||||
def command(
|
def command(
|
||||||
create_assignment_completion,
|
create_assignment_completion,
|
||||||
create_assignment_evaluation,
|
create_assignment_evaluation,
|
||||||
create_edoniq_test_results,
|
create_edoniq_test_results,
|
||||||
|
create_feedback_responses,
|
||||||
):
|
):
|
||||||
print("cypress reset data")
|
print("cypress reset data")
|
||||||
CourseCompletion.objects.all().delete()
|
CourseCompletion.objects.all().delete()
|
||||||
Notification.objects.all().delete()
|
Notification.objects.all().delete()
|
||||||
AssignmentCompletion.objects.all().delete()
|
AssignmentCompletion.objects.all().delete()
|
||||||
|
FeedbackResponse.objects.all().delete()
|
||||||
User.objects.all().update(language="de")
|
User.objects.all().update(language="de")
|
||||||
User.objects.all().update(additional_json_data={})
|
User.objects.all().update(additional_json_data={})
|
||||||
|
|
||||||
|
|
@ -74,3 +86,66 @@ def command(
|
||||||
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
|
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
|
||||||
points=19,
|
points=19,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if create_feedback_responses:
|
||||||
|
print("create_feedback_responses")
|
||||||
|
course_session = CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID)
|
||||||
|
learning_content_feedback_page = LearningContentFeedback.objects.get(
|
||||||
|
slug="test-lehrgang-lp-circle-fahrzeug-lc-feedback"
|
||||||
|
)
|
||||||
|
create_feedback_response_data(
|
||||||
|
feedback_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
|
||||||
|
course_session=course_session,
|
||||||
|
learning_content_feedback_page=learning_content_feedback_page,
|
||||||
|
submitted=True,
|
||||||
|
feedback_data={
|
||||||
|
"satisfaction": 4,
|
||||||
|
"goal_attainment": 3,
|
||||||
|
"proficiency": 80,
|
||||||
|
"preparation_task_clarity": True,
|
||||||
|
"instructor_competence": 4,
|
||||||
|
"instructor_respect": 4,
|
||||||
|
"instructor_open_feedback": "Super Kurs!",
|
||||||
|
"would_recommend": True,
|
||||||
|
"course_negative_feedback": "Nichts Schlechtes",
|
||||||
|
"course_positive_feedback": "Nur Gutes.",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
create_feedback_response_data(
|
||||||
|
feedback_user=User.objects.get(id=TEST_STUDENT2_USER_ID),
|
||||||
|
course_session=course_session,
|
||||||
|
learning_content_feedback_page=learning_content_feedback_page,
|
||||||
|
submitted=True,
|
||||||
|
feedback_data={
|
||||||
|
"satisfaction": 4,
|
||||||
|
"goal_attainment": 4,
|
||||||
|
"proficiency": 100,
|
||||||
|
"preparation_task_clarity": True,
|
||||||
|
"instructor_competence": 3,
|
||||||
|
"instructor_respect": 3,
|
||||||
|
"instructor_open_feedback": "Super, bin begeistert",
|
||||||
|
"would_recommend": True,
|
||||||
|
"course_negative_feedback": "Es wäre praktisch, Zugang zu einer FAQ zu haben.",
|
||||||
|
"course_positive_feedback": "Das Beispiel mit der Katze fand ich sehr gut veranschaulicht!",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
create_feedback_response_data(
|
||||||
|
feedback_user=User.objects.get(id=TEST_STUDENT3_USER_ID),
|
||||||
|
course_session=course_session,
|
||||||
|
learning_content_feedback_page=learning_content_feedback_page,
|
||||||
|
submitted=True,
|
||||||
|
feedback_data={
|
||||||
|
"satisfaction": 2,
|
||||||
|
"goal_attainment": 2,
|
||||||
|
"proficiency": 40,
|
||||||
|
"preparation_task_clarity": True,
|
||||||
|
"instructor_competence": 1,
|
||||||
|
"instructor_respect": 2,
|
||||||
|
"instructor_open_feedback": "Ok, entspricht den Erwartungen",
|
||||||
|
"would_recommend": False,
|
||||||
|
"course_negative_feedback": "Mehr Videos wären schön.",
|
||||||
|
"course_positive_feedback": "Die Präsentation war super",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ from vbv_lernwelt.course_session.models import (
|
||||||
CourseSessionAssignment,
|
CourseSessionAssignment,
|
||||||
CourseSessionAttendanceCourse,
|
CourseSessionAttendanceCourse,
|
||||||
)
|
)
|
||||||
|
from vbv_lernwelt.feedback.services import update_feedback_response
|
||||||
from vbv_lernwelt.learnpath.models import (
|
from vbv_lernwelt.learnpath.models import (
|
||||||
Circle,
|
Circle,
|
||||||
LearningContentAssignment,
|
LearningContentAssignment,
|
||||||
|
|
@ -200,6 +201,12 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
|
||||||
user=student2,
|
user=student2,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
student3 = User.objects.get(email="test-student3@example.com")
|
||||||
|
_csu = CourseSessionUser.objects.create(
|
||||||
|
course_session=cs_bern,
|
||||||
|
user=student3,
|
||||||
|
)
|
||||||
|
|
||||||
return course
|
return course
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -286,6 +293,36 @@ def create_edoniq_test_result_data(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_feedback_response_data(
|
||||||
|
course_session,
|
||||||
|
feedback_user,
|
||||||
|
learning_content_feedback_page,
|
||||||
|
submitted=True,
|
||||||
|
feedback_data=None,
|
||||||
|
):
|
||||||
|
if feedback_data is None:
|
||||||
|
feedback_data = {
|
||||||
|
"satisfaction": 4,
|
||||||
|
"goal_attainment": 3,
|
||||||
|
"proficiency": 80,
|
||||||
|
"preparation_task_clarity": True,
|
||||||
|
"instructor_competence": 4,
|
||||||
|
"instructor_respect": 4,
|
||||||
|
"instructor_open_feedback": "Super Kurs!",
|
||||||
|
"would_recommend": True,
|
||||||
|
"course_negative_feedback": "Nichts Schlechtes",
|
||||||
|
"course_positive_feedback": "Nur Gutes.",
|
||||||
|
}
|
||||||
|
|
||||||
|
return update_feedback_response(
|
||||||
|
feedback_user=feedback_user,
|
||||||
|
course_session=course_session,
|
||||||
|
learning_content_feedback_page=learning_content_feedback_page,
|
||||||
|
submitted=submitted,
|
||||||
|
validated_data=feedback_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_test_course_with_categories(apps=None, schema_editor=None):
|
def create_test_course_with_categories(apps=None, schema_editor=None):
|
||||||
if apps is not None:
|
if apps is not None:
|
||||||
Course = apps.get_model("course", "Course")
|
Course = apps.get_model("course", "Course")
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@ from vbv_lernwelt.course_session.models import (
|
||||||
CourseSessionAssignment,
|
CourseSessionAssignment,
|
||||||
CourseSessionAttendanceCourse,
|
CourseSessionAttendanceCourse,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.feedback.creators.create_demo_feedback import create_feedback
|
|
||||||
from vbv_lernwelt.importer.services import (
|
from vbv_lernwelt.importer.services import (
|
||||||
import_course_sessions_from_excel,
|
import_course_sessions_from_excel,
|
||||||
import_students_from_excel,
|
import_students_from_excel,
|
||||||
|
|
@ -237,10 +236,11 @@ def create_versicherungsvermittlerin_course(
|
||||||
circles = Circle.objects.filter(
|
circles = Circle.objects.filter(
|
||||||
slug__startswith="versicherungsvermittler-in-lp"
|
slug__startswith="versicherungsvermittler-in-lp"
|
||||||
)
|
)
|
||||||
for i, circle in enumerate(circles):
|
|
||||||
expert = experts[i % len(experts)]
|
# for i, circle in enumerate(circles):
|
||||||
expert.expert.add(circle)
|
# expert = experts[i % len(experts)]
|
||||||
create_feedback(circle, cs, 3)
|
# expert.expert.add(circle)
|
||||||
|
# create_feedback(circle, cs, 3)
|
||||||
|
|
||||||
for admin_email in ADMIN_EMAILS:
|
for admin_email in ADMIN_EMAILS:
|
||||||
CourseSessionUser.objects.create(
|
CourseSessionUser.objects.create(
|
||||||
|
|
@ -395,19 +395,20 @@ def create_course_uk_de_course_sessions():
|
||||||
user=User.objects.get(username="patrick.muster@eiger-versicherungen.ch"),
|
user=User.objects.get(username="patrick.muster@eiger-versicherungen.ch"),
|
||||||
)
|
)
|
||||||
|
|
||||||
create_feedback(
|
# TODO: feedback must now contain a `feedback_user`
|
||||||
Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-kickoff"),
|
# create_feedback(
|
||||||
cs,
|
# Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-kickoff"),
|
||||||
3,
|
# cs,
|
||||||
)
|
# 3,
|
||||||
create_feedback(
|
# )
|
||||||
Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-haushalt-teil-2"),
|
# create_feedback(
|
||||||
cs,
|
# Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-haushalt-teil-2"),
|
||||||
14,
|
# cs,
|
||||||
)
|
# 14,
|
||||||
create_feedback(
|
# )
|
||||||
Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-basis"), cs, 4
|
# create_feedback(
|
||||||
)
|
# Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-basis"), cs, 4
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
def create_course_uk_fr():
|
def create_course_uk_fr():
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,18 @@ def has_course_access(user, course_id):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def has_course_session_access(user, course_session_id: int):
|
||||||
|
if user.is_superuser:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if CourseSessionUser.objects.filter(
|
||||||
|
course_session_id=course_session_id, user=user
|
||||||
|
).exists():
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def is_course_session_expert(user, course_session_id: int):
|
def is_course_session_expert(user, course_session_id: int):
|
||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class EdoniqUserExportTestCase(TestCase):
|
||||||
|
|
||||||
def test_fetch_course_session_users(self):
|
def test_fetch_course_session_users(self):
|
||||||
users = fetch_course_session_users([COURSE_TEST_ID], excluded_domains=[])
|
users = fetch_course_session_users([COURSE_TEST_ID], excluded_domains=[])
|
||||||
self.assertEqual(len(users), 2)
|
self.assertEqual(len(users), 3)
|
||||||
|
|
||||||
def test_fetch_course_session_trainers(self):
|
def test_fetch_course_session_trainers(self):
|
||||||
users = fetch_course_session_users(
|
users = fetch_course_session_users(
|
||||||
|
|
@ -51,11 +51,11 @@ class EdoniqUserExportTestCase(TestCase):
|
||||||
users = fetch_course_session_users(
|
users = fetch_course_session_users(
|
||||||
[COURSE_TEST_ID], excluded_domains=["eiger-versicherungen.ch"]
|
[COURSE_TEST_ID], excluded_domains=["eiger-versicherungen.ch"]
|
||||||
)
|
)
|
||||||
self.assertEqual(len(users), 1)
|
self.assertEqual(len(users), 2)
|
||||||
|
|
||||||
def test_export_students_and_trainers(self):
|
def test_export_students_and_trainers(self):
|
||||||
users = fetch_course_session_all_users([COURSE_TEST_ID], excluded_domains=[])
|
users = fetch_course_session_all_users([COURSE_TEST_ID], excluded_domains=[])
|
||||||
self.assertEqual(len(users), 3)
|
self.assertEqual(len(users), 4)
|
||||||
|
|
||||||
def test_deduplicates_users(self):
|
def test_deduplicates_users(self):
|
||||||
trainer1 = User.objects.get(email="test-trainer1@example.com")
|
trainer1 = User.objects.get(email="test-trainer1@example.com")
|
||||||
|
|
@ -67,7 +67,7 @@ class EdoniqUserExportTestCase(TestCase):
|
||||||
user=trainer1,
|
user=trainer1,
|
||||||
)
|
)
|
||||||
users = fetch_course_session_all_users([COURSE_TEST_ID], excluded_domains=[])
|
users = fetch_course_session_all_users([COURSE_TEST_ID], excluded_domains=[])
|
||||||
self.assertEqual(len(users), 3)
|
self.assertEqual(len(users), 4)
|
||||||
|
|
||||||
def test_response_csv(self):
|
def test_response_csv(self):
|
||||||
users = fetch_course_session_users([COURSE_TEST_ID], excluded_domains=[])
|
users = fetch_course_session_users([COURSE_TEST_ID], excluded_domains=[])
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,5 @@ from vbv_lernwelt.learnpath.models import Circle
|
||||||
|
|
||||||
def create_feedback(circle: Circle, course_session: CourseSession, amount: int):
|
def create_feedback(circle: Circle, course_session: CourseSession, amount: int):
|
||||||
for _i in range(amount):
|
for _i in range(amount):
|
||||||
|
# FIXME needs `feedback_user` to work again
|
||||||
FeedbackResponseFactory(circle=circle, course_session=course_session).save()
|
FeedbackResponseFactory(circle=circle, course_session=course_session).save()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class FeedbackResponseFactory(DjangoModelFactory):
|
||||||
[
|
[
|
||||||
"Alles gut, manchmal etwas langfädig",
|
"Alles gut, manchmal etwas langfädig",
|
||||||
"Super, bin begeistert",
|
"Super, bin begeistert",
|
||||||
"Ok, enspricht den Erwartungen",
|
"Ok, entspricht den Erwartungen",
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
"would_recommend": FuzzyChoice([True, False]),
|
"would_recommend": FuzzyChoice([True, False]),
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,94 @@
|
||||||
|
import graphene
|
||||||
import structlog
|
import structlog
|
||||||
from graphene import ClientIDMutation, Field, Int, List
|
|
||||||
from graphene.types.generic import GenericScalar
|
from graphene.types.generic import GenericScalar
|
||||||
from graphene_django.types import ErrorType
|
from graphene_django.types import ErrorType
|
||||||
|
|
||||||
from vbv_lernwelt.course.models import CourseSession
|
from vbv_lernwelt.course.models import CourseSession
|
||||||
from vbv_lernwelt.feedback.graphql.types import FeedbackResponse as FeedbackResponseType
|
from vbv_lernwelt.course.permissions import has_course_session_access
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
from vbv_lernwelt.feedback.graphql.types import (
|
||||||
|
FeedbackResponseObjectType as FeedbackResponseType,
|
||||||
|
)
|
||||||
from vbv_lernwelt.feedback.serializers import CourseFeedbackSerializer
|
from vbv_lernwelt.feedback.serializers import CourseFeedbackSerializer
|
||||||
from wagtail.models import Page
|
from vbv_lernwelt.feedback.services import update_feedback_response
|
||||||
|
from vbv_lernwelt.learnpath.models import LearningContentFeedback
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# https://medium.com/open-graphql/jsonfield-models-in-graphene-django-308ae43d14ee
|
# https://medium.com/open-graphql/jsonfield-models-in-graphene-django-308ae43d14ee
|
||||||
class SendFeedback(ClientIDMutation):
|
class SendFeedbackMutation(graphene.Mutation):
|
||||||
feedback_response = Field(FeedbackResponseType)
|
feedback_response = graphene.Field(FeedbackResponseType)
|
||||||
errors = List(
|
errors = graphene.List(
|
||||||
ErrorType, description="May contain more than one error for same field."
|
ErrorType, description="May contain more than one error for same field."
|
||||||
)
|
)
|
||||||
|
|
||||||
class Input:
|
class Arguments:
|
||||||
page = Int(required=True)
|
course_session_id = graphene.ID(required=True)
|
||||||
course_session = Int(required=True)
|
learning_content_page_id = graphene.ID(required=True)
|
||||||
data = GenericScalar()
|
data = GenericScalar()
|
||||||
|
submitted = graphene.Boolean(required=False, default_value=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mutate_and_get_payload(cls, _, info, **input):
|
def mutate(
|
||||||
page_id = input["page"]
|
cls,
|
||||||
course_session_id = input["course_session"]
|
root,
|
||||||
logger.info("creating feedback")
|
info,
|
||||||
|
course_session_id,
|
||||||
learning_content = Page.objects.get(id=page_id)
|
learning_content_page_id,
|
||||||
circle = learning_content.get_parent().specific
|
data,
|
||||||
|
submitted,
|
||||||
|
):
|
||||||
|
feedback_user_id = info.context.user.id
|
||||||
|
learning_content = LearningContentFeedback.objects.get(
|
||||||
|
id=learning_content_page_id
|
||||||
|
)
|
||||||
|
circle = learning_content.get_circle()
|
||||||
course_session = CourseSession.objects.get(id=course_session_id)
|
course_session = CourseSession.objects.get(id=course_session_id)
|
||||||
data = input.get("data", {})
|
|
||||||
|
if not has_course_session_access(
|
||||||
|
info.context.user,
|
||||||
|
course_session.id,
|
||||||
|
):
|
||||||
|
return SendFeedbackMutation(
|
||||||
|
errors=[
|
||||||
|
ErrorType(
|
||||||
|
field="send_feedback", messages=["Insufficient permissions"]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"creating feedback",
|
||||||
|
label="feedback",
|
||||||
|
feedback_user_id=feedback_user_id,
|
||||||
|
circle_title=circle.title,
|
||||||
|
course_session_id=course_session_id,
|
||||||
|
)
|
||||||
|
|
||||||
serializer = CourseFeedbackSerializer(data=data)
|
serializer = CourseFeedbackSerializer(data=data)
|
||||||
|
|
||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
logger.error(serializer.errors)
|
logger.error(
|
||||||
return SendFeedback(errors=serializer.errors)
|
"creating feedback serializer invalid",
|
||||||
|
error_list=serializer.errors,
|
||||||
|
label="feedback",
|
||||||
|
)
|
||||||
|
errors = [
|
||||||
|
ErrorType(field=field, messages=msgs)
|
||||||
|
for field, msgs in serializer.errors.items()
|
||||||
|
]
|
||||||
|
return SendFeedbackMutation(errors=errors)
|
||||||
|
|
||||||
feedback_response = FeedbackResponse.objects.create(
|
feedback_response = update_feedback_response(
|
||||||
circle=circle,
|
feedback_user=info.context.user,
|
||||||
course_session=course_session,
|
course_session=course_session,
|
||||||
data=serializer.validated_data,
|
learning_content_feedback_page=learning_content,
|
||||||
|
submitted=submitted,
|
||||||
|
validated_data=serializer.validated_data,
|
||||||
)
|
)
|
||||||
logger.info(feedback_response)
|
|
||||||
|
|
||||||
return SendFeedback(feedback_response=feedback_response)
|
return SendFeedbackMutation(feedback_response=feedback_response)
|
||||||
|
|
||||||
|
|
||||||
class FeedbackMutation(object):
|
class FeedbackMutation(object):
|
||||||
send_feedback = SendFeedback.Field()
|
send_feedback = SendFeedbackMutation.Field()
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
from graphene.relay import Node
|
|
||||||
from graphene.types.generic import GenericScalar
|
from graphene.types.generic import GenericScalar
|
||||||
from graphene_django import DjangoObjectType
|
from graphene_django import DjangoObjectType
|
||||||
|
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse as FeedbackResponseModel
|
from vbv_lernwelt.feedback.models import FeedbackResponse as FeedbackResponseModel
|
||||||
|
|
||||||
|
|
||||||
class FeedbackResponse(DjangoObjectType):
|
class FeedbackResponseObjectType(DjangoObjectType):
|
||||||
data = GenericScalar()
|
data = GenericScalar()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FeedbackResponseModel
|
model = FeedbackResponseModel
|
||||||
interfaces = (Node,)
|
fields = [
|
||||||
|
"id",
|
||||||
|
"submitted",
|
||||||
|
"data",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-09-21 13:30
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("feedback", "0003_alter_feedbackresponse_course_session"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="feedbackresponse",
|
||||||
|
name="feedback_user",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
default="872efd96-3bd7-4a1e-a239-2d72cad9f604",
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="core.user",
|
||||||
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-09-22 09:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("feedback", "0004_feedbackresponse_feedback_user"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="feedbackresponse",
|
||||||
|
name="submitted",
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="feedbackresponse",
|
||||||
|
name="notification_sent",
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="feedbackresponse",
|
||||||
|
name="updated_at",
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-09-22 09:31
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def set_feedback_submitted_true(apps, schema_editor):
|
||||||
|
FeedbackResponse = apps.get_model("feedback", "FeedbackResponse")
|
||||||
|
FeedbackResponse.objects.update(submitted=True)
|
||||||
|
FeedbackResponse.objects.update(notification_sent=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("feedback", "0005_auto_20230922_1131"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(set_feedback_submitted_true),
|
||||||
|
]
|
||||||
|
|
@ -25,6 +25,7 @@ class FeedbackIntegerField(models.IntegerField):
|
||||||
|
|
||||||
class FeedbackResponse(models.Model):
|
class FeedbackResponse(models.Model):
|
||||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
|
feedback_user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
|
||||||
class DiscoveredChoices(models.TextChoices):
|
class DiscoveredChoices(models.TextChoices):
|
||||||
INTERNET = "I", _("Internet")
|
INTERNET = "I", _("Internet")
|
||||||
|
|
@ -48,14 +49,10 @@ class FeedbackResponse(models.Model):
|
||||||
HUNDRED = 100, "100%"
|
HUNDRED = 100, "100%"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
# with `id=UUIDField` it is always set...
|
|
||||||
create_new = self._state.adding
|
|
||||||
|
|
||||||
super(FeedbackResponse, self).save(*args, **kwargs)
|
super(FeedbackResponse, self).save(*args, **kwargs)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if create_new:
|
if self.submitted and not self.notification_sent:
|
||||||
# with `id=UUIDField` it is always set...
|
|
||||||
course_session_users = CourseSessionUser.objects.filter(
|
course_session_users = CourseSessionUser.objects.filter(
|
||||||
role="EXPERT",
|
role="EXPERT",
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
|
|
@ -66,6 +63,8 @@ class FeedbackResponse(models.Model):
|
||||||
recipient=csu.user,
|
recipient=csu.user,
|
||||||
feedback_response=self,
|
feedback_response=self,
|
||||||
)
|
)
|
||||||
|
self.notification_sent = True
|
||||||
|
self.save()
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
"Failed to send feedback notification",
|
"Failed to send feedback notification",
|
||||||
|
|
@ -75,6 +74,9 @@ class FeedbackResponse(models.Model):
|
||||||
|
|
||||||
data = models.JSONField(default=dict)
|
data = models.JSONField(default=dict)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
submitted = models.BooleanField(default=False)
|
||||||
|
notification_sent = models.BooleanField(default=False)
|
||||||
|
|
||||||
circle = models.ForeignKey("learnpath.Circle", models.PROTECT)
|
circle = models.ForeignKey("learnpath.Circle", models.PROTECT)
|
||||||
course_session = models.ForeignKey("course.CourseSession", models.CASCADE)
|
course_session = models.ForeignKey("course.CourseSession", models.CASCADE)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import structlog
|
import structlog
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -29,3 +31,9 @@ class CourseFeedbackSerializer(serializers.Serializer):
|
||||||
course_negative_feedback = serializers.CharField(
|
course_negative_feedback = serializers.CharField(
|
||||||
required=False, allow_null=True, allow_blank=True
|
required=False, allow_null=True, allow_blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CypressFeedbackResponseSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = FeedbackResponse
|
||||||
|
fields = "__all__"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
import structlog
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSession
|
||||||
|
from vbv_lernwelt.course.services import mark_course_completion
|
||||||
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
|
from vbv_lernwelt.learnpath.models import LearningContentFeedback
|
||||||
|
|
||||||
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def update_feedback_response(
|
||||||
|
feedback_user: User,
|
||||||
|
course_session: CourseSession,
|
||||||
|
learning_content_feedback_page: LearningContentFeedback,
|
||||||
|
submitted: bool,
|
||||||
|
validated_data: dict,
|
||||||
|
):
|
||||||
|
circle = learning_content_feedback_page.get_circle()
|
||||||
|
|
||||||
|
feedback_response, _ = FeedbackResponse.objects.get_or_create(
|
||||||
|
feedback_user_id=feedback_user.id,
|
||||||
|
circle_id=circle.id,
|
||||||
|
course_session=course_session,
|
||||||
|
)
|
||||||
|
|
||||||
|
original_data = feedback_response.data
|
||||||
|
updated_data = validated_data
|
||||||
|
initial_data = {
|
||||||
|
"satisfaction": None,
|
||||||
|
"goal_attainment": None,
|
||||||
|
"proficiency": None,
|
||||||
|
"preparation_task_clarity": None,
|
||||||
|
"instructor_competence": None,
|
||||||
|
"instructor_respect": None,
|
||||||
|
"instructor_open_feedback": "",
|
||||||
|
"would_recommend": None,
|
||||||
|
"course_negative_feedback": "",
|
||||||
|
"course_positive_feedback": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
merged_data = initial_data | {
|
||||||
|
key: updated_data[key]
|
||||||
|
if updated_data.get(key, "") != ""
|
||||||
|
else original_data.get(key)
|
||||||
|
for key in initial_data.keys()
|
||||||
|
}
|
||||||
|
|
||||||
|
feedback_response.data = merged_data
|
||||||
|
|
||||||
|
# save the response before completion mark,
|
||||||
|
# because who knows what could happen in between...
|
||||||
|
if submitted:
|
||||||
|
feedback_response.submitted = submitted
|
||||||
|
feedback_response.save()
|
||||||
|
|
||||||
|
if submitted:
|
||||||
|
mark_course_completion(
|
||||||
|
user=feedback_user,
|
||||||
|
page=learning_content_feedback_page,
|
||||||
|
course_session=course_session,
|
||||||
|
completion_status=CourseCompletionStatus.SUCCESS.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"feedback successfully created",
|
||||||
|
label="feedback",
|
||||||
|
feedback_user_id=feedback_user.id,
|
||||||
|
circle_title=circle.title,
|
||||||
|
course_session_id=course_session.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
return feedback_response
|
||||||
|
|
@ -2,7 +2,6 @@ from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
|
||||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
from vbv_lernwelt.feedback.factories import FeedbackResponseFactory
|
from vbv_lernwelt.feedback.factories import FeedbackResponseFactory
|
||||||
|
|
@ -15,70 +14,35 @@ from vbv_lernwelt.notify.models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class FeedbackApiBaseTestCase(APITestCase):
|
class FeedbackBaseTestCase(APITestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
create_default_users()
|
create_default_users()
|
||||||
create_test_course()
|
create_test_course(include_vv=False, with_sessions=True)
|
||||||
|
self.course_session = CourseSession.objects.get(title="Test Bern 2022 a")
|
||||||
self.user = User.objects.get(username="student")
|
self.trainer = User.objects.get(username="test-trainer1@example.com")
|
||||||
self.expert = User.objects.get(
|
self.student = User.objects.get(username="test-student1@example.com")
|
||||||
username="patrizia.huggel@eiger-versicherungen.ch"
|
self.circle_basis = Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug")
|
||||||
)
|
|
||||||
|
|
||||||
self.course_session = CourseSession.objects.create(
|
|
||||||
course_id=COURSE_TEST_ID,
|
|
||||||
title="Test Lehrgang Session",
|
|
||||||
)
|
|
||||||
|
|
||||||
csu = CourseSessionUser.objects.create(
|
|
||||||
course_session=self.course_session,
|
|
||||||
user=User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch"),
|
|
||||||
role=CourseSessionUser.Role.EXPERT,
|
|
||||||
)
|
|
||||||
csu.expert.add(Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug"))
|
|
||||||
|
|
||||||
_csu = CourseSessionUser.objects.create(
|
|
||||||
course_session=self.course_session,
|
|
||||||
user=self.user,
|
|
||||||
role=CourseSessionUser.Role.MEMBER,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.test_data = {
|
|
||||||
"file_name": "test.pdf",
|
|
||||||
"file_type": "application/pdf",
|
|
||||||
"name": "Test",
|
|
||||||
"course_session": self.course_session.id,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client.login(
|
|
||||||
username="patrizia.huggel@eiger-versicherungen.ch", password="myvbv1234"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FeedbackSummaryApiTestCase(FeedbackApiBaseTestCase):
|
class FeedbackNotificationTestCase(FeedbackBaseTestCase):
|
||||||
def test_triggers_notification(self):
|
def test_creating_submitted_feedback_triggers_notification(self):
|
||||||
expert = User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch")
|
|
||||||
csu = CourseSessionUser.objects.get(
|
|
||||||
course_session=self.course_session,
|
|
||||||
user=expert,
|
|
||||||
role=CourseSessionUser.Role.EXPERT,
|
|
||||||
)
|
|
||||||
basis_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-reisen")
|
|
||||||
csu.expert.add(basis_circle)
|
|
||||||
|
|
||||||
feedback = FeedbackResponse.objects.create(
|
feedback = FeedbackResponse.objects.create(
|
||||||
circle=basis_circle, course_session=csu.course_session
|
circle=self.circle_basis,
|
||||||
|
course_session=self.course_session,
|
||||||
|
feedback_user=self.student,
|
||||||
|
submitted=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(Notification.objects.count(), 1)
|
self.assertEqual(Notification.objects.count(), 1)
|
||||||
notification = Notification.objects.first()
|
notification = Notification.objects.first()
|
||||||
self.assertEqual(notification.recipient, expert)
|
self.assertEqual(notification.recipient, self.trainer)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
notification.verb, f"Feedback abgeschickt für Circle «{basis_circle.title}»"
|
notification.verb,
|
||||||
|
f"Feedback abgeschickt für Circle «{self.circle_basis.title}»",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
notification.target_url,
|
notification.target_url,
|
||||||
f"/course/{self.course_session.course.slug}/cockpit/feedback/{basis_circle.id}/",
|
f"/course/{self.course_session.course.slug}/cockpit/feedback/{self.circle_basis.id}/",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
notification.notification_category, NotificationCategory.INFORMATION
|
notification.notification_category, NotificationCategory.INFORMATION
|
||||||
|
|
@ -87,121 +51,22 @@ class FeedbackSummaryApiTestCase(FeedbackApiBaseTestCase):
|
||||||
notification.notification_trigger, NotificationTrigger.NEW_FEEDBACK
|
notification.notification_trigger, NotificationTrigger.NEW_FEEDBACK
|
||||||
)
|
)
|
||||||
self.assertEqual(notification.action_object, feedback)
|
self.assertEqual(notification.action_object, feedback)
|
||||||
self.assertEqual(notification.course_session, csu.course_session)
|
self.assertEqual(notification.course_session, self.course_session)
|
||||||
|
|
||||||
def test_triggers_notification_only_on_create(self):
|
def test_only_submitted_feedback_triggers_notification(self):
|
||||||
expert = User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch")
|
feedback = FeedbackResponse.objects.create(
|
||||||
csu = CourseSessionUser.objects.get(
|
circle=self.circle_basis,
|
||||||
course_session=self.course_session,
|
course_session=self.course_session,
|
||||||
user=expert,
|
feedback_user=self.student,
|
||||||
role=CourseSessionUser.Role.EXPERT,
|
|
||||||
)
|
|
||||||
basis_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-reisen")
|
|
||||||
csu.expert.add(basis_circle)
|
|
||||||
|
|
||||||
feedback = FeedbackResponseFactory(
|
|
||||||
circle=basis_circle, course_session=csu.course_session
|
|
||||||
)
|
|
||||||
feedback.save()
|
|
||||||
|
|
||||||
# Check that the initial notification was created and then deleted
|
|
||||||
self.assertEqual(len(Notification.objects.all()), 1)
|
|
||||||
Notification.objects.all().delete()
|
|
||||||
self.assertEqual(len(Notification.objects.all()), 0)
|
|
||||||
|
|
||||||
# Check that an update of the feedback does not trigger a notification
|
|
||||||
feedback.name = "Test2"
|
|
||||||
feedback.save()
|
|
||||||
self.assertEqual(len(Notification.objects.all()), 0)
|
|
||||||
|
|
||||||
def test_can_get_feedback_summary_for_circles(self):
|
|
||||||
number_reisen_feedback = 5
|
|
||||||
number_fahrzeug_feedback = 10
|
|
||||||
|
|
||||||
csu = CourseSessionUser.objects.get(
|
|
||||||
course_session=self.course_session,
|
|
||||||
user=User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch"),
|
|
||||||
role=CourseSessionUser.Role.EXPERT,
|
|
||||||
)
|
|
||||||
fahrzeug_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug")
|
|
||||||
reisen_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-reisen")
|
|
||||||
csu.expert.add(reisen_circle)
|
|
||||||
|
|
||||||
for i in range(number_reisen_feedback):
|
|
||||||
FeedbackResponseFactory(
|
|
||||||
circle=reisen_circle, course_session=csu.course_session
|
|
||||||
).save()
|
|
||||||
|
|
||||||
for i in range(number_fahrzeug_feedback):
|
|
||||||
FeedbackResponseFactory(
|
|
||||||
circle=fahrzeug_circle, course_session=csu.course_session
|
|
||||||
).save()
|
|
||||||
|
|
||||||
response = self.client.get(
|
|
||||||
f"/api/core/feedback/{csu.course_session.id}/summary/"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(Notification.objects.count(), 0)
|
||||||
expected = [
|
|
||||||
{"circle_id": fahrzeug_circle.id, "count": number_fahrzeug_feedback},
|
|
||||||
{"circle_id": reisen_circle.id, "count": number_reisen_feedback},
|
|
||||||
]
|
|
||||||
self.assertEqual(response.data, expected)
|
|
||||||
|
|
||||||
def test_can_only_see_feedback_from_own_circle(self):
|
|
||||||
number_basis_feedback = 5
|
|
||||||
number_analyse_feedback = 10
|
|
||||||
|
|
||||||
csu = CourseSessionUser.objects.get(
|
|
||||||
course_session=self.course_session,
|
|
||||||
user=User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch"),
|
|
||||||
role=CourseSessionUser.Role.EXPERT,
|
|
||||||
)
|
|
||||||
fahrzeug_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug")
|
|
||||||
reisen_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-reisen")
|
|
||||||
|
|
||||||
for i in range(number_basis_feedback):
|
|
||||||
FeedbackResponseFactory(
|
|
||||||
circle=reisen_circle, course_session=csu.course_session
|
|
||||||
).save()
|
|
||||||
|
|
||||||
for i in range(number_analyse_feedback):
|
|
||||||
FeedbackResponseFactory(
|
|
||||||
circle=fahrzeug_circle, course_session=csu.course_session
|
|
||||||
).save()
|
|
||||||
|
|
||||||
response = self.client.get(
|
|
||||||
f"/api/core/feedback/{csu.course_session.id}/summary/"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
expected = [
|
|
||||||
{"circle_id": fahrzeug_circle.id, "count": number_analyse_feedback},
|
|
||||||
]
|
|
||||||
self.assertEqual(response.data, expected)
|
|
||||||
|
|
||||||
def test_student_does_not_see_feedback(self):
|
|
||||||
self.client.login(username="student", password="test")
|
|
||||||
csu = CourseSessionUser.objects.get(
|
|
||||||
course_session=self.course_session,
|
|
||||||
user=self.user,
|
|
||||||
)
|
|
||||||
fahrzeug_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug")
|
|
||||||
FeedbackResponseFactory(
|
|
||||||
circle=fahrzeug_circle, course_session=csu.course_session
|
|
||||||
).save()
|
|
||||||
|
|
||||||
response = self.client.get(
|
|
||||||
f"/api/core/feedback/{csu.course_session.id}/summary/"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertEqual(response.data, [])
|
|
||||||
|
|
||||||
|
|
||||||
class FeedbackDetailApiTestCase(FeedbackApiBaseTestCase):
|
class FeedbackRestApiTestCase(FeedbackBaseTestCase):
|
||||||
def test_can_receive_feedback(self):
|
def setUp(self) -> None:
|
||||||
feedback_data = {
|
super().setUp()
|
||||||
|
self.feedback_data = {
|
||||||
"satisfaction": [1, 4, 2],
|
"satisfaction": [1, 4, 2],
|
||||||
"goal_attainment": [2, 4, 3],
|
"goal_attainment": [2, 4, 3],
|
||||||
"proficiency": [20, 60, 80],
|
"proficiency": [20, 60, 80],
|
||||||
|
|
@ -213,81 +78,83 @@ class FeedbackDetailApiTestCase(FeedbackApiBaseTestCase):
|
||||||
"course_positive_feedback": ["Bla", "Katze", "Hund"],
|
"course_positive_feedback": ["Bla", "Katze", "Hund"],
|
||||||
"course_negative_feedback": ["Maus", "Hase", "Fuchs"],
|
"course_negative_feedback": ["Maus", "Hase", "Fuchs"],
|
||||||
}
|
}
|
||||||
csu = CourseSessionUser.objects.get(
|
|
||||||
course_session=self.course_session,
|
self.students = [
|
||||||
user=User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch"),
|
self.student,
|
||||||
role=CourseSessionUser.Role.EXPERT,
|
User.objects.get(username="test-student2@example.com"),
|
||||||
)
|
User.objects.get(username="test-student3@example.com"),
|
||||||
circle = Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug")
|
]
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
FeedbackResponseFactory(
|
FeedbackResponseFactory(
|
||||||
circle=circle,
|
circle=self.circle_basis,
|
||||||
course_session=csu.course_session,
|
course_session=self.course_session,
|
||||||
data={
|
data={
|
||||||
"satisfaction": feedback_data["satisfaction"][i],
|
"satisfaction": self.feedback_data["satisfaction"][i],
|
||||||
"goal_attainment": feedback_data["goal_attainment"][i],
|
"goal_attainment": self.feedback_data["goal_attainment"][i],
|
||||||
"proficiency": feedback_data["proficiency"][i],
|
"proficiency": self.feedback_data["proficiency"][i],
|
||||||
"preparation_task_clarity": feedback_data[
|
"preparation_task_clarity": self.feedback_data[
|
||||||
"preparation_task_clarity"
|
"preparation_task_clarity"
|
||||||
][i],
|
][i],
|
||||||
"instructor_competence": feedback_data["instructor_competence"][i],
|
"instructor_competence": self.feedback_data[
|
||||||
"instructor_open_feedback": feedback_data[
|
"instructor_competence"
|
||||||
|
][i],
|
||||||
|
"instructor_open_feedback": self.feedback_data[
|
||||||
"instructor_open_feedback"
|
"instructor_open_feedback"
|
||||||
][i],
|
][i],
|
||||||
"would_recommend": feedback_data["would_recommend"][i],
|
"would_recommend": self.feedback_data["would_recommend"][i],
|
||||||
"instructor_respect": feedback_data["instructor_respect"][i],
|
"instructor_respect": self.feedback_data["instructor_respect"][i],
|
||||||
"course_positive_feedback": feedback_data[
|
"course_positive_feedback": self.feedback_data[
|
||||||
"course_positive_feedback"
|
"course_positive_feedback"
|
||||||
][i],
|
][i],
|
||||||
"course_negative_feedback": feedback_data[
|
"course_negative_feedback": self.feedback_data[
|
||||||
"course_negative_feedback"
|
"course_negative_feedback"
|
||||||
][i],
|
][i],
|
||||||
},
|
},
|
||||||
).save()
|
feedback_user=self.students[i],
|
||||||
|
submitted=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_detail_trainer_can_fetch_feedback(self):
|
||||||
|
self.client.force_login(self.trainer)
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
f"/api/core/feedback/{csu.course_session.id}/{circle.id}/"
|
f"/api/core/feedback/{self.course_session.id}/{self.circle_basis.id}/"
|
||||||
)
|
)
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
"amount": 3,
|
"amount": 3,
|
||||||
"questions": feedback_data,
|
"questions": self.feedback_data,
|
||||||
}
|
}
|
||||||
|
print(response.data)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertDictEqual(response.data, expected)
|
self.assertDictEqual(response.data, expected)
|
||||||
|
|
||||||
def test_cannot_receive_feedback_from_other_circle(self):
|
def test_summary_trainer_can_fetch_feedback(self):
|
||||||
csu = CourseSessionUser.objects.get(
|
self.client.force_login(self.trainer)
|
||||||
course_session=self.course_session,
|
|
||||||
user=User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch"),
|
|
||||||
role=CourseSessionUser.Role.EXPERT,
|
|
||||||
)
|
|
||||||
|
|
||||||
circle = Circle.objects.get(slug="test-lehrgang-lp-circle-reisen")
|
|
||||||
FeedbackResponseFactory(circle=circle, course_session=csu.course_session).save()
|
|
||||||
|
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
f"/api/core/feedback/{csu.course_session.id}/{circle.id}/"
|
f"/api/core/feedback/{self.course_session.id}/summary/"
|
||||||
)
|
)
|
||||||
|
self.maxDiff = None
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.data, {"amount": 0, "questions": {}})
|
self.assertDictEqual(
|
||||||
|
response.data[0], {"circle_id": self.circle_basis.id, "count": 3}
|
||||||
def test_student_cannot_receive_feedback(self):
|
|
||||||
self.client.login(username="student", password="test")
|
|
||||||
csu = CourseSessionUser.objects.get(
|
|
||||||
course_session=self.course_session,
|
|
||||||
user=self.user,
|
|
||||||
)
|
)
|
||||||
circle = Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug")
|
|
||||||
FeedbackResponseFactory(circle=circle, course_session=csu.course_session).save()
|
|
||||||
|
|
||||||
|
def test_detail_student_cannot_fetch_feedback(self):
|
||||||
|
self.client.force_login(self.student)
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
f"/api/core/feedback/{csu.course_session.id}/{circle.id}/"
|
f"/api/core/feedback/{self.course_session.id}/{self.circle_basis.id}/"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertEqual(response.data, {"amount": 0, "questions": {}})
|
|
||||||
|
def test_summary_student_cannot_fetch_feedback(self):
|
||||||
|
self.client.force_login(self.student)
|
||||||
|
response = self.client.get(
|
||||||
|
f"/api/core/feedback/{self.course_session.id}/summary/"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ import itertools
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from vbv_lernwelt.course.permissions import is_course_session_expert
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
@ -24,8 +26,12 @@ FEEDBACK_FIELDS = [
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
def get_expert_feedbacks_for_course(request, course_session_id):
|
def get_expert_feedbacks_for_course(request, course_session_id):
|
||||||
|
if not is_course_session_expert(request.user, course_session_id):
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
feedbacks = FeedbackResponse.objects.filter(
|
feedbacks = FeedbackResponse.objects.filter(
|
||||||
course_session__id=course_session_id, circle__expert__user=request.user
|
course_session__id=course_session_id,
|
||||||
|
submitted=True,
|
||||||
).order_by("circle_id")
|
).order_by("circle_id")
|
||||||
circle_count = []
|
circle_count = []
|
||||||
|
|
||||||
|
|
@ -44,9 +50,12 @@ def get_expert_feedbacks_for_course(request, course_session_id):
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
def get_feedback_for_circle(request, course_session_id, circle_id):
|
def get_feedback_for_circle(request, course_session_id, circle_id):
|
||||||
|
if not is_course_session_expert(request.user, course_session_id):
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
feedbacks = FeedbackResponse.objects.filter(
|
feedbacks = FeedbackResponse.objects.filter(
|
||||||
course_session__id=course_session_id,
|
course_session__id=course_session_id,
|
||||||
circle__expert__user=request.user,
|
submitted=True,
|
||||||
circle_id=circle_id,
|
circle_id=circle_id,
|
||||||
).order_by("created_at")
|
).order_by("created_at")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-09-27 13:49
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("learnpath", "0005_alter_learningcontentedoniqtest_content_assignment"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="learningcontentfeedback",
|
||||||
|
name="can_user_self_toggle_course_completion",
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -314,7 +314,7 @@ class LearningContentPlaceholder(LearningContent):
|
||||||
class LearningContentFeedback(LearningContent):
|
class LearningContentFeedback(LearningContent):
|
||||||
parent_page_types = ["learnpath.Circle"]
|
parent_page_types = ["learnpath.Circle"]
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
can_user_self_toggle_course_completion = models.BooleanField(default=True)
|
can_user_self_toggle_course_completion = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
|
||||||
class LearningContentLearningModule(LearningContent):
|
class LearningContentLearningModule(LearningContent):
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ class TestAttendanceCourseReminders(TestCase):
|
||||||
|
|
||||||
attendance_course_reminder_notification_job()
|
attendance_course_reminder_notification_job()
|
||||||
|
|
||||||
self.assertEquals(3, len(Notification.objects.all()))
|
self.assertEquals(4, len(Notification.objects.all()))
|
||||||
notification = Notification.objects.get(
|
notification = Notification.objects.get(
|
||||||
recipient__username="test-student1@example.com"
|
recipient__username="test-student1@example.com"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue