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 ..nodes import ContentBlockNode 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) content_block = graphene.Argument(ContentBlockInput) content_block = graphene.Field(ContentBlockNode) @classmethod def mutate_and_get_payload(cls, root, info, **kwargs): try: 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) 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 content_block.type = set_user_defined_block_type(block_type) module = content_block.get_parent().get_parent().specific if visibility_list is not None: if content_block.user_created: set_visible_for(content_block, visibility_list) else: set_hidden_for(content_block, visibility_list) if title is not None: 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.save() return cls(content_block=content_block) except ValidationError as e: errors = get_errors(e) raise errors except Exception as e: logger.error(e) raise e 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 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 ): if after is None and parent is None: 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) ) 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") 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") revision = new_content_block.save_revision() revision.publish() new_content_block.save() 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.save() return new_content_block @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, ) return cls(new_content_block=new_content_block) except ValidationError as e: errors = get_errors(e) except Exception as e: errors = ["Error: {}".format(e)] return cls(new_content_block=None, errors=errors) class DeleteContentBlock(relay.ClientIDMutation): class Input: id = graphene.ID(required=True) success = graphene.Boolean() errors = graphene.String() @classmethod def mutate_and_get_payload(cls, root, info, **kwargs): id = from_global_id(kwargs.get("id"))[1] user = info.context.user try: content_block = ContentBlock.objects.get(pk=id, owner=user) content_block.delete() return cls(success=True) except ContentBlock.DoesNotExist: return cls(success=False, errors="Content block not found") class DuplicateContentBlock(relay.ClientIDMutation): class Input: id = graphene.ID(required=True) content_block = graphene.Field(ContentBlockNode) @classmethod def mutate_and_get_payload(cls, root, info, **kwargs): 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 ) return cls(content_block=new_content_block) except ContentBlock.DoesNotExist: return cls(content_block=None)