Merged in feature/objectives-by-user (pull request #10)
Feature/objectives by user
This commit is contained in:
commit
58d42e2472
|
|
@ -2,20 +2,13 @@
|
|||
<div class="content-block__container">
|
||||
<div class="content-block" :class="specialClass">
|
||||
<div class="content-block__actions">
|
||||
<a @click="toggleVisibility()" v-if="canManageContent" class="content-block__action-button">
|
||||
<eye-icon class="content-block__action-icon"></eye-icon>
|
||||
</a>
|
||||
<visibility-popover
|
||||
@hide-me="showVisibility = false"
|
||||
:show="showVisibility"
|
||||
:content-block="contentBlock"
|
||||
class="content-block__visibility-menu"
|
||||
></visibility-popover>
|
||||
<visibility-action
|
||||
:block="contentBlock"></visibility-action>
|
||||
<a @click="editContentBlock()" v-if="contentBlock.mine" class="content-block__action-button">
|
||||
<pen-icon class="content-block__action-icon"></pen-icon>
|
||||
<pen-icon class="content-block__action-icon action-icon"></pen-icon>
|
||||
</a>
|
||||
<a @click="deleteContentBlock(contentBlock.id)" v-if="contentBlock.mine" class="content-block__action-button">
|
||||
<trash-icon class="content-block__action-icon"></trash-icon>
|
||||
<trash-icon class="content-block__action-icon action-icon"></trash-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -46,11 +39,11 @@
|
|||
import DocumentBlock from '@/components/content-blocks/DocumentBlock';
|
||||
import Assignment from '@/components/content-blocks/assignment/Assignment';
|
||||
import AddContentBlockButton from '@/components/AddContentBlockButton';
|
||||
import VisibilityPopover from '@/components/VisibilityPopover';
|
||||
import VisibilityAction from '@/components/visibility/VisibilityAction';
|
||||
import EyeIcon from '@/components/icons/EyeIcon';
|
||||
import PenIcon from '@/components/icons/PenIcon';
|
||||
import TrashIcon from '@/components/icons/TrashIcon';
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
|
||||
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
|
||||
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
|
||||
|
||||
|
|
@ -68,7 +61,7 @@
|
|||
Assignment,
|
||||
Task,
|
||||
AddContentBlockButton,
|
||||
VisibilityPopover,
|
||||
VisibilityAction,
|
||||
EyeIcon,
|
||||
PenIcon,
|
||||
TrashIcon
|
||||
|
|
@ -77,16 +70,10 @@
|
|||
computed: {
|
||||
specialClass() {
|
||||
return `content-block--${this.contentBlock.type.toLowerCase()}`
|
||||
},
|
||||
canManageContent() {
|
||||
return this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleVisibility() {
|
||||
this.showVisibility = !this.showVisibility;
|
||||
},
|
||||
editContentBlock() {
|
||||
this.$store.dispatch('editContentBlock', this.contentBlock.id);
|
||||
},
|
||||
|
|
@ -118,18 +105,9 @@
|
|||
}
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: {
|
||||
query: ME_QUERY,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
showVisibility: false,
|
||||
me: {
|
||||
permissions: []
|
||||
}
|
||||
showVisibility: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -158,16 +136,6 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__action-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
fill: $color-grey;
|
||||
}
|
||||
|
||||
&__visibility-menu {
|
||||
top: 40px;
|
||||
}
|
||||
|
||||
&--base_communication {
|
||||
@include content-box($color-accent-1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,28 +8,30 @@
|
|||
<div class="module__intro" v-html="module.intro"></div>
|
||||
|
||||
<h3 id="objectives">Lernziele</h3>
|
||||
<objective-group v-for="group in module.objectiveGroups" :key="group.id" :group="group"></objective-group>
|
||||
|
||||
<objective-groups :groups="languageCommunicationObjectiveGroups"></objective-groups>
|
||||
<objective-groups :groups="societyObjectiveGroups"></objective-groups>
|
||||
|
||||
<chapter :chapter="chapter" :index="index" v-for="(chapter, index) in module.chapters" :key="chapter.id"></chapter>
|
||||
<h3 id="objectives-confirm">Alles klar?</h3>
|
||||
<objective-group-control
|
||||
v-for="(group, index) in module.objectiveGroups"
|
||||
:key="`${group.id}${index}`"
|
||||
:group="group"
|
||||
@updateObjectiveProgress="updateObjectiveProgress"></objective-group-control>
|
||||
|
||||
<objective-groups @updateObjectiveProgress="updateObjectiveProgress" :groups="languageCommunicationObjectiveGroups" :control="true"></objective-groups>
|
||||
<objective-groups @updateObjectiveProgress="updateObjectiveProgress" :groups="societyObjectiveGroups" :control="true"></objective-groups>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ObjectiveGroup from '@/components/modules/ObjectiveGroup.vue';
|
||||
import ObjectiveGroups from '@/components/modules/ObjectiveGroups.vue';
|
||||
import ObjectiveGroupControl from '@/components/modules/ObjectiveGroupControl.vue';
|
||||
import Chapter from '@/components/Chapter.vue';
|
||||
import UPDATE_OBJECTIVE_PROGRESS_MUTATION from '@/graphql/gql/mutations/updateObjectiveProgress.gql';
|
||||
import OBJECTIVE_QUERY from '@/graphql/gql/objectiveQuery.gql';
|
||||
|
||||
const withoutOwnerFirst = (a, b) => a.owner ? 1 : 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ObjectiveGroup,
|
||||
ObjectiveGroups,
|
||||
ObjectiveGroupControl,
|
||||
Chapter
|
||||
},
|
||||
|
|
@ -41,15 +43,20 @@
|
|||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
chapter: {
|
||||
title: '1.1 Lehrbeginn'
|
||||
}
|
||||
}
|
||||
created() {
|
||||
},
|
||||
|
||||
created() {
|
||||
computed: {
|
||||
languageCommunicationObjectiveGroups() {
|
||||
return this.module.objectiveGroups ? this.module.objectiveGroups
|
||||
.filter(group => group.title === 'LANGUAGE_COMMUNICATION')
|
||||
.sort(withoutOwnerFirst) : [];
|
||||
},
|
||||
societyObjectiveGroups() {
|
||||
return this.module.objectiveGroups ? this.module.objectiveGroups
|
||||
.filter(group => group.title === 'SOCIETY')
|
||||
.sort(withoutOwnerFirst) : [];
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<template>
|
||||
<div class="objective-group">
|
||||
<h4>{{group.title}}</h4>
|
||||
<div class="objective-group__actions">
|
||||
<visibility-action :block="group">
|
||||
</visibility-action>
|
||||
</div>
|
||||
|
||||
<h4>{{group.displayTitle}}</h4>
|
||||
|
||||
<ul class="objective-group__objective-list">
|
||||
<li class="objective-group__objective" v-for="objective in group.objectives" :key="objective.id">
|
||||
|
|
@ -12,9 +17,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'objective-group',
|
||||
import VisibilityAction from '@/components/visibility/VisibilityAction';
|
||||
import EyeIcon from '@/components/icons/EyeIcon';
|
||||
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
group: {
|
||||
required: true,
|
||||
|
|
@ -22,7 +30,35 @@
|
|||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
VisibilityAction,
|
||||
EyeIcon
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: {
|
||||
query: ME_QUERY,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
canManageContent() {
|
||||
return this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.objective-group {
|
||||
position: relative;
|
||||
|
||||
&__actions {
|
||||
position: absolute;
|
||||
left: -70px;
|
||||
top: -4px;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="objective-group objective-group-control">
|
||||
<h4>{{group.title}}</h4>
|
||||
<h4>{{group.displayTitle}}</h4>
|
||||
|
||||
<ul class="objective-group__objective-list objective-group__objective-list--no-list">
|
||||
<li v-if="objectives" class="objective-group__objective" v-for="objective in objectives" :key="objective.id">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div>
|
||||
<template v-if="control">
|
||||
<objective-group-control
|
||||
v-for="(group, index) in objectiveGroups"
|
||||
:key="`${group.id}${index}`"
|
||||
:group="group"
|
||||
@updateObjectiveProgress="updateObjectiveProgress"></objective-group-control>
|
||||
</template>
|
||||
<template v-if="!control">
|
||||
<objective-group v-for="group in objectiveGroups" :key="group.id" :group="group"></objective-group>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ObjectiveGroup from '@/components/modules/ObjectiveGroup';
|
||||
import ObjectiveGroupControl from '@/components/modules/ObjectiveGroupControl';
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
groups: Array,
|
||||
control: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
ObjectiveGroup,
|
||||
ObjectiveGroupControl
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: meQuery
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
me: {
|
||||
permissions: []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
objectiveGroups() {
|
||||
/*
|
||||
a teacher should get multiple blocks, so he can manage the visibility for his students.
|
||||
students don't care about the blocks, so they should get just one block that contains all the objectives
|
||||
*/
|
||||
if (this.me.permissions.includes('users.can_manage_school_class_content') || !this.groups.length) {
|
||||
return this.groups;
|
||||
} else {
|
||||
// todo: maybe this can be done a bit more elegantly
|
||||
const groups = [...this.groups];
|
||||
const objectives = groups.map(g => g.objectives).flat(); // get all objectives in one array
|
||||
const firstGroup = Object.assign({}, groups.shift(), {objectives});
|
||||
|
||||
return [firstGroup];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateObjectiveProgress(checked, id) {
|
||||
this.$emit('updateObjectiveProgress', checked, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<div class="visibility-action">
|
||||
<a @click="toggleVisibility()" v-if="canManageContent" class="visibility-action__action-button">
|
||||
<eye-icon class="visibility-action__action-icon action-icon"></eye-icon>
|
||||
</a>
|
||||
<visibility-popover
|
||||
@hide-me="showVisibility = false"
|
||||
:show="showVisibility"
|
||||
:block="block"
|
||||
class="visibility-action__visibility-menu"
|
||||
></visibility-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EyeIcon from '@/components/icons/EyeIcon';
|
||||
import VisibilityPopover from '@/components/visibility/VisibilityPopover';
|
||||
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
|
||||
export default {
|
||||
props: ['block'],
|
||||
|
||||
components: {
|
||||
VisibilityPopover,
|
||||
EyeIcon
|
||||
},
|
||||
|
||||
computed: {
|
||||
canManageContent() {
|
||||
return this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleVisibility() {
|
||||
this.showVisibility = !this.showVisibility;
|
||||
},
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: {
|
||||
query: ME_QUERY,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
showVisibility: false,
|
||||
me: {
|
||||
permissions: []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.visibility-action {
|
||||
|
||||
&__visibility-menu {
|
||||
top: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -13,14 +13,12 @@
|
|||
|
||||
<script>
|
||||
import CHANGE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/mutateContentBlock.gql';
|
||||
import UPDATE_OBJECTIVE_GROUP_VISIBILITY_MUTATION from '@/graphql/gql/mutations/updateObjectiveGroupVisibility.gql';
|
||||
import Checkbox from '@/components/Checkbox';
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql'
|
||||
// import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
|
||||
|
||||
// import store from '@/store/index';
|
||||
|
||||
export default {
|
||||
props: ['show', 'content-block'],
|
||||
props: ['show', 'block'],
|
||||
|
||||
components: {
|
||||
Checkbox
|
||||
|
|
@ -42,30 +40,48 @@
|
|||
updateVisibility(checked, item) {
|
||||
item.hidden = !checked;
|
||||
|
||||
this.$apollo.mutate({
|
||||
mutation: CHANGE_CONTENT_BLOCK_MUTATION,
|
||||
variables: {
|
||||
const visibility = this.schoolClassVisibility.map(g => {
|
||||
return {
|
||||
schoolClassId: g.id,
|
||||
hidden: g.hidden || false
|
||||
}
|
||||
});
|
||||
|
||||
let mutation, variables;
|
||||
const id = this.block.id;
|
||||
|
||||
if (this.isContentBlock) {
|
||||
mutation = CHANGE_CONTENT_BLOCK_MUTATION;
|
||||
variables = {
|
||||
input: {
|
||||
id: this.contentBlock.id,
|
||||
id,
|
||||
contentBlock: {
|
||||
visibility: this.schoolClassVisibility.map(g => {
|
||||
return {
|
||||
schoolClassId: g.id,
|
||||
hidden: g.hidden || false
|
||||
}
|
||||
})
|
||||
visibility
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mutation = UPDATE_OBJECTIVE_GROUP_VISIBILITY_MUTATION;
|
||||
variables = {
|
||||
input: {
|
||||
id,
|
||||
visibility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.$apollo.mutate({
|
||||
mutation,
|
||||
variables
|
||||
});
|
||||
},
|
||||
hidePopover() {
|
||||
this.$emit('hide-me');
|
||||
},
|
||||
isSchoolClassHidden(schoolClass) {
|
||||
return this.contentBlock.userCreated
|
||||
? this.contentBlock.visibleFor.findIndex(el => el.id === schoolClass.id) === -1
|
||||
: this.contentBlock.hiddenFor.findIndex(el => el.id === schoolClass.id) > -1;
|
||||
return (this.isContentBlock ? this.block.userCreated : !!this.block.owner)
|
||||
? this.block.visibleFor.findIndex(el => el.id === schoolClass.id) === -1
|
||||
: this.block.hiddenFor.findIndex(el => el.id === schoolClass.id) > -1;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -74,6 +90,10 @@
|
|||
return this.$getRidOfEdges(this.me.schoolClasses);
|
||||
},
|
||||
|
||||
isContentBlock() {
|
||||
return this.block.__typename === 'ContentBlockNode';
|
||||
},
|
||||
|
||||
schoolClassVisibility() {
|
||||
return this.schoolClasses.map(schoolClass => {
|
||||
return {
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
fragment ObjectiveGroupParts on ObjectiveGroupNode {
|
||||
id
|
||||
title
|
||||
displayTitle
|
||||
owner {
|
||||
id
|
||||
}
|
||||
hiddenFor {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
visibleFor {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#import "./fragments/chapterParts.gql"
|
||||
#import "./fragments/assignmentParts.gql"
|
||||
#import "./fragments/objectiveGroupParts.gql"
|
||||
query ModulesQuery($slug: String!) {
|
||||
module(slug: $slug) {
|
||||
id
|
||||
|
|
@ -19,8 +20,7 @@ query ModulesQuery($slug: String!) {
|
|||
objectiveGroups {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
...ObjectiveGroupParts
|
||||
objectives {
|
||||
edges {
|
||||
node {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#import "../fragments/objectiveGroupParts.gql"
|
||||
mutation UpdateObjectiveGroupVisibility($input: UpdateObjectiveGroupVisibilityInput!) {
|
||||
updateObjectiveGroupVisibility(input: $input) {
|
||||
objectiveGroup {
|
||||
...ObjectiveGroupParts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import MODULE_DETAILS_QUERY from './gql/moduleDetailsQuery.gql';
|
||||
import ME_QUERY from './gql/meQuery.gql';
|
||||
|
||||
export function moduleQuery() {
|
||||
return {
|
||||
|
|
@ -11,3 +12,9 @@ export function moduleQuery() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function meQuery() {
|
||||
return {
|
||||
query: ME_QUERY
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
.action-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
fill: $color-grey;
|
||||
}
|
||||
|
|
@ -12,3 +12,4 @@
|
|||
@import "help-text";
|
||||
@import "objective-group";
|
||||
@import "article";
|
||||
@import "actions";
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class ContentElementValueInput(InputObjectType):
|
|||
# we'll handle this with a single input, even tho it would be nice to have a type for every different possibility
|
||||
# see discussion at https://github.com/graphql/graphql-js/issues/207
|
||||
text = graphene.String(description='To be used for link_block, text_block types')
|
||||
url = graphene.String(description='To be used for link, basic_knowledge, image_block types')
|
||||
url = graphene.String(description='To be used for link, image_block types')
|
||||
description = graphene.String(description='To be used for basic_knowledge type')
|
||||
title = graphene.String(description='To be used for image_block, assignment type')
|
||||
assignment = graphene.String(description='To be used for assignment type')
|
||||
|
|
@ -29,7 +29,7 @@ class ContentElementInput(InputObjectType):
|
|||
value = ContentElementValueInput()
|
||||
|
||||
|
||||
class UserGroupContentBlockVisibility(InputObjectType):
|
||||
class UserGroupBlockVisibility(InputObjectType):
|
||||
school_class_id = graphene.ID(required=True)
|
||||
hidden = graphene.Boolean(required=True)
|
||||
|
||||
|
|
@ -38,4 +38,4 @@ class ContentBlockInput(InputObjectType):
|
|||
title = graphene.String()
|
||||
type = graphene.String()
|
||||
contents = graphene.List(ContentElementInput)
|
||||
visibility = graphene.List(UserGroupContentBlockVisibility)
|
||||
visibility = graphene.List(UserGroupBlockVisibility)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from api.utils import get_object, get_errors
|
|||
from books.models import ContentBlock, Chapter, SchoolClass
|
||||
from books.schema.inputs import ContentBlockInput
|
||||
from books.schema.queries import ContentBlockNode
|
||||
from core.utils import set_hidden_for, set_visible_for
|
||||
from .utils import handle_content_block, set_user_defined_block_type
|
||||
|
||||
|
||||
|
|
@ -37,9 +38,9 @@ class MutateContentBlock(relay.ClientIDMutation):
|
|||
|
||||
if visibility_list is not None:
|
||||
if content_block.user_created:
|
||||
cls.set_visible_for(content_block, visibility_list)
|
||||
set_visible_for(content_block, visibility_list)
|
||||
else:
|
||||
cls.set_hidden_for(content_block, visibility_list)
|
||||
set_hidden_for(content_block, visibility_list)
|
||||
|
||||
if title is not None:
|
||||
content_block.title = title
|
||||
|
|
@ -59,24 +60,6 @@ class MutateContentBlock(relay.ClientIDMutation):
|
|||
|
||||
return cls(content_block=None, errors=errors)
|
||||
|
||||
@classmethod
|
||||
def set_hidden_for(cls, content_block, visibility_list):
|
||||
for v in visibility_list:
|
||||
school_class = get_object(SchoolClass, v.school_class_id)
|
||||
if v.hidden:
|
||||
content_block.hidden_for.add(school_class)
|
||||
else:
|
||||
content_block.hidden_for.remove(school_class)
|
||||
|
||||
@classmethod
|
||||
def set_visible_for(cls, content_block, visibility_list):
|
||||
for v in visibility_list:
|
||||
school_class = get_object(SchoolClass, v.school_class_id)
|
||||
if v.hidden:
|
||||
content_block.visible_for.remove(school_class)
|
||||
else:
|
||||
content_block.visible_for.add(school_class)
|
||||
|
||||
|
||||
class AddContentBlock(relay.ClientIDMutation):
|
||||
class Input:
|
||||
|
|
|
|||
|
|
@ -56,11 +56,6 @@ class ChapterNode(DjangoObjectType):
|
|||
|
||||
return publisher_content_blocks.union(user_created_content_blocks)
|
||||
|
||||
# if user.has_perm('users.can_manage_school_class_content'):
|
||||
# return ContentBlock.get_by_parent(self)
|
||||
# else:
|
||||
# return ContentBlock.get_by_parent(self)
|
||||
|
||||
|
||||
class ModuleNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
|
|
@ -88,6 +83,22 @@ class ModuleNode(DjangoObjectType):
|
|||
def resolve_chapters(self, info, **kwargs):
|
||||
return Chapter.get_by_parent(self)
|
||||
|
||||
def resolve_objective_groups(self, info, **kwargs):
|
||||
user = info.context.user
|
||||
school_classes = user.school_classes.values_list('pk')
|
||||
|
||||
if user.has_perm('users.can_manage_school_class_content'): # teacher
|
||||
publisher_objective_groups = self.objective_groups.filter(owner=None)
|
||||
user_created_objective_groups = self.objective_groups.filter(owner=user)
|
||||
else: # student
|
||||
publisher_objective_groups = self.objective_groups.filter(owner=None).exclude(
|
||||
hidden_for__in=school_classes)
|
||||
|
||||
user_created_objective_groups = self.objective_groups.filter(owner__isnull=False,
|
||||
visible_for__in=school_classes)
|
||||
|
||||
return publisher_objective_groups.union(user_created_objective_groups)
|
||||
|
||||
|
||||
class TopicNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from users.services import create_users
|
|||
|
||||
data = [
|
||||
{
|
||||
'title': 'Gesellschaft',
|
||||
'title': 'society',
|
||||
'topics': [
|
||||
{
|
||||
'order': 1,
|
||||
|
|
@ -42,7 +42,7 @@ data = [
|
|||
""",
|
||||
'objective_groups': [
|
||||
{
|
||||
'title': 'Gesellschaft',
|
||||
'title': 'society',
|
||||
'objectives': [
|
||||
{
|
||||
'text': 'Ich kann wichtige personelle und organisatorische Strukturen der Berufsfachschule beschreiben und deren Angebote, Konzepte erklären und Weisungen nennen.'
|
||||
|
|
@ -56,7 +56,7 @@ data = [
|
|||
]
|
||||
},
|
||||
{
|
||||
'title': 'Sprache und Kommunikation',
|
||||
'title': 'language_communication',
|
||||
'objectives': [
|
||||
{
|
||||
'text': 'Ich kenne verschiedene Arten von Fragen.'
|
||||
|
|
@ -348,7 +348,7 @@ data = [
|
|||
""",
|
||||
'objective_groups': [
|
||||
{
|
||||
'title': 'Gesellschaft',
|
||||
'title': 'society',
|
||||
'objectives': [
|
||||
{
|
||||
'text': 'Ich kann Anlageformen für mich so auswählen, dass sie meiner wirtschaftlichen Situation und meiner Risikofreude entsprechen.'
|
||||
|
|
@ -359,7 +359,7 @@ data = [
|
|||
]
|
||||
},
|
||||
{
|
||||
'title': 'Sprache und Kommunikation',
|
||||
'title': 'language_communication',
|
||||
'objectives': [
|
||||
{
|
||||
'text': 'Ich kann zu wichtigen Aussagen aus einem Erklärvideo Notizen machen.'
|
||||
|
|
@ -755,7 +755,7 @@ class Command(BaseCommand):
|
|||
for objective_group_idx, objective_group_entry in enumerate(objective_group_data):
|
||||
factory_params = self.filter_data(objective_group_entry, 'objectives')
|
||||
objective_group = ObjectiveGroupFactory.create(module=module,
|
||||
user=None,
|
||||
owner=None,
|
||||
**factory_params)
|
||||
|
||||
default_objectives = [{} for i in range(0, 4)]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
from api.utils import get_object
|
||||
from users.models import SchoolClass
|
||||
|
||||
|
||||
def set_hidden_for(block, visibility_list):
|
||||
for v in visibility_list:
|
||||
school_class = get_object(SchoolClass, v.school_class_id)
|
||||
if v.hidden:
|
||||
block.hidden_for.add(school_class)
|
||||
else:
|
||||
block.hidden_for.remove(school_class)
|
||||
|
||||
|
||||
def set_visible_for(block, visibility_list):
|
||||
for v in visibility_list:
|
||||
school_class = get_object(SchoolClass, v.school_class_id)
|
||||
if v.hidden:
|
||||
block.visible_for.remove(school_class)
|
||||
else:
|
||||
block.visible_for.add(school_class)
|
||||
|
|
@ -5,8 +5,8 @@ from objectives.models import ObjectiveGroup, Objective, ObjectiveProgressStatus
|
|||
|
||||
@admin.register(ObjectiveGroup)
|
||||
class ObjectiveGroupAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'module', 'user')
|
||||
list_filter = ('title', 'module', 'user')
|
||||
list_display = ('title', 'module', 'owner')
|
||||
list_filter = ('title', 'module', 'owner')
|
||||
|
||||
|
||||
@admin.register(Objective)
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ class ObjectiveGroupFactory(factory.django.DjangoModelFactory):
|
|||
class Meta:
|
||||
model = ObjectiveGroup
|
||||
|
||||
title = factory.Iterator(['Gesellschaft', 'Sprache und Kommunikation'])
|
||||
title = factory.Iterator([ObjectiveGroup.LANGUAGE_COMMUNICATION, ObjectiveGroup.SOCIETY])
|
||||
|
||||
module = factory.SubFactory(ModuleFactory)
|
||||
user = factory.Iterator(get_user_model().objects.all())
|
||||
owner = factory.Iterator(get_user_model().objects.all())
|
||||
|
||||
|
||||
class ObjectiveFactory(factory.django.DjangoModelFactory):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.0.6 on 2018-10-30 17:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('objectives', '0003_auto_20181022_1646'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='objectivegroup',
|
||||
name='title',
|
||||
field=models.CharField(blank=True, choices=[('language_communication', 'Sprache & Kommunikation'), ('society', 'Gesellschaft')], default='language_communication', max_length=255, verbose_name='title'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from django.db import migrations
|
||||
|
||||
|
||||
def migrate_titles(apps, schema_editor):
|
||||
ObjectiveGroup = apps.get_model('objectives', 'ObjectiveGroup')
|
||||
for og in ObjectiveGroup.objects.filter(title='Sprache und Kommunikation'):
|
||||
og.title = 'language_communication'
|
||||
og.save()
|
||||
for og in ObjectiveGroup.objects.filter(title='Gesellschaft'):
|
||||
og.title = 'society'
|
||||
og.save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('objectives', '0004_auto_20181030_1726')
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_titles)
|
||||
]
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 2.0.6 on 2018-10-31 13:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_auto_20181017_1340'),
|
||||
('objectives', '0005_migrate_titles'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='objectivegroup',
|
||||
old_name='user',
|
||||
new_name='owner',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='objectivegroup',
|
||||
name='hidden_for',
|
||||
field=models.ManyToManyField(related_name='hidden_objective_groups', to='users.SchoolClass'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='objectivegroup',
|
||||
name='visible_for',
|
||||
field=models.ManyToManyField(related_name='visible_objective_groups', to='users.SchoolClass'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.0.6 on 2018-10-31 13:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('objectives', '0006_auto_20181031_1323'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='objectivegroup',
|
||||
name='hidden_for',
|
||||
field=models.ManyToManyField(blank=True, related_name='hidden_objective_groups', to='users.SchoolClass'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='objectivegroup',
|
||||
name='visible_for',
|
||||
field=models.ManyToManyField(blank=True, related_name='visible_objective_groups', to='users.SchoolClass'),
|
||||
),
|
||||
]
|
||||
|
|
@ -2,6 +2,7 @@ from django.contrib.auth import get_user_model
|
|||
from django.db import models
|
||||
|
||||
from books.models import Module
|
||||
from users.models import SchoolClass
|
||||
|
||||
|
||||
class ObjectiveGroup(models.Model):
|
||||
|
|
@ -9,10 +10,21 @@ class ObjectiveGroup(models.Model):
|
|||
verbose_name = 'Lernzielgruppe'
|
||||
verbose_name_plural = 'Lernzielgruppen'
|
||||
|
||||
title = models.CharField('title', blank=True, null=False, max_length=255)
|
||||
LANGUAGE_COMMUNICATION = 'language_communication'
|
||||
SOCIETY = 'society'
|
||||
|
||||
TITLE_CHOICES = (
|
||||
(LANGUAGE_COMMUNICATION, 'Sprache & Kommunikation'),
|
||||
(SOCIETY, 'Gesellschaft'),
|
||||
)
|
||||
|
||||
title = models.CharField('title', blank=True, null=False, max_length=255, choices=TITLE_CHOICES, default=LANGUAGE_COMMUNICATION)
|
||||
module = models.ForeignKey(Module, blank=False, null=False, on_delete=models.CASCADE, related_name='objective_groups')
|
||||
# a user can define her own objectives, hence this optional param
|
||||
user = models.ForeignKey(get_user_model(), blank=True, null=True, on_delete=models.CASCADE)
|
||||
owner = models.ForeignKey(get_user_model(), blank=True, null=True, on_delete=models.CASCADE)
|
||||
|
||||
hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_objective_groups', blank=True)
|
||||
visible_for = models.ManyToManyField(SchoolClass, related_name='visible_objective_groups', blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return 'ObjectiveGroup {}-{}-{}'.format(self.id, self.module, self.title)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import graphene
|
||||
from graphene import relay
|
||||
from graphene import relay, InputObjectType
|
||||
from api.utils import get_object
|
||||
from objectives.models import ObjectiveProgressStatus, Objective
|
||||
from objectives.schema import ObjectiveNode
|
||||
from books.schema.inputs import UserGroupBlockVisibility
|
||||
from core.utils import set_visible_for, set_hidden_for
|
||||
from objectives.models import ObjectiveProgressStatus, Objective, ObjectiveGroup
|
||||
from objectives.schema import ObjectiveNode, ObjectiveGroupNode
|
||||
|
||||
|
||||
class UpdateObjectiveProgress(relay.ClientIDMutation):
|
||||
|
|
@ -29,5 +31,31 @@ class UpdateObjectiveProgress(relay.ClientIDMutation):
|
|||
return cls(objective=objective)
|
||||
|
||||
|
||||
class UpdateObjectiveGroupVisibility(relay.ClientIDMutation):
|
||||
class Input:
|
||||
id = graphene.ID(required=True, description='The ID of the objective group')
|
||||
visibility = graphene.List(UserGroupBlockVisibility)
|
||||
|
||||
objective_group = graphene.Field(ObjectiveGroupNode)
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||
objective_group_id = kwargs.get('id')
|
||||
visibility_list = kwargs.get('visibility')
|
||||
objective_group = get_object(ObjectiveGroup, objective_group_id) # info.context.user = django user
|
||||
|
||||
if visibility_list is not None:
|
||||
if objective_group.owner is not None:
|
||||
set_visible_for(objective_group, visibility_list)
|
||||
else:
|
||||
set_hidden_for(objective_group, visibility_list)
|
||||
|
||||
objective_group.save()
|
||||
|
||||
|
||||
return cls(objective_group=objective_group)
|
||||
|
||||
|
||||
class ObjectiveMutations:
|
||||
update_objective_progress = UpdateObjectiveProgress.Field()
|
||||
update_objective_group_visibility = UpdateObjectiveGroupVisibility.Field()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from objectives.models import ObjectiveGroup, Objective, ObjectiveProgressStatus
|
|||
|
||||
class ObjectiveGroupNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
display_title = graphene.String()
|
||||
|
||||
class Meta:
|
||||
model = ObjectiveGroup
|
||||
|
|
@ -17,6 +18,10 @@ class ObjectiveGroupNode(DjangoObjectType):
|
|||
def resolve_pk(self, *args, **kwargs):
|
||||
return self.id
|
||||
|
||||
def resolve_display_title(self, *args, **kwargs):
|
||||
return self.get_title_display()
|
||||
|
||||
|
||||
|
||||
class ObjectiveNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
|
|
@ -26,6 +31,9 @@ class ObjectiveNode(DjangoObjectType):
|
|||
filter_fields = ['text']
|
||||
interfaces = (relay.Node,)
|
||||
|
||||
def resolve_objective_progress(self, info, **kwargs):
|
||||
return self.objective_progress.filter(user=info.context.user)
|
||||
|
||||
|
||||
class ObjectiveProgressStatusNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
|
|
@ -40,4 +48,5 @@ class ObjectiveProgressStatusNode(DjangoObjectType):
|
|||
|
||||
|
||||
class ObjectivesQuery(object):
|
||||
objective_group = relay.Node.Field(ObjectiveGroupNode)
|
||||
objective_groups = DjangoFilterConnectionField(ObjectiveGroupNode)
|
||||
|
|
|
|||
Loading…
Reference in New Issue