Merged in feature/extended-instrument-categories (pull request #118)

Feature/extended instrument categories
This commit is contained in:
Ramon Wenger 2022-09-14 11:33:48 +00:00
commit d787367020
24 changed files with 377 additions and 112 deletions

View File

@ -1,22 +1,50 @@
const LANGUAGE_COMMUNICATION = 'LANGUAGE_COMMUNICATION';
const LANGUAGE_COMMUNICATION_VALUE = 'Sprache & Kommunikation';
const SOCIETY = 'SOCIETY';
const SOCIETY_VALUE = 'Gesellschaft';
const INTERDISCIPLINARY = 'INTERDISCIPLINARY';
const INTERDISCIPLINARY_VALUE = 'Überfachliche Instrumente';
describe('Instruments Page', () => { describe('Instruments Page', () => {
beforeEach(() => { beforeEach(() => {
cy.setup(); cy.setup();
const languageCategory = {
name: LANGUAGE_COMMUNICATION_VALUE,
id: LANGUAGE_COMMUNICATION,
foreground: '#000000',
background: '#00ffff',
};
const societyCategory = {
name: SOCIETY_VALUE,
id: SOCIETY,
foreground: '#ffffff',
background: '#000fff',
};
const ANALYSE = 'analyse';
const ARGUMENTATION = 'argumentation';
const ETHIK = 'ethik';
const analyse = { const analyse = {
name: 'Analyse', name: 'Analyse',
category: 'LANGUAGE_COMMUNICATION', category: languageCategory,
type: 'analyse', type: ANALYSE,
id: ANALYSE
}; };
const argumentation = { const argumentation = {
name: 'Argumentation', name: 'Argumentation',
category: 'LANGUAGE_COMMUNICATION', category: languageCategory,
type: 'argumentation', type: ARGUMENTATION,
id: ARGUMENTATION
}; };
const ethik = { const ethik = {
name: 'Ethik', name: 'Ethik',
category: 'SOCIETY', category: societyCategory,
type: 'ethik', type: ETHIK,
id: ETHIK
}; };
cy.mockGraphqlOps({ cy.mockGraphqlOps({
@ -38,23 +66,40 @@ describe('Instruments Page', () => {
type: ethik, type: ethik,
title: 'Instrument: Ethik', title: 'Instrument: Ethik',
slug: 'ethik', slug: 'ethik',
} },
], ],
}, },
InstrumentTypesQuery: { InstrumentCategoriesQuery: {
instrumentTypes: [ instrumentCategories: [
analyse,
argumentation,
{ {
name: 'Beschreibung', name: 'Sprache & Kommunikation',
category: 'LANGUAGE_COMMUNICATION', id: LANGUAGE_COMMUNICATION,
type: 'beschreibung', types: [
analyse,
argumentation,
{
name: 'Beschreibung',
category: languageCategory,
type: 'beschreibung',
},
],
}, },
ethik,
{ {
name: 'Identität und Sozialisation', name: SOCIETY_VALUE,
category: 'SOCIETY', id: SOCIETY,
type: 'identitt-und-sozialisation', types: [
ethik,
{
name: 'Identität und Sozialisation',
category: societyCategory,
type: 'identitt-und-sozialisation',
},
],
},
{
name: INTERDISCIPLINARY_VALUE,
id: INTERDISCIPLINARY,
types: [],
}, },
], ],
}, },
@ -67,19 +112,19 @@ describe('Instruments Page', () => {
cy.getByDataCy('instrument').should('have.length', 3); cy.getByDataCy('instrument').should('have.length', 3);
cy.getByDataCy('filter-language-communication').click(); cy.contains(LANGUAGE_COMMUNICATION_VALUE).click();
cy.getByDataCy('instrument').should('have.length', 2); cy.getByDataCy('instrument').should('have.length', 2);
cy.getByDataCy('filter-society').click(); cy.contains(SOCIETY_VALUE).click();
cy.getByDataCy('instrument').should('have.length', 1); cy.getByDataCy('instrument').should('have.length', 1);
cy.getByDataCy('filter-interdisciplinary').click(); cy.contains(INTERDISCIPLINARY_VALUE).click();
cy.getByDataCy('instrument').should('have.length', 0); cy.getByDataCy('instrument').should('have.length', 0);
cy.getByDataCy('filter-analyse').click(); cy.contains('Analyse').click();
cy.getByDataCy('instrument').should('have.length', 1); cy.getByDataCy('instrument').should('have.length', 1);
cy.getByDataCy('filter-ethik').click(); cy.contains('Ethik').click();
cy.getByDataCy('instrument').should('have.length', 1); cy.getByDataCy('instrument').should('have.length', 1);
cy.getByDataCy('filter-all-instruments').click(); cy.getByDataCy('filter-all-instruments').click();

View File

@ -12,7 +12,6 @@
</template> </template>
<script> <script>
import {INTERDISCIPLINARY, LANGUAGE_COMMUNICATION, SOCIETY} from '@/consts/instrument.consts';
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql'; import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
const ChevronRight = () => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight'); const ChevronRight = () => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight');
@ -22,7 +21,7 @@
type: String, type: String,
required: true, required: true,
}, },
type: { id: {
type: String, type: String,
default: '', default: '',
}, },
@ -31,8 +30,8 @@
default: false, default: false,
}, },
category: { category: {
type: String, type: Object,
default: '', default: () => {},
}, },
}, },
@ -65,9 +64,6 @@
}, },
typeClass() { typeClass() {
return { return {
'filter-entry--language-communication': this.category === LANGUAGE_COMMUNICATION,
'filter-entry--society': this.category === SOCIETY,
'filter-entry--interdisciplinary': this.category === INTERDISCIPLINARY,
'filter-entry--active': this.isActive, 'filter-entry--active': this.isActive,
'filter-entry--category': this.isCategory, 'filter-entry--category': this.isCategory,
}; };

View File

@ -3,20 +3,20 @@
<filter-entry <filter-entry
:text="title" :text="title"
v-bind="$attrs" v-bind="$attrs"
:type="category"
:category="category" :category="category"
:is-category="true" :is-category="true"
@click.native="setCategoryFilter(category)" :id="category.id"
@click.native="setCategoryFilter(category.id)"
/> />
<div class="filter-group__children"> <div class="filter-group__children">
<filter-entry <filter-entry
:data-cy="`filter-${type.type}`" :data-cy="`filter-${type.type}`"
:category="type.category" :category="type.category"
:text="type.name" :text="type.name"
:type="type.type"
v-for="type in types" v-for="type in types"
:id="type.id"
:key="type.id" :key="type.id"
@click.native="setFilter(`type:${type.type}`)" @click.native="setFilter(`type:${type.id}`)"
/> />
</div> </div>
</div> </div>
@ -39,8 +39,8 @@
default: () => [], default: () => [],
}, },
category: { category: {
type: String, type: Object,
default: '', default: () => ({}),
}, },
}, },
components: { components: {

View File

@ -1,15 +1,21 @@
<template> <template>
<div <div
:class="typeClass" :style="{
color: instrument.type.category.foreground,
backgroundColor: instrument.type.category.background
}"
class="instrument-entry" class="instrument-entry"
> >
<h4 <h4
class="instrument-entry__category" class="instrument-entry__category"
:style="{color: instrument.type.category.foreground}"
data-cy="instrument-subheader" data-cy="instrument-subheader"
> >
{{ categoryName }} {{ categoryName }}
</h4> </h4>
<h3 class="instrument-entry__title"> <h3
class="instrument-entry__title"
>
{{ instrument.title }} {{ instrument.title }}
</h3> </h3>
</div> </div>

View File

@ -6,72 +6,34 @@
/> />
<filter-group <filter-group
:types="languageCommunicationTypes" :types="category.types"
:category="LANGUAGE_COMMUNICATION" :category="category"
title="Sprache und Kommunikation" :title="category.name"
data-cy="filter-language-communication" v-for="category in instrumentCategories"
class="instrument-filter__group--language" :key="category.id"
/>
<filter-group
:types="societyTypes"
:category="SOCIETY"
title="Gesellschaft"
data-cy="filter-society"
/>
<filter-group
:category="INTERDISCIPLINARY"
title="Überfachliche Instrumente"
data-cy="filter-interdisciplinary"
/> />
</div> </div>
</template> </template>
<script> <script>
import {INTERDISCIPLINARY, LANGUAGE_COMMUNICATION, SOCIETY} from '@/consts/instrument.consts';
import FilterGroup from '@/components/instruments/FilterGroup'; import FilterGroup from '@/components/instruments/FilterGroup';
import INSTRUMENT_TYPES_QUERY from 'gql/queries/instrumentTypesQuery'; import INSTRUMENT_CATEGORIES_QUERY from 'gql/queries/instrumentCategoriesQuery.gql';
export default { export default {
components: { components: {
FilterGroup, FilterGroup,
}, },
data() { data() {
return { return {
filter: '', instrumentCategories: [],
LANGUAGE_COMMUNICATION,
SOCIETY,
INTERDISCIPLINARY,
instrumentTypes: [],
}; };
}, },
computed: {
languageCommunicationTypes() {
return this.instrumentTypes.filter(t => t.category === 'LANGUAGE_COMMUNICATION');
},
societyTypes() {
return this.instrumentTypes.filter(t => t.category === 'SOCIETY');
},
interdisciplinaryTypes() {
return this.instrumentTypes.filter(t => t.category === 'INTERDISCIPLINARY');
},
},
methods: {
setFilter(filter) {
this.filter = filter;
this.$emit('filter', filter);
}
},
apollo: { apollo: {
instrumentTypes: { instrumentCategories: {
query: INSTRUMENT_TYPES_QUERY, query: INSTRUMENT_CATEGORIES_QUERY,
}, },
}, },
}; };

View File

@ -13,7 +13,12 @@ fragment InstrumentParts on InstrumentNode {
type { type {
id id
name name
category category {
id
name
foreground
background
}
type type
} }
contents contents

View File

@ -0,0 +1,17 @@
query InstrumentCategoriesQuery {
instrumentCategories {
name
id
types {
name
id
type
category {
id
name
foreground
background
}
}
}
}

View File

@ -2,6 +2,11 @@ query InstrumentTypesQuery {
instrumentTypes { instrumentTypes {
name name
type type
category category {
id
name
foreground
background
}
} }
} }

View File

@ -7,7 +7,12 @@ query InstrumentsQuery {
type { type {
id id
type type
category category {
id
name
foreground
background
}
name name
} }
} }

