Add first skeleton of a duplicate method
Add other base ideas for the copy mechanism for content block attached entities like assignments and surveys Relates to MS-651 Add test for duplicating entries, also update implementation
This commit is contained in:
parent
d6221e8cd5
commit
71dbfeb1f4
|
|
@ -9,12 +9,38 @@ from wagtail.models import Page, Site
|
|||
from wagtail.rich_text import RichText
|
||||
|
||||
from assignments.models import Assignment
|
||||
from basicknowledge.models import BasicKnowledge, INTERDISCIPLINARY, INTERDISCIPLINARY_LABEL, InstrumentCategory, \
|
||||
InstrumentType, \
|
||||
LANGUAGE_COMMUNICATION, LANGUAGE_COMMUNICATION_LABEL, SOCIETY, SOCIETY_LABEL
|
||||
from books.blocks import AssignmentBlock, BasicKnowledgeBlock, ImageUrlBlock, LinkBlock, VideoBlock
|
||||
from basicknowledge.models import (
|
||||
BasicKnowledge,
|
||||
INTERDISCIPLINARY,
|
||||
INTERDISCIPLINARY_LABEL,
|
||||
InstrumentCategory,
|
||||
InstrumentType,
|
||||
LANGUAGE_COMMUNICATION,
|
||||
LANGUAGE_COMMUNICATION_LABEL,
|
||||
SOCIETY,
|
||||
SOCIETY_LABEL,
|
||||
)
|
||||
from books.blocks import (
|
||||
AssignmentBlock,
|
||||
BasicKnowledgeBlock,
|
||||
ImageUrlBlock,
|
||||
LinkBlock,
|
||||
SurveyBlock,
|
||||
VideoBlock,
|
||||
)
|
||||
from books.models import Book, Chapter, ContentBlock, Module, TextBlock, Topic
|
||||
from core.factories import BasePageFactory, DummyImageFactory, fake, fake_paragraph, fake_title
|
||||
from core.factories import (
|
||||
BasePageFactory,
|
||||
DummyImageFactory,
|
||||
fake,
|
||||
fake_paragraph,
|
||||
fake_title,
|
||||
)
|
||||
from core.logger import get_logger
|
||||
from surveys.factories import SurveyFactory
|
||||
from surveys.models import Survey
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class BookFactory(BasePageFactory):
|
||||
|
|
@ -24,18 +50,25 @@ class BookFactory(BasePageFactory):
|
|||
@staticmethod
|
||||
def create_default_structure():
|
||||
site = wagtail_factories.SiteFactory.create(is_default_site=True)
|
||||
Page.objects.get(title='Root').delete()
|
||||
Page.objects.get(title="Root").delete()
|
||||
|
||||
book = BookFactory.create(parent=site.root_page, title='A book')
|
||||
topic = TopicFactory.create(parent=book, order=1, title='A topic')
|
||||
module = ModuleFactory.create(parent=topic,
|
||||
title="A module",
|
||||
meta_title="Modul 1",
|
||||
teaser="Whatever",
|
||||
intro="<p>Hello</p>")
|
||||
book = BookFactory.create(parent=site.root_page, title="A book")
|
||||
topic = TopicFactory.create(parent=book, order=1, title="A topic")
|
||||
module = ModuleFactory.create(
|
||||
parent=topic,
|
||||
title="A module",
|
||||
meta_title="Modul 1",
|
||||
teaser="Whatever",
|
||||
intro="<p>Hello</p>",
|
||||
)
|
||||
chapter = ChapterFactory.create(parent=module, title="A chapter")
|
||||
content_block = ContentBlockFactory.create(parent=chapter, module=module, title="A content block", type="task",
|
||||
contents=[])
|
||||
content_block = ContentBlockFactory.create(
|
||||
parent=chapter,
|
||||
module=module,
|
||||
title="A content block",
|
||||
type="task",
|
||||
contents=[],
|
||||
)
|
||||
|
||||
return book, topic, module, chapter, content_block
|
||||
|
||||
|
|
@ -45,7 +78,9 @@ class TopicFactory(BasePageFactory):
|
|||
model = Topic
|
||||
|
||||
order = 0
|
||||
teaser = factory.LazyAttribute(lambda x: fake.sentence(nb_words=random.randint(8, 12)))
|
||||
teaser = factory.LazyAttribute(
|
||||
lambda x: fake.sentence(nb_words=random.randint(8, 12))
|
||||
)
|
||||
description = factory.LazyAttribute(lambda x: fake.text(max_nb_chars=200))
|
||||
|
||||
|
||||
|
|
@ -54,7 +89,9 @@ class ModuleFactory(BasePageFactory):
|
|||
model = Module
|
||||
|
||||
meta_title = factory.LazyAttribute(lambda x: fake.text(max_nb_chars=20))
|
||||
teaser = factory.LazyAttribute(lambda x: fake.sentence(nb_words=random.randint(8, 12)))
|
||||
teaser = factory.LazyAttribute(
|
||||
lambda x: fake.sentence(nb_words=random.randint(8, 12))
|
||||
)
|
||||
intro = factory.LazyAttribute(lambda x: fake.text(max_nb_chars=200))
|
||||
|
||||
hero_image = factory.SubFactory(DummyImageFactory)
|
||||
|
|
@ -75,11 +112,14 @@ class TextBlockFactory(wagtail_factories.StructBlockFactory):
|
|||
class InstrumentCategoryFactory(factory.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = InstrumentCategory
|
||||
django_get_or_create = ('name',)
|
||||
django_get_or_create = ("name",)
|
||||
|
||||
name = factory.Iterator(
|
||||
[LANGUAGE_COMMUNICATION_LABEL, SOCIETY_LABEL, INTERDISCIPLINARY_LABEL]
|
||||
)
|
||||
foreground = factory.Iterator(["FF0000", "FFFFFF", "000000"])
|
||||
background = factory.Iterator(["FF0000", "FFFFFF", "000000"])
|
||||
|
||||
name = factory.Iterator([LANGUAGE_COMMUNICATION_LABEL, SOCIETY_LABEL, INTERDISCIPLINARY_LABEL])
|
||||
foreground = factory.Iterator(['FF0000', 'FFFFFF', '000000'])
|
||||
background = factory.Iterator(['FF0000', 'FFFFFF', '000000'])
|
||||
|
||||
class InstrumentTypeFactory(factory.DjangoModelFactory):
|
||||
class Meta:
|
||||
|
|
@ -99,7 +139,7 @@ class InstrumentFactory(BasePageFactory):
|
|||
|
||||
@classmethod
|
||||
def _create(cls, model_class, *args, **kwargs):
|
||||
kwargs['parent'] = Site.objects.get(is_default_site=True).root_page
|
||||
kwargs["parent"] = Site.objects.get(is_default_site=True).root_page
|
||||
return super()._create(model_class, *args, **kwargs)
|
||||
|
||||
|
||||
|
|
@ -113,7 +153,7 @@ class BasicKnowledgeBlockFactory(wagtail_factories.StructBlockFactory):
|
|||
|
||||
class ImageUrlBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
title = fake_title()
|
||||
url = factory.LazyAttribute(lambda x: 'https://picsum.photos/600/400/?random')
|
||||
url = factory.LazyAttribute(lambda x: "https://picsum.photos/600/400/?random")
|
||||
|
||||
class Meta:
|
||||
model = ImageUrlBlock
|
||||
|
|
@ -121,127 +161,202 @@ class ImageUrlBlockFactory(wagtail_factories.StructBlockFactory):
|
|||
|
||||
class LinkBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
text = fake_title()
|
||||
url = factory.LazyAttribute(lambda x: 'https://picsum.photos/600/400/?random')
|
||||
url = factory.LazyAttribute(lambda x: "https://picsum.photos/600/400/?random")
|
||||
|
||||
class Meta:
|
||||
model = LinkBlock
|
||||
|
||||
|
||||
class AssignmentBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
class Meta:
|
||||
model = AssignmentBlock
|
||||
|
||||
class EntityBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
@classmethod
|
||||
def _build(cls, model_class, *args, **kwargs):
|
||||
block = model_class()
|
||||
return blocks.StructValue(
|
||||
block,
|
||||
# todo: build in a more generic fashion
|
||||
[
|
||||
(name, kwargs['assignment']) for name, child_block in block.child_blocks.items()
|
||||
],
|
||||
)
|
||||
logger.debug(cls.id_key)
|
||||
logger.debug(cls.entity_key)
|
||||
logger.debug(kwargs)
|
||||
value = block.to_python({cls.id_key: kwargs.get(cls.entity_key).id})
|
||||
clean_value = block.clean(value)
|
||||
return clean_value
|
||||
|
||||
|
||||
class AssignmentBlockFactory(EntityBlockFactory):
|
||||
class Meta:
|
||||
model = AssignmentBlock
|
||||
|
||||
id_key = "assignment_id"
|
||||
entity_key = "assignment"
|
||||
|
||||
|
||||
class SurveyBlockFactory(EntityBlockFactory):
|
||||
class Meta:
|
||||
model = SurveyBlock
|
||||
|
||||
id_key = "survey_id"
|
||||
entity_key = "survey"
|
||||
|
||||
|
||||
class VideoBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
url = factory.LazyAttribute(lambda x: 'https://www.youtube.com/watch?v=lO9d-AJai8Q')
|
||||
url = factory.LazyAttribute(lambda x: "https://www.youtube.com/watch?v=lO9d-AJai8Q")
|
||||
|
||||
class Meta:
|
||||
model = VideoBlock
|
||||
|
||||
|
||||
block_types = ['text_block', 'basic_knowledge', 'student_entry', 'image_url_block', 'solution']
|
||||
block_types = [
|
||||
"text_block",
|
||||
"basic_knowledge",
|
||||
"student_entry",
|
||||
"image_url_block",
|
||||
"solution",
|
||||
]
|
||||
|
||||
|
||||
class ContentBlockFactory(BasePageFactory):
|
||||
class Meta:
|
||||
model = ContentBlock
|
||||
|
||||
type = factory.LazyAttribute(lambda x: random.choice(['normal', 'instrument', 'task',]))
|
||||
type = factory.LazyAttribute(
|
||||
lambda x: random.choice(
|
||||
[
|
||||
"normal",
|
||||
"instrument",
|
||||
"task",
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
contents = wagtail_factories.StreamFieldFactory({
|
||||
'text_block': TextBlockFactory,
|
||||
'basic_knowledge': BasicKnowledgeBlockFactory,
|
||||
'assignment': AssignmentBlockFactory,
|
||||
'image_block': wagtail_factories.ImageChooserBlockFactory,
|
||||
'image_url_block': ImageUrlBlockFactory,
|
||||
'link_block': LinkBlockFactory,
|
||||
'video_block': VideoBlockFactory,
|
||||
'solution': TextBlockFactory
|
||||
})
|
||||
contents = wagtail_factories.StreamFieldFactory(
|
||||
{
|
||||
"text_block": TextBlockFactory,
|
||||
"basic_knowledge": BasicKnowledgeBlockFactory,
|
||||
"assignment": AssignmentBlockFactory,
|
||||
"image_block": wagtail_factories.ImageChooserBlockFactory,
|
||||
"image_url_block": ImageUrlBlockFactory,
|
||||
"link_block": LinkBlockFactory,
|
||||
"video_block": VideoBlockFactory,
|
||||
"solution": TextBlockFactory,
|
||||
"survey": SurveyBlockFactory,
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def stream_field_magic(cls, module, kwargs, stream_field_name):
|
||||
if stream_field_name in kwargs:
|
||||
"""
|
||||
"""
|
||||
stream_field_name is most likely 'contents'
|
||||
this means: if there is a property named contents, use the defined ones in this block.
|
||||
otherwise, go into the other block and randomize the contents
|
||||
"""
|
||||
for idx, resource in enumerate(kwargs[stream_field_name]):
|
||||
value = resource['value']
|
||||
block_type = resource['type']
|
||||
value = resource["value"]
|
||||
block_type = resource["type"]
|
||||
|
||||
if block_type == 'assignment':
|
||||
if block_type == "assignment":
|
||||
user = get_user_model().objects.first()
|
||||
assignment = Assignment.objects.create(
|
||||
title=value['title'],
|
||||
assignment=value['assignment'],
|
||||
title=value["title"],
|
||||
assignment=value["assignment"],
|
||||
owner=user,
|
||||
module=module
|
||||
module=module,
|
||||
)
|
||||
kwargs['{}__{}__{}__{}'.format(stream_field_name, idx, block_type, 'assignment')] = assignment
|
||||
kwargs[
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, idx, block_type, "assignment"
|
||||
)
|
||||
] = assignment
|
||||
elif block_type == "survey":
|
||||
survey = Survey.objects.create(
|
||||
title=value["title"], data=value["data"], module=module
|
||||
)
|
||||
kwargs[
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, idx, block_type, "survey"
|
||||
)
|
||||
] = survey
|
||||
else:
|
||||
for jdx, field in enumerate(value):
|
||||
|
||||
if block_type == 'text_block':
|
||||
kwargs['{}__{}__{}__{}'.format(stream_field_name, idx, block_type, field)] = RichText(
|
||||
value[field])
|
||||
elif block_type == 'solution':
|
||||
kwargs['{}__{}__{}__{}'.format(stream_field_name, idx, block_type, field)] = RichText(
|
||||
value[field])
|
||||
elif block_type == 'basic_knowledge':
|
||||
if field == 'description':
|
||||
if block_type == "text_block":
|
||||
kwargs[
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, idx, block_type, field
|
||||
)
|
||||
] = RichText(value[field])
|
||||
elif block_type == "solution":
|
||||
kwargs[
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, idx, block_type, field
|
||||
)
|
||||
] = RichText(value[field])
|
||||
elif block_type == "basic_knowledge":
|
||||
if field == "description":
|
||||
kwargs[
|
||||
'{}__{}__{}__{}'.format(stream_field_name, idx, block_type, field)] = RichText(
|
||||
value[field])
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, idx, block_type, field
|
||||
)
|
||||
] = RichText(value[field])
|
||||
else:
|
||||
kwargs[
|
||||
'{}__{}__{}__{}'.format(stream_field_name, idx, block_type,
|
||||
field)] = 'https://google.ch'
|
||||
elif block_type == 'image_url_block':
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, idx, block_type, field
|
||||
)
|
||||
] = "https://google.ch"
|
||||
elif block_type == "image_url_block":
|
||||
kwargs[
|
||||
'{}__{}__{}__{}'.format(stream_field_name, idx, block_type, field)] = value[field]
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, idx, block_type, field
|
||||
)
|
||||
] = value[field]
|
||||
|
||||
else:
|
||||
kwargs[
|
||||
'{}__{}__{}__{}'.format(stream_field_name, idx, block_type, field)] = value[field]
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, idx, block_type, field
|
||||
)
|
||||
] = value[field]
|
||||
|
||||
del kwargs[stream_field_name]
|
||||
else: # random contents from generator
|
||||
for i in range(0, random.randint(3, 7)):
|
||||
block_type = random.choice(block_types)
|
||||
if block_type == 'text_block':
|
||||
kwargs['{}__{}__{}__{}'.format(stream_field_name, i, 'text_block', 'text')] = RichText(
|
||||
fake_paragraph())
|
||||
elif block_type == 'basic_knowledge':
|
||||
kwargs['{}__{}__{}__{}'.format(stream_field_name, i, 'basic_knowledge', 'description')] = RichText(
|
||||
fake_paragraph())
|
||||
elif block_type == 'assignment':
|
||||
kwargs['{}__{}__{}__{}'.format(stream_field_name, i, 'assignment', 'task_text')] = RichText(
|
||||
fake_paragraph())
|
||||
elif block_type == 'image_url_block':
|
||||
if block_type == "text_block":
|
||||
kwargs[
|
||||
'{}__{}__{}__{}'.format(stream_field_name, i, 'image_url_block', 'title')] = fake_paragraph()
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, i, "text_block", "text"
|
||||
)
|
||||
] = RichText(fake_paragraph())
|
||||
elif block_type == "basic_knowledge":
|
||||
kwargs[
|
||||
'{}__{}__{}__{}'.format(stream_field_name, i, 'image_url_block',
|
||||
'url')] = 'https://picsum.photos/400/?random={}'.format(
|
||||
''.join(random.choice('abcdefghiklmn') for _ in range(6)))
|
||||
elif block_type == 'solution':
|
||||
kwargs['{}__{}__{}__{}'.format(stream_field_name, i, 'solution', 'text')] = RichText(
|
||||
fake_paragraph())
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, i, "basic_knowledge", "description"
|
||||
)
|
||||
] = RichText(fake_paragraph())
|
||||
elif block_type == "assignment":
|
||||
kwargs[
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, i, "assignment", "task_text"
|
||||
)
|
||||
] = RichText(fake_paragraph())
|
||||
elif block_type == "image_url_block":
|
||||
kwargs[
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, i, "image_url_block", "title"
|
||||
)
|
||||
] = fake_paragraph()
|
||||
kwargs[
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, i, "image_url_block", "url"
|
||||
)
|
||||
] = "https://picsum.photos/400/?random={}".format(
|
||||
"".join(random.choice("abcdefghiklmn") for _ in range(6))
|
||||
)
|
||||
elif block_type == "solution":
|
||||
kwargs[
|
||||
"{}__{}__{}__{}".format(
|
||||
stream_field_name, i, "solution", "text"
|
||||
)
|
||||
] = RichText(fake_paragraph())
|
||||
|
||||
@classmethod
|
||||
def create(cls, module, **kwargs):
|
||||
cls.stream_field_magic(module, kwargs, 'contents')
|
||||
cls.stream_field_magic(module, kwargs, "contents")
|
||||
return cls._generate(CREATE_STRATEGY, kwargs)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import logging
|
|||
|
||||
from django.db import models
|
||||
from wagtail.admin.panels import FieldPanel, TabbedInterface, ObjectList
|
||||
from books.models.contentblock import ContentBlock
|
||||
|
||||
from core.wagtail_utils import StrictHierarchyPage, get_default_settings
|
||||
from users.models import SchoolClass
|
||||
|
|
@ -38,6 +39,9 @@ class Chapter(StrictHierarchyPage, GraphqlNodeMixin):
|
|||
SchoolClass, related_name="hidden_chapter_descriptions"
|
||||
)
|
||||
|
||||
def get_content_blocks(self):
|
||||
return ContentBlock.objects.all().descendants_of(self)
|
||||
|
||||
def sync_title_visibility(self, school_class_template, school_class_to_sync):
|
||||
if (
|
||||
self.title_hidden_for.filter(id=school_class_template.id).exists()
|
||||
|
|
@ -53,8 +57,7 @@ class Chapter(StrictHierarchyPage, GraphqlNodeMixin):
|
|||
|
||||
def sync_description_visibility(self, school_class_template, school_class_to_sync):
|
||||
if (
|
||||
self.description_hidden_for.filter(
|
||||
id=school_class_template.id).exists()
|
||||
self.description_hidden_for.filter(id=school_class_template.id).exists()
|
||||
and not self.description_hidden_for.filter(
|
||||
id=school_class_to_sync.id
|
||||
).exists()
|
||||
|
|
@ -62,8 +65,7 @@ class Chapter(StrictHierarchyPage, GraphqlNodeMixin):
|
|||
self.description_hidden_for.add(school_class_to_sync)
|
||||
|
||||
if (
|
||||
self.description_hidden_for.filter(
|
||||
id=school_class_to_sync.id).exists()
|
||||
self.description_hidden_for.filter(id=school_class_to_sync.id).exists()
|
||||
and not self.description_hidden_for.filter(
|
||||
id=school_class_template.id
|
||||
).exists()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import logging
|
||||
|
||||
from django.db import models
|
||||
from assignments.models import Assignment
|
||||
from wagtail.admin.panels import (
|
||||
FieldPanel,
|
||||
TabbedInterface,
|
||||
|
|
@ -11,6 +10,7 @@ from wagtail.fields import StreamField
|
|||
from wagtail.images.blocks import ImageChooserBlock
|
||||
|
||||
from books.managers import ContentBlockManager
|
||||
from core.logger import get_logger
|
||||
from core.wagtail_utils import get_default_settings
|
||||
from books.blocks import (
|
||||
CMSDocumentBlock,
|
||||
|
|
@ -30,14 +30,53 @@ from books.blocks import (
|
|||
ThinglinkBlock,
|
||||
InstructionBlock,
|
||||
)
|
||||
from books.utils import get_type_and_value
|
||||
from core.wagtail_utils import StrictHierarchyPage
|
||||
from notes.models import ContentBlockBookmark
|
||||
from surveys.models import Survey
|
||||
from users.models import SchoolClass, User
|
||||
from core.mixins import GraphqlNodeMixin
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def duplicate_entities_generator(new_module):
|
||||
def duplicate_entities(block):
|
||||
content_type = block.block_type
|
||||
value = block.value
|
||||
logger.debug(block)
|
||||
if content_type == "assignment":
|
||||
assignment = value.get("assignment_id")
|
||||
assignment.pk = None
|
||||
assignment.title = f"{assignment.title} (Kopie)"
|
||||
assignment.module = new_module
|
||||
logger.debug(f"setting new module {new_module}, {assignment.module}")
|
||||
assignment.save()
|
||||
block = AssignmentBlock()
|
||||
data = {"assignment_id": assignment.pk}
|
||||
value = block.to_python(data)
|
||||
cleaned_value = block.clean(value)
|
||||
new_block = ("assignment", cleaned_value)
|
||||
logger.debug(new_block)
|
||||
return new_block
|
||||
if content_type == "survey":
|
||||
logger.debug(value)
|
||||
survey = value.get("survey_id")
|
||||
survey.pk = None
|
||||
survey.title = f"{survey.title} (Kopie)"
|
||||
survey.module = new_module
|
||||
logger.debug(f"setting new module {new_module}, {survey.module}")
|
||||
survey.save()
|
||||
block = SurveyBlock()
|
||||
data = {"survey_id": survey.pk}
|
||||
value = block.to_python(data)
|
||||
cleaned_value = block.clean(value)
|
||||
new_block = ("survey", cleaned_value)
|
||||
# logger.debug(new_block)
|
||||
logger.debug(new_block)
|
||||
return new_block
|
||||
return block
|
||||
|
||||
return duplicate_entities
|
||||
|
||||
|
||||
class ContentBlock(StrictHierarchyPage, GraphqlNodeMixin):
|
||||
|
|
@ -102,8 +141,7 @@ class ContentBlock(StrictHierarchyPage, GraphqlNodeMixin):
|
|||
use_json_field=True,
|
||||
)
|
||||
|
||||
type = models.CharField(
|
||||
max_length=100, choices=TYPE_CHOICES, default=NORMAL)
|
||||
type = models.CharField(max_length=100, choices=TYPE_CHOICES, default=NORMAL)
|
||||
|
||||
content_panels = [
|
||||
FieldPanel("title", classname="full title"),
|
||||
|
|
@ -127,30 +165,56 @@ class ContentBlock(StrictHierarchyPage, GraphqlNodeMixin):
|
|||
def module(self):
|
||||
return self.get_parent().get_parent().specific
|
||||
|
||||
# duplicate all attached Surveys and Assignments
|
||||
def duplicate_attached_entities(self):
|
||||
logger.debug("starting to duplicate inside the content block")
|
||||
duplicate_entities = duplicate_entities_generator(self.module)
|
||||
logger.debug(f"new module: {self.module}")
|
||||
iterator = map(duplicate_entities, self.contents)
|
||||
logger.debug("here is the iterator")
|
||||
new_contents = list(iterator)
|
||||
logger.debug(new_contents)
|
||||
# we can't just insert a list here, we need a StreamValue data type
|
||||
# so we need to clear the list, then add each element in turn
|
||||
self.contents.clear()
|
||||
# like this, the internal methods of the SteamValue data type can work on the single elements
|
||||
for content in new_contents:
|
||||
logger.debug(content)
|
||||
self.contents.append(content)
|
||||
|
||||
# as an illustration
|
||||
# block = SolutionBlock()
|
||||
# data = {'text': 'This is me'}
|
||||
# value = block.to_python(data)
|
||||
# clean_value = block.clean(value)
|
||||
# self.contents.append(('solution', clean_value))
|
||||
logger.debug("self.contents")
|
||||
logger.debug(self.contents)
|
||||
self.save()
|
||||
|
||||
def is_hidden_for_class(self, school_class):
|
||||
return (
|
||||
not self.user_created
|
||||
and self.hidden_for.filter(id=school_class.id).exists()
|
||||
not self.user_creted and self.hidden_for.filter(id=school_class.id).exists()
|
||||
) or (
|
||||
self.user_created
|
||||
and not self.visible_for.filter(id=school_class.id).exists()
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# todo: move this to the after_create_page and after_edit_page hooks, and remove from here.
|
||||
for data in self.contents.raw_data:
|
||||
block_type, value = get_type_and_value(data)
|
||||
# def save(self, *args, **kwargs):
|
||||
# todo: move this to the after_create_page and after_edit_page hooks, and remove from here.
|
||||
# for data in self.contents.raw_data:
|
||||
# block_type, value = get_type_and_value(data)
|
||||
|
||||
# todo: also do the same for assignments
|
||||
if block_type == "survey":
|
||||
module = self.module
|
||||
survey = value["survey_id"]
|
||||
if isinstance(survey, int):
|
||||
survey = Survey.objects.get(pk=survey)
|
||||
if survey.module != module:
|
||||
survey.module = module
|
||||
survey.save()
|
||||
super().save(*args, **kwargs)
|
||||
# todo: also do the same for assignments
|
||||
# if block_type == "survey":
|
||||
# module = self.module
|
||||
# survey = value["survey_id"]
|
||||
# if isinstance(survey, int):
|
||||
# survey = Survey.objects.get(pk=survey)
|
||||
# if survey.module != module:
|
||||
# survey.module = module
|
||||
# survey.save()
|
||||
# super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class ContentBlockSnapshot(ContentBlock):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
from django.test import TestCase
|
||||
from assignments.factories import AssignmentFactory
|
||||
from assignments.models import Assignment
|
||||
|
||||
from books.factories import BookFactory, ContentBlockFactory
|
||||
from books.models.contentblock import ContentBlock
|
||||
from core.logger import get_logger
|
||||
from surveys.factories import SurveyFactory
|
||||
from surveys.models import Survey
|
||||
from users.services import create_users
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class DuplicateContentsTestCase(TestCase):
|
||||
def setUp(self) -> None:
|
||||
create_users()
|
||||
_, _, self.module, chapter, _ = BookFactory.create_default_structure()
|
||||
text = {"type": "text_block", "value": {"text": "Hallo"}}
|
||||
assignment = {
|
||||
"type": "assignment",
|
||||
"value": {"title": "Hello", "assignment": "Assignment"},
|
||||
}
|
||||
survey = {"type": "survey", "value": {"title": "Survey Title", "data": "null"}}
|
||||
self.content_block = ContentBlockFactory.create(
|
||||
parent=chapter,
|
||||
module=self.module,
|
||||
title="Another content block",
|
||||
type="task",
|
||||
contents=[text, assignment, survey],
|
||||
)
|
||||
self.assignment = Assignment.objects.first()
|
||||
|
||||
def test_duplicate_entities(self):
|
||||
self.assertEqual(
|
||||
self.content_block.contents[1].value["assignment_id"], self.assignment
|
||||
)
|
||||
self.assertEqual(
|
||||
self.content_block.contents[1].value["assignment_id"].title, "Hello"
|
||||
)
|
||||
self.assertEqual(Assignment.objects.count(), 1)
|
||||
self.assertEqual(Survey.objects.count(), 1)
|
||||
self.content_block.duplicate_attached_entities()
|
||||
self.assertEqual(Assignment.objects.count(), 2)
|
||||
self.assertEqual(Survey.objects.count(), 2)
|
||||
new_assignment = Assignment.objects.get(id=2)
|
||||
new_survey = Survey.objects.get(id=2)
|
||||
content_block = ContentBlock.objects.get(id=self.content_block.id)
|
||||
self.assertEqual(len(content_block.contents), 3)
|
||||
self.assertEqual(
|
||||
content_block.contents[1].value["assignment_id"], new_assignment
|
||||
)
|
||||
self.assertEqual(content_block.contents[2].value["survey_id"], new_survey)
|
||||
|
|
@ -1,107 +1,125 @@
|
|||
import wagtail.admin.rich_text.editors.draftail.features as draftail_features
|
||||
from wagtail.admin.rich_text.converters.html_to_contentstate import InlineStyleElementHandler
|
||||
from wagtail.admin.rich_text.converters.html_to_contentstate import (
|
||||
InlineStyleElementHandler,
|
||||
)
|
||||
from wagtail import hooks
|
||||
|
||||
from basicknowledge.models import BasicKnowledge
|
||||
from books.models import ContentBlockSnapshot
|
||||
from books.models.contentblock import ContentBlock
|
||||
from core.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
# 1. Use the register_rich_text_features hook.
|
||||
@hooks.register('register_rich_text_features')
|
||||
@hooks.register("register_rich_text_features")
|
||||
def register_brand_feature(features):
|
||||
"""
|
||||
Registering the feature, which uses the `BRAND` Draft.js inline style type,
|
||||
and is stored as HTML with a `<span class="brand">` tag.
|
||||
"""
|
||||
feature_name = 'brand'
|
||||
type_ = 'BRAND'
|
||||
feature_name = "brand"
|
||||
type_ = "BRAND"
|
||||
|
||||
# 2. Configure how Draftail handles the feature in its toolbar.
|
||||
control = {
|
||||
'type': type_,
|
||||
'label': 'Grün',
|
||||
'description': 'Grün',
|
||||
'style': {
|
||||
'color': '#17A887',
|
||||
'font-weight': '600'
|
||||
},
|
||||
"type": type_,
|
||||
"label": "Grün",
|
||||
"description": "Grün",
|
||||
"style": {"color": "#17A887", "font-weight": "600"},
|
||||
}
|
||||
|
||||
# 3. Call register_editor_plugin to register the configuration for Draftail.
|
||||
features.register_editor_plugin(
|
||||
'draftail', feature_name, draftail_features.InlineStyleFeature(control)
|
||||
"draftail", feature_name, draftail_features.InlineStyleFeature(control)
|
||||
)
|
||||
|
||||
# 4.configure the content transform from the DB to the editor and back.
|
||||
db_conversion = {
|
||||
'from_database_format': {'span[class="brand"]': InlineStyleElementHandler(type_)},
|
||||
'to_database_format': {'style_map': {type_: 'span class="brand""'}},
|
||||
"from_database_format": {
|
||||
'span[class="brand"]': InlineStyleElementHandler(type_)
|
||||
},
|
||||
"to_database_format": {"style_map": {type_: 'span class="brand""'}},
|
||||
}
|
||||
|
||||
# 5. Call register_converter_rule to register the content transformation conversion.
|
||||
features.register_converter_rule('contentstate', feature_name, db_conversion)
|
||||
features.register_converter_rule("contentstate", feature_name, db_conversion)
|
||||
|
||||
# 6. (optional) Add the feature to the default features list to make it available
|
||||
# on rich text fields that do not specify an explicit 'features' list
|
||||
features.default_features.append(feature_name)
|
||||
|
||||
|
||||
@hooks.register('register_rich_text_features')
|
||||
@hooks.register("register_rich_text_features")
|
||||
def register_secondary_feature(features):
|
||||
"""
|
||||
Registering the feature, which uses the `SECONDARY` Draft.js inline style type,
|
||||
and is stored as HTML with a `<span class="secondary">` tag.
|
||||
"""
|
||||
feature_name = 'secondary'
|
||||
type_ = 'SECONDARY'
|
||||
feature_name = "secondary"
|
||||
type_ = "SECONDARY"
|
||||
|
||||
# 2. Configure how Draftail handles the feature in its toolbar.
|
||||
control = {
|
||||
'type': type_,
|
||||
'label': 'Blau',
|
||||
'description': 'Blau',
|
||||
'style': {
|
||||
'color': '#078CC6',
|
||||
'font-weight': '600'
|
||||
},
|
||||
"type": type_,
|
||||
"label": "Blau",
|
||||
"description": "Blau",
|
||||
"style": {"color": "#078CC6", "font-weight": "600"},
|
||||
}
|
||||
|
||||
# 3. Call register_editor_plugin to register the configuration for Draftail.
|
||||
features.register_editor_plugin(
|
||||
'draftail', feature_name, draftail_features.InlineStyleFeature(control)
|
||||
"draftail", feature_name, draftail_features.InlineStyleFeature(control)
|
||||
)
|
||||
|
||||
# 4.configure the content transform from the DB to the editor and back.
|
||||
db_conversion = {
|
||||
'from_database_format': {'span[class="secondary"]': InlineStyleElementHandler(type_)},
|
||||
'to_database_format': {'style_map': {type_: 'span class="secondary"'}},
|
||||
"from_database_format": {
|
||||
'span[class="secondary"]': InlineStyleElementHandler(type_)
|
||||
},
|
||||
"to_database_format": {"style_map": {type_: 'span class="secondary"'}},
|
||||
}
|
||||
|
||||
# 5. Call register_converter_rule to register the content transformation conversion.
|
||||
features.register_converter_rule('contentstate', feature_name, db_conversion)
|
||||
features.register_converter_rule("contentstate", feature_name, db_conversion)
|
||||
|
||||
# 6. (optional) Add the feature to the default features list to make it available
|
||||
# on rich text fields that do not specify an explicit 'features' list
|
||||
features.default_features.append(feature_name)
|
||||
|
||||
|
||||
@hooks.register('construct_explorer_page_queryset')
|
||||
@hooks.register("construct_explorer_page_queryset")
|
||||
def remove_page_types_from_menu(parent_page, pages, request):
|
||||
return pages.not_type(ContentBlockSnapshot).not_type(BasicKnowledge).exclude(contentblock__user_created=True)
|
||||
return (
|
||||
pages.not_type(ContentBlockSnapshot)
|
||||
.not_type(BasicKnowledge)
|
||||
.exclude(contentblock__user_created=True)
|
||||
)
|
||||
|
||||
@hooks.register('after_copy_page')
|
||||
|
||||
@hooks.register("after_copy_page")
|
||||
def after_copy_hook(request, page, new_page):
|
||||
# todo: find every ContentBlock further down in the tree, see if there are any Surveys or Assignments and copy them and reassign them
|
||||
logger.debug(f'After copy page {page.title}, {new_page.title}')
|
||||
if type(page.specific) == ContentBlock:
|
||||
logger.debug("It's a content block")
|
||||
content_block: ContentBlock = new_page.specific
|
||||
logger.debug(f"duplicatin {content_block.title, content_block.pk}")
|
||||
content_block.duplicate_attached_entities()
|
||||
|
||||
@hooks.register('after_edit_page')
|
||||
else:
|
||||
logger.debug(f"It's something else {type(page.specific)}, {ContentBlock}")
|
||||
|
||||
logger.debug(
|
||||
f"After copy page old: {page.title} {page.pk}, {new_page.title} {new_page.pk}"
|
||||
)
|
||||
|
||||
|
||||
@hooks.register("after_edit_page")
|
||||
def after_edit_hook(request, page):
|
||||
logger.debug(f'After edit page {page.title}, {type(page).__name__}')
|
||||
logger.debug(f"After edit page {page.title}, {type(page).__name__}")
|
||||
|
||||
@hooks.register('after_create_page')
|
||||
|
||||
@hooks.register("after_create_page")
|
||||
def after_create_hook(request, page):
|
||||
logger.debug(f'After create page {page.title}')
|
||||
|
||||
logger.debug(f"After create page {page.title}")
|
||||
|
|
|
|||
|
|
@ -9,20 +9,25 @@ class StrictHierarchyPage(Page):
|
|||
abstract = True
|
||||
|
||||
def get_child_ids(self):
|
||||
return self.get_children().values_list('id', flat=True)
|
||||
return self.get_children().values_list("id", flat=True)
|
||||
|
||||
@classmethod
|
||||
def get_by_parent(cls, parent):
|
||||
return cls.objects.filter(id__in=parent.get_child_ids()).live()
|
||||
|
||||
def get_content_blocks(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def wagtail_parent_filter(parent_cls, child_cls):
|
||||
class ParentValueFilter(admin.SimpleListFilter):
|
||||
title = 'parent'
|
||||
parameter_name = 'parent'
|
||||
title = "parent"
|
||||
parameter_name = "parent"
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
return list((parent.slug, parent.title) for parent in parent_cls.objects.all())
|
||||
return list(
|
||||
(parent.slug, parent.title) for parent in parent_cls.objects.all()
|
||||
)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
filter_value = self.value()
|
||||
|
|
@ -34,7 +39,4 @@ def wagtail_parent_filter(parent_cls, child_cls):
|
|||
|
||||
|
||||
def get_default_settings():
|
||||
return ObjectList([
|
||||
FieldPanel('slug'),
|
||||
CommentPanel()
|
||||
], heading='Settings')
|
||||
return ObjectList([FieldPanel("slug"), CommentPanel()], heading="Settings")
|
||||
|
|
|
|||
Loading…
Reference in New Issue