Add snapshot module detail query

This commit is contained in:
Ramon Wenger 2021-05-04 15:25:28 +02:00
parent a71f893e0b
commit 85a3131680
13 changed files with 249 additions and 67 deletions

View File

@ -0,0 +1,12 @@
query SnapshotDetail($id: ID!) {
snapshot(id: $id) {
id
chapters {
id
description
title
titleHidden
descriptionHidden
}
}
}

View File

@ -2,11 +2,12 @@
<div>
Hello
{{ id }}
</div>
</template>
<script>
import SNAPSHOT_DETAIL_QUERY from 'gql/queries/snapshots/details.gql';
import SNAPSHOT_DETAIL_QUERY from '@/graphql/gql/queries/snapshots/detail.gql';
export default {
props: {
@ -17,7 +18,15 @@
},
apollo: {
snapshot: SNAPSHOT_DETAIL_QUERY,
snapshot: {
query: SNAPSHOT_DETAIL_QUERY,
variables() {
return {
id: this.id
};
}
},
},
};

View File

@ -27,11 +27,8 @@ class ChapterSnapshot(models.Model):
class SnapshotManager(models.Manager):
def create_snapshot(self, module, school_class, user, *args, **kwargs):
snapshot = self.create(module=module, creator=user, *args, **kwargs)
chapters_with_hidden_properties = Chapter.get_by_parent(module).filter(
Q(description_hidden_for=school_class)
| Q(title_hidden_for=school_class)
)
for chapter in chapters_with_hidden_properties:
chapters = Chapter.get_by_parent(module)
for chapter in chapters:
ChapterSnapshot.objects.create(
chapter=chapter,
snapshot=snapshot,

View File

@ -3,4 +3,5 @@ from graphene import relay
from graphene_django.filter import DjangoFilterConnectionField
class ChapterInterface(relay.Node):
content_blocks = DjangoFilterConnectionField('books.schema.nodes.ContentBlockNode')
description = graphene.String()
title = graphene.String()

View File

@ -0,0 +1,17 @@
import graphene
from graphene import relay
from api.graphene_wagtail import GenericStreamFieldType
class ContentBlockType(graphene.Enum):
NORMAL = 'normal'
BASE_COMMUNICATION = 'base_communication'
TASK = 'task'
BASE_SOCIETY = 'base_society'
BASE_INTERDISCIPLINARY = 'base_interdisciplinary'
class ContentBlockInterface(relay.Node):
title = graphene.String()
contents = GenericStreamFieldType()
type = ContentBlockType()

View File

@ -4,6 +4,14 @@ from graphene import relay
class ModuleInterface(relay.Node):
pk = graphene.Int()
hero_image = graphene.String()
topic = graphene.Field('books.schema.nodes.TopicNode')
def resolve_pk(self, info, **kwargs):
return self.id
@staticmethod
def resolve_pk(parent, info, **kwargs):
return parent.id
@staticmethod
def resolve_hero_image(parent, info, **kwargs):
if parent.hero_image:
return parent.hero_image.file.url

View File

@ -2,6 +2,7 @@ import graphene
from django.db.models import Q
from graphene import relay
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from graphql_relay import to_global_id
from books.models import Chapter, ContentBlock
@ -13,6 +14,7 @@ from notes.schema import ChapterBookmarkNode
class ChapterNode(DjangoObjectType):
bookmark = graphene.Field(ChapterBookmarkNode)
content_blocks = DjangoFilterConnectionField('books.schema.nodes.ContentBlockNode')
class Meta:
model = Chapter
@ -50,7 +52,7 @@ class ChapterNode(DjangoObjectType):
).first()
class SnapshotChapterNode(DjangoObjectType):
class ChapterInSnapshotNode(DjangoObjectType):
title_hidden = graphene.Boolean()
description_hidden = graphene.Boolean()
title = graphene.String()

View File

@ -1,8 +1,9 @@
import graphene
from graphene import relay
from graphene import relay, ObjectType
from graphene_django import DjangoObjectType
from books.models import ContentBlock
from books.schema.interfaces.contentblock import ContentBlockInterface
from books.utils import are_solutions_enabled_for
from notes.models import ContentBlockBookmark
from notes.schema import ContentBlockBookmarkNode
@ -39,8 +40,6 @@ class ContentBlockNode(DjangoObjectType):
mine = graphene.Boolean()
bookmarks = graphene.List(ContentBlockBookmarkNode)
# contents = graphene.List(ContentNode)
class Meta:
model = ContentBlock
only_fields = [
@ -49,7 +48,7 @@ class ContentBlockNode(DjangoObjectType):
filter_fields = [
'slug', 'title',
]
interfaces = (relay.Node,)
interfaces = (ContentBlockInterface,)
def resolve_mine(self, info, **kwargs):
return self.owner is not None and self.owner.pk == info.context.user.pk
@ -79,6 +78,7 @@ class ContentBlockNode(DjangoObjectType):
)
def process_module_room_slug_block(content):
if content['type'] == 'module_room_slug':
try:

