diff --git a/server/books/schema/inputs.py b/server/books/schema/inputs.py index 99820e11..663852ac 100644 --- a/server/books/schema/inputs.py +++ b/server/books/schema/inputs.py @@ -3,35 +3,35 @@ from graphene import InputObjectType class InputTypes(graphene.Enum): - text_block = 'text_block' + text_block = "text_block" # basic_knowledge = 'basic_knowledge' # probably won't be using this over the API - assignment = 'assignment' - image_block = 'image_block' - image_url_block = 'image_url_block' - link_block = 'link_block' - video_block = 'video_block' - document_block = 'document_block' - content_list_item = 'content_list_item' - subtitle = 'subtitle' - readonly = 'readonly' + assignment = "assignment" + image_block = "image_block" + image_url_block = "image_url_block" + link_block = "link_block" + video_block = "video_block" + document_block = "document_block" + content_list_item = "content_list_item" + subtitle = "subtitle" + readonly = "readonly" class ContentElementValueInput(InputObjectType): # we'll handle this with a single input, even tho it would be nice to have a type for every different possibility # see discussion at https://github.com/graphql/graphql-js/issues/207 - text = graphene.String(description='To be used for link_block, text_block types') - url = graphene.String(description='To be used for link, image_block types') - description = graphene.String(description='To be used for basic_knowledge type') - title = graphene.String(description='To be used for image_block, assignment type') - assignment = graphene.String(description='To be used for assignment type') - id = graphene.String(description='To be used for assignment type') + text = graphene.String(description="To be used for link_block, text_block types") + url = graphene.String(description="To be used for link, image_block types") + description = graphene.String(description="To be used for basic_knowledge type") + title = graphene.String(description="To be used for image_block, assignment type") + assignment = graphene.String(description="To be used for assignment type") + id = graphene.String(description="To be used for assignment type") class ContentElementInput(InputObjectType): id = graphene.String() type = InputTypes(required=True) value = ContentElementValueInput() - contents = graphene.List('books.schema.inputs.ContentElementInput') + contents = graphene.List("books.schema.inputs.ContentElementInput") class UserGroupBlockVisibility(InputObjectType): diff --git a/server/books/schema/mutations/contentblock.py b/server/books/schema/mutations/contentblock.py index a6ffe7ab..a74ce39d 100644 --- a/server/books/schema/mutations/contentblock.py +++ b/server/books/schema/mutations/contentblock.py @@ -1,20 +1,21 @@ import json import graphene +from api.utils import get_errors, get_object +from books.models import Chapter, ContentBlock +from books.schema.inputs import ContentBlockInput +from core.logger import get_logger +from core.utils import set_hidden_for, set_visible_for from django.core.exceptions import ValidationError from graphene import relay from graphql_relay import from_global_id -from api.utils import get_object, get_errors -from books.models import ContentBlock, Chapter, SchoolClass -from books.schema.inputs import ContentBlockInput -from core.logger import get_logger from ..nodes import ContentBlockNode -from core.utils import set_hidden_for, set_visible_for -from .utils import handle_content_block, handle_text, set_user_defined_block_type +from .utils import handle_content_block, set_user_defined_block_type logger = get_logger(__name__) + class MutateContentBlock(relay.ClientIDMutation): class Input: id = graphene.ID(required=True) @@ -25,17 +26,19 @@ class MutateContentBlock(relay.ClientIDMutation): @classmethod def mutate_and_get_payload(cls, root, info, **kwargs): try: - id_param = kwargs['id'] - content_block_data = kwargs.get('content_block') + id_param = kwargs["id"] + content_block_data = kwargs.get("content_block") # type_param = content_block_data.get('type') - title = content_block_data.get('title', None) - contents = content_block_data.get('contents', None) - visibility_list = content_block_data.get('visibility', None) + title = content_block_data.get("title", None) + contents = content_block_data.get("contents", None) + visibility_list = content_block_data.get("visibility", None) content_block = get_object(ContentBlock, id_param) - block_type = content_block_data.get('type') - if block_type is not None: # only change the type if the user has set a type + block_type = content_block_data.get("type") + if ( + block_type is not None + ): # only change the type if the user has set a type content_block.type = set_user_defined_block_type(block_type) module = content_block.get_parent().get_parent().specific @@ -50,7 +53,18 @@ class MutateContentBlock(relay.ClientIDMutation): content_block.title = title if contents is not None: - content_block.contents = json.dumps([handle_content_block(c, info.context, module, previous_contents=content_block.contents) for c in contents if c is not None]) + content_block.contents = json.dumps( + [ + handle_content_block( + c, + info.context, + module, + previous_contents=content_block.contents, + ) + for c in contents + if c is not None + ] + ) content_block.save() @@ -67,33 +81,41 @@ class MutateContentBlock(relay.ClientIDMutation): class AddContentBlock(relay.ClientIDMutation): class Input: content_block = graphene.Argument(ContentBlockInput) - parent = graphene.ID() # ID of chapter node; new content block will be inserted at the start of it + parent = ( + graphene.ID() + ) # ID of chapter node; new content block will be inserted at the start of it after = graphene.ID() # ID of content block node; new content block will be inserted after this content block node new_content_block = graphene.Field(ContentBlockNode) errors = graphene.List(graphene.String) @classmethod - def create_content_block(cls, content_block_data, parent=None, after=None, context=None): + def create_content_block( + cls, content_block_data, parent=None, after=None, context=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") - title = content_block_data.get('title') - contents = content_block_data.get('contents') - block_type = set_user_defined_block_type(content_block_data.get('type', ContentBlock.NORMAL)) + title = content_block_data.get("title") + contents = content_block_data.get("contents") + block_type = set_user_defined_block_type( + content_block_data.get("type", ContentBlock.NORMAL) + ) - new_content_block = ContentBlock(title=title, user_created=True, owner=context.user, type=block_type) + new_content_block = ContentBlock( + title=title, user_created=True, owner=context.user, type=block_type + ) if parent is not None: parent_chapter = get_object(Chapter, parent).specific first_sibling = parent_chapter.get_first_child() if first_sibling is not None: - first_sibling.add_sibling(instance=new_content_block, pos='left') + first_sibling.add_sibling(instance=new_content_block, pos="left") else: parent_chapter.add_child(instance=new_content_block) elif after is not None: sibling = get_object(ContentBlock, after).specific - sibling.add_sibling(instance=new_content_block, pos='right') + sibling.add_sibling(instance=new_content_block, pos="right") revision = new_content_block.save_revision() revision.publish() @@ -101,8 +123,9 @@ class AddContentBlock(relay.ClientIDMutation): module = new_content_block.get_parent().get_parent().specific - new_content_block.contents = json.dumps([handle_content_block(c, context, module) for c in - contents]) # can only do this after the content block has been saved + new_content_block.contents = json.dumps( + [handle_content_block(c, context, module) for c in contents] + ) # can only do this after the content block has been saved new_content_block.save() return new_content_block @@ -110,19 +133,21 @@ class AddContentBlock(relay.ClientIDMutation): @classmethod def mutate_and_get_payload(cls, root, info, **args): try: - parent = args.get('parent', None) - after = args.get('after', None) - new_content_block = cls.create_content_block(content_block_data=args.get('content_block'), - parent=parent, - after=after, - context=info.context) + parent = args.get("parent", None) + after = args.get("after", None) + new_content_block = cls.create_content_block( + content_block_data=args.get("content_block"), + parent=parent, + after=after, + context=info.context, + ) return cls(new_content_block=new_content_block) except ValidationError as e: errors = get_errors(e) except Exception as e: - errors = ['Error: {}'.format(e)] + errors = ["Error: {}".format(e)] return cls(new_content_block=None, errors=errors) @@ -136,7 +161,7 @@ class DeleteContentBlock(relay.ClientIDMutation): @classmethod def mutate_and_get_payload(cls, root, info, **kwargs): - id = from_global_id(kwargs.get('id'))[1] + id = from_global_id(kwargs.get("id"))[1] user = info.context.user try: @@ -144,7 +169,7 @@ class DeleteContentBlock(relay.ClientIDMutation): content_block.delete() return cls(success=True) except ContentBlock.DoesNotExist: - return cls(success=False, errors='Content block not found') + return cls(success=False, errors="Content block not found") class DuplicateContentBlock(relay.ClientIDMutation): @@ -155,12 +180,14 @@ class DuplicateContentBlock(relay.ClientIDMutation): @classmethod def mutate_and_get_payload(cls, root, info, **kwargs): - id = from_global_id(kwargs.get('id'))[1] + id = from_global_id(kwargs.get("id"))[1] user = info.context.user try: content_block = ContentBlock.objects.get(pk=id) - new_content_block = ContentBlock.objects.duplicate(content_block=content_block, user=user) + new_content_block = ContentBlock.objects.duplicate( + content_block=content_block, user=user + ) return cls(content_block=new_content_block) except ContentBlock.DoesNotExist: return cls(content_block=None) diff --git a/server/books/schema/mutations/utils.py b/server/books/schema/mutations/utils.py index 20e87a1d..6a9058e2 100644 --- a/server/books/schema/mutations/utils.py +++ b/server/books/schema/mutations/utils.py @@ -1,15 +1,12 @@ -import bleach import re import uuid - - from typing import List, Union -from wagtail.blocks import StreamValue - +import bleach from api.utils import get_object from assignments.models import Assignment from books.models import ContentBlock +from wagtail.blocks import StreamValue class AssignmentParameterException(Exception):