Merged in feature/VBV-488-import-edoniq-data (pull request #211)

Feature/VBV-488 import edoniq data
This commit is contained in:
Daniel Egger 2023-10-05 16:52:05 +00:00
commit 56bfb71b35
63 changed files with 1451 additions and 190 deletions

View File

@ -3,6 +3,10 @@
"current": "currentColor",
"white": "#ffffff",
"black": "#0A0A0A",
"error-red": {
"200": "#FEDADA",
"500": "#DD2424"
},
"blue": {
"200": "#D2E0FA",
"300": "#B4CCFA",

View File

@ -3,23 +3,25 @@ import dayjs from "dayjs";
import LocalizedFormat from "dayjs/plugin/localizedFormat";
import i18next from "i18next";
export const formatDueDate = (start: string, end: string) => {
export const formatDueDate = (start: string, end?: string) => {
dayjs.extend(LocalizedFormat);
const startDayjs = dayjs(start);
const endDayjs = dayjs(end);
const startDateString = getDateString(startDayjs);
const endDateString = getDateString(endDayjs);
// if startDayjs isundefined, dont show the day twice
let endDayjs;
let endDateString;
if (end) {
endDayjs = dayjs(end);
endDateString = getDateString(endDayjs);
}
if (!startDayjs.isValid() && !endDayjs.isValid()) {
// at least `start` must be provided and valid
if (!startDayjs.isValid()) {
return i18next.t("Termin nicht festgelegt");
}
if (!startDayjs || (!startDayjs.isValid() && endDayjs.isValid())) {
return `${endDateString} ${getTimeString(endDayjs)} ${endDayjs.format("[Uhr]")}`;
}
if (!endDayjs || (!endDayjs.isValid() && startDayjs.isValid())) {
// when only `start` is provided, show only the start date with time
if (!endDayjs || !endDayjs.isValid()) {
return `${startDateString} ${getTimeString(startDayjs)} ${startDayjs.format(
"[Uhr]"
)}`;

View File

@ -14,12 +14,12 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
*/
const documents = {
"\n mutation AttendanceCheckMutation(\n $attendanceCourseId: ID!\n $attendanceUserList: [AttendanceUserInputType]!\n ) {\n update_course_session_attendance_course_users(\n id: $attendanceCourseId\n attendance_user_list: $attendanceUserList\n ) {\n course_session_attendance_course {\n id\n attendance_user_list {\n user_id\n first_name\n last_name\n email\n status\n }\n }\n }\n }\n": types.AttendanceCheckMutationDocument,
"\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n": types.UpsertAssignmentCompletionDocument,
"\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_points\n completion_data\n }\n }\n }\n": types.UpsertAssignmentCompletionDocument,
"\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc,
"\n query attendanceCheckQuery($courseSessionId: ID!) {\n course_session_attendance_course(id: $courseSessionId) {\n id\n attendance_user_list {\n user_id\n status\n }\n }\n }\n": types.AttendanceCheckQueryDocument,
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
"\n query courseQuery($courseId: Int!) {\n course(id: $courseId) {\n id\n slug\n title\n category_name\n learning_path {\n id\n }\n }\n }\n": types.CourseQueryDocument,
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument,
};
@ -44,7 +44,7 @@ export function graphql(source: "\n mutation AttendanceCheckMutation(\n $att
/**
* 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 UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n"): (typeof documents)["\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n"];
export function graphql(source: "\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_points\n completion_data\n }\n }\n }\n"): (typeof documents)["\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_points\n completion_data\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@ -56,7 +56,7 @@ export function graphql(source: "\n query attendanceCheckQuery($courseSessionId
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n"): (typeof documents)["\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n"];
export function graphql(source: "\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n }\n }\n"): (typeof documents)["\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@ -64,7 +64,7 @@ export function graphql(source: "\n query courseQuery($courseId: Int!) {\n c
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"];
export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/

View File

@ -78,7 +78,9 @@ export type AssignmentCompletionObjectType = {
completion_data?: Maybe<Scalars['GenericScalar']['output']>;
completion_status: AssignmentAssignmentCompletionCompletionStatusChoices;
created_at: Scalars['DateTime']['output'];
evaluation_grade?: Maybe<Scalars['Float']['output']>;
edoniq_extended_time_flag: Scalars['Boolean']['output'];
evaluation_max_points?: Maybe<Scalars['Float']['output']>;
evaluation_passed?: Maybe<Scalars['Boolean']['output']>;
evaluation_points?: Maybe<Scalars['Float']['output']>;
evaluation_submitted_at?: Maybe<Scalars['DateTime']['output']>;
evaluation_user?: Maybe<UserType>;
@ -117,6 +119,8 @@ export type AssignmentObjectType = CoursePageInterface & {
learning_content?: Maybe<LearningContentInterface>;
live?: Maybe<Scalars['Boolean']['output']>;
max_points?: Maybe<Scalars['Int']['output']>;
/** Muss der Auftrag durch eine Expertin oder einen Experten beurteilt werden? */
needs_expert_evaluation: Scalars['Boolean']['output'];
performance_objectives?: Maybe<Scalars['JSONStreamField']['output']>;
slug?: Maybe<Scalars['String']['output']>;
tasks?: Maybe<Scalars['JSONStreamField']['output']>;
@ -561,7 +565,7 @@ export type MutationUpsertAssignmentCompletionArgs = {
completion_data_string?: InputMaybe<Scalars['String']['input']>;
completion_status?: InputMaybe<AssignmentCompletionStatus>;
course_session_id: Scalars['ID']['input'];
evaluation_grade?: InputMaybe<Scalars['Float']['input']>;
evaluation_passed?: InputMaybe<Scalars['Boolean']['input']>;
evaluation_points?: InputMaybe<Scalars['Float']['input']>;
initialize_completion?: InputMaybe<Scalars['Boolean']['input']>;
learning_content_page_id?: InputMaybe<Scalars['ID']['input']>;
@ -691,13 +695,12 @@ export type UpsertAssignmentCompletionMutationVariables = Exact<{
assignmentUserId?: InputMaybe<Scalars['UUID']['input']>;
completionStatus: AssignmentCompletionStatus;
completionDataString: Scalars['String']['input'];
evaluationGrade?: InputMaybe<Scalars['Float']['input']>;
evaluationPoints?: InputMaybe<Scalars['Float']['input']>;
initializeCompletion?: InputMaybe<Scalars['Boolean']['input']>;
}>;
export type UpsertAssignmentCompletionMutation = { __typename?: 'Mutation', upsert_assignment_completion?: { __typename?: 'AssignmentCompletionMutation', assignment_completion?: { __typename?: 'AssignmentCompletionObjectType', id: any, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_grade?: number | null, evaluation_points?: number | null, completion_data?: any | null } | null } | null };
export type UpsertAssignmentCompletionMutation = { __typename?: 'Mutation', upsert_assignment_completion?: { __typename?: 'AssignmentCompletionMutation', assignment_completion?: { __typename?: 'AssignmentCompletionObjectType', id: any, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_points?: number | null, completion_data?: any | null } | null } | null };
type CoursePageFieldsAssignmentObjectTypeFragment = { __typename?: 'AssignmentObjectType', title?: string | null, id?: string | null, slug?: string | null, content_type?: string | null, frontend_url?: string | null } & { ' $fragmentName'?: 'CoursePageFieldsAssignmentObjectTypeFragment' };
@ -732,10 +735,10 @@ export type AssignmentCompletionQueryQueryVariables = Exact<{
}>;
export type AssignmentCompletionQueryQuery = { __typename?: 'Query', assignment?: { __typename?: 'AssignmentObjectType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, max_points?: number | null, content_type?: string | null, effort_required: string, evaluation_description: string, evaluation_document_url: string, evaluation_tasks?: any | null, id?: string | null, intro_text: string, performance_objectives?: any | null, slug?: string | null, tasks?: any | null, title?: string | null, translation_key?: string | null, competence_certificate?: (
export type AssignmentCompletionQueryQuery = { __typename?: 'Query', assignment?: { __typename?: 'AssignmentObjectType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, needs_expert_evaluation: boolean, max_points?: number | null, content_type?: string | null, effort_required: string, evaluation_description: string, evaluation_document_url: string, evaluation_tasks?: any | null, id?: string | null, intro_text: string, performance_objectives?: any | null, slug?: string | null, tasks?: any | null, title?: string | null, translation_key?: string | null, competence_certificate?: (
{ __typename?: 'CompetenceCertificateObjectType' }
& { ' $fragmentRefs'?: { 'CoursePageFieldsCompetenceCertificateObjectTypeFragment': CoursePageFieldsCompetenceCertificateObjectTypeFragment } }
) | null } | null, assignment_completion?: { __typename?: 'AssignmentCompletionObjectType', id: any, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_grade?: number | null, evaluation_points?: number | null, completion_data?: any | null, evaluation_user?: { __typename?: 'UserType', id: any } | null, assignment_user: { __typename?: 'UserType', id: any } } | null };
) | null } | null, assignment_completion?: { __typename?: 'AssignmentCompletionObjectType', id: any, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_points?: number | null, evaluation_max_points?: number | null, evaluation_passed?: boolean | null, edoniq_extended_time_flag: boolean, completion_data?: any | null, evaluation_user?: { __typename?: 'UserType', id: any } | null, assignment_user: { __typename?: 'UserType', id: any } } | null };
export type CourseQueryQueryVariables = Exact<{
courseId: Scalars['Int']['input'];
@ -753,7 +756,7 @@ export type CompetenceCertificateQueryQueryVariables = Exact<{
export type CompetenceCertificateQueryQuery = { __typename?: 'Query', competence_certificate_list?: (
{ __typename?: 'CompetenceCertificateListObjectType', competence_certificates?: Array<(
{ __typename?: 'CompetenceCertificateObjectType', assignments?: Array<(
{ __typename?: 'AssignmentObjectType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, max_points?: number | null, completion?: { __typename?: 'AssignmentCompletionObjectType', id: any, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_points?: number | null } | null, learning_content?: { __typename?: 'LearningContentAssignmentObjectType', title?: string | null, id?: string | null, slug?: string | null, content_type?: string | null, frontend_url?: string | null, circle?: (
{ __typename?: 'AssignmentObjectType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, max_points?: number | null, completion?: { __typename?: 'AssignmentCompletionObjectType', id: any, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_points?: number | null, evaluation_max_points?: number | null, evaluation_passed?: boolean | null } | null, learning_content?: { __typename?: 'LearningContentAssignmentObjectType', title?: string | null, id?: string | null, slug?: string | null, content_type?: string | null, frontend_url?: string | null, circle?: (
{ __typename?: 'CircleObjectType' }
& { ' $fragmentRefs'?: { 'CoursePageFieldsCircleObjectTypeFragment': CoursePageFieldsCircleObjectTypeFragment } }
) | null } | { __typename?: 'LearningContentAttendanceCourseObjectType', title?: string | null, id?: string | null, slug?: string | null, content_type?: string | null, frontend_url?: string | null, circle?: (
@ -803,9 +806,9 @@ export type SendFeedbackMutationMutation = { __typename?: 'Mutation', send_feedb
export const CoursePageFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<CoursePageFieldsFragment, unknown>;
export const AttendanceCheckMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AttendanceCheckMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"attendanceCourseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"attendanceUserList"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AttendanceUserInputType"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update_course_session_attendance_course_users"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"attendanceCourseId"}}},{"kind":"Argument","name":{"kind":"Name","value":"attendance_user_list"},"value":{"kind":"Variable","name":{"kind":"Name","value":"attendanceUserList"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_session_attendance_course"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"attendance_user_list"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user_id"}},{"kind":"Field","name":{"kind":"Name","value":"first_name"}},{"kind":"Field","name":{"kind":"Name","value":"last_name"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]}}]} as unknown as DocumentNode<AttendanceCheckMutationMutation, AttendanceCheckMutationMutationVariables>;
export const UpsertAssignmentCompletionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertAssignmentCompletion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AssignmentCompletionStatus"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"initializeCompletion"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsert_assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_data_string"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_grade"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_points"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}}},{"kind":"Argument","name":{"kind":"Name","value":"initialize_completion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"initializeCompletion"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}}]}}]} as unknown as DocumentNode<UpsertAssignmentCompletionMutation, UpsertAssignmentCompletionMutationVariables>;
export const UpsertAssignmentCompletionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertAssignmentCompletion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AssignmentCompletionStatus"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"initializeCompletion"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsert_assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_data_string"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_points"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}}},{"kind":"Argument","name":{"kind":"Name","value":"initialize_completion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"initializeCompletion"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}}]}}]} as unknown as DocumentNode<UpsertAssignmentCompletionMutation, UpsertAssignmentCompletionMutationVariables>;
export const AttendanceCheckQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"attendanceCheckQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_session_attendance_course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"attendance_user_list"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user_id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]} as unknown as DocumentNode<AttendanceCheckQueryQuery, AttendanceCheckQueryQueryVariables>;
export const AssignmentCompletionQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"assignmentCompletionQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"max_points"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"effort_required"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_description"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_document_url"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_tasks"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"intro_text"}},{"kind":"Field","name":{"kind":"Name","value":"performance_objectives"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"tasks"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"translation_key"}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<AssignmentCompletionQueryQuery, AssignmentCompletionQueryQueryVariables>;
export const AssignmentCompletionQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"assignmentCompletionQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"needs_expert_evaluation"}},{"kind":"Field","name":{"kind":"Name","value":"max_points"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"effort_required"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_description"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_document_url"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_tasks"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"intro_text"}},{"kind":"Field","name":{"kind":"Name","value":"performance_objectives"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"tasks"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"translation_key"}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_max_points"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_passed"}},{"kind":"Field","name":{"kind":"Name","value":"edoniq_extended_time_flag"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<AssignmentCompletionQueryQuery, AssignmentCompletionQueryQueryVariables>;
export const CourseQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"category_name"}},{"kind":"Field","name":{"kind":"Name","value":"learning_path"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<CourseQueryQuery, CourseQueryQueryVariables>;
export const CompetenceCertificateQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"competenceCertificateQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_certificate_list"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificates"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"assignments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"max_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_content"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}},{"kind":"Field","name":{"kind":"Name","value":"circle"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<CompetenceCertificateQueryQuery, CompetenceCertificateQueryQueryVariables>;
export const CompetenceCertificateQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"competenceCertificateQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_certificate_list"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificates"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"assignments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"max_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_max_points"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_passed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_content"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}},{"kind":"Field","name":{"kind":"Name","value":"circle"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode<CompetenceCertificateQueryQuery, CompetenceCertificateQueryQueryVariables>;
export const SendFeedbackMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendFeedbackMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GenericScalar"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"send_feedback"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"submitted"},"value":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feedback_response"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"submitted"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"messages"}}]}}]}}]}}]} as unknown as DocumentNode<SendFeedbackMutationMutation, SendFeedbackMutationMutationVariables>;