View File

@ -15,17 +15,6 @@ from surveys.schema import AnswerNode
class ModuleNode(DjangoObjectType):
chapters = DjangoFilterConnectionField(ChapterNode)
topic = graphene.Field('books.schema.nodes.TopicNode')
hero_image = graphene.String()
solutions_enabled = graphene.Boolean()
bookmark = graphene.Field(ModuleBookmarkNode)
my_submissions = DjangoFilterConnectionField(StudentSubmissionNode)
my_answers = DjangoFilterConnectionField(AnswerNode)
my_content_bookmarks = DjangoFilterConnectionField(ContentBlockBookmarkNode)
my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode)
snapshots = graphene.List('books.schema.nodes.SnapshotNode')
class Meta:
model = Module
only_fields = [
@ -37,9 +26,16 @@ class ModuleNode(DjangoObjectType):
}
interfaces = (ModuleInterface, )
def resolve_hero_image(self, info, **kwargs):
if self.hero_image:
return self.hero_image.file.url
chapters = DjangoFilterConnectionField(ChapterNode)
solutions_enabled = graphene.Boolean()
bookmark = graphene.Field(ModuleBookmarkNode)
my_submissions = DjangoFilterConnectionField(StudentSubmissionNode)
my_answers = DjangoFilterConnectionField(AnswerNode)
my_content_bookmarks = DjangoFilterConnectionField(ContentBlockBookmarkNode)
my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode)
snapshots = graphene.List('books.schema.nodes.SnapshotNode')
def resolve_chapters(self, info, **kwargs):
return Chapter.get_by_parent(self)
@ -88,8 +84,9 @@ class ModuleNode(DjangoObjectType):
return parent.objective_groups.all() \
.prefetch_related('hidden_for')
def resolve_snapshots(self, info, **kwargs):
return self.snapshots.all()
@staticmethod
def resolve_snapshots(parent, info, **kwargs):
return parent.snapshots.all()
class RecentModuleNode(DjangoObjectType):
class Meta:

View File

@ -1,25 +1,88 @@
import graphene
from graphene import relay
from django.db.models import Q
from graphene import relay, ObjectType
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from .chapter import SnapshotChapterNode
from . import ChapterInSnapshotNode
from books.models.snapshot import Snapshot
from ..interfaces import ModuleInterface, ChapterInterface
from ..interfaces.contentblock import ContentBlockInterface
from ...models import Module, Chapter, ChapterSnapshot, ContentBlock
class SnapshotContentBlock:
def __init__(self, content_block, snapshot):
self.title = content_block.title
self.contents = content_block.title
self.type = content_block.type
self.hidden = snapshot.hidden_content_blocks.filter(id=content_block.id).exists()
class SnapshotChapter:
def __init__(self, chapter, snapshot, description_hidden=False, title_hidden=False):
self.title = chapter.title
self.description = chapter.description
self.title_hidden = title_hidden
self.description_hidden = description_hidden
self.content_blocks = []
base_qs = ContentBlock.get_by_parent(chapter)
without_owner = Q(owner__isnull=True)
this_snapshot = Q(contentblocksnapshot__snapshot=snapshot)
self.content_blocks = [
SnapshotContentBlock(
content_block=content_block,
snapshot=snapshot
) for content_block in
base_qs.filter(without_owner | this_snapshot)
]
# all from module without owner
# all with snapshotcontentblock with this snapshot
class SnapshotContentBlockNode(ObjectType):
class Meta:
interfaces = (ContentBlockInterface,)
hidden = graphene.Boolean()
class SnapshotChapterNode(ObjectType):
class Meta:
interfaces = (ChapterInterface,)
content_blocks = graphene.List(SnapshotContentBlockNode)
description_hidden = graphene.Boolean()
title_hidden = graphene.Boolean()
class SnapshotNode(DjangoObjectType):
title = graphene.String()
# chapters = graphene.Field(SnapshotChapterNode)
snapshot_chapters = DjangoFilterConnectionField(ChapterInSnapshotNode)
chapters = graphene.List(SnapshotChapterNode)
class Meta:
model = Snapshot
interfaces = (relay.Node,)
# chapters = relay.ConnectionField('books.schema.connections.ChapterSnapshotConnection')
chapters = DjangoFilterConnectionField(SnapshotChapterNode)
def resolve_chapters(self, info, **kwargs):
@staticmethod
def resolve_snapshot_chapters(parent, info, **kwargs):
# return Chapter.objects.filter(chapter_snapshots__snapshot=self)
return self.chapters.through.objects.all()
return parent.chapters.through.objects.all()
@staticmethod
def resolve_chapters(parent, info, **kwargs):
return [
SnapshotChapter(
chapter_snapshot.chapter,
snapshot=parent,
title_hidden=chapter_snapshot.title_hidden,
description_hidden=chapter_snapshot.description_hidden
)
for chapter_snapshot in parent.chapters.through.objects.all()
]
@staticmethod
def resolve_title(parent, info, **kwargs):