View File

@ -6,7 +6,7 @@ const instrumentType = (instrument) => {
if (typeof instrument === 'string') { if (typeof instrument === 'string') {
category = instrument; category = instrument;
} else { } else {
category = instrument.type.category; return instrument.type.category.name;
} }
return typeDictionary[category] || ''; return typeDictionary[category] || '';
}; };

View File

@ -48,7 +48,11 @@
const {currentFilter} = instrumentFilter; const {currentFilter} = instrumentFilter;
if (currentFilter && currentFilter.indexOf(':') > -1) { if (currentFilter && currentFilter.indexOf(':') > -1) {
const [filterType, identifier] = currentFilter.split(':'); const [filterType, identifier] = currentFilter.split(':');
this.filter = i => i.type[filterType] === identifier; if(filterType === 'type') {
this.filter = i => i.type.id === identifier;
} else {
this.filter = i => i.type.category.id === identifier;
}
} else { } else {
this.filter = i => i; // identity this.filter = i => i; // identity
} }

View File

@ -16,9 +16,9 @@ from users.models import User
from users.services import create_users from users.services import create_users
class MyAssignemntsText(DefaultUserTestCase): class MyAssignmentsTest(DefaultUserTestCase):
def setUp(self): def setUp(self):
super(MyAssignemntsText, self).setUp() super(MyAssignmentsTest, self).setUp()
self.assignment = AssignmentFactory( self.assignment = AssignmentFactory(
owner=self.teacher owner=self.teacher
) )
@ -119,7 +119,10 @@ class MyAssignemntsText(DefaultUserTestCase):
type { type {
id id
type type
category category {
id
name
}
} }
slug slug
bookmarks { bookmarks {

View File

@ -0,0 +1,31 @@
# Generated by Django 3.2.13 on 2022-09-13 08:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('basicknowledge', '0018_alter_basicknowledge_contents'),
]
operations = [
migrations.CreateModel(
name='InstrumentCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True)),
('background', models.CharField(max_length=7, verbose_name='background color')),
('foreground', models.CharField(max_length=7, verbose_name='foreground color')),
],
options={
'verbose_name_plural': 'instrument categories',
},
),
migrations.AddField(
model_name='instrumenttype',
name='new_category',
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='basicknowledge.instrumentcategory'),
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 3.2.13 on 2022-09-08 15:17
from django.db import migrations
LANGUAGE_COMMUNICATION = 'language_communication'
SOCIETY = 'society'
INTERDISCIPLINARY = 'interdisciplinary'
CATEGORY_CHOICES = (
(LANGUAGE_COMMUNICATION, '#DAA009', '#FFF5D9', 'Sprache & Kommunikation'),
(SOCIETY, '#0F7CAC', '#DBEEF6', 'Gesellschaft'),
(INTERDISCIPLINARY, '#99B53E', '#F3F9E3', 'Überfachliche Instrumente'),
)
def create_categories(apps, schema_editor):
InstrumentCategory = apps.get_model('basicknowledge', 'InstrumentCategory')
InstrumentType = apps.get_model('basicknowledge', 'InstrumentType')
for code, foreground, background, category in CATEGORY_CHOICES:
instrument_category = InstrumentCategory.objects.create(name=category, background=background, foreground=foreground)
InstrumentType.objects.filter(category=code).update(new_category=instrument_category)
class Migration(migrations.Migration):
dependencies = [
('basicknowledge', '0019_auto_20220913_0820'),
]
operations = [
migrations.RunPython(create_categories, migrations.RunPython.noop)
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.13 on 2022-09-13 08:52
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('basicknowledge', '0020_auto_20220908_1517'),
]
operations = [
migrations.RemoveField(
model_name='instrumenttype',
name='category',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.13 on 2022-09-13 08:52
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('basicknowledge', '0021_remove_instrumenttype_category'),
]
operations = [
migrations.RenameField(
model_name='instrumenttype',
old_name='new_category',
new_name='category',
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.13 on 2022-09-13 09:01
import basicknowledge.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('basicknowledge', '0022_rename_new_category_instrumenttype_category'),
]
operations = [
migrations.AlterField(
model_name='instrumenttype',
name='category',
field=models.ForeignKey(default=basicknowledge.models.default_category, on_delete=django.db.models.deletion.PROTECT, to='basicknowledge.instrumentcategory'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.13 on 2022-09-13 10:55
import basicknowledge.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('basicknowledge', '0023_alter_instrumenttype_category'),
]
operations = [
migrations.AlterField(
model_name='basicknowledge',
name='old_type',
field=models.CharField(blank=True, choices=[('language_communication', 'Sprache & Kommunikation'), ('society', 'Gesellschaft'), ('interdisciplinary', 'Überfachliche Instrumente')], max_length=100),
),
migrations.AlterField(
model_name='instrumenttype',
name='category',
field=models.ForeignKey(default=basicknowledge.models.default_category, on_delete=django.db.models.deletion.PROTECT, related_name='instrument_types', to='basicknowledge.instrumentcategory'),
),
]

View File

@ -13,19 +13,41 @@ from core.wagtail_utils import StrictHierarchyPage
LANGUAGE_COMMUNICATION = 'language_communication' LANGUAGE_COMMUNICATION = 'language_communication'
SOCIETY = 'society' SOCIETY = 'society'
INTERDISCIPLINARY = 'interdisciplinary' INTERDISCIPLINARY = 'interdisciplinary'
LANGUAGE_COMMUNICATION_LABEL = 'Sprache & Kommunikation'
SOCIETY_LABEL = 'Gesellschaft'
INTERDISCIPLINARY_LABEL = 'Überfachliche Instrumente'
class InstrumentCategory(models.Model):
name = models.CharField(max_length=255, unique=True)
background = models.CharField('background color', max_length=7)
foreground = models.CharField('foreground color', max_length=7)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'instrument categories'
def default_category():
return InstrumentCategory.objects.first().pk
class InstrumentType(models.Model): class InstrumentType(models.Model):
CATEGORY_CHOICES = ( CATEGORY_CHOICES = (
(LANGUAGE_COMMUNICATION, 'Sprache & Kommunikation'), (LANGUAGE_COMMUNICATION, LANGUAGE_COMMUNICATION_LABEL),
(SOCIETY, 'Gesellschaft'), (SOCIETY, SOCIETY_LABEL),
(INTERDISCIPLINARY, 'Überfachliches Instrument'), (INTERDISCIPLINARY, INTERDISCIPLINARY_LABEL),
) )
name = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255, unique=True)
category = models.CharField( category = models.ForeignKey(
max_length=100, InstrumentCategory,
choices=CATEGORY_CHOICES on_delete=models.PROTECT,
null=False,
default=default_category,
related_name='instrument_types'
) )
@property @property
@ -36,7 +58,6 @@ class InstrumentType(models.Model):
return self.type return self.type
class BasicKnowledge(StrictHierarchyPage): class BasicKnowledge(StrictHierarchyPage):
parent_page_types = ['books.book'] parent_page_types = ['books.book']

View File

@ -6,14 +6,31 @@ from api.graphene_wagtail import GenericStreamFieldType
from api.utils import get_object from api.utils import get_object
from notes.models import InstrumentBookmark from notes.models import InstrumentBookmark
from notes.schema import InstrumentBookmarkNode from notes.schema import InstrumentBookmarkNode
from .models import BasicKnowledge, InstrumentType from .models import BasicKnowledge, InstrumentCategory, InstrumentType
class InstrumentCategoryNode(DjangoObjectType):
types = graphene.List('basicknowledge.queries.InstrumentTypeNode')
class Meta:
model = InstrumentCategory
interfaces = (relay.Node,)
only_fields = [
'name', 'foreground', 'background', 'id'
]
@staticmethod
def resolve_types(root: InstrumentCategory, info, **kwargs):
return root.instrument_types.filter(instruments__isnull=False).order_by('name').distinct()
class InstrumentTypeNode(DjangoObjectType): class InstrumentTypeNode(DjangoObjectType):
type = graphene.String(required=True) type = graphene.String(required=True)
category = graphene.Field(InstrumentCategoryNode)
class Meta: class Meta:
model = InstrumentType model = InstrumentType
interfaces = (relay.Node,)
only_fields = [ only_fields = [
'name', 'category', 'type', 'id' 'name', 'category', 'type', 'id'
] ]
@ -51,6 +68,7 @@ class InstrumentQuery(object):
instrument = graphene.Field(InstrumentNode, slug=graphene.String(), id=graphene.ID()) instrument = graphene.Field(InstrumentNode, slug=graphene.String(), id=graphene.ID())
instruments = graphene.List(InstrumentNode) instruments = graphene.List(InstrumentNode)
instrument_types = graphene.List(InstrumentTypeNode) instrument_types = graphene.List(InstrumentTypeNode)
instrument_categories = graphene.List(InstrumentCategoryNode)
def resolve_instrument(self, info, **kwargs): def resolve_instrument(self, info, **kwargs):
slug = kwargs.get('slug') slug = kwargs.get('slug')
@ -67,3 +85,6 @@ class InstrumentQuery(object):
def resolve_instrument_types(self, info, **kwargs): def resolve_instrument_types(self, info, **kwargs):
return InstrumentType.objects.filter(instruments__isnull=False).order_by('name').distinct() return InstrumentType.objects.filter(instruments__isnull=False).order_by('name').distinct()
def resolve_instrument_categories(self, info, **kwargs):
return InstrumentCategory.objects.all()

View File

@ -6,7 +6,12 @@ query InstrumentTypesQuery {
instrumentTypes { instrumentTypes {
name name
type type
category category {
id
name
foreground
background
}
} }
} }
""" """

View File

@ -1,5 +1,5 @@
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .models import BasicKnowledge from .models import BasicKnowledge, InstrumentCategory, InstrumentType
class InstrumentAdmin(ModelAdmin): class InstrumentAdmin(ModelAdmin):
@ -9,4 +9,18 @@ class InstrumentAdmin(ModelAdmin):
search_fields = ('title', 'new_type__name') search_fields = ('title', 'new_type__name')
class InstrumentCategoryAdmin(ModelAdmin):
model = InstrumentCategory
list_display = ('name', 'background', 'foreground')
class InstrumentTypeAdmin(ModelAdmin):
model = InstrumentType
list_display = ('name', 'category',)
inspect_view_enabled = True
inspect_view_fields = ('name', 'category', 'instruments',)
modeladmin_register(InstrumentAdmin) modeladmin_register(InstrumentAdmin)
modeladmin_register(InstrumentCategoryAdmin)
modeladmin_register(InstrumentTypeAdmin)

View File

@ -9,7 +9,9 @@ from wagtail.core.models import Page, Site
from wagtail.core.rich_text import RichText from wagtail.core.rich_text import RichText
from assignments.models import Assignment from assignments.models import Assignment
from basicknowledge.models import BasicKnowledge, INTERDISCIPLINARY, InstrumentType, LANGUAGE_COMMUNICATION, SOCIETY from basicknowledge.models import BasicKnowledge, INTERDISCIPLINARY, INTERDISCIPLINARY_LABEL, InstrumentCategory, \
InstrumentType, \
LANGUAGE_COMMUNICATION, LANGUAGE_COMMUNICATION_LABEL, SOCIETY, SOCIETY_LABEL
from books.blocks import AssignmentBlock, BasicKnowledgeBlock, ImageUrlBlock, LinkBlock, VideoBlock from books.blocks import AssignmentBlock, BasicKnowledgeBlock, ImageUrlBlock, LinkBlock, VideoBlock
from books.models import Book, Chapter, ContentBlock, Module, TextBlock, Topic from books.models import Book, Chapter, ContentBlock, Module, TextBlock, Topic
from core.factories import BasePageFactory, DummyImageFactory, fake, fake_paragraph, fake_title from core.factories import BasePageFactory, DummyImageFactory, fake, fake_paragraph, fake_title
@ -70,11 +72,20 @@ class TextBlockFactory(wagtail_factories.StructBlockFactory):
model = TextBlock model = TextBlock
class InstrumentCategoryFactory(factory.DjangoModelFactory):
class Meta:
model = InstrumentCategory
django_get_or_create = ('name',)
name = factory.Iterator([LANGUAGE_COMMUNICATION_LABEL, SOCIETY_LABEL, INTERDISCIPLINARY_LABEL])
foreground = factory.Iterator(['FF0000', 'FFFFFF', '000000'])
background = factory.Iterator(['FF0000', 'FFFFFF', '000000'])
class InstrumentTypeFactory(factory.DjangoModelFactory): class InstrumentTypeFactory(factory.DjangoModelFactory):
class Meta: class Meta:
model = InstrumentType model = InstrumentType
category = factory.Iterator([LANGUAGE_COMMUNICATION, SOCIETY, INTERDISCIPLINARY]) category = factory.SubFactory(InstrumentCategoryFactory)
name = factory.LazyAttribute(lambda x: fake.text(max_nb_chars=20)) name = factory.LazyAttribute(lambda x: fake.text(max_nb_chars=20))

View File

@ -511,6 +511,14 @@ type InstrumentBookmarkNode implements Node {
instrument: InstrumentNode! instrument: InstrumentNode!
} }
type InstrumentCategoryNode {
id: ID!
name: String!
background: String!
foreground: String!
types: [InstrumentTypeNode]
}
type InstrumentNode implements Node { type InstrumentNode implements Node {
title: String! title: String!
slug: String! slug: String!
@ -531,16 +539,10 @@ type InstrumentNodeEdge {
cursor: String! cursor: String!
} }
enum InstrumentTypeCategory {
LANGUAGE_COMMUNICATION
SOCIETY
INTERDISCIPLINARY
}
type InstrumentTypeNode { type InstrumentTypeNode {
id: ID! id: ID!
name: String! name: String!
category: InstrumentTypeCategory! category: InstrumentCategoryNode
type: String! type: String!
} }
@ -874,6 +876,7 @@ type Query {
instrument(slug: String, id: ID): InstrumentNode instrument(slug: String, id: ID): InstrumentNode
instruments: [InstrumentNode] instruments: [InstrumentNode]
instrumentTypes: [InstrumentTypeNode] instrumentTypes: [InstrumentTypeNode]
instrumentCategories: [InstrumentCategoryNode]
studentSubmission(id: ID!): StudentSubmissionNode studentSubmission(id: ID!): StudentSubmissionNode
assignment(id: ID!): AssignmentNode assignment(id: ID!): AssignmentNode
assignments: [AssignmentNode] assignments: [AssignmentNode]