diff --git a/client/src/components/modules/SnapshotCreated.vue b/client/src/components/modules/SnapshotCreated.vue
index 3b73543f..2d75f969 100644
--- a/client/src/components/modules/SnapshotCreated.vue
+++ b/client/src/components/modules/SnapshotCreated.vue
@@ -9,7 +9,7 @@
{{ snapshot.title }}
- {{ created }} - {{ snapshot.creator.firstName }} {{ snapshot.creator.lastName }}
+ {{ created }} - {{ snapshot.creator }}
diff --git a/client/src/components/modules/SnapshotHeader.vue b/client/src/components/modules/SnapshotHeader.vue
index 82ed2531..6f108b5b 100644
--- a/client/src/components/modules/SnapshotHeader.vue
+++ b/client/src/components/modules/SnapshotHeader.vue
@@ -2,7 +2,7 @@
@@ -39,6 +38,16 @@
};
},
+ computed: {
+ snapshots() {
+ if (this.selectedLink === 'mine') {
+ return this.module.snapshots.filter(snapshot => snapshot.mine);
+ } else {
+ return this.module.snapshots.filter(snapshot => snapshot.shared);
+ }
+ }
+ },
+
apollo: {
module: {
query: MODULE_SNAPSHOTS_QUERY,
diff --git a/server/books/schema/nodes/module.py b/server/books/schema/nodes/module.py
index 7156658a..1d8d6cd3 100644
--- a/server/books/schema/nodes/module.py
+++ b/server/books/schema/nodes/module.py
@@ -1,4 +1,5 @@
import graphene
+from django.db.models import Q
from graphene import relay
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
@@ -86,7 +87,8 @@ class ModuleNode(DjangoObjectType):
@staticmethod
def resolve_snapshots(parent, info, **kwargs):
- return parent.snapshots.filter(creator=info.context.user)
+ user = info.context.user
+ return parent.snapshots.filter(Q(creator=user) | Q(Q(creator__team=user.team ) & Q(shared=True)))
class RecentModuleNode(DjangoObjectType):
diff --git a/server/books/schema/nodes/snapshot.py b/server/books/schema/nodes/snapshot.py
index 95542bb4..5df5041f 100644
--- a/server/books/schema/nodes/snapshot.py
+++ b/server/books/schema/nodes/snapshot.py
@@ -72,6 +72,9 @@ class SnapshotNode(DjangoObjectType):
meta_title = graphene.String()
hero_image = graphene.String()
changes = graphene.Field(SnapshotChangesNode)
+ mine = graphene.Boolean()
+ shared = graphene.Boolean(required=True)
+ creator = graphene.String(required=True)
class Meta:
model = Snapshot
@@ -111,3 +114,11 @@ class SnapshotNode(DjangoObjectType):
'hidden_content_blocks': parent.hidden_content_blocks.count(),
'new_content_blocks': parent.custom_content_blocks.count()
}
+
+ @staticmethod
+ def resolve_mine(parent, info, **kwargs):
+ return parent.creator == info.context.user
+
+ @staticmethod
+ def resolve_creator(parent, info, **kwargs):
+ return f'{parent.creator.first_name} {parent.creator.last_name}'
diff --git a/server/books/tests/test_snapshots.py b/server/books/tests/test_snapshots.py
index e0a34ff8..641289d7 100644
--- a/server/books/tests/test_snapshots.py
+++ b/server/books/tests/test_snapshots.py
@@ -6,6 +6,7 @@ from api.schema import schema
from books.factories import ModuleFactory, ChapterFactory, ContentBlockFactory
from books.models import Snapshot, ChapterSnapshot
from core.tests.base_test import SkillboxTestCase
+from objectives.factories import ObjectiveGroupFactory, ObjectiveFactory
from users.factories import SchoolClassFactory
from users.models import User, SchoolClass
@@ -14,6 +15,18 @@ query ModulesQuery($slug: String, $id: ID) {
module(slug: $slug, id: $id) {
id
title
+ objectiveGroups {
+ objectives {
+ id
+ text
+ hiddenFor {
+ name
+ }
+ visibleFor {
+ name
+ }
+ }
+ }
chapters {
id
contentBlocks {
@@ -40,6 +53,11 @@ mutation CreateSnapshot($input: CreateSnapshotInput!) {
creator {
username
}
+ objectiveGroups {
+ objectives {
+ text
+ }
+ }
chapters {
id
descriptionHidden
@@ -152,31 +170,55 @@ class CreateSnapshotTestCase(SkillboxTestCase):
# we make a snapshot S of the module M
# snapshot S looks like module M for school class X
+ objective_group = ObjectiveGroupFactory(module=self.module)
+ self.visible_objective = ObjectiveFactory(text='visible-objective', group=objective_group)
+ self.hidden_objective = ObjectiveFactory(text='hidden-objective', group=objective_group)
+ self.custom_objective = ObjectiveFactory(text='custom-objective', group=objective_group, owner=self.teacher)
+
+ self.hidden_objective.hidden_for.add(self.skillbox_class)
+ self.custom_objective.visible_for.add(self.skillbox_class)
+
def _test_module_visibility(self, client, school_class_name):
result = client.execute(MODULE_QUERY, variables={
'slug': self.module.slug
})
self.assertIsNone(result.get('errors'))
module = result.get('data').get('module')
- chapter = edges_to_array(module.get('chapters'))[0]
+ chapter = module.get('chapters')[0]
self.assertIsNotNone(chapter)
content_blocks = chapter.get('contentBlocks')
- content_block_titles = [node['title'] for node in content_blocks]
+ content_block_titles = [content_block['title'] for content_block 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
+ hidden_content_block = [content_block for content_block in content_blocks if
+ content_block['title'] == self.title_hidden][0]
+ custom_content_block = [content_block for content_block in content_blocks if
+ content_block['title'] == self.title_custom][0]
+ # check if hidden content block is hidden for this school class
self.assertTrue(
school_class_name in [school_class['name'] for school_class in
- hidden_node.get('hiddenFor')])
- # check if the custom node is visible for this school class
+ hidden_content_block.get('hiddenFor')])
+ # check if the custom content block is visible for this school class
self.assertTrue(
school_class_name in [school_class['name'] for school_class in
- custom_node.get('visibleFor')])
+ custom_content_block.get('visibleFor')])
+
+ objectives = module['objectiveGroups'][0]['objectives']
+
+ hidden_objective = [objective for objective in objectives if
+ objective['text'] == self.hidden_objective.text][0]
+ custom_objective = [objective for objective in objectives if
+ objective['text'] == self.custom_objective.text][0]
+
+ # check if hidden objective is hidden for this school class
+ self.assertTrue(
+ school_class_name in [school_class['name'] for school_class in
+ hidden_objective.get('hiddenFor')])
+ # check if the custom objective is visible for this school class
+ self.assertTrue(
+ school_class_name in [school_class['name'] for school_class in
+ custom_objective.get('visibleFor')])
def test_setup(self):
# make sure everything is setup correctly
@@ -200,6 +242,7 @@ class CreateSnapshotTestCase(SkillboxTestCase):
self.assertFalse(chapter['descriptionHidden'])
_, chapter_id = from_global_id(chapter['id'])
self.assertEqual(int(chapter_id), self.chapter.id)
+
content_blocks = chapter['contentBlocks']
self.assertEqual(len(content_blocks), 3)
visible, hidden, custom = content_blocks
@@ -211,6 +254,15 @@ class CreateSnapshotTestCase(SkillboxTestCase):
self.assertEqual(custom['hidden'], False)
self.assertEqual(ChapterSnapshot.objects.count(), 2)
+ visible, hidden, custom = snapshot['objectiveGroups'][0]['objectives']
+ self.assertEqual(visible['text'], self.visible_objective.text)
+ self.assertEqual(visible['hidden'], False)
+ self.assertEqual(hidden['text'], self.hidden_objective.text)
+ self.assertEqual(hidden['hidden'], True)
+ self.assertEqual(custom['text'], self.custom_objective.text)
+ self.assertEqual(custom['hidden'], False)
+
+
def test_apply_snapshot(self):
self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class,
user=self.teacher)
diff --git a/server/schema.graphql b/server/schema.graphql
index 44c1600a..ffc9812b 100644
--- a/server/schema.graphql
+++ b/server/schema.graphql
@@ -419,6 +419,7 @@ type CustomMutation {
syncModuleVisibility(input: SyncModuleVisibilityInput!): SyncModuleVisibilityPayload
createSnapshot(input: CreateSnapshotInput!): CreateSnapshotPayload
applySnapshot(input: ApplySnapshotInput!): ApplySnapshotPayload
+ shareSnapshot(input: ShareSnapshotInput!): ShareSnapshotPayload
_debug: DjangoDebug
}
@@ -882,6 +883,18 @@ type SchoolClassNodeEdge {
cursor: String!
}
+input ShareSnapshotInput {
+ snapshot: ID!
+ shared: Boolean!
+ clientMutationId: String
+}
+
+type ShareSnapshotPayload {
+ success: Boolean!
+ snapshot: SnapshotNode
+ clientMutationId: String
+}
+
type SnapshotChangesNode {
hiddenObjectives: Int!
newObjectives: Int!
@@ -912,11 +925,13 @@ type SnapshotNode implements Node {
chapters: [SnapshotChapterNode]
hiddenContentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection!
created: DateTime!
- creator: UserNode
+ creator: String!
+ shared: Boolean!
title: String
metaTitle: String
heroImage: String
changes: SnapshotChangesNode
+ mine: Boolean
}
input SpellCheckInput {