Merged in feature/MS-931-LoadingTimeBasicKnowlege (pull request #150)

Feature/MS-931 LoadingTimeBasicKnowlege

Approved-by: Ramon Wenger
This commit is contained in:
Lorenz Padberg 2024-05-02 07:47:11 +00:00
commit 6bd6599ba9
21 changed files with 3 additions and 655 deletions

View File

@ -33,7 +33,6 @@ const videoModule = {
},
bookmark: null,
__typename: 'ModuleNode',
objectiveGroups: [],
chapters: [
{
id: 'Q2hhcHRlck5vZGU6Nzc2',

View File

@ -1,87 +0,0 @@
import { getMe } from '../../../support/helpers';
import mocks from '../../../fixtures/mocks';
const modules = {
'lohn-und-budget': {
objectiveGroups: {
edges: [
{
node: {
title: 'LANGUAGE_COMMUNICATION',
objectives: {
edges: [
{
node: {
text: 'i-am-an-objective',
hiddenFor: {
edges: [],
},
},
},
],
},
},
},
],
},
},
};
const operations = {
MeQuery() {
return getMe({
schoolClasses: ['FLID2018a'],
teacher: false,
});
},
ModulesQuery: modules,
MySchoolClassQuery: {
me: {},
},
UpdateLastModule: {
updateLastModule: {
success: true,
},
},
SyncModuleVisibility: {
syncModuleVisibility: {
success: true,
},
},
};
// const mocks = {
// UUID: () => 'Whatever',
// GenericStreamFieldType: () => [],
// ObjectiveGroup: () => ({}),
// Module: () => ({
// title: 'title',
// slug: 'slug',
// metaTitle: 'metaTitle',
// teaser: 'teaser',
// intro: 'intro',
// assignments: {edges: []},
// objectiveGroups: {edges: []},
// id: 'ID',
// }),
// };
describe('Objective Visibility', () => {
beforeEach(() => {
cy.task('getSchema').then((schema) => {
cy.mockGraphql({
schema,
// endpoint: '/api/graphql'
mocks,
operations,
});
});
});
//todo: finish writing this test, this does nothing
it.skip('should display the correct objectives', () => {
cy.fakeLogin('rachel.green', 'test');
cy.visit('/module/lohn-und-budget');
});
});

View File

@ -13,7 +13,6 @@ describe('New project', () => {
appearance: 'blue',
description: 'This description rocks',
slug: 'some-random-title',
objectives: 'Git gud',
final: false,
schoolClass,
student: {

View File

@ -22,7 +22,6 @@ describe('Project Page', () => {
appearance: 'yellow',
description: 'I am Groot',
slug: 'groot',
objectives: 'Be Groot\nBe awesome',
final: false,
student: {
firstName: 'Rachel',
@ -49,7 +48,6 @@ describe('Project Page', () => {
appearance: 'red',
description: 'I am Groot',
slug: 'groot',
objectives: 'Be Groot\nBe awesome',
final: false,
student: {
firstName: 'Rachel',

View File

@ -45,10 +45,6 @@
],
"__typename": "AssignmentNodeConnection"
},
"objectiveGroups": {
"edges": [],
"__typename": "ObjectiveGroupNodeConnection"
},
"chapters": {
"edges": [
{
@ -117,10 +113,6 @@
"edges": [],
"__typename": "AssignmentNodeConnection"
},
"objectiveGroups": {
"edges": [],
"__typename": "ObjectiveGroupNodeConnection"
},
"chapters": {
"edges": [
{
@ -192,10 +184,6 @@
"edges": [],
"__typename": "AssignmentNodeConnection"
},
"objectiveGroups": {
"edges": [],
"__typename": "ObjectiveGroupNodeConnection"
},
"chapters": {
"edges": [
{
@ -234,10 +222,6 @@
"edges": [],
"__typename": "AssignmentNodeConnection"
},
"objectiveGroups": {
"edges": [],
"__typename": "ObjectiveGroupNodeConnection"
},
"chapters": {
"edges": [
{

View File

@ -95,7 +95,6 @@ export default {
intro: '',
assignments: [],
highlights: [],
objectiveGroups: [],
id: getModuleId(),
bookmark: null,
}),

View File

@ -13,10 +13,6 @@
"edges": [],
"__typename": "AssignmentNodeConnection"
},
"objectiveGroups": {
"edges": [],
"__typename": "ObjectiveGroupNodeConnection"
},
"chapters": {
"edges": [
{

View File

@ -44,10 +44,6 @@
],
"__typename": "AssignmentNodeConnection"
},
"objectiveGroups": {
"edges": [],
"__typename": "ObjectiveGroupNodeConnection"
},
"chapters": {
"edges": [
{

View File

@ -6,7 +6,6 @@ export default {
intro: 'intro',
assignments: [],
highlights: [],
objectiveGroups: [],
id: 'TW9kdWxlTm9kZToxMjM=',
chapters: [],
topic: {

View File

@ -133,10 +133,6 @@ export const getModules = () => {
],
__typename: 'AssignmentNodeConnection',
},
objectiveGroups: {
edges: [],
__typename: 'ObjectiveGroupNodeConnection',
},
chapters: {
edges: [
{
@ -207,10 +203,6 @@ export const getModules = () => {
edges: [],
__typename: 'AssignmentNodeConnection',
},
objectiveGroups: {
edges: [],
__typename: 'ObjectiveGroupNodeConnection',
},
chapters: {
edges: [
{
@ -284,10 +276,6 @@ export const getModules = () => {
edges: [],
__typename: 'AssignmentNodeConnection',
},
objectiveGroups: {
edges: [],
__typename: 'ObjectiveGroupNodeConnection',
},
chapters: {
edges: [
{
@ -328,10 +316,6 @@ export const getModules = () => {
edges: [],
__typename: 'AssignmentNodeConnection',
},
objectiveGroups: {
edges: [],
__typename: 'ObjectiveGroupNodeConnection',
},
chapters: {
edges: [
{

View File

@ -37,7 +37,6 @@ const EditRoomEntryWizard = defineAsyncComponent(() =>
);
const NewProjectEntryWizard = defineAsyncComponent(() => import('@/components/portfolio/NewProjectEntryWizard.vue'));
const EditProjectEntryWizard = defineAsyncComponent(() => import('@/components/portfolio/EditProjectEntryWizard.vue'));
const NewObjectiveWizard = defineAsyncComponent(() => import('@/components/objective-groups/NewObjectiveWizard.vue'));
const NewNoteWizard = defineAsyncComponent(() => import('@/components/notes/NewNoteWizard.vue'));
const EditNoteWizard = defineAsyncComponent(() => import('@/components/notes/EditNoteWizard.vue'));
const EditClassNameWizard = defineAsyncComponent(() => import('@/components/school-class/EditClassNameWizard.vue'));
@ -69,7 +68,6 @@ export default {
EditRoomEntryWizard,
NewProjectEntryWizard,
EditProjectEntryWizard,
NewObjectiveWizard,
NewNoteWizard,
EditNoteWizard,
EditClassNameWizard,

View File

@ -60,33 +60,6 @@
/>
</div>
<h3
v-if="$flavor.showObjectivesTitle && module.objectiveGroups.length && showObjectives"
id="objectives"
>
<span>Lernziele</span>
</h3>
<div
class="module__objective-groups"
v-if="module.objectiveGroups.length && showObjectives"
>
<objective-groups
:groups="languageCommunicationObjectiveGroups"
v-if="languageCommunicationObjectiveGroups.length"
/>
<objective-groups
:groups="societyObjectiveGroups"
v-if="societyObjectiveGroups.length"
/>
<objective-groups
:groups="interdisciplinaryObjectiveGroups"
v-if="interdisciplinaryObjectiveGroups.length"
/>
</div>
<chapter
:chapter="chapter"
:index="index"
@ -98,7 +71,6 @@
</template>
<script setup lang="ts">
import ObjectiveGroups from '@/components/objective-groups/ObjectiveGroups.vue';
import Chapter from '@/components/Chapter.vue';
import BookmarkActions from '@/components/notes/BookmarkActions.vue';
import Pill from '@/components/ui/Pill.vue';
@ -115,12 +87,10 @@ import { graphql } from '@/__generated__';
import highlightSidebar from '@/helpers/highlight-sidebar';
import { doUpdateHighlight } from '@/graphql/mutations';
import Mark from 'mark.js';
import { useRoute } from 'vue-router';
export interface Props {
module: ModuleNode;
}
const route = useRoute();
const props = defineProps<Props>();
let selectionHandler: SelectionHandlerType;
@ -223,24 +193,7 @@ const note = computed(() => {
return props.module.bookmark.note;
});
const filterObjectiveGroup = (title: string) => {
return props.module.objectiveGroups
? props.module.objectiveGroups.filter((group) => group.title.toLowerCase() === title)
: [];
};
const languageCommunicationObjectiveGroups = computed(() => {
return filterObjectiveGroup('language_communication');
});
const societyObjectiveGroups = computed(() => {
return filterObjectiveGroup('society');
});
const interdisciplinaryObjectiveGroups = computed(() => {
return filterObjectiveGroup('interdisciplinary');
});
const showObjectives = computed(() => {
return route && route.query['show-objectives'] !== undefined;
});
</script>
<style scoped lang="scss">
@ -315,8 +268,5 @@ const showObjectives = computed(() => {
margin-top: 3px;
}
&__objective-groups {
margin-bottom: 2 * $large-spacing;
}
}
</style>

View File

@ -1,120 +0,0 @@
<template>
<modal
:hide-header="true"
:small="true"
>
<modal-input
:placeholder="'Lernziel'"
:value="text"
@input="text = $event"
/>
<template #footer>
<div>
<a
:class="{ 'button--disabled': disableSave }"
class="button button--primary"
data-cy="modal-save-button"
@click="save(text)"
>Speichern</a
>
<a
class="button"
@click="hide()"
>Abbrechen</a
>
</div>
</template>
</modal>
</template>
<script>
import Modal from '@/components/Modal.vue';
import ModalInput from '@/components/ModalInput.vue';
import NEW_OBJECTIVE_MUTATION from '@/graphql/gql/mutations/addObjective.gql';
import OBJECTIVE_GROUP_QUERY from '@/graphql/gql/queries/objectiveGroupQuery.gql';
export default {
components: {
Modal,
ModalInput,
},
data() {
return {
text: '',
saving: false,
};
},
computed: {
objectiveGroup() {
return this.$modal.state.payload.parent;
},
disableSave() {
return this.saving;
},
},
methods: {
save(entry) {
this.saving = true;
this.$apollo
.mutate({
mutation: NEW_OBJECTIVE_MUTATION,
variables: {
input: {
objective: Object.assign(
{},
{
objectiveGroup: this.objectiveGroup,
text: entry,
}
),
},
},
update: (
store,
{
data: {
addObjective: { objective },
},
}
) => {
try {
const query = OBJECTIVE_GROUP_QUERY;
const variables = { id: this.objectiveGroup };
const { objectiveGroup } = store.readQuery({ query, variables });
if (objectiveGroup && objectiveGroup.objectives) {
const objectives = [
...objectiveGroup.objectives,
{
...objective,
__typename: 'ObjectiveNode',
},
];
const data = {
objectiveGroup: {
...objectiveGroup,
objectives,
},
};
store.writeQuery({ query, variables, data });
}
} catch (e) {
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
}
},
})
.then(() => {
this.saving = false;
this.$modal.confirm();
});
},
hide() {
this.$modal.cancel();
},
},
};
</script>

View File

@ -1,127 +0,0 @@
<template>
<li
:class="{ 'hideable-element--greyed-out': hidden }"
class="objective hideable-element"
v-if="editMode || !hidden"
>
<visibility-action
:block="objective"
:type="type"
v-if="editMode"
/>
<div
class="block-actions"
v-if="editMode && canEdit"
>
<user-widget
v-bind="me"
class="block-actions__user-widget objective__user-widget"
/>
<more-options-widget>
<div class="popover-links__link">
<a @click="deleteObjective(objective)">Löschen</a>
</div>
</more-options-widget>
</div>
<div>
{{ objective.text }}
</div>
</li>
</template>
<script>
import VisibilityAction from '@/components/visibility/VisibilityAction.vue';
import UserWidget from '@/components/UserWidget.vue';
import MoreOptionsWidget from '@/components/MoreOptionsWidget.vue';
import DELETE_OBJECTIVE_MUTATION from '@/graphql/gql/mutations/deleteObjective.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
import { hidden } from '@/helpers/visibility';
import { OBJECTIVE_TYPE } from '@/consts/types';
import me from '@/mixins/me';
export default {
props: {
objective: {
type: Object,
default: null,
},
editMode: {
type: Boolean,
default: false,
},
},
mixins: [me],
components: {
MoreOptionsWidget,
VisibilityAction,
UserWidget,
},
data() {
return {
type: OBJECTIVE_TYPE,
};
},
computed: {
hidden() {
return hidden({
block: this.objective,
schoolClass: this.schoolClass,
type: this.type,
});
},
canEdit() {
return this.objective.mine;
},
},
methods: {
deleteObjective(objective) {
this.$apollo.mutate({
mutation: DELETE_OBJECTIVE_MUTATION,
variables: {
input: {
id: objective.id,
},
},
refetchQueries: [
{
query: MODULE_DETAILS_QUERY,
variables: {
slug: this.$route.params.slug,
},
},
],
// todo: make update work here also
// update(store, {data: {deleteObjective: {success}}}) {
// if (success) {
// const query = MODULE_DETAILS_QUERY;
// const variables = {slug: this.$route.params.slug};
// const data = store.readQuery({query, variables});
// if (data) {
// data.module.objectiveGroups.edges.
// data.rooms.edges.splice(data.rooms.edges.findIndex(edge => edge.node.id === theId), 1);
// store.writeQuery({query, variables, data});
// }
// }
// }
});
},
},
};
</script>
<style scoped lang="scss">
.objective {
min-height: 50px;
&__user-widget {
margin-right: 0;
}
}
</style>

View File

@ -1,45 +0,0 @@
<template>
<div class="objective-form">
<modal-input
:value="objective.text"
class="objective-form__input"
placeholder="Lernziel erfassen..."
@input="$emit('input', $event)"
/>
<a
class="icon-button"
@click="$emit('delete')"
>
<trash-icon class="icon-button__icon icon-button__icon--subtle" />
</a>
</div>
</template>
<script>
import ModalInput from '@/components/ModalInput.vue';
import { defineAsyncComponent } from 'vue';
const TrashIcon = defineAsyncComponent(() => import('@/components/icons/TrashIcon.vue'));
export default {
props: ['objective'],
components: {
ModalInput,
TrashIcon,
},
};
</script>
<style scoped lang="scss">
@import '@/styles/_variables.scss';
.objective-form {
display: grid;
grid-template-columns: 1fr 50px;
margin-bottom: 10px;
&__input {
width: $modal-input-width;
}
}
</style>

View File

@ -1,92 +0,0 @@
<template>
<div
:class="{ 'hideable-element--greyed-out': hidden }"
class="objective-group hideable-element"
v-if="editMode || !hidden"
>
<visibility-action
:block="group"
:type="type"
v-if="editMode"
/>
<h4>{{ group.displayTitle }}</h4>
<ul class="objective-group__objective-list">
<objective
:objective="objective"
class="objective-group__objective"
:edit-mode="editMode"
v-for="objective in group.objectives"
:key="objective.id"
/>
</ul>
<add-content-button
:parent="group"
:where="{ parent: group }"
v-if="editMode"
/>
</div>
</template>
<script>
import Objective from '@/components/objective-groups/Objective.vue';
import VisibilityAction from '@/components/visibility/VisibilityAction.vue';
import AddContentButton from '@/components/AddContentButton.vue';
import me from '@/mixins/me';
import { OBJECTIVE_GROUP_TYPE } from '@/consts/types';
import { hidden } from '@/helpers/visibility';
export default {
props: {
group: {
required: true,
type: Object,
},
},
mixins: [me],
components: {
AddContentButton,
Objective,
VisibilityAction,
},
data() {
return {
type: OBJECTIVE_GROUP_TYPE,
};
},
computed: {
editMode() {
return this.group.module?.inEditMode || false;
},
hidden() {
return hidden({
block: this.group,
schoolClass: this.schoolClass,
type: this.type,
});
},
},
};
</script>
<style scoped lang="scss">
@import 'styles/helpers';
.objective-group {
position: relative;
&__actions {
position: absolute;
left: -70px;
top: -4px;
display: grid;
}
}
</style>

View File

@ -1,75 +0,0 @@
<template>
<div class="objective-groups">
<template v-for="group in filteredObjectiveGroups">
<objective-group
:group="group"
v-if="group.objectives.length"
:key="group.id"
/>
</template>
</div>
</template>
<script>
import ObjectiveGroup from '@/components/objective-groups/ObjectiveGroup.vue';
import { meQuery } from '@/graphql/queries';
export default {
props: {
groups: {
type: Array,
default: () => [],
},
},
components: {
ObjectiveGroup,
},
apollo: {
me: meQuery,
},
data() {
return {
me: {},
};
},
computed: {
filteredObjectiveGroups() {
return this.objectiveGroups.filter((g) => !g.hidden);
},
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.isTeacher || !this.groups.length) {
return this.groups;
} else {
// todo: maybe this can be done a bit more elegantly
let groups = this.groups;
const objectives = groups.map((g) => g.objectives).flat(); // get all objectives in one array
const firstGroup = Object.assign({}, groups[0], { objectives });
return [firstGroup];
}
},
},
};
</script>
<style scoped lang="scss">
@import 'styles/helpers';
.objective-groups {
margin-bottom: $large-spacing;
display: flex;
flex-direction: column;
&:last-child {
margin-bottom: 0;
}
}
</style>

View File

@ -2,7 +2,6 @@ query InstrumentsQuery {
instruments {
id
title
contents
slug
language
type {

View File

@ -1,18 +1,10 @@
#import "gql/fragments/chapterParts.gql"
#import "gql/fragments/objectiveGroupParts.gql"
#import "gql/fragments/objectiveParts.gql"
#import "gql/fragments/moduleParts.gql"
#import "gql/fragments/contentBlockInterfaceParts.gql"
#import "gql/fragments/contentBlockParts.gql"
query ModuleDetailsQuery($slug: String, $id: ID) {
module(slug: $slug, id: $id) {
...ModuleLegacyParts
objectiveGroups {
...ObjectiveGroupParts
objectives {
...ObjectiveParts
}
}
chapters {
...ChapterParts
contentBlocks {

View File

@ -100,8 +100,9 @@ class InstrumentQuery(object):
return None
def resolve_instruments(self, info, **kwargs):
return BasicKnowledge.objects.all().order_by("title").live()
return BasicKnowledge.objects.all().order_by("title").live().select_related("new_type",
"locale",
"new_type__category")
def resolve_instrument_types(self, info, **kwargs):
return (
InstrumentType.objects.filter(instruments__isnull=False)