Add assignment handling on API
This commit is contained in:
parent
826849f1e0
commit
8f7f8e4d4f
|
|
@ -22,12 +22,21 @@
|
||||||
:class="{'contents-form__chooser': type(element) === 'content-block-element-chooser-widget'}"
|
:class="{'contents-form__chooser': type(element) === 'content-block-element-chooser-widget'}"
|
||||||
:element="element" v-bind="element" :index="index"
|
:element="element" v-bind="element" :index="index"
|
||||||
v-on:change-type="changeType"
|
v-on:change-type="changeType"
|
||||||
|
|
||||||
v-on:link-change-url="changeLinkUrl"
|
v-on:link-change-url="changeLinkUrl"
|
||||||
v-on:link-change-text="changeLinkText"
|
v-on:link-change-text="changeLinkText"
|
||||||
|
|
||||||
v-on:text-change-value="changeTextValue"
|
v-on:text-change-value="changeTextValue"
|
||||||
|
|
||||||
v-on:document-change-url="changeDocumentUrl"
|
v-on:document-change-url="changeDocumentUrl"
|
||||||
|
|
||||||
v-on:image-change-url="changeImageUrl"
|
v-on:image-change-url="changeImageUrl"
|
||||||
v-on:video-change-url="changeVideoUrl">
|
|
||||||
|
v-on:video-change-url="changeVideoUrl"
|
||||||
|
|
||||||
|
v-on:assignment-change-title="changeAssignmentTitle"
|
||||||
|
v-on:assignment-change-assignment="changeAssignmentAssignment"
|
||||||
|
>
|
||||||
</component>
|
</component>
|
||||||
<a class="contents-form__remove" v-on:click="removeElement(index)">
|
<a class="contents-form__remove" v-on:click="removeElement(index)">
|
||||||
<trash-icon v-if="type(element) !== 'content-block-element-chooser-widget'"
|
<trash-icon v-if="type(element) !== 'content-block-element-chooser-widget'"
|
||||||
|
|
@ -56,7 +65,7 @@
|
||||||
import VideoForm from '@/components/content-forms/VideoForm';
|
import VideoForm from '@/components/content-forms/VideoForm';
|
||||||
import ImageForm from '@/components/content-forms/ImageForm';
|
import ImageForm from '@/components/content-forms/ImageForm';
|
||||||
import DocumentForm from '@/components/content-forms/DocumentForm';
|
import DocumentForm from '@/components/content-forms/DocumentForm';
|
||||||
import ExerciseForm from '@/components/content-forms/ExerciseForm';
|
import AssignmentForm from '@/components/content-forms/AssignmentForm';
|
||||||
import TextForm from '@/components/content-forms/TextForm';
|
import TextForm from '@/components/content-forms/TextForm';
|
||||||
import TrashIcon from '@/components/icons/TrashIcon';
|
import TrashIcon from '@/components/icons/TrashIcon';
|
||||||
|
|
||||||
|
|
@ -78,7 +87,7 @@
|
||||||
VideoForm,
|
VideoForm,
|
||||||
ImageForm,
|
ImageForm,
|
||||||
DocumentForm,
|
DocumentForm,
|
||||||
ExerciseForm,
|
AssignmentForm,
|
||||||
TextForm,
|
TextForm,
|
||||||
TrashIcon
|
TrashIcon
|
||||||
},
|
},
|
||||||
|
|
@ -112,8 +121,8 @@
|
||||||
return 'image-form';
|
return 'image-form';
|
||||||
case 'text_block':
|
case 'text_block':
|
||||||
return 'text-form';
|
return 'text-form';
|
||||||
case 'exercise':
|
case 'assignment':
|
||||||
return 'exercise-form';
|
return 'assignment-form';
|
||||||
case 'document_block':
|
case 'document_block':
|
||||||
return 'document-form';
|
return 'document-form';
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +156,12 @@
|
||||||
changeTextValue(value, index) {
|
changeTextValue(value, index) {
|
||||||
this._updateProperty(value, index, 'text')
|
this._updateProperty(value, index, 'text')
|
||||||
},
|
},
|
||||||
|
changeAssignmentTitle(value, index) {
|
||||||
|
this._updateProperty(value, index, 'title')
|
||||||
|
},
|
||||||
|
changeAssignmentAssignment(value, index) {
|
||||||
|
this._updateProperty(value, index, 'assignment')
|
||||||
|
},
|
||||||
removeElement(index) {
|
removeElement(index) {
|
||||||
this.localContentBlock.contents.splice(index, 1);
|
this.localContentBlock.contents.splice(index, 1);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="exercise-form">
|
<div class="exercise-form">
|
||||||
<input class="exercise-form__title skillbox-input" placeholder="Aufgabentitel">
|
<input class="exercise-form__title skillbox-input"
|
||||||
<textarea class="exercise-form__exercise-text skillbox-textarea" placeholder="Aufgabe erfassen..."></textarea>
|
placeholder="Aufgabentitel"
|
||||||
|
:value="value.title"
|
||||||
|
v-on:input="$emit('assignment-change-title', $event.target.value, index)"
|
||||||
|
>
|
||||||
|
<textarea class="exercise-form__exercise-text skillbox-textarea"
|
||||||
|
placeholder="Aufgabe erfassen..."
|
||||||
|
:value="value.assignment"
|
||||||
|
v-on:input="$emit('assignment-change-assignment', $event.target.value, index)"
|
||||||
|
></textarea>
|
||||||
<info-icon class="exercise-form__help-icon help-text__icon"></info-icon>
|
<info-icon class="exercise-form__help-icon help-text__icon"></info-icon>
|
||||||
<p class="exercise-form__help-description help-text__description">
|
<p class="exercise-form__help-description help-text__description">
|
||||||
Ein Eingabefeld für die Antwort wird automatisch hinzugefügt.
|
Ein Eingabefeld für die Antwort wird automatisch hinzugefügt.
|
||||||
|
|
@ -13,6 +21,8 @@
|
||||||
import InfoIcon from '@/components/icons/InfoIcon';
|
import InfoIcon from '@/components/icons/InfoIcon';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
props: ['value', 'index'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
InfoIcon
|
InfoIcon
|
||||||
}
|
}
|
||||||
|
|
@ -44,6 +54,7 @@
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__help-description {}
|
&__help-description {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
<text-icon class="content-block-element-chooser-widget__link-icon"></text-icon>
|
<text-icon class="content-block-element-chooser-widget__link-icon"></text-icon>
|
||||||
<div class="content-block-element-chooser-widget__link-title">Text</div>
|
<div class="content-block-element-chooser-widget__link-title">Text</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-block-element-chooser-widget__link" v-on:click="$emit('change-type', index, 'exercise')">
|
<div class="content-block-element-chooser-widget__link" v-on:click="$emit('change-type', index, 'assignment')">
|
||||||
<speech-bubble-icon class="content-block-element-chooser-widget__link-icon"></speech-bubble-icon>
|
<speech-bubble-icon class="content-block-element-chooser-widget__link-icon"></speech-bubble-icon>
|
||||||
<div class="content-block-element-chooser-widget__link-title">Aufgabe & Ergebnis</div>
|
<div class="content-block-element-chooser-widget__link-title">Aufgabe & Ergebnis</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ from graphene_django.converter import convert_django_field
|
||||||
from wagtail.core.fields import StreamField
|
from wagtail.core.fields import StreamField
|
||||||
from wagtail.images.models import Image
|
from wagtail.images.models import Image
|
||||||
|
|
||||||
|
from assignments.models import Assignment
|
||||||
|
|
||||||
|
|
||||||
class GenericStreamFieldType(Scalar):
|
class GenericStreamFieldType(Scalar):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -23,6 +25,15 @@ class GenericStreamFieldType(Scalar):
|
||||||
'path': Image.objects.get(id=_value).file.url
|
'path': Image.objects.get(id=_value).file.url
|
||||||
}
|
}
|
||||||
d['value'] = value
|
d['value'] = value
|
||||||
|
if _type == 'assignment':
|
||||||
|
_value = d['value']
|
||||||
|
assignment = Assignment.objects.get(pk=_value['assignment_id'])
|
||||||
|
value = {
|
||||||
|
'title' : assignment.title,
|
||||||
|
'assignment': assignment.assignment
|
||||||
|
}
|
||||||
|
d['value'] = value
|
||||||
|
|
||||||
|
|
||||||
# value = dict(d['value'])
|
# value = dict(d['value'])
|
||||||
# if 'document' in value:
|
# if 'document' in value:
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,12 @@ class ImageUrlBlock(blocks.StructBlock):
|
||||||
url = blocks.URLBlock()
|
url = blocks.URLBlock()
|
||||||
|
|
||||||
|
|
||||||
# 'student_entry'
|
# 'assignment'
|
||||||
class StudentEntryBlock(blocks.StructBlock):
|
class AssignmentBlock(blocks.StructBlock):
|
||||||
class Meta:
|
class Meta:
|
||||||
icon = 'download'
|
icon = 'download'
|
||||||
|
|
||||||
task_text = blocks.RichTextBlock()
|
assignment_id = blocks.IntegerBlock()
|
||||||
|
|
||||||
|
|
||||||
# 'video_block'
|
# 'video_block'
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList,
|
||||||
from wagtail.core.fields import StreamField
|
from wagtail.core.fields import StreamField
|
||||||
from wagtail.images.blocks import ImageChooserBlock
|
from wagtail.images.blocks import ImageChooserBlock
|
||||||
|
|
||||||
from book.blocks import TextBlock, BasicKnowledgeBlock, StudentEntryBlock, LinkBlock, VideoBlock, DocumentBlock, \
|
from book.blocks import TextBlock, BasicKnowledgeBlock, LinkBlock, VideoBlock, DocumentBlock, \
|
||||||
ImageUrlBlock
|
ImageUrlBlock, AssignmentBlock
|
||||||
from core.wagtail_utils import StrictHierarchyPage
|
from core.wagtail_utils import StrictHierarchyPage
|
||||||
from user.models import UserGroup
|
from user.models import UserGroup
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ class ContentBlock(StrictHierarchyPage):
|
||||||
contents = StreamField([
|
contents = StreamField([
|
||||||
('text_block', TextBlock()),
|
('text_block', TextBlock()),
|
||||||
('basic_knowledge', BasicKnowledgeBlock()),
|
('basic_knowledge', BasicKnowledgeBlock()),
|
||||||
('student_entry', StudentEntryBlock()),
|
('assignment', AssignmentBlock()),
|
||||||
('image_block', ImageChooserBlock()),
|
('image_block', ImageChooserBlock()),
|
||||||
('image_url_block', ImageUrlBlock()),
|
('image_url_block', ImageUrlBlock()),
|
||||||
('link_block', LinkBlock()),
|
('link_block', LinkBlock()),
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ class InputTypes(graphene.Enum):
|
||||||
image_block = 'image_block'
|
image_block = 'image_block'
|
||||||
image_url_block = 'image_url_block'
|
image_url_block = 'image_url_block'
|
||||||
link_block = 'link_block'
|
link_block = 'link_block'
|
||||||
task = 'task'
|
|
||||||
video_block = 'video_block'
|
video_block = 'video_block'
|
||||||
document_block = 'document_block'
|
document_block = 'document_block'
|
||||||
|
|
||||||
|
|
@ -20,8 +19,8 @@ class ContentElementValueInput(InputObjectType):
|
||||||
text = graphene.String(description='To be used for link_block, text_block types')
|
text = graphene.String(description='To be used for link_block, text_block types')
|
||||||
url = graphene.String(description='To be used for link, basic_knowledge, image_block types')
|
url = graphene.String(description='To be used for link, basic_knowledge, image_block types')
|
||||||
description = graphene.String(description='To be used for basic_knowledge type')
|
description = graphene.String(description='To be used for basic_knowledge type')
|
||||||
title = graphene.String(description='To be used for image_block type')
|
title = graphene.String(description='To be used for image_block, assignment type')
|
||||||
task_text = graphene.String(description='To be used for task type')
|
assignment = graphene.String(description='To be used for assignment type')
|
||||||
|
|
||||||
|
|
||||||
class ContentElementInput(InputObjectType):
|
class ContentElementInput(InputObjectType):
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class MutateContentBlock(relay.ClientIDMutation):
|
||||||
content_block = graphene.Field(ContentBlockNode)
|
content_block = graphene.Field(ContentBlockNode)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mutate_and_get_payload(cls, *args, **kwargs):
|
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||||
try:
|
try:
|
||||||
id_param = kwargs['id']
|
id_param = kwargs['id']
|
||||||
content_block_data = kwargs.get('content_block')
|
content_block_data = kwargs.get('content_block')
|
||||||
|
|
@ -44,7 +44,7 @@ class MutateContentBlock(relay.ClientIDMutation):
|
||||||
content_block.title = title
|
content_block.title = title
|
||||||
|
|
||||||
if contents is not None:
|
if contents is not None:
|
||||||
content_block.contents = json.dumps(list(map(handle_content_block, contents)))
|
content_block.contents = json.dumps([handle_content_block(c, info.context) for c in contents])
|
||||||
|
|
||||||
content_block.save()
|
content_block.save()
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ class AddContentBlock(relay.ClientIDMutation):
|
||||||
errors = graphene.List(graphene.String)
|
errors = graphene.List(graphene.String)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_content_block(cls, content_block_data, parent=None, after=None):
|
def create_content_block(cls, content_block_data, parent=None, after=None, context=None):
|
||||||
if after is None and parent is None:
|
if after is None and parent is None:
|
||||||
raise Exception('Define either a parent or a sibling id')
|
raise Exception('Define either a parent or a sibling id')
|
||||||
|
|
||||||
|
|
@ -92,8 +92,8 @@ class AddContentBlock(relay.ClientIDMutation):
|
||||||
revision.publish()
|
revision.publish()
|
||||||
new_content_block.save()
|
new_content_block.save()
|
||||||
|
|
||||||
new_content_block.contents = json.dumps(
|
new_content_block.contents = json.dumps([handle_content_block(c, context) for c in
|
||||||
list(map(handle_content_block, contents))) # can only do this after the content block has been saved
|
contents]) # can only do this after the content block has been saved
|
||||||
new_content_block.save()
|
new_content_block.save()
|
||||||
|
|
||||||
return new_content_block
|
return new_content_block
|
||||||
|
|
@ -105,7 +105,8 @@ class AddContentBlock(relay.ClientIDMutation):
|
||||||
after = args.get('after', None)
|
after = args.get('after', None)
|
||||||
new_content_block = cls.create_content_block(content_block_data=args.get('content_block'),
|
new_content_block = cls.create_content_block(content_block_data=args.get('content_block'),
|
||||||
parent=parent,
|
parent=parent,
|
||||||
after=after)
|
after=after,
|
||||||
|
context=info.context)
|
||||||
|
|
||||||
return cls(new_content_block=new_content_block)
|
return cls(new_content_block=new_content_block)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
import bleach
|
import bleach
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from assignments.models import Assignment
|
||||||
|
|
||||||
|
|
||||||
def newlines_to_paragraphs(text):
|
def newlines_to_paragraphs(text):
|
||||||
parts = re.split(r'[\r\n]+', text)
|
parts = re.split(r'[\r\n]+', text)
|
||||||
|
|
@ -24,11 +26,12 @@ ALLOWED_BLOCKS = (
|
||||||
'image_url_block',
|
'image_url_block',
|
||||||
'link_block',
|
'link_block',
|
||||||
'video_block',
|
'video_block',
|
||||||
|
'assignment',
|
||||||
'document_block',
|
'document_block',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def handle_content_block(content, allowed_blocks=ALLOWED_BLOCKS):
|
def handle_content_block(content, context, allowed_blocks=ALLOWED_BLOCKS):
|
||||||
# todo: add all the content blocks
|
# todo: add all the content blocks
|
||||||
# todo: sanitize user inputs!
|
# todo: sanitize user inputs!
|
||||||
if content['type'] not in allowed_blocks:
|
if content['type'] not in allowed_blocks:
|
||||||
|
|
@ -40,8 +43,18 @@ def handle_content_block(content, allowed_blocks=ALLOWED_BLOCKS):
|
||||||
'value': {
|
'value': {
|
||||||
'text': newlines_to_paragraphs(bleach.clean(content['value']['text'], strip=True))
|
'text': newlines_to_paragraphs(bleach.clean(content['value']['text'], strip=True))
|
||||||
}}
|
}}
|
||||||
elif content['type'] == 'student_entry':
|
elif content['type'] == 'assignment':
|
||||||
return
|
assignment = Assignment.objects.create(
|
||||||
|
title=content['value']['title'],
|
||||||
|
assignment=content['value']['assignment'],
|
||||||
|
owner=context.user
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': 'assignment',
|
||||||
|
'value': {
|
||||||
|
'assignment_id': assignment.id
|
||||||
|
}}
|
||||||
elif content['type'] == 'image_url_block':
|
elif content['type'] == 'image_url_block':
|
||||||
return {
|
return {
|
||||||
'type': 'image_url_block',
|
'type': 'image_url_block',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 2.0.6 on 2018-09-27 09:45
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import wagtail.core.blocks
|
||||||
|
import wagtail.core.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rooms', '0002_auto_20180911_1414'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='roomentry',
|
||||||
|
name='contents',
|
||||||
|
field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock())])), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.RichTextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())]))], blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
Loading…
Reference in New Issue