Merged in feature/VBV-563-feedback-vv (pull request #242)
Feature/VBV-563 feedback vv Approved-by: Daniel Egger
This commit is contained in:
commit
987159a531
|
|
@ -24,7 +24,7 @@ const documents = {
|
||||||
"\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n }\n }\n": types.DashboardConfigDocument,
|
"\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n }\n }\n": types.DashboardConfigDocument,
|
||||||
"\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n": types.DashboardProgressDocument,
|
"\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n": types.DashboardProgressDocument,
|
||||||
"\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n": types.CourseStatisticsDocument,
|
"\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n": types.CourseStatisticsDocument,
|
||||||
"\n 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,
|
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $learningContentType: String!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n learning_content_type: $learningContentType\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -88,7 +88,7 @@ export function graphql(source: "\n query courseStatistics($courseId: ID!) {\n
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* 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: "\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $learningContentType: String!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n learning_content_type: $learningContentType\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n"): (typeof documents)["\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $learningContentType: String!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n learning_content_type: $learningContentType\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n"];
|
||||||
|
|
||||||
export function graphql(source: string) {
|
export function graphql(source: string) {
|
||||||
return (documents as any)[source] ?? {};
|
return (documents as any)[source] ?? {};
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -9,7 +9,8 @@ type Query {
|
||||||
learning_content_media_library: LearningContentMediaLibraryObjectType
|
learning_content_media_library: LearningContentMediaLibraryObjectType
|
||||||
learning_content_assignment: LearningContentAssignmentObjectType
|
learning_content_assignment: LearningContentAssignmentObjectType
|
||||||
learning_content_attendance_course: LearningContentAttendanceCourseObjectType
|
learning_content_attendance_course: LearningContentAttendanceCourseObjectType
|
||||||
learning_content_feedback: LearningContentFeedbackObjectType
|
learning_content_feedback_uk: LearningContentFeedbackUKObjectType
|
||||||
|
learning_content_feedback_vv: LearningContentFeedbackVVObjectType
|
||||||
learning_content_learning_module: LearningContentLearningModuleObjectType
|
learning_content_learning_module: LearningContentLearningModuleObjectType
|
||||||
learning_content_knowledge_assessment: LearningContentKnowledgeAssessmentObjectType
|
learning_content_knowledge_assessment: LearningContentKnowledgeAssessmentObjectType
|
||||||
learning_content_placeholder: LearningContentPlaceholderObjectType
|
learning_content_placeholder: LearningContentPlaceholderObjectType
|
||||||
|
|
@ -718,7 +719,23 @@ type LearningContentMediaLibraryObjectType implements CoursePageInterface & Lear
|
||||||
circle: CircleLightObjectType
|
circle: CircleLightObjectType
|
||||||
}
|
}
|
||||||
|
|
||||||
type LearningContentFeedbackObjectType implements CoursePageInterface & LearningContentInterface {
|
type LearningContentFeedbackUKObjectType implements CoursePageInterface & LearningContentInterface {
|
||||||
|
id: ID!
|
||||||
|
title: String!
|
||||||
|
slug: String!
|
||||||
|
content_type: String!
|
||||||
|
live: Boolean!
|
||||||
|
translation_key: String!
|
||||||
|
frontend_url: String!
|
||||||
|
course: CourseObjectType
|
||||||
|
minutes: Int
|
||||||
|
description: String!
|
||||||
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
|
circle: CircleLightObjectType
|
||||||
|
}
|
||||||
|
|
||||||
|
type LearningContentFeedbackVVObjectType implements CoursePageInterface & LearningContentInterface {
|
||||||
id: ID!
|
id: ID!
|
||||||
title: String!
|
title: String!
|
||||||
slug: String!
|
slug: String!
|
||||||
|
|
@ -844,7 +861,7 @@ type CompetenceCertificateListObjectType implements CoursePageInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
send_feedback(course_session_id: ID!, data: GenericScalar, learning_content_page_id: ID!, submitted: Boolean = false): SendFeedbackMutation
|
send_feedback(course_session_id: ID!, data: GenericScalar, learning_content_page_id: ID!, learning_content_type: String!, 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_passed: Boolean, 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_passed: Boolean, evaluation_points: Float, initialize_completion: Boolean, learning_content_page_id: ID): AssignmentCompletionMutation
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,8 @@ export const LearningContentAssignmentObjectType = "LearningContentAssignmentObj
|
||||||
export const LearningContentAttendanceCourseObjectType = "LearningContentAttendanceCourseObjectType";
|
export const LearningContentAttendanceCourseObjectType = "LearningContentAttendanceCourseObjectType";
|
||||||
export const LearningContentDocumentListObjectType = "LearningContentDocumentListObjectType";
|
export const LearningContentDocumentListObjectType = "LearningContentDocumentListObjectType";
|
||||||
export const LearningContentEdoniqTestObjectType = "LearningContentEdoniqTestObjectType";
|
export const LearningContentEdoniqTestObjectType = "LearningContentEdoniqTestObjectType";
|
||||||
export const LearningContentFeedbackObjectType = "LearningContentFeedbackObjectType";
|
export const LearningContentFeedbackUKObjectType = "LearningContentFeedbackUKObjectType";
|
||||||
|
export const LearningContentFeedbackVVObjectType = "LearningContentFeedbackVVObjectType";
|
||||||
export const LearningContentInterface = "LearningContentInterface";
|
export const LearningContentInterface = "LearningContentInterface";
|
||||||
export const LearningContentKnowledgeAssessmentObjectType = "LearningContentKnowledgeAssessmentObjectType";
|
export const LearningContentKnowledgeAssessmentObjectType = "LearningContentKnowledgeAssessmentObjectType";
|
||||||
export const LearningContentLearningModuleObjectType = "LearningContentLearningModuleObjectType";
|
export const LearningContentLearningModuleObjectType = "LearningContentLearningModuleObjectType";
|
||||||
|
|
|
||||||
|
|
@ -18,70 +18,24 @@
|
||||||
</span>
|
</span>
|
||||||
{{ $t("feedback.feedbackPageInfo") }}
|
{{ $t("feedback.feedbackPageInfo") }}
|
||||||
</p>
|
</p>
|
||||||
<ol v-if="feedbackData.amount > 0">
|
<FeedbackPageVV v-if="feedbackType === 'vv'" :feedback-data="feedbackData" />
|
||||||
<li
|
<FeedbackPageUK
|
||||||
v-for="(question, i) in orderedQuestions"
|
v-else-if="feedbackType === 'uk'"
|
||||||
:key="i"
|
:feedback-data="feedbackData"
|
||||||
:data-cy="`question-${i + 1}`"
|
|
||||||
>
|
|
||||||
<RatingScale
|
|
||||||
v-if="ratingKeys.includes(question.key)"
|
|
||||||
class="mb-8 bg-white"
|
|
||||||
:ratings="feedbackData.questions[question.key]"
|
|
||||||
:title="`${$t('feedback.questionTitle')} ${i + 1}`"
|
|
||||||
:text="question.question"
|
|
||||||
/>
|
/>
|
||||||
<VerticalBarChart
|
|
||||||
v-else-if="verticalChartKyes.includes(question.key)"
|
|
||||||
class="mb-8 bg-white"
|
|
||||||
:title="`${$t('feedback.questionTitle')} ${i + 1}`"
|
|
||||||
:ratings="feedbackData.questions[question.key]"
|
|
||||||
:text="question.question"
|
|
||||||
:ratio="0.2"
|
|
||||||
/>
|
|
||||||
<OpenFeedback
|
|
||||||
v-else-if="
|
|
||||||
openKeys.includes(question.key) && feedbackData.questions[question.key]
|
|
||||||
"
|
|
||||||
class="mb-8 bg-white"
|
|
||||||
:title="`${$t('feedback.questionTitle')} ${i + 1}`"
|
|
||||||
:text="question.question"
|
|
||||||
:answers="feedbackData.questions[question.key].filter((a: string) => a !== '')"
|
|
||||||
></OpenFeedback>
|
|
||||||
<HorizontalBarChart
|
|
||||||
v-else-if="
|
|
||||||
horizontalChartKeys.includes(question.key) &&
|
|
||||||
feedbackData.questions[question.key]
|
|
||||||
"
|
|
||||||
class="mb-8 bg-white"
|
|
||||||
:title="`${$t('feedback.questionTitle')} ${i}`"
|
|
||||||
:text="question.question"
|
|
||||||
:items="feedbackData.questions[question.key].map((a: string) => `${a}%`)"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import HorizontalBarChart from "@/components/ui/HorizontalBarChart.vue";
|
|
||||||
import OpenFeedback from "@/components/ui/OpenFeedback.vue";
|
|
||||||
import RatingScale from "@/components/ui/RatingScale.vue";
|
|
||||||
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, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import { useTranslation } from "i18next-vue";
|
import type { FeedbackData, FeedbackType } from "@/types";
|
||||||
|
import FeedbackPageVV from "@/pages/cockpit/FeedbackPageVV.vue";
|
||||||
interface FeedbackData {
|
import FeedbackPageUK from "@/pages/cockpit/FeedbackPageUK.vue";
|
||||||
amount: number;
|
|
||||||
questions: {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
courseSlug: string;
|
courseSlug: string;
|
||||||
|
|
@ -91,72 +45,21 @@ const props = defineProps<{
|
||||||
log.debug("FeedbackPage created", props.circleId);
|
log.debug("FeedbackPage created", props.circleId);
|
||||||
|
|
||||||
const courseSession = useCurrentCourseSession();
|
const courseSession = useCurrentCourseSession();
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const orderedQuestions = [
|
|
||||||
{
|
|
||||||
key: "satisfaction",
|
|
||||||
question: t("feedback.satisfactionLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "goal_attainment",
|
|
||||||
question: t("feedback.goalAttainmentLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "proficiency",
|
|
||||||
question: t("feedback.proficiencyLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "preparation_task_clarity",
|
|
||||||
question: t("feedback.preparationTaskClarityLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "instructor_competence",
|
|
||||||
question: t("feedback.instructorCompetenceLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "instructor_respect",
|
|
||||||
question: t("feedback.instructorRespectLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "instructor_open_feedback",
|
|
||||||
question: t("feedback.instructorOpenFeedbackLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "would_recommend",
|
|
||||||
question: t("feedback.recommendLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "course_negative_feedback",
|
|
||||||
question: t("feedback.courseNegativeFeedbackLabel"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "course_positive_feedback",
|
|
||||||
question: t("feedback.coursePositiveFeedbackLabel"),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const ratingKeys = [
|
|
||||||
"satisfaction",
|
|
||||||
"goal_attainment",
|
|
||||||
"instructor_competence",
|
|
||||||
"instructor_respect",
|
|
||||||
];
|
|
||||||
const verticalChartKyes = ["preparation_task_clarity", "would_recommend"];
|
|
||||||
const horizontalChartKeys = ["proficiency"];
|
|
||||||
const openKeys = [
|
|
||||||
"course_negative_feedback",
|
|
||||||
"course_positive_feedback",
|
|
||||||
"instructor_open_feedback",
|
|
||||||
];
|
|
||||||
|
|
||||||
const feedbackData = ref<FeedbackData | undefined>(undefined);
|
const feedbackData = ref<FeedbackData | undefined>(undefined);
|
||||||
|
const feedbackType = ref<FeedbackType | undefined>(undefined);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug("FeedbackPage mounted");
|
log.debug("FeedbackPage mounted");
|
||||||
feedbackData.value = await itGet(
|
feedbackData.value = await itGet(
|
||||||
`/api/core/feedback/${courseSession.value.id}/${props.circleId}`
|
`/api/core/feedback/${courseSession.value.id}/${props.circleId}`
|
||||||
);
|
);
|
||||||
|
log.debug("FeedbackPage feedbackData", feedbackData.value);
|
||||||
|
if (
|
||||||
|
feedbackData.value &&
|
||||||
|
["uk", "vv"].includes(feedbackData.value?.feedbackType ?? "")
|
||||||
|
) {
|
||||||
|
feedbackType.value = feedbackData.value.feedbackType;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
<template>
|
||||||
|
<FeedbackResults
|
||||||
|
:ordered-questions="orderedQuestions"
|
||||||
|
:feedback-data="feedbackData"
|
||||||
|
:rating-keys="ratingKeys"
|
||||||
|
:vertical-chart-keys="verticalChartKeys"
|
||||||
|
:horizontal-chart-keys="horizontalChartKeys"
|
||||||
|
:open-keys="openKeys"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import FeedbackResults from "@/pages/cockpit/FeedbackResults.vue";
|
||||||
|
import type { FeedbackData } from "@/types";
|
||||||
|
import * as log from "loglevel";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
feedbackData: FeedbackData;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
log.debug("FeedbackPageUK created");
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const orderedQuestions = [
|
||||||
|
{
|
||||||
|
key: "satisfaction",
|
||||||
|
question: t("feedback.satisfactionLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "goal_attainment",
|
||||||
|
question: t("feedback.goalAttainmentLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "proficiency",
|
||||||
|
question: t("feedback.proficiencyLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "preparation_task_clarity",
|
||||||
|
question: t("feedback.preparationTaskClarityLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "instructor_competence",
|
||||||
|
question: t("feedback.instructorCompetenceLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "instructor_respect",
|
||||||
|
question: t("feedback.instructorRespectLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "instructor_open_feedback",
|
||||||
|
question: t("feedback.instructorOpenFeedbackLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "would_recommend",
|
||||||
|
question: t("feedback.recommendLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "course_negative_feedback",
|
||||||
|
question: t("feedback.courseNegativeFeedbackLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "course_positive_feedback",
|
||||||
|
question: t("feedback.coursePositiveFeedbackLabel"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ratingKeys = [
|
||||||
|
"satisfaction",
|
||||||
|
"goal_attainment",
|
||||||
|
"instructor_competence",
|
||||||
|
"instructor_respect",
|
||||||
|
];
|
||||||
|
const verticalChartKeys = ["preparation_task_clarity", "would_recommend"];
|
||||||
|
const horizontalChartKeys = ["proficiency"];
|
||||||
|
const openKeys = [
|
||||||
|
"course_negative_feedback",
|
||||||
|
"course_positive_feedback",
|
||||||
|
"instructor_open_feedback",
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<FeedbackResults
|
||||||
|
:ordered-questions="orderedQuestions"
|
||||||
|
:feedback-data="feedbackData"
|
||||||
|
:rating-keys="ratingKeys"
|
||||||
|
:vertical-chart-keys="verticalChartKeys"
|
||||||
|
:horizontal-chart-keys="horizontalChartKeys"
|
||||||
|
:open-keys="openKeys"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import FeedbackResults from "@/pages/cockpit/FeedbackResults.vue";
|
||||||
|
import type { FeedbackData } from "@/types";
|
||||||
|
import * as log from "loglevel";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
feedbackData: FeedbackData;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
log.debug("FeedbackPageVV created");
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const orderedQuestions = [
|
||||||
|
{
|
||||||
|
key: "satisfaction",
|
||||||
|
question: t("feedback.satisfactionLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "goal_attainment",
|
||||||
|
question: t("feedback.goalAttainmentLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "proficiency",
|
||||||
|
question: t("feedback.proficiencyLabelVV"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "preparation_task_clarity",
|
||||||
|
question: t("feedback.praxisAssignmentClarity"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "would_recommend",
|
||||||
|
question: t("feedback.recommendLabelVV"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "course_negative_feedback",
|
||||||
|
question: t("feedback.courseNegativeFeedbackLabel"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "course_positive_feedback",
|
||||||
|
question: t("feedback.coursePositiveFeedbackLabel"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ratingKeys = ["satisfaction", "goal_attainment"];
|
||||||
|
const verticalChartKeys = ["preparation_task_clarity", "would_recommend"];
|
||||||
|
const horizontalChartKeys = ["proficiency"];
|
||||||
|
const openKeys = ["course_negative_feedback", "course_positive_feedback"];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<ol v-if="feedbackData.amount > 0">
|
||||||
|
<li
|
||||||
|
v-for="(question, i) in orderedQuestions"
|
||||||
|
:key="i"
|
||||||
|
:data-cy="`question-${i + 1}`"
|
||||||
|
>
|
||||||
|
<RatingScale
|
||||||
|
v-if="ratingKeys.includes(question.key)"
|
||||||
|
class="mb-8 bg-white"
|
||||||
|
:ratings="feedbackData.questions[question.key]"
|
||||||
|
:title="`${$t('feedback.questionTitle')} ${i + 1}`"
|
||||||
|
:text="question.question"
|
||||||
|
/>
|
||||||
|
<VerticalBarChart
|
||||||
|
v-else-if="verticalChartKeys.includes(question.key)"
|
||||||
|
class="mb-8 bg-white"
|
||||||
|
:title="`${$t('feedback.questionTitle')} ${i + 1}`"
|
||||||
|
:ratings="feedbackData.questions[question.key]"
|
||||||
|
:text="question.question"
|
||||||
|
:ratio="0.2"
|
||||||
|
/>
|
||||||
|
<OpenFeedback
|
||||||
|
v-else-if="
|
||||||
|
openKeys.includes(question.key) && feedbackData.questions[question.key]
|
||||||
|
"
|
||||||
|
class="mb-8 bg-white"
|
||||||
|
:title="`${$t('feedback.questionTitle')} ${i + 1}`"
|
||||||
|
:text="question.question"
|
||||||
|
:answers="feedbackData.questions[question.key].filter((a: string) => a !== '')"
|
||||||
|
></OpenFeedback>
|
||||||
|
<HorizontalBarChart
|
||||||
|
v-else-if="
|
||||||
|
horizontalChartKeys.includes(question.key) &&
|
||||||
|
feedbackData.questions[question.key]
|
||||||
|
"
|
||||||
|
class="mb-8 bg-white"
|
||||||
|
:title="`${$t('feedback.questionTitle')} ${i + 1}`"
|
||||||
|
:text="question.question"
|
||||||
|
:items="feedbackData.questions[question.key].map((a: string) => `${a}%`)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import HorizontalBarChart from "@/components/ui/HorizontalBarChart.vue";
|
||||||
|
import OpenFeedback from "@/components/ui/OpenFeedback.vue";
|
||||||
|
import RatingScale from "@/components/ui/RatingScale.vue";
|
||||||
|
import VerticalBarChart from "@/components/ui/VerticalBarChart.vue";
|
||||||
|
import type { FeedbackData } from "@/types";
|
||||||
|
import * as log from "loglevel";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
orderedQuestions?: {
|
||||||
|
key: string;
|
||||||
|
question: string;
|
||||||
|
}[];
|
||||||
|
feedbackData: FeedbackData;
|
||||||
|
ratingKeys?: string[];
|
||||||
|
verticalChartKeys?: string[];
|
||||||
|
horizontalChartKeys?: string[];
|
||||||
|
openKeys?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
withDefaults(defineProps<Props>(), {
|
||||||
|
orderedQuestions: () => [],
|
||||||
|
ratingKeys: () => [],
|
||||||
|
verticalChartKeys: () => [],
|
||||||
|
horizontalChartKeys: () => [],
|
||||||
|
openKeys: () => [],
|
||||||
|
});
|
||||||
|
|
||||||
|
log.debug("FeedbackBasePage created");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -52,7 +52,8 @@ const submittables = computed(() => {
|
||||||
const learningContents = circleFlatLearningContents(circle).filter(
|
const learningContents = circleFlatLearningContents(circle).filter(
|
||||||
(lc) =>
|
(lc) =>
|
||||||
lc.content_type === "learnpath.LearningContentAssignment" ||
|
lc.content_type === "learnpath.LearningContentAssignment" ||
|
||||||
lc.content_type === "learnpath.LearningContentFeedback" ||
|
lc.content_type === "learnpath.LearningContentFeedbackUK" ||
|
||||||
|
lc.content_type === "learnpath.LearningContentFeedbackVV" ||
|
||||||
lc.content_type === "learnpath.LearningContentEdoniqTest"
|
lc.content_type === "learnpath.LearningContentEdoniqTest"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -72,7 +73,10 @@ const submittables = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const isFeedback = (lc: LearningContent) => {
|
const isFeedback = (lc: LearningContent) => {
|
||||||
return lc.content_type === "learnpath.LearningContentFeedback";
|
return (
|
||||||
|
lc.content_type === "learnpath.LearningContentFeedbackUK" ||
|
||||||
|
lc.content_type === "learnpath.LearningContentFeedbackVV"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isAssignment = (lc: LearningContent) => {
|
const isAssignment = (lc: LearningContent) => {
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,13 @@ 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 "./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";
|
||||||
import RichTextBlock from "./blocks/RichTextBlock.vue";
|
import RichTextBlock from "./blocks/RichTextBlock.vue";
|
||||||
import VideoBlock from "./blocks/VideoBlock.vue";
|
import VideoBlock from "./blocks/VideoBlock.vue";
|
||||||
|
import FeedbackBlockUK from "./feedback/FeedbackBlockUK.vue";
|
||||||
|
import FeedbackBlockVV from "./feedback/FeedbackBlockVV.vue";
|
||||||
import { getPreviousRoute } from "@/router/history";
|
import { getPreviousRoute } from "@/router/history";
|
||||||
import { stringifyParse } from "@/utils/utils";
|
import { stringifyParse } from "@/utils/utils";
|
||||||
import { useCourseDataWithCompletion } from "@/composables";
|
import { useCourseDataWithCompletion } from "@/composables";
|
||||||
|
|
@ -42,7 +43,8 @@ const COMPONENTS: Record<LearningContentContentType, Component> = {
|
||||||
"learnpath.LearningContentAssignment": AssignmentBlock,
|
"learnpath.LearningContentAssignment": AssignmentBlock,
|
||||||
"learnpath.LearningContentAttendanceCourse": AttendanceCourseBlock,
|
"learnpath.LearningContentAttendanceCourse": AttendanceCourseBlock,
|
||||||
"learnpath.LearningContentDocumentList": DocumentListBlock,
|
"learnpath.LearningContentDocumentList": DocumentListBlock,
|
||||||
"learnpath.LearningContentFeedback": FeedbackBlock,
|
"learnpath.LearningContentFeedbackUK": FeedbackBlockUK,
|
||||||
|
"learnpath.LearningContentFeedbackVV": FeedbackBlockVV,
|
||||||
"learnpath.LearningContentLearningModule": IframeBlock,
|
"learnpath.LearningContentLearningModule": IframeBlock,
|
||||||
"learnpath.LearningContentKnowledgeAssessment": IframeBlock,
|
"learnpath.LearningContentKnowledgeAssessment": IframeBlock,
|
||||||
"learnpath.LearningContentMediaLibrary": MediaLibraryBlock,
|
"learnpath.LearningContentMediaLibrary": MediaLibraryBlock,
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,32 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ItRadioGroup from "@/components/ui/ItRadioGroup.vue";
|
|
||||||
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
|
||||||
import { graphql } from "@/gql";
|
import { graphql } from "@/gql";
|
||||||
import FeedbackCompletition from "@/pages/learningPath/learningContentPage/feedback/FeedbackCompletition.vue";
|
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 LearningContentMultiLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentMultiLayout.vue";
|
||||||
import type { LearningContentFeedback } from "@/types";
|
import type { LearningContentFeedbackUK, LearningContentFeedbackVV } from "@/types";
|
||||||
import { useMutation } from "@urql/vue";
|
import { useMutation } from "@urql/vue";
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import { computed, onMounted, reactive, ref } from "vue";
|
import { computed, onMounted, reactive, ref } from "vue";
|
||||||
import { useTranslation } from "i18next-vue";
|
|
||||||
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
|
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
|
||||||
|
import { bustItGetCache } from "@/fetchHelpers";
|
||||||
|
import { useUserStore } from "@/stores/user";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
content: LearningContentFeedback;
|
content: LearningContentFeedbackVV | LearningContentFeedbackUK;
|
||||||
|
stepLabels: string[];
|
||||||
|
questionData: any[];
|
||||||
|
introduction: string;
|
||||||
|
title: string;
|
||||||
|
completionTitle: string;
|
||||||
|
completionDescription: string;
|
||||||
|
showAvatar: boolean;
|
||||||
}>();
|
}>();
|
||||||
const courseSession = useCurrentCourseSession();
|
const courseSession = useCurrentCourseSession();
|
||||||
const courseSessionDetailResult = useCourseSessionDetailQuery();
|
const courseSessionDetailResult = useCourseSessionDetailQuery();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const stepNo = useRouteQuery("step", "0", { transform: Number, mode: "push" });
|
const stepNo = useRouteQuery("step", "0", { transform: Number, mode: "push" });
|
||||||
|
|
||||||
const title = computed(
|
const title = computed(() => `«${props.content.circle?.title}»: ${props.title}`);
|
||||||
() => `«${props.content.circle?.title}»: ${t("feedback.areYouSatisfied")}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const circleExperts = computed(() => {
|
const circleExperts = computed(() => {
|
||||||
if (props.content?.circle?.slug) {
|
if (props.content?.circle?.slug) {
|
||||||
|
|
@ -38,34 +35,29 @@ const circleExperts = computed(() => {
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const stepLabels = [
|
const localStepLabels = ref(props.stepLabels);
|
||||||
t("general.introduction"),
|
const localQuestionData = ref(props.questionData);
|
||||||
t("feedback.satisfactionLabel"),
|
const feedbackData: FeedbackData = reactive(feedbackDataFactory());
|
||||||
t("feedback.goalAttainmentLabel"),
|
|
||||||
t("feedback.proficiencyLabel"),
|
|
||||||
t("feedback.preparationTaskClarityLabel"),
|
|
||||||
t("feedback.instructorCompetenceLabel"),
|
|
||||||
t("feedback.instructorRespectLabel"),
|
|
||||||
t("feedback.instructorOpenFeedbackLabel"),
|
|
||||||
t("feedback.recommendLabel"),
|
|
||||||
t("feedback.coursePositiveFeedbackLabel"),
|
|
||||||
t("feedback.courseNegativeFeedbackLabel"),
|
|
||||||
t("general.submission"),
|
|
||||||
];
|
|
||||||
|
|
||||||
const numSteps = stepLabels.length;
|
const numSteps = computed(() => localStepLabels.value.length);
|
||||||
|
const textQuestionKeys = computed(() =>
|
||||||
|
props.questionData.filter((item) => isTextNode(item)).map((item) => item.modelKey)
|
||||||
|
);
|
||||||
|
const avatarUrl = computed(() => circleExperts.value[0]?.avatar_url);
|
||||||
|
|
||||||
// noinspection GraphQLUnresolvedReference -> mute IntelliJ warning
|
// noinspection GraphQLUnresolvedReference -> mute IntelliJ warning
|
||||||
const sendFeedbackMutation = graphql(`
|
const sendFeedbackMutation = graphql(`
|
||||||
mutation SendFeedbackMutation(
|
mutation SendFeedbackMutation(
|
||||||
$courseSessionId: ID!
|
$courseSessionId: ID!
|
||||||
$learningContentId: ID!
|
$learningContentId: ID!
|
||||||
|
$learningContentType: String!
|
||||||
$data: GenericScalar!
|
$data: GenericScalar!
|
||||||
$submitted: Boolean
|
$submitted: Boolean
|
||||||
) {
|
) {
|
||||||
send_feedback(
|
send_feedback(
|
||||||
course_session_id: $courseSessionId
|
course_session_id: $courseSessionId
|
||||||
learning_content_page_id: $learningContentId
|
learning_content_page_id: $learningContentId
|
||||||
|
learning_content_type: $learningContentType
|
||||||
data: $data
|
data: $data
|
||||||
submitted: $submitted
|
submitted: $submitted
|
||||||
) {
|
) {
|
||||||
|
|
@ -90,69 +82,6 @@ interface FeedbackData {
|
||||||
[key: string]: number | string | null;
|
[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_positive_feedback: "",
|
|
||||||
course_negative_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_positive_feedback",
|
|
||||||
component: ItTextarea,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
modelKey: "course_negative_feedback",
|
|
||||||
component: ItTextarea,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const previousStep = () => {
|
const previousStep = () => {
|
||||||
if (stepNo.value > 0) {
|
if (stepNo.value > 0) {
|
||||||
stepNo.value -= 1;
|
stepNo.value -= 1;
|
||||||
|
|
@ -160,23 +89,17 @@ const previousStep = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const nextStep = () => {
|
const nextStep = () => {
|
||||||
if (stepNo.value < numSteps && hasStepValidInput(stepNo.value)) {
|
if (stepNo.value < numSteps.value && hasStepValidInput(stepNo.value)) {
|
||||||
stepNo.value += 1;
|
stepNo.value += 1;
|
||||||
}
|
}
|
||||||
log.debug(`next step ${stepNo.value} of ${numSteps}`);
|
log.debug(`next step ${stepNo.value} of ${numSteps.value}`);
|
||||||
mutateFeedback(feedbackData);
|
mutateFeedback(feedbackData);
|
||||||
};
|
};
|
||||||
|
|
||||||
function hasStepValidInput(stepNumber: number) {
|
function hasStepValidInput(stepNumber: number) {
|
||||||
const question = questionData[stepNumber - 1];
|
const question = localQuestionData.value[stepNumber - 1];
|
||||||
if (question) {
|
if (question) {
|
||||||
if (
|
if (textQuestionKeys.value.includes(question.modelKey)) {
|
||||||
[
|
|
||||||
"instructor_open_feedback",
|
|
||||||
"course_negative_feedback",
|
|
||||||
"course_positive_feedback",
|
|
||||||
].includes(question.modelKey)
|
|
||||||
) {
|
|
||||||
// text response questions need to have a "truthy" value (not "" or null)
|
// text response questions need to have a "truthy" value (not "" or null)
|
||||||
return feedbackData[question.modelKey];
|
return feedbackData[question.modelKey];
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -192,6 +115,7 @@ function mutateFeedback(data: FeedbackData, submit = false) {
|
||||||
return executeMutation({
|
return executeMutation({
|
||||||
courseSessionId: courseSession.value.id,
|
courseSessionId: courseSession.value.id,
|
||||||
learningContentId: props.content.id,
|
learningContentId: props.content.id,
|
||||||
|
learningContentType: props.content.content_type,
|
||||||
data: data,
|
data: data,
|
||||||
submitted: submit,
|
submitted: submit,
|
||||||
})
|
})
|
||||||
|
|
@ -199,29 +123,40 @@ function mutateFeedback(data: FeedbackData, submit = false) {
|
||||||
log.debug("feedback mutation result", result);
|
log.debug("feedback mutation result", result);
|
||||||
if (result.data?.send_feedback?.feedback_response?.data) {
|
if (result.data?.send_feedback?.feedback_response?.data) {
|
||||||
const responseData = result.data.send_feedback.feedback_response.data;
|
const responseData = result.data.send_feedback.feedback_response.data;
|
||||||
if (!responseData.instructor_open_feedback) {
|
textQuestionKeys.value.map((key) => {
|
||||||
responseData.instructor_open_feedback = "";
|
if (!responseData[key]) {
|
||||||
}
|
responseData[key] = "";
|
||||||
if (!responseData.course_negative_feedback) {
|
|
||||||
responseData.course_negative_feedback = "";
|
|
||||||
}
|
|
||||||
if (!responseData.course_positive_feedback) {
|
|
||||||
responseData.course_positive_feedback = "";
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
Object.assign(feedbackData, responseData);
|
Object.assign(feedbackData, responseData);
|
||||||
log.debug("feedback data", feedbackData);
|
log.debug("feedback data", feedbackData);
|
||||||
feedbackSubmitted.value =
|
feedbackSubmitted.value =
|
||||||
result.data?.send_feedback?.feedback_response?.submitted || false;
|
result.data?.send_feedback?.feedback_response?.submitted || false;
|
||||||
}
|
}
|
||||||
|
bustItGetCache(
|
||||||
|
`/api/course/completion/${courseSession.value.id}/${useUserStore().id}/`
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.catch((e) => log.error(e));
|
.catch((e) => log.error(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function feedbackDataFactory() {
|
||||||
|
const data: FeedbackData = {};
|
||||||
|
localQuestionData.value.map((item) => {
|
||||||
|
data[item.modelKey] = isTextNode(item) ? "" : null;
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTextNode(item: any) {
|
||||||
|
return item.component.props.placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug("Feedback mounted");
|
log.debug("Feedback mounted");
|
||||||
await mutateFeedback({});
|
await mutateFeedback({});
|
||||||
if (feedbackSubmitted.value) {
|
if (feedbackSubmitted.value) {
|
||||||
stepNo.value = numSteps - 1;
|
stepNo.value = numSteps.value - 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -246,22 +181,22 @@ onMounted(async () => {
|
||||||
@next="nextStep()"
|
@next="nextStep()"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<p v-if="stepNo === 0" class="mt-10">
|
<p v-if="stepNo === 0" class="mt-10" data-cy="introduction">
|
||||||
{{
|
{{ introduction }}
|
||||||
$t("feedback.intro", {
|
|
||||||
name: `${circleExperts[0]?.first_name} ${circleExperts[0]?.last_name}`,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</p>
|
</p>
|
||||||
<p v-if="stepNo > 0 && stepNo + 1 < numSteps" class="pb-2">
|
<p
|
||||||
{{ stepLabels[stepNo] }}
|
v-if="stepNo > 0 && stepNo + 1 < numSteps"
|
||||||
|
class="pb-2"
|
||||||
|
:data-cy="`question-${stepNo}`"
|
||||||
|
>
|
||||||
|
{{ localStepLabels[stepNo] }}
|
||||||
</p>
|
</p>
|
||||||
<div v-for="(question, index) in questionData" :key="index">
|
<div v-for="(question, index) in questionData" :key="index">
|
||||||
<!-- eslint-disable -->
|
<!-- eslint-disable -->
|
||||||
<!-- eslint does not like the dynamic v-model... -->
|
<!-- eslint does not like the dynamic v-model... -->
|
||||||
<component
|
<component
|
||||||
:is="question.component"
|
:is="question.component"
|
||||||
v-if="index + 1 === stepNo"
|
v-if="index + 1 === stepNo && feedbackData != undefined"
|
||||||
v-model="feedbackData[question.modelKey] as any"
|
v-model="feedbackData[question.modelKey] as any"
|
||||||
:items="question['items']"
|
:items="question['items']"
|
||||||
:cy-key="question.modelKey"
|
:cy-key="question.modelKey"
|
||||||
|
|
@ -269,14 +204,11 @@ onMounted(async () => {
|
||||||
<!-- eslint-enable -->
|
<!-- eslint-enable -->
|
||||||
</div>
|
</div>
|
||||||
<FeedbackCompletition
|
<FeedbackCompletition
|
||||||
v-if="stepNo === 11"
|
v-if="stepNo === numSteps - 1"
|
||||||
:avatar-url="circleExperts[0].avatar_url"
|
:avatar-url="avatarUrl"
|
||||||
:title="
|
:show-avatar="showAvatar"
|
||||||
$t('feedback.completionTitle', {
|
:title="completionTitle"
|
||||||
name: `${circleExperts[0].first_name} ${circleExperts[0].last_name}`,
|
:description="completionDescription"
|
||||||
})
|
|
||||||
"
|
|
||||||
:description="$t('feedback.completionDescription')"
|
|
||||||
:feedback-sent="feedbackSubmitted"
|
:feedback-sent="feedbackSubmitted"
|
||||||
@send-feedback="mutateFeedback(feedbackData, true)"
|
@send-feedback="mutateFeedback(feedbackData, true)"
|
||||||
/>
|
/>
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ItRadioGroup from "@/components/ui/ItRadioGroup.vue";
|
||||||
|
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
||||||
|
import {
|
||||||
|
PERCENTAGES,
|
||||||
|
RATINGS,
|
||||||
|
YES_NO,
|
||||||
|
} from "@/pages/learningPath/learningContentPage/feedback/feedback.constants";
|
||||||
|
import type { LearningContentFeedbackUK, LearningContentFeedbackVV } from "@/types";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
|
import { useCourseSessionDetailQuery } from "@/composables";
|
||||||
|
import FeedbackBase from "@/pages/learningPath/learningContentPage/feedback/FeedbackBase.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
content: LearningContentFeedbackVV | LearningContentFeedbackUK;
|
||||||
|
}>();
|
||||||
|
const courseSessionDetailResult = useCourseSessionDetailQuery();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const circleExperts = computed(() => {
|
||||||
|
if (props.content?.circle?.slug) {
|
||||||
|
return courseSessionDetailResult.filterCircleExperts(props.content.circle.slug);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
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.coursePositiveFeedbackLabel"),
|
||||||
|
t("feedback.courseNegativeFeedbackLabel"),
|
||||||
|
t("general.submission"),
|
||||||
|
];
|
||||||
|
|
||||||
|
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_positive_feedback",
|
||||||
|
component: ItTextarea,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "course_negative_feedback",
|
||||||
|
component: ItTextarea,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FeedbackBase
|
||||||
|
:step-labels="stepLabels"
|
||||||
|
:question-data="questionData"
|
||||||
|
:content="props.content"
|
||||||
|
:introduction="
|
||||||
|
$t('feedback.intro', {
|
||||||
|
name: `${circleExperts[0]?.first_name} ${circleExperts[0]?.last_name}`,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:title="$t('feedback.areYouSatisfied')"
|
||||||
|
:completion-title="
|
||||||
|
$t('feedback.completionTitle', {
|
||||||
|
name: `${circleExperts[0].first_name} ${circleExperts[0].last_name}`,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:completion-description="$t('feedback.completionDescription')"
|
||||||
|
:show-avatar="true"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ItRadioGroup from "@/components/ui/ItRadioGroup.vue";
|
||||||
|
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
||||||
|
import {
|
||||||
|
PERCENTAGES,
|
||||||
|
RATINGS,
|
||||||
|
YES_NO,
|
||||||
|
} from "@/pages/learningPath/learningContentPage/feedback/feedback.constants";
|
||||||
|
import type { LearningContentFeedbackUK, LearningContentFeedbackVV } from "@/types";
|
||||||
|
import FeedbackBase from "@/pages/learningPath/learningContentPage/feedback/FeedbackBase.vue";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
content: LearningContentFeedbackVV | LearningContentFeedbackUK;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const stepLabels = [
|
||||||
|
t("general.introduction"),
|
||||||
|
t("feedback.satisfactionLabel"),
|
||||||
|
t("feedback.goalAttainmentLabel"),
|
||||||
|
t("feedback.proficiencyLabelVV"),
|
||||||
|
t("feedback.praxisAssignmentClarity"),
|
||||||
|
t("feedback.recommendLabelVV"),
|
||||||
|
t("feedback.coursePositiveFeedbackLabel"),
|
||||||
|
t("feedback.courseNegativeFeedbackLabel"),
|
||||||
|
t("general.submission"),
|
||||||
|
];
|
||||||
|
|
||||||
|
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: "would_recommend",
|
||||||
|
items: YES_NO,
|
||||||
|
component: ItRadioGroup,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "course_positive_feedback",
|
||||||
|
component: ItTextarea,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modelKey: "course_negative_feedback",
|
||||||
|
component: ItTextarea,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FeedbackBase
|
||||||
|
:step-labels="stepLabels"
|
||||||
|
:question-data="questionData"
|
||||||
|
:content="props.content"
|
||||||
|
:introduction="$t('a.feedback.introductionVV')"
|
||||||
|
:title="$t('Feedback')"
|
||||||
|
:completion-title="$t('feedback.completionDescriptionVV')"
|
||||||
|
:completion-description="$t('feedback.completionDescriptionVV')"
|
||||||
|
:show-avatar="false"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
@ -4,7 +4,11 @@
|
||||||
<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
|
||||||
|
v-if="showAvatar"
|
||||||
|
:src="avatarUrl"
|
||||||
|
class="mb-6 h-16 w-16 rounded-full lg:mr-12"
|
||||||
|
/>
|
||||||
<h2 class="mb-8 block lg:hidden">{{ title }}</h2>
|
<h2 class="mb-8 block lg:hidden">{{ title }}</h2>
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-6">{{ description }}</p>
|
<p class="mb-6">{{ description }}</p>
|
||||||
|
|
@ -33,6 +37,7 @@ interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
feedbackSent?: boolean;
|
feedbackSent?: boolean;
|
||||||
|
showAvatar?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
withDefaults(defineProps<Props>(), {
|
withDefaults(defineProps<Props>(), {
|
||||||
|
|
@ -40,6 +45,7 @@ withDefaults(defineProps<Props>(), {
|
||||||
title: "",
|
title: "",
|
||||||
description: "",
|
description: "",
|
||||||
feedbackSent: false,
|
feedbackSent: false,
|
||||||
|
showAvatar: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(["sendFeedback"]);
|
defineEmits(["sendFeedback"]);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ import type {
|
||||||
LearningContentAttendanceCourseObjectType,
|
LearningContentAttendanceCourseObjectType,
|
||||||
LearningContentDocumentListObjectType,
|
LearningContentDocumentListObjectType,
|
||||||
LearningContentEdoniqTestObjectType,
|
LearningContentEdoniqTestObjectType,
|
||||||
LearningContentFeedbackObjectType,
|
LearningContentFeedbackUkObjectType,
|
||||||
|
LearningContentFeedbackVvObjectType,
|
||||||
LearningContentKnowledgeAssessmentObjectType,
|
LearningContentKnowledgeAssessmentObjectType,
|
||||||
LearningContentLearningModuleObjectType,
|
LearningContentLearningModuleObjectType,
|
||||||
LearningContentMediaLibraryObjectType,
|
LearningContentMediaLibraryObjectType,
|
||||||
|
|
@ -68,8 +69,12 @@ export type LearningContentEdoniqTest = LearningContentEdoniqTestObjectType & {
|
||||||
readonly content_type: "learnpath.LearningContentEdoniqTest";
|
readonly content_type: "learnpath.LearningContentEdoniqTest";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LearningContentFeedback = LearningContentFeedbackObjectType & {
|
export type LearningContentFeedbackVV = LearningContentFeedbackVvObjectType & {
|
||||||
readonly content_type: "learnpath.LearningContentFeedback";
|
readonly content_type: "learnpath.LearningContentFeedbackVV";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LearningContentFeedbackUK = LearningContentFeedbackUkObjectType & {
|
||||||
|
readonly content_type: "learnpath.LearningContentFeedbackUK";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LearningContentLearningModule = LearningContentLearningModuleObjectType & {
|
export type LearningContentLearningModule = LearningContentLearningModuleObjectType & {
|
||||||
|
|
@ -102,7 +107,8 @@ export type LearningContent =
|
||||||
| LearningContentAttendanceCourse
|
| LearningContentAttendanceCourse
|
||||||
| LearningContentDocumentList
|
| LearningContentDocumentList
|
||||||
| LearningContentEdoniqTest
|
| LearningContentEdoniqTest
|
||||||
| LearningContentFeedback
|
| LearningContentFeedbackUK
|
||||||
|
| LearningContentFeedbackVV
|
||||||
| LearningContentLearningModule
|
| LearningContentLearningModule
|
||||||
| LearningContentKnowledgeAssessment
|
| LearningContentKnowledgeAssessment
|
||||||
| LearningContentMediaLibrary
|
| LearningContentMediaLibrary
|
||||||
|
|
@ -560,3 +566,13 @@ export type DueDate = SimpleDueDate & {
|
||||||
course_session_id: string;
|
course_session_id: string;
|
||||||
circle: CircleLight | null;
|
circle: CircleLight | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FeedbackType = "uk" | "vv";
|
||||||
|
|
||||||
|
export interface FeedbackData {
|
||||||
|
amount: number;
|
||||||
|
questions: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
feedbackType: FeedbackType;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,8 @@ export function learningContentTypeData(
|
||||||
return { title: t("learningContentTypes.test"), icon: "it-icon-lc-test" };
|
return { title: t("learningContentTypes.test"), icon: "it-icon-lc-test" };
|
||||||
case "learnpath.LearningContentRichText":
|
case "learnpath.LearningContentRichText":
|
||||||
return { title: t("learningContentTypes.text"), icon: "it-icon-lc-resource" };
|
return { title: t("learningContentTypes.text"), icon: "it-icon-lc-resource" };
|
||||||
case "learnpath.LearningContentFeedback":
|
case "learnpath.LearningContentFeedbackUK":
|
||||||
|
case "learnpath.LearningContentFeedbackVV":
|
||||||
return { title: t("learningContentTypes.feedback"), icon: "it-icon-lc-feedback" };
|
return { title: t("learningContentTypes.feedback"), icon: "it-icon-lc-feedback" };
|
||||||
case "learnpath.LearningContentPlaceholder":
|
case "learnpath.LearningContentPlaceholder":
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ describe("dashboardSupervisor.cy.js", () => {
|
||||||
describe("feedback summary box", () => {
|
describe("feedback summary box", () => {
|
||||||
it("contains correct numbers", () => {
|
it("contains correct numbers", () => {
|
||||||
getDashboardStatistics("feedback.average").should("have.text", "3.3");
|
getDashboardStatistics("feedback.average").should("have.text", "3.3");
|
||||||
getDashboardStatistics("feedback.count").should("have.text", "3");
|
getDashboardStatistics("feedback.count").should("have.text", "6");
|
||||||
});
|
});
|
||||||
it("contains correct details link", () => {
|
it("contains correct details link", () => {
|
||||||
clickOnDetailsLink("feedback");
|
clickOnDetailsLink("feedback");
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.manageCommand("cypress_reset");
|
cy.manageCommand("cypress_reset");
|
||||||
login("test-student1@example.com", "test");
|
login("test-student1@example.com", "test");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Feedback UK", () => {
|
||||||
|
beforeEach(() => {
|
||||||
cy.visit("/course/test-lehrgang/learn/fahrzeug/feedback");
|
cy.visit("/course/test-lehrgang/learn/fahrzeug/feedback");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -26,6 +30,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
// fill feedback form
|
// fill feedback form
|
||||||
// step 1
|
// step 1
|
||||||
cy.url().should("include", "step=1");
|
cy.url().should("include", "step=1");
|
||||||
|
cy.get('[data-cy="question-1"]').should(
|
||||||
|
"contain",
|
||||||
|
"Zufriedenheit insgesamt"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="radio-4"]').click();
|
cy.get('[data-cy="radio-4"]').click();
|
||||||
cy.wait(200);
|
cy.wait(200);
|
||||||
|
|
@ -34,6 +42,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 2
|
// step 2
|
||||||
cy.url().should("include", "step=2");
|
cy.url().should("include", "step=2");
|
||||||
|
cy.get('[data-cy="question-2"]').should(
|
||||||
|
"contain",
|
||||||
|
"Zielerreichung insgesamt"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
// the system should store after every step -> check stored data
|
// the system should store after every step -> check stored data
|
||||||
cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then(
|
cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then(
|
||||||
|
|
@ -50,6 +62,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 3
|
// step 3
|
||||||
cy.url().should("include", "step=3");
|
cy.url().should("include", "step=3");
|
||||||
|
cy.get('[data-cy="question-3"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Kurs?"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="radio-80"]').click();
|
cy.get('[data-cy="radio-80"]').click();
|
||||||
cy.wait(200);
|
cy.wait(200);
|
||||||
|
|
@ -58,6 +74,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 4
|
// step 4
|
||||||
cy.url().should("include", "step=4");
|
cy.url().should("include", "step=4");
|
||||||
|
cy.get('[data-cy="question-4"]').should(
|
||||||
|
"contain",
|
||||||
|
"Waren die Vorbereitungsaufträge klar und verständlich?"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="radio-false"]').click();
|
cy.get('[data-cy="radio-false"]').click();
|
||||||
cy.wait(200);
|
cy.wait(200);
|
||||||
|
|
@ -66,6 +86,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 5
|
// step 5
|
||||||
cy.url().should("include", "step=5");
|
cy.url().should("include", "step=5");
|
||||||
|
cy.get('[data-cy="question-5"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wie beurteilst du die Themensicherheit und Fachkompetenz des Kursleiters/der Kursleiterin?"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="radio-2"]').click();
|
cy.get('[data-cy="radio-2"]').click();
|
||||||
cy.wait(200);
|
cy.wait(200);
|
||||||
|
|
@ -74,6 +98,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 6
|
// step 6
|
||||||
cy.url().should("include", "step=6");
|
cy.url().should("include", "step=6");
|
||||||
|
cy.get('[data-cy="question-6"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wurden Fragen und Anregungen der Kursteilnehmenden ernst genommen und aufgegriffen?"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="radio-1"]').click();
|
cy.get('[data-cy="radio-1"]').click();
|
||||||
cy.wait(200);
|
cy.wait(200);
|
||||||
|
|
@ -82,6 +110,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 7
|
// step 7
|
||||||
cy.url().should("include", "step=7");
|
cy.url().should("include", "step=7");
|
||||||
|
cy.get('[data-cy="question-7"]').should(
|
||||||
|
"contain",
|
||||||
|
"Was möchtest du dem Kursleiter/der Kursleiterin sonst noch sagen?"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="it-textarea-instructor_open_feedback"]').type(
|
cy.get('[data-cy="it-textarea-instructor_open_feedback"]').type(
|
||||||
"Der Kursleiter ist eigentlich ganz nett."
|
"Der Kursleiter ist eigentlich ganz nett."
|
||||||
|
|
@ -92,6 +124,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 8
|
// step 8
|
||||||
cy.url().should("include", "step=8");
|
cy.url().should("include", "step=8");
|
||||||
|
cy.get('[data-cy="question-8"]').should(
|
||||||
|
"contain",
|
||||||
|
"Würdest du den Kurs weiterempfehlen?"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="radio-true"]').click();
|
cy.get('[data-cy="radio-true"]').click();
|
||||||
cy.wait(200);
|
cy.wait(200);
|
||||||
|
|
@ -100,6 +136,11 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 9
|
// step 9
|
||||||
cy.url().should("include", "step=9");
|
cy.url().should("include", "step=9");
|
||||||
|
cy.get('[data-cy="question-9"]').should(
|
||||||
|
"contain",
|
||||||
|
"Was hat dir besonders gut gefallen?"
|
||||||
|
);
|
||||||
|
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="it-textarea-course_positive_feedback"]').type(
|
cy.get('[data-cy="it-textarea-course_positive_feedback"]').type(
|
||||||
"Ich bin zufrieden mit den meisten Dingen."
|
"Ich bin zufrieden mit den meisten Dingen."
|
||||||
|
|
@ -110,6 +151,10 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
|
|
||||||
// step 10
|
// step 10
|
||||||
cy.url().should("include", "step=10");
|
cy.url().should("include", "step=10");
|
||||||
|
cy.get('[data-cy="question-10"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wo siehst du Verbesserungspotential?"
|
||||||
|
);
|
||||||
cy.get('[data-cy="next-step"]').should("be.disabled");
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
cy.get('[data-cy="it-textarea-course_negative_feedback"]').type(
|
cy.get('[data-cy="it-textarea-course_negative_feedback"]').type(
|
||||||
"Ich bin unzufrieden mit einigen Sachen."
|
"Ich bin unzufrieden mit einigen Sachen."
|
||||||
|
|
@ -141,17 +186,178 @@ describe("feedbackStudent.cy.js", () => {
|
||||||
expect(ac.submitted).to.be.true;
|
expect(ac.submitted).to.be.true;
|
||||||
expect(ac.data).to.deep.equal({
|
expect(ac.data).to.deep.equal({
|
||||||
course_negative_feedback: "Ich bin unzufrieden mit einigen Sachen.",
|
course_negative_feedback: "Ich bin unzufrieden mit einigen Sachen.",
|
||||||
course_positive_feedback: "Ich bin zufrieden mit den meisten Dingen.",
|
course_positive_feedback:
|
||||||
|
"Ich bin zufrieden mit den meisten Dingen.",
|
||||||
goal_attainment: 3,
|
goal_attainment: 3,
|
||||||
instructor_competence: 2,
|
instructor_competence: 2,
|
||||||
instructor_open_feedback: "Der Kursleiter ist eigentlich ganz nett.",
|
instructor_open_feedback:
|
||||||
|
"Der Kursleiter ist eigentlich ganz nett.",
|
||||||
instructor_respect: 1,
|
instructor_respect: 1,
|
||||||
preparation_task_clarity: false,
|
preparation_task_clarity: false,
|
||||||
proficiency: 80,
|
proficiency: 80,
|
||||||
satisfaction: 4,
|
satisfaction: 4,
|
||||||
would_recommend: true,
|
would_recommend: true,
|
||||||
|
feedback_type: "uk",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Feedback VV", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit("/course/test-lehrgang/learn/reisen/feedback");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can open feedback page", () => {
|
||||||
|
cy.testLearningContentTitle("Feedback");
|
||||||
|
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(/\/reisen\/feedback(\?step=0)?$/);
|
||||||
|
});
|
||||||
|
cy.get('[data-cy="introduction"]').contains(
|
||||||
|
"Wir bitten dich um dein Feedback. Es hilft uns, damit wir deine Lernerlebnisse verbessern können."
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// fill feedback form
|
||||||
|
// step 1
|
||||||
|
cy.url().should("include", "step=1");
|
||||||
|
cy.get('[data-cy="question-1"]').should(
|
||||||
|
"contain",
|
||||||
|
"Zufriedenheit insgesamt"
|
||||||
|
);
|
||||||
|
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="question-2"]').should(
|
||||||
|
"contain",
|
||||||
|
"Zielerreichung insgesamt"
|
||||||
|
);
|
||||||
|
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.course_positive_feedback).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="question-3"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Circle?"
|
||||||
|
);
|
||||||
|
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="question-4"]').should(
|
||||||
|
"contain",
|
||||||
|
"Waren die Praxisaufträge klar und verständlich?"
|
||||||
|
);
|
||||||
|
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="question-5"]').should(
|
||||||
|
"contain",
|
||||||
|
"Würdest du den Circle weiterempfehlen?"
|
||||||
|
);
|
||||||
|
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 6
|
||||||
|
cy.url().should("include", "step=6");
|
||||||
|
cy.get('[data-cy="question-6"]').should(
|
||||||
|
"contain",
|
||||||
|
"Was hat dir besonders gut gefallen?"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get('[data-cy="next-step"]').should("be.disabled");
|
||||||
|
cy.get('[data-cy="it-textarea-course_positive_feedback"]').type(
|
||||||
|
"Der Circle ist eigentlich ganz nett."
|
||||||
|
);
|
||||||
|
cy.wait(200);
|
||||||
|
cy.learningContentMultiLayoutNextStep();
|
||||||
|
cy.wait(200);
|
||||||
|
|
||||||
|
// step 7
|
||||||
|
cy.url().should("include", "step=7");
|
||||||
|
cy.get('[data-cy="question-7"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wo siehst du Verbesserungspotential?"
|
||||||
|
);
|
||||||
|
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);
|
||||||
|
|
||||||
|
cy.url().should("include", "step=8");
|
||||||
|
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(/\/reisen#lu-transfer-reflexion-feedback?$/);
|
||||||
|
});
|
||||||
|
cy.reload();
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="test-lehrgang-lp-circle-reisen-lc-feedback-checkbox"]'
|
||||||
|
).should("have.class", "cy-checked");
|
||||||
|
|
||||||
|
// reopening page should get directly to last step
|
||||||
|
cy.visit("/course/test-lehrgang/learn/reisen/feedback");
|
||||||
|
cy.url().should("include", "step=8");
|
||||||
|
|
||||||
|
// 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: "Der Circle ist eigentlich ganz nett.",
|
||||||
|
goal_attainment: 3,
|
||||||
|
preparation_task_clarity: false,
|
||||||
|
proficiency: 80,
|
||||||
|
satisfaction: 4,
|
||||||
|
would_recommend: false,
|
||||||
|
feedback_type: "vv",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ describe("feedbackTrainer.cy.js", () => {
|
||||||
cy.get('[data-cy="feedback-data-amount"]').should("contain", "0");
|
cy.get('[data-cy="feedback-data-amount"]').should("contain", "0");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("FeedbackUK", function () {
|
||||||
it("can open feedback results page with results", () => {
|
it("can open feedback results page with results", () => {
|
||||||
cy.manageCommand("cypress_reset --create-feedback-responses");
|
cy.manageCommand("cypress_reset --create-feedback-responses");
|
||||||
login("test-trainer1@example.com", "test");
|
login("test-trainer1@example.com", "test");
|
||||||
|
|
@ -26,6 +27,48 @@ describe("feedbackTrainer.cy.js", () => {
|
||||||
|
|
||||||
cy.get('[data-cy="feedback-data-amount"]').should("contain", "3");
|
cy.get('[data-cy="feedback-data-amount"]').should("contain", "3");
|
||||||
|
|
||||||
|
// check titles of questions
|
||||||
|
cy.get('[data-cy="question-1"]').should(
|
||||||
|
"contain",
|
||||||
|
"Zufriedenheit insgesamt"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-2"]').should(
|
||||||
|
"contain",
|
||||||
|
"Zielerreichung insgesamt"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-3"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Kurs?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-4"]').should(
|
||||||
|
"contain",
|
||||||
|
"Waren die Vorbereitungsaufträge klar und verständlich?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-5"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wie beurteilst du die Themensicherheit und Fachkompetenz des Kursleiters/der Kursleiterin?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-6"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wurden Fragen und Anregungen der Kursteilnehmenden ernst genommen und aufgegriffen?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-7"]').should(
|
||||||
|
"contain",
|
||||||
|
"Was möchtest du dem Kursleiter/der Kursleiterin sonst noch sagen?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-8"]').should(
|
||||||
|
"contain",
|
||||||
|
"Würdest du den Kurs weiterempfehlen?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-9"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wo siehst du Verbesserungspotential?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-10"]').should(
|
||||||
|
"contain",
|
||||||
|
"Was hat dir besonders gut gefallen?"
|
||||||
|
);
|
||||||
|
|
||||||
cy.get('[data-cy="question-1"]')
|
cy.get('[data-cy="question-1"]')
|
||||||
.find('[data-cy="rating-scale-average"]')
|
.find('[data-cy="rating-scale-average"]')
|
||||||
.should("contain", "3.3");
|
.should("contain", "3.3");
|
||||||
|
|
@ -89,4 +132,100 @@ describe("feedbackTrainer.cy.js", () => {
|
||||||
.should("contain", "Das Beispiel mit der Katze fand ich sehr gut")
|
.should("contain", "Das Beispiel mit der Katze fand ich sehr gut")
|
||||||
.should("contain", "Die Präsentation war super");
|
.should("contain", "Die Präsentation war super");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("FeedbackVV", function () {
|
||||||
|
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="dropdown-select"]').click();
|
||||||
|
cy.get('[data-cy="dropdown-select-option-Reisen"]').click();
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="show-feedback-btn-test-lehrgang-lp-circle-reisen-lc-feedback"]'
|
||||||
|
).click();
|
||||||
|
|
||||||
|
cy.get('[data-cy="feedback-data-amount"]').should("contain", "3");
|
||||||
|
|
||||||
|
// check titles of questions
|
||||||
|
cy.get('[data-cy="question-1"]').should(
|
||||||
|
"contain",
|
||||||
|
"Zufriedenheit insgesamt"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-2"]').should(
|
||||||
|
"contain",
|
||||||
|
"Zielerreichung insgesamt"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-3"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Circle?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-4"]').should(
|
||||||
|
"contain",
|
||||||
|
"Waren die Praxisaufträge klar und verständlich?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-5"]').should(
|
||||||
|
"contain",
|
||||||
|
"Würdest du den Circle weiterempfehlen?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-6"]').should(
|
||||||
|
"contain",
|
||||||
|
"Wo siehst du Verbesserungspotential?"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="question-7"]').should(
|
||||||
|
"contain",
|
||||||
|
"Was hat dir besonders gut gefallen?"
|
||||||
|
);
|
||||||
|
|
||||||
|
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="popover-yes"]')
|
||||||
|
.click()
|
||||||
|
.find('[data-cy="num-yes"]')
|
||||||
|
.should("contain", "2");
|
||||||
|
cy.get('[data-cy="question-5"]')
|
||||||
|
.find('[data-cy="popover-no"]')
|
||||||
|
.click()
|
||||||
|
.find('[data-cy="num-no"]')
|
||||||
|
.should("contain", "1");
|
||||||
|
|
||||||
|
cy.get('[data-cy="question-6"]')
|
||||||
|
.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-7"]')
|
||||||
|
.should("contain", "Nur Gutes.")
|
||||||
|
.should("contain", "Das Beispiel mit der Katze fand ich sehr gut")
|
||||||
|
.should("contain", "Die Präsentation war super");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ from vbv_lernwelt.course_session.services.attendance import AttendanceUserStatus
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
from vbv_lernwelt.learnpath.models import (
|
from vbv_lernwelt.learnpath.models import (
|
||||||
LearningContentAttendanceCourse,
|
LearningContentAttendanceCourse,
|
||||||
LearningContentFeedback,
|
LearningContentFeedbackUK,
|
||||||
|
LearningContentFeedbackVV,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.notify.models import Notification
|
from vbv_lernwelt.notify.models import Notification
|
||||||
|
|
||||||
|
|
@ -155,7 +156,9 @@ def command(
|
||||||
if create_feedback_responses:
|
if create_feedback_responses:
|
||||||
print("create_feedback_responses")
|
print("create_feedback_responses")
|
||||||
course_session = CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID)
|
course_session = CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID)
|
||||||
learning_content_feedback_page = LearningContentFeedback.objects.get(
|
|
||||||
|
# feedback fahrzeug
|
||||||
|
learning_content_feedback_page = LearningContentFeedbackUK.objects.get(
|
||||||
slug="test-lehrgang-lp-circle-fahrzeug-lc-feedback"
|
slug="test-lehrgang-lp-circle-fahrzeug-lc-feedback"
|
||||||
)
|
)
|
||||||
create_feedback_response_data(
|
create_feedback_response_data(
|
||||||
|
|
@ -174,6 +177,7 @@ def command(
|
||||||
"would_recommend": True,
|
"would_recommend": True,
|
||||||
"course_negative_feedback": "Nichts Schlechtes",
|
"course_negative_feedback": "Nichts Schlechtes",
|
||||||
"course_positive_feedback": "Nur Gutes.",
|
"course_positive_feedback": "Nur Gutes.",
|
||||||
|
"feedback_type": "uk",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -193,6 +197,7 @@ def command(
|
||||||
"would_recommend": True,
|
"would_recommend": True,
|
||||||
"course_negative_feedback": "Es wäre praktisch, Zugang zu einer FAQ zu haben.",
|
"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!",
|
"course_positive_feedback": "Das Beispiel mit der Katze fand ich sehr gut veranschaulicht!",
|
||||||
|
"feedback_type": "uk",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -212,6 +217,62 @@ def command(
|
||||||
"would_recommend": False,
|
"would_recommend": False,
|
||||||
"course_negative_feedback": "Mehr Videos wären schön.",
|
"course_negative_feedback": "Mehr Videos wären schön.",
|
||||||
"course_positive_feedback": "Die Präsentation war super",
|
"course_positive_feedback": "Die Präsentation war super",
|
||||||
|
"feedback_type": "uk",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# feedback reisen
|
||||||
|
learning_content_feedback_page = LearningContentFeedbackVV.objects.get(
|
||||||
|
slug="test-lehrgang-lp-circle-reisen-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,
|
||||||
|
"would_recommend": True,
|
||||||
|
"course_negative_feedback": "Nichts Schlechtes",
|
||||||
|
"course_positive_feedback": "Nur Gutes.",
|
||||||
|
"feedback_type": "vv",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
"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!",
|
||||||
|
"feedback_type": "vv",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
"would_recommend": False,
|
||||||
|
"course_negative_feedback": "Mehr Videos wären schön.",
|
||||||
|
"course_positive_feedback": "Die Präsentation war super",
|
||||||
|
"feedback_type": "vv",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,8 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
||||||
LearningContentAssignmentFactory,
|
LearningContentAssignmentFactory,
|
||||||
LearningContentAttendanceCourseFactory,
|
LearningContentAttendanceCourseFactory,
|
||||||
LearningContentEdoniqTestFactory,
|
LearningContentEdoniqTestFactory,
|
||||||
LearningContentFeedbackFactory,
|
LearningContentFeedbackUKFactory,
|
||||||
|
LearningContentFeedbackVVFactory,
|
||||||
LearningContentKnowledgeAssessmentFactory,
|
LearningContentKnowledgeAssessmentFactory,
|
||||||
LearningContentLearningModuleFactory,
|
LearningContentLearningModuleFactory,
|
||||||
LearningContentMediaLibraryFactory,
|
LearningContentMediaLibraryFactory,
|
||||||
|
|
@ -371,6 +372,7 @@ def create_feedback_response_data(
|
||||||
"would_recommend": True,
|
"would_recommend": True,
|
||||||
"course_negative_feedback": "Nichts Schlechtes",
|
"course_negative_feedback": "Nichts Schlechtes",
|
||||||
"course_positive_feedback": "Nur Gutes.",
|
"course_positive_feedback": "Nur Gutes.",
|
||||||
|
"feedback_type": "uk",
|
||||||
}
|
}
|
||||||
|
|
||||||
return update_feedback_response(
|
return update_feedback_response(
|
||||||
|
|
@ -549,7 +551,8 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
|
||||||
slug__startswith="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs"
|
slug__startswith="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
|
title="Feedback",
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -633,7 +636,8 @@ def create_test_circle_reisen(lp):
|
||||||
title="Reflexion",
|
title="Reflexion",
|
||||||
parent=parent,
|
parent=parent,
|
||||||
)
|
)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
|
title="Feedback",
|
||||||
parent=parent,
|
parent=parent,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
||||||
LearningContentAttendanceCourseFactory,
|
LearningContentAttendanceCourseFactory,
|
||||||
LearningContentDocumentListFactory,
|
LearningContentDocumentListFactory,
|
||||||
LearningContentEdoniqTestFactory,
|
LearningContentEdoniqTestFactory,
|
||||||
LearningContentFeedbackFactory,
|
LearningContentFeedbackUKFactory,
|
||||||
LearningContentMediaLibraryFactory,
|
LearningContentMediaLibraryFactory,
|
||||||
LearningContentPlaceholderFactory,
|
LearningContentPlaceholderFactory,
|
||||||
LearningPathFactory,
|
LearningPathFactory,
|
||||||
|
|
@ -260,7 +260,7 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
|
||||||
title="Unterlagen für den Unterricht",
|
title="Unterlagen für den Unterricht",
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
||||||
|
|
@ -370,7 +370,7 @@ In diesem Circle erfährst du wie die überbetrieblichen Kurse aufgebaut sind. Z
|
||||||
# test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
# test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||||
# )
|
# )
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
||||||
|
|
@ -485,7 +485,7 @@ Dans ce cercle, tu apprendras comment les cours interentreprises sont structuré
|
||||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||||
)
|
)
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Transfert", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Transfert", parent=circle, icon="it-icon-ls-end")
|
||||||
|
|
@ -600,7 +600,7 @@ In questo Circle imparerai come sono strutturati i corsi interaziendali. Imparer
|
||||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||||
)
|
)
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Trasferimento", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Trasferimento", parent=circle, icon="it-icon-ls-end")
|
||||||
|
|
@ -705,7 +705,7 @@ In diesem Circle lernst du die wichtigsten Grundlagen bezüglich Versicherungswi
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
||||||
|
|
@ -815,7 +815,7 @@ Dans ce cercle, tu apprends les bases les plus importantes en matière d'assuran
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Transfert", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Transfert", parent=circle, icon="it-icon-ls-end")
|
||||||
|
|
@ -924,7 +924,7 @@ In questo Circle imparerai le basi più importanti del settore assicurativo e de
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Trasferimento", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Trasferimento", parent=circle, icon="it-icon-ls-end")
|
||||||
|
|
@ -1064,7 +1064,7 @@ def create_uk_circle_fahrzeug(lp, title="Fahrzeug"):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1198,7 +1198,7 @@ def create_uk_fr_circle_fahrzeug(lp, title="Véhicule"):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1336,7 +1336,7 @@ def create_uk_it_circle_fahrzeug(lp, title="Veicolo"):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackUKFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ from vbv_lernwelt.learnpath.graphql.types import (
|
||||||
LearningContentAttendanceCourseObjectType,
|
LearningContentAttendanceCourseObjectType,
|
||||||
LearningContentDocumentListObjectType,
|
LearningContentDocumentListObjectType,
|
||||||
LearningContentEdoniqTestObjectType,
|
LearningContentEdoniqTestObjectType,
|
||||||
LearningContentFeedbackObjectType,
|
LearningContentFeedbackUKObjectType,
|
||||||
|
LearningContentFeedbackVVObjectType,
|
||||||
LearningContentKnowledgeAssessmentObjectType,
|
LearningContentKnowledgeAssessmentObjectType,
|
||||||
LearningContentLearningModuleObjectType,
|
LearningContentLearningModuleObjectType,
|
||||||
LearningContentMediaLibraryObjectType,
|
LearningContentMediaLibraryObjectType,
|
||||||
|
|
@ -50,7 +51,8 @@ class CourseQuery(graphene.ObjectType):
|
||||||
learning_content_attendance_course = graphene.Field(
|
learning_content_attendance_course = graphene.Field(
|
||||||
LearningContentAttendanceCourseObjectType
|
LearningContentAttendanceCourseObjectType
|
||||||
)
|
)
|
||||||
learning_content_feedback = graphene.Field(LearningContentFeedbackObjectType)
|
learning_content_feedback_uk = graphene.Field(LearningContentFeedbackUKObjectType)
|
||||||
|
learning_content_feedback_vv = graphene.Field(LearningContentFeedbackVVObjectType)
|
||||||
learning_content_learning_module = graphene.Field(
|
learning_content_learning_module = graphene.Field(
|
||||||
LearningContentLearningModuleObjectType
|
LearningContentLearningModuleObjectType
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ class FeedbackResponseFactory(DjangoModelFactory):
|
||||||
"Das Beispiel mit der Katze fand ich sehr gut veranschaulicht!",
|
"Das Beispiel mit der Katze fand ich sehr gut veranschaulicht!",
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
"feedback_type": FuzzyChoice(["uk", "vv"]),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,16 @@ from vbv_lernwelt.course.models import CourseSession
|
||||||
from vbv_lernwelt.feedback.graphql.types import (
|
from vbv_lernwelt.feedback.graphql.types import (
|
||||||
FeedbackResponseObjectType as FeedbackResponseType,
|
FeedbackResponseObjectType as FeedbackResponseType,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.feedback.serializers import CourseFeedbackSerializer
|
from vbv_lernwelt.feedback.serializers import (
|
||||||
|
CourseFeedbackSerializerUK,
|
||||||
|
CourseFeedbackSerializerVV,
|
||||||
|
)
|
||||||
from vbv_lernwelt.feedback.services import update_feedback_response
|
from vbv_lernwelt.feedback.services import update_feedback_response
|
||||||
from vbv_lernwelt.iam.permissions import has_course_session_access
|
from vbv_lernwelt.iam.permissions import has_course_session_access
|
||||||
from vbv_lernwelt.learnpath.models import LearningContentFeedback
|
from vbv_lernwelt.learnpath.models import (
|
||||||
|
LearningContentFeedbackUK,
|
||||||
|
LearningContentFeedbackVV,
|
||||||
|
)
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
@ -25,6 +31,7 @@ class SendFeedbackMutation(graphene.Mutation):
|
||||||
class Arguments:
|
class Arguments:
|
||||||
course_session_id = graphene.ID(required=True)
|
course_session_id = graphene.ID(required=True)
|
||||||
learning_content_page_id = graphene.ID(required=True)
|
learning_content_page_id = graphene.ID(required=True)
|
||||||
|
learning_content_type = graphene.String(required=True)
|
||||||
data = GenericScalar()
|
data = GenericScalar()
|
||||||
submitted = graphene.Boolean(required=False, default_value=False)
|
submitted = graphene.Boolean(required=False, default_value=False)
|
||||||
|
|
||||||
|
|
@ -35,11 +42,29 @@ class SendFeedbackMutation(graphene.Mutation):
|
||||||
info,
|
info,
|
||||||
course_session_id,
|
course_session_id,
|
||||||
learning_content_page_id,
|
learning_content_page_id,
|
||||||
|
learning_content_type,
|
||||||
data,
|
data,
|
||||||
submitted,
|
submitted,
|
||||||
):
|
):
|
||||||
feedback_user_id = info.context.user.id
|
feedback_user_id = info.context.user.id
|
||||||
learning_content = LearningContentFeedback.objects.get(
|
|
||||||
|
if learning_content_type == "learnpath.LearningContentFeedbackVV":
|
||||||
|
learningContentFeedbackModel = LearningContentFeedbackVV
|
||||||
|
serializerClass = CourseFeedbackSerializerVV
|
||||||
|
data["feedback_type"] = "vv"
|
||||||
|
elif learning_content_type == "learnpath.LearningContentFeedbackUK":
|
||||||
|
learningContentFeedbackModel = LearningContentFeedbackUK
|
||||||
|
serializerClass = CourseFeedbackSerializerUK
|
||||||
|
data["feedback_type"] = "uk"
|
||||||
|
else:
|
||||||
|
errors = [
|
||||||
|
ErrorType(
|
||||||
|
field="learningContentType", messages="Invalid learningContentType"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
return SendFeedbackMutation(errors=errors)
|
||||||
|
|
||||||
|
learning_content = learningContentFeedbackModel.objects.get(
|
||||||
id=learning_content_page_id
|
id=learning_content_page_id
|
||||||
)
|
)
|
||||||
circle = learning_content.get_circle()
|
circle = learning_content.get_circle()
|
||||||
|
|
@ -65,7 +90,7 @@ class SendFeedbackMutation(graphene.Mutation):
|
||||||
course_session_id=course_session_id,
|
course_session_id=course_session_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = CourseFeedbackSerializer(data=data)
|
serializer = serializerClass(data=data)
|
||||||
|
|
||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-12-07 14:01
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def add_field_to_json(apps, _schema_editor):
|
||||||
|
FeedbackResponse = apps.get_model("feedback", "FeedbackResponse")
|
||||||
|
for instance in FeedbackResponse.objects.all():
|
||||||
|
if instance.data is None:
|
||||||
|
instance.data = {}
|
||||||
|
instance.data["feedback_type"] = "uk" # Set the default value
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("feedback", "0006_auto_20230922_1131"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(add_field_to_json),
|
||||||
|
]
|
||||||
|
|
@ -5,6 +5,11 @@ from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
FEEDBACK_TYPES = (
|
||||||
|
("uk", "Feedback UK"),
|
||||||
|
("vv", "Feedback VV"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FeedbackIntegerField(serializers.IntegerField):
|
class FeedbackIntegerField(serializers.IntegerField):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
@ -13,7 +18,8 @@ class FeedbackIntegerField(serializers.IntegerField):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CourseFeedbackSerializer(serializers.Serializer):
|
class CourseFeedbackSerializerUK(serializers.Serializer):
|
||||||
|
feedback_type = serializers.ChoiceField(choices=FEEDBACK_TYPES)
|
||||||
satisfaction = FeedbackIntegerField()
|
satisfaction = FeedbackIntegerField()
|
||||||
goal_attainment = FeedbackIntegerField()
|
goal_attainment = FeedbackIntegerField()
|
||||||
proficiency = serializers.IntegerField(required=False, allow_null=True)
|
proficiency = serializers.IntegerField(required=False, allow_null=True)
|
||||||
|
|
@ -33,6 +39,22 @@ class CourseFeedbackSerializer(serializers.Serializer):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CourseFeedbackSerializerVV(serializers.Serializer):
|
||||||
|
feedback_type = serializers.ChoiceField(choices=FEEDBACK_TYPES)
|
||||||
|
satisfaction = FeedbackIntegerField()
|
||||||
|
goal_attainment = FeedbackIntegerField()
|
||||||
|
proficiency = serializers.IntegerField(required=False, allow_null=True)
|
||||||
|
preparation_task_clarity = serializers.BooleanField(required=False, allow_null=True)
|
||||||
|
materials_rating = FeedbackIntegerField()
|
||||||
|
would_recommend = serializers.BooleanField(required=False, allow_null=True)
|
||||||
|
course_positive_feedback = serializers.CharField(
|
||||||
|
required=False, allow_null=True, allow_blank=True
|
||||||
|
)
|
||||||
|
course_negative_feedback = serializers.CharField(
|
||||||
|
required=False, allow_null=True, allow_blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CypressFeedbackResponseSerializer(serializers.ModelSerializer):
|
class CypressFeedbackResponseSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FeedbackResponse
|
model = FeedbackResponse
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
|
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSession
|
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSession
|
||||||
from vbv_lernwelt.course.services import mark_course_completion
|
from vbv_lernwelt.course.services import mark_course_completion
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
from vbv_lernwelt.learnpath.models import LearningContentFeedback
|
from vbv_lernwelt.learnpath.models import (
|
||||||
|
LearningContentFeedbackUK,
|
||||||
|
LearningContentFeedbackVV,
|
||||||
|
)
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
@ -12,7 +17,9 @@ logger = structlog.get_logger(__name__)
|
||||||
def update_feedback_response(
|
def update_feedback_response(
|
||||||
feedback_user: User,
|
feedback_user: User,
|
||||||
course_session: CourseSession,
|
course_session: CourseSession,
|
||||||
learning_content_feedback_page: LearningContentFeedback,
|
learning_content_feedback_page: Union[
|
||||||
|
LearningContentFeedbackUK, LearningContentFeedbackVV
|
||||||
|
],
|
||||||
submitted: bool,
|
submitted: bool,
|
||||||
validated_data: dict,
|
validated_data: dict,
|
||||||
):
|
):
|
||||||
|
|
@ -26,18 +33,7 @@ def update_feedback_response(
|
||||||
|
|
||||||
original_data = feedback_response.data
|
original_data = feedback_response.data
|
||||||
updated_data = validated_data
|
updated_data = validated_data
|
||||||
initial_data = {
|
initial_data = initial_data_for_feedback_page(learning_content_feedback_page)
|
||||||
"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 | {
|
merged_data = initial_data | {
|
||||||
key: updated_data[key]
|
key: updated_data[key]
|
||||||
|
|
@ -71,3 +67,36 @@ def update_feedback_response(
|
||||||
)
|
)
|
||||||
|
|
||||||
return feedback_response
|
return feedback_response
|
||||||
|
|
||||||
|
|
||||||
|
def initial_data_for_feedback_page(
|
||||||
|
learning_content_feedback_page: Union[
|
||||||
|
LearningContentFeedbackUK, LearningContentFeedbackVV
|
||||||
|
]
|
||||||
|
):
|
||||||
|
if hasattr(learning_content_feedback_page, "learningcontentfeedbackuk"):
|
||||||
|
return {
|
||||||
|
"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": "",
|
||||||
|
"feedback_type": "uk",
|
||||||
|
}
|
||||||
|
if hasattr(learning_content_feedback_page, "learningcontentfeedbackvv"):
|
||||||
|
return {
|
||||||
|
"satisfaction": None,
|
||||||
|
"goal_attainment": None,
|
||||||
|
"proficiency": None,
|
||||||
|
"preparation_task_clarity": None,
|
||||||
|
"would_recommend": None,
|
||||||
|
"course_negative_feedback": "",
|
||||||
|
"course_positive_feedback": "",
|
||||||
|
"feedback_type": "vv",
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ class FeedbackRestApiTestCase(FeedbackBaseTestCase):
|
||||||
"course_negative_feedback": self.feedback_data[
|
"course_negative_feedback": self.feedback_data[
|
||||||
"course_negative_feedback"
|
"course_negative_feedback"
|
||||||
][i],
|
][i],
|
||||||
|
"feedback_type": "uk",
|
||||||
},
|
},
|
||||||
feedback_user=self.feedback_users[i],
|
feedback_user=self.feedback_users[i],
|
||||||
submitted=True,
|
submitted=True,
|
||||||
|
|
@ -129,6 +130,7 @@ class FeedbackRestApiTestCase(FeedbackBaseTestCase):
|
||||||
expected = {
|
expected = {
|
||||||
"amount": 3,
|
"amount": 3,
|
||||||
"questions": self.feedback_data,
|
"questions": self.feedback_data,
|
||||||
|
"feedbackType": "uk",
|
||||||
}
|
}
|
||||||
print(response.data)
|
print(response.data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from graphene_django.utils.testing import GraphQLTestCase
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
|
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.models import CourseSession
|
||||||
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
|
from vbv_lernwelt.learnpath.models import LearningContentFeedbackUK
|
||||||
|
|
||||||
|
|
||||||
|
class FeedbackMutationTestCase(GraphQLTestCase):
|
||||||
|
GRAPHQL_URL = "/server/graphql/"
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
create_default_users()
|
||||||
|
create_test_course(include_vv=False, with_sessions=True)
|
||||||
|
self.course_session = CourseSession.objects.get(title="Test Bern 2022 a")
|
||||||
|
self.learning_content_feedback_page = LearningContentFeedbackUK.objects.get(
|
||||||
|
slug="test-lehrgang-lp-circle-fahrzeug-lc-feedback"
|
||||||
|
)
|
||||||
|
self.student = User.objects.get(username="test-student1@example.com")
|
||||||
|
self.client.force_login(self.student)
|
||||||
|
|
||||||
|
def test_creates_response(self):
|
||||||
|
data = {
|
||||||
|
"course_negative_feedback": "schlecht",
|
||||||
|
"course_positive_feedback": "gut",
|
||||||
|
"feedback_type": "uk",
|
||||||
|
"goal_attainment": 3,
|
||||||
|
"preparation_task_clarity": False,
|
||||||
|
"proficiency": 100,
|
||||||
|
"satisfaction": 3,
|
||||||
|
"would_recommend": False,
|
||||||
|
"instructor_competence": None,
|
||||||
|
"instructor_respect": None,
|
||||||
|
"instructor_open_feedback": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.query(
|
||||||
|
f"""
|
||||||
|
mutation {{
|
||||||
|
send_feedback(
|
||||||
|
course_session_id: "{COURSE_TEST_ID}"
|
||||||
|
learning_content_page_id: "{self.learning_content_feedback_page.id}"
|
||||||
|
learning_content_type: "learnpath.LearningContentFeedbackUK"
|
||||||
|
data: {{
|
||||||
|
course_negative_feedback: "{data['course_negative_feedback']}",
|
||||||
|
course_positive_feedback: "{data['course_positive_feedback']}",
|
||||||
|
feedback_type: null,
|
||||||
|
goal_attainment: {data['goal_attainment']},
|
||||||
|
preparation_task_clarity: {str(data['preparation_task_clarity']).lower()},
|
||||||
|
proficiency: {data['proficiency']},
|
||||||
|
satisfaction: {data['satisfaction']},
|
||||||
|
would_recommend: {str(data['would_recommend']).lower()},
|
||||||
|
instructor_competence: null,
|
||||||
|
instructor_respect: null,
|
||||||
|
instructor_open_feedback: null,
|
||||||
|
}},
|
||||||
|
submitted: false
|
||||||
|
) {{
|
||||||
|
feedback_response {{
|
||||||
|
id
|
||||||
|
data
|
||||||
|
submitted
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
errors {{
|
||||||
|
field
|
||||||
|
messages
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
__typename
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
content = json.loads(response.content)
|
||||||
|
|
||||||
|
self.assertResponseNoErrors(response)
|
||||||
|
self.assertDictEqual(
|
||||||
|
content["data"]["send_feedback"]["feedback_response"]["data"], data
|
||||||
|
)
|
||||||
|
|
||||||
|
feedback = FeedbackResponse.objects.first()
|
||||||
|
self.assertEqual(feedback.data, data)
|
||||||
|
self.assertEqual(feedback.submitted, False)
|
||||||
|
self.assertEqual(feedback.feedback_user, self.student)
|
||||||
|
|
@ -61,12 +61,13 @@ def get_feedback_for_circle(request, course_session_id, circle_id):
|
||||||
feedback_user__in=feedback_users(course_session_id),
|
feedback_user__in=feedback_users(course_session_id),
|
||||||
).order_by("created_at")
|
).order_by("created_at")
|
||||||
|
|
||||||
# I guess this is ok for the üK case
|
feedback_data = {"amount": len(feedbacks), "questions": {}, "feedbackType": None}
|
||||||
feedback_data = {"amount": len(feedbacks), "questions": {}}
|
|
||||||
|
|
||||||
if feedback_data["amount"] == 0:
|
if feedback_data["amount"] == 0:
|
||||||
return Response(status=200, data=feedback_data)
|
return Response(status=200, data=feedback_data)
|
||||||
|
|
||||||
|
feedback_data["feedbackType"] = feedbacks[0].data.get("feedback_type", None)
|
||||||
|
|
||||||
for field in FEEDBACK_FIELDS:
|
for field in FEEDBACK_FIELDS:
|
||||||
feedback_data["questions"][field] = []
|
feedback_data["questions"][field] = []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from vbv_lernwelt.course.models import CourseCategory, CoursePage
|
||||||
from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
||||||
CircleFactory,
|
CircleFactory,
|
||||||
LearningContentAssignmentFactory,
|
LearningContentAssignmentFactory,
|
||||||
LearningContentFeedbackFactory,
|
LearningContentFeedbackVVFactory,
|
||||||
LearningContentLearningModuleFactory,
|
LearningContentLearningModuleFactory,
|
||||||
LearningContentMediaLibraryFactory,
|
LearningContentMediaLibraryFactory,
|
||||||
LearningContentPlaceholderFactory,
|
LearningContentPlaceholderFactory,
|
||||||
|
|
@ -201,7 +201,7 @@ def create_circle_basis(lp, title="Basis"):
|
||||||
slug__startswith=f"versicherungsvermittler-in-assignment-reflexion"
|
slug__startswith=f"versicherungsvermittler-in-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -278,7 +278,7 @@ def create_circle_gewinnen(lp, title="Gewinnen"):
|
||||||
slug__startswith=f"{course_slug}-assignment-reflexion"
|
slug__startswith=f"{course_slug}-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -368,7 +368,7 @@ def create_circle_fahrzeug(lp, title="Fahrzeug"):
|
||||||
# slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
# slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
||||||
# ),
|
# ),
|
||||||
# ),
|
# ),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -554,7 +554,7 @@ def create_circle_reisen(lp, title="Reisen"):
|
||||||
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -647,7 +647,7 @@ def create_circle_einkommenssicherung(lp, title="Einkommenssicherung"):
|
||||||
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -700,7 +700,7 @@ def create_circle_wohneigentum(lp, title="Wohneigentum"):
|
||||||
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -782,7 +782,7 @@ def create_circle_pensionierung(lp, title="Pensionierung"):
|
||||||
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -839,7 +839,7 @@ def create_circle_erben(lp, title="Erben/Vererben"):
|
||||||
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -929,7 +929,7 @@ def create_circle_gesundheit(lp, title="Gesundheit"):
|
||||||
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
slug__startswith=f"{circle.get_course().slug}-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=circle,
|
parent=circle,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1352,7 +1352,7 @@ def create_learning_sequence_transfer(parent, title, lc_praxis_title=None):
|
||||||
slug__startswith=f"versicherungsvermittler-in-assignment-reflexion"
|
slug__startswith=f"versicherungsvermittler-in-assignment-reflexion"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LearningContentFeedbackFactory(
|
LearningContentFeedbackVVFactory(
|
||||||
parent=parent,
|
parent=parent,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ from vbv_lernwelt.learnpath.models import (
|
||||||
LearningContentAttendanceCourse,
|
LearningContentAttendanceCourse,
|
||||||
LearningContentDocumentList,
|
LearningContentDocumentList,
|
||||||
LearningContentEdoniqTest,
|
LearningContentEdoniqTest,
|
||||||
LearningContentFeedback,
|
LearningContentFeedbackUK,
|
||||||
|
LearningContentFeedbackVV,
|
||||||
LearningContentKnowledgeAssessment,
|
LearningContentKnowledgeAssessment,
|
||||||
LearningContentLearningModule,
|
LearningContentLearningModule,
|
||||||
LearningContentMediaLibrary,
|
LearningContentMediaLibrary,
|
||||||
|
|
@ -49,8 +50,10 @@ class LearningContentInterface(CoursePageInterface):
|
||||||
return LearningContentAssignmentObjectType
|
return LearningContentAssignmentObjectType
|
||||||
elif isinstance(instance, LearningContentAttendanceCourse):
|
elif isinstance(instance, LearningContentAttendanceCourse):
|
||||||
return LearningContentAttendanceCourseObjectType
|
return LearningContentAttendanceCourseObjectType
|
||||||
elif isinstance(instance, LearningContentFeedback):
|
elif isinstance(instance, LearningContentFeedbackUK):
|
||||||
return LearningContentFeedbackObjectType
|
return LearningContentFeedbackUKObjectType
|
||||||
|
elif isinstance(instance, LearningContentFeedbackVV):
|
||||||
|
return LearningContentFeedbackVVObjectType
|
||||||
elif isinstance(instance, LearningContentLearningModule):
|
elif isinstance(instance, LearningContentLearningModule):
|
||||||
return LearningContentLearningModuleObjectType
|
return LearningContentLearningModuleObjectType
|
||||||
elif isinstance(instance, LearningContentKnowledgeAssessment):
|
elif isinstance(instance, LearningContentKnowledgeAssessment):
|
||||||
|
|
@ -105,9 +108,19 @@ class LearningContentPlaceholderObjectType(DjangoObjectType):
|
||||||
fields = []
|
fields = []
|
||||||
|
|
||||||
|
|
||||||
class LearningContentFeedbackObjectType(DjangoObjectType):
|
class LearningContentFeedbackUKObjectType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = LearningContentFeedback
|
model = LearningContentFeedbackUK
|
||||||
|
interfaces = (
|
||||||
|
CoursePageInterface,
|
||||||
|
LearningContentInterface,
|
||||||
|
)
|
||||||
|
fields = []
|
||||||
|
|
||||||
|
|
||||||
|
class LearningContentFeedbackVVObjectType(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = LearningContentFeedbackVV
|
||||||
interfaces = (
|
interfaces = (
|
||||||
CoursePageInterface,
|
CoursePageInterface,
|
||||||
LearningContentInterface,
|
LearningContentInterface,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-11-29 07:27
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import wagtail.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("wagtailcore", "0089_log_entry_data_json_null_to_object"),
|
||||||
|
("learnpath", "0011_learningcontentknowledgeassessment"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel("LearningContentFeedback", "LearningContentFeedbackUK"),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="LearningContentFeedbackVV",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"page_ptr",
|
||||||
|
models.OneToOneField(
|
||||||
|
auto_created=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
parent_link=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
to="wagtailcore.page",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("minutes", models.PositiveIntegerField(default=15)),
|
||||||
|
("description", wagtail.fields.RichTextField(blank=True)),
|
||||||
|
("content_url", models.TextField(blank=True)),
|
||||||
|
("has_course_completion_status", models.BooleanField(default=True)),
|
||||||
|
(
|
||||||
|
"can_user_self_toggle_course_completion",
|
||||||
|
models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
bases=("wagtailcore.page",),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="learningcontentassignment",
|
||||||
|
name="assignment_type",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("VOLUNTARY_CASEWORK", "VOLUNTARY_CASEWORK"),
|
||||||
|
("MANDATORY_CASEWORK", "MANDATORY_CASEWORK"),
|
||||||
|
("PREP_ASSIGNMENT", "PREP_ASSIGNMENT"),
|
||||||
|
("REFLECTION", "REFLECTION"),
|
||||||
|
("CONDITION_ACCEPTANCE", "CONDITION_ACCEPTANCE"),
|
||||||
|
("EDONIQ_TEST", "EDONIQ_TEST"),
|
||||||
|
],
|
||||||
|
default="MANDATORY_CASEWORK",
|
||||||
|
max_length=50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -72,7 +72,8 @@ class Circle(CourseBasePage):
|
||||||
"learnpath.LearningUnit",
|
"learnpath.LearningUnit",
|
||||||
"learnpath.LearningContentAssignment",
|
"learnpath.LearningContentAssignment",
|
||||||
"learnpath.LearningContentAttendanceCourse",
|
"learnpath.LearningContentAttendanceCourse",
|
||||||
"learnpath.LearningContentFeedback",
|
"learnpath.LearningContentFeedbackUK",
|
||||||
|
"learnpath.LearningContentFeedbackVV",
|
||||||
"learnpath.LearningContentLearningModule",
|
"learnpath.LearningContentLearningModule",
|
||||||
"learnpath.LearningContentKnowledgeAssessment",
|
"learnpath.LearningContentKnowledgeAssessment",
|
||||||
"learnpath.LearningContentMediaLibrary",
|
"learnpath.LearningContentMediaLibrary",
|
||||||
|
|
@ -318,7 +319,13 @@ class LearningContentPlaceholder(LearningContent):
|
||||||
can_user_self_toggle_course_completion = models.BooleanField(default=True)
|
can_user_self_toggle_course_completion = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
|
||||||
class LearningContentFeedback(LearningContent):
|
class LearningContentFeedbackUK(LearningContent):
|
||||||
|
parent_page_types = ["learnpath.Circle"]
|
||||||
|
subpage_types = []
|
||||||
|
can_user_self_toggle_course_completion = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
class LearningContentFeedbackVV(LearningContent):
|
||||||
parent_page_types = ["learnpath.Circle"]
|
parent_page_types = ["learnpath.Circle"]
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
can_user_self_toggle_course_completion = models.BooleanField(default=False)
|
can_user_self_toggle_course_completion = models.BooleanField(default=False)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ from vbv_lernwelt.learnpath.models import (
|
||||||
LearningContentAttendanceCourse,
|
LearningContentAttendanceCourse,
|
||||||
LearningContentDocumentList,
|
LearningContentDocumentList,
|
||||||
LearningContentEdoniqTest,
|
LearningContentEdoniqTest,
|
||||||
LearningContentFeedback,
|
LearningContentFeedbackUK,
|
||||||
|
LearningContentFeedbackVV,
|
||||||
LearningContentKnowledgeAssessment,
|
LearningContentKnowledgeAssessment,
|
||||||
LearningContentLearningModule,
|
LearningContentLearningModule,
|
||||||
LearningContentMediaLibrary,
|
LearningContentMediaLibrary,
|
||||||
|
|
@ -120,14 +121,24 @@ class LearningContentPlaceholderFactory(wagtail_factories.PageFactory):
|
||||||
model = LearningContentPlaceholder
|
model = LearningContentPlaceholder
|
||||||
|
|
||||||
|
|
||||||
class LearningContentFeedbackFactory(wagtail_factories.PageFactory):
|
class LearningContentFeedbackVVFactory(wagtail_factories.PageFactory):
|
||||||
title = "Feedback"
|
title = "FeedbackVV"
|
||||||
minutes = 0
|
minutes = 0
|
||||||
content_url = ""
|
content_url = ""
|
||||||
description = RichText("")
|
description = RichText("")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = LearningContentFeedback
|
model = LearningContentFeedbackVV
|
||||||
|
|
||||||
|
|
||||||
|
class LearningContentFeedbackUKFactory(wagtail_factories.PageFactory):
|
||||||
|
title = "FeedbackUK"
|
||||||
|
minutes = 0
|
||||||
|
content_url = ""
|
||||||
|
description = RichText("")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = LearningContentFeedbackUK
|
||||||
|
|
||||||
|
|
||||||
class LearningContentLearningModuleFactory(wagtail_factories.PageFactory):
|
class LearningContentLearningModuleFactory(wagtail_factories.PageFactory):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue