diff --git a/client/src/components/modules/ModuleFilter.vue b/client/src/components/modules/ModuleFilter.vue index 5f952422..731b143a 100644 --- a/client/src/components/modules/ModuleFilter.vue +++ b/client/src/components/modules/ModuleFilter.vue @@ -2,9 +2,9 @@
+ :defaultSelectedItem="initialLanguage" + class="module-filter__language-selection" + @update:selectedItem="item => selectedLanguage = item"/>
@@ -58,9 +58,9 @@ const selectedLanguage = ref(initialLanguage) - const {result: moduleCategoriesResult} = useQuery(gql` - query ModuleCategoriesQuery { - moduleCategories { + const {result: moduleLevelsResult} = useQuery(gql` + query ModuleLevelsQuery { + moduleLevels { name id } @@ -71,12 +71,12 @@ name: '---', id: null, }; - let defaultCategory = loadDefaultCategory(props.me); + let defaultLevel = loadDefaultLevel(props.me); - let selectedCategory = ref(defaultCategory); + let selectedLevel = ref(defaultLevel); - const firstLevelCategories = computed(() => { - return [nullCategory, ...moduleCategoriesResult.value?.moduleCategories || []]; + const levelOptions = computed(() => { + return [nullCategory, ...moduleLevelsResult.value?.moduleLevels || []]; }); const nullLernfeld = { @@ -104,8 +104,8 @@ return filterModules(); }); - function loadDefaultCategory(me) { - return me?.lastModuleCategory || nullCategory; + function loadDefaultLevel(me) { + return me?.lastModuleLevel || nullCategory; } @@ -113,14 +113,14 @@ let filteredModules = props.modules; - if (selectedCategory.value === null) { + if (selectedLevel.value === null) { return props.modules; } // filter by Lehrjahr - if (selectedCategory.value.name !== '---') { + if (selectedLevel.value.name !== '---') { filteredModules = filteredModules.filter((module) => { - return module.category?.id == selectedCategory.value.id; + return module.level?.id == selectedLevel.value.id; }); } @@ -130,8 +130,8 @@ return module.categoryType?.id == selectedLernfeld.value.id; }); } - updateLastModuleCategory(selectedCategory.value); - filteredModules = filterByLanguage(selectedLanguage.value, filteredModules); + updateLastModuleLevel(selectedLevel.value); + filteredModules = filterByLanguage(selectedLanguage.value, filteredModules); return filteredModules; } @@ -142,24 +142,23 @@ } - function updateLastModuleCategory(moduleCategory: Object) { - console.log(moduleCategory); + function updateLastModuleLevel(moduleLevel: Object) { - const {mutate: updateLastModuleCategory} = useMutation(gql` - mutation ($input: UpdateLastModuleCategoryInput!){updateLastModuleCategory(input: $input) { + const {mutate: updateLastModuleLevel} = useMutation(gql` + mutation ($input: UpdateLastModuleLevelInput!){updateLastModuleLevel(input: $input) { clientMutationId user { username - lastModuleCategory { + lastModuleLevel { name id } } }}`); - updateLastModuleCategory({ + updateLastModuleLevel({ input: { - id: moduleCategory.id, + id: moduleLevel.id, }, }); diff --git a/client/src/components/modules/ModuleTeaser.vue b/client/src/components/modules/ModuleTeaser.vue index 1943bf5c..57a459a2 100644 --- a/client/src/components/modules/ModuleTeaser.vue +++ b/client/src/components/modules/ModuleTeaser.vue @@ -18,7 +18,7 @@ {{ teaser }}

- +
@@ -30,7 +30,7 @@ export default { components: {Pill}, - props: ['metaTitle', 'title', 'teaser', 'id', 'slug', 'heroImage', 'category', 'categoryType'], + props: ['metaTitle', 'title', 'teaser', 'id', 'slug', 'heroImage', 'level', 'categoryType'], computed: { diff --git a/client/src/graphql/gql/fragments/moduleParts.gql b/client/src/graphql/gql/fragments/moduleParts.gql index eec8e9f3..9a03d012 100644 --- a/client/src/graphql/gql/fragments/moduleParts.gql +++ b/client/src/graphql/gql/fragments/moduleParts.gql @@ -9,7 +9,7 @@ fragment ModuleParts on ModuleNode { heroSource solutionsEnabled inEditMode @client - category { + level { id name } diff --git a/client/src/graphql/gql/fragments/userParts.gql b/client/src/graphql/gql/fragments/userParts.gql index d7631fb0..453aaf49 100644 --- a/client/src/graphql/gql/fragments/userParts.gql +++ b/client/src/graphql/gql/fragments/userParts.gql @@ -10,7 +10,7 @@ fragment UserParts on PrivateUserNode { avatarUrl expiryDate readOnly - lastModuleCategory { + lastModuleLevel { id name } diff --git a/client/src/mixins/me.ts b/client/src/mixins/me.ts index baa9353d..de2136ec 100644 --- a/client/src/mixins/me.ts +++ b/client/src/mixins/me.ts @@ -14,7 +14,7 @@ export interface Me { team: any; lastTopic: any; readOnly: boolean; - lastModuleCategory: any; + lastModuleLevel: any; } export interface MeQuery { @@ -45,7 +45,7 @@ const defaultMe: MeQuery = { team: null, readOnly: false, lastTopic: undefined, - lastModuleCategory: undefined, + lastModuleLevel: undefined, }, }; @@ -80,8 +80,8 @@ const getCurrentClassName = (me: Me) => { return currentClass ? currentClass.name : me.schoolClasses.length ? me.schoolClasses[0].name : ''; }; -const getLastModuleCategory = (me: Me) => { - return me.lastModuleCategory; +const getLastModuleLevel = (me: Me) => { + return me.lastModuleLevel; } const getMe = () => { @@ -134,9 +134,9 @@ const meMixin = { // @ts-ignore return getCurrentClassName(this.$data.me); }, - lastModuleCategory(): any { + lastModuleLevel(): any { // @ts-ignore - return getLastModuleCategory(this.$data.me); + return getLastModuleLevel(this.$data.me); } }, diff --git a/server/books/categorize_modules.py b/server/books/categorize_modules.py index d47de0a5..4cd8f329 100644 --- a/server/books/categorize_modules.py +++ b/server/books/categorize_modules.py @@ -1,4 +1,4 @@ -from .models.module import Module, ModuleCategory, ModuleType +from .models.module import Module, ModuleLevel, ModuleType def analyze_module_meta_titles(): all_nodes = [] @@ -19,14 +19,14 @@ def analyze_module_meta_titles(): def create_default_categories(): for lehrjahr in range(1,4): - module_category, created = ModuleCategory.objects.get_or_create(name=f"{lehrjahr}. Lehrjahr") + module_category, created = ModuleLevel.objects.get_or_create(name=f"{lehrjahr}. Lehrjahr") for type in range(1, 10): ModuleType.objects.get_or_create(name=f"Lernfeld {type}") def categorize_modules(): - for category in ModuleCategory.objects.all(): + for category in ModuleLevel.objects.all(): modules = Module.objects.filter(category__isnull=True, meta_title__icontains=category.name) print(f"{category.name}: {modules.count()}") modules.update(category=category) @@ -39,10 +39,10 @@ def categorize_modules(): def uncategorize_modules(): ModuleType.objects.all().delete() - ModuleCategory.objects.all().delete() + ModuleLevel.objects.all().delete() def delete_unused_categories(): - for category in ModuleCategory.objects.all(): + for category in ModuleLevel.objects.all(): if not category.module_set.exists(): category.delete() diff --git a/server/books/factories.py b/server/books/factories.py index db270cdc..5b89ffe7 100644 --- a/server/books/factories.py +++ b/server/books/factories.py @@ -28,7 +28,7 @@ from books.blocks import ( SurveyBlock, VideoBlock, ) -from books.models import Book, Chapter, ContentBlock, Module, TextBlock, Topic, ModuleCategory, ModuleType +from books.models import Book, Chapter, ContentBlock, Module, TextBlock, Topic, ModuleLevel, ModuleType from core.factories import ( BasePageFactory, DummyImageFactory, @@ -201,9 +201,9 @@ class VideoBlockFactory(wagtail_factories.StructBlockFactory): class Meta: model = VideoBlock -class ModuleCategoryFactory(factory.DjangoModelFactory): +class ModuleLevelFactory(factory.DjangoModelFactory): class Meta: - model = ModuleCategory + model = ModuleLevel name = '1. Lehrjahr' diff --git a/server/books/models/module.py b/server/books/models/module.py index 638034a1..e236bfee 100644 --- a/server/books/models/module.py +++ b/server/books/models/module.py @@ -9,19 +9,19 @@ from core.wagtail_utils import StrictHierarchyPage, get_default_settings from users.models import SchoolClass from django.utils.text import slugify -class ModuleCategory(models.Model): +class ModuleLevel(models.Model): name = models.CharField(max_length=255, unique=True) def __str__(self): return self.name class Meta: - verbose_name_plural = _("module categories") - verbose_name = _("module category") + verbose_name_plural = _("module Levels") + verbose_name = _("module level") def default_category(): - return ModuleCategory.objects.first().pk + return ModuleLevel.objects.first().pk class ModuleType(models.Model): @@ -42,7 +42,7 @@ class Module(StrictHierarchyPage): verbose_name_plural = "Module" meta_title = models.CharField(max_length=255, help_text="e.g. 'Intro' or 'Modul 1'") - category = models.ForeignKey(ModuleCategory, on_delete=models.SET_NULL, blank=True, null=True) + level = models.ForeignKey(ModuleLevel, on_delete=models.SET_NULL, blank=True, null=True) category_type = models.ForeignKey(ModuleType, on_delete=models.SET_NULL, blank=True, null=True) @@ -62,12 +62,10 @@ class Module(StrictHierarchyPage): solutions_enabled_for = models.ManyToManyField(SchoolClass) - # TODO: Filter category_type by category - content_panels = [ FieldPanel("title", classname="full title"), FieldPanel("meta_title", classname="full title"), - FieldPanel("category"), + FieldPanel("level"), FieldPanel("category_type"), FieldPanel("hero_image"), FieldPanel("hero_source"), @@ -161,12 +159,6 @@ class Module(StrictHierarchyPage): def get_admin_display_title(self): return f"{self.meta_title} - {self.title}" - @property - def category_name(self) -> str: - return self.category.name if self.category else "" - @property - def category_type_name(self) -> str: - return self.category_type.name if self.category_type else "" class RecentModule(models.Model): diff --git a/server/books/schema/mutations/__init__.py b/server/books/schema/mutations/__init__.py index 0dcc09c7..f25027d5 100644 --- a/server/books/schema/mutations/__init__.py +++ b/server/books/schema/mutations/__init__.py @@ -1,7 +1,7 @@ from books.schema.mutations.chapter import UpdateChapterVisibility from books.schema.mutations.contentblock import DuplicateContentBlock, MutateContentBlock, AddContentBlock, \ DeleteContentBlock -from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, SyncModuleVisibility, UpdateLastModuleCategory +from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, SyncModuleVisibility, UpdateLastModuleLevel from books.schema.mutations.snapshot import CreateSnapshot, ApplySnapshot, ShareSnapshot, UpdateSnapshot, DeleteSnapshot from books.schema.mutations.topic import UpdateLastTopic @@ -14,7 +14,7 @@ class BookMutations(object): update_solution_visibility = UpdateSolutionVisibility.Field() update_last_module = UpdateLastModule.Field() update_last_topic = UpdateLastTopic.Field() - update_last_module_category = UpdateLastModuleCategory.Field() + update_last_module_level = UpdateLastModuleLevel.Field() update_chapter_visibility = UpdateChapterVisibility.Field() sync_module_visibility = SyncModuleVisibility.Field() create_snapshot = CreateSnapshot.Field() diff --git a/server/books/schema/mutations/module.py b/server/books/schema/mutations/module.py index f551c97b..1df5bf00 100644 --- a/server/books/schema/mutations/module.py +++ b/server/books/schema/mutations/module.py @@ -3,7 +3,7 @@ from django.utils import timezone from graphene import relay from api.utils import get_object -from books.models import Module, RecentModule, ModuleCategory +from books.models import Module, RecentModule, ModuleLevel from books.schema.nodes import ModuleNode from users.models import SchoolClass, User from users.schema import PrivateUserNode @@ -105,7 +105,7 @@ class SyncModuleVisibility(relay.ClientIDMutation): return cls(success=True) -class UpdateLastModuleCategory(relay.ClientIDMutation): +class UpdateLastModuleLevel(relay.ClientIDMutation): class Input: id = graphene.ID() @@ -115,8 +115,8 @@ class UpdateLastModuleCategory(relay.ClientIDMutation): def mutate_and_get_payload(cls, root, info, **args): user = info.context.user id = args.get('id') - module_category = get_object(ModuleCategory, id) + module_level = get_object(ModuleLevel, id) - User.objects.filter(pk=user.id).update(last_module_category_id=module_category.id) + User.objects.filter(pk=user.id).update(last_module_level_id=module_level.id) return cls(user=user) diff --git a/server/books/schema/nodes/module.py b/server/books/schema/nodes/module.py index 66e89fe8..dbbd2fbd 100644 --- a/server/books/schema/nodes/module.py +++ b/server/books/schema/nodes/module.py @@ -6,10 +6,10 @@ from graphene_django.filter import DjangoFilterConnectionField from assignments.models import StudentSubmission from assignments.schema.types import AssignmentNode, StudentSubmissionNode -from books.models import Module, Chapter, ContentBlock, RecentModule, ModuleCategory, ModuleType +from books.models import Module, Chapter, ContentBlock, RecentModule, ModuleLevel, ModuleType from books.schema.interfaces.module import ModuleInterface from books.schema.nodes.chapter import ChapterNode -from books.schema.nodes.module_category import ModuleCategoryNode +from books.schema.nodes.module_category import ModuleLevelNode from books.schema.nodes.module_category_type import ModuleCategoryTypeNode from notes.models import ModuleBookmark, ContentBlockBookmark, ChapterBookmark from notes.schema import ( @@ -36,7 +36,7 @@ class ModuleNode(DjangoObjectType): "hero_image", "hero_source", "topic", - "category", + "level", "category_type", ] filter_fields = { @@ -56,7 +56,7 @@ class ModuleNode(DjangoObjectType): snapshots = graphene.List("books.schema.nodes.SnapshotNode") objective_groups = graphene.List(ObjectiveGroupNode) assignments = graphene.List(AssignmentNode) - category = graphene.Field(ModuleCategoryNode) + category = graphene.Field(ModuleLevelNode) category_type = graphene.Field(ModuleCategoryTypeNode) def resolve_chapters(self, info, **kwargs): @@ -111,7 +111,7 @@ class ModuleNode(DjangoObjectType): return parent.objective_groups.all().prefetch_related("hidden_for") def resolve_category(self, info, **kwargs): - return ModuleCategory.objects.get(pk=self.category_id) if self.category_id else None + return ModuleLevel.objects.get(pk=self.category_id) if self.category_id else None def resolve_category_type(self, info, **kwargs): return ModuleType.objects.get(pk=self.category_type_id) if self.category_type_id else None diff --git a/server/books/schema/nodes/module_category.py b/server/books/schema/nodes/module_category.py index add10b08..165d8e83 100644 --- a/server/books/schema/nodes/module_category.py +++ b/server/books/schema/nodes/module_category.py @@ -2,12 +2,12 @@ from graphene import relay from graphene import relay from graphene_django import DjangoObjectType -from books.models import ModuleCategory +from books.models import ModuleLevel -class ModuleCategoryNode(DjangoObjectType): +class ModuleLevelNode(DjangoObjectType): class Meta: - model = ModuleCategory + model = ModuleLevel interfaces = (relay.Node,) only_fields = [ "id", diff --git a/server/books/schema/queries.py b/server/books/schema/queries.py index 74e24d29..7793140b 100644 --- a/server/books/schema/queries.py +++ b/server/books/schema/queries.py @@ -7,9 +7,9 @@ from core.logger import get_logger from .connections import TopicConnection, ModuleConnection from .nodes import ContentBlockNode, ChapterNode, ModuleNode, NotFoundFailure, SnapshotNode, \ TopicOr404Node -from .nodes.module_category import ModuleCategoryNode +from .nodes.module_category import ModuleLevelNode from .nodes.module_category_type import ModuleCategoryTypeNode -from ..models import Book, Topic, Module, Chapter, Snapshot, ModuleCategory, ModuleType +from ..models import Book, Topic, Module, Chapter, Snapshot, ModuleLevel, ModuleType logger = get_logger(__name__) @@ -27,8 +27,8 @@ class BookQuery(object): modules = relay.ConnectionField(ModuleConnection) chapters = DjangoFilterConnectionField(ChapterNode) - module_category = graphene.Field(ModuleCategoryNode, id=graphene.ID(required=True)) - module_categories = graphene.List(ModuleCategoryNode) + module_level = graphene.Field(ModuleLevelNode, id=graphene.ID(required=True)) + module_levels = graphene.List(ModuleLevelNode) module_category_type= graphene.Field(ModuleCategoryTypeNode, id=graphene.ID(required=True)) module_category_types = graphene.List(ModuleCategoryTypeNode) @@ -80,18 +80,17 @@ class BookQuery(object): return None - def resolve_module_category(self, info, **kwargs): - id = kwargs.get('id') + def resolve_module_level(self, info, **kwargs): + module_level_id = kwargs.get('id') try: - if id is not None: - module_category = get_object(Module, id) - return module_category + if module_level_id is not None: + return get_object(Module, module_level_id) except Module.DoesNotExist: return None - def resolve_module_categories(self, *args, **kwargs): - return ModuleCategory.objects.filter(**kwargs) + def resolve_module_levels(self, *args, **kwargs): + return ModuleLevel.objects.filter(**kwargs) def resolve_module_category_type(self, info, **kwargs): id = kwargs.get('id') diff --git a/server/books/tests/test_module_mutations.py b/server/books/tests/test_module_mutations.py index 68faf7aa..08f32b22 100644 --- a/server/books/tests/test_module_mutations.py +++ b/server/books/tests/test_module_mutations.py @@ -5,7 +5,7 @@ from graphql_relay import to_global_id from api.schema import schema from api.utils import get_object from books.models import ContentBlock, Chapter -from books.factories import ModuleFactory, ModuleCategoryFactory +from books.factories import ModuleFactory, ModuleLevelFactory from core.factories import UserFactory from users.models import User @@ -122,26 +122,26 @@ class NewContentBlockMutationTest(TestCase): self.assertEqual(content.get('type'), 'image_url_block') self.assertEqual(content.get('value'), {'url': '/test.png'}) - def test_updateLastModuleCategory(self): + def test_updateLastModuleLevel(self): self.assertIsNone(self.user.last_module_category, None) - moduleCategory = ModuleCategoryFactory(name='1. Lehrjahr') - moduleCategory1 = ModuleCategoryFactory(name='2. Lehrjahr') + moduleLevel= ModuleLevelFactory(name='1. Lehrjahr') + moduleLevel1 = ModuleLevelFactory(name='2. Lehrjahr') mutation = """ - mutation ($input: UpdateLastModuleCategoryInput!){updateLastModuleCategory(input: $input) { + mutation ($input: UpdateLastModuleLevelInput!){updateLastModuleLevel(input: $input) { clientMutationId user { username - lastModuleCategory { + lastModuleLevel { name id } } }} """ - result = self.client.execute(mutation, variables={"input": {"id": moduleCategory1.id}}) + result = self.client.execute(mutation, variables={"input": {"id": moduleLevel1.id}}) self.assertIsNone(result.get('errors')) updated_user = User.objects.get(id=self.user.id) - self.assertEqual(updated_user.last_module_category.name, moduleCategory1.name) + self.assertEqual(updated_user.last_module_category.name, moduleLevel1.name) diff --git a/server/books/wagtail_hooks.py b/server/books/wagtail_hooks.py index 75c3d9d3..570e006c 100644 --- a/server/books/wagtail_hooks.py +++ b/server/books/wagtail_hooks.py @@ -5,19 +5,19 @@ from wagtail.contrib.modeladmin.options import ( ) from wagtail import hooks -from .models.module import ModuleCategory, ModuleType, Module +from .models.module import ModuleLevel, ModuleType, Module from django.utils.translation import gettext_lazy as _ class ModuleAdmin(ModelAdmin): model = Module - list_display = ("title", "meta_title", "category", "category_type") + list_display = ("title", "meta_title", "level", "category_type") search_fields = ("title", "meta_title") - list_filter = ("category", "category_type") + list_filter = ("level", "category_type") -class ModuleCategoryAdmin(ModelAdmin): - model = ModuleCategory +class ModuleLevelAdmin(ModelAdmin): + model = ModuleLevel list_display = ("name",) ordering = ("name",) @@ -33,7 +33,7 @@ class InstrumentGroup(ModelAdminGroup): menu_label = _("Modules") items = ( ModuleAdmin, - ModuleCategoryAdmin, + ModuleLevelAdmin, ModuleTypeAdmin, ) diff --git a/server/users/migrations/0034_user_last_module_level.py b/server/users/migrations/0034_user_last_module_level.py new file mode 100644 index 00000000..06f30efb --- /dev/null +++ b/server/users/migrations/0034_user_last_module_level.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.16 on 2023-08-21 12:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('books', '0048_auto_20230821_0922'), + ('users', '0033_alter_license_isbn'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='last_module_level', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='books.modulelevel'), + ), + ] diff --git a/server/users/models.py b/server/users/models.py index 77208ca1..5fc79d38 100644 --- a/server/users/models.py +++ b/server/users/models.py @@ -33,7 +33,7 @@ class User(AbstractUser): last_module = models.ForeignKey('books.Module', related_name='+', on_delete=models.SET_NULL, null=True) recent_modules = models.ManyToManyField('books.Module', related_name='+', through='books.RecentModule') - last_module_category = models.ForeignKey('books.ModuleCategory', related_name='+', on_delete=models.SET_NULL, null=True) + last_module_level = models.ForeignKey('books.ModuleLevel', related_name='+', on_delete=models.SET_NULL, null=True) last_topic = models.ForeignKey('books.Topic', related_name='+', on_delete=models.SET_NULL, null=True) avatar_url = models.CharField(max_length=254, blank=True, default='') email = models.EmailField(_('email address'), unique=True) diff --git a/server/users/schema/types.py b/server/users/schema/types.py index afebcfd4..031330f6 100644 --- a/server/users/schema/types.py +++ b/server/users/schema/types.py @@ -11,7 +11,7 @@ from graphql_relay import to_global_id from api.types import FailureNode from books.models import Module -from books.schema.nodes import ModuleCategoryNode +from books.schema.nodes import ModuleLevelNode from books.schema.queries import ModuleNode from users.models import SchoolClass, SchoolClassMember, Team, User @@ -104,7 +104,7 @@ class PrivateUserNode(DjangoObjectType): "onboarding_visited", "team", "read_only", - "last_module_category" + "last_module_level" ] interfaces = (relay.Node,)