Refactor module server code
This commit is contained in:
parent
da2253a73d
commit
85706d73d1
|
|
@ -428,7 +428,7 @@ type CustomQuery {
|
||||||
chapter(id: ID!): ChapterNode
|
chapter(id: ID!): ChapterNode
|
||||||
contentBlock(id: ID!): ContentBlockNode
|
contentBlock(id: ID!): ContentBlockNode
|
||||||
books(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): BookNodeConnection
|
books(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): BookNodeConnection
|
||||||
topics(before: String, after: String, first: Int, last: Int): TopicConnection
|
topics(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): TopicNodeConnection
|
||||||
modules(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): ModuleNodeConnection
|
modules(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, slug_Icontains: String, slug_In: [String], title: String, title_Icontains: String, title_In: [String]): ModuleNodeConnection
|
||||||
chapters(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection
|
chapters(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ChapterNodeConnection
|
||||||
objectiveGroup(id: ID!): ObjectiveGroupNode
|
objectiveGroup(id: ID!): ObjectiveGroupNode
|
||||||
|
|
@ -955,16 +955,6 @@ type TeamNode implements Node {
|
||||||
pk: Int
|
pk: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type TopicConnection {
|
|
||||||
pageInfo: PageInfo!
|
|
||||||
edges: [TopicEdge]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type TopicEdge {
|
|
||||||
node: TopicNode
|
|
||||||
cursor: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type TopicNode implements Node {
|
type TopicNode implements Node {
|
||||||
title: String!
|
title: String!
|
||||||
slug: String!
|
slug: String!
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import graphene
|
||||||
|
from graphene import relay
|
||||||
|
|
||||||
|
from books.schema.nodes import ModuleNode, TopicNode
|
||||||
|
|
||||||
|
|
||||||
|
class NodeConnection(relay.Connection):
|
||||||
|
"""
|
||||||
|
Custom connection type, so we don't need to deal with the edges and nodes if we don't want to, while still
|
||||||
|
adhering to the relay specification
|
||||||
|
Idea from: https://javascript.plainenglish.io/graphql-pagination-using-edges-vs-nodes-in-connections-f2ddb8edffa0
|
||||||
|
|
||||||
|
Always has property called `nodes` which is filled with the Meta node class.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
class TopicConnection(NodeConnection):
|
||||||
|
class Meta:
|
||||||
|
node = TopicNode
|
||||||
|
# will have
|
||||||
|
# nodes = graphene.List(TopicNode)
|
||||||
|
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def resolve_nodes(self, *args, **kwargs):
|
||||||
|
return [edge.node for edge in self.edges]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __init_subclass_with_meta__(cls, node=None, name=None, **options):
|
||||||
|
cls.nodes = graphene.List(node)
|
||||||
|
return super().__init_subclass_with_meta__(
|
||||||
|
node, name, **options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TopicConnection(NodeConnection):
|
||||||
|
class Meta:
|
||||||
|
node = TopicNode
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleConnection(NodeConnection):
|
||||||
|
class Meta:
|
||||||
|
node = ModuleNode
|
||||||
|
|
@ -4,7 +4,7 @@ from graphene import relay
|
||||||
from api.utils import get_object
|
from api.utils import get_object
|
||||||
from books.models import Chapter
|
from books.models import Chapter
|
||||||
from books.schema.inputs import UserGroupBlockVisibility
|
from books.schema.inputs import UserGroupBlockVisibility
|
||||||
from books.schema.queries import ChapterNode
|
from books.schema.nodes import ChapterNode
|
||||||
from users.models import SchoolClass
|
from users.models import SchoolClass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from graphql_relay import from_global_id
|
||||||
from api.utils import get_object, get_errors
|
from api.utils import get_object, get_errors
|
||||||
from books.models import ContentBlock, Chapter, SchoolClass
|
from books.models import ContentBlock, Chapter, SchoolClass
|
||||||
from books.schema.inputs import ContentBlockInput
|
from books.schema.inputs import ContentBlockInput
|
||||||
from books.schema.queries import ContentBlockNode
|
from ..nodes import ContentBlockNode
|
||||||
from core.utils import set_hidden_for, set_visible_for
|
from core.utils import set_hidden_for, set_visible_for
|
||||||
from .utils import handle_content_block, set_user_defined_block_type
|
from .utils import handle_content_block, set_user_defined_block_type
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from graphene import relay
|
||||||
|
|
||||||
from api.utils import get_object
|
from api.utils import get_object
|
||||||
from books.models import Module, RecentModule
|
from books.models import Module, RecentModule
|
||||||
from books.schema.queries import ModuleNode
|
from books.schema.nodes import ModuleNode
|
||||||
from users.models import SchoolClass
|
from users.models import SchoolClass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from graphene import relay
|
||||||
|
|
||||||
from api.utils import get_object
|
from api.utils import get_object
|
||||||
from books.models import Topic
|
from books.models import Topic
|
||||||
from books.schema.queries import TopicNode
|
from books.schema.nodes import TopicNode
|
||||||
|
|
||||||
|
|
||||||
class UpdateLastTopic(relay.ClientIDMutation):
|
class UpdateLastTopic(relay.ClientIDMutation):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,260 @@
|
||||||
|
import graphene
|
||||||
|
from django.db.models import Q
|
||||||
|
from graphene import relay
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from graphene_django.filter import DjangoFilterConnectionField
|
||||||
|
|
||||||
|
from assignments.models import StudentSubmission
|
||||||
|
from assignments.schema.types import StudentSubmissionNode
|
||||||
|
from books.models import ContentBlock, Chapter, Module, RecentModule, Topic, Book
|
||||||
|
from books.utils import are_solutions_enabled_for
|
||||||
|
from core.logger import get_logger
|
||||||
|
from notes.models import ContentBlockBookmark, ChapterBookmark, ModuleBookmark
|
||||||
|
from notes.schema import ContentBlockBookmarkNode, ChapterBookmarkNode, ModuleBookmarkNode
|
||||||
|
from rooms.models import ModuleRoomSlug
|
||||||
|
from surveys.models import Answer
|
||||||
|
from surveys.schema import AnswerNode
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TextBlockNode(graphene.ObjectType):
|
||||||
|
text = graphene.String()
|
||||||
|
|
||||||
|
def resolve_text(root, info, **kwargs):
|
||||||
|
return root['value']['text']
|
||||||
|
|
||||||
|
|
||||||
|
class ContentNode(graphene.Union):
|
||||||
|
class Meta:
|
||||||
|
types = (TextBlockNode,)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resolve_type(cls, instance, info):
|
||||||
|
logger.info(instance)
|
||||||
|
if instance['type'] == 'text_block':
|
||||||
|
return TextBlockNode
|
||||||
|
|
||||||
|
|
||||||
|
class ContentBlockNode(DjangoObjectType):
|
||||||
|
mine = graphene.Boolean()
|
||||||
|
bookmarks = graphene.List(ContentBlockBookmarkNode)
|
||||||
|
|
||||||
|
# contents = graphene.List(ContentNode)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ContentBlock
|
||||||
|
only_fields = [
|
||||||
|
'slug', 'title', 'type', 'contents', 'hidden_for', 'visible_for', 'user_created'
|
||||||
|
]
|
||||||
|
filter_fields = [
|
||||||
|
'slug', 'title',
|
||||||
|
]
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
def resolve_mine(self, info, **kwargs):
|
||||||
|
return self.owner is not None and self.owner.pk == info.context.user.pk
|
||||||
|
|
||||||
|
def resolve_contents(self, info, **kwargs):
|
||||||
|
updated_stream_data = []
|
||||||
|
for content in self.contents.stream_data:
|
||||||
|
# only show solutions to teachers and students for whom their teachers have them enabled
|
||||||
|
if content['type'] == 'solution' \
|
||||||
|
and not (are_solutions_enabled_for(info.context.user, self.module) or info.context.user.is_teacher()):
|
||||||
|
logger.debug('Solution is hidden for this user')
|
||||||
|
continue
|
||||||
|
|
||||||
|
if content['type'] == 'content_list_item':
|
||||||
|
for index, list_block in enumerate(content['value']):
|
||||||
|
content['value'][index] = process_module_room_slug_block(list_block)
|
||||||
|
|
||||||
|
content = process_module_room_slug_block(content)
|
||||||
|
updated_stream_data.append(content)
|
||||||
|
|
||||||
|
self.contents.stream_data = updated_stream_data
|
||||||
|
return self.contents
|
||||||
|
|
||||||
|
def resolve_bookmarks(self, info, **kwargs):
|
||||||
|
return ContentBlockBookmark.objects.filter(
|
||||||
|
user=info.context.user,
|
||||||
|
content_block=self
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ChapterNode(DjangoObjectType):
|
||||||
|
content_blocks = DjangoFilterConnectionField(ContentBlockNode)
|
||||||
|
bookmark = graphene.Field(ChapterBookmarkNode)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Chapter
|
||||||
|
only_fields = [
|
||||||
|
'slug', 'title', 'description', 'title_hidden_for', 'description_hidden_for'
|
||||||
|
]
|
||||||
|
filter_fields = [
|
||||||
|
'slug', 'title',
|
||||||
|
]
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
def resolve_content_blocks(self, info, **kwargs):
|
||||||
|
user = info.context.user
|
||||||
|
school_classes = user.school_classes.values_list('pk')
|
||||||
|
|
||||||
|
by_parent = ContentBlock.get_by_parent(self) \
|
||||||
|
.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))
|
||||||
|
|
||||||
|
if user.has_perm('users.can_manage_school_class_content'): # teacher
|
||||||
|
return by_parent.filter(default_blocks | owned_by_user | teacher_created_and_visible).distinct()
|
||||||
|
else: # student
|
||||||
|
return by_parent.filter(default_blocks | teacher_created_and_visible).distinct()
|
||||||
|
|
||||||
|
def resolve_bookmark(self, info, **kwargs):
|
||||||
|
return ChapterBookmark.objects.filter(
|
||||||
|
user=info.context.user,
|
||||||
|
chapter=self
|
||||||
|
).first()
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleNode(DjangoObjectType):
|
||||||
|
pk = graphene.Int()
|
||||||
|
chapters = DjangoFilterConnectionField(ChapterNode)
|
||||||
|
topic = graphene.Field('books.schema.queries.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)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Module
|
||||||
|
only_fields = [
|
||||||
|
'slug', 'title', 'meta_title', 'teaser', 'intro', 'objective_groups', 'assignments', 'hero_image', 'topic'
|
||||||
|
]
|
||||||
|
filter_fields = {
|
||||||
|
'slug': ['exact', 'icontains', 'in'],
|
||||||
|
'title': ['exact', 'icontains', 'in'],
|
||||||
|
}
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
def resolve_pk(self, info, **kwargs):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def resolve_hero_image(self, info, **kwargs):
|
||||||
|
if self.hero_image:
|
||||||
|
return self.hero_image.file.url
|
||||||
|
|
||||||
|
def resolve_chapters(self, info, **kwargs):
|
||||||
|
return Chapter.get_by_parent(self)
|
||||||
|
|
||||||
|
def resolve_topic(self, info, **kwargs):
|
||||||
|
return self.get_parent().specific
|
||||||
|
|
||||||
|
def resolve_solutions_enabled(self, info, **kwargs):
|
||||||
|
school_class = info.context.user.selected_class()
|
||||||
|
return self.solutions_enabled_for.filter(pk=school_class.pk).exists() if school_class is not None else False
|
||||||
|
|
||||||
|
def resolve_bookmark(self, info, **kwags):
|
||||||
|
return ModuleBookmark.objects.filter(
|
||||||
|
user=info.context.user,
|
||||||
|
module=self
|
||||||
|
).first()
|
||||||
|
|
||||||
|
def resolve_my_submissions(self, info, **kwargs):
|
||||||
|
user = info.context.user
|
||||||
|
return StudentSubmission.objects.filter(student=user, assignment__module=self)
|
||||||
|
# we want:
|
||||||
|
# StudentSubmission
|
||||||
|
|
||||||
|
def resolve_my_answers(self, info, **kwargs):
|
||||||
|
user = info.context.user
|
||||||
|
return Answer.objects.filter(owner=user, survey__module=self)
|
||||||
|
# Survey
|
||||||
|
|
||||||
|
def resolve_my_content_bookmarks(self, info, **kwargs):
|
||||||
|
user = info.context.user
|
||||||
|
content_blocks = ContentBlock.objects.live().descendant_of(self)
|
||||||
|
return ContentBlockBookmark.objects.filter(content_block__in=content_blocks, user=user)
|
||||||
|
# Bookmark Text
|
||||||
|
# Bookmark Image etc
|
||||||
|
# Bookmark Other
|
||||||
|
# Note
|
||||||
|
#
|
||||||
|
|
||||||
|
def resolve_my_chapter_bookmarks(self, info, **kwargs):
|
||||||
|
user = info.context.user
|
||||||
|
chapters = Chapter.objects.live().descendant_of(self)
|
||||||
|
return ChapterBookmark.objects.filter(chapter__in=chapters, user=user)
|
||||||
|
|
||||||
|
def resolve_objective_groups(self, root, **kwargs):
|
||||||
|
return self.objective_groups.all() \
|
||||||
|
.prefetch_related('hidden_for')
|
||||||
|
|
||||||
|
|
||||||
|
class RecentModuleNode(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = RecentModule
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
|
||||||
|
class TopicNode(DjangoObjectType):
|
||||||
|
pk = graphene.Int()
|
||||||
|
modules = DjangoFilterConnectionField(ModuleNode)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Topic
|
||||||
|
only_fields = [
|
||||||
|
'slug', 'title', 'teaser', 'description', 'vimeo_id', 'order', 'instructions'
|
||||||
|
]
|
||||||
|
filter_fields = {
|
||||||
|
'slug': ['exact', 'icontains', 'in'],
|
||||||
|
'title': ['exact', 'icontains', 'in'],
|
||||||
|
}
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
def resolve_pk(self, *args, **kwargs):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def resolve_modules(self, *args, **kwargs):
|
||||||
|
return Module.get_by_parent(self)
|
||||||
|
|
||||||
|
|
||||||
|
class BookNode(DjangoObjectType):
|
||||||
|
pk = graphene.Int()
|
||||||
|
topics = DjangoFilterConnectionField(TopicNode)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Book
|
||||||
|
only_fields = [
|
||||||
|
'slug', 'title',
|
||||||
|
]
|
||||||
|
filter_fields = {
|
||||||
|
'slug': ['exact', 'icontains', 'in'],
|
||||||
|
'title': ['exact', 'icontains', 'in'],
|
||||||
|
}
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
def resolve_pk(self, *args, **kwargs):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def resolve_topics(self, *args, **kwargs):
|
||||||
|
return Topic.get_by_parent(self)
|
||||||
|
|
||||||
|
|
||||||
|
def process_module_room_slug_block(content):
|
||||||
|
if content['type'] == 'module_room_slug':
|
||||||
|
try:
|
||||||
|
module_room_slug = ModuleRoomSlug.objects.get(title=content['value']['title'])
|
||||||
|
content['value'] = {
|
||||||
|
'title': content['value']['title'],
|
||||||
|
'slug': module_room_slug.slug
|
||||||
|
}
|
||||||
|
except ModuleRoomSlug.DoesNotExist:
|
||||||
|
pass
|
||||||
|
return content
|
||||||
|
|
@ -1,244 +1,15 @@
|
||||||
import graphene
|
import graphene
|
||||||
from django.db.models import Q
|
|
||||||
from graphene import relay
|
from graphene import relay
|
||||||
from graphene_django import DjangoObjectType
|
|
||||||
from graphene_django.filter import DjangoFilterConnectionField
|
from graphene_django.filter import DjangoFilterConnectionField
|
||||||
|
|
||||||
from api.utils import get_object
|
from api.utils import get_object
|
||||||
from assignments.models import StudentSubmission
|
from core.logger import get_logger
|
||||||
from assignments.schema.types import StudentSubmissionNode
|
|
||||||
from books.utils import are_solutions_enabled_for
|
|
||||||
from notes.models import ContentBlockBookmark, ChapterBookmark, ModuleBookmark
|
|
||||||
from notes.schema import ContentBlockBookmarkNode, ChapterBookmarkNode, ModuleBookmarkNode
|
|
||||||
from rooms.models import ModuleRoomSlug
|
|
||||||
from surveys.models import Answer
|
|
||||||
from surveys.schema import AnswerNode
|
|
||||||
from ..models import Book, Topic, Module, Chapter, ContentBlock, RecentModule
|
|
||||||
|
|
||||||
|
from ..models import Book, Topic, Module, Chapter
|
||||||
|
from .nodes import ContentBlockNode, ChapterNode, ModuleNode, TopicNode, BookNode
|
||||||
|
from .connections import TopicConnection, ModuleConnection
|
||||||
|
|
||||||
def process_module_room_slug_block(content):
|
logger = get_logger(__name__)
|
||||||
if content['type'] == 'module_room_slug':
|
|
||||||
try:
|
|
||||||
module_room_slug = ModuleRoomSlug.objects.get(title=content['value']['title'])
|
|
||||||
content['value'] = {
|
|
||||||
'title': content['value']['title'],
|
|
||||||
'slug': module_room_slug.slug
|
|
||||||
}
|
|
||||||
except ModuleRoomSlug.DoesNotExist:
|
|
||||||
pass
|
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
class ContentBlockNode(DjangoObjectType):
|
|
||||||
mine = graphene.Boolean()
|
|
||||||
bookmarks = graphene.List(ContentBlockBookmarkNode)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ContentBlock
|
|
||||||
only_fields = [
|
|
||||||
'slug', 'title', 'type', 'contents', 'hidden_for', 'visible_for', 'user_created'
|
|
||||||
]
|
|
||||||
filter_fields = [
|
|
||||||
'slug', 'title',
|
|
||||||
]
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
def resolve_mine(self, info, **kwargs):
|
|
||||||
return self.owner is not None and self.owner.pk == info.context.user.pk
|
|
||||||
|
|
||||||
def resolve_contents(self, info, **kwargs):
|
|
||||||
updated_stream_data = []
|
|
||||||
for content in self.contents.stream_data:
|
|
||||||
# only show solutions to teachers and students for whom their teachers have them enabled
|
|
||||||
if content['type'] == 'solution' and not (are_solutions_enabled_for(info.context.user, self.module) or info.context.user.is_teacher()):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if content['type'] == 'content_list_item':
|
|
||||||
for index, list_block in enumerate(content['value']):
|
|
||||||
content['value'][index] = process_module_room_slug_block(list_block)
|
|
||||||
|
|
||||||
content = process_module_room_slug_block(content)
|
|
||||||
updated_stream_data.append(content)
|
|
||||||
|
|
||||||
self.contents.stream_data = updated_stream_data
|
|
||||||
return self.contents
|
|
||||||
|
|
||||||
def resolve_bookmarks(self, info, **kwargs):
|
|
||||||
return ContentBlockBookmark.objects.filter(
|
|
||||||
user=info.context.user,
|
|
||||||
content_block=self
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ChapterNode(DjangoObjectType):
|
|
||||||
content_blocks = DjangoFilterConnectionField(ContentBlockNode)
|
|
||||||
bookmark = graphene.Field(ChapterBookmarkNode)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Chapter
|
|
||||||
only_fields = [
|
|
||||||
'slug', 'title', 'description', 'title_hidden_for', 'description_hidden_for'
|
|
||||||
]
|
|
||||||
filter_fields = [
|
|
||||||
'slug', 'title',
|
|
||||||
]
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
def resolve_content_blocks(self, info, **kwargs):
|
|
||||||
user = info.context.user
|
|
||||||
school_classes = user.school_classes.values_list('pk')
|
|
||||||
|
|
||||||
by_parent = ContentBlock.get_by_parent(self) \
|
|
||||||
.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))
|
|
||||||
|
|
||||||
if user.has_perm('users.can_manage_school_class_content'): # teacher
|
|
||||||
return by_parent.filter(default_blocks | owned_by_user | teacher_created_and_visible).distinct()
|
|
||||||
else: # student
|
|
||||||
return by_parent.filter(default_blocks | teacher_created_and_visible).distinct()
|
|
||||||
|
|
||||||
def resolve_bookmark(self, info, **kwags):
|
|
||||||
return ChapterBookmark.objects.filter(
|
|
||||||
user=info.context.user,
|
|
||||||
chapter=self
|
|
||||||
).first()
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleNode(DjangoObjectType):
|
|
||||||
pk = graphene.Int()
|
|
||||||
chapters = DjangoFilterConnectionField(ChapterNode)
|
|
||||||
topic = graphene.Field('books.schema.queries.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)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Module
|
|
||||||
only_fields = [
|
|
||||||
'slug', 'title', 'meta_title', 'teaser', 'intro', 'objective_groups', 'assignments', 'hero_image', 'topic'
|
|
||||||
]
|
|
||||||
filter_fields = {
|
|
||||||
'slug': ['exact', 'icontains', 'in'],
|
|
||||||
'title': ['exact', 'icontains', 'in'],
|
|
||||||
}
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
def resolve_pk(self, info, **kwargs):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
def resolve_hero_image(self, info, **kwargs):
|
|
||||||
if self.hero_image:
|
|
||||||
return self.hero_image.file.url
|
|
||||||
|
|
||||||
def resolve_chapters(self, info, **kwargs):
|
|
||||||
return Chapter.get_by_parent(self)
|
|
||||||
|
|
||||||
def resolve_topic(self, info, **kwargs):
|
|
||||||
return self.get_parent().specific
|
|
||||||
|
|
||||||
def resolve_solutions_enabled(self, info, **kwargs):
|
|
||||||
school_class = info.context.user.selected_class()
|
|
||||||
return self.solutions_enabled_for.filter(pk=school_class.pk).exists() if school_class is not None else False
|
|
||||||
|
|
||||||
def resolve_bookmark(self, info, **kwags):
|
|
||||||
return ModuleBookmark.objects.filter(
|
|
||||||
user=info.context.user,
|
|
||||||
module=self
|
|
||||||
).first()
|
|
||||||
|
|
||||||
def resolve_my_submissions(self, info, **kwargs):
|
|
||||||
user = info.context.user
|
|
||||||
return StudentSubmission.objects.filter(student=user, assignment__module=self)
|
|
||||||
# we want:
|
|
||||||
# StudentSubmission
|
|
||||||
|
|
||||||
def resolve_my_answers(self, info, **kwargs):
|
|
||||||
user = info.context.user
|
|
||||||
return Answer.objects.filter(owner=user, survey__module=self)
|
|
||||||
# Survey
|
|
||||||
|
|
||||||
def resolve_my_content_bookmarks(self, info, **kwargs):
|
|
||||||
user = info.context.user
|
|
||||||
content_blocks = ContentBlock.objects.live().descendant_of(self)
|
|
||||||
return ContentBlockBookmark.objects.filter(content_block__in=content_blocks, user=user)
|
|
||||||
# Bookmark Text
|
|
||||||
# Bookmark Image etc
|
|
||||||
# Bookmark Other
|
|
||||||
# Note
|
|
||||||
#
|
|
||||||
|
|
||||||
def resolve_my_chapter_bookmarks(self, info, **kwargs):
|
|
||||||
user = info.context.user
|
|
||||||
chapters = Chapter.objects.live().descendant_of(self)
|
|
||||||
return ChapterBookmark.objects.filter(chapter__in=chapters, user=user)
|
|
||||||
|
|
||||||
def resolve_objective_groups(self, root, **kwargs):
|
|
||||||
return self.objective_groups.all() \
|
|
||||||
.prefetch_related('hidden_for')
|
|
||||||
|
|
||||||
|
|
||||||
class RecentModuleNode(DjangoObjectType):
|
|
||||||
class Meta:
|
|
||||||
model = RecentModule
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
|
|
||||||
class TopicNode(DjangoObjectType):
|
|
||||||
pk = graphene.Int()
|
|
||||||
modules = DjangoFilterConnectionField(ModuleNode)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Topic
|
|
||||||
only_fields = [
|
|
||||||
'slug', 'title', 'teaser', 'description', 'vimeo_id', 'order', 'instructions'
|
|
||||||
]
|
|
||||||
filter_fields = {
|
|
||||||
'slug': ['exact', 'icontains', 'in'],
|
|
||||||
'title': ['exact', 'icontains', 'in'],
|
|
||||||
}
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
def resolve_pk(self, *args, **kwargs):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
def resolve_modules(self, *args, **kwargs):
|
|
||||||
return Module.get_by_parent(self)
|
|
||||||
|
|
||||||
|
|
||||||
class BookNode(DjangoObjectType):
|
|
||||||
pk = graphene.Int()
|
|
||||||
topics = DjangoFilterConnectionField(TopicNode)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Book
|
|
||||||
only_fields = [
|
|
||||||
'slug', 'title',
|
|
||||||
]
|
|
||||||
filter_fields = {
|
|
||||||
'slug': ['exact', 'icontains', 'in'],
|
|
||||||
'title': ['exact', 'icontains', 'in'],
|
|
||||||
}
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
def resolve_pk(self, *args, **kwargs):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
def resolve_topics(self, *args, **kwargs):
|
|
||||||
return Topic.get_by_parent(self)
|
|
||||||
|
|
||||||
|
|
||||||
class TopicConnection(relay.Connection):
|
|
||||||
class Meta:
|
|
||||||
node = TopicNode
|
|
||||||
|
|
||||||
|
|
||||||
class BookQuery(object):
|
class BookQuery(object):
|
||||||
|
|
@ -250,7 +21,9 @@ class BookQuery(object):
|
||||||
content_block = relay.Node.Field(ContentBlockNode)
|
content_block = relay.Node.Field(ContentBlockNode)
|
||||||
|
|
||||||
books = DjangoFilterConnectionField(BookNode)
|
books = DjangoFilterConnectionField(BookNode)
|
||||||
topics = relay.ConnectionField(TopicConnection)
|
topics = DjangoFilterConnectionField(TopicNode)
|
||||||
|
# topics = relay.ConnectionField(TopicConnection)
|
||||||
|
# modules = relay.ConnectionField(ModuleConnection)
|
||||||
modules = DjangoFilterConnectionField(ModuleNode)
|
modules = DjangoFilterConnectionField(ModuleNode)
|
||||||
chapters = DjangoFilterConnectionField(ChapterNode)
|
chapters = DjangoFilterConnectionField(ChapterNode)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue