Merge branch 'develop'
This commit is contained in:
commit
ffd87e57bf
|
|
@ -12,7 +12,7 @@
|
||||||
@edit-note="editNote"
|
@edit-note="editNote"
|
||||||
@bookmark="bookmark(!chapter.bookmark)"
|
@bookmark="bookmark(!chapter.bookmark)"
|
||||||
/>
|
/>
|
||||||
<p class="chapter__description">
|
<p class="chapter__description intro">
|
||||||
{{ chapter.description }}
|
{{ chapter.description }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
grid-template-columns: 50px 1fr 200px;
|
grid-template-columns: 50px 1fr auto;
|
||||||
grid-template-rows: 50px;
|
grid-template-rows: 50px;
|
||||||
grid-auto-rows: auto;
|
grid-auto-rows: auto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
<p
|
<p
|
||||||
class="solution__text solution-text fade"
|
class="solution__text solution-text fade"
|
||||||
data-cy="solution-text"
|
data-cy="solution-text"
|
||||||
|
|
||||||
v-if="visible"
|
v-if="visible"
|
||||||
v-html="value.text"/>
|
v-html="value.text"/>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
@edit-note="editNote"
|
@edit-note="editNote"
|
||||||
@bookmark="bookmark(!module.bookmark)"/>
|
@bookmark="bookmark(!module.bookmark)"/>
|
||||||
<div
|
<div
|
||||||
class="module__intro"
|
class="module__intro intro"
|
||||||
v-html="module.intro"/>
|
v-html="module.intro"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
<objective-groups :groups="societyObjectiveGroups"/>
|
<objective-groups :groups="societyObjectiveGroups"/>
|
||||||
|
|
||||||
|
<objective-groups :groups="interdisciplinaryObjectiveGroups"/>
|
||||||
|
|
||||||
<chapter
|
<chapter
|
||||||
:chapter="chapter"
|
:chapter="chapter"
|
||||||
:index="index"
|
:index="index"
|
||||||
|
|
@ -86,6 +88,11 @@
|
||||||
.filter(group => group.title === 'SOCIETY')
|
.filter(group => group.title === 'SOCIETY')
|
||||||
.sort(withoutOwnerFirst) : [];
|
.sort(withoutOwnerFirst) : [];
|
||||||
},
|
},
|
||||||
|
interdisciplinaryObjectiveGroups() {
|
||||||
|
return this.module.objectiveGroups ? this.module.objectiveGroups
|
||||||
|
.filter(group => group.title === 'INTERDISCIPLINARY')
|
||||||
|
.sort(withoutOwnerFirst) : [];
|
||||||
|
},
|
||||||
isStudent() {
|
isStudent() {
|
||||||
return !this.me.permissions.includes('users.can_manage_school_class_content');
|
return !this.me.permissions.includes('users.can_manage_school_class_content');
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
<img
|
<img
|
||||||
:src="teaser.imageUrl"
|
:src="teaser.imageUrl"
|
||||||
class="news-teaser__image">
|
class="news-teaser__image">
|
||||||
<p class="news-teaser__image-source">
|
<a
|
||||||
<a
|
:href="teaser.imageSource"
|
||||||
:href="teaser.imageSource"
|
class="news-teaser__image-source">Quelle {{ teaser.imageSource }}</a>
|
||||||
class="tiny-text">Quelle {{ teaser.imageSource }}</a></p>
|
|
||||||
<h4 class="news-teaser__title">{{ teaser.title }}</h4>
|
<h4 class="news-teaser__title">{{ teaser.title }}</h4>
|
||||||
<p class="news-teaser__description">{{ teaser.description }}</p>
|
<p class="news-teaser__description">{{ teaser.description }}</p>
|
||||||
<p class="news-teaser__date">{{ teaser.displayDate }}</p>
|
<p class="news-teaser__date">{{ teaser.displayDate }}</p>
|
||||||
|
|
@ -16,52 +16,54 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
teaser: {
|
teaser: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => {
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/styles/_variables.scss";
|
@import "@/styles/_variables.scss";
|
||||||
@import "@/styles/_functions.scss";
|
@import "@/styles/_functions.scss";
|
||||||
@import "@/styles/_mixins.scss";
|
@import "@/styles/_mixins.scss";
|
||||||
|
|
||||||
.news-teaser {
|
.news-teaser {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: $large-spacing;
|
|
||||||
|
|
||||||
&__image {
|
&__image {
|
||||||
display: block;
|
display: block;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
max-width: $news_width;
|
max-width: $news-width;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__image-source {
|
|
||||||
line-height: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__description {
|
|
||||||
margin-bottom: $large-spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__date {
|
|
||||||
font-family: $sans-serif-font-family;
|
|
||||||
font-weight: $font-weight-regular;
|
|
||||||
color: $color-silver-dark;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__image-source {
|
||||||
|
line-height: 25px;
|
||||||
|
@include tiny-text;
|
||||||
|
margin-bottom: $medium-spacing;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin-bottom: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
margin-bottom: $small-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__date {
|
||||||
|
@include regular-text;
|
||||||
|
color: $color-silver-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -35,14 +35,15 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
margin-bottom: $large-spacing;
|
margin-bottom: $large-spacing;
|
||||||
grid-gap: 40px;
|
grid-gap: $large-spacing;
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
grid-column-gap: 40px;
|
grid-column-gap: $large-spacing;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(320px, $news_width));
|
grid-row-gap: $section-spacing;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(320px, $news-width));
|
||||||
grid-auto-rows: minmax(400px, auto);
|
grid-auto-rows: minmax(400px, auto);
|
||||||
grid-template-rows: auto auto;
|
grid-template-rows: auto auto;
|
||||||
-ms-grid-columns: $news_width $news_width;
|
-ms-grid-columns: $news-width $news-width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
<div>
|
<div>
|
||||||
{{ objective.text }}
|
{{ objective.text }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,9 @@
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
break-inside: avoid;
|
break-inside: avoid-page;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
@include desktop {
|
@include desktop {
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
import CurrentClass from '@/components/school-class/CurrentClass';
|
import CurrentClass from '@/components/school-class/CurrentClass';
|
||||||
import AddIcon from '@/components/icons/AddIcon';
|
import AddIcon from '@/components/icons/AddIcon';
|
||||||
|
|
||||||
import updateSelectedClassMixin from '@/mixins/updateSelectedClass';
|
import updateSelectedClassMixin from '@/mixins/update-selected-class';
|
||||||
import sidebarMixin from '@/mixins/sidebar';
|
import sidebarMixin from '@/mixins/sidebar';
|
||||||
import meMixin from '@/mixins/me';
|
import meMixin from '@/mixins/me';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
fragment InstrumentParts on InstrumentNode {
|
fragment InstrumentParts on InstrumentNode {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
|
intro
|
||||||
slug
|
slug
|
||||||
bookmarks {
|
bookmarks {
|
||||||
uuid
|
uuid
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,19 @@
|
||||||
const extractAnswerFromQuestion = (previous, question) => {
|
const extractAnswerFromQuestion = (previous, question) => {
|
||||||
return [...previous, {title: question.title, answer: question.correctAnswer}];
|
let answer = question.correctAnswer;
|
||||||
|
if (question.getType() === 'matrix') {
|
||||||
|
const correctAnswer = question.correctAnswer;
|
||||||
|
const questionRows = question.getRows();
|
||||||
|
const keys = questionRows.map(question => {
|
||||||
|
const text = question.value;
|
||||||
|
if (/[,.!?]/.test(text.slice(-1))) {
|
||||||
|
return text.slice(0, -1);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}); // get the keys as they appear in the question, without punctuation at the end
|
||||||
|
|
||||||
|
answer = keys.map(key => `${key}: ${correctAnswer[key]}`); // return an array, it gets converted to a string further up
|
||||||
|
}
|
||||||
|
return [...previous, {title: question.title, answer, type: question.getType()}];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const extractSurveySolutions = (prev, element) => {
|
export const extractSurveySolutions = (prev, element) => {
|
||||||
|
|
|
||||||
|
|
@ -79,16 +79,33 @@
|
||||||
max-width: $footer-width;
|
max-width: $footer-width;
|
||||||
padding: 2*$large-spacing 0;
|
padding: 2*$large-spacing 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
flex-direction: column;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__who-are-we {
|
&__who-are-we {
|
||||||
width: 330px;
|
width: 100%;
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
width: 330px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__logo-hep {
|
&__logo-hep {
|
||||||
width: 147px;
|
width: auto;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
|
margin-bottom: $large-spacing;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
width: 147px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__logo-ehb {
|
&__logo-ehb {
|
||||||
|
|
@ -100,11 +117,22 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: $footer-width;
|
max-width: $footer-width;
|
||||||
padding: $large-spacing 0;
|
padding: $large-spacing 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
@include aside-with-cheese;
|
@include aside-with-cheese;
|
||||||
margin-right: $large-spacing;
|
margin-right: $large-spacing;
|
||||||
|
margin-bottom: $small-spacing;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
<div class="instrument">
|
<div class="instrument">
|
||||||
<h1 class="instrument__title">{{ instrument.title }}</h1>
|
<h1 class="instrument__title">{{ instrument.title }}</h1>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="instrument__intro intro"
|
||||||
|
v-html="instrument.intro"/>
|
||||||
|
|
||||||
<content-component
|
<content-component
|
||||||
:key="component.id"
|
:key="component.id"
|
||||||
:component="component"
|
:component="component"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
<script>
|
<script>
|
||||||
import OLD_CLASSES_QUERY from '@/graphql/gql/oldClasses.gql';
|
import OLD_CLASSES_QUERY from '@/graphql/gql/oldClasses.gql';
|
||||||
|
|
||||||
import updateSelectedClassMixin from '@/mixins/updateSelectedClass';
|
import updateSelectedClassMixin from '@/mixins/update-selected-class';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [updateSelectedClassMixin],
|
mixins: [updateSelectedClassMixin],
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__modules {
|
&__modules {
|
||||||
margin-bottom: $large-spacing;
|
margin-bottom: $section-spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__modules-list {
|
&__modules-list {
|
||||||
|
|
@ -201,6 +201,10 @@
|
||||||
-ms-grid-column: 3;
|
-ms-grid-column: 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__news {
|
||||||
|
margin-bottom: $section-spacing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.news {
|
.news {
|
||||||
|
|
|
||||||
|
|
@ -15,207 +15,206 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as SurveyVue from 'survey-vue';
|
import * as SurveyVue from 'survey-vue';
|
||||||
import {css} from '@/survey.config';
|
import {css} from '@/survey.config';
|
||||||
|
|
||||||
import SURVEY_QUERY from '@/graphql/gql/surveyQuery.gql';
|
import SURVEY_QUERY from '@/graphql/gql/surveyQuery.gql';
|
||||||
import MODULE_QUERY from '@/graphql/gql/moduleByIdQuery.gql';
|
import MODULE_QUERY from '@/graphql/gql/moduleByIdQuery.gql';
|
||||||
import UPDATE_ANSWER from '@/graphql/gql/mutations/updateAnswer.gql';
|
import UPDATE_ANSWER from '@/graphql/gql/mutations/updateAnswer.gql';
|
||||||
import Solution from '@/components/content-blocks/Solution';
|
import Solution from '@/components/content-blocks/Solution';
|
||||||
|
|
||||||
import {extractSurveySolutions} from '@/helpers/survey-solutions';
|
import {extractSurveySolutions} from '@/helpers/survey-solutions';
|
||||||
import {isTeacher} from '@/helpers/is-teacher';
|
import {isTeacher} from '@/helpers/is-teacher';
|
||||||
|
|
||||||
import {meQuery} from '@/graphql/queries';
|
import {meQuery} from '@/graphql/queries';
|
||||||
|
|
||||||
const Survey = SurveyVue.Survey;
|
const Survey = SurveyVue.Survey;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['id'],
|
props: ['id'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Solution,
|
Solution,
|
||||||
Survey
|
Survey
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
survey: this.initSurvey(),
|
||||||
|
title: '',
|
||||||
|
module: {},
|
||||||
|
completed: false,
|
||||||
|
me: {
|
||||||
|
permissions: []
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
surveyComplete() {
|
||||||
|
return this.survey && this.survey.isCompleted;
|
||||||
},
|
},
|
||||||
|
showSolution() {
|
||||||
data() {
|
return (module.solutionsEnabled || isTeacher) && !this.survey.isCompleted;
|
||||||
|
},
|
||||||
|
solution() {
|
||||||
|
// todo: should this be done inside of Solution.vue?
|
||||||
return {
|
return {
|
||||||
survey: this.initSurvey(),
|
text: this.answers.reduce((previous, answer) => {
|
||||||
title: '',
|
if (!answer.answer) {
|
||||||
module: {},
|
return previous;
|
||||||
completed: false,
|
}
|
||||||
me: {
|
if (answer.type === 'matrix') {
|
||||||
permissions: []
|
// wrap all the answers inside li tags and convert to a single string
|
||||||
},
|
const answerText = answer.answer.map(a => `<li class="solution-text__list-item">${a}</li>`).join('');
|
||||||
};
|
return `
|
||||||
},
|
${previous}
|
||||||
|
<h2 class="solution-text__heading">${answer.title}</h2>
|
||||||
computed: {
|
<ul class="solution-text__answer solution-text__list">${answerText}</ul>
|
||||||
surveyComplete() {
|
`;
|
||||||
return this.survey && this.survey.isCompleted;
|
} else {
|
||||||
},
|
|
||||||
showSolution() {
|
|
||||||
return (module.solutionsEnabled || isTeacher) && !this.survey.isCompleted;
|
|
||||||
},
|
|
||||||
solution() {
|
|
||||||
return {
|
|
||||||
text: this.answers.reduce((previous, answer) => {
|
|
||||||
if (!answer.answer) {
|
|
||||||
return previous;
|
|
||||||
}
|
|
||||||
let answerText;
|
|
||||||
if (typeof answer.answer === 'object') {
|
|
||||||
// this means the answer comes from a matrix, where the keys are the labels and the values are the respective answers
|
|
||||||
let answerObject = answer.answer;
|
|
||||||
let keysAndValues = [];
|
|
||||||
for (let prop of Object.keys(answerObject)) {
|
|
||||||
keysAndValues.push(`${prop}: ${answerObject[prop]}`);
|
|
||||||
}
|
|
||||||
answerText = keysAndValues.join(', ');
|
|
||||||
} else {
|
|
||||||
answerText = answer.answer;
|
|
||||||
}
|
|
||||||
return `
|
return `
|
||||||
${previous}
|
${previous}
|
||||||
<h2 class="solution-text__heading">${answer.title}</h2>
|
<h2 class="solution-text__heading">${answer.title}</h2>
|
||||||
<p class="solution-text__answer">${answerText}</p>
|
<p class="solution-text__answer">${answer.answer}</p>
|
||||||
`;
|
`;
|
||||||
}, '')
|
}
|
||||||
};
|
}, '')
|
||||||
},
|
};
|
||||||
answers() {
|
|
||||||
return this.survey.currentPage && this.survey.currentPage.elements
|
|
||||||
? this.survey.currentPage.elements.reduce(extractSurveySolutions, [])
|
|
||||||
: [];
|
|
||||||
},
|
|
||||||
isTeacher() {
|
|
||||||
return isTeacher(this);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
answers() {
|
||||||
|
return this.survey.currentPage && this.survey.currentPage.elements
|
||||||
|
? this.survey.currentPage.elements.reduce(extractSurveySolutions, [])
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
isTeacher() {
|
||||||
|
return isTeacher(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
initSurvey(data, answers) {
|
initSurvey(data, answers) {
|
||||||
let survey = new SurveyVue.Model(data);
|
let survey = new SurveyVue.Model(data);
|
||||||
const flatAnswers = {};
|
const flatAnswers = {};
|
||||||
for (let k in answers) {
|
for (let k in answers) {
|
||||||
flatAnswers[k] = answers[k].answer;
|
flatAnswers[k] = answers[k].answer;
|
||||||
|
}
|
||||||
|
survey.data = flatAnswers;
|
||||||
|
|
||||||
|
const saveSurvey = (sender, options) => {
|
||||||
|
// sender.clear(false);
|
||||||
|
//
|
||||||
|
// sender.mode = 'display';
|
||||||
|
|
||||||
|
this.completed = true;
|
||||||
|
|
||||||
|
const data = {};
|
||||||
|
|
||||||
|
for (let k in survey.data) {
|
||||||
|
if (survey.data.hasOwnProperty(k)) {
|
||||||
|
let question = sender.getQuestionByName(k);
|
||||||
|
data[k] = {
|
||||||
|
answer: survey.data[k],
|
||||||
|
correct: question && question.correctAnswer ? question.correctAnswer : ''
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
survey.data = flatAnswers;
|
|
||||||
|
|
||||||
const saveSurvey = (sender, options) => {
|
this.$apollo.mutate({
|
||||||
// sender.clear(false);
|
mutation: UPDATE_ANSWER,
|
||||||
//
|
variables: {
|
||||||
// sender.mode = 'display';
|
input: {
|
||||||
|
answer: {
|
||||||
this.completed = true;
|
surveyId: this.id,
|
||||||
|
data: JSON.stringify(data)
|
||||||
const data = {};
|
}
|
||||||
|
}
|
||||||
for (let k in survey.data) {
|
},
|
||||||
if (survey.data.hasOwnProperty(k)) {
|
// fixme: make the update work instead of refetching
|
||||||
let question = sender.getQuestionByName(k);
|
update: (store, {data: {updateAnswer: {answer}}}) => {
|
||||||
data[k] = {
|
const query = SURVEY_QUERY;
|
||||||
answer: survey.data[k],
|
const variables = {id: this.id};
|
||||||
correct: question && question.correctAnswer ? question.correctAnswer : ''
|
const queryData = store.readQuery({query, variables});
|
||||||
};
|
if (queryData.survey) {
|
||||||
|
queryData.survey.answer = answer;
|
||||||
|
store.writeQuery({query, variables, data: queryData});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.$apollo.mutate({
|
survey.onComplete.add(saveSurvey);
|
||||||
mutation: UPDATE_ANSWER,
|
survey.onCurrentPageChanged.add(saveSurvey);
|
||||||
|
|
||||||
|
survey.css = css;
|
||||||
|
survey.locale = 'de';
|
||||||
|
survey.showProgressBar = 'bottom';
|
||||||
|
survey.pageNextText = 'Speichern & Weiter';
|
||||||
|
return survey;
|
||||||
|
},
|
||||||
|
reopen() {
|
||||||
|
this.completed = false;
|
||||||
|
let data = this.survey.data; // save the data
|
||||||
|
this.survey.clear();
|
||||||
|
this.survey.data = data; // reapply it
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
apollo: {
|
||||||
|
survey: {
|
||||||
|
query: SURVEY_QUERY,
|
||||||
|
variables() {
|
||||||
|
return {
|
||||||
|
id: this.id
|
||||||
|
};
|
||||||
|
},
|
||||||
|
manual: true,
|
||||||
|
result({data, loading, networkStatus}) {
|
||||||
|
if (!loading) {
|
||||||
|
let json = JSON.parse(data.survey.data);
|
||||||
|
json.showTitle = false;
|
||||||
|
let answer = {};
|
||||||
|
if (data.survey.answer && data.survey.answer.data) {
|
||||||
|
answer = JSON.parse(data.survey.answer.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.completed) {
|
||||||
|
this.survey = this.initSurvey(json, answer);
|
||||||
|
}
|
||||||
|
this.title = json.title;
|
||||||
|
const module = data.survey.module;
|
||||||
|
|
||||||
|
this.$apollo.addSmartQuery('module', {
|
||||||
|
query: MODULE_QUERY,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
id: module.id
|
||||||
answer: {
|
|
||||||
surveyId: this.id,
|
|
||||||
data: JSON.stringify(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// fixme: make the update work instead of refetching
|
|
||||||
update: (store, {data: {updateAnswer: {answer}}}) => {
|
|
||||||
const query = SURVEY_QUERY;
|
|
||||||
const variables = {id: this.id};
|
|
||||||
const queryData = store.readQuery({query, variables});
|
|
||||||
if (queryData.survey) {
|
|
||||||
queryData.survey.answer = answer;
|
|
||||||
store.writeQuery({query, variables, data: queryData});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
survey.onComplete.add(saveSurvey);
|
|
||||||
survey.onCurrentPageChanged.add(saveSurvey);
|
|
||||||
|
|
||||||
survey.css = css;
|
|
||||||
survey.locale = 'de';
|
|
||||||
survey.showProgressBar = 'bottom';
|
|
||||||
survey.pageNextText = 'Speichern & Weiter';
|
|
||||||
return survey;
|
|
||||||
},
|
},
|
||||||
reopen() {
|
|
||||||
this.completed = false;
|
|
||||||
let data = this.survey.data; // save the data
|
|
||||||
this.survey.clear();
|
|
||||||
this.survey.data = data; // reapply it
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
me: meQuery
|
||||||
apollo: {
|
}
|
||||||
survey: {
|
};
|
||||||
query: SURVEY_QUERY,
|
|
||||||
variables() {
|
|
||||||
return {
|
|
||||||
id: this.id
|
|
||||||
};
|
|
||||||
},
|
|
||||||
manual: true,
|
|
||||||
result({data, loading, networkStatus}) {
|
|
||||||
if (!loading) {
|
|
||||||
let json = JSON.parse(data.survey.data);
|
|
||||||
json.showTitle = false;
|
|
||||||
let answer = {};
|
|
||||||
if (data.survey.answer && data.survey.answer.data) {
|
|
||||||
answer = JSON.parse(data.survey.answer.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.completed) {
|
|
||||||
this.survey = this.initSurvey(json, answer);
|
|
||||||
}
|
|
||||||
this.title = json.title;
|
|
||||||
const module = data.survey.module;
|
|
||||||
|
|
||||||
this.$apollo.addSmartQuery('module', {
|
|
||||||
query: MODULE_QUERY,
|
|
||||||
variables: {
|
|
||||||
id: module.id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
me: meQuery
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/styles/_variables.scss";
|
@import "@/styles/_variables.scss";
|
||||||
@import "@/styles/_mixins.scss";
|
@import "@/styles/_mixins.scss";
|
||||||
|
|
||||||
.survey-page {
|
.survey-page {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
grid-auto-rows: auto;
|
grid-auto-rows: auto;
|
||||||
grid-row-gap: $large-spacing;
|
grid-row-gap: $large-spacing;
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
padding: 100px 0;
|
padding: 100px 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
@include meta-title;
|
@include meta-title;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
@supports (display: grid) {
|
@supports (display: grid) {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
grid-template-rows: auto 1fr auto;
|
grid-template-rows: auto 1fr max-content;
|
||||||
grid-template-areas: "h" "c" "f";
|
grid-template-areas: "h" "c" "f";
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
grid-auto-rows: 1fr;
|
grid-auto-rows: 1fr;
|
||||||
|
|
@ -46,9 +46,27 @@
|
||||||
grid-area: h;
|
grid-area: h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
padding: 0 $small-spacing;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
grid-area: f;
|
grid-area: f;
|
||||||
|
// we usually set the margin to the bottom and the right, but here we want the footer to always have
|
||||||
|
// this margin, and we don't want to set it on every content element. And we don't want to set it on
|
||||||
|
// the content element, for if there's no footer.
|
||||||
margin-top: 3*$large-spacing;
|
margin-top: 3*$large-spacing;
|
||||||
|
margin-bottom: -3*$large-spacing;
|
||||||
|
|
||||||
|
padding: 0 $small-spacing;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
.intro {
|
||||||
|
@include lead-paragraph;
|
||||||
|
|
||||||
|
> p {
|
||||||
|
@include lead-paragraph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
}
|
}
|
||||||
&--blue {
|
&--blue {
|
||||||
background-color: $color-accent-2;
|
background-color: $color-accent-2;
|
||||||
|
|
||||||
& .widget-footer {
|
& .widget-footer {
|
||||||
background-color: $color-accent-2-dark;
|
background-color: $color-accent-2-dark;
|
||||||
}
|
}
|
||||||
|
|
@ -44,12 +45,14 @@
|
||||||
}
|
}
|
||||||
&--red {
|
&--red {
|
||||||
background-color: $color-accent-3;
|
background-color: $color-accent-3;
|
||||||
|
|
||||||
& .widget-footer {
|
& .widget-footer {
|
||||||
background-color: $color-accent-3-dark;
|
background-color: $color-accent-3-dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&--green {
|
&--green {
|
||||||
background-color: $color-accent-4;
|
background-color: $color-accent-4;
|
||||||
|
|
||||||
& .widget-footer {
|
& .widget-footer {
|
||||||
background-color: $color-accent-4-dark;
|
background-color: $color-accent-4-dark;
|
||||||
}
|
}
|
||||||
|
|
@ -118,6 +121,13 @@
|
||||||
font-size: toRem(14px);
|
font-size: toRem(14px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin tiny-text {
|
||||||
|
font-size: toRem(11px);
|
||||||
|
font-family: $sans-serif-font-family;
|
||||||
|
font-weight: $font-weight-regular;
|
||||||
|
color: $color-silver-dark;
|
||||||
|
}
|
||||||
|
|
||||||
@mixin aside-text {
|
@mixin aside-text {
|
||||||
@include regular-text;
|
@include regular-text;
|
||||||
font-size: toRem(14px);
|
font-size: toRem(14px);
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
.room {
|
.room {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: max-content 1fr;
|
||||||
margin-bottom: -50px;
|
margin-bottom: -90px;
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,14 @@
|
||||||
@include regular-text;
|
@include regular-text;
|
||||||
margin-bottom: $medium-spacing;
|
margin-bottom: $medium-spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
list-style: disc;
|
||||||
|
padding-left: $medium-spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list-item {
|
||||||
|
@include inline-title;
|
||||||
|
margin-bottom: $medium-spacing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,3 @@ input, textarea, select, button {
|
||||||
color: $color-brand-dark;
|
color: $color-brand-dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiny-text {
|
|
||||||
font-size: toRem(11px);
|
|
||||||
font-family: $sans-serif-font-family;
|
|
||||||
font-weight: $font-weight-regular;
|
|
||||||
color: $color-silver-dark;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ $default-padding: 30px;
|
||||||
$small-spacing: 10px;
|
$small-spacing: 10px;
|
||||||
$medium-spacing: 20px;
|
$medium-spacing: 20px;
|
||||||
$large-spacing: 30px;
|
$large-spacing: 30px;
|
||||||
|
$section-spacing: 60px;
|
||||||
|
|
||||||
$font-weight-bold: 700;
|
$font-weight-bold: 700;
|
||||||
$font-weight-semibold: 600;
|
$font-weight-semibold: 600;
|
||||||
|
|
@ -77,4 +78,4 @@ $default-heading-line-height: 1.2;
|
||||||
$popover-default-bottom: -110px;
|
$popover-default-bottom: -110px;
|
||||||
|
|
||||||
$footer-width: 800px;
|
$footer-width: 800px;
|
||||||
$news_width: 550px;
|
$news-width: 550px;
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,4 @@
|
||||||
@import "simple-list";
|
@import "simple-list";
|
||||||
@import "widget-popover";
|
@import "widget-popover";
|
||||||
@import "toast";
|
@import "toast";
|
||||||
|
@import "intro";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 2.2.12 on 2020-09-29 07:54
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import wagtail.core.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('basicknowledge', '0006_auto_20200520_0954'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='basicknowledge',
|
||||||
|
name='intro',
|
||||||
|
field=wagtail.core.fields.RichTextField(blank=True, default=''),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,16 +1,18 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
|
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
|
||||||
from wagtail.core.fields import StreamField
|
from wagtail.core.fields import StreamField, RichTextField
|
||||||
from wagtail.images.blocks import ImageChooserBlock
|
from wagtail.images.blocks import ImageChooserBlock
|
||||||
|
|
||||||
from books.blocks import LinkBlock, VideoBlock, DocumentBlock, SectionTitleBlock, InfogramBlock, \
|
from books.blocks import LinkBlock, VideoBlock, DocumentBlock, SectionTitleBlock, InfogramBlock, \
|
||||||
GeniallyBlock, InstrumentTextBlock, SubtitleBlock, ThinglinkBlock
|
GeniallyBlock, InstrumentTextBlock, SubtitleBlock, ThinglinkBlock, DEFAULT_RICH_TEXT_FEATURES
|
||||||
from core.wagtail_utils import StrictHierarchyPage
|
from core.wagtail_utils import StrictHierarchyPage
|
||||||
|
|
||||||
|
|
||||||
class BasicKnowledge(StrictHierarchyPage):
|
class BasicKnowledge(StrictHierarchyPage):
|
||||||
parent_page_types = ['books.book']
|
parent_page_types = ['books.book']
|
||||||
|
|
||||||
|
intro = RichTextField(features=DEFAULT_RICH_TEXT_FEATURES, default='', blank=True)
|
||||||
|
|
||||||
contents = StreamField([
|
contents = StreamField([
|
||||||
('text_block', InstrumentTextBlock()),
|
('text_block', InstrumentTextBlock()),
|
||||||
('image_block', ImageChooserBlock()),
|
('image_block', ImageChooserBlock()),
|
||||||
|
|
@ -42,6 +44,7 @@ class BasicKnowledge(StrictHierarchyPage):
|
||||||
content_panels = [
|
content_panels = [
|
||||||
FieldPanel('title', classname="full title"),
|
FieldPanel('title', classname="full title"),
|
||||||
FieldPanel('type'),
|
FieldPanel('type'),
|
||||||
|
FieldPanel('intro'),
|
||||||
StreamFieldPanel('contents')
|
StreamFieldPanel('contents')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class InstrumentNode(DjangoObjectType):
|
||||||
filter_fields = ['slug', 'type']
|
filter_fields = ['slug', 'type']
|
||||||
interfaces = (relay.Node,)
|
interfaces = (relay.Node,)
|
||||||
only_fields = [
|
only_fields = [
|
||||||
'slug', 'title', 'type', 'contents',
|
'slug', 'title', 'intro', 'type', 'contents',
|
||||||
]
|
]
|
||||||
|
|
||||||
def resolve_bookmarks(self, info, **kwargs):
|
def resolve_bookmarks(self, info, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class ObjectiveGroupAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(Objective)
|
@admin.register(Objective)
|
||||||
class ObjectiveAdmin(admin.ModelAdmin):
|
class ObjectiveAdmin(admin.ModelAdmin):
|
||||||
list_display = ('text', 'get_topic', 'group', 'owner')
|
list_display = ('text', 'get_topic', 'group', 'order', 'owner')
|
||||||
list_filter = ('group', 'owner')
|
list_filter = ('group', 'owner')
|
||||||
|
|
||||||
def get_topic(self, obj):
|
def get_topic(self, obj):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.14 on 2020-09-28 15:47
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('objectives', '0008_auto_20190821_1252'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='objectivegroup',
|
||||||
|
name='title',
|
||||||
|
field=models.CharField(blank=True, choices=[('language_communication', 'Sprache & Kommunikation'), ('society', 'Gesellschaft'), ('interdisciplinary', 'Überfachliche Lernziele')], default='language_communication', max_length=255, verbose_name='title'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.2.14 on 2020-09-30 13:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.expressions
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('objectives', '0009_auto_20200928_1547'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='objective',
|
||||||
|
options={'ordering': [django.db.models.expressions.OrderBy(django.db.models.expressions.F('owner'), nulls_first=True), django.db.models.expressions.OrderBy(django.db.models.expressions.F('order'), nulls_last=True)], 'verbose_name': 'Lernziel', 'verbose_name_plural': 'Lernziele'},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='objective',
|
||||||
|
name='order',
|
||||||
|
field=models.IntegerField(null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import F
|
||||||
|
|
||||||
from books.models import Module
|
from books.models import Module
|
||||||
from users.models import SchoolClass
|
from users.models import SchoolClass
|
||||||
|
|
@ -12,10 +13,12 @@ class ObjectiveGroup(models.Model):
|
||||||
|
|
||||||
LANGUAGE_COMMUNICATION = 'language_communication'
|
LANGUAGE_COMMUNICATION = 'language_communication'
|
||||||
SOCIETY = 'society'
|
SOCIETY = 'society'
|
||||||
|
INTERDISCIPLINARY = 'interdisciplinary'
|
||||||
|
|
||||||
TITLE_CHOICES = (
|
TITLE_CHOICES = (
|
||||||
(LANGUAGE_COMMUNICATION, 'Sprache & Kommunikation'),
|
(LANGUAGE_COMMUNICATION, 'Sprache & Kommunikation'),
|
||||||
(SOCIETY, 'Gesellschaft'),
|
(SOCIETY, 'Gesellschaft'),
|
||||||
|
(INTERDISCIPLINARY, 'Überfachliche Lernziele'),
|
||||||
)
|
)
|
||||||
|
|
||||||
title = models.CharField('title', blank=True, null=False, max_length=255, choices=TITLE_CHOICES, default=LANGUAGE_COMMUNICATION)
|
title = models.CharField('title', blank=True, null=False, max_length=255, choices=TITLE_CHOICES, default=LANGUAGE_COMMUNICATION)
|
||||||
|
|
@ -34,6 +37,7 @@ class Objective(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Lernziel'
|
verbose_name = 'Lernziel'
|
||||||
verbose_name_plural = 'Lernziele'
|
verbose_name_plural = 'Lernziele'
|
||||||
|
ordering = [F('owner').asc(nulls_first=True), F('order').asc(nulls_last=True)]
|
||||||
|
|
||||||
text = models.CharField('text', blank=True, null=False, max_length=255)
|
text = models.CharField('text', blank=True, null=False, max_length=255)
|
||||||
group = models.ForeignKey(ObjectiveGroup, blank=False, null=False, on_delete=models.CASCADE,
|
group = models.ForeignKey(ObjectiveGroup, blank=False, null=False, on_delete=models.CASCADE,
|
||||||
|
|
@ -41,6 +45,7 @@ class Objective(models.Model):
|
||||||
owner = 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_objectives', blank=True)
|
hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_objectives', blank=True)
|
||||||
visible_for = models.ManyToManyField(SchoolClass, related_name='visible_objectives', blank=True)
|
visible_for = models.ManyToManyField(SchoolClass, related_name='visible_objectives', blank=True)
|
||||||
|
order = models.IntegerField(null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Objective {}-{}'.format(self.id, self.text)
|
return 'Objective {}-{}'.format(self.id, self.text)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
from django.test import TestCase, RequestFactory
|
||||||
|
from graphene.test import Client
|
||||||
|
from graphql_relay import to_global_id
|
||||||
|
|
||||||
|
from api.schema import schema
|
||||||
|
from api.utils import get_object, get_graphql_mutation
|
||||||
|
from books.models import ContentBlock, Chapter
|
||||||
|
from books.factories import ModuleFactory
|
||||||
|
from core.factories import UserFactory
|
||||||
|
from core.management.commands import create_teacher
|
||||||
|
from notes.factories import ChapterBookmarkFactory, ModuleBookmarkFactory
|
||||||
|
from objectives.factories import ObjectiveGroupFactory
|
||||||
|
from objectives.models import Objective
|
||||||
|
from users.models import User
|
||||||
|
from users.services import create_users
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectiveOrderTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
create_users()
|
||||||
|
|
||||||
|
self.user = user = User.objects.get(username='teacher')
|
||||||
|
|
||||||
|
self.objective_group = ObjectiveGroupFactory(owner=None)
|
||||||
|
|
||||||
|
|
||||||
|
request = RequestFactory().get('/')
|
||||||
|
request.user = user
|
||||||
|
|
||||||
|
self.client = Client(schema=schema, context_value=request)
|
||||||
|
|
||||||
|
Objective.objects.create(owner=None, text='first', group=self.objective_group, order=0)
|
||||||
|
Objective.objects.create(owner=None, text='second', group=self.objective_group, order=1)
|
||||||
|
Objective.objects.create(owner=None, text='third', group=self.objective_group)
|
||||||
|
Objective.objects.create(owner=user, text='fourth', group=self.objective_group)
|
||||||
|
|
||||||
|
def test_objective_order(self):
|
||||||
|
query = """
|
||||||
|
query ObjectiveGroupQuery($id: ID!) {
|
||||||
|
objectiveGroup(id: $id) {
|
||||||
|
objectives {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = self.client.execute(query, variables={
|
||||||
|
'id': to_global_id('ObjectiveGroupNode', self.objective_group.pk)
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertIsNone(result.get('errors'))
|
||||||
|
objective_nodes = result.get('data').get('objectiveGroup').get('objectives').get('edges')
|
||||||
|
|
||||||
|
objective1, objective2, objective3, objective4 = [node['node'] for node in objective_nodes]
|
||||||
|
|
||||||
|
self.assertEqual(objective1.get('text'), 'first')
|
||||||
|
self.assertEqual(objective2.get('text'), 'second')
|
||||||
|
self.assertEqual(objective3.get('text'), 'third')
|
||||||
|
self.assertEqual(objective4.get('text'), 'fourth')
|
||||||
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.14 on 2020-09-28 15:47
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0023_user_onboarding_visited'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='license',
|
||||||
|
name='isbn',
|
||||||
|
field=models.CharField(default='978-3-0355-1397-4', max_length=50),
|
||||||
|
),
|
||||||
|
]
|
||||||
Loading…
Reference in New Issue