Merged in feature/cleanup-assignment-graphql (pull request #151)

Feature/cleanup assignment graphql

Approved-by: Elia Bieri
This commit is contained in:
Daniel Egger 2023-07-07 16:59:40 +00:00
commit d3caff9aa0
27 changed files with 769 additions and 743 deletions

View File

@ -15,6 +15,16 @@ const config: CodegenConfig = {
},
plugins: [],
},
"./src/gql/typenames.ts": {
plugins: ["./codegenGenerateTypenames.js"],
},
},
config: {
enumsAsTypes: true,
namingConvention: {
typeNames: "change-case-all#pascalCase",
enumValues: "change-case-all#upperCase",
},
},
};

View File

@ -0,0 +1,11 @@
// eslint-disable-next-line no-undef
module.exports = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
plugin: (schema, _documents, _config) => {
const typeNames = Object.keys(schema.getTypeMap())
.filter((name) => !name.startsWith("__")) // Filter out built-in types
.map((name) => `export const ${name} = "${name}";\n`);
return typeNames.join("");
},
};

View File

@ -1,13 +1,11 @@
import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
import type { FragmentDefinitionNode } from 'graphql';
import type { Incremental } from './graphql';
import type { ResultOf, TypedDocumentNode as DocumentNode, } from '@graphql-typed-document-node/core';
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
export type FragmentType<TDocumentType extends DocumentNode<any, any>> = TDocumentType extends DocumentNode<
infer TType,
any
>
? [TType] extends [{ ' $fragmentName'?: infer TKey }]
? TType extends { ' $fragmentName'?: infer TKey }
? TKey extends string
? { ' $fragmentRefs'?: { [key in TKey]: TType } }
: never
@ -16,51 +14,35 @@ export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>>
// return non-nullable if `fragmentType` is non-nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
_documentNode: DocumentNode<TType, any>,
fragmentType: FragmentType<DocumentNode<TType, any>>
): TType;
// return nullable if `fragmentType` is nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
_documentNode: DocumentNode<TType, any>,
fragmentType: FragmentType<DocumentNode<TType, any>> | null | undefined
): TType | null | undefined;
// return array of non-nullable if `fragmentType` is array of non-nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
_documentNode: DocumentNode<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentNode<TType, any>>>
): ReadonlyArray<TType>;
// return array of nullable if `fragmentType` is array of nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
_documentNode: DocumentNode<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentNode<TType, any>>> | null | undefined
): ReadonlyArray<TType> | null | undefined;
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
_documentNode: DocumentNode<TType, any>,
fragmentType: FragmentType<DocumentNode<TType, any>> | ReadonlyArray<FragmentType<DocumentNode<TType, any>>> | null | undefined
): TType | ReadonlyArray<TType> | null | undefined {
return fragmentType as any;
}
export function makeFragmentData<
F extends DocumentTypeDecoration<any, any>,
F extends DocumentNode,
FT extends ResultOf<F>
>(data: FT, _fragment: F): FragmentType<F> {
return data as FragmentType<F>;
}
export function isFragmentReady<TQuery, TFrag>(
queryNode: DocumentTypeDecoration<TQuery, any>,
fragmentNode: TypedDocumentNode<TFrag>,
data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined
): data is FragmentType<typeof fragmentNode> {
const deferredFields = (queryNode as { __meta__?: { deferredFields: Record<string, (keyof TFrag)[]> } }).__meta__
?.deferredFields;
if (!deferredFields) return true;
const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined;
const fragName = fragDef?.name?.value;
const fields = (fragName && deferredFields[fragName]) || [];
return fields.length > 0 && fields.every(field => data && field in data);
}
}

View File

