parent
96528e8926
commit
8d6f30b2d2
|
|
@ -120,3 +120,19 @@ class ContentBlockSnapshot(ContentBlock):
|
|||
null=True,
|
||||
related_name='custom_content_blocks'
|
||||
)
|
||||
|
||||
def to_regular_content_block(self, owner, school_class):
|
||||
cb = ContentBlock(
|
||||
contents=self.contents,
|
||||
type=self.type,
|
||||
title=self.title,
|
||||
owner=owner
|
||||
)
|
||||
self.add_sibling(instance=cb, pos='right')
|
||||
# some wagtail magic
|
||||
revision = cb.save_revision()
|
||||
revision.publish()
|
||||
cb.visible_for.add(school_class)
|
||||
cb.save()
|
||||
|
||||
return cb
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from books.schema.mutations.chapter import UpdateChapterVisibility
|
||||
from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock, DeleteContentBlock
|
||||
from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, SyncModuleVisibility
|
||||
from books.schema.mutations.snapshot import CreateSnapshot
|
||||
from books.schema.mutations.snapshot import CreateSnapshot, ApplySnapshot
|
||||
from books.schema.mutations.topic import UpdateLastTopic
|
||||
|
||||
|
||||
|
|
@ -15,3 +15,4 @@ class BookMutations(object):
|
|||
update_chapter_visibility = UpdateChapterVisibility.Field()
|
||||
sync_module_visibility = SyncModuleVisibility.Field()
|
||||
create_snapshot = CreateSnapshot.Field()
|
||||
apply_snapshot = ApplySnapshot.Field()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import graphene
|
|||
from graphene import relay
|
||||
|
||||
from api.utils import get_object
|
||||
from books.models import Module
|
||||
from books.models import Module, ContentBlock
|
||||
from books.models.snapshot import Snapshot
|
||||
from books.schema.nodes import SnapshotNode
|
||||
from users.models import SchoolClass
|
||||
|
|
@ -25,3 +25,32 @@ class CreateSnapshot(relay.ClientIDMutation):
|
|||
selected_class = get_object(SchoolClass, selected_class_id)
|
||||
snapshot = Snapshot.objects.create_snapshot(module, selected_class, user)
|
||||
return cls(snapshot=snapshot, success=True)
|
||||
|
||||
|
||||
class ApplySnapshot(relay.ClientIDMutation):
|
||||
class Input:
|
||||
snapshot = graphene.ID(required=True)
|
||||
selected_class = graphene.ID(required=True)
|
||||
|
||||
success = graphene.Boolean()
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **args):
|
||||
snapshot_id = args.get('snapshot')
|
||||
snapshot = get_object(Snapshot, snapshot_id)
|
||||
user = info.context.user
|
||||
selected_class_id = args.get('selected_class')
|
||||
selected_class = get_object(SchoolClass, selected_class_id)
|
||||
if not selected_class.users.filter(username=user.username).exists() or not user.is_teacher():
|
||||
raise PermissionError('Not allowed')
|
||||
for content_block in snapshot.hidden_content_blocks.all():
|
||||
content_block.hidden_for.add(selected_class)
|
||||
for custom_content_block in snapshot.custom_content_blocks.all():
|
||||
custom_content_block.to_regular_content_block(user, selected_class)
|
||||
for chapter_snapshot in snapshot.chapters.through.objects.all():
|
||||
chapter = chapter_snapshot.chapter
|
||||
if chapter_snapshot.title_hidden:
|
||||
chapter.title_hidden_for.add(selected_class)
|
||||
if chapter_snapshot.description_hidden:
|
||||
chapter.description_hidden_for.add(selected_class)
|
||||
return cls(success=True)
|
||||
|
|
|
|||
|
|
@ -26,16 +26,17 @@ class ChapterNode(DjangoObjectType):
|
|||
|
||||
def resolve_content_blocks(self, info, **kwargs):
|
||||
user = info.context.user
|
||||
school_classes = user.school_classes.values_list('pk')
|
||||
school_classes = user.school_classes.values_list('pk', flat=True)
|
||||
|
||||
by_parent = ContentBlock.get_by_parent(self) \
|
||||
.prefetch_related('visible_for') \
|
||||
.prefetch_related('hidden_for')
|
||||
.filter(contentblocksnapshot__isnull=True) # exclude snapshots
|
||||
# .prefetch_related('visible_for') \
|
||||
# .prefetch_related('hidden_for')
|
||||
|
||||
# don't filter the hidden blocks on the server any more, we do this on the client now, as they are not secret
|
||||
default_blocks = Q(user_created=False)
|
||||
owned_by_user = Q(user_created=True, owner=user)
|
||||
teacher_created_and_visible = Q(Q(user_created=True) & Q(visible_for__in=school_classes))
|
||||
teacher_created_and_visible = Q(Q(user_created=True) & Q(visible_for__pk__in=school_classes))
|
||||
|
||||
if user.can_manage_school_class_content: # teacher
|
||||
return by_parent.filter(default_blocks | owned_by_user | teacher_created_and_visible).distinct()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from graphql_relay import to_global_id, from_global_id
|
|||
|
||||
from api.schema import schema
|
||||
from books.factories import ModuleFactory, ChapterFactory, ContentBlockFactory
|
||||
from books.models import Snapshot
|
||||
from users.models import User, SchoolClass
|
||||
from users.services import create_users
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ query ModulesQuery($slug: String!) {
|
|||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
visibleFor {
|
||||
edges {
|
||||
node {
|
||||
|
|
@ -73,6 +75,13 @@ mutation CreateSnapshot($input: CreateSnapshotInput!) {
|
|||
}
|
||||
}
|
||||
"""
|
||||
APPLY_SNAPSHOT_MUTATION = """
|
||||
mutation ApplySnapshot($input: ApplySnapshotInput!) {
|
||||
applySnapshot(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def edges_to_array(entity):
|
||||
|
|
@ -82,20 +91,22 @@ def edges_to_array(entity):
|
|||
class CreateSnapshotTestCase(TestCase):
|
||||
def setUp(self):
|
||||
create_users()
|
||||
self.skillbox_class = SchoolClass.objects.get(name='skillbox')
|
||||
second_class = SchoolClass.objects.get(name='second_class')
|
||||
# teacher will create snapshot
|
||||
self.teacher = User.objects.get(username='teacher')
|
||||
self.module = ModuleFactory(slug='some-module')
|
||||
self.skillbox_class = SchoolClass.objects.get(name='skillbox')
|
||||
|
||||
# module M has a chapter
|
||||
self.chapter = ChapterFactory(parent=self.module, slug='some-chapter')
|
||||
|
||||
# chapter has some content blocks a, b, c
|
||||
self.title_visible = 'visible'
|
||||
self.title_hidden = 'hidden'
|
||||
self.title_custom = 'custom'
|
||||
self.visible_content_block = ContentBlockFactory(parent=self.chapter, module=self.module,
|
||||
title=self.title_visible, slug='cb-a')
|
||||
self.hidden_content_block = ContentBlockFactory(parent=self.chapter, module=self.module, title=self.title_hidden, slug='cb-b')
|
||||
self.hidden_content_block = ContentBlockFactory(parent=self.chapter, module=self.module,
|
||||
title=self.title_hidden, slug='cb-b')
|
||||
# content block c is user created
|
||||
self.custom_content_block = ContentBlockFactory(parent=self.chapter, owner=self.teacher, user_created=True,
|
||||
module=self.module, title=self.title_custom,
|
||||
|
|
@ -114,9 +125,8 @@ class CreateSnapshotTestCase(TestCase):
|
|||
# we make a snapshot S of the module M
|
||||
# snapshot S looks like module M for school class X
|
||||
|
||||
def test_setup(self):
|
||||
# make sure everything is setup correctly
|
||||
result = self.client.execute(MODULE_QUERY, variables={
|
||||
def _test_module_visibility(self, client, school_class_name):
|
||||
result = client.execute(MODULE_QUERY, variables={
|
||||
'slug': self.module.slug
|
||||
})
|
||||
self.assertIsNone(result.get('errors'))
|
||||
|
|
@ -124,16 +134,26 @@ class CreateSnapshotTestCase(TestCase):
|
|||
chapter = edges_to_array(module.get('chapters'))[0]
|
||||
self.assertIsNotNone(chapter)
|
||||
content_blocks = edges_to_array(chapter.get('contentBlocks'))
|
||||
content_block_ids = [node['id'] for node in content_blocks]
|
||||
self.assertTrue(to_global_id('ContentBlockNode', self.visible_content_block.id) in content_block_ids)
|
||||
self.assertTrue(to_global_id('ContentBlockNode', self.hidden_content_block.id) in content_block_ids)
|
||||
self.assertTrue(to_global_id('ContentBlockNode', self.custom_content_block.id) in content_block_ids)
|
||||
b = [node for node in content_blocks if
|
||||
node['id'] == to_global_id('ContentBlockNode', self.hidden_content_block.id)][0]
|
||||
c = [node for node in content_blocks if
|
||||
node['id'] == to_global_id('ContentBlockNode', self.custom_content_block.id)][0]
|
||||
self.assertTrue('skillbox' in [school_class['name'] for school_class in edges_to_array(b.get('hiddenFor'))])
|
||||
self.assertTrue('skillbox' in [school_class['name'] for school_class in edges_to_array(c.get('visibleFor'))])
|
||||
content_block_titles = [node['title'] for node in content_blocks]
|
||||
self.assertTrue(self.title_visible in content_block_titles)
|
||||
self.assertTrue(self.title_hidden in content_block_titles)
|
||||
self.assertTrue(self.title_custom in content_block_titles)
|
||||
hidden_node = [node for node in content_blocks if
|
||||
node['title'] == self.title_hidden][0]
|
||||
custom_node = [node for node in content_blocks if
|
||||
node['title'] == self.title_custom][0]
|
||||
# check if hidden node is hidden for this school class
|
||||
self.assertTrue(
|
||||
school_class_name in [school_class['name'] for school_class in
|
||||
edges_to_array(hidden_node.get('hiddenFor'))])
|
||||
# check if the custom node is visible for this school class
|
||||
self.assertTrue(
|
||||
school_class_name in [school_class['name'] for school_class in
|
||||
edges_to_array(custom_node.get('visibleFor'))])
|
||||
|
||||
def test_setup(self):
|
||||
# make sure everything is setup correctly
|
||||
self._test_module_visibility(self.client, 'skillbox')
|
||||
|
||||
def test_create_snapshot(self):
|
||||
result = self.client.execute(CREATE_SNAPSHOT_MUTATION, variables={
|
||||
|
|
@ -154,4 +174,21 @@ class CreateSnapshotTestCase(TestCase):
|
|||
self.assertEqual(content_blocks[0]['title'], self.title_visible)
|
||||
self.assertEqual(content_blocks[1]['title'], self.title_custom)
|
||||
|
||||
|
||||
def test_apply_snapshot(self):
|
||||
self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class,
|
||||
user=self.teacher)
|
||||
self.assertEqual(Snapshot.objects.count(), 1)
|
||||
school_class_name = 'second_class'
|
||||
second_class = SchoolClass.objects.get(name=school_class_name)
|
||||
request = RequestFactory().get('/')
|
||||
teacher2 = User.objects.get(username='teacher2')
|
||||
request.user = teacher2
|
||||
client = Client(schema=schema, context_value=request)
|
||||
result = client.execute(APPLY_SNAPSHOT_MUTATION, variables={
|
||||
'input': {
|
||||
'snapshot': to_global_id('SnapshotNode', self.snapshot.pk),
|
||||
'selectedClass': to_global_id('SchoolClassNode', second_class.pk),
|
||||
}
|
||||
})
|
||||
self.assertIsNone(result.get('errors'))
|
||||
self._test_module_visibility(client, school_class_name)
|
||||
|
|
|
|||
Loading…
Reference in New Issue