View File

@ -5,8 +5,8 @@ from graphene_django.filter import DjangoFilterConnectionField
from api.utils import get_object
from core.logger import get_logger
from ..models import Book, Topic, Module, Chapter
from .nodes import ContentBlockNode, ChapterNode, ModuleNode, TopicNode
from ..models import Book, Topic, Module, Chapter, Snapshot
from .nodes import ContentBlockNode, ChapterNode, ModuleNode, TopicNode, SnapshotNode
from .connections import TopicConnection, ModuleConnection
logger = get_logger(__name__)
@ -18,6 +18,7 @@ class BookQuery(object):
module = graphene.Field(ModuleNode, slug=graphene.String(), id=graphene.ID())
chapter = relay.Node.Field(ChapterNode)
content_block = relay.Node.Field(ContentBlockNode)
snapshot = relay.Node.Field(SnapshotNode)
topics = relay.ConnectionField(TopicConnection)
modules = relay.ConnectionField(ModuleConnection)
@ -35,6 +36,11 @@ class BookQuery(object):
def resolve_chapters(self, *args, **kwargs):
return Chapter.objects.filter(**kwargs).live()
def resolve_snapshot(self, info, **kwargs):
id = kwargs.get('id')
snapshot = get_object(Snapshot, id)
return snapshot
def resolve_module(self, info, **kwargs):
slug = kwargs.get('slug')
id = kwargs.get('id')

View File