@ -10,11 +10,11 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
* 3. It does not support dead code elimination, so it will add unused operations.
*
* Therefore it is highly recommended to use the babel or swc plugin for production.
* Therefore it is highly recommended to use the babel-plugin for production.
*/
const documents = {
"\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n send_feedback(input: $input) {\n feedback_response {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument,
"\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n $completionStatus: String!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\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 $assignmentUserId: ID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\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 query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\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 }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
"\n query courseQuery($courseId: Int!) {\n course(id: $courseId) {\n id\n slug\n title\n category_name\n learning_path {\n id\n }\n }\n }\n": types.CourseQueryDocument,
};
@ -25,7 +25,7 @@ const documents = {
*
* @example
* ```ts
* const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
* const query = gql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
* ```
*
* The query argument is unknown!
@ -40,7 +40,7 @@ export function graphql(source: "\n mutation SendFeedbackMutation($input: SendF
/**
* 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 $assignmentUserId: ID\n $completionStatus: String!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\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 $assignmentUserId: ID\n $completionStatus: String!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\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 $assignmentUserId: ID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\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 $assignmentUserId: ID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\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"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/

View File

@ -5,178 +5,180 @@ export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: { input: string; output: string; }
String: { input: string; output: string; }
Boolean: { input: boolean; output: boolean; }
Int: { input: number; output: number; }
Float: { input: number; output: number; }
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
/**
* The `DateTime` scalar type represents a DateTime
* value as specified by
* [iso8601](https://en.wikipedia.org/wiki/ISO_8601).
*/
DateTime: { input: any; output: any; }
DateTime: any;
/**
* The `GenericScalar` scalar type represents a generic
* GraphQL scalar value that could be:
* String, Boolean, Int, Float, List or Object.
*/
GenericScalar: { input: any; output: any; }
JSONStreamField: { input: any; output: any; }
GenericScalar: any;
JSONStreamField: any;
/**
* Allows use of a JSON String for input / output from the GraphQL schema.
*
* Use of this type is *not recommended* as you lose the benefits of having a defined, static
* schema (one of the key benefits of GraphQL).
*/
JSONString: { input: any; output: any; }
JSONString: any;
};
/** An enumeration. */
export enum AssignmentAssignmentAssignmentTypeChoices {
export type AssignmentAssignmentAssignmentTypeChoices =
/** CASEWORK */
Casework = 'CASEWORK',
| 'CASEWORK'
/** PREP_ASSIGNMENT */
PrepAssignment = 'PREP_ASSIGNMENT',
| 'PREP_ASSIGNMENT'
/** REFLECTION */
Reflection = 'REFLECTION'
}
| 'REFLECTION';
/** An enumeration. */
export enum AssignmentAssignmentCompletionCompletionStatusChoices {
export type AssignmentAssignmentCompletionCompletionStatusChoices =
/** EVALUATION_IN_PROGRESS */
EvaluationInProgress = 'EVALUATION_IN_PROGRESS',
| 'EVALUATION_IN_PROGRESS'
/** EVALUATION_SUBMITTED */
EvaluationSubmitted = 'EVALUATION_SUBMITTED',
| 'EVALUATION_SUBMITTED'
/** IN_PROGRESS */
InProgress = 'IN_PROGRESS',
| 'IN_PROGRESS'
/** SUBMITTED */
Submitted = 'SUBMITTED'
}
| 'SUBMITTED';
export type AssignmentCompletionMutation = {
__typename?: 'AssignmentCompletionMutation';
assignment_completion?: Maybe<AssignmentCompletionType>;
assignment_completion?: Maybe<AssignmentCompletionObjectType>;
};
export type AssignmentCompletionType = {
__typename?: 'AssignmentCompletionType';
additional_json_data: Scalars['JSONString']['output'];
assignment: AssignmentType;
export type AssignmentCompletionObjectType = {
__typename?: 'AssignmentCompletionObjectType';
additional_json_data: Scalars['JSONString'];
assignment: AssignmentObjectType;
assignment_user: UserType;
completion_data?: Maybe<Scalars['GenericScalar']['output']>;
completion_data?: Maybe<Scalars['GenericScalar']>;
completion_status: AssignmentAssignmentCompletionCompletionStatusChoices;
created_at: Scalars['DateTime']['output'];
evaluation_grade?: Maybe<Scalars['Float']['output']>;
evaluation_points?: Maybe<Scalars['Float']['output']>;
evaluation_submitted_at?: Maybe<Scalars['DateTime']['output']>;
created_at: Scalars['DateTime'];
evaluation_grade?: Maybe<Scalars['Float']>;
evaluation_points?: Maybe<Scalars['Float']>;
evaluation_submitted_at?: Maybe<Scalars['DateTime']>;
evaluation_user?: Maybe<UserType>;
id: Scalars['ID']['output'];
submitted_at?: Maybe<Scalars['DateTime']['output']>;
updated_at: Scalars['DateTime']['output'];
};
export type AssignmentType = CoursePageInterface & {
__typename?: 'AssignmentType';
assignment_type: AssignmentAssignmentAssignmentTypeChoices;
content_type?: Maybe<Scalars['String']['output']>;
/** Zeitaufwand als Text */
effort_required: Scalars['String']['output'];
/** Beschreibung der Bewertung */
evaluation_description: Scalars['String']['output'];
/** URL zum Beurteilungsinstrument */
evaluation_document_url: Scalars['String']['output'];
evaluation_tasks?: Maybe<Scalars['JSONStreamField']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
/** Erläuterung der Ausgangslage */
intro_text: Scalars['String']['output'];
live?: Maybe<Scalars['Boolean']['output']>;
performance_objectives?: Maybe<Scalars['JSONStreamField']['output']>;
slug?: Maybe<Scalars['String']['output']>;
tasks?: Maybe<Scalars['JSONStreamField']['output']>;
title?: Maybe<Scalars['String']['output']>;
translation_key?: Maybe<Scalars['String']['output']>;
id: Scalars['ID'];
submitted_at?: Maybe<Scalars['DateTime']>;
updated_at: Scalars['DateTime'];
};
/** An enumeration. */
export enum CoreUserLanguageChoices {
export type AssignmentCompletionStatus =
| 'EVALUATION_IN_PROGRESS'
| 'EVALUATION_SUBMITTED'
| 'IN_PROGRESS'
| 'SUBMITTED';
export type AssignmentObjectType = CoursePageInterface & {
__typename?: 'AssignmentObjectType';
assignment_type: AssignmentAssignmentAssignmentTypeChoices;
content_type?: Maybe<Scalars['String']>;
/** Zeitaufwand als Text */
effort_required: Scalars['String'];
/** Beschreibung der Bewertung */
evaluation_description: Scalars['String'];
/** URL zum Beurteilungsinstrument */
evaluation_document_url: Scalars['String'];
evaluation_tasks?: Maybe<Scalars['JSONStreamField']>;
frontend_url?: Maybe<Scalars['String']>;
id?: Maybe<Scalars['ID']>;
/** Erläuterung der Ausgangslage */
intro_text: Scalars['String'];
live?: Maybe<Scalars['Boolean']>;
performance_objectives?: Maybe<Scalars['JSONStreamField']>;
slug?: Maybe<Scalars['String']>;
tasks?: Maybe<Scalars['JSONStreamField']>;
title?: Maybe<Scalars['String']>;
translation_key?: Maybe<Scalars['String']>;
};
/** An enumeration. */
export type CoreUserLanguageChoices =
/** Deutsch */
De = 'DE',
| 'DE'
/** Français */
Fr = 'FR',
| 'FR'
/** Italiano */
It = 'IT'
}
| 'IT';
export type CoursePageInterface = {
content_type?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
live?: Maybe<Scalars['Boolean']['output']>;
slug?: Maybe<Scalars['String']['output']>;
title?: Maybe<Scalars['String']['output']>;
translation_key?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']>;
frontend_url?: Maybe<Scalars['String']>;
id?: Maybe<Scalars['ID']>;
live?: Maybe<Scalars['Boolean']>;
slug?: Maybe<Scalars['String']>;
title?: Maybe<Scalars['String']>;
translation_key?: Maybe<Scalars['String']>;
};
export type CourseType = {
__typename?: 'CourseType';
category_name: Scalars['String']['output'];
id: Scalars['ID']['output'];
category_name: Scalars['String'];
id: Scalars['ID'];
learning_path?: Maybe<LearningPathType>;
slug: Scalars['String']['output'];
title: Scalars['String']['output'];
slug: Scalars['String'];
title: Scalars['String'];
};
export type ErrorType = {
__typename?: 'ErrorType';
field: Scalars['String']['output'];
messages: Array<Scalars['String']['output']>;
field: Scalars['String'];
messages: Array<Scalars['String']>;
};
export type FeedbackResponse = Node & {
__typename?: 'FeedbackResponse';
created_at: Scalars['DateTime']['output'];
data?: Maybe<Scalars['GenericScalar']['output']>;
created_at: Scalars['DateTime'];
data?: Maybe<Scalars['GenericScalar']>;
/** The ID of the object */
id: Scalars['ID']['output'];
id: Scalars['ID'];
};
export type LearningPathType = CoursePageInterface & {
__typename?: 'LearningPathType';
content_type?: Maybe<Scalars['String']['output']>;
depth: Scalars['Int']['output'];
draft_title: Scalars['String']['output'];
expire_at?: Maybe<Scalars['DateTime']['output']>;
expired: Scalars['Boolean']['output'];
first_published_at?: Maybe<Scalars['DateTime']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
go_live_at?: Maybe<Scalars['DateTime']['output']>;
has_unpublished_changes: Scalars['Boolean']['output'];
id?: Maybe<Scalars['ID']['output']>;
last_published_at?: Maybe<Scalars['DateTime']['output']>;
latest_revision_created_at?: Maybe<Scalars['DateTime']['output']>;
live?: Maybe<Scalars['Boolean']['output']>;
locked: Scalars['Boolean']['output'];
locked_at?: Maybe<Scalars['DateTime']['output']>;
content_type?: Maybe<Scalars['String']>;
depth: Scalars['Int'];
draft_title: Scalars['String'];
expire_at?: Maybe<Scalars['DateTime']>;
expired: Scalars['Boolean'];
first_published_at?: Maybe<Scalars['DateTime']>;
frontend_url?: Maybe<Scalars['String']>;
go_live_at?: Maybe<Scalars['DateTime']>;
has_unpublished_changes: Scalars['Boolean'];
id?: Maybe<Scalars['ID']>;
last_published_at?: Maybe<Scalars['DateTime']>;
latest_revision_created_at?: Maybe<Scalars['DateTime']>;
live?: Maybe<Scalars['Boolean']>;
locked: Scalars['Boolean'];
locked_at?: Maybe<Scalars['DateTime']>;
locked_by?: Maybe<UserType>;
numchild: Scalars['Int']['output'];
numchild: Scalars['Int'];
owner?: Maybe<UserType>;
path: Scalars['String']['output'];
path: Scalars['String'];
/** Die informative Beschreibung, dargestellt in Suchmaschinen-Ergebnissen unter der Überschrift. */
search_description: Scalars['String']['output'];
search_description: Scalars['String'];
/** Der Titel der Seite, dargestellt in Suchmaschinen-Ergebnissen als die verlinkte Überschrift. */
seo_title: Scalars['String']['output'];
seo_title: Scalars['String'];
/** Ob ein Link zu dieser Seite in automatisch generierten Menüs auftaucht. */
show_in_menus: Scalars['Boolean']['output'];
slug?: Maybe<Scalars['String']['output']>;
title?: Maybe<Scalars['String']['output']>;
translation_key?: Maybe<Scalars['String']['output']>;
url_path: Scalars['String']['output'];
show_in_menus: Scalars['Boolean'];
slug?: Maybe<Scalars['String']>;
title?: Maybe<Scalars['String']>;
translation_key?: Maybe<Scalars['String']>;
url_path: Scalars['String'];
};
export type Mutation = {
@ -186,62 +188,62 @@ export type Mutation = {
};
export type MutationSend_FeedbackArgs = {
export type MutationSendFeedbackArgs = {
input: SendFeedbackInput;
};
export type MutationUpsert_Assignment_CompletionArgs = {
assignment_id: Scalars['ID']['input'];
assignment_user_id?: InputMaybe<Scalars['ID']['input']>;
completion_data_string?: InputMaybe<Scalars['String']['input']>;
completion_status?: InputMaybe<Scalars['String']['input']>;
course_session_id: Scalars['ID']['input'];
evaluation_grade?: InputMaybe<Scalars['Float']['input']>;
evaluation_points?: InputMaybe<Scalars['Float']['input']>;
export type MutationUpsertAssignmentCompletionArgs = {
assignment_id: Scalars['ID'];
assignment_user_id?: InputMaybe<Scalars['ID']>;
completion_data_string?: InputMaybe<Scalars['String']>;
completion_status?: InputMaybe<AssignmentCompletionStatus>;
course_session_id: Scalars['ID'];
evaluation_grade?: InputMaybe<Scalars['Float']>;
evaluation_points?: InputMaybe<Scalars['Float']>;
};
/** An object with an ID */
export type Node = {
/** The ID of the object */
id: Scalars['ID']['output'];
id: Scalars['ID'];
};
export type Query = {
__typename?: 'Query';
assignment?: Maybe<AssignmentType>;
assignment_completion?: Maybe<AssignmentCompletionType>;
assignment?: Maybe<AssignmentObjectType>;
assignment_completion?: Maybe<AssignmentCompletionObjectType>;
course?: Maybe<CourseType>;
};
export type QueryAssignmentArgs = {
id?: InputMaybe<Scalars['ID']['input']>;
slug?: InputMaybe<Scalars['String']['input']>;
id?: InputMaybe<Scalars['ID']>;
slug?: InputMaybe<Scalars['String']>;
};
export type QueryAssignment_CompletionArgs = {
assignment_id: Scalars['ID']['input'];
assignment_user_id?: InputMaybe<Scalars['ID']['input']>;
course_session_id: Scalars['ID']['input'];
export type QueryAssignmentCompletionArgs = {
assignment_id: Scalars['ID'];
assignment_user_id?: InputMaybe<Scalars['ID']>;
course_session_id: Scalars['ID'];
};
export type QueryCourseArgs = {
id?: InputMaybe<Scalars['Int']['input']>;
id?: InputMaybe<Scalars['Int']>;
};
export type SendFeedbackInput = {
clientMutationId?: InputMaybe<Scalars['String']['input']>;
course_session: Scalars['Int']['input'];
data?: InputMaybe<Scalars['GenericScalar']['input']>;
page: Scalars['String']['input'];
clientMutationId?: InputMaybe<Scalars['String']>;
course_session: Scalars['Int'];
data?: InputMaybe<Scalars['GenericScalar']>;
page: Scalars['String'];
};
export type SendFeedbackPayload = {
__typename?: 'SendFeedbackPayload';
clientMutationId?: Maybe<Scalars['String']['output']>;
clientMutationId?: Maybe<Scalars['String']>;
/** May contain more than one error for same field. */
errors?: Maybe<Array<Maybe<ErrorType>>>;
feedback_response?: Maybe<FeedbackResponse>;
@ -249,14 +251,14 @@ export type SendFeedbackPayload = {
export type UserType = {
__typename?: 'UserType';
avatar_url: Scalars['String']['output'];
email: Scalars['String']['output'];
first_name: Scalars['String']['output'];
id: Scalars['ID']['output'];
avatar_url: Scalars['String'];
email: Scalars['String'];
first_name: Scalars['String'];
id: Scalars['ID'];
language: CoreUserLanguageChoices;
last_name: Scalars['String']['output'];
last_name: Scalars['String'];
/** Erforderlich. 150 Zeichen oder weniger. Nur Buchstaben, Ziffern und @/./+/-/_. */
username: Scalars['String']['output'];
username: Scalars['String'];
};
export type SendFeedbackMutationMutationVariables = Exact<{
@ -267,29 +269,29 @@ export type SendFeedbackMutationMutationVariables = Exact<{
export type SendFeedbackMutationMutation = { __typename?: 'Mutation', send_feedback?: { __typename?: 'SendFeedbackPayload', feedback_response?: { __typename?: 'FeedbackResponse', id: string } | null, errors?: Array<{ __typename?: 'ErrorType', field: string, messages: Array<string> } | null> | null } | null };
export type UpsertAssignmentCompletionMutationVariables = Exact<{
assignmentId: Scalars['ID']['input'];
courseSessionId: Scalars['ID']['input'];
assignmentUserId?: InputMaybe<Scalars['ID']['input']>;
completionStatus: Scalars['String']['input'];
completionDataString: Scalars['String']['input'];
evaluationGrade?: InputMaybe<Scalars['Float']['input']>;
evaluationPoints?: InputMaybe<Scalars['Float']['input']>;
assignmentId: Scalars['ID'];
courseSessionId: Scalars['ID'];
assignmentUserId?: InputMaybe<Scalars['ID']>;
completionStatus: AssignmentCompletionStatus;
completionDataString: Scalars['String'];
evaluationGrade?: InputMaybe<Scalars['Float']>;
evaluationPoints?: InputMaybe<Scalars['Float']>;
}>;
export type UpsertAssignmentCompletionMutation = { __typename?: 'Mutation', upsert_assignment_completion?: { __typename?: 'AssignmentCompletionMutation', assignment_completion?: { __typename?: 'AssignmentCompletionType', id: string, 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: string, 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 AssignmentCompletionQueryQueryVariables = Exact<{
assignmentId: Scalars['ID']['input'];
courseSessionId: Scalars['ID']['input'];
assignmentUserId?: InputMaybe<Scalars['ID']['input']>;
assignmentId: Scalars['ID'];
courseSessionId: Scalars['ID'];
assignmentUserId?: InputMaybe<Scalars['ID']>;
}>;
export type AssignmentCompletionQueryQuery = { __typename?: 'Query', assignment?: { __typename?: 'AssignmentType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, 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 } | null, assignment_completion?: { __typename?: 'AssignmentCompletionType', id: string, 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: string } | null, assignment_user: { __typename?: 'UserType', id: string } } | null };
export type AssignmentCompletionQueryQuery = { __typename?: 'Query', assignment?: { __typename?: 'AssignmentObjectType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, 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 } | null, assignment_completion?: { __typename?: 'AssignmentCompletionObjectType', id: string, 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: string } | null, assignment_user: { __typename?: 'UserType', id: string } } | null };
export type CourseQueryQueryVariables = Exact<{
courseId: Scalars['Int']['input'];
courseId: Scalars['Int'];
}>;
@ -297,6 +299,6 @@ export type CourseQueryQuery = { __typename?: 'Query', course?: { __typename?: '
export const SendFeedbackMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendFeedbackMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SendFeedbackInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"send_feedback"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feedback_response"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"messages"}}]}}]}}]}}]} as unknown as DocumentNode<SendFeedbackMutationMutation, SendFeedbackMutationMutationVariables>;
export const 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":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"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"}}}],"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":"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"}}}],"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":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"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"}}}],"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":"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"}}}],"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 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":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"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":"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":"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"}}}],"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"}}]}}]}}]} 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>;

View File

@ -1,7 +1,7 @@
type Query {
course(id: Int): CourseType
assignment(id: ID, slug: String): AssignmentType
assignment_completion(assignment_id: ID!, course_session_id: ID!, assignment_user_id: ID): AssignmentCompletionType
assignment(id: ID, slug: String): AssignmentObjectType
assignment_completion(assignment_id: ID!, course_session_id: ID!, assignment_user_id: ID): AssignmentCompletionObjectType
}
type CourseType {
@ -96,7 +96,7 @@ enum CoreUserLanguageChoices {
IT
}
type AssignmentType implements CoursePageInterface {
type AssignmentObjectType implements CoursePageInterface {
assignment_type: AssignmentAssignmentAssignmentTypeChoices!
"""Erläuterung der Ausgangslage"""
@ -136,7 +136,7 @@ enum AssignmentAssignmentAssignmentTypeChoices {
scalar JSONStreamField
type AssignmentCompletionType {
type AssignmentCompletionObjectType {
id: ID!
created_at: DateTime!
updated_at: DateTime!
@ -146,7 +146,7 @@ type AssignmentCompletionType {
evaluation_grade: Float
evaluation_points: Float
assignment_user: UserType!
assignment: AssignmentType!
assignment: AssignmentObjectType!
completion_status: AssignmentAssignmentCompletionCompletionStatusChoices!
completion_data: GenericScalar
additional_json_data: JSONString!
@ -184,7 +184,7 @@ scalar JSONString
type Mutation {
send_feedback(input: SendFeedbackInput!): SendFeedbackPayload
upsert_assignment_completion(assignment_id: ID!, assignment_user_id: ID, completion_data_string: String, completion_status: String, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float): AssignmentCompletionMutation
upsert_assignment_completion(assignment_id: ID!, assignment_user_id: ID, completion_data_string: String, completion_status: AssignmentCompletionStatus, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float): AssignmentCompletionMutation
}
type SendFeedbackPayload {
@ -221,5 +221,13 @@ input SendFeedbackInput {
}
type AssignmentCompletionMutation {
assignment_completion: AssignmentCompletionType
assignment_completion: AssignmentCompletionObjectType
}
"""An enumeration."""
enum AssignmentCompletionStatus {
IN_PROGRESS
SUBMITTED
EVALUATION_IN_PROGRESS
EVALUATION_SUBMITTED
}

View File

@ -0,0 +1,27 @@
export const AssignmentAssignmentAssignmentTypeChoices = "AssignmentAssignmentAssignmentTypeChoices";
export const AssignmentAssignmentCompletionCompletionStatusChoices = "AssignmentAssignmentCompletionCompletionStatusChoices";
export const AssignmentCompletionMutation = "AssignmentCompletionMutation";
export const AssignmentCompletionObjectType = "AssignmentCompletionObjectType";
export const AssignmentCompletionStatus = "AssignmentCompletionStatus";
export const AssignmentObjectType = "AssignmentObjectType";
export const Boolean = "Boolean";
export const CoreUserLanguageChoices = "CoreUserLanguageChoices";
export const CoursePageInterface = "CoursePageInterface";
export const CourseType = "CourseType";
export const DateTime = "DateTime";
export const ErrorType = "ErrorType";
export const FeedbackResponse = "FeedbackResponse";
export const Float = "Float";
export const GenericScalar = "GenericScalar";
export const ID = "ID";
export const Int = "Int";
export const JSONStreamField = "JSONStreamField";
export const JSONString = "JSONString";
export const LearningPathType = "LearningPathType";
export const Mutation = "Mutation";
export const Node = "Node";
export const Query = "Query";
export const SendFeedbackInput = "SendFeedbackInput";
export const SendFeedbackPayload = "SendFeedbackPayload";
export const String = "String";
export const UserType = "UserType";

View File

@ -5,6 +5,10 @@ import schema from "../gql/minifiedSchema.json";
import { devtoolsExchange } from "@urql/devtools";
import { cacheExchange } from "@urql/exchange-graphcache";
import { Client, fetchExchange } from "@urql/vue";
import {
AssignmentCompletionMutation,
AssignmentCompletionObjectType,
} from "../gql/typenames";
export const graphqlClient = new Client({
url: import.meta.env.VITE_GRAPHQL_URL || "/server/graphql/",
@ -28,13 +32,13 @@ export const graphqlClient = new Client({
const id = info.variables.id;
if (id) {
const completionData = cache.resolve(
{ __typename: "AssignmentCompletionType", id: "1" },
{ __typename: AssignmentCompletionObjectType, id: "1" },
"completion_data"
);
return {
__typename: "AssignmentCompletionMutation",
__typename: AssignmentCompletionMutation,
assignment_completion: {
__typename: "AssignmentCompletionType",
__typename: AssignmentCompletionObjectType,
id: id,
completion_data: Object.assign(
{},

View File

@ -5,7 +5,7 @@ export const UPSERT_ASSIGNMENT_COMPLETION_MUTATION = graphql(`
$assignmentId: ID!
$courseSessionId: ID!
$assignmentUserId: ID
$completionStatus: String!
$completionStatus: AssignmentCompletionStatus!
$completionDataString: String!
$evaluationGrade: Float
$evaluationPoints: Float

View File

@ -1,3 +1,4 @@
import type { AssignmentCompletionStatus as AssignmentCompletionStatusGenerated } from "@/gql/graphql";
import type { Circle } from "@/services/circle";
import type { Component } from "vue";
@ -495,12 +496,7 @@ export interface Notification {
course: string | null;
}
export type AssignmentCompletionStatus =
| "unknwown"
| "IN_PROGRESS"
| "SUBMITTED"
| "EVALUATION_IN_PROGRESS"
| "EVALUATION_SUBMITTED";
export type AssignmentCompletionStatus = AssignmentCompletionStatusGenerated;
export interface UserDataText {
text: string;

View File

@ -372,6 +372,11 @@ if IT_DJANGO_LOGGING_CONF == "IT_DJANGO_LOGGING_CONF_CONSOLE_COLOR":
"level": "INFO",
"propagate": False,
},
"wagtail": {
"handlers": ["default"],
"level": "WARNING" if IT_LOCAL_HIDE_DJANGO_SERVER_LOGS else "INFO",
"propagate": False,
},
"vbv_lernwelt": {
"handlers": ["default"],
"level": "DEBUG",

View File

@ -11,13 +11,7 @@ from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
from ratelimit.exceptions import Ratelimited
from vbv_lernwelt.assignment.views import (
evaluate_assignment_completion,
request_assignment_completion,
request_assignment_completion_for_user,
request_assignment_completion_status,
upsert_user_assignment_completion,
)
from vbv_lernwelt.assignment.views import request_assignment_completion_status
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
from vbv_lernwelt.core.schema import schema
from vbv_lernwelt.core.views import (
@ -120,21 +114,10 @@ urlpatterns = [
name="request_course_completion_for_user"),
# assignment
path(r"api/assignment/upsert/", upsert_user_assignment_completion,
name="upsert_user_assignment_completion"),
path(r"api/assignment/evaluate/", evaluate_assignment_completion,
name="evaluate_assignment_completion"),
path(r"api/assignment/<signed_int:assignment_id>/<signed_int:course_session_id>/",
request_assignment_completion,
name="request_assignment_completion"),
path(
r"api/assignment/<signed_int:assignment_id>/<signed_int:course_session_id>/status/",
request_assignment_completion_status,
name="request_assignment_completion_status"),
path(
r"api/assignment/<signed_int:assignment_id>/<signed_int:course_session_id>/<signed_int:user_id>/",
request_assignment_completion_for_user,
name="request_assignment_completion_for_user"),
# documents
path(r'api/core/document/start/', document_upload_start,

View File

@ -4,8 +4,8 @@ import graphene
import structlog
from rest_framework.exceptions import PermissionDenied
from vbv_lernwelt.assignment.graphql.types import AssignmentCompletionType
from vbv_lernwelt.assignment.models import Assignment
from vbv_lernwelt.assignment.graphql.types import AssignmentCompletionObjectType
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseSession
@ -15,14 +15,16 @@ logger = structlog.get_logger(__name__)
class AssignmentCompletionMutation(graphene.Mutation):
assignment_completion = graphene.Field(AssignmentCompletionType)
assignment_completion = graphene.Field(AssignmentCompletionObjectType)
class Input:
class Arguments:
assignment_id = graphene.ID(required=True)
course_session_id = graphene.ID(required=True)
assignment_user_id = graphene.ID()
completion_status = graphene.String()
completion_status = graphene.Argument(
graphene.Enum.from_enum(AssignmentCompletionStatus)
)
completion_data_string = graphene.String()
evaluation_grade = graphene.Float()
@ -36,7 +38,7 @@ class AssignmentCompletionMutation(graphene.Mutation):
assignment_id,
course_session_id,
assignment_user_id=None,
completion_status="IN_PROGRESS",
completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS,
completion_data_string="{}",
evaluation_grade=None,
evaluation_points=None,
@ -63,7 +65,10 @@ class AssignmentCompletionMutation(graphene.Mutation):
evaluation_data = {}
if completion_status in ["EVALUATION_SUBMITTED", "EVALUATION_IN_PROGRESS"]:
if completion_status in [
AssignmentCompletionStatus.EVALUATION_SUBMITTED,
AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
]:
if not is_course_session_expert(info.context.user, course_session_id):
raise PermissionDenied()

View File

@ -2,8 +2,8 @@ import graphene
from rest_framework.exceptions import PermissionDenied
from vbv_lernwelt.assignment.graphql.types import (
AssignmentCompletionType,
AssignmentType,
AssignmentCompletionObjectType,
AssignmentObjectType,
)
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletion
from vbv_lernwelt.course.graphql.types import resolve_course_page
@ -13,14 +13,14 @@ from vbv_lernwelt.course.permissions import has_course_access, is_course_session
class AssignmentQuery(object):
assignment = graphene.Field(
AssignmentType, id=graphene.ID(), slug=graphene.String()
AssignmentObjectType, id=graphene.ID(), slug=graphene.String()
)
def resolve_assignment(root, info, id=None, slug=None):
return resolve_course_page(Assignment, root, info, id=id, slug=slug)
assignment_completion = graphene.Field(
AssignmentCompletionType,
AssignmentCompletionObjectType,
assignment_id=graphene.ID(required=True),
course_session_id=graphene.ID(required=True),
assignment_user_id=graphene.ID(required=False),

View File

@ -6,7 +6,7 @@ from vbv_lernwelt.core.graphql.types import JSONStreamField
from vbv_lernwelt.course.schema import CoursePageInterface
class AssignmentType(DjangoObjectType):
class AssignmentObjectType(DjangoObjectType):
tasks = JSONStreamField()
evaluation_tasks = JSONStreamField()
performance_objectives = JSONStreamField()
@ -23,7 +23,7 @@ class AssignmentType(DjangoObjectType):
)
class AssignmentCompletionType(DjangoObjectType):
class AssignmentCompletionObjectType(DjangoObjectType):
completion_data = GenericScalar()
class Meta:

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.13 on 2023-06-28 14:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("assignment", "0004_assignment_assignment_type"),
]
operations = [
migrations.AlterField(
model_name="assignmentcompletionauditlog",
name="completion_status",
field=models.CharField(
choices=[
("IN_PROGRESS", "IN_PROGRESS"),
("SUBMITTED", "SUBMITTED"),
("EVALUATION_IN_PROGRESS", "EVALUATION_IN_PROGRESS"),
("EVALUATION_SUBMITTED", "EVALUATION_SUBMITTED"),
],
default="IN_PROGRESS",
max_length=255,
),
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 3.2.13 on 2023-06-28 14:16
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("learnpath", "0007_learningunit_title_hidden"),
("assignment", "0005_alter_assignmentcompletionauditlog_completion_status"),
]
operations = [
migrations.RemoveConstraint(
model_name="assignmentcompletion",
name="assignment_completion_unique_user_assignment_course_session",
),
migrations.AddField(
model_name="assignmentcompletion",
name="circle",
field=models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="learnpath.circle",
),
),
migrations.AddConstraint(
model_name="assignmentcompletion",
constraint=models.UniqueConstraint(
fields=("assignment_user", "assignment", "course_session", "circle"),
name="assignment_completion_unique_user_assignment_course_session",
),
),
]

View File

@ -107,14 +107,10 @@ class EvaluationTaskBlock(blocks.StructBlock):
label = "Beurteilungskriterium"
AssignmentType = Enum(
"AssignmentType",
[
"CASEWORK", # Geleitete Fallarbeit
"PREP_ASSIGNMENT", # Vorbereitungsauftrag
"REFLECTION", # Reflexion
],
)
class AssignmentType(Enum):
CASEWORK = "CASEWORK" # Geleitete Fallarbeit
PREP_ASSIGNMENT = "PREP_ASSIGNMENT" # Vorbereitungsauftrag
REFLECTION = "REFLECTION" # Reflexion
class Assignment(CourseBasePage):
@ -130,8 +126,8 @@ class Assignment(CourseBasePage):
assignment_type = models.CharField(
max_length=50,
choices=[(tag.name, tag.name) for tag in AssignmentType],
default=AssignmentType.CASEWORK.name,
choices=[(tag.value, tag.value) for tag in AssignmentType],
default=AssignmentType.CASEWORK.value,
)
intro_text = RichTextField(
@ -242,14 +238,17 @@ class Assignment(CourseBasePage):
return self.filter_user_subtasks() + self.get_evaluation_tasks()
AssignmentCompletionStatus = Enum(
"AssignmentCompletionStatus",
["IN_PROGRESS", "SUBMITTED", "EVALUATION_IN_PROGRESS", "EVALUATION_SUBMITTED"],
)
class AssignmentCompletionStatus(Enum):
IN_PROGRESS = "IN_PROGRESS"
SUBMITTED = "SUBMITTED"
EVALUATION_IN_PROGRESS = "EVALUATION_IN_PROGRESS"
EVALUATION_SUBMITTED = "EVALUATION_SUBMITTED"
def is_valid_assignment_completion_status(status):
return status in AssignmentCompletionStatus.__members__
def is_valid_assignment_completion_status(
completion_status: AssignmentCompletionStatus,
):
return completion_status.value in AssignmentCompletionStatus.__members__
class AssignmentCompletion(models.Model):
@ -271,11 +270,18 @@ class AssignmentCompletion(models.Model):
assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)
circle = models.ForeignKey(
"learnpath.Circle",
on_delete=models.CASCADE,
null=True,
blank=True,
default=None,
)
completion_status = models.CharField(
max_length=255,
choices=[(acs.name, acs.name) for acs in AssignmentCompletionStatus],
default="IN_PROGRESS",
choices=[(acs.value, acs.value) for acs in AssignmentCompletionStatus],
default=AssignmentCompletionStatus.IN_PROGRESS.value,
)
completion_data = models.JSONField(default=dict)
@ -284,7 +290,7 @@ class AssignmentCompletion(models.Model):
class Meta:
constraints = [
UniqueConstraint(
fields=["assignment_user", "assignment", "course_session"],
fields=["assignment_user", "assignment", "course_session", "circle"],
name="assignment_completion_unique_user_assignment_course_session",
)
]

View File

@ -1,3 +0,0 @@
import structlog
logger = structlog.get_logger(__name__)

View File

@ -1,5 +1,4 @@
from copy import deepcopy
from typing import Type
from django.utils import timezone
from rest_framework import serializers
@ -21,7 +20,7 @@ def update_assignment_completion(
assignment: Assignment,
course_session: CourseSession,
completion_data=None,
completion_status: Type[AssignmentCompletionStatus] = "IN_PROGRESS",
completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS,
evaluation_user: User | None = None,
evaluation_grade: float | None = None,
evaluation_points: float | None = None,
@ -58,12 +57,14 @@ def update_assignment_completion(
if not is_valid_assignment_completion_status(completion_status):
raise serializers.ValidationError(
{"completion_status": f"Invalid completion status {completion_status}"}
{
"completion_status": f"Invalid completion status {completion_status.value}"
}
)
if validate_completion_status_change:
# TODO: check time?
if completion_status == "SUBMITTED":
if completion_status == AssignmentCompletionStatus.SUBMITTED:
if ac.completion_status in [
"SUBMITTED",
"EVALUATION_IN_PROGRESS",
@ -74,7 +75,7 @@ def update_assignment_completion(
"completion_status": f"Cannot update completion status from {ac.completion_status} to SUBMITTED"
}
)
elif completion_status == "EVALUATION_SUBMITTED":
elif completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED:
if ac.completion_status == "EVALUATION_SUBMITTED":
raise serializers.ValidationError(
{
@ -82,14 +83,20 @@ def update_assignment_completion(
}
)
if completion_status == "IN_PROGRESS" and ac.completion_status != "IN_PROGRESS":
if (
completion_status == AssignmentCompletionStatus.IN_PROGRESS
and ac.completion_status != "IN_PROGRESS"
):
raise serializers.ValidationError(
{
"completion_status": f"Cannot set completion status to IN_PROGRESS when it is {ac.completion_status}"
}
)
if completion_status in ["EVALUATION_SUBMITTED", "EVALUATION_IN_PROGRESS"]:
if completion_status in [
AssignmentCompletionStatus.EVALUATION_SUBMITTED,
AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
]:
if evaluation_user is None:
raise serializers.ValidationError(
{
@ -98,7 +105,7 @@ def update_assignment_completion(
)
ac.evaluation_user = evaluation_user
if completion_status == "EVALUATION_SUBMITTED":
if completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED:
if evaluation_grade is None:
raise serializers.ValidationError(
{
@ -115,12 +122,12 @@ def update_assignment_completion(
ac.evaluation_grade = evaluation_grade
ac.evaluation_points = evaluation_points
if completion_status == "SUBMITTED":
if completion_status == AssignmentCompletionStatus.SUBMITTED:
ac.submitted_at = timezone.now()
elif completion_status == "EVALUATION_SUBMITTED":
elif completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED:
ac.evaluation_submitted_at = timezone.now()
ac.completion_status = completion_status
ac.completion_status = completion_status.value
# TODO: make more validation of the provided input -> maybe with graphql
completion_data = _remove_unknown_entries(assignment, completion_data)
@ -140,13 +147,16 @@ def update_assignment_completion(
ac.save()
if completion_status in ["EVALUATION_SUBMITTED", "SUBMITTED"]:
if completion_status in [
AssignmentCompletionStatus.EVALUATION_SUBMITTED,
AssignmentCompletionStatus.SUBMITTED,
]:
acl = AssignmentCompletionAuditLog.objects.create(
assignment_user=assignment_user,
assignment=assignment,
course_session=course_session,
evaluation_user=evaluation_user,
completion_status=completion_status,
completion_status=completion_status.value,
assignment_user_email=assignment_user.email,
assignment_slug=assignment.slug,
completion_data=deepcopy(ac.completion_data),

View File

@ -1,299 +0,0 @@
import json
from django.utils import timezone
from rest_framework.test import APITestCase
from vbv_lernwelt.assignment.creators.create_assignments import (
create_uk_fahrzeug_casework,
)
from vbv_lernwelt.assignment.models import (
AssignmentCompletion,
AssignmentCompletionAuditLog,
)
from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.core.models import User
from vbv_lernwelt.core.utils import find_first
from vbv_lernwelt.course.consts import COURSE_TEST_ID
from vbv_lernwelt.course.creators.test_course import create_test_course
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
class AssignmentApiTestCase(APITestCase):
def setUp(self) -> None:
create_default_users()
create_test_course(include_vv=False)
self.assignment = create_uk_fahrzeug_casework(course_id=COURSE_TEST_ID)
self.assignment_subtasks = self.assignment.filter_user_subtasks()
self.cs = CourseSession.objects.create(
course_id=COURSE_TEST_ID,
title="Test Lehrgang Session",
)
self.student = User.objects.get(username="student")
self.student_csu = CourseSessionUser.objects.create(
course_session=self.cs,
user=self.student,
)
self.expert = User.objects.get(username="admin")
self.expert_csu = CourseSessionUser.objects.create(
course_session=self.cs, user=self.expert, role="EXPERT"
)
def test_student_can_upsertAssignmentCompletion(self):
self.client.login(username="student", password="test")
url = f"/api/assignment/upsert/"
user_text_input = find_first(
self.assignment_subtasks, pred=lambda x: x["type"] == "user_text_input"
)
response = self.client.post(
url,
{
"assignment_id": self.assignment.id,
"course_session_id": self.cs.id,
"completion_status": "IN_PROGRESS",
"completion_data": {
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
},
},
format="json",
)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(response_json["assignment_user"], self.student.id)
self.assertEqual(response_json["assignment"], self.assignment.id)
self.assertEqual(response_json["completion_status"], "IN_PROGRESS")
self.assertDictEqual(
response_json["completion_data"],
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
},
)
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.cs.id,
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "IN_PROGRESS")
self.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
},
)
# read data via request api
response = self.client.get(
f"/api/assignment/{self.assignment.id}/{self.cs.id}/",
format="json",
)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertDictEqual(
response_json["completion_data"],
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
},
)
# submit the assignment
response = self.client.post(
url,
{
"assignment_id": self.assignment.id,
"course_session_id": self.cs.id,
"completion_status": "SUBMITTED",
"completion_data": {
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
},
},
format="json",
)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(response_json["assignment_user"], self.student.id)
self.assertEqual(response_json["assignment"], self.assignment.id)
self.assertEqual(response_json["completion_status"], "SUBMITTED")
self.assertDictEqual(
response_json["completion_data"],
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
},
)
# second submit will fail
response = self.client.post(
url,
{
"assignment_id": self.assignment.id,
"course_session_id": self.cs.id,
"completion_status": "SUBMITTED",
"completion_data": {
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
},
},
format="json",
)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 404)
self.assertTrue("Cannot update completion status" in str(response_json))
def test_expert_can_gradeAssignmentCompletion(self):
# setup AssignmentCompletion
subtasks = self.assignment.filter_user_subtasks(
subtask_types=["user_text_input"]
)
user_text_input = find_first(
subtasks,
pred=lambda x: (value := x.get("value"))
and value.get("text", "").startswith(
"Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest?"
),
)
ac = AssignmentCompletion.objects.create(
assignment_user=self.student,
assignment=self.assignment,
course_session=self.cs,
completion_status="SUBMITTED",
submitted_at=timezone.now(),
completion_data={
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
},
},
)
# make api call
self.client.login(username="admin", password="test")
url = f"/api/assignment/evaluate/"
response = self.client.post(
url,
{
"assignment_id": self.assignment.id,
"assignment_user_id": self.student.id,
"course_session_id": self.cs.id,
"completion_status": "EVALUATION_IN_PROGRESS",
"completion_data": {
user_text_input["id"]: {
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
},
},
format="json",
)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(response_json["assignment_user"], self.student.id)
self.assertEqual(response_json["assignment"], self.assignment.id)
self.assertEqual(response_json["completion_status"], "EVALUATION_IN_PROGRESS")
self.assertDictEqual(
response_json["completion_data"],
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.cs.id,
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "EVALUATION_IN_PROGRESS")
self.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
# finish grading
response = self.client.post(
url,
{
"assignment_id": self.assignment.id,
"assignment_user_id": self.student.id,
"course_session_id": self.cs.id,
"completion_status": "EVALUATION_SUBMITTED",
"completion_data": {
user_text_input["id"]: {
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
},
"evaluation_grade": 4.5,
"evaluation_points": 16,
},
format="json",
)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(response_json["assignment_user"], self.student.id)
self.assertEqual(response_json["assignment"], self.assignment.id)
self.assertEqual(response_json["completion_status"], "EVALUATION_SUBMITTED")
self.assertDictEqual(
response_json["completion_data"],
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.cs.id,
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "EVALUATION_SUBMITTED")
self.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
# `EVALUATION_SUBMITTED` will create a new AssignmentCompletionAuditLog
acl = AssignmentCompletionAuditLog.objects.get(
assignment_user=self.student,
course_session_id=self.cs.id,
assignment_id=self.assignment.id,
completion_status="EVALUATION_SUBMITTED",
)
self.maxDiff = None
self.assertDictEqual(
acl.completion_data,
{
user_text_input["id"]: {
"id": user_text_input["id"],
"type": "user_text_input",
"value": {
"text": "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest? Begründe deine Empfehlung",
},
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)

View File

@ -0,0 +1,368 @@
import json
from datetime import date
from django.utils import timezone
from graphene_django.utils import GraphQLTestCase
from vbv_lernwelt.assignment.models import (
Assignment,
AssignmentCompletion,
AssignmentCompletionAuditLog,
)
from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.core.models import User
from vbv_lernwelt.core.utils import find_first
from vbv_lernwelt.course.creators.test_course import create_test_course
from vbv_lernwelt.course.models import CourseSession
class AttendanceCourseUserMutationTestCase(GraphQLTestCase):
GRAPHQL_URL = "/server/graphql/"
def setUp(self):
create_default_users()
create_test_course(include_vv=False, with_sessions=True)
self.course_session = CourseSession.objects.get(title="Test Bern 2022 a")
self.trainer = User.objects.get(username="test-trainer1@example.com")
self.student = User.objects.get(username="test-student1@example.com")
self.assignment = Assignment.objects.get(
slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"
)
self.assignment_subtasks = self.assignment.filter_user_subtasks()
# self.client.force_login(self.trainer)
def test_student_can_upsertAssignmentCompletion(self):
self.client.force_login(self.student)
user_text_input = find_first(
self.assignment_subtasks, pred=lambda x: x["type"] == "user_text_input"
)
completion_data_string = json.dumps(
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
}
).replace('"', '\\"')
query = f"""
mutation {{
upsert_assignment_completion(
assignment_id: {self.assignment.id}
course_session_id: {self.course_session.id}
completion_status: IN_PROGRESS
completion_data_string: "{completion_data_string}"
) {{
assignment_completion {{
id
completion_status
completion_data
assignment_user {{ id }}
assignment {{ id }}
}}
}}
}}
"""
response = self.query(query)
self.assertResponseNoErrors(response)
data = json.loads(response.content)["data"]["upsert_assignment_completion"][
"assignment_completion"
]
self.assertEqual(data["assignment_user"]["id"], str(self.student.id))
self.assertEqual(data["assignment"]["id"], str(self.assignment.id))
self.assertEqual(data["completion_status"], "IN_PROGRESS")
self.assertDictEqual(
data["completion_data"],
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
},
)
# check DB data
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.course_session.id,
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "IN_PROGRESS")
self.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
},
)
# submit the response
completion_data_string = json.dumps(
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
}
).replace('"', '\\"')
query = f"""
mutation {{
upsert_assignment_completion(
assignment_id: {self.assignment.id}
course_session_id: {self.course_session.id}
completion_status: SUBMITTED
completion_data_string: "{completion_data_string}"
) {{
assignment_completion {{
id
completion_status
completion_data
assignment_user {{ id }}
assignment {{ id }}
}}
}}
}}
"""
response = self.query(query)
self.assertResponseNoErrors(response)
data = json.loads(response.content)["data"]["upsert_assignment_completion"][
"assignment_completion"
]
self.assertEqual(data["assignment_user"]["id"], str(self.student.id))
self.assertEqual(data["assignment"]["id"], str(self.assignment.id))
self.assertEqual(data["completion_status"], "SUBMITTED")
self.assertDictEqual(
data["completion_data"],
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
},
)
# check DB data
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.course_session.id,
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "SUBMITTED")
self.assertEqual(db_entry.submitted_at.date(), date.today())
self.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
},
)
# second submit will fail
completion_data_string = json.dumps(
{
user_text_input["id"]: {"user_data": {"text": "Hallo via API 3"}},
}
).replace('"', '\\"')
query = f"""
mutation {{
upsert_assignment_completion(
assignment_id: {self.assignment.id}
course_session_id: {self.course_session.id}
completion_status: SUBMITTED
completion_data_string: "{completion_data_string}"
) {{
assignment_completion {{
id
completion_status
completion_data
assignment_user {{ id }}
assignment {{ id }}
}}
}}
}}
"""
response = self.query(query)
self.assertResponseHasErrors(response)
self.assertTrue("Cannot update completion status" in str(response.json()))
def test_expert_can_gradeAssignmentCompletion(self):
# setup AssignmentCompletion
subtasks = self.assignment.filter_user_subtasks(
subtask_types=["user_text_input"]
)
user_text_input = find_first(
subtasks,
pred=lambda x: (value := x.get("value"))
and value.get("text", "").startswith(
"Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest?"
),
)
ac = AssignmentCompletion.objects.create(
assignment_user=self.student,
assignment=self.assignment,
course_session=self.course_session,
completion_status="SUBMITTED",
submitted_at=timezone.now(),
completion_data={
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
},
},
)
self.client.force_login(self.trainer)
completion_data_string = json.dumps(
{
user_text_input["id"]: {
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
}
).replace('"', '\\"')
query = f"""
mutation {{
upsert_assignment_completion(
assignment_id: {self.assignment.id}
assignment_user_id: {self.student.id}
course_session_id: {self.course_session.id}
completion_status: EVALUATION_IN_PROGRESS
completion_data_string: "{completion_data_string}"
) {{
assignment_completion {{
id
completion_status
completion_data
assignment_user {{ id }}
assignment {{ id }}
}}
}}
}}
"""
response = self.query(query)
self.assertResponseNoErrors(response)
data = json.loads(response.content)["data"]["upsert_assignment_completion"][
"assignment_completion"
]
self.assertEqual(data["assignment_user"]["id"], str(self.student.id))
self.assertEqual(data["assignment"]["id"], str(self.assignment.id))
self.assertEqual(data["completion_status"], "EVALUATION_IN_PROGRESS")
self.assertDictEqual(
data["completion_data"],
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.course_session.id,
assignment_id=self.assignment.id,
)
self.assertEqual(db_entry.completion_status, "EVALUATION_IN_PROGRESS")
self.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
# finish grading
completion_data_string = json.dumps(
{
user_text_input["id"]: {
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
}
).replace('"', '\\"')
query = f"""
mutation {{
upsert_assignment_completion(
assignment_id: {self.assignment.id}
assignment_user_id: {self.student.id}
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 {{
id
completion_status
completion_data
assignment_user {{ id }}
assignment {{ id }}
}}
}}
}}
"""
response = self.query(query)
self.assertResponseNoErrors(response)
data = json.loads(response.content)["data"]["upsert_assignment_completion"][
"assignment_completion"
]
self.assertEqual(data["assignment_user"]["id"], str(self.student.id))
self.assertEqual(data["assignment"]["id"], str(self.assignment.id))
self.assertEqual(data["completion_status"], "EVALUATION_SUBMITTED")
self.assertDictEqual(
data["completion_data"],
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
db_entry = AssignmentCompletion.objects.get(
assignment_user=self.student,
course_session_id=self.course_session.id,
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.assertDictEqual(
db_entry.completion_data,
{
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)
# `EVALUATION_SUBMITTED` will create a new AssignmentCompletionAuditLog
acl = AssignmentCompletionAuditLog.objects.get(
assignment_user=self.student,
course_session_id=self.course_session.id,
assignment_id=self.assignment.id,
completion_status="EVALUATION_SUBMITTED",
)
self.maxDiff = None
self.assertDictEqual(
acl.completion_data,
{
user_text_input["id"]: {
"id": user_text_input["id"],
"type": "user_text_input",
"value": {
"text": "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest? Begründe deine Empfehlung",
},
"user_data": {"text": "Ich würde nichts weiteres empfehlen."},
"expert_data": {"points": 1, "comment": "Gut gemacht!"},
},
},
)

View File

@ -8,6 +8,7 @@ from vbv_lernwelt.assignment.models import (
Assignment,
AssignmentCompletion,
AssignmentCompletionAuditLog,
AssignmentCompletionStatus,
)
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.core.create_default_users import create_default_users
@ -161,7 +162,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED,
)
ac = AssignmentCompletion.objects.get(
@ -216,7 +217,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment=self.assignment,
course_session=self.course_session,
submitted_at=timezone.now(),
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
completion_data={
user_text_input0["id"]: {
"user_data": {"text": "Am Anfang war das Wort... 0"}
@ -232,7 +233,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED,
)
# can submit twice with flag
@ -240,7 +241,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED,
validate_completion_status_change=False,
)
@ -280,7 +281,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED,
copy_task_data=True,
)
@ -306,7 +307,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED,
)
evaluation_task = self.assignment.get_evaluation_tasks()[0]
@ -320,7 +321,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
"expert_data": {"points": 2, "text": "Gut gemacht!"}
},
},
completion_status="EVALUATION_IN_PROGRESS",
completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
evaluation_user=self.trainer,
)
@ -352,7 +353,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED,
completion_data={
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
@ -369,7 +370,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
},
},
completion_status="EVALUATION_IN_PROGRESS",
completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
evaluation_user=self.trainer,
)
@ -404,7 +405,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="IN_PROGRESS",
completion_status=AssignmentCompletionStatus.IN_PROGRESS.value,
completion_data={
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
@ -417,7 +418,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="EVALUATION_IN_PROGRESS",
completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
completion_data={
user_text_input["id"]: {
"expert_data": {"points": 1, "comment": "Gut gemacht!"}
@ -445,7 +446,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment_user=self.user,
assignment=self.assignment,
course_session=self.course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED.value,
completion_data={
user_text_input["id"]: {
"user_data": {"text": "Ich würde nichts weiteres empfehlen."}
@ -464,7 +465,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
"expert_data": {"points": 2, "text": "Gut gemacht!"}
},
},
completion_status="EVALUATION_IN_PROGRESS",
completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
evaluation_user=self.trainer,
)
@ -475,7 +476,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment=self.assignment,
course_session=self.course_session,
completion_data={},
completion_status="EVALUATION_SUBMITTED",
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED,
evaluation_user=self.trainer,
evaluation_grade=None,
evaluation_points=None,
@ -486,7 +487,7 @@ class UpdateAssignmentCompletionTestCase(TestCase):
assignment=self.assignment,
course_session=self.course_session,
completion_data={},
completion_status="EVALUATION_SUBMITTED",
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED,
evaluation_user=self.trainer,
evaluation_grade=4.5,
evaluation_points=16,

View File

@ -1,69 +1,14 @@
import structlog
from rest_framework.decorators import api_view
from rest_framework.exceptions import PermissionDenied
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from wagtail.models import Page
from vbv_lernwelt.assignment.models import AssignmentCompletion
from vbv_lernwelt.assignment.serializers import AssignmentCompletionSerializer
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseSession
from vbv_lernwelt.course.permissions import (
has_course_access,
has_course_access_by_page_request,
is_course_session_expert,
)
from vbv_lernwelt.course.permissions import is_course_session_expert
logger = structlog.get_logger(__name__)
def _request_assignment_completion(
assignment_id, course_session_id, assignment_user_id
):
try:
response_data = AssignmentCompletionSerializer(
AssignmentCompletion.objects.get(
assignment_user_id=assignment_user_id,
assignment_id=assignment_id,
course_session_id=course_session_id,
),
).data
return Response(status=200, data=response_data)
except Exception as e:
logger.error(e)
return Response({"error": str(e)}, status=404)
@api_view(["GET"])
def request_assignment_completion(request, assignment_id, course_session_id):
course_id = get_object_or_404(CourseSession, id=course_session_id).course_id
if has_course_access(request.user, course_id):
return _request_assignment_completion(
assignment_id=assignment_id,
course_session_id=course_session_id,
assignment_user_id=request.user.id,
)
raise PermissionDenied()
@api_view(["GET"])
def request_assignment_completion_for_user(
request, assignment_id, course_session_id, user_id
):
if request.user.id == user_id or is_course_session_expert(
request.user, course_session_id
):
return _request_assignment_completion(
assignment_id=assignment_id,
course_session_id=course_session_id,
assignment_user_id=user_id,
)
raise PermissionDenied()
@api_view(["GET"])
def request_assignment_completion_status(request, assignment_id, course_session_id):
# TODO quickfix before GraphQL...
@ -75,102 +20,3 @@ def request_assignment_completion_status(request, assignment_id, course_session_
return Response(status=200, data=qs)
raise PermissionDenied()
@api_view(["POST"])
def upsert_user_assignment_completion(request):
try:
assignment_id = request.data.get("assignment_id")
course_session_id = request.data.get("course_session_id")
completion_status = request.data.get("completion_status", "IN_PROGRESS")
completion_data = request.data.get("completion_data", {})
assignment_page = Page.objects.get(id=assignment_id)
if not has_course_access_by_page_request(request, assignment_page):
raise PermissionDenied()
assignment = assignment_page.specific
ac = update_assignment_completion(
assignment_user=request.user,
assignment=assignment,
course_session=CourseSession.objects.get(id=course_session_id),
completion_data=completion_data,
completion_status=completion_status,
copy_task_data=False,
)
logger.debug(
"upsert_user_assignment_completion successful",
label="assignment_api",
assignment_id=assignment.id,
assignment_title=assignment.title,
assignment_user_id=request.user.id,
course_session_id=course_session_id,
completion_status=completion_status,
)
return Response(status=200, data=AssignmentCompletionSerializer(ac).data)
except PermissionDenied as e:
raise e
except Exception as e:
logger.error(e, exc_info=True)
return Response({"error": str(e)}, status=404)
@api_view(["POST"])
def evaluate_assignment_completion(request):
try:
assignment_id = request.data.get("assignment_id")
assignment_user_id = request.data.get("assignment_user_id")
course_session_id = request.data.get("course_session_id")
completion_status = request.data.get(
"completion_status", "EVALUATION_IN_PROGRESS"
)
completion_data = request.data.get("completion_data", {})
evaluation_grade = request.data.get("evaluation_grade", None)
evaluation_points = request.data.get("evaluation_grade", None)
assignment_page = Page.objects.get(id=assignment_id)
assignment_user = User.objects.get(id=assignment_user_id)
if not has_course_access_by_page_request(request, assignment_page):
raise PermissionDenied()
if not is_course_session_expert(request.user, course_session_id):
raise PermissionDenied()
assignment = assignment_page.specific
ac = update_assignment_completion(
assignment_user=assignment_user,
assignment=assignment,
course_session=CourseSession.objects.get(id=course_session_id),
completion_data=completion_data,
completion_status=completion_status,
copy_task_data=False,
evaluation_user=request.user,
evaluation_grade=evaluation_grade,
evaluation_points=evaluation_points,
)
logger.debug(
"grade_assignment_completion successful",
label="assignment_api",
assignment_id=assignment.id,
assignment_title=assignment.title,
assignment_user_id=assignment_user_id,
course_session_id=course_session_id,
completion_status=completion_status,
evaluation_user_id=request.user.id,
evaluation_grade=evaluation_grade,
evaluation_points=evaluation_points,
)
return Response(status=200, data=AssignmentCompletionSerializer(ac).data)
except PermissionDenied as e:
raise e
except Exception as e:
logger.error(e, exc_info=True)
return Response({"error": str(e)}, status=404)

View File

@ -1,5 +1,3 @@
import traceback
import structlog
from graphene import ResolveInfo
@ -12,5 +10,7 @@ class GrapheneErrorLoggingMiddleware(object):
try:
return next(root, info, **args)
except Exception as error:
logger.error(traceback.format_exc())
logger.error(
"GraphQL error", label="graphql_error", info=info, exc_info=error
)
raise error

View File

@ -10,7 +10,7 @@ from vbv_lernwelt.assignment.creators.create_assignments import (
create_uk_fahrzeug_casework,
create_uk_fahrzeug_prep_assignment,
)
from vbv_lernwelt.assignment.models import Assignment
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.assignment.tests.assignment_factories import AssignmentListPageFactory
from vbv_lernwelt.competence.factories import (
@ -139,7 +139,7 @@ def create_test_assignment_submitted_data(assignment, course_session, user):
assignment_user=user,
assignment=assignment,
course_session=course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED,
)

View File

@ -20,7 +20,7 @@ from vbv_lernwelt.assignment.creators.create_assignments import (
create_vv_gewinnen_casework,
create_vv_reflection,
)
from vbv_lernwelt.assignment.models import Assignment
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.assignment.tests.assignment_factories import AssignmentListPageFactory
from vbv_lernwelt.competence.create_uk_competence_profile import (
@ -450,7 +450,7 @@ def create_course_uk_de_assignment_completion_data(assignment, course_session, u
assignment_user=user,
assignment=assignment,
course_session=course_session,
completion_status="SUBMITTED",
completion_status=AssignmentCompletionStatus.SUBMITTED,
)