View File

@ -226,6 +226,11 @@ type LearningContentAssignmentObjectType implements LearningContentInterface {
type AssignmentObjectType implements CoursePageInterface {
assignment_type: AssignmentAssignmentAssignmentTypeChoices!
"""
Muss der Auftrag durch eine Expertin oder einen Experten beurteilt werden?
"""
needs_expert_evaluation: Boolean!
competence_certificate: CompetenceCertificateObjectType
"""Erläuterung der Ausgangslage"""
@ -296,8 +301,10 @@ type AssignmentCompletionObjectType {
submitted_at: DateTime
evaluation_submitted_at: DateTime
evaluation_user: UserType
evaluation_grade: Float
evaluation_points: Float
evaluation_max_points: Float
evaluation_passed: Boolean
edoniq_extended_time_flag: Boolean!
assignment_user: UserType!
assignment: AssignmentObjectType!
completion_status: AssignmentAssignmentCompletionCompletionStatusChoices!
@ -548,7 +555,7 @@ type CompetenceCertificateListObjectType implements CoursePageInterface {
type Mutation {
send_feedback(course_session_id: ID!, data: GenericScalar, learning_content_page_id: ID!, submitted: Boolean = false): SendFeedbackMutation
update_course_session_attendance_course_users(attendance_user_list: [AttendanceUserInputType]!, id: ID!): AttendanceCourseUserMutation
upsert_assignment_completion(assignment_id: ID!, assignment_user_id: UUID, completion_data_string: String, completion_status: AssignmentCompletionStatus, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float, initialize_completion: Boolean, learning_content_page_id: ID): AssignmentCompletionMutation
upsert_assignment_completion(assignment_id: ID!, assignment_user_id: UUID, completion_data_string: String, completion_status: AssignmentCompletionStatus, course_session_id: ID!, evaluation_passed: Boolean, evaluation_points: Float, initialize_completion: Boolean, learning_content_page_id: ID): AssignmentCompletionMutation
}
type SendFeedbackMutation {

View File

@ -47,7 +47,6 @@ export const graphqlClient = new Client({
JSON.parse(args.completion_data_string || "{}")
),
completion_status: args.completion_status,
evaluation_grade: args.evaluation_grade,
evaluation_points: args.evaluation_points,
},
};

View File

@ -31,7 +31,6 @@ export const UPSERT_ASSIGNMENT_COMPLETION_MUTATION = graphql(`
$assignmentUserId: UUID
$completionStatus: AssignmentCompletionStatus!
$completionDataString: String!
$evaluationGrade: Float
$evaluationPoints: Float
$initializeCompletion: Boolean
) {
@ -42,7 +41,6 @@ export const UPSERT_ASSIGNMENT_COMPLETION_MUTATION = graphql(`
assignment_user_id: $assignmentUserId
completion_status: $completionStatus
completion_data_string: $completionDataString
evaluation_grade: $evaluationGrade
evaluation_points: $evaluationPoints
initialize_completion: $initializeCompletion
) {
@ -51,7 +49,6 @@ export const UPSERT_ASSIGNMENT_COMPLETION_MUTATION = graphql(`
completion_status
submitted_at
evaluation_submitted_at
evaluation_grade
evaluation_points
completion_data
}

View File

@ -32,6 +32,7 @@ export const ASSIGNMENT_COMPLETION_QUERY = graphql(`
) {
assignment(id: $assignmentId) {
assignment_type
needs_expert_evaluation
max_points
content_type
effort_required
@ -65,8 +66,10 @@ export const ASSIGNMENT_COMPLETION_QUERY = graphql(`
assignment_user {
id
}
evaluation_grade
evaluation_points
evaluation_max_points
evaluation_passed
edoniq_extended_time_flag
completion_data
}
}
@ -101,6 +104,8 @@ export const COMPETENCE_NAVI_CERTIFICATE_QUERY = graphql(`
completion_status
submitted_at
evaluation_points
evaluation_max_points
evaluation_passed
}
learning_content {
title

View File

@ -17,6 +17,7 @@
"Feedback anschauen": "Feedback anschauen",
"Feedback: Feedback zum Lehrgang": "Feedback: Feedback zum Lehrgang",
"Freigabetermin Bewertungen:": "Freigabetermin Bewertungen:",
"Hier überprüfst und bestätigst du die Anwesenheit deiner Teilnehmenden": [],
"MS Teams öffnen": "MS Teams öffnen",
"Nächste Termine": "Nächste Termine",
"Passwort": "Passwort",
@ -29,26 +30,44 @@
"Wissens - und Verständnisfragen": "Wissens - und Verständnisfragen",
"Zur Zeit sind keine Termine vorhanden": "Zur Zeit sind keine Termine vorhanden",
"a": {
"Alle aufklappen": "Alle aufklappen",
"AlleTermine": "a.AlleTermine",
"Begründung": "Begründung",
"Beurteilungsinstrument anzeigen": "Beurteilungsinstrument anzeigen",
"Beurteilungskriterium": "Beurteilungskriterium",
"Bewertung": "Bewertung",
"Bewertung fortsetzen": "a.Bewertung fortsetzen",
"Bewertung freigegeben": "Bewertung freigegeben",
"Datum": "a.Datum",
"Bewertung starten": "Bewertung starten",
"Circle": "Circle",
"Datum": "Datum",
"Details anzeigen": "Details anzeigen",
"Ergebnisse": "Ergebnisse",
"Ergebnisse abgegeben": "Ergebnisse abgegeben",
"Ergebnisse anschauen": "Ergebnisse anschauen",
"Geleitete Fallarbeit": "Geleitete Fallarbeit",
"Gesamtpunktzahl": "Gesamtpunktzahl",
"Handlungskompetenzen": "a.Handlungskompetenzen",
"Handlungskompetenzen": "Handlungskompetenzen",
"Höchstpunktzahl": "Höchstpunktzahl",
"Kein Circle verfügbar oder ausgewählt": [],
"KompetenzNavi": "KompetenzNavi",
"Kompetenznachweis": "Kompetenznachweis",
"Kompetenznachweise": "Kompetenznachweise",
"Mediathek": "a.Mediathek",
"Leistungsziele": "Leistungsziele",
"Mediathek": "Mediathek",
"Punkte": "Punkte",
"Selbsteinschätzung": "a.Selbsteinschätzung",
"Selbsteinschätzung": "Selbsteinschätzung",
"Selbsteinschätzung anschauen": "a.Selbsteinschätzung anschauen",
"Selbsteinschätzungen": "Selbsteinschätzungen",
"Standort": "a.Standort",
"Trainer": "a.Trainer",
"Standort": "Standort",
"Trainer": "Trainer",
"VorschauTeilnehmer": "Vorschau Teilnehmer",
"Zwischenstand": "Zwischenstand",
"competenceCertificateNoUserPoints": "a.competenceCertificateNoUserPoints",
"competenceCertificateNoUserPoints": "Der Punktestand wird zu einem späteren Zeitpunkt berechnet.",
"Übersicht": "Übersicht"
},
"assignment": {
"Du musst die Bewertung bis am x um y Uhr abschliessen und freigeben": "Du musst die Bewertung bis am {{x}} um {{y}} Uhr abschliessen und freigeben.",
"acceptConditionsDisclaimer": "Bedingungen akzeptieren und Ergebnisse abgeben",
"assessmentDocumentDisclaimer": "Diese geleitete Fallarbeit wird auf Grund des folgenden Beurteilungsinstrument bewertet:",
"assessmentTitle": "Bewertung",
@ -61,7 +80,9 @@
"dueDateSubmission": "Abgabetermin",
"edit": "Bearbeiten",
"effortTitle": "Zeitaufwand",
"evaluationInstrumentDescriptionText": "Die Gesamtpunktzahl und die daraus resultierende Note wird auf Grund des hinterlegeten Beurteilungsinstrument berechnet.",
"initialSituationTitle": "Ausgangslage",
"justificationRequiredText": "Hier muss zwingend eine Begründung erfasst werden.",
"lastChangesNotSaved": "Die letzte Änderung konnte nicht gespeichert werden.",
"performanceObjectivesTitle": "Leistungsziele",
"showAssessmentDocument": "Bewertungsinstrument anzeigen",
@ -70,12 +91,13 @@
"taskDefinition": "Bearbeite die Teilaufgaben und dokumentiere deine Ergebnisse.",
"taskDefinitionTitle": "Aufgabenstellung",
"von x Punkten": "von {{x}} Punkten",
"x hat die Ergebnisse am y um z Uhr abgegeben": "{{x}} hat die Ergebnisse am {{y}} um {{z}} Uhr abgegeben",
"x von y Arbeiten abgeschlossen": "{{x}} von {{y}} Arbeiten abgeschlossen",
"x von y Kompetenznachweis-Elementen abgeschlossen": "assignment.x von y Kompetenznachweis-Elementen abgeschlossen"
"x von y Kompetenznachweis-Elementen abgeschlossen": "{{x}} von {{y}} Kompetenznachweis-Elementen abgeschlossen"
},
"circlePage": {
"Dieser Inhalt gehört zu x": "circlePage.Dieser Inhalt gehört zu x",
"Im KompetenzNavi anschauen": "circlePage.Im KompetenzNavi anschauen",
"Dieser Inhalt gehört zu x": "Dieser Inhalt gehört zu «{{x}}»",
"Im KompetenzNavi anschauen": "Im KompetenzNavi anzeigen",
"circleContentBoxTitle": "Das lernst du in diesem Circle",
"contactExpertButton": "Trainer/-in kontaktieren",
"contactExpertDescription": "Tausche dich mit der Trainer/-in für den Circle {{circleName}} aus.",
@ -117,11 +139,14 @@
"title": "Cockpit",
"trainerFilesText": "Hier findest du die Trainerunterlagen (Lösungsblätter, Präsentationen etc.) für deinen Circle."
},
"competence": {
"listPageDescription": "competence.listPageDescription"
},
"competenceCertificate": {
"mainTitle": "competenceCertificate.mainTitle"
"mainTitle": "Kompetenznachweis"
},
"competenceCertificates": {
"mainTitle": "competenceCertificates.mainTitle"
"mainTitle": "Kompetenznachweise"
},
"competences": {
"assessAgain": "Selbsteinschätzung im Circle «{{x}}» anschauen",
@ -152,7 +177,8 @@
},
"edoniqTest": {
"qualifiesForExtendedTime": "Ich bestätige, dass ich Anrecht auf einen Test mit Nachteilsausgleich habe.",
"qualifiesForExtendedTimeTitle": "Lernende mit Nachteilsausgleich (bewilligungspflichtig)"
"qualifiesForExtendedTimeTitle": "Lernende mit Nachteilsausgleich (bewilligungspflichtig)",
"startTest": "Test starten"
},
"feedback": {
"answers": "Antworten",

View File

@ -1,4 +1,5 @@
{
"Alle": "Tout",
"Anwesenheit Präsenzkurse": "Présence aux cours",
"Anwesenheit bestätigen": "Confirmer la présence",
"Anwesenheit prüfen": "Vérifier la présence",
@ -7,29 +8,55 @@
"Ergebnisse anschauen": "Consulter les résultats",
"Feedback": "Feedback",
"Feedback anschauen": "Consulter le feedback",
"Hier überprüfst und bestätigst du die Anwesenheit deiner Teilnehmenden": [],
"MS Teams öffnen": "Ouvrir MS Teams ",
"Nächste Termine": "Prochaines dates",
"Passwort": "Mot de passe",
"Sehr unzufrieden": "très insatisfait",
"Sehr zufrieden": "très satisfait",
"Status anschauen": "Consulter le statut",
"TODO: Nächste Termine": "TODO: prochaines dates",
"Trainerunterlagen": "Documents du formateur / de la formatrice",
"Wissens - und Verständnisfragen": "Questions de connaissance et de compréhension ",
"Zur Zeit sind keine Termine vorhanden": "Aucune réunion nest prévue pour le moment",
"a": {
"Alle aufklappen": "Tout déplier",
"Begründung": "Justification",
"Beurteilungsinstrument anzeigen": "Afficher l'outil d'évaluation",
"Beurteilungskriterium": "Critère d'évaluation",
"Bewertung": "Évaluation",
"Bewertung fortsetzen": "Continuer l'évaluation",
"Bewertung freigegeben": "Évaluations validée/s",
"Bewertung starten": "Continuer l'évaluation",
"Circle": "Cercle",
"Datum": "Date",
"Details anzeigen": "Afficher les détails",
"Ergebnisse": "Résultats",
"Ergebnisse abgegeben": "Résultats remis",
"Ergebnisse anschauen": "Voir les résultats",
"Geleitete Fallarbeit": "Étude de cas dirigée",
"Gesamtpunktzahl": "Score total",
"Handlungskompetenzen": "Compétences opérationnelles",
"Höchstpunktzahl": "Score maximum",
"Kein Circle verfügbar oder ausgewählt": [],
"KompetenzNavi": "NaviCompétence",
"Kompetenznachweis": "Contrôle de compétences",
"Kompetenznachweise": "Contrôles de compétences",
"Kompetenznachweise": "Contrôles de compétences",
"Leistungsziele": "Objectif évaluateur",
"Mediathek": "Médiathèque",
"Punkte": "points",
"Selbsteinschätzung": "Auto-évaluation",
"Selbsteinschätzung anschauen": "Voir l'auto-évaluation",
"Selbsteinschätzungen": "Auto-évaluations",
"Standort": "Emplacement",
"Trainer": "Formateur / Formatrice",
"VorschauTeilnehmer": "Aperçu participant",
"Zwischenstand": "Point intermédiaire",
"competenceCertificateNoUserPoints": "Le score sera calculé à un moment ultérieur.",
"Übersicht": "Aperçu"
},
"assignment": {
"Du musst die Bewertung bis am x um y Uhr abschliessen und freigeben": "Vous devez terminer et valider l'évaluation avant le {{x}} à {{y}} heures.",
"acceptConditionsDisclaimer": "Accepter les conditions et remettre les résultats",
"assessmentDocumentDisclaimer": "Cette étude de cas dirigée est évaluée par loutil suivant :",
"assessmentTitle": "Évaluation",
@ -42,7 +69,9 @@
"dueDateSubmission": "Date de remise",
"edit": "Traiter",
"effortTitle": "Temps nécessaire",
"evaluationInstrumentDescriptionText": "Le score total et la note en résultant sont calculés sur la base de l'outil d'évaluation enregistré.",
"initialSituationTitle": "Situation initiale",
"justificationRequiredText": "Ici, une justification doit impérativement être saisie.",
"lastChangesNotSaved": "La dernière modification na pas pu être enregistrée.",
"performanceObjectivesTitle": "Objectifs",
"showAssessmentDocument": "Afficher loutil dévaluation",
@ -51,9 +80,13 @@
"taskDefinition": "Résous les exercices et documente tes résultats.",
"taskDefinitionTitle": "Énoncé du problème",
"von x Punkten": "sur {{x}} points",
"x von y Arbeiten abgeschlossen": "{{x}} sur {{y}} épreuves terminées"
"x hat die Ergebnisse am y um z Uhr abgegeben": "{{x}} a soumis les résultats le {{y}} à {{z}} heures",
"x von y Arbeiten abgeschlossen": "{{x}} sur {{y}} épreuves terminées",
"x von y Kompetenznachweis-Elementen abgeschlossen": "{{x}} sur {{y}} éléments de contrôle de compétences terminés"
},
"circlePage": {
"Dieser Inhalt gehört zu x": "Ce contenu appartient à «{{x}}»",
"Im KompetenzNavi anschauen": "Afficher dans le \"KompetenzNavi\"",
"circleContentBoxTitle": "Ce que tu vas apprendre dans ce cercle",
"contactExpertButton": "Contacter le formateur / la formatrice",
"contactExpertDescription": "Échanger avec le formateur / la formatrice si tu as des questions sur le cercle {{circleName}}.",
@ -95,6 +128,12 @@
"title": "Cockpit",
"trainerFilesText": "Tu trouves ici les documents de formation (feuilles de solution, présentations, etc.) pour ton cercle."
},
"competenceCertificate": {
"mainTitle": "Contrôle de compétences"
},
"competenceCertificates": {
"mainTitle": "Contrôles de compétences"
},
"competences": {
"assessAgain": "Voir l'auto-évaluation dans le cercle «{{x}}»",
"assessment": "Évaluations",
@ -124,7 +163,8 @@
},
"edoniqTest": {
"qualifiesForExtendedTime": "Je confirme que j'ai droit à un test avec compensation des inégalités.",
"qualifiesForExtendedTimeTitle": "Apprenti-e-s avec compensation des inégalités (soumis à autorisation)"
"qualifiesForExtendedTimeTitle": "Apprenti-e-s avec compensation des inégalités (soumis à autorisation)",
"startTest": "Démarrer l'essai"
},
"feedback": {
"answers": "Réponses",
@ -315,8 +355,10 @@
"welcome": "Tout s'est bien passé, tu es maintenant inscrit/e à l'environnement d'apprentissage numérique de l'Association pour la formation professionnelle en assurance (AFA) !"
}
},
"unzufrieden": "insatisfait",
"x von y Bewertungen freigegeben": "{{x}} de {{y}} évaluations validée/s",
"x von y Ergebnisse abgegeben": "{{x}} de {{y}} résultats remis",
"x von y Feedbacks abgegeben": "{{x}} de {{y}} feedbacks remis",
"x von y abgeschlossen": "{{x}} de {{y}} achevée/s"
"x von y abgeschlossen": "{{x}} de {{y}} achevée/s",
"zufrieden": "satisfait"
}

View File

@ -1,4 +1,5 @@
{
"Alle": "Tutto",
"Anwesenheit Präsenzkurse": "Presenza ai corsi",
"Anwesenheit bestätigen": "Confermare la presenza",
"Anwesenheit prüfen": "Verificare la presenza",
@ -7,29 +8,55 @@
"Ergebnisse anschauen": "Vedere i risultati",
"Feedback": "Feedback",
"Feedback anschauen": "Vedere il feedback",
"Hier überprüfst und bestätigst du die Anwesenheit deiner Teilnehmenden": [],
"MS Teams öffnen": "Aprire MS Teams",
"Nächste Termine": "Prossime date",
"Passwort": "Password",
"Sehr unzufrieden": "molto insoddisfatto",
"Sehr zufrieden": "molto soddisfatto",
"Status anschauen": "Vedere lo stato",
"TODO: Nächste Termine": "TODO: prossime date",
"Trainerunterlagen": "Documenti del/della trainer",
"Wissens - und Verständnisfragen": "Domande di conoscenza e di comprensione",
"Zur Zeit sind keine Termine vorhanden": "Al momento non ci sono scadenze",
"a": {
"Alle aufklappen": "Apri tutto",
"Begründung": "Motivazione",
"Beurteilungsinstrument anzeigen": "Mostra lo strumento di valutazione",
"Beurteilungskriterium": "Criterio di valutazione",
"Bewertung": "Valutazione",
"Bewertung fortsetzen": "Continua la valutazione",
"Bewertung freigegeben": "Valutazioni approvate/a",
"Bewertung starten": "Continua la valutazione",
"Circle": "Cerchio",
"Datum": "Data",
"Details anzeigen": "Mostrare i dettagli",
"Ergebnisse": "Risultati",
"Ergebnisse abgegeben": "Risultati consegnati",
"Ergebnisse anschauen": "Guarda i risultati",
"Geleitete Fallarbeit": "Caso di studio guidato",
"Gesamtpunktzahl": "Punteggio totale",
"Handlungskompetenzen": "Competenze operative",
"Höchstpunktzahl": "Punteggio massimo",
"Kein Circle verfügbar oder ausgewählt": [],
"KompetenzNavi": "NaviCompetenze",
"Kompetenznachweis": "Controllo delle competenze",
"Kompetenznachweise": "Controlli delle competenze",
"Kompetenznachweise": "Controlli delle competenze",
"Leistungsziele": "Obiettivo di valutazione",
"Mediathek": "Mediateca",
"Punkte": "punti",
"Selbsteinschätzung": "Auto-valutazione",
"Selbsteinschätzung anschauen": "Guarda l'auto-valutazione",
"Selbsteinschätzungen": "Auto-valutaziones",
"Standort": "Posizione",
"Trainer": "Trainer",
"VorschauTeilnehmer": "Anteprima partecipante",
"Zwischenstand": "Punto intermedio",
"competenceCertificateNoUserPoints": "Il punteggio verrà calcolato in un momento successivo.",
"Übersicht": "Panoramica"
},
"assignment": {
"Du musst die Bewertung bis am x um y Uhr abschliessen und freigeben": " Devi completare e rilasciare la valutazione entro il {{x}} alle ore {{y}}.",
"acceptConditionsDisclaimer": "Accettare le condizioni e consegnare i risultati",
"assessmentDocumentDisclaimer": "Questa analisi guidata del caso viene valutata sulla base del seguente strumento di valutazione:",
"assessmentTitle": "Valutazione",
@ -42,7 +69,9 @@
"dueDateSubmission": "Termine di consegna",
"edit": "Modificare",
"effortTitle": "Tempo richiesto",
"evaluationInstrumentDescriptionText": "Il punteggio totale e il voto risultante vengono calcolati sulla base dello strumento di valutazione archiviato.",
"initialSituationTitle": "Situazione di partenza",
"justificationRequiredText": "Qui è assolutamente necessario inserire una motivazione.",
"lastChangesNotSaved": "Non è stato possibile salvare lultima modifica.",
"performanceObjectivesTitle": "Obiettivi di valutazione",
"showAssessmentDocument": "Mostrare lo strumento di valutazione",
@ -51,9 +80,13 @@
"taskDefinition": "Svolgi le attività parziali e documenta i tuoi risultati.",
"taskDefinitionTitle": "Compito",
"von x Punkten": "di {{x}} punti",
"x von y Arbeiten abgeschlossen": "{{x}} di {{y}} compiti completati"
"x hat die Ergebnisse am y um z Uhr abgegeben": "{{x}} ha consegnato i risultati il {{y}} alle ore {{z}}",
"x von y Arbeiten abgeschlossen": "{{x}} di {{y}} compiti completati",
"x von y Kompetenznachweis-Elementen abgeschlossen": "{{x}} su {{y}} elementi del controllo delle competenze completati"
},
"circlePage": {
"Dieser Inhalt gehört zu x": "Questo contenuto appartiene a «{{x}}»",
"Im KompetenzNavi anschauen": "Mostra nel \"KompetenzNavi\"",
"circleContentBoxTitle": "Cosa apprenderai in questo Circle",
"contactExpertButton": "Contattare il/la trainer",
"contactExpertDescription": "Confrontati con il/la trainer per il Circle {{circleName}}.",
@ -95,6 +128,12 @@
"title": "Cockpit",
"trainerFilesText": "Qui trovi i documenti del/della trainer (soluzioni, presentazioni ecc.) per il tuo Circle."
},
"competenceCertificate": {
"mainTitle": "Controllo delle competenze"
},
"competenceCertificates": {
"mainTitle": "Controlli delle competenze"
},
"competences": {
"assessAgain": "Guarda l'autovalutazione nel cerchio «{{x}}»",
"assessment": "Valutazioni",
@ -124,7 +163,8 @@
},
"edoniqTest": {
"qualifiesForExtendedTime": "Confermo di avere diritto a un test con compensazione delli ineguaglianze",
"qualifiesForExtendedTimeTitle": "Impiegati con compensazione delli ineguaglianze (soggetto ad approvazione)"
"qualifiesForExtendedTimeTitle": "Impiegati con compensazione delli ineguaglianze (soggetto ad approvazione)",
"startTest": "Inizia la prova"
},
"feedback": {
"answers": "Risposte",
@ -315,8 +355,10 @@
"welcome": "Tutto ha funzionato, ora sei registrata/o nell'ambiente di apprendimento digitale dell'Associazione per la formazione professionale nell'assicurazione (AFA)!"
}
},
"unzufrieden": "insoddisfatto",
"x von y Bewertungen freigegeben": "{{x}} di {{y}} valutazioni approvate/a",
"x von y Ergebnisse abgegeben": "{{x}} di {{y}} risultati consegnati/o",
"x von y Feedbacks abgegeben": "{{x}} di {{y}} feedback consegnati/o",
"x von y abgeschlossen": "{{x}} di {{y}} completate/a"
"x von y abgeschlossen": "{{x}} di {{y}} completate/a",
"zufrieden": "soddisfatto"
}

View File

@ -14,6 +14,7 @@ import log from "loglevel";
import { computed, onMounted, reactive } from "vue";
import { useRouter } from "vue-router";
import { getPreviousRoute } from "@/router/history";
import { getAssignmentTypeTitle } from "@/utils/utils";
const props = defineProps<{
courseSlug: string;
@ -87,7 +88,10 @@ const assignment = computed(
<div class="flex items-center text-gray-900">
<it-icon-assignment class="h-6 w-6"></it-icon-assignment>
<div class="ml-2">
{{ $t("a.Geleitete Fallarbeit") }}: {{ assignment?.title }}
<span v-if="assignment">
{{ getAssignmentTypeTitle(assignment.assignment_type) }}:
</span>
{{ assignment?.title }}
</div>
</div>
<button
@ -104,7 +108,10 @@ const assignment = computed(
class="relative"
>
<div class="md:h-content flex flex-col md:flex-row">
<div class="bg-white md:h-full md:w-1/2 md:overflow-y-auto">
<div
class="bg-white md:h-full md:overflow-y-auto"
:class="{ 'md:w-1/2': assignment.needs_expert_evaluation }"
>
<!-- Left part content goes here -->
<div class="p-10" data-cy="student-submission">
<h3>{{ $t("a.Ergebnisse") }}</h3>
@ -127,6 +134,7 @@ const assignment = computed(
</div>
</div>
<div
v-if="assignment.needs_expert_evaluation"
class="order-first bg-gray-200 md:order-last md:w-1/2 md:overflow-y-auto"
>
<EvaluationContainer

View File

@ -4,7 +4,6 @@ import { useCurrentCourseSession } from "@/composables";
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
import {
maxAssignmentPoints,
pointsToGrade,
userAssignmentPoints,
} from "@/services/assignmentService";
import type {
@ -47,7 +46,6 @@ async function submitEvaluation() {
assignmentUserId: props.assignmentUser.user_id.toString(),
completionStatus: "EVALUATION_SUBMITTED",
completionDataString: JSON.stringify({}),
evaluationGrade: grade.value ?? 1,
evaluationPoints: userPoints.value,
// next line used for urql
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -78,12 +76,6 @@ const maxPoints = computed(() => maxAssignmentPoints(props.assignment));
const userPoints = computed(() =>
userAssignmentPoints(props.assignment, props.assignmentCompletion)
);
const grade = computed(() => {
if (props.assignmentCompletion.completion_status === "EVALUATION_SUBMITTED") {
return props.assignmentCompletion.evaluation_grade;
}
return pointsToGrade(userPoints.value, maxPoints.value);
});
const evaluationUser = computed(() => {
if (props.assignmentCompletion.evaluation_user) {
@ -102,18 +94,31 @@ const evaluationUser = computed(() => {
<h3 v-if="evaluationUser && props.showEvaluationUser" class="mb-6">
Bewertung von {{ evaluationUser.first_name }} {{ evaluationUser.last_name }}
</h3>
<h3 v-else class="mb-6">{{ $t("a.Bewertung Freigabe") }}</h3>
<h3 v-else class="mb-6" data-cy="sub-title">{{ $t("a.Bewertung Freigabe") }}</h3>
<section class="mb-6 border p-6">
<section class="mb-6 border p-6" data-cy="result-section">
<section class="flex items-center">
<div class="heading-1 py-4">
<div class="heading-1 py-4" data-cy="user-points">
{{ userPoints }}
</div>
<div class="pl-2">
<div class="pl-2" data-cy="total-points">
{{ $t("assignment.von x Punkten", { x: maxPoints }) }}
({{
(
((props.assignmentCompletion?.evaluation_points ?? 0) /
(props.assignmentCompletion?.evaluation_max_points ?? 1)) *
100
).toFixed(0)
}}%)
</div>
</section>
<div v-if="!props.assignmentCompletion.evaluation_passed">
<span class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5">
{{ $t("a.Nicht Bestanden") }}
</span>
</div>
<p class="my-4">
{{ $t("assignment.evaluationInstrumentDescriptionText") }}
</p>
@ -140,7 +145,11 @@ const evaluationUser = computed(() => {
Uhr
</div>
<div v-else>
<button class="btn-primary text-large" @click="submitEvaluation()">
<button
class="btn-primary text-large"
data-cy="submit-evaluation"
@click="submitEvaluation()"
>
{{ $t("a.Bewertung freigeben") }}
</button>
</div>

View File

@ -92,7 +92,7 @@ function showFileInformation() {
<p>{{ formData.file.name }}</p>
</div>
<div v-if="formErrors.file">
<p class="text-red-700">{{ $t("circlePage.documents.selectFile") }}</p>
<p class="text-red-error-500">{{ $t("circlePage.documents.selectFile") }}</p>
</div>
</div>
<p class="mb-8">{{ $t("circlePage.documents.maxFileSize") }}</p>
@ -103,7 +103,7 @@ function showFileInformation() {
<input id="name" v-model="formData.name" type="text" class="mb-2 w-1/2" />
<p>{{ $t("circlePage.documents.modalNameInformation") }}</p>
<div v-if="formErrors.name">
<p class="text-red-700">{{ $t("circlePage.documents.chooseName") }}</p>
<p class="text-error-red-500">{{ $t("circlePage.documents.chooseName") }}</p>
</div>
</div>
<div class="mb-8">
@ -116,13 +116,13 @@ function showFileInformation() {
:items="props.learningSequences"
></ItDropdownSelect>
<div v-if="formErrors.learningSequence">
<p class="text-red-700">
<p class="text-error-red-500">
{{ $t("circlePage.documents.chooseLearningSequence") }}
</p>
</div>
</div>
<div v-if="showUploadErrorMessage">
<p class="mb-4 text-red-700">
<p class="mb-4 text-error-red-500">
{{ $t("circlePage.documents.uploadErrorMessage") }}
</p>
</div>

View File

@ -35,6 +35,7 @@ const getIconName = () => {
v-if="assignment.learning_content"
:href="assignment.frontend_url"
class="link"
data-cy="open-learning-content"
>
{{
$t("general.im circle x anschauen", {
@ -84,6 +85,19 @@ const getIconName = () => {
</div>
<div>
{{ $t("assignment.von x Punkten", { x: assignment.max_points }) }}
({{
(
((assignment.completion?.evaluation_points ?? 0) /
(assignment.completion?.evaluation_max_points ?? 1)) *
100
).toFixed(0)
}}%)
</div>
<div
v-if="assignment.completion && !assignment.completion.evaluation_passed"
class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5"
>
{{ $t("a.Nicht Bestanden") }}
</div>
</div>
</div>

View File

@ -2,17 +2,20 @@
import { useTranslation } from "i18next-vue";
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import LearningContentSimpleLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentSimpleLayout.vue";
import type {
Assignment,
AssignmentCompletion,
LearningContentEdoniqTest,
} from "@/types";
import type { AssignmentCompletion, LearningContentEdoniqTest } from "@/types";
import { computed, ref } from "vue";
import * as log from "loglevel";
import { itPost } from "@/fetchHelpers";
import { useQuery } from "@urql/vue";
import { ASSIGNMENT_COMPLETION_QUERY } from "@/graphql/queries";
import { useCurrentCourseSession } from "@/composables";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import dayjs from "dayjs";
import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue";
import {
formatDueDate,
getDateString,
} from "../../../../components/dueDates/dueDatesUtils";
const { t } = useTranslation();
@ -21,6 +24,11 @@ const props = defineProps<{
}>();
const courseSession = useCurrentCourseSession();
const courseSessionsStore = useCourseSessionsStore();
const courseSessionEdoniqTest = computed(() => {
return courseSessionsStore.findCourseSessionEdoniqTest(props.content.id);
});
const queryResult = useQuery({
query: ASSIGNMENT_COMPLETION_QUERY,
@ -31,23 +39,32 @@ const queryResult = useQuery({
},
});
const assignment = computed(
() => queryResult.data.value?.assignment as Assignment | undefined
);
const assignmentCompletion = computed(
() =>
queryResult.data.value?.assignment_completion as AssignmentCompletion | undefined
);
const completionStatus = computed(() => {
return assignmentCompletion.value?.completion_status ?? "IN_PROGRESS";
return assignmentCompletion.value?.completion_status ?? "";
});
const termsAccepted = ref(false);
const extendedTimeTest = ref(false);
const deadlineInPast = computed(() => {
// with 16 minutes buffer
return dayjs(courseSessionEdoniqTest.value?.deadline_start)
.add(16, "minute")
.isBefore(dayjs());
});
async function startTest() {
log.info("start test", props.content);
if (assignmentCompletion.value?.edoniq_extended_time_flag) {
extendedTimeTest.value = true;
}
const response = await itPost("/api/core/edoniq-test/redirect/", {
learning_content_id: props.content.id,
extended_time_test: extendedTimeTest.value,
@ -62,24 +79,46 @@ async function startTest() {
:title="props.content.title"
:learning-content="props.content"
>
<!-- eslint-disable vue/no-v-html -->
<div v-if="queryResult.data" class="container-medium">
<div v-if="completionStatus !== 'IN_PROGRESS'">
<div v-if="completionStatus === 'EVALUATION_SUBMITTED'">
{{ $t("a.Bewertung") }}:
{{ assignmentCompletion?.evaluation_points }}
{{ $t("assignment.von x Punkten", { x: assignment?.max_points }) }}
</div>
<div v-else>Ergebnisse abgeben, Bewertung ausstehend</div>
</div>
<div v-else>
<p
v-if="props.content.description"
class="default-wagtail-rich-text text-large my-4"
v-html="props.content.description"
></p>
<div class="container-medium">
<section class="py-4">
<h3>{{ $t("a.Aufgabe") }}</h3>
<p class="mt-2 text-lg">{{ $t("edoniqTest.testDescription") }}</p>
</section>
<div class="my-8">
<section v-if="courseSessionEdoniqTest" class="py-4">
<h3>{{ $t("a.Abgabetermin") }}</h3>
<p class="mt-2 text-lg">
{{
$t("edoniqTest.submitDateDescription", {
x: formatDueDate(courseSessionEdoniqTest.deadline_start),
})
}}
</p>
</section>
<section class="py-4">
<h3>{{ $t("a.Kompetenznachweis") }}</h3>
<div class="mt-2 text-lg">
{{
$t("circlePage.Dieser Inhalt gehört zu x", {
x: content.competence_certificate?.title,
})
}}.
</div>
<div class="mt-2 text-lg">
<router-link
:to="content.competence_certificate?.frontend_url ?? ''"
class="link"
data-cy="show-results"
>
{{ $t("circlePage.Im KompetenzNavi anschauen") }}
</router-link>
</div>
</section>
<section v-if="!completionStatus">
<div class="mt-8 border p-8">
<h3 class="mb-4">{{ $t("edoniqTest.checkboxTitle") }}</h3>
<ItCheckbox
v-if="props.content.checkbox_text"
:checkbox-item="{
@ -90,7 +129,7 @@ async function startTest() {
@toggle="termsAccepted = !termsAccepted"
/>
</div>
<div class="my-8 border-t pt-8">
<div class="my-8 border-b border-t py-8">
<h3 class="mb-4">{{ $t("edoniqTest.qualifiesForExtendedTimeTitle") }}</h3>
<ItCheckbox
v-if="props.content.has_extended_time_test"
@ -103,17 +142,66 @@ async function startTest() {
/>
</div>
<div class="my-8">
<div>
<button
:disabled="!termsAccepted"
:disabled="!termsAccepted || deadlineInPast"
class="btn-primary inline-flex items-center"
@click="startTest()"
>
{{ $t("edoniqTest.startTest") }}
<it-icon-external-link class="it-icon ml-2 h-5 w-5"></it-icon-external-link>
</button>
<div class="mt-4">
<div v-if="deadlineInPast" class="text-error-red-500">
{{ $t("edoniqTest.deadlineInPast") }}
</div>
<div v-else>
{{ $t("a.Abgabetermin") }}:
{{ getDateString(dayjs(courseSessionEdoniqTest?.deadline_start)) }}
</div>
</div>
</div>
</div>
</section>
<section v-else>
<div v-if="assignmentCompletion" class="mt-8 border p-8" data-cy="test-result">
<ItSuccessAlert :text="$t('edoniqTest.testSubmitted')" />
<div class="my-4">
{{ $t("a.Resultat") }}:
<span class="font-bold">
{{ assignmentCompletion.evaluation_points }}
</span>
{{
$t("assignment.von x Punkten", {
x: assignmentCompletion.evaluation_max_points,
})
}},
{{
(
((assignmentCompletion.evaluation_points ?? 0) /
(assignmentCompletion.evaluation_max_points ?? 1)) *
100
).toFixed(0)
}}%,
<span v-if="assignmentCompletion.evaluation_passed">
({{ $t("a.Bestanden") }})
</span>
<span v-else class="text-error-red-500">
({{ $t("a.Nicht Bestanden") }})
</span>
</div>
<div class="mt-4">
<button class="btn-primary inline-flex items-center" @click="startTest()">
{{ $t("edoniqTest.viewResults") }}
<it-icon-external-link
class="it-icon ml-2 h-5 w-5"
></it-icon-external-link>
</button>
</div>
</div>
</section>
</div>
</LearningContentSimpleLayout>
</template>

View File

@ -16,7 +16,6 @@ import pick from "lodash/pick";
export interface GradedUser {
user: CourseSessionUser;
grade: number;
points: number;
}
@ -64,7 +63,6 @@ export async function loadAssignmentCompletionStatusData(
if (userAssignmentStatus?.completion_status === "EVALUATION_SUBMITTED") {
gradedUsers.push({
user: csu,
grade: userAssignmentStatus.evaluation_grade ?? 0,
points: userAssignmentStatus.evaluation_points ?? 0,
});
}

View File

@ -5,6 +5,7 @@ import type {
CourseSession,
CourseSessionAssignment,
CourseSessionAttendanceCourse,
CourseSessionEdoniqTest,
CourseSessionUser,
DueDate,
ExpertSessionUser,
@ -282,6 +283,16 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
}
}
function findCourseSessionEdoniqTest(
contentId?: number
): CourseSessionEdoniqTest | undefined {
if (contentId && currentCourseSession.value) {
return currentCourseSession.value.edoniq_tests.find(
(a) => a.learning_content_id === contentId
);
}
}
return {
uniqueCourseSessionsByCourse,
allCurrentCourseSessions,
@ -299,6 +310,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
removeDocument,
findAttendanceCourse,
findCourseSessionAssignment,
findCourseSessionEdoniqTest,
allDueDates,
// use `useCurrentCourseSession` whenever possible

View File

@ -371,6 +371,7 @@ export type AssignmentType =
export interface Assignment extends BaseCourseWagtailPage {
readonly content_type: "assignment.Assignment";
readonly assignment_type: AssignmentType;
readonly needs_expert_evaluation: boolean;
readonly intro_text: string;
readonly effort_required: string;
readonly performance_objectives: AssignmentPerformanceObjective[];
@ -423,6 +424,8 @@ export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
completion_status: AssignmentCompletionStatus;
evaluation_submitted_at: string | null;
evaluation_points: number | null;
evaluation_max_points: number | null;
evaluation_passed: boolean | null;
} | null;
}
@ -485,6 +488,14 @@ export interface CourseSessionAssignment {
evaluation_deadline_start: string;
}
export interface CourseSessionEdoniqTest {
id: number;
course_session_id: number;
learning_content_id: number;
deadline_id: number;
deadline_start: string;
}
export interface CourseSession {
id: number;
created_at: string;
@ -500,6 +511,7 @@ export interface CourseSession {
media_library_url: string;
attendance_courses: CourseSessionAttendanceCourse[];
assignments: CourseSessionAssignment[];
edoniq_tests: CourseSessionEdoniqTest[];
documents: CircleDocument[];
users: CourseSessionUser[];
due_dates: DueDate[];
@ -595,8 +607,10 @@ export interface AssignmentCompletion {
completion_status: AssignmentCompletionStatus;
evaluation_user: string | null;
completion_data: AssignmentCompletionData;
evaluation_grade: number | null;
edoniq_extended_time_flag: boolean;
evaluation_points: number | null;
evaluation_max_points: number | null;
evaluation_passed: boolean | null;
}
export type UpsertUserAssignmentCompletion = {
@ -608,15 +622,15 @@ export type UpsertUserAssignmentCompletion = {
export type EvaluationCompletionData = UpsertUserAssignmentCompletion & {
assignment_user_id: string;
evaluation_grade?: number;
evaluation_points?: number;
evaluation_max_points?: number;
evaluation_passed?: boolean;
};
export interface UserAssignmentCompletionStatus {
id: string;
assignment_user_id: string;
completion_status: AssignmentCompletionStatus;
evaluation_grade: number | null;
evaluation_points: number | null;
learning_content_page_id: number;
}

View File

@ -1,5 +1,5 @@
import type { LearningContent } from "@/types";
import { assertUnreachable } from "@/utils/utils";
import { assertUnreachable, getAssignmentTypeTitle } from "@/utils/utils";
import { useTranslation } from "i18next-vue";
export interface LearningContentIdentifier {
@ -13,18 +13,8 @@ export function learningContentTypeData(
const { t } = useTranslation();
switch (lc.content_type) {
case "learnpath.LearningContentAssignment": {
let title = "unknown";
if (lc.assignment_type === "CASEWORK") {
title = t("learningContentTypes.casework");
} else if (lc.assignment_type === "PREP_ASSIGNMENT") {
title = t("learningContentTypes.prepAssignment");
} else if (lc.assignment_type === "REFLECTION") {
title = t("learningContentTypes.reflection");
} else if (lc.assignment_type === "CONDITION_ACCEPTANCE") {
title = t("learningContentTypes.task");
}
return {
title: title,
title: getAssignmentTypeTitle(lc.assignment_type),
icon: "it-icon-lc-assignment",
};
}

View File

@ -1,4 +1,5 @@
import type { CourseSession } from "@/types";
import type { AssignmentType, CourseSession } from "@/types";
import { useTranslation } from "i18next-vue";
export function assertUnreachable(msg: string): never {
throw new Error("Didn't expect to get here, " + msg);
@ -11,3 +12,22 @@ export function getCompetenceBaseUrl(courseSession: CourseSession): string {
""
);
}
export function getAssignmentTypeTitle(assignmentType: AssignmentType): string {
const { t } = useTranslation();
switch (assignmentType) {
case "CASEWORK":
return t("learningContentTypes.casework");
case "PREP_ASSIGNMENT":
return t("learningContentTypes.prepAssignment");
case "REFLECTION":
return t("learningContentTypes.reflection");
case "CONDITION_ACCEPTANCE":
return t("learningContentTypes.task");
case "EDONIQ_TEST":
return t("learningContentTypes.edoniqTest");
default:
return "unknown";
}
}

View File

@ -4,5 +4,8 @@
# Run every 6 hours
0 */6 * * * /usr/local/bin/python /app/manage.py simple_dummy_job
# Run every hour
0 */1 * * * /usr/local/bin/python /app/manage.py edoniq_import_results
# every day at 19:30
30 19 * * * /usr/local/bin/python /app/manage.py send_attendance_course_reminders

View File

@ -1,4 +1,4 @@
import {login} from "./helpers";
import { login } from "./helpers";
// constants
const COURSE_SELECT = "[data-cy=appointments-course-select]";
@ -19,7 +19,7 @@ describe("appointments.cy.js", () => {
cy.get(SESSION_SELECT).should("contain", "Bern");
cy.get(CIRCLE_SELECT).should("contain", "Alle");
cy.get(".cy-single-due-date").should("have.length", 4);
cy.get(".cy-single-due-date").should("have.length", 5);
});
it("can filter by circle", () => {

View File

@ -1,3 +1,4 @@
import { TEST_STUDENT1_USER_ID } from "../../consts";
import { login } from "../helpers";
describe("assignmentStudent.cy.js", () => {
@ -109,4 +110,134 @@ describe("assignmentStudent.cy.js", () => {
);
});
});
it("can make complete assignment", () => {
cy.learningContentMultiLayoutNextStep();
cy.testLearningContentTitle(
"Teilaufgabe 1: Beispiel einer Versicherungspolice finden"
);
// Click confirmation
cy.get('[data-cy="it-checkbox-confirmation-1"]').click();
cy.learningContentMultiLayoutNextStep();
// step 2
cy.testLearningContentTitle(
"Teilaufgabe 2: Kundensituation und Ausgangslage"
);
cy.get('[data-cy="it-textarea-user-text-input-1"]')
.clear()
.type("Hallo Teilaufgabe 2");
// wait because of input debounce
cy.wait(550);
cy.learningContentMultiLayoutNextStep();
// check that results are stored on server
// load AssignmentCompletion from DB and check
cy.loadAssignmentCompletion(
"assignment_user_id",
TEST_STUDENT1_USER_ID
).then((ac) => {
expect(ac.completion_status).to.equal("IN_PROGRESS");
expect(JSON.stringify(ac.completion_data)).to.include(
"Hallo Teilaufgabe 2"
);
});
// step 3
cy.testLearningContentTitle("Teilaufgabe 3: Aktuelle Versicherung");
cy.get('[data-cy="it-textarea-user-text-input-1"]')
.clear()
.type("Hallo Teilaufgabe 3");
// wait because of input debounce
cy.wait(550);
cy.learningContentMultiLayoutNextStep();
// step 4
cy.testLearningContentTitle("Teilaufgabe 4: Deine Empfehlungen");
cy.get('[data-cy="it-textarea-user-text-input-1"]')
.clear()
.type("Hallo Teilaufgabe 4.1");
cy.wait(550);
cy.get('[data-cy="it-textarea-user-text-input-2"]')
.clear()
.type("Hallo Teilaufgabe 4.2");
cy.wait(550);
cy.get('[data-cy="it-textarea-user-text-input-3"]')
.clear()
.type("Hallo Teilaufgabe 4.3");
// wait because of input debounce
cy.wait(550);
cy.learningContentMultiLayoutNextStep();
// step 5
cy.testLearningContentTitle("Teilaufgabe 5: Reflexion");
cy.get('[data-cy="it-textarea-user-text-input-1"]')
.clear()
.type("Hallo Teilaufgabe 5.1");
cy.wait(550);
cy.get('[data-cy="it-textarea-user-text-input-2"]')
.clear()
.type("Hallo Teilaufgabe 5.2");
cy.wait(550);
cy.get('[data-cy="it-textarea-user-text-input-3"]')
.clear()
.type("Hallo Teilaufgabe 5.3");
// wait because of input debounce
cy.wait(550);
cy.learningContentMultiLayoutNextStep();
// step 6
cy.testLearningContentTitle("Teilaufgabe 6: Learnings");
cy.get('[data-cy="it-textarea-user-text-input-1"]')
.clear()
.type("Hallo Teilaufgabe 6.1");
cy.wait(550);
cy.get('[data-cy="it-textarea-user-text-input-2"]')
.clear()
.type("Hallo Teilaufgabe 6.2");
// wait because of input debounce
cy.wait(550);
cy.learningContentMultiLayoutNextStep();
cy.get('[data-cy="confirm-submit-results"]').click();
cy.get('[data-cy="confirm-submit-person"]').click();
cy.get('[data-cy="submit-assignment"]').click();
cy.get('[data-cy="success-text"]').should("exist");
// app goes back to circle view -> check if assignment is marked as completed
cy.url().should((url) => {
expect(url).to.match(/\/fahrzeug#lu-transfer?$/);
});
cy.reload();
cy.get(
'[data-cy="test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice-checkbox"]'
).should("have.class", "cy-checked");
// reopening page should get directly to last step
cy.visit(
"/course/test-lehrgang/learn/fahrzeug/überprüfen-einer-motorfahrzeug-versicherungspolice"
);
cy.url().should("include", "step=7");
// load AssignmentCompletion from DB and check
cy.loadAssignmentCompletion(
"assignment_user_id",
TEST_STUDENT1_USER_ID
).then((ac) => {
expect(ac.completion_status).to.equal("SUBMITTED");
expect(ac.evaluation_max_points).to.equal(24);
const completionString = JSON.stringify(ac.completion_data);
console.log(completionString);
expect(completionString).to.include("Hallo Teilaufgabe 2");
expect(completionString).to.include("Hallo Teilaufgabe 3");
expect(completionString).to.include("Hallo Teilaufgabe 4.1");
expect(completionString).to.include("Hallo Teilaufgabe 4.2");
expect(completionString).to.include("Hallo Teilaufgabe 4.3");
expect(completionString).to.include("Hallo Teilaufgabe 5.1");
expect(completionString).to.include("Hallo Teilaufgabe 5.2");
expect(completionString).to.include("Hallo Teilaufgabe 5.3");
expect(completionString).to.include("Hallo Teilaufgabe 6.1");
expect(completionString).to.include("Hallo Teilaufgabe 6.2");
});
});
});

View File

@ -90,4 +90,108 @@ describe("assignmentTrainer.cy.js", () => {
});
});
});
it("can make complete evaluation", () => {
cy.visit("/course/test-lehrgang/cockpit");
cy.get(
'[data-cy="show-details-btn-test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice"]'
).click();
cy.get('[data-cy="Student1"]').find('[data-cy="show-results"]').click();
cy.get('[data-cy="start-evaluation"]').click();
// step 1
cy.get('[data-cy="evaluation-task"]').should(
"contain",
"Beurteilungskriterium 1 / 5"
);
cy.get('[data-cy="subtask-6"]').click();
cy.get('[data-cy="reason-text"]').type("Begründung Schritt 1");
// wait for debounce
cy.wait(500);
cy.get('[data-cy="next-step"]').click();
// step 2
cy.get('[data-cy="evaluation-task"]').should(
"contain",
"Beurteilungskriterium 2 / 5"
);
cy.get('[data-cy="subtask-4"]').click();
cy.get('[data-cy="reason-text"]').type("Begründung Schritt 2");
cy.wait(500);
cy.get('[data-cy="next-step"]').click();
// step 3
cy.get('[data-cy="evaluation-task"]').should(
"contain",
"Beurteilungskriterium 3 / 5"
);
cy.get('[data-cy="subtask-2"]').click();
cy.get('[data-cy="reason-text"]').type("Begründung Schritt 3");
cy.wait(500);
cy.get('[data-cy="next-step"]').click();
// step 4
cy.get('[data-cy="evaluation-task"]').should(
"contain",
"Beurteilungskriterium 4 / 5"
);
cy.get('[data-cy="subtask-3"]').click();
cy.get('[data-cy="reason-text"]').type("Begründung Schritt 4");
cy.wait(500);
cy.get('[data-cy="next-step"]').click();
// step 5
cy.get('[data-cy="evaluation-task"]').should(
"contain",
"Beurteilungskriterium 5 / 5"
);
cy.get('[data-cy="subtask-2"]').click();
cy.get('[data-cy="reason-text"]').type("Begründung Schritt 5");
cy.wait(500);
cy.get('[data-cy="next-step"]').click();
// freigabe
cy.get('[data-cy="sub-title"]').should("contain", "Bewertung Freigabe");
cy.get('[data-cy="user-points"]').should("contain", "17");
cy.get('[data-cy="total-points"]').should("contain", "24");
cy.get('[data-cy="submit-evaluation"]').click();
cy.get('[data-cy="result-section"]').should(
"contain",
"Deine Bewertung für Test Student1 wurde freigegeben"
);
// going back to cockpit should show points for student
cy.visit("/course/test-lehrgang/cockpit");
cy.reload();
cy.get(
'[data-cy="show-details-btn-test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice"]'
).click();
cy.get('[data-cy="Student1"]').should("contain", "Bewertung freigegeben");
cy.get('[data-cy="Student1"]').should("contain", "17 Punkte");
// clicking on results page will go to last step
cy.get('[data-cy="Student1"]').find('[data-cy="show-results"]').click();
cy.url().should("include", "step=6");
// load AssignmentCompletion from DB and check
cy.loadAssignmentCompletion(
"assignment_user_id",
TEST_STUDENT1_USER_ID
).then((ac) => {
expect(ac.completion_status).to.equal("EVALUATION_SUBMITTED");
expect(ac.evaluation_points).to.equal(17);
expect(ac.evaluation_max_points).to.equal(24);
expect(ac.evaluation_passed).to.equal(true);
const completionString = JSON.stringify(ac.completion_data);
expect(completionString).to.include("Begründung Schritt 1");
expect(completionString).to.include("Begründung Schritt 2");
expect(completionString).to.include("Begründung Schritt 3");
expect(completionString).to.include("Begründung Schritt 4");
expect(completionString).to.include("Begründung Schritt 5");
});
});
});

View File

@ -46,9 +46,9 @@ describe("competenceCertificate.cy.js", () => {
).should("contain", "Höchstpunktzahl");
});
it("check with finished edoniq test", () => {
it("check with finished passed edoniq test", () => {
cy.manageCommand(
"cypress_reset --create-assignment-completion --create-edoniq-test-results"
"cypress_reset --create-assignment-completion --create-edoniq-test-results 19 24"
);
login("test-student1@example.com", "test");
cy.visit("/course/test-lehrgang/competence");
@ -90,12 +90,51 @@ describe("competenceCertificate.cy.js", () => {
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
)
.should("contain", "19")
.and("contain", "Bewertung freigegeben");
.and("contain", "Bewertung freigegeben")
.and("not.contain", "Nicht Bestanden");
// it can open learning content page directly
cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]'
).click();
cy.get('[data-cy="test-result"]')
.should("contain", "19 von 24 Punkten")
.and("contain", "79%")
.and("contain", "Bestanden");
});
it("check with finished failed edoniq test", () => {
cy.manageCommand(
"cypress_reset --create-assignment-completion --create-edoniq-test-results 10 24"
);
login("test-student1@example.com", "test");
// go to certificate detail page
cy.visit(
"/course/test-lehrgang/competence/certificates/kompetenznachweis-1"
);
cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
)
.should("contain", "10")
.and("contain", "Bewertung freigegeben")
.and("contain", "42%")
.and("contain", "Nicht Bestanden");
// it can open learning content page directly
cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]'
).click();
cy.get('[data-cy="test-result"]')
.should("contain", "10 von 24 Punkten")
.and("contain", "42%")
.and("contain", "Nicht Bestanden");
});
it("check with finished edoniq test and finished casework", () => {
cy.manageCommand(
"cypress_reset --create-assignment-evaluation --create-edoniq-test-results"
"cypress_reset --create-assignment-evaluation --create-edoniq-test-results 19 24"
);
login("test-student1@example.com", "test");
cy.visit("/course/test-lehrgang/competence");

View File

@ -133,6 +133,7 @@ describe("feedbackStudent.cy.js", () => {
// reopening page should get directly to last step
cy.visit("/course/test-lehrgang/learn/fahrzeug/feedback");
cy.url().should("include", "step=11");
// check stored data
cy.loadFeedbackResponse("feedback_user_id", TEST_STUDENT1_USER_ID).then(

Binary file not shown.

Binary file not shown.

View File

@ -128,6 +128,7 @@ LOCAL_APPS = [
"vbv_lernwelt.assignment",
"vbv_lernwelt.duedate",
"vbv_lernwelt.importer",
"vbv_lernwelt.edoniq_test",
]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

View File

@ -43,6 +43,8 @@ azure-storage-blob==12.17.0
# django-storages
backcall==0.2.0
# via ipython
bcrypt==4.0.1
# via paramiko
beautifulsoup4==4.11.2
# via wagtail
black==23.7.0
@ -69,6 +71,7 @@ cffi==1.15.1
# via
# argon2-cffi-bindings
# cryptography
# pynacl
cfgv==3.3.1
# via pre-commit
charset-normalizer==3.2.0
@ -95,6 +98,7 @@ cryptography==41.0.3
# azure-identity
# azure-storage-blob
# msal
# paramiko
# pyjwt
decorator==5.1.1
# via
@ -336,6 +340,8 @@ packaging==23.1
# marshmallow
# pytest
# pytest-sugar
paramiko==3.3.1
# via -r requirements.in
parso==0.8.3
# via jedi
pathspec==0.11.2
@ -400,6 +406,8 @@ pylint-django==2.5.3
# via -r requirements-dev.in
pylint-plugin-utils==0.8.2
# via pylint-django
pynacl==1.5.0
# via paramiko
pyproject-hooks==1.0.0
# via build
pytest==7.4.0

View File

@ -40,6 +40,7 @@ python-json-logger
concurrent-log-handler
python-dateutil
pycryptodome
paramiko
wagtail>=4
wagtail-factories>=4

View File

@ -34,6 +34,8 @@ azure-storage-blob==12.17.0
# via
# -r requirements.in
# django-storages
bcrypt==4.0.1
# via paramiko
beautifulsoup4==4.11.2
# via wagtail
boto3==1.28.23
@ -52,6 +54,7 @@ cffi==1.15.1
# via
# argon2-cffi-bindings
# cryptography
# pynacl
charset-normalizer==3.2.0
# via requests
click==8.1.6
@ -67,6 +70,7 @@ cryptography==41.0.3
# azure-identity
# azure-storage-blob
# msal
# paramiko
# pyjwt
defusedxml==0.7.1
# via willow
@ -211,6 +215,8 @@ packaging==23.1
# via
# gunicorn
# marshmallow
paramiko==3.3.1
# via -r requirements.in
pillow==10.0.0
# via
# -r requirements.in
@ -234,6 +240,8 @@ pycryptodome==3.18.0
# via -r requirements.in
pyjwt[crypto]==2.8.0
# via msal
pynacl==1.5.0
# via paramiko
python-dateutil==2.8.2
# via
# -r requirements.in

View File

@ -36,6 +36,7 @@ def create_uk_fahrzeug_casework(course_id=COURSE_UK, competence_certificate=None
assignment = AssignmentFactory(
parent=assignment_list_page,
title="Überprüfen einer Motorfahrzeugs-Versicherungspolice",
needs_expert_evaluation=True,
competence_certificate=competence_certificate,
effort_required="ca. 5 Stunden",
intro_text=replace_whitespace(

View File

@ -29,8 +29,8 @@ class AssignmentCompletionMutation(graphene.Mutation):
)
completion_data_string = graphene.String()
evaluation_grade = graphene.Float()
evaluation_points = graphene.Float()
evaluation_passed = graphene.Boolean()
initialize_completion = graphene.Boolean(required=False)
@classmethod
@ -44,8 +44,8 @@ class AssignmentCompletionMutation(graphene.Mutation):
assignment_user_id=None,
completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS,
completion_data_string="{}",
evaluation_grade=None,
evaluation_points=None,
evaluation_passed=None,
initialize_completion=False,
):
if assignment_user_id is None:
@ -97,11 +97,11 @@ class AssignmentCompletionMutation(graphene.Mutation):
evaluation_data = {
"evaluation_user": info.context.user,
"evaluation_grade": evaluation_grade,
"evaluation_points": evaluation_points,
"evaluation_passed": evaluation_passed,
}
ac = update_assignment_completion(
ac, created = update_assignment_completion(
copy_task_data=False,
initialize_completion=initialize_completion,
**assignment_data,
@ -116,6 +116,7 @@ class AssignmentCompletionMutation(graphene.Mutation):
assignment_user_id=assignment_user_id,
course_session_id=course_session_id,
completion_status=completion_status,
created=created,
)
return AssignmentCompletionMutation(assignment_completion=ac)

View File

@ -31,8 +31,10 @@ class AssignmentCompletionObjectType(DjangoObjectType):
"completion_data",
"evaluation_user",
"additional_json_data",
"evaluation_grade",
"edoniq_extended_time_flag",
"evaluation_points",
"evaluation_passed",
"evaluation_max_points",
)
@ -54,6 +56,7 @@ class AssignmentObjectType(DjangoObjectType):
interfaces = (CoursePageInterface,)
fields = (
"assignment_type",
"needs_expert_evaluation",
"intro_text",
"effort_required",
"evaluation_description",

View File

@ -0,0 +1,40 @@
# Generated by Django 3.2.20 on 2023-09-28 10:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("assignment", "0007_auto_20230901_1112"),
]
operations = [
migrations.RemoveField(
model_name="assignmentcompletion",
name="evaluation_grade",
),
migrations.RemoveField(
model_name="assignmentcompletionauditlog",
name="evaluation_grade",
),
migrations.AddField(
model_name="assignmentcompletion",
name="evaluation_max_points",
field=models.FloatField(blank=True, null=True),
),
migrations.AddField(
model_name="assignmentcompletion",
name="evaluation_passed",
field=models.BooleanField(blank=True, null=True),
),
migrations.AddField(
model_name="assignmentcompletionauditlog",
name="evaluation_max_points",
field=models.FloatField(blank=True, null=True),
),
migrations.AddField(
model_name="assignmentcompletionauditlog",
name="evaluation_passed",
field=models.BooleanField(blank=True, null=True),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.20 on 2023-09-29 10:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("assignment", "0008_auto_20230928_1254"),
]
operations = [
migrations.AddField(
model_name="assignment",
name="needs_expert_evaluation",
field=models.BooleanField(
default=False,
help_text="Muss der Auftrag durch eine Expertin oder einen Experten beurteilt werden?",
),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.20 on 2023-10-05 16:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("assignment", "0009_assignment_needs_evaluation"),
]
operations = [
migrations.AddField(
model_name="assignmentcompletion",
name="edoniq_extended_time_flag",
field=models.BooleanField(default=False),
),
]

View File

@ -139,6 +139,11 @@ class Assignment(CourseBasePage):
default=AssignmentType.CASEWORK.value,
)
needs_expert_evaluation = models.BooleanField(
default=False,
help_text="Muss der Auftrag durch eine Expertin oder einen Experten beurteilt werden?",
)
competence_certificate = models.ForeignKey(
"competence.CompetenceCertificate",
null=True,
@ -194,6 +199,7 @@ class Assignment(CourseBasePage):
content_panels = Page.content_panels + [
FieldPanel("assignment_type"),
FieldPanel("needs_expert_evaluation"),
PageChooserPanel("competence_certificate", "competence.CompetenceCertificate"),
FieldPanel("intro_text"),
FieldPanel("effort_required"),
@ -316,8 +322,10 @@ class AssignmentCompletion(models.Model):
blank=True,
related_name="+",
)
evaluation_grade = models.FloatField(null=True, blank=True)
evaluation_points = models.FloatField(null=True, blank=True)
evaluation_max_points = models.FloatField(null=True, blank=True)
evaluation_passed = models.BooleanField(null=True, blank=True)
edoniq_extended_time_flag = models.BooleanField(default=False)
assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
@ -395,5 +403,6 @@ class AssignmentCompletionAuditLog(models.Model):
assignment_user_email = models.CharField(max_length=255)
assignment_slug = models.CharField(max_length=255)
evaluation_user_email = models.CharField(max_length=255, blank=True, default="")
evaluation_grade = models.FloatField(null=True, blank=True)
evaluation_points = models.FloatField(null=True, blank=True)
evaluation_max_points = models.FloatField(null=True, blank=True)
evaluation_passed = models.BooleanField(null=True, blank=True)

View File

@ -19,6 +19,7 @@ class AssignmentCompletionSerializer(serializers.ModelSerializer):
"completion_data",
"evaluation_user",
"additional_json_data",
"evaluation_grade",
"evaluation_points",
"evaluation_max_points",
"evaluation_passed",
]

View File

@ -26,12 +26,16 @@ def update_assignment_completion(
completion_data=None,
completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS,
evaluation_user: User | None = None,
evaluation_grade: float | None = None,
evaluation_points: float | None = None,
evaluation_passed: bool | None = None,
evaluation_max_points: float | None = None,
validate_completion_status_change: bool = True,
copy_task_data: bool = False,
initialize_completion: bool = False,
) -> AssignmentCompletion:
additional_json_data: dict | None = None,
edoniq_extended_time_flag: bool = False,
validate_submission_update: bool = True,
) -> tuple[AssignmentCompletion, bool]:
"""
:param completion_data: should have the following structure:
{
@ -51,11 +55,13 @@ def update_assignment_completion(
context
:param initialize_completion: if true, the completion will be created, but not updated
used as a workaround for initial work with the object on the frontend
:return: AssignmentCompletion
"""
if completion_data is None:
completion_data = {}
if additional_json_data is None:
additional_json_data = {}
ac, created = AssignmentCompletion.objects.get_or_create(
assignment_user_id=assignment_user.id,
assignment_id=assignment.id,
@ -67,7 +73,7 @@ def update_assignment_completion(
if initialize_completion:
# ignore further updates
return ac
return ac, True
if not is_valid_assignment_completion_status(completion_status):
raise serializers.ValidationError(
@ -78,17 +84,22 @@ def update_assignment_completion(
if validate_completion_status_change:
if completion_status == AssignmentCompletionStatus.SUBMITTED:
if ac.completion_status in [
"SUBMITTED",
completion_status_to_check = [
"EVALUATION_IN_PROGRESS",
"EVALUATION_SUBMITTED",
]:
]
if validate_submission_update:
completion_status_to_check.append("SUBMITTED")
if ac.completion_status in completion_status_to_check:
raise serializers.ValidationError(
{
"completion_status": f"Cannot update completion status from {ac.completion_status} to SUBMITTED"
}
)
elif completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED:
elif (
completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED
and validate_submission_update
):
if ac.completion_status == "EVALUATION_SUBMITTED":
raise serializers.ValidationError(
{
@ -119,12 +130,6 @@ def update_assignment_completion(
ac.evaluation_user = evaluation_user
if completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED:
if evaluation_grade is None:
raise serializers.ValidationError(
{
"evaluation_grade": "evaluation_grade is required for EVALUATION_SUBMITTED status"
}
)
if evaluation_points is None:
raise serializers.ValidationError(
{
@ -132,9 +137,20 @@ def update_assignment_completion(
}
)
ac.evaluation_grade = evaluation_grade
if evaluation_max_points is None:
ac.evaluation_max_points = assignment.get_max_points()
else:
ac.evaluation_max_points = evaluation_max_points
ac.evaluation_points = evaluation_points
# if no evaluation_passed is provided, we calculate it from the points
if evaluation_passed is None:
if evaluation_points is not None and ac.evaluation_max_points is not None:
# if more or equal than 60% of the points are reached, the assignment is passed
ac.evaluation_passed = (evaluation_points / ac.evaluation_max_points) >= 0.6
else:
ac.evaluation_passed = evaluation_passed
if completion_status == AssignmentCompletionStatus.SUBMITTED:
ac.submitted_at = timezone.now()
if evaluation_user:
@ -160,6 +176,8 @@ def update_assignment_completion(
)
ac.completion_status = completion_status.value
ac.edoniq_extended_time_flag = edoniq_extended_time_flag
ac.additional_json_data = ac.additional_json_data | additional_json_data
# TODO: make more validation of the provided input -> maybe with graphql
completion_data = _remove_unknown_entries(assignment, completion_data)
@ -192,6 +210,9 @@ def update_assignment_completion(
assignment_user_email=assignment_user.email,
assignment_slug=assignment.slug,
completion_data=deepcopy(ac.completion_data),
evaluation_points=evaluation_points,
evaluation_max_points=ac.evaluation_max_points,
evaluation_passed=ac.evaluation_passed,
)
if evaluation_user:
acl.evaluation_user_email = evaluation_user.email
@ -222,7 +243,7 @@ def update_assignment_completion(
completion_status=CourseCompletionStatus.SUCCESS.value,
)
return ac
return ac, created
def _remove_unknown_entries(assignment, completion_data):

View File

@ -330,7 +330,6 @@ class AttendanceCourseUserMutationTestCase(GraphQLTestCase):
course_session_id: {self.course_session.id}
completion_status: EVALUATION_SUBMITTED
completion_data_string: "{completion_data_string}"
evaluation_grade: 4.5,
evaluation_points: 16,
) {{
assignment_completion {{
@ -370,8 +369,9 @@ class AttendanceCourseUserMutationTestCase(GraphQLTestCase):
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "EVALUATION_SUBMITTED")
self.assertEqual(db_entry.evaluation_grade, 4.5)
self.assertEqual(db_entry.evaluation_points, 16)
self.assertEqual(db_entry.evaluation_max_points, 24)
self.assertTrue(db_entry.evaluation_passed)
self.assertDictEqual(
db_entry.completion_data,
{
@ -440,6 +440,9 @@ class AttendanceCourseUserMutationTestCase(GraphQLTestCase):
},
},
)
self.assertEqual(acl.evaluation_points, 16)
self.assertEqual(acl.evaluation_max_points, 24)
self.assertTrue(acl.evaluation_passed)
def test_student_can_upsert_reflection(self):
"""

View File

@ -478,7 +478,6 @@ class UpdateAssignmentCompletionTestCase(TestCase):
completion_data={},
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED,
evaluation_user=self.trainer,
evaluation_grade=None,
evaluation_points=None,
)
@ -489,7 +488,6 @@ class UpdateAssignmentCompletionTestCase(TestCase):
completion_data={},
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED,
evaluation_user=self.trainer,
evaluation_grade=4.5,
evaluation_points=16,
)
@ -500,8 +498,9 @@ class UpdateAssignmentCompletionTestCase(TestCase):
)
self.assertEqual(ac.completion_status, "EVALUATION_SUBMITTED")
self.assertEqual(ac.evaluation_grade, 4.5)
self.assertEqual(ac.evaluation_points, 16)
self.assertEqual(ac.evaluation_max_points, 24)
self.assertTrue(ac.evaluation_passed)
trainer_input = ac.completion_data[evaluation_task["id"]]
self.assertDictEqual(
trainer_input["expert_data"], {"points": 2, "text": "Gut gemacht!"}
@ -532,6 +531,9 @@ class UpdateAssignmentCompletionTestCase(TestCase):
self.assertDictEqual(
user_input["user_data"], {"text": "Ich würde nichts weiteres empfehlen."}
)
self.assertEqual(acl.evaluation_points, 16)
self.assertEqual(acl.evaluation_max_points, 24)
self.assertTrue(acl.evaluation_passed)
# AssignmentCompletionAuditLog entry will remain event after deletion of foreign keys
ac.delete()

View File

@ -20,8 +20,8 @@ def request_assignment_completion_status(request, assignment_id, course_session_
"id",
"assignment_user_id",
"completion_status",
"evaluation_grade",
"evaluation_points",
"evaluation_passed",
"learning_content_page_id",
)
return Response(status=200, data=qs)

View File

@ -33,9 +33,16 @@ from vbv_lernwelt.notify.models import Notification
help="will create assignment evaluation data for test-student1@example.com",
)
@click.option(
"--create-edoniq-test-results/--no-create-edoniq-test-results",
default=False,
help="will create edoniq result data for test-student1@example.com",
"--assignment-evaluation-scores",
default=None,
help="Provide assignment evaluation scores in the format: 6,6,6,3,3",
)
@click.option(
"--create-edoniq-test-results",
type=(int, int),
default=(None, None),
metavar="USER_POINTS MAX_POINTS",
help="Create edoniq result data for test-student1@example.com with user points and max points",
)
@click.option(
"--create-feedback-responses/--no-create-feedback-responses",
@ -45,6 +52,7 @@ from vbv_lernwelt.notify.models import Notification
def command(
create_assignment_completion,
create_assignment_evaluation,
assignment_evaluation_scores,
create_edoniq_test_results,
create_feedback_responses,
):
@ -66,7 +74,16 @@ def command(
user=User.objects.get(id=TEST_STUDENT1_USER_ID),
)
if create_assignment_evaluation:
print("create assignment evaulation data for test course")
if not assignment_evaluation_scores:
assignment_evaluation_scores = [6, 6, 6, 3, 3]
else:
assignment_evaluation_scores = tuple(
map(int, assignment_evaluation_scores.split(","))
)
print(
"create assignment evaluation data for test course",
assignment_evaluation_scores,
)
create_test_assignment_evaluation_data(
assignment=Assignment.objects.get(
slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"
@ -74,17 +91,22 @@ def command(
course_session=CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID),
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
evaluation_user=User.objects.get(id=TEST_TRAINER1_USER_ID),
input_scores=assignment_evaluation_scores,
)
if create_edoniq_test_results:
print("create edoniq test results")
user_points, max_points = create_edoniq_test_results
if user_points is not None and max_points is not None:
print(
f"Create edoniq test results: User Points: {user_points}, Max Points: {max_points}"
)
create_edoniq_test_result_data(
assignment=Assignment.objects.get(
slug="test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"
),
course_session=CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID),
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
points=19,
user_points=user_points,
max_points=max_points,
)
if create_feedback_responses:

View File

@ -135,29 +135,42 @@ def check_rate_limit(request):
def cypress_reset_view(request):
if not settings.APP_ENVIRONMENT.startswith("prod"):
# Checking for the flags in the POST request
options = {}
create_assignment_completion = (
request.data.get("create_assignment_completion") == "true"
)
if create_assignment_completion:
options["create_assignment_completion"] = create_assignment_completion
create_assignment_evaluation = (
request.data.get("create_assignment_evaluation") == "true"
)
create_edoniq_test_results = (
request.data.get("create_edoniq_test_results") == "true"
)
if create_assignment_evaluation:
options["create_assignment_evaluation"] = create_assignment_evaluation
# assignment evaluation scores
assignment_evaluation_scores = request.data.get("assignment_evaluation_scores")
if assignment_evaluation_scores:
options["assignment_evaluation_scores"] = assignment_evaluation_scores
# TODO: Handle the flags as needed. For example:
if create_assignment_completion:
# Logic for creating assignment completion
pass
if create_edoniq_test_results:
# Logic for creating Edoniq test results
pass
create_feedback_responses = (
request.data.get("create_feedback_responses") == "true"
)
if create_feedback_responses:
options["create_feedback_responses"] = create_feedback_responses
# edoniq test results
edoniq_test_user_points = request.data.get("edoniq_test_user_points")
edoniq_test_max_points = request.data.get("edoniq_test_max_points")
if bool(edoniq_test_user_points and edoniq_test_max_points):
options["create_edoniq_test_results"] = (
int(edoniq_test_user_points),
int(edoniq_test_max_points),
)
call_command(
"cypress_reset",
create_assignment_completion=create_assignment_completion,
create_assignment_evaluation=create_assignment_evaluation,
create_edoniq_test_results=create_edoniq_test_results,
**options,
)
return HttpResponseRedirect("/server/admin/")

View File

@ -49,12 +49,14 @@ from vbv_lernwelt.course.utils import get_wagtail_default_site
from vbv_lernwelt.course_session.models import (
CourseSessionAssignment,
CourseSessionAttendanceCourse,
CourseSessionEdoniqTest,
)
from vbv_lernwelt.feedback.services import update_feedback_response
from vbv_lernwelt.learnpath.models import (
Circle,
LearningContentAssignment,
LearningContentAttendanceCourse,
LearningContentEdoniqTest,
)
from vbv_lernwelt.learnpath.tests.learning_path_factories import (
CircleFactory,
@ -168,6 +170,19 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
)
csa.submission_deadline.save()
cset = CourseSessionEdoniqTest.objects.create(
course_session=cs_bern,
learning_content=LearningContentEdoniqTest.objects.get(
slug="test-lehrgang-lp-circle-fahrzeug-lc-wissens-und-verständnisfragen"
),
)
cset.deadline.start = timezone.make_aware(
(next_monday + relativedelta(days=3)).replace(
hour=21, minute=0, second=0, microsecond=0
)
)
cset.deadline.save()
cs_zurich = CourseSession.objects.create(
course_id=COURSE_TEST_ID,
title="Test Zürich 2022 a",
@ -237,14 +252,18 @@ def create_test_assignment_submitted_data(assignment, course_session, user):
def create_test_assignment_evaluation_data(
assignment, course_session, assignment_user, evaluation_user
assignment, course_session, assignment_user, evaluation_user, input_scores=None
):
if assignment and course_session and assignment_user and evaluation_user:
subtasks = assignment.get_evaluation_tasks()
evaluation_points = 0
for index, evaluation_task in enumerate(subtasks):
evaluation_points += evaluation_task["value"]["max_points"]
task_score = evaluation_task["value"]["max_points"]
if input_scores[index] < len(input_scores):
task_score = input_scores[index]
evaluation_points += task_score
update_assignment_completion(
assignment_user=assignment_user,
@ -254,8 +273,8 @@ def create_test_assignment_evaluation_data(
completion_data={
evaluation_task["id"]: {
"expert_data": {
"points": evaluation_task["value"]["max_points"],
"text": "Gut gemacht!",
"points": task_score,
"text": f"Kriterium {index + 1}: Gut gemacht!",
}
},
},
@ -271,13 +290,12 @@ def create_test_assignment_evaluation_data(
completion_data={},
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED,
evaluation_user=evaluation_user,
evaluation_grade=6,
evaluation_points=evaluation_points,
)
def create_edoniq_test_result_data(
assignment, course_session, assignment_user, points=24
assignment, course_session, assignment_user, user_points=19, max_points=24
):
if assignment and course_session and assignment_user:
update_assignment_completion(
@ -288,8 +306,8 @@ def create_edoniq_test_result_data(
completion_data={},
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED,
evaluation_user=User.objects.get(username="admin"),
evaluation_grade=6,
evaluation_points=points,
evaluation_points=user_points,
evaluation_max_points=max_points,
)
@ -458,7 +476,11 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
),
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
edoniq_course_release_id="1689096523730",
edoniq_sequence_id="1688059890497",
extended_time_test_url="https://exam2.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
edoniq_extended_course_release_id="1691157696911",
edoniq_extended_sequence_id="1691151920116",
)
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")

View File

@ -10,10 +10,12 @@ from vbv_lernwelt.course.models import (
from vbv_lernwelt.course_session.models import (
CourseSessionAssignment,
CourseSessionAttendanceCourse,
CourseSessionEdoniqTest,
)
from vbv_lernwelt.course_session.serializers import (
CourseSessionAssignmentSerializer,
CourseSessionAttendanceCourseSerializer,
CourseSessionEdoniqTestSerializer,
)
from vbv_lernwelt.duedate.models import DueDate
from vbv_lernwelt.duedate.serializers import DueDateSerializer
@ -61,6 +63,7 @@ class CourseSessionSerializer(serializers.ModelSerializer):
documents = serializers.SerializerMethodField()
attendance_courses = serializers.SerializerMethodField()
assignments = serializers.SerializerMethodField()
edoniq_tests = serializers.SerializerMethodField()
due_dates = serializers.SerializerMethodField()
def get_course(self, obj):
@ -97,6 +100,11 @@ class CourseSessionSerializer(serializers.ModelSerializer):
CourseSessionAssignment.objects.filter(course_session=obj), many=True
).data
def get_edoniq_tests(self, obj):
return CourseSessionEdoniqTestSerializer(
CourseSessionEdoniqTest.objects.filter(course_session=obj), many=True
).data
def get_due_dates(self, obj):
due_dates = DueDate.objects.filter(course_session=obj)
return DueDateSerializer(due_dates, many=True).data
@ -114,6 +122,7 @@ class CourseSessionSerializer(serializers.ModelSerializer):
"additional_json_data",
"attendance_courses",
"assignments",
"edoniq_tests",
"learning_path_url",
"cockpit_url",
"competence_url",

View File

@ -3,6 +3,7 @@ from rest_framework import serializers
from vbv_lernwelt.course_session.models import (
CourseSessionAssignment,
CourseSessionAttendanceCourse,
CourseSessionEdoniqTest,
)
@ -61,3 +62,21 @@ class CourseSessionAssignmentSerializer(serializers.ModelSerializer):
def get_submission_deadline_start(self, obj):
if obj.submission_deadline:
return obj.submission_deadline.start
class CourseSessionEdoniqTestSerializer(serializers.ModelSerializer):
deadline_start = serializers.SerializerMethodField()
class Meta:
model = CourseSessionEdoniqTest
fields = [
"id",
"course_session_id",
"learning_content_id",
"deadline_id",
"deadline_start",
]
def get_deadline_start(self, obj):
if obj.deadline:
return obj.deadline.start

View File

@ -44,6 +44,7 @@ def reverse_func(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
("duedate", "0004_alter_duedate_start"),
("learnpath", "0008_add_edoniq_sequence_id"),
]
operations = [

View File

@ -1,7 +1,7 @@
from django.apps import AppConfig
class LearnpathConfig(AppConfig):
class EdoniqTestConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "vbv_lernwelt.edoniq_test"

View File

@ -0,0 +1,61 @@
import structlog
from vbv_lernwelt.core.base import LoggedCommand
from vbv_lernwelt.edoniq_test.result_import.edoniq_sftp_connection import (
load_edoniq_test_results_csv_data,
)
from vbv_lernwelt.edoniq_test.result_import.services import (
create_edoniq_csv_test_result,
filter_relevant_rows,
upsert_edoniq_test_result,
)
logger = structlog.get_logger(__name__)
class Command(LoggedCommand):
help = "Imports Test Results from Edoniq"
def handle(self, *args, **options):
logger.info(
"edoniq_import_results job started",
label="edoniq_import_results",
)
stat_results = {
"num_created": 0,
"num_updated": 0,
"num_error": 0,
"num_passed": 0,
"num_failed": 0,
}
csv_data = load_edoniq_test_results_csv_data()
relevant_rows = filter_relevant_rows(csv_data)
stat_results["num_relevant_edoniq_csv_rows"] = len(relevant_rows)
for row in relevant_rows:
result_row = create_edoniq_csv_test_result(row)
assignment_completion, created = upsert_edoniq_test_result(
result_row, fail_silently=True
)
if assignment_completion:
if created:
stat_results["num_created"] += 1
else:
stat_results["num_updated"] += 1
if assignment_completion.evaluation_passed:
stat_results["num_passed"] += 1
else:
stat_results["num_failed"] += 1
else:
stat_results["num_error"] += 1
self.job_log.json_data = stat_results
self.job_log.save()
logger.info(
"edoniq_import_results job finished",
label="edoniq_import_results",
stat_results=stat_results,
)

View File

@ -0,0 +1,67 @@
import csv
import os
from io import StringIO
import paramiko
import structlog
logger = structlog.get_logger(__name__)
def _create_edoniq_sftp_client():
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Get the private key from the environment variable and convert it to a key object
private_key_env_var_name = "IT_EDONIQ_SFTP_PRIVATE_KEY"
private_key_str = os.environ.get(private_key_env_var_name).replace("\\n", "\n")
if not private_key_str:
logger.warn(
"Environment variable not set or empty",
env_var=private_key_env_var_name,
label="edoniq_import_results",
)
raise ValueError(
f"Environment variable {private_key_env_var_name} not set or empty."
)
private_key_file = StringIO(private_key_str)
private_key = paramiko.Ed25519Key.from_private_key(private_key_file)
client.connect(
hostname="217.20.192.94",
port=20041,
username="sftpuser",
pkey=private_key,
)
return client.open_sftp(), client
class EdoniqSftpClient:
def __enter__(self):
(self.sftp_client, self.ssh_client) = _create_edoniq_sftp_client()
return self.sftp_client
def __exit__(self, exc_type, exc_value, exc_traceback):
# self.sftp_client.close()
self.ssh_client.close()
def load_edoniq_test_results_csv_data():
with EdoniqSftpClient() as sftp_client:
with sftp_client.file("/upload/PruefungResultate.csv", "r") as file:
data = file.read().decode("utf-8")
return data
if __name__ == "__main__":
csv_data = load_edoniq_test_results_csv_data()
csv_reader = csv.reader(StringIO(csv_data), delimiter=";")
for i, row in enumerate(csv_reader):
# if len(row) > 4 and row[4] == "AG 2023 A" and row[5] == "de_üK1_KO_Testlauf":
# print(row)
if len(row) > 4 and row[6] == "1691151920116":
print(row)
# if i < 10:
# print(row)

View File

@ -0,0 +1,163 @@
import csv
from dataclasses import dataclass
from io import StringIO
import structlog
from django.db.models import Q
from vbv_lernwelt.assignment.models import AssignmentCompletionStatus
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
from vbv_lernwelt.learnpath.models import LearningContentEdoniqTest
logger = structlog.get_logger(__name__)
def get_distinct_course_release_ids():
distinct_course_release_ids = LearningContentEdoniqTest.objects.values_list(
"edoniq_course_release_id", flat=True
).distinct()
distinct_course_release_extended_ids = (
LearningContentEdoniqTest.objects.values_list(
"edoniq_extended_course_release_id", flat=True
).distinct()
)
# filter out empty values
return {
seq_id
for seq_id in list(distinct_course_release_ids)
+ list(distinct_course_release_extended_ids)
if seq_id
}
def filter_relevant_rows(csv_data):
release_ids = get_distinct_course_release_ids()
csv_reader = csv.reader(StringIO(csv_data), delimiter=";")
# read headers
headers = next(csv_reader)
headers = [header.strip() for header in headers]
# Read the rest of the lines and create a list of dictionaries
records = []
for row in csv_reader:
# strip all whitespace around the column entries
row = [item.strip() for item in row]
records.append(dict(zip(headers, row)))
relevant_rows = [
row for row in records if row.get("Kursfreigaben ID") in release_ids
]
return relevant_rows
@dataclass
class EdoniqCsvTestResult:
user_id: str
user_points: float
max_points: float
passed: bool
edoniq_course_release_id: str
unfinished: bool = False
def create_edoniq_csv_test_result(edoniq_csv_row_dict) -> EdoniqCsvTestResult:
user_id = edoniq_csv_row_dict["Benutzer Login"]
user_points = float(edoniq_csv_row_dict["erreichte Punkte absolut"])
max_points = float(edoniq_csv_row_dict["erreichbares Punktetotal absolut"])
passed = edoniq_csv_row_dict["Bestanden-/Nicht-Bestanden Status"] == "Bestanden"
unfinished = edoniq_csv_row_dict["Bestanden-/Nicht-Bestanden Status"] == "Begonnen"
edoniq_course_release_id = edoniq_csv_row_dict["Kursfreigaben ID"]
return EdoniqCsvTestResult(
user_id=user_id,
user_points=user_points,
max_points=max_points,
passed=passed,
edoniq_course_release_id=edoniq_course_release_id,
unfinished=unfinished,
)
def upsert_edoniq_test_result(
edoniq_csv_test_result: EdoniqCsvTestResult, fail_silently=False
):
try:
u = User.objects.get(id=edoniq_csv_test_result.user_id)
# because the same edoniq test is used in multiple languages,
# we have to consider multiple learning contents
learning_content_qs = LearningContentEdoniqTest.objects.filter(
Q(edoniq_course_release_id=edoniq_csv_test_result.edoniq_course_release_id)
| Q(
edoniq_extended_course_release_id=edoniq_csv_test_result.edoniq_course_release_id
)
)
user_course_session_ids = set(
CourseSessionUser.objects.filter(
user_id=u.id,
course_session__course_id__in=[
lc.get_course().id for lc in learning_content_qs
],
).values_list("course_session__id", flat=True)
)
learning_content = learning_content_qs.first()
for course_session_id in user_course_session_ids:
course_session = CourseSession.objects.get(id=course_session_id)
# find the assignment which is attached to this language course
assignment = (
learning_content_qs.descendant_of(course_session.course.coursepage)
.first()
.content_assignment
)
completion_status = AssignmentCompletionStatus.EVALUATION_SUBMITTED
if edoniq_csv_test_result.unfinished:
completion_status = AssignmentCompletionStatus.SUBMITTED
edoniq_extended_time_flag = False
if (
learning_content.edoniq_extended_course_release_id
== edoniq_csv_test_result.edoniq_course_release_id
):
edoniq_extended_time_flag = True
return update_assignment_completion(
assignment_user=u,
assignment=assignment,
course_session=course_session,
learning_content_page=assignment.find_attached_learning_content(),
completion_data={},
completion_status=completion_status,
evaluation_user=User.objects.get(username="admin"),
evaluation_points=edoniq_csv_test_result.user_points,
evaluation_passed=edoniq_csv_test_result.passed,
evaluation_max_points=edoniq_csv_test_result.max_points,
additional_json_data={
"edoniq_course_release_id": edoniq_csv_test_result.edoniq_course_release_id,
},
edoniq_extended_time_flag=edoniq_extended_time_flag,
validate_submission_update=False,
)
except Exception as e:
logger.warning(
"edoniq_test_result_import_fail",
label="edoniq_import_results",
error="user or learning_content not found",
user_id=edoniq_csv_test_result.user_id,
edoniq_course_release_id=edoniq_csv_test_result.edoniq_course_release_id,
exc_info=True,
)
if not fail_silently:
raise e
return None, False

View File

@ -0,0 +1,6 @@
Benutzer Login;Benutzer Name;Benutzer Vorname;Korrespondenzsprache;OE;Sequenztitel;Sequenz ID;letztes Datum der Durchführung des Kandidaten;Zeitstempel Beginn;Bearbeitungsdauer;erreichte Punkte absolut ; erreichbares Punktetotal absolut;erreichte Punkte;Bestehensgrenze prozentual;Bestanden-/Nicht-Bestanden Status;Note;prozentualer Durchschnitt der Versuche dieses Kandidaten;prozentualer Durchschnitt aller Kandidaten;Kursfreigaben ID
;
65c73ad0-6d53-43a9-a4a4-64143f27b03a;Student1;Test;de;Test Bern 2022 a;de_üK1_BA_KN;1688059890497;06.09.2023;06.09.2023 18:48;00:17:10.4650;21.25 ; 29;73.28;60;Bestanden;;73.28;65.91;1689096523730
19c40d94-15cc-4198-aaad-ef707c4b0900;Student2;Test;de;Test Bern 2022 a;de_üK1_BA_KN;1688059890497;07.09.2023;07.09.2023 19:00;00:17:49.8550;12.23 ; 29;42.16;60;Nicht bestanden;;42.16;65.91;1689096523730
bcf94dba-53bc-474b-a22d-e4af39aa042b;Student3;Test;de;Test Bern 2022 a;de_üK1_BA_KN;1691151920116;10.09.2023;10.09.2023 10:48;00:18:22.9710;0.00 ; 29;60.76;60;Begonnen;;60.76;65.91;1689096523730
bcf94dba-53bc-474b-a22d-e4af39aa042b;Student3;Test;de;Test Bern 2022 a;AG 2023 A;de_üK1_KO_Testlauf;1686325621487;10.09.2023;10.09.2023 16:32;00:00:00.0000;0.00 ; 15;0.00;60;Begonnen;;38.11;55.30;1689096897473
1 Benutzer Login;Benutzer Name;Benutzer Vorname;Korrespondenzsprache;OE;Sequenztitel;Sequenz ID;letztes Datum der Durchführung des Kandidaten;Zeitstempel Beginn;Bearbeitungsdauer;erreichte Punkte absolut ; erreichbares Punktetotal absolut;erreichte Punkte;Bestehensgrenze prozentual;Bestanden-/Nicht-Bestanden Status;Note;prozentualer Durchschnitt der Versuche dieses Kandidaten;prozentualer Durchschnitt aller Kandidaten;Kursfreigaben ID
2 ;
3 65c73ad0-6d53-43a9-a4a4-64143f27b03a;Student1;Test;de;Test Bern 2022 a;de_üK1_BA_KN;1688059890497;06.09.2023;06.09.2023 18:48;00:17:10.4650;21.25 ; 29;73.28;60;Bestanden;;73.28;65.91;1689096523730
4 19c40d94-15cc-4198-aaad-ef707c4b0900;Student2;Test;de;Test Bern 2022 a;de_üK1_BA_KN;1688059890497;07.09.2023;07.09.2023 19:00;00:17:49.8550;12.23 ; 29;42.16;60;Nicht bestanden;;42.16;65.91;1689096523730
5 bcf94dba-53bc-474b-a22d-e4af39aa042b;Student3;Test;de;Test Bern 2022 a;de_üK1_BA_KN;1691151920116;10.09.2023;10.09.2023 10:48;00:18:22.9710;0.00 ; 29;60.76;60;Begonnen;;60.76;65.91;1689096523730
6 bcf94dba-53bc-474b-a22d-e4af39aa042b;Student3;Test;de;Test Bern 2022 a;AG 2023 A;de_üK1_KO_Testlauf;1686325621487;10.09.2023;10.09.2023 16:32;00:00:00.0000;0.00 ; 15;0.00;60;Begonnen;;38.11;55.30;1689096897473

View File

@ -0,0 +1,92 @@
import os
from django.test import TestCase
from vbv_lernwelt.assignment.models import AssignmentCompletion
from vbv_lernwelt.core.constants import (
TEST_STUDENT1_USER_ID,
TEST_STUDENT2_USER_ID,
TEST_STUDENT3_USER_ID,
)
from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.course.creators.test_course import create_test_course
from vbv_lernwelt.edoniq_test.result_import.services import (
create_edoniq_csv_test_result,
EdoniqCsvTestResult,
filter_relevant_rows,
get_distinct_course_release_ids,
upsert_edoniq_test_result,
)
class EdoniqResultImportTestCase(TestCase):
def setUp(self) -> None:
create_default_users()
create_test_course(with_sessions=True)
def test_get_distinct_release_ids(self):
release_ids = get_distinct_course_release_ids()
self.assertEqual(len(release_ids), 2)
self.assertSetEqual(release_ids, {"1689096523730", "1691157696911"})
def test_filter_relevant_rows(self):
csv_file_path = os.path.dirname(__file__) + "/edoniq_pruefung_resultate.csv"
with open(
csv_file_path,
"r",
encoding="utf-8",
) as file:
csv_data = file.read()
relevant_rows = filter_relevant_rows(csv_data)
self.assertEqual(len(relevant_rows), 3)
print(relevant_rows)
self.assertEqual(relevant_rows[0]["Benutzer Login"], TEST_STUDENT1_USER_ID)
self.assertEqual(relevant_rows[1]["Benutzer Login"], TEST_STUDENT2_USER_ID)
self.assertEqual(relevant_rows[2]["Benutzer Login"], TEST_STUDENT3_USER_ID)
self.assertEqual(relevant_rows[0]["erreichte Punkte absolut"], "21.25")
self.assertEqual(relevant_rows[0]["erreichbares Punktetotal absolut"], "29")
self.assertEqual(
relevant_rows[0]["Bestanden-/Nicht-Bestanden Status"], "Bestanden"
)
self.assertEqual(relevant_rows[0]["Zeitstempel Beginn"], "06.09.2023 18:48")
self.assertEqual(relevant_rows[2]["erreichte Punkte absolut"], "0.00")
self.assertEqual(relevant_rows[2]["erreichbares Punktetotal absolut"], "29")
self.assertEqual(
relevant_rows[2]["Bestanden-/Nicht-Bestanden Status"], "Begonnen"
)
edoniq_test_result_row = create_edoniq_csv_test_result(relevant_rows[0])
self.assertEqual(edoniq_test_result_row.user_id, TEST_STUDENT1_USER_ID)
self.assertEqual(edoniq_test_result_row.user_points, 21.25)
self.assertEqual(edoniq_test_result_row.max_points, 29)
self.assertEqual(edoniq_test_result_row.passed, True)
self.assertEqual(
edoniq_test_result_row.edoniq_course_release_id, "1689096523730"
)
def test_upsert_edoniq_test_results(self):
row = EdoniqCsvTestResult(
user_id=TEST_STUDENT1_USER_ID,
edoniq_course_release_id="1689096523730",
user_points=21.25,
max_points=29,
passed=True,
)
_ac, created = upsert_edoniq_test_result(row)
self.assertTrue(created)
_ac, created = upsert_edoniq_test_result(row)
self.assertFalse(created)
ac = AssignmentCompletion.objects.get(
assignment_user_id=TEST_STUDENT1_USER_ID,
)
self.assertEqual(ac.evaluation_points, 21.25)
self.assertEqual(ac.evaluation_max_points, 29)
self.assertTrue(ac.evaluation_passed)

View File

@ -0,0 +1,35 @@
# Generated by Django 3.2.20 on 2023-09-29 12:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"learnpath",
"0006_alter_learningcontentfeedback_can_user_self_toggle_course_completion",
),
]
operations = [
migrations.AddField(
model_name="learningcontentedoniqtest",
name="edoniq_extended_course_release_id",
field=models.CharField(blank=True, default="", max_length=255),
),
migrations.AddField(
model_name="learningcontentedoniqtest",
name="edoniq_course_release_id",
field=models.CharField(blank=True, default="", max_length=255),
),
migrations.AddField(
model_name="learningcontentedoniqtest",
name="edoniq_extended_sequence_id",
field=models.CharField(blank=True, default="", max_length=255),
),
migrations.AddField(
model_name="learningcontentedoniqtest",
name="edoniq_sequence_id",
field=models.CharField(blank=True, default="", max_length=255),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.20 on 2023-09-29 14:02
from django.db import migrations
def update_edoniq_ids(apps, schema_editor):
LearningContentEdoniqTest = apps.get_model("learnpath", "LearningContentEdoniqTest")
LearningContentEdoniqTest.objects.filter(
test_url__icontains="1689096523730"
).update(
edoniq_sequence_id="1688059890497",
edoniq_course_release_id="1689096523730",
edoniq_extended_sequence_id="1691151920116",
edoniq_extended_course_release_id="1691157696911",
)
class Migration(migrations.Migration):
dependencies = [
("learnpath", "0007_auto_20230929_1455"),
]
operations = [
migrations.RunPython(update_edoniq_ids),
]

View File

@ -347,10 +347,26 @@ class LearningContentEdoniqTest(LearningContent):
test_url = models.TextField(blank=True)
extended_time_test_url = models.TextField(blank=True)
# Sequenz ID von Edoniq
edoniq_sequence_id = models.CharField(max_length=255, blank=True, default="")
edoniq_extended_sequence_id = models.CharField(
max_length=255, blank=True, default=""
)
# Kursfreigaben ID von Edoniq
edoniq_course_release_id = models.CharField(max_length=255, blank=True, default="")
edoniq_extended_course_release_id = models.CharField(
max_length=255, blank=True, default=""
)
content_panels = LearningContent.content_panels + [
FieldPanel("checkbox_text", classname="Text"),
FieldPanel("test_url", classname="Text"),
FieldPanel("edoniq_course_release_id", classname="Text"),
FieldPanel("edoniq_sequence_id", classname="Text"),
FieldPanel("extended_time_test_url", classname="Text"),
FieldPanel("edoniq_extended_course_release_id", classname="Text"),
FieldPanel("edoniq_extended_sequence_id", classname="Text"),
PageChooserPanel("content_assignment", "assignment.Assignment"),
]

View File

@ -51,18 +51,34 @@
<input type="checkbox" name="create_assignment_completion" value="true">
create_assignment_completion
</label>
<div style="margin: 8px"></div>
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
<label>
<input type="checkbox" name="create_assignment_evaluation" value="true">
create_assignment_evaluation
</label><br>
<label>
evaluation score:
<input type="text" name="assignment_evaluation_scores" placeholder="6,6,6,3,3">
</label>
<div style="margin: 8px"></div>
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
<label>create_edoniq_test_result_data</label><br>
<label>
user points:
<input type="number" name="edoniq_test_user_points" min="0">
</label><br>
<label>
max points:
<input type="number" name="edoniq_test_max_points" min="0">
</label>
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
<label>
<input type="checkbox" name="create_edoniq_test_results" value="true">
create_edoniq_test_results
<input type="checkbox" name="create_feedback_responses" value="true">
create_feedback_responses
</label>
<div style="margin: 8px"></div>
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
<button class="btn">Testdaten zurück setzen</button>
</form>
</div>