@ -4,7 +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 books.models import Snapshot, ChapterSnapshot
from users.models import User, SchoolClass
from users.services import create_users
@ -55,7 +55,7 @@ mutation CreateSnapshot($input: CreateSnapshotInput!) {
creator {
username
}
chapters {
snapshotChapters {
edges {
node {
id
@ -87,6 +87,26 @@ mutation ApplySnapshot($input: ApplySnapshotInput!) {
}
"""
SNAPSHOT_MODULE_QUERY = """
query SnapshotDetail($id: ID!) {
snapshot(id: $id) {
id
chapters {
id
description
title
titleHidden
descriptionHidden
contentBlocks {
id
title
hidden
}
}
}
}
"""
def edges_to_array(entity):
return [edge['node'] for edge in entity.get('edges')]
@ -103,6 +123,7 @@ class CreateSnapshotTestCase(TestCase):
# module M has a chapter
self.chapter = ChapterFactory(parent=self.module, slug='some-chapter')
ChapterFactory(parent=self.module, slug='some-other-chapter')
# chapter has some content blocks a, b, c
self.title_visible = 'visible'
@ -169,7 +190,7 @@ class CreateSnapshotTestCase(TestCase):
})
self.assertIsNone(result.get('errors'))
snapshot = result.get('data').get('createSnapshot').get('snapshot')
chapter = snapshot.get('chapters').get('edges')[0]['node']
chapter = snapshot.get('snapshotChapters').get('edges')[0]['node']
self.assertIsNotNone(snapshot.get('created'))
self.assertEqual(snapshot.get('creator').get('username'), self.teacher.username)
@ -182,6 +203,7 @@ class CreateSnapshotTestCase(TestCase):
self.assertEqual(len(content_blocks), 2)
self.assertEqual(content_blocks[0]['title'], self.title_visible)
self.assertEqual(content_blocks[1]['title'], self.title_custom)
self.assertEqual(ChapterSnapshot.objects.count(), 2)
def test_apply_snapshot(self):
self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class,
@ -201,3 +223,23 @@ class CreateSnapshotTestCase(TestCase):
})
self.assertIsNone(result.get('errors'))
self._test_module_visibility(client, school_class_name)
def test_display_snapshot_module(self):
self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class,
user=self.teacher)
id = to_global_id('SnapshotNode', self.snapshot.id)
snapshot_result = self.client.execute(SNAPSHOT_MODULE_QUERY, variables={
'id': id
})
self.assertIsNone(snapshot_result.get('errors'))
snapshot = snapshot_result.get('data').get('snapshot')
chapters = snapshot.get('chapters')
self.assertEqual(len(chapters), 2)
chapter = chapters[0]
content_blocks = chapter.get('contentBlocks')
self.assertEqual(len(content_blocks), 3)
first, second, third = content_blocks
self.assertEqual(first['title'], 'visible')
self.assertEqual(second['title'], 'hidden')
self.assertEqual(second['hidden'], True)
self.assertEqual(third['title'], 'custom')

View File

@ -218,20 +218,41 @@ type ChapterBookmarkNodeEdge {
cursor: String!
}
type ChapterInSnapshotNode implements ChapterInterface {
id: ID!
chapter: ChapterNode!
snapshot: SnapshotNode!
titleHidden: Boolean
descriptionHidden: Boolean
description: String
title: String
}
type ChapterInSnapshotNodeConnection {
pageInfo: PageInfo!
edges: [ChapterInSnapshotNodeEdge]!
}
type ChapterInSnapshotNodeEdge {
node: ChapterInSnapshotNode
cursor: String!
}
interface ChapterInterface {
id: ID!
contentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection
description: String
title: String
}
type ChapterNode implements ChapterInterface {
title: String!
title: String
slug: String!
description: String!
description: String
titleHiddenFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection!
descriptionHiddenFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection!
id: ID!
contentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection
bookmark: ChapterBookmarkNode
contentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection
}
type ChapterNodeConnection {
@ -278,14 +299,21 @@ input ContentBlockInput {
visibility: [UserGroupBlockVisibility]
}
type ContentBlockNode implements Node {
title: String!
interface ContentBlockInterface {
id: ID!
title: String
contents: GenericStreamFieldType
type: ContentBlockType
}
type ContentBlockNode implements ContentBlockInterface {
title: String
slug: String!
hiddenFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection!
visibleFor(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection!
userCreated: Boolean!
contents: GenericStreamFieldType
type: ContentBlockType!
type: ContentBlockType
id: ID!
mine: Boolean
bookmarks: [ContentBlockBookmarkNode]
@ -438,6 +466,7 @@ type CustomQuery {
module(slug: String, id: ID): ModuleNode
chapter(id: ID!): ChapterNode
contentBlock(id: ID!): ContentBlockNode
snapshot(id: ID!): SnapshotNode
topics(before: String, after: String, first: Int, last: Int): TopicConnection
modules(before: String, after: String, first: Int, last: Int): ModuleConnection
chapters(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection
@ -638,6 +667,8 @@ type ModuleEdge {
interface ModuleInterface {
id: ID!
pk: Int
heroImage: String
topic: TopicNode
}
type ModuleNode implements ModuleInterface {
@ -651,8 +682,8 @@ type ModuleNode implements ModuleInterface {
objectiveGroups(offset: Int, before: String, after: String, first: Int, last: Int, title: String, module_Slug: String): ObjectiveGroupNodeConnection!
id: ID!
pk: Int
chapters(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection
topic: TopicNode
chapters(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection
solutionsEnabled: Boolean
bookmark: ModuleBookmarkNode
mySubmissions(offset: Int, before: String, after: String, first: Int, last: Int): StudentSubmissionNodeConnection
@ -882,34 +913,31 @@ type SchoolClassNodeEdge {
type SnapshotChapterNode implements ChapterInterface {
id: ID!
chapter: ChapterNode!
snapshot: SnapshotNode!
titleHidden: Boolean
descriptionHidden: Boolean
contentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection
title: String
description: String
title: String
contentBlocks: [SnapshotContentBlockNode]
descriptionHidden: Boolean
titleHidden: Boolean
}
type SnapshotChapterNodeConnection {
pageInfo: PageInfo!
edges: [SnapshotChapterNodeEdge]!
}
type SnapshotChapterNodeEdge {
node: SnapshotChapterNode
cursor: String!
type SnapshotContentBlockNode implements ContentBlockInterface {
id: ID!
title: String
contents: GenericStreamFieldType
type: ContentBlockType
hidden: Boolean
}
type SnapshotNode implements Node {
id: ID!
module: ModuleNode!
chapters(offset: Int, before: String, after: String, first: Int, last: Int, id: ID): SnapshotChapterNodeConnection
chapters: [SnapshotChapterNode]
hiddenContentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection!
created: DateTime!
creator: UserNode
chapterSnapshots(offset: Int, before: String, after: String, first: Int, last: Int, id: ID): SnapshotChapterNodeConnection!
chapterSnapshots(offset: Int, before: String, after: String, first: Int, last: Int, id: ID): ChapterInSnapshotNodeConnection!
title: String
snapshotChapters(offset: Int, before: String, after: String, first: Int, last: Int, id: ID): ChapterInSnapshotNodeConnection
}
input SpellCheckInput {