Add models for KompetenzNavi and GraphQL-Types

This commit is contained in:
Daniel Egger 2023-09-01 11:15:11 +02:00
parent 10a8e5ef04
commit 0537d96dbb
22 changed files with 573 additions and 48 deletions

View File

@ -45,6 +45,10 @@ export type Scalars = {
export type AssignmentAssignmentAssignmentTypeChoices =
/** CASEWORK */
| 'CASEWORK'
/** CONDITION_ACCEPTANCE */
| 'CONDITION_ACCEPTANCE'
/** EDONIQ_TEST */
| 'EDONIQ_TEST'
/** PREP_ASSIGNMENT */
| 'PREP_ASSIGNMENT'
/** REFLECTION */
@ -94,7 +98,9 @@ export type AssignmentCompletionStatus =
export type AssignmentObjectType = CoursePageInterface & {
__typename?: 'AssignmentObjectType';
assignment_type: AssignmentAssignmentAssignmentTypeChoices;
circle?: Maybe<CircleObjectType>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
/** Zeitaufwand als Text */
effort_required: Scalars['String']['output'];
/** Beschreibung der Bewertung */
@ -106,7 +112,9 @@ export type AssignmentObjectType = CoursePageInterface & {
id?: Maybe<Scalars['ID']['output']>;
/** Erläuterung der Ausgangslage */
intro_text: Scalars['String']['output'];
learning_content?: Maybe<LearningContentInterface>;
live?: Maybe<Scalars['Boolean']['output']>;
max_points?: Maybe<Scalars['Int']['output']>;
performance_objectives?: Maybe<Scalars['JSONStreamField']['output']>;
slug?: Maybe<Scalars['String']['output']>;
tasks?: Maybe<Scalars['JSONStreamField']['output']>;
@ -140,7 +148,9 @@ export type AttendanceUserType = {
export type CircleObjectType = CoursePageInterface & {
__typename?: 'CircleObjectType';
circle?: Maybe<CircleObjectType>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description: Scalars['String']['output'];
frontend_url?: Maybe<Scalars['String']['output']>;
goals: Scalars['String']['output'];
@ -152,6 +162,20 @@ export type CircleObjectType = CoursePageInterface & {
translation_key?: Maybe<Scalars['String']['output']>;
};
export type CompetenceCertificateObjectType = CoursePageInterface & {
__typename?: 'CompetenceCertificateObjectType';
assignments?: Maybe<Array<Maybe<AssignmentObjectType>>>;
circle?: Maybe<CircleObjectType>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
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']>;
};
/** An enumeration. */
export type CoreUserLanguageChoices =
/** Deutsch */
@ -171,7 +195,9 @@ export type CourseObjectType = {
};
export type CoursePageInterface = {
circle?: Maybe<CircleObjectType>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
live?: Maybe<Scalars['Boolean']['output']>;
@ -211,9 +237,11 @@ export type FeedbackResponse = Node & {
export type LearningContentAssignmentObjectType = LearningContentInterface & {
__typename?: 'LearningContentAssignmentObjectType';
assignment_type: LearnpathLearningContentAssignmentAssignmentTypeChoices;
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_assignment: AssignmentObjectType;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -226,8 +254,10 @@ export type LearningContentAssignmentObjectType = LearningContentInterface & {
export type LearningContentAttendanceCourseObjectType = LearningContentInterface & {
__typename?: 'LearningContentAttendanceCourseObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -240,8 +270,10 @@ export type LearningContentAttendanceCourseObjectType = LearningContentInterface
export type LearningContentDocumentListObjectType = LearningContentInterface & {
__typename?: 'LearningContentDocumentListObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -254,8 +286,10 @@ export type LearningContentDocumentListObjectType = LearningContentInterface & {
export type LearningContentFeedbackObjectType = LearningContentInterface & {
__typename?: 'LearningContentFeedbackObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -267,8 +301,10 @@ export type LearningContentFeedbackObjectType = LearningContentInterface & {
};
export type LearningContentInterface = {
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -281,8 +317,10 @@ export type LearningContentInterface = {
export type LearningContentLearningModuleObjectType = LearningContentInterface & {
__typename?: 'LearningContentLearningModuleObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -295,8 +333,10 @@ export type LearningContentLearningModuleObjectType = LearningContentInterface &
export type LearningContentMediaLibraryObjectType = LearningContentInterface & {
__typename?: 'LearningContentMediaLibraryObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -309,8 +349,10 @@ export type LearningContentMediaLibraryObjectType = LearningContentInterface & {
export type LearningContentPlaceholderObjectType = LearningContentInterface & {
__typename?: 'LearningContentPlaceholderObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -323,8 +365,10 @@ export type LearningContentPlaceholderObjectType = LearningContentInterface & {
export type LearningContentRichTextObjectType = LearningContentInterface & {
__typename?: 'LearningContentRichTextObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -337,8 +381,11 @@ export type LearningContentRichTextObjectType = LearningContentInterface & {
export type LearningContentTestObjectType = LearningContentInterface & {
__typename?: 'LearningContentTestObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_assignment?: Maybe<AssignmentObjectType>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -351,8 +398,10 @@ export type LearningContentTestObjectType = LearningContentInterface & {
export type LearningContentVideoObjectType = LearningContentInterface & {
__typename?: 'LearningContentVideoObjectType';
circle?: Maybe<CircleObjectType>;
content?: Maybe<Scalars['String']['output']>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
description?: Maybe<Scalars['String']['output']>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
@ -365,7 +414,9 @@ export type LearningContentVideoObjectType = LearningContentInterface & {
export type LearningPathObjectType = CoursePageInterface & {
__typename?: 'LearningPathObjectType';
circle?: Maybe<CircleObjectType>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
depth: Scalars['Int']['output'];
draft_title: Scalars['String']['output'];
expire_at?: Maybe<Scalars['DateTime']['output']>;
@ -399,7 +450,9 @@ export type LearningPathObjectType = CoursePageInterface & {
export type LearningSequenceObjectType = CoursePageInterface & {
__typename?: 'LearningSequenceObjectType';
circle?: Maybe<CircleObjectType>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
frontend_url?: Maybe<Scalars['String']['output']>;
icon: Scalars['String']['output'];
id?: Maybe<Scalars['ID']['output']>;
@ -412,7 +465,9 @@ export type LearningSequenceObjectType = CoursePageInterface & {
export type LearningUnitObjectType = CoursePageInterface & {
__typename?: 'LearningUnitObjectType';
circle?: Maybe<CircleObjectType>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
learning_contents?: Maybe<Array<Maybe<LearningContentInterface>>>;
@ -426,6 +481,10 @@ export type LearningUnitObjectType = CoursePageInterface & {
export type LearnpathLearningContentAssignmentAssignmentTypeChoices =
/** CASEWORK */
| 'CASEWORK'
/** CONDITION_ACCEPTANCE */
| 'CONDITION_ACCEPTANCE'
/** EDONIQ_TEST */
| 'EDONIQ_TEST'
/** PREP_ASSIGNMENT */
| 'PREP_ASSIGNMENT'
/** REFLECTION */
@ -472,6 +531,7 @@ export type Query = {
assignment?: Maybe<AssignmentObjectType>;
assignment_completion?: Maybe<AssignmentCompletionObjectType>;
circle?: Maybe<CircleObjectType>;
competence_certificate?: Maybe<CompetenceCertificateObjectType>;
course?: Maybe<CourseObjectType>;
course_session_attendance_course?: Maybe<CourseSessionAttendanceCourseType>;
learning_content_assignment?: Maybe<LearningContentAssignmentObjectType>;
@ -508,6 +568,11 @@ export type QueryCircleArgs = {
};
export type QueryCompetenceCertificateArgs = {
id?: InputMaybe<Scalars['ID']['input']>;
};
export type QueryCourseArgs = {
id?: InputMaybe<Scalars['Int']['input']>;
};
@ -541,8 +606,10 @@ export type SendFeedbackPayload = {
export type TopicObjectType = CoursePageInterface & {
__typename?: 'TopicObjectType';
circle?: Maybe<CircleObjectType>;
circles?: Maybe<Array<Maybe<CircleObjectType>>>;
content_type?: Maybe<Scalars['String']['output']>;
course?: Maybe<CourseObjectType>;
frontend_url?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
is_visible: Scalars['Boolean']['output'];

View File

@ -13,6 +13,7 @@ type Query {
learning_content_document_list: LearningContentDocumentListObjectType
course_session_attendance_course(id: ID!, assignment_user_id: ID): CourseSessionAttendanceCourseType
course(id: Int): CourseObjectType
competence_certificate(id: ID): CompetenceCertificateObjectType
assignment(id: ID, slug: String): AssignmentObjectType
assignment_completion(assignment_id: ID!, course_session_id: ID!, learning_content_page_id: ID, assignment_user_id: UUID): AssignmentCompletionObjectType
}
@ -27,6 +28,8 @@ type CircleObjectType implements CoursePageInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
learning_sequences: [LearningSequenceObjectType]
}
@ -38,42 +41,16 @@ interface CoursePageInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
}
type LearningSequenceObjectType implements CoursePageInterface {
icon: String!
id: ID
title: String
slug: String
content_type: String
live: Boolean
translation_key: String
frontend_url: String
learning_units: [LearningUnitObjectType]
}
type LearningUnitObjectType implements CoursePageInterface {
id: ID
title: String
slug: String
content_type: String
live: Boolean
translation_key: String
frontend_url: String
learning_contents: [LearningContentInterface]
}
interface LearningContentInterface {
id: ID
title: String
slug: String
content_type: String
live: Boolean
translation_key: String
frontend_url: String
minutes: Int
description: String
content: String
type CourseObjectType {
id: ID!
title: String!
category_name: String!
slug: String!
learning_path: LearningPathObjectType
}
type LearningPathObjectType implements CoursePageInterface {
@ -115,6 +92,8 @@ type LearningPathObjectType implements CoursePageInterface {
search_description: String!
latest_revision_created_at: DateTime
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
topics: [TopicObjectType]
}
@ -165,9 +144,53 @@ type TopicObjectType implements CoursePageInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
circles: [CircleObjectType]
}
type LearningSequenceObjectType implements CoursePageInterface {
icon: String!
id: ID
title: String
slug: String
content_type: String
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
learning_units: [LearningUnitObjectType]
}
type LearningUnitObjectType implements CoursePageInterface {
id: ID
title: String
slug: String
content_type: String
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
learning_contents: [LearningContentInterface]
}
interface LearningContentInterface {
id: ID
title: String
slug: String
content_type: String
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
}
type LearningContentMediaLibraryObjectType implements LearningContentInterface {
id: ID
title: String
@ -176,6 +199,8 @@ type LearningContentMediaLibraryObjectType implements LearningContentInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -191,6 +216,8 @@ type LearningContentAssignmentObjectType implements LearningContentInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -217,9 +244,13 @@ type AssignmentObjectType implements CoursePageInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
tasks: JSONStreamField
evaluation_tasks: JSONStreamField
performance_objectives: JSONStreamField
max_points: Int
learning_content: LearningContentInterface
}
"""An enumeration."""
@ -232,6 +263,12 @@ enum AssignmentAssignmentAssignmentTypeChoices {
"""REFLECTION"""
REFLECTION
"""CONDITION_ACCEPTANCE"""
CONDITION_ACCEPTANCE
"""EDONIQ_TEST"""
EDONIQ_TEST
}
scalar JSONStreamField
@ -246,6 +283,12 @@ enum LearnpathLearningContentAssignmentAssignmentTypeChoices {
"""REFLECTION"""
REFLECTION
"""CONDITION_ACCEPTANCE"""
CONDITION_ACCEPTANCE
"""EDONIQ_TEST"""
EDONIQ_TEST
}
type LearningContentAttendanceCourseObjectType implements LearningContentInterface {
@ -256,6 +299,8 @@ type LearningContentAttendanceCourseObjectType implements LearningContentInterfa
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -269,6 +314,8 @@ type LearningContentFeedbackObjectType implements LearningContentInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -282,6 +329,8 @@ type LearningContentLearningModuleObjectType implements LearningContentInterface
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -295,6 +344,8 @@ type LearningContentPlaceholderObjectType implements LearningContentInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -308,12 +359,15 @@ type LearningContentRichTextObjectType implements LearningContentInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
}
type LearningContentTestObjectType implements LearningContentInterface {
content_assignment: AssignmentObjectType
id: ID
title: String
slug: String
@ -321,6 +375,8 @@ type LearningContentTestObjectType implements LearningContentInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -334,6 +390,8 @@ type LearningContentVideoObjectType implements LearningContentInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -347,6 +405,8 @@ type LearningContentDocumentListObjectType implements LearningContentInterface {
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
minutes: Int
description: String
content: String
@ -378,12 +438,17 @@ enum AttendanceUserStatus {
ABSENT
}
type CourseObjectType {
id: ID!
title: String!
category_name: String!
slug: String!
learning_path: LearningPathObjectType
type CompetenceCertificateObjectType implements CoursePageInterface {
id: ID
title: String
slug: String
content_type: String
live: Boolean
translation_key: String
frontend_url: String
circle: CircleObjectType
course: CourseObjectType
assignments: [AssignmentObjectType]
}
type AssignmentCompletionObjectType {
@ -492,4 +557,4 @@ enum AssignmentCompletionStatus {
SUBMITTED
EVALUATION_IN_PROGRESS
EVALUATION_SUBMITTED
}
}

View File

@ -10,6 +10,7 @@ export const AttendanceUserStatus = "AttendanceUserStatus";
export const AttendanceUserType = "AttendanceUserType";
export const Boolean = "Boolean";
export const CircleObjectType = "CircleObjectType";
export const CompetenceCertificateObjectType = "CompetenceCertificateObjectType";
export const CoreUserLanguageChoices = "CoreUserLanguageChoices";
export const CourseObjectType = "CourseObjectType";
export const CoursePageInterface = "CoursePageInterface";

View File

@ -0,0 +1,15 @@
{
competence_certificate(id:1) {
title,
assignments {
id
title
max_points
learning_content {
title
frontend_url
content_type
}
}
}
}

View File

@ -0,0 +1,30 @@
{
learning_path(slug: "test-lehrgang-lp") {
id
title
content_type
topics {
id
title
content_type
circles {
id
title
content_type
learning_sequences {
id
title
icon
learning_units {
id
title
learning_contents {
id
title
}
}
}
}
}
}
}

View File

@ -25,7 +25,7 @@ from wagtail.blocks.list_block import ListBlock, ListValue
from wagtail.rich_text import RichText
def create_uk_fahrzeug_casework(course_id=COURSE_UK):
def create_uk_fahrzeug_casework(course_id=COURSE_UK, competence_certificate=None):
assignment_list_page = (
CoursePage.objects.get(course_id=course_id)
.get_children()
@ -36,6 +36,7 @@ def create_uk_fahrzeug_casework(course_id=COURSE_UK):
assignment = AssignmentFactory(
parent=assignment_list_page,
title="Überprüfen einer Motorfahrzeugs-Versicherungspolice",
competence_certificate=competence_certificate,
effort_required="ca. 5 Stunden",
intro_text=replace_whitespace(
"""

View File

@ -5,12 +5,15 @@ from graphene_django import DjangoObjectType
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletion
from vbv_lernwelt.core.graphql.types import JSONStreamField
from vbv_lernwelt.course.graphql.interfaces import CoursePageInterface
from vbv_lernwelt.learnpath.graphql.types import LearningContentInterface
class AssignmentObjectType(DjangoObjectType):
tasks = JSONStreamField()
evaluation_tasks = JSONStreamField()
performance_objectives = JSONStreamField()
max_points = graphene.Int()
learning_content = graphene.Field(LearningContentInterface)
class Meta:
model = Assignment
@ -21,8 +24,15 @@ class AssignmentObjectType(DjangoObjectType):
"effort_required",
"evaluation_description",
"evaluation_document_url",
"learning_content",
)
def resolve_max_points(self, info):
return self.get_max_points()
def resolve_learning_content(self, info):
return self.find_attached_learning_content()
class AssignmentCompletionObjectType(DjangoObjectType):
completion_data = GenericScalar()

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.20 on 2023-09-01 09:12
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('competence', '0003_competencecertificate_competencecertificatelist_competencenavipage'),
('assignment', '0006_auto_20230823_1127'),
]
operations = [
migrations.AddField(
model_name='assignment',
name='competence_certificate',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='competence.competencecertificate'),
),
migrations.AlterField(
model_name='assignment',
name='assignment_type',
field=models.CharField(choices=[('CASEWORK', 'CASEWORK'), ('PREP_ASSIGNMENT', 'PREP_ASSIGNMENT'), ('REFLECTION', 'REFLECTION'), ('CONDITION_ACCEPTANCE', 'CONDITION_ACCEPTANCE'), ('EDONIQ_TEST', 'EDONIQ_TEST')], default='CASEWORK', max_length=50),
),
]

View File

@ -5,7 +5,7 @@ from django.db import models
from django.db.models import UniqueConstraint
from slugify import slugify
from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.admin.panels import FieldPanel, PageChooserPanel
from wagtail.fields import RichTextField, StreamField
from wagtail.models import Page
@ -113,6 +113,7 @@ class AssignmentType(Enum):
PREP_ASSIGNMENT = "PREP_ASSIGNMENT" # Vorbereitungsauftrag
REFLECTION = "REFLECTION" # Reflexion
CONDITION_ACCEPTANCE = "CONDITION_ACCEPTANCE" # Bedingungsannahme
EDONIQ_TEST = "EDONIQ_TEST" # EdonIQ Test
class Assignment(CourseBasePage):
@ -132,6 +133,13 @@ class Assignment(CourseBasePage):
default=AssignmentType.CASEWORK.value,
)
competence_certificate = models.ForeignKey(
"competence.CompetenceCertificate",
null=True,
blank=True,
on_delete=models.SET_NULL,
)
intro_text = RichTextField(
help_text="Erläuterung der Ausgangslage",
features=DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER,
@ -180,6 +188,7 @@ class Assignment(CourseBasePage):
content_panels = Page.content_panels + [
FieldPanel("assignment_type"),
PageChooserPanel("competence_certificate", "competence.CompetenceCertificate"),
FieldPanel("intro_text"),
FieldPanel("effort_required"),
FieldPanel("performance_objectives"),
@ -239,6 +248,23 @@ class Assignment(CourseBasePage):
def get_input_tasks(self):
return self.filter_user_subtasks() + self.get_evaluation_tasks()
def get_max_points(self):
return sum(
[task["value"].get("max_points", 0) for task in self.get_evaluation_tasks()]
)
def find_attached_learning_content(self):
"""
Returns the first learning content page attached to this assignment
"""
page = self.learningcontentassignment_set.first()
if page:
return page.specific
page = self.learningcontentedoniqtest_set.first()
if page:
return page.specific
return None
class AssignmentCompletionStatus(Enum):
IN_PROGRESS = "IN_PROGRESS"

View File

@ -4,9 +4,33 @@ from vbv_lernwelt.competence.models import (
CompetencePage,
CompetenceProfilePage,
PerformanceCriteria,
CompetenceCertificate,
CompetenceNaviPage,
CompetenceCertificateList,
)
class CompetenceNaviPageFactory(wagtail_factories.PageFactory):
title = "KompetenzNavi"
class Meta:
model = CompetenceNaviPage
class CompetenceCertificateListFactory(wagtail_factories.PageFactory):
title = "Kompetenznachweise"
class Meta:
model = CompetenceCertificateList
class CompetenceCertificateFactory(wagtail_factories.PageFactory):
title = "Kompetenznachweis"
class Meta:
model = CompetenceCertificate
class CompetenceProfilePageFactory(wagtail_factories.PageFactory):
title = "KompetenzNavi"

View File

@ -0,0 +1,14 @@
import graphene
from vbv_lernwelt.competence.graphql.types import CompetenceCertificateObjectType
from vbv_lernwelt.competence.models import CompetenceCertificate
class CompetenceCertificateQuery(object):
competence_certificate = graphene.Field(
CompetenceCertificateObjectType,
id=graphene.ID(),
)
def resolve_competence_certificate(root, info, id):
return CompetenceCertificate.objects.get(id=id)

View File

@ -0,0 +1,19 @@
from graphene import List
from graphene_django import DjangoObjectType
from vbv_lernwelt.assignment.graphql.types import AssignmentObjectType
from vbv_lernwelt.competence.models import CompetenceCertificate
from vbv_lernwelt.course.graphql.interfaces import CoursePageInterface
class CompetenceCertificateObjectType(DjangoObjectType):
assignments = List(AssignmentObjectType)
class Meta:
model = CompetenceCertificate
interfaces = (CoursePageInterface,)
fields = ["assignments"]
# resolver for the assignments
def resolve_assignments(self, info):
return self.assignment_set.all()

View File

@ -0,0 +1,45 @@
# Generated by Django 3.2.20 on 2023-09-01 09:12
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wagtailcore', '0089_log_entry_data_json_null_to_object'),
('competence', '0002_performancecriteria_learning_unit'),
]
operations = [
migrations.CreateModel(
name='CompetenceCertificate',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
migrations.CreateModel(
name='CompetenceCertificateList',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
migrations.CreateModel(
name='CompetenceNaviPage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
]

View File

@ -9,6 +9,38 @@ from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.course.models import CourseBasePage
class CompetenceNaviPage(CourseBasePage):
parent_page_types = ["course.CoursePage"]
subpage_types = ["competence.CompetenceCertificateList"]
def get_frontend_url(self):
return f"/course/{self.slug.replace('-competencenavi', '')}/competence"
def save(self, clean=True, user=None, log_action=False, **kwargs):
self.slug = find_available_slug(
slugify(f"{self.get_parent().slug}-competencenavi", allow_unicode=True),
ignore_page_id=self.id,
)
super(CompetenceNaviPage, self).save(clean, user, log_action, **kwargs)
class CompetenceCertificateList(CourseBasePage):
"""Kompetenznachweise für einen Lehrgang"""
parent_page_types = ["competence.CompetenceNaviPage"]
subpage_types = ["competence.CompetenceCertificate"]
class CompetenceCertificate(CourseBasePage):
"""einzelner Kompetenznachweis"""
parent_page_types = ["competence.CompetenceCertificateList"]
def __str__(self):
course = self.get_course()
return f"{course.title} - {self.title}"
class CompetenceProfilePage(CourseBasePage):
serialize_field_names = [
"course",
@ -35,6 +67,8 @@ class CompetenceProfilePage(CourseBasePage):
class CompetencePage(CourseBasePage):
"""Handlungskompetenz"""
serialize_field_names = [
"competence_id",
"children",

View File

@ -2,6 +2,7 @@ import graphene
from vbv_lernwelt.assignment.graphql.mutations import AssignmentMutation
from vbv_lernwelt.assignment.graphql.queries import AssignmentQuery
from vbv_lernwelt.competence.graphql.queries import CompetenceCertificateQuery
from vbv_lernwelt.course.graphql.queries import CourseQuery
from vbv_lernwelt.course_session.graphql.mutations import CourseSessionMutation
from vbv_lernwelt.course_session.graphql.queries import CourseSessionQuery
@ -10,7 +11,12 @@ from vbv_lernwelt.learnpath.graphql.queries import CircleQuery
class Query(
AssignmentQuery, CourseQuery, CourseSessionQuery, CircleQuery, graphene.ObjectType
AssignmentQuery,
CompetenceCertificateQuery,
CourseQuery,
CourseSessionQuery,
CircleQuery,
graphene.ObjectType,
):
pass

View File

@ -11,13 +11,24 @@ from vbv_lernwelt.assignment.creators.create_assignments import (
create_uk_fahrzeug_prep_assignment,
create_uk_reflection,
)
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus
from vbv_lernwelt.assignment.models import (
Assignment,
AssignmentCompletionStatus,
AssignmentListPage,
)
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.assignment.tests.assignment_factories import AssignmentListPageFactory
from vbv_lernwelt.assignment.tests.assignment_factories import (
AssignmentListPageFactory,
AssignmentFactory,
EvaluationTaskBlockFactory,
)
from vbv_lernwelt.competence.factories import (
CompetencePageFactory,
CompetenceProfilePageFactory,
PerformanceCriteriaFactory,
CompetenceNaviPageFactory,
CompetenceCertificateListFactory,
CompetenceCertificateFactory,
)
from vbv_lernwelt.competence.models import CompetencePage
from vbv_lernwelt.core.constants import (
@ -74,15 +85,21 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False):
create_test_competence_profile()
if include_uk:
competence_certificate = create_test_competence_navi()
# assignments create assignments parent page
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
_assignment_list_page = AssignmentListPageFactory(
parent=course_page,
)
create_uk_fahrzeug_casework(course_id=COURSE_TEST_ID)
create_uk_fahrzeug_casework(
course_id=COURSE_TEST_ID, competence_certificate=competence_certificate
)
create_uk_fahrzeug_prep_assignment(course_id=COURSE_TEST_ID)
create_uk_condition_acceptance(course_id=COURSE_TEST_ID)
create_uk_reflection(course_id=COURSE_TEST_ID)
create_test_assignment_edoniq(
course_id=COURSE_TEST_ID, competence_certificate=competence_certificate
)
create_test_learning_path(include_uk=include_uk, include_vv=include_vv)
create_test_media_library()
@ -316,6 +333,9 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
description=RichText(
"<p>Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.</p>"
),
content_assignment=Assignment.objects.get(
slug__startswith="test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"
),
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",
extended_time_test_url="https://exam2.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
@ -488,6 +508,27 @@ def create_test_competence_profile():
)
def create_test_competence_navi():
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
competence_navi_page = CompetenceNaviPageFactory(
title="KompetenzNavi",
parent=course_page,
)
competence_certificate_list = CompetenceCertificateListFactory(
title="Kompetenznachweise",
parent=competence_navi_page,
)
competence_certificate = CompetenceCertificateFactory(
title="Kompetenznachweis 1",
parent=competence_certificate_list,
)
return competence_certificate
def create_test_media_library():
course = Course.objects.get(id=COURSE_TEST_ID)
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
@ -564,3 +605,40 @@ def create_test_media_library():
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
),
)
def create_test_assignment_edoniq(
course_id=COURSE_TEST_ID, competence_certificate=None
):
assignment_list_page = (
CoursePage.objects.get(course_id=course_id)
.get_children()
.exact_type(AssignmentListPage)
.first()
)
assignment = AssignmentFactory(
parent=assignment_list_page,
title="Edoniq Wissens- und Verständisfragen - Circle Fahrzeug (Demo)",
competence_certificate=competence_certificate,
effort_required="ca. 5 Stunden",
intro_text="Edoniq Test",
performance_objectives=[],
evaluation_document_url="",
evaluation_description="",
)
assignment.evaluation_tasks = []
assignment.evaluation_tasks.append(
(
"task",
EvaluationTaskBlockFactory(
title="Maximale Punktzahl vom Edoniq Test",
max_points=24,
),
),
)
assignment.save()
return assignment

View File

@ -1,6 +1,7 @@
import graphene
from vbv_lernwelt.core.utils import get_django_content_type
from vbv_lernwelt.learnpath.models import Circle
class CoursePageInterface(graphene.Interface):
@ -11,9 +12,20 @@ class CoursePageInterface(graphene.Interface):
live = graphene.Boolean()
translation_key = graphene.String()
frontend_url = graphene.String()
circle = graphene.Field("vbv_lernwelt.learnpath.graphql.types.CircleObjectType")
course = graphene.Field("vbv_lernwelt.course.graphql.types.CourseObjectType")
def resolve_frontend_url(self, info):
return self.get_frontend_url()
def resolve_content_type(self, info):
return get_django_content_type(self)
def resolve_circle(self, info):
circle = self.get_ancestors().exact_type(Circle).first()
if circle:
return circle.specific
return None
def resolve_course(self, info):
return self.get_course()

View File

@ -106,7 +106,9 @@ class LearningContentTestObjectType(DjangoObjectType):
class Meta:
model = LearningContentEdoniqTest
interfaces = (LearningContentInterface,)
fields = []
fields = [
"content_assignment",
]
class LearningContentRichTextObjectType(DjangoObjectType):

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.20 on 2023-09-01 09:12
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assignment', '0007_auto_20230901_1112'),
('learnpath', '0003_auto_20230810_0817'),
]
operations = [
migrations.AddField(
model_name='learningcontentedoniqtest',
name='content_assignment',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='assignment.assignment'),
),
migrations.AlterField(
model_name='learningcontentassignment',
name='assignment_type',
field=models.CharField(choices=[('CASEWORK', 'CASEWORK'), ('PREP_ASSIGNMENT', 'PREP_ASSIGNMENT'), ('REFLECTION', 'REFLECTION'), ('CONDITION_ACCEPTANCE', 'CONDITION_ACCEPTANCE'), ('EDONIQ_TEST', 'EDONIQ_TEST')], default='CASEWORK', max_length=50),
),
]

View File

@ -337,8 +337,16 @@ class LearningContentEdoniqTest(LearningContent):
FieldPanel("checkbox_text", classname="Text"),
FieldPanel("test_url", classname="Text"),
FieldPanel("extended_time_test_url", classname="Text"),
PageChooserPanel("content_assignment", "assignment.Assignment"),
]
content_assignment = models.ForeignKey(
"assignment.Assignment",
# `null=True` is only set because of existing data...
null=True,
on_delete=models.SET_NULL,
)
@property
def has_extended_time_test(self):
return bool(self.extended_time_test_url)

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2023-09-01 09:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('notify', '0003_truncate_notifications'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='notification_trigger',
field=models.CharField(choices=[('ATTENDANCE_COURSE_REMINDER', 'Attendance Course Reminder'), ('CASEWORK_SUBMITTED', 'Casework Submitted'), ('CASEWORK_EVALUATED', 'Casework Evaluated'), ('NEW_FEEDBACK', 'New Feedback')], default='', max_length=255),
),
]