-
+
@@ -36,8 +38,7 @@
diff --git a/client/src/pages/survey.vue b/client/src/pages/survey.vue
index cc21caf6..9f1a7d5a 100644
--- a/client/src/pages/survey.vue
+++ b/client/src/pages/survey.vue
@@ -17,9 +17,9 @@
diff --git a/client/src/router/index.js b/client/src/router/index.js
index 86ece71d..cac8b30e 100644
--- a/client/src/router/index.js
+++ b/client/src/router/index.js
@@ -24,6 +24,7 @@ import authRoutes from './auth.routes';
import roomRoutes from './room.routes';
import store from '@/store/index';
+import {LAYOUT_SIMPLE} from '@/router/core.constants';
const routes = [
{
@@ -34,24 +35,25 @@ const routes = [
...moduleRoutes,
...authRoutes,
...roomRoutes,
- {path: '/article/:slug', name: 'article', component: article, meta: {layout: 'simple'}},
+ ...onboardingRoutes,
+ ...portfolioRoutes,
+ ...meRoutes,
+ {path: '/article/:slug', name: 'article', component: article, meta: {layout: LAYOUT_SIMPLE}},
{
path: '/instruments/',
name: 'instrument-overview',
component: instrumentOverview,
},
- {path: '/instrument/:slug', name: 'instrument', component: instrument, meta: {layout: 'simple'}},
- {path: '/submission/:id', name: 'submission', component: submission, meta: {layout: 'simple'}},
- ...portfolioRoutes,
+ {path: '/instrument/:slug', name: 'instrument', component: instrument, meta: {layout: LAYOUT_SIMPLE}},
+ {path: '/submission/:id', name: 'submission', component: submission, meta: {layout: LAYOUT_SIMPLE}},
{path: '/topic/:topicSlug', name: 'topic', component: topic, alias: '/book/topic/:topicSlug'},
- ...meRoutes,
- {path: '/join-class', name: 'join-class', component: joinClass, meta: {layout: 'simple'}},
+ {path: '/join-class', name: 'join-class', component: joinClass, meta: {layout: LAYOUT_SIMPLE}},
{
path: '/survey/:id',
component: surveyPage,
name: 'survey',
props: true,
- meta: {layout: 'simple'},
+ meta: {layout: LAYOUT_SIMPLE},
},
{
path: '/check-email',
@@ -93,7 +95,6 @@ const routes = [
component: news,
name: 'news',
},
- ...onboardingRoutes,
{path: '/styleguide', component: styleGuidePage},
{
path: '*',
diff --git a/client/src/router/me.routes.js b/client/src/router/me.routes.js
index b78197b4..93fa1c26 100644
--- a/client/src/router/me.routes.js
+++ b/client/src/router/me.routes.js
@@ -11,6 +11,7 @@ import joinTeam from '@/pages/me/joinTeam';
import createTeam from '@/pages/me/createTeam';
import {CREATE_TEAM, JOIN_TEAM, MY_TEAM, SHOW_SCHOOL_CLASS_CODE, SHOW_TEAM_CODE} from './me.names';
+import {LAYOUT_SIMPLE} from '@/router/core.constants';
export default [
{
@@ -32,23 +33,23 @@ export default [
alias: 'create-class',
name: 'create-class',
component: createClass,
- meta: {layout: 'simple'},
+ meta: {layout: LAYOUT_SIMPLE},
},
{
path: 'class/code',
alias: 'show-code',
name: SHOW_SCHOOL_CLASS_CODE,
component: showSchoolClassCode,
- meta: {layout: 'simple'},
+ meta: {layout: LAYOUT_SIMPLE},
},
{path: 'team', name: MY_TEAM, component: myTeam, meta: {isProfile: true}},
- {path: 'team/join', name: JOIN_TEAM, component: joinTeam, meta: {isProfile: true, layout: 'simple'}},
- {path: 'team/create', name: CREATE_TEAM, component: createTeam, meta: {isProfile: true, layout: 'simple'}},
+ {path: 'team/join', name: JOIN_TEAM, component: joinTeam, meta: {isProfile: true, layout: LAYOUT_SIMPLE}},
+ {path: 'team/create', name: CREATE_TEAM, component: createTeam, meta: {isProfile: true, layout: LAYOUT_SIMPLE}},
{
path: 'team/code',
name: SHOW_TEAM_CODE,
component: showTeamCode,
- meta: {layout: 'simple'},
+ meta: {layout: LAYOUT_SIMPLE},
},
],
},
diff --git a/client/src/router/module.routes.js b/client/src/router/module.routes.js
index 88b09458..cf740b7d 100644
--- a/client/src/router/module.routes.js
+++ b/client/src/router/module.routes.js
@@ -2,10 +2,18 @@ import moduleBase from '@/pages/module/module-base';
import module from '@/pages/module/module';
import submissions from '@/pages/submissions';
import moduleVisibility from '@/pages/module/moduleVisibility';
-import {MODULE_PAGE, MODULE_SETTINGS_PAGE, SUBMISSIONS_PAGE, VISIBILITY_PAGE, SNAPSHOT_LIST, SNAPSHOT_DETAIL} from '@/router/module.names';
+import {
+ MODULE_PAGE,
+ MODULE_SETTINGS_PAGE,
+ SNAPSHOT_DETAIL,
+ SNAPSHOT_LIST,
+ SUBMISSIONS_PAGE,
+ VISIBILITY_PAGE,
+} from '@/router/module.names';
import settingsPage from '@/pages/module/moduleSettings';
import snapshots from '@/pages/snapshot/snapshots';
import snapshot from '@/pages/snapshot/snapshot';
+import {LAYOUT_SIMPLE} from '@/router/core.constants';
export default [
{
@@ -40,7 +48,7 @@ export default [
name: VISIBILITY_PAGE,
component: moduleVisibility,
meta: {
- layout: 'simple',
+ layout: LAYOUT_SIMPLE,
hideNavigation: true,
},
},
@@ -56,8 +64,13 @@ export default [
path: 'snapshot/:id',
component: snapshot,
name: SNAPSHOT_DETAIL,
- props: true
- }
+ props: true,
+ meta: {
+ layout: LAYOUT_SIMPLE,
+ hideNavigation: true,
+ fullWidth: true
+ },
+ },
],
},
];
diff --git a/client/src/styles/_buttons.scss b/client/src/styles/_buttons.scss
index 09d43154..0610e307 100644
--- a/client/src/styles/_buttons.scss
+++ b/client/src/styles/_buttons.scss
@@ -12,9 +12,17 @@
&--white-bg {
background-color: $color-white;
}
+ @mixin disabled {
+ cursor: default;
+ }
&--disabled {
+ @include disabled;
background-color: $color-silver-light;
}
+ &--disabled-alt {
+ @include disabled;
+ opacity: 0.3;
+ }
&--big {
padding: 15px;
}
diff --git a/client/src/styles/_forms.scss b/client/src/styles/_forms.scss
index 80b35650..8058fd4b 100644
--- a/client/src/styles/_forms.scss
+++ b/client/src/styles/_forms.scss
@@ -73,6 +73,7 @@ $icon-size: 20px;
&__icon {
width: $icon-size;
height: $icon-size;
+ overflow: hidden;
display: flex;
border: 2px solid $color-silver-dark;
justify-content: center;
diff --git a/server/books/schema/nodes/module.py b/server/books/schema/nodes/module.py
index 0ec64f38..051d3faa 100644
--- a/server/books/schema/nodes/module.py
+++ b/server/books/schema/nodes/module.py
@@ -10,6 +10,7 @@ from books.schema.interfaces.module import ModuleInterface
from books.schema.nodes.chapter import ChapterNode
from notes.models import ModuleBookmark, ContentBlockBookmark, ChapterBookmark
from notes.schema import ModuleBookmarkNode, ContentBlockBookmarkNode, ChapterBookmarkNode
+from objectives.schema import ObjectiveGroupNode
from surveys.models import Answer
from surveys.schema import AnswerNode
@@ -26,7 +27,7 @@ class ModuleNode(DjangoObjectType):
}
interfaces = (ModuleInterface,)
- chapters = DjangoFilterConnectionField(ChapterNode)
+ chapters = graphene.List(ChapterNode)
solutions_enabled = graphene.Boolean()
bookmark = graphene.Field(ModuleBookmarkNode)
my_submissions = DjangoFilterConnectionField(StudentSubmissionNode)
@@ -34,6 +35,7 @@ class ModuleNode(DjangoObjectType):
my_content_bookmarks = DjangoFilterConnectionField(ContentBlockBookmarkNode)
my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode)
snapshots = graphene.List('books.schema.nodes.SnapshotNode')
+ objective_groups = graphene.List(ObjectiveGroupNode)
def resolve_chapters(self, info, **kwargs):
return Chapter.get_by_parent(self)
diff --git a/server/books/schema/queries.py b/server/books/schema/queries.py
index 43fc75ea..3db2b931 100644
--- a/server/books/schema/queries.py
+++ b/server/books/schema/queries.py
@@ -44,14 +44,17 @@ class BookQuery(object):
slug = kwargs.get('slug')
id = kwargs.get('id')
module = None
+ try:
+ if id is not None:
+ module = get_object(Module, id)
- if id is not None:
- module = get_object(Module, id)
+ elif slug is not None:
+ module = Module.objects.get(slug=slug)
- elif slug is not None:
- module = Module.objects.get(slug=slug)
+ return module
- return module
+ except Module.DoesNotExist:
+ return None
def resolve_topic(self, info, **kwargs):
slug = kwargs.get('slug')
diff --git a/server/books/tests/test_create_snapshot.py b/server/books/tests/test_create_snapshot.py
index 7c8c46fc..ce2c9022 100644
--- a/server/books/tests/test_create_snapshot.py
+++ b/server/books/tests/test_create_snapshot.py
@@ -6,28 +6,25 @@ 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 users.factories import SchoolClassFactory
from users.models import User, SchoolClass
MODULE_QUERY = """
-query ModulesQuery($slug: String!) {
- module(slug: $slug) {
+query ModulesQuery($slug: String, $id: ID) {
+ module(slug: $slug, id: $id) {
id
title
chapters {
- edges {
- node {
- id
- contentBlocks {
- id
- title
- visibleFor {
- name
- }
- hiddenFor {
- name
- }
- }
+ id
+ contentBlocks {
+ id
+ title
+ visibleFor {
+ name
}
+ hiddenFor {
+ name
+ }
}
}
}
@@ -226,3 +223,43 @@ class CreateSnapshotTestCase(SkillboxTestCase):
self.assertEqual(second['title'], 'hidden')
self.assertEqual(second['hidden'], True)
self.assertEqual(third['title'], 'custom')
+
+ def test_not_too_much_user_creator_info(self):
+ self.assertTrue(False)
+
+ def test_apply_initial_snapshot(self):
+ teacher2 = User.objects.get(username='teacher2')
+ teacher2_client = self.get_client(user=teacher2)
+ third_class = SchoolClassFactory(
+ users=[teacher2],
+ name='third_class'
+ )
+
+ # make a neutral snapshot, nothing new, nothing hidden
+ result = teacher2_client.execute(CREATE_SNAPSHOT_MUTATION, variables={
+ 'input': {
+ 'module': self.slug,
+ 'selectedClass': to_global_id('SchoolClassNode', third_class.pk),
+ }
+ })
+ self.assertIsNone(result.get('errors'))
+ snapshot_id = result['data']['createSnapshot']['snapshot']['id']
+
+ result = self.client.execute(APPLY_SNAPSHOT_MUTATION, variables={
+ 'input': {
+ 'snapshot': snapshot_id,
+ 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk),
+ }
+ })
+ self.assertIsNone(result.get('errors'))
+
+ result = self.client.execute(MODULE_QUERY, variables={
+ 'slug': self.module.slug
+ })
+ self.assertIsNone(result.get('errors'))
+ module = result['data']['module']
+ chapter1, chapter2 = module['chapters']
+ cb1, cb2, cb3 = chapter1['contentBlocks']
+ self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in cb1['hiddenFor']])
+ self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in cb2['hiddenFor']])
+ self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in cb3['visibleFor']])
diff --git a/server/objectives/schema.py b/server/objectives/schema.py
index d2f203a9..84614661 100644
--- a/server/objectives/schema.py
+++ b/server/objectives/schema.py
@@ -8,9 +8,30 @@ from core.mixins import HiddenAndVisibleForMixin, HiddenForMixin
from objectives.models import ObjectiveGroup, Objective
+class ObjectiveNode(DjangoObjectType, HiddenAndVisibleForMixin):
+ pk = graphene.Int()
+ user_created = graphene.Boolean()
+ mine = graphene.Boolean()
+
+ class Meta:
+ model = Objective
+ filter_fields = ['text']
+ interfaces = (relay.Node,)
+
+ def resolve_objective_progress(self, info, **kwargs):
+ return self.objective_progress.filter(user=info.context.user)
+
+ def resolve_user_created(self, info, **kwargs):
+ return self.owner is not None
+
+ def resolve_mine(self, info, **kwargs):
+ return self.owner is not None and self.owner.pk == info.context.user.pk
+
+
class ObjectiveGroupNode(DjangoObjectType, HiddenForMixin):
pk = graphene.Int()
display_title = graphene.String()
+ objectives = graphene.List(ObjectiveNode)
class Meta:
model = ObjectiveGroup
@@ -37,26 +58,6 @@ class ObjectiveGroupNode(DjangoObjectType, HiddenForMixin):
return self.objectives.filter(objectives_from_publisher | objectives_from_teacher)
-class ObjectiveNode(DjangoObjectType, HiddenAndVisibleForMixin):
- pk = graphene.Int()
- user_created = graphene.Boolean()
- mine = graphene.Boolean()
-
- class Meta:
- model = Objective
- filter_fields = ['text']
- interfaces = (relay.Node,)
-
- def resolve_objective_progress(self, info, **kwargs):
- return self.objective_progress.filter(user=info.context.user)
-
- def resolve_user_created(self, info, **kwargs):
- return self.owner is not None
-
- def resolve_mine(self, info, **kwargs):
- return self.owner is not None and self.owner.pk == info.context.user.pk
-
-
class ObjectivesQuery(object):
objective_group = relay.Node.Field(ObjectiveGroupNode)
objective_groups = DjangoFilterConnectionField(ObjectiveGroupNode)