From 00d2527b2907e600a1ee9e8c5a31ff01c5e1b627 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 2 Dec 2022 11:10:12 +0100 Subject: [PATCH] Refactor urls for courses, learning paths and competence profile --- client/src/components/MainNavigationBar.vue | 25 +++++++++++++------ .../learningPath/LearningPathDiagramSmall.vue | 6 ++++- .../pages/competence/CompetenceParentPage.vue | 7 +++--- client/src/pages/learningPath/CirclePage.vue | 8 +++--- .../learningPath/LearningContentPage.vue | 8 +++--- .../pages/learningPath/LearningPathPage.vue | 6 ++--- .../pages/learningPath/SelfEvaluationPage.vue | 6 ++--- client/src/router/index.ts | 12 ++++----- client/src/stores/circle.ts | 11 ++++---- server/vbv_lernwelt/competence/models.py | 2 +- .../course/creators/test_course.py | 2 ++ .../creators/versicherungsvermittlerin.py | 2 ++ .../course/migrations/0006_course_slug.py | 18 +++++++++++++ server/vbv_lernwelt/course/models.py | 3 +++ server/vbv_lernwelt/course/serializers.py | 2 +- server/vbv_lernwelt/learnpath/models.py | 2 +- 16 files changed, 80 insertions(+), 40 deletions(-) create mode 100644 server/vbv_lernwelt/course/migrations/0006_course_slug.py diff --git a/client/src/components/MainNavigationBar.vue b/client/src/components/MainNavigationBar.vue index 72a80903..199f64fd 100644 --- a/client/src/components/MainNavigationBar.vue +++ b/client/src/components/MainNavigationBar.vue @@ -35,13 +35,22 @@ function toggleNav() { state.showMenu = !state.showMenu; } -function isInRoutePath(checkPaths: string[]) { - log.debug("isInRoutePath", checkPaths, route.path); - return checkPaths.some((path) => route.path.startsWith(path)); +function inCourse() { + return route.path.startsWith("/course/"); } -function inCourse() { - return isInRoutePath(["/learn", "/competence"]); +function inLearningPath() { + const regex = new RegExp("/course/[^/]+/learn"); + return regex.test(route.path); +} + +function inCompetenceProfile() { + const regex = new RegExp("/course/[^/]+/competence"); + return regex.test(route.path); +} + +function inMediaLibrary() { + return route.path.startsWith("/media/"); } function handleDropdownSelect(data: DropdownData) { @@ -149,7 +158,7 @@ const profileDropdownData: DropdownListItem[] = [ v-if="inCourse() && courseSessionsStore.courseSessionForRoute" :to="courseSessionsStore.courseSessionForRoute.learning_path_url" class="nav-item" - :class="{ 'nav-item--active': isInRoutePath(['/learn']) }" + :class="{ 'nav-item--active': inLearningPath() }" > {{ $t("general.learningPath") }} @@ -158,7 +167,7 @@ const profileDropdownData: DropdownListItem[] = [ v-if="inCourse() && courseSessionsStore.courseSessionForRoute" :to="courseSessionsStore.courseSessionForRoute.competence_url" class="nav-item" - :class="{ 'nav-item--active': isInRoutePath(['/competence']) }" + :class="{ 'nav-item--active': inCompetenceProfile() }" > KompetenzNavi @@ -173,7 +182,7 @@ const profileDropdownData: DropdownListItem[] = [ {{ $t("mediaLibrary.title") }} diff --git a/client/src/components/learningPath/LearningPathDiagramSmall.vue b/client/src/components/learningPath/LearningPathDiagramSmall.vue index 9680f8be..96eb5455 100644 --- a/client/src/components/learningPath/LearningPathDiagramSmall.vue +++ b/client/src/components/learningPath/LearningPathDiagramSmall.vue @@ -17,7 +17,11 @@ const learningPathData = ref(undefined); const learningPathStore = useLearningPathStore(); learningPathStore - .loadLearningPath(props.learningPathUrl.replace("/learn/", ""), false, false) + .loadLearningPath( + props.learningPathUrl.replace(/\/learn$/, "-lp").replace(/^\/course\//, ""), + false, + false + ) .then((data) => { learningPathData.value = data; }); diff --git a/client/src/pages/competence/CompetenceParentPage.vue b/client/src/pages/competence/CompetenceParentPage.vue index aea76a37..78051812 100644 --- a/client/src/pages/competence/CompetenceParentPage.vue +++ b/client/src/pages/competence/CompetenceParentPage.vue @@ -6,16 +6,17 @@ import { onMounted } from "vue"; log.debug("CompetenceParentPage created"); const props = defineProps<{ - competenceProfilePageSlug: string; + courseSlug: string; }>(); const competenceStore = useCompetenceStore(); onMounted(async () => { - log.debug("CompetencesView mounted", props.competenceProfilePageSlug); + log.debug("CompetencesView mounted", props.courseSlug); try { - await competenceStore.loadCompetenceProfilePage(props.competenceProfilePageSlug); + const competencePageSlug = props.courseSlug + "-competence"; + await competenceStore.loadCompetenceProfilePage(competencePageSlug); } catch (error) { log.error(error); } diff --git a/client/src/pages/learningPath/CirclePage.vue b/client/src/pages/learningPath/CirclePage.vue index e0f890f8..76a76900 100644 --- a/client/src/pages/learningPath/CirclePage.vue +++ b/client/src/pages/learningPath/CirclePage.vue @@ -16,7 +16,7 @@ const route = useRoute(); log.debug("CircleView.vue created", route); const props = defineProps<{ - learningPathSlug: string; + courseSlug: string; circleSlug: string; }>(); @@ -35,10 +35,10 @@ const duration = computed(() => { }); onMounted(async () => { - log.debug("CircleView.vue mounted", props.learningPathSlug, props.circleSlug); + log.debug("CircleView.vue mounted", props.courseSlug, props.circleSlug); try { - await circleStore.loadCircle(props.learningPathSlug, props.circleSlug); + await circleStore.loadCircle(props.courseSlug, props.circleSlug); if (route.hash.startsWith("#ls-") || route.hash.startsWith("#lu-")) { const slugEnd = route.hash.replace("#", ""); @@ -86,7 +86,7 @@ onMounted(async () => {
diff --git a/client/src/pages/learningPath/LearningContentPage.vue b/client/src/pages/learningPath/LearningContentPage.vue index e4b0fd7f..aa4d2289 100644 --- a/client/src/pages/learningPath/LearningContentPage.vue +++ b/client/src/pages/learningPath/LearningContentPage.vue @@ -9,7 +9,7 @@ import { onMounted, reactive, watch } from "vue"; log.debug("LearningContentView created"); const props = defineProps<{ - learningPathSlug: string; + courseSlug: string; circleSlug: string; contentSlug: string; }>(); @@ -24,7 +24,7 @@ const circleStore = useCircleStore(); const loadLearningContent = async () => { try { state.learningContent = await circleStore.loadLearningContent( - props.learningPathSlug, + props.courseSlug, props.circleSlug, props.contentSlug ); @@ -38,7 +38,7 @@ watch( async () => { log.debug( "LearningContentView props.contentSlug changed", - props.learningPathSlug, + props.courseSlug, props.circleSlug, props.contentSlug ); @@ -49,7 +49,7 @@ watch( onMounted(async () => { log.debug( "LearningContentView mounted", - props.learningPathSlug, + props.courseSlug, props.circleSlug, props.contentSlug ); diff --git a/client/src/pages/learningPath/LearningPathPage.vue b/client/src/pages/learningPath/LearningPathPage.vue index 6ad634fe..7d59555f 100644 --- a/client/src/pages/learningPath/LearningPathPage.vue +++ b/client/src/pages/learningPath/LearningPathPage.vue @@ -12,7 +12,7 @@ import type { LearningPath } from "@/services/learningPath"; log.debug("LearningPathView created"); const props = defineProps<{ - learningPathSlug: string; + courseSlug: string; }>(); const learningPathStore = useLearningPathStore(); @@ -22,7 +22,7 @@ onMounted(async () => { log.debug("LearningPathView mounted"); try { - await learningPathStore.loadLearningPath(props.learningPathSlug); + await learningPathStore.loadLearningPath(props.courseSlug + "-lp"); } catch (error) { log.error(error); } @@ -49,7 +49,7 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => { diff --git a/client/src/pages/learningPath/SelfEvaluationPage.vue b/client/src/pages/learningPath/SelfEvaluationPage.vue index f527f677..c2f41e36 100644 --- a/client/src/pages/learningPath/SelfEvaluationPage.vue +++ b/client/src/pages/learningPath/SelfEvaluationPage.vue @@ -11,7 +11,7 @@ import { onMounted, reactive } from "vue"; log.debug("LearningUnitSelfEvaluationView created"); const props = defineProps<{ - learningPathSlug: string; + courseSlug: string; circleSlug: string; learningUnitSlug: string; }>(); @@ -26,14 +26,14 @@ const state: { learningUnit?: LearningUnit } = reactive({}); onMounted(async () => { log.debug( "LearningUnitSelfEvaluationView mounted", - props.learningPathSlug, + props.courseSlug, props.circleSlug, props.learningUnitSlug ); try { state.learningUnit = await circleStore.loadSelfEvaluation( - props.learningPathSlug, + props.courseSlug, props.circleSlug, props.learningUnitSlug ); diff --git a/client/src/router/index.ts b/client/src/router/index.ts index a9ed7521..a61031c1 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -53,7 +53,7 @@ const router = createRouter({ ], }, { - path: "/competence/:competenceProfilePageSlug", + path: "/course/:courseSlug/competence", props: true, component: () => import("@/pages/competence/CompetenceParentPage.vue"), children: [ @@ -77,27 +77,27 @@ const router = createRouter({ ], }, { - path: "/learn/:learningPathSlug", + path: "/course/:courseSlug/learn", component: () => import("../pages/learningPath/LearningPathPage.vue"), props: true, }, { - path: "/learn/:learningPathSlug/:circleSlug", + path: "/course/:courseSlug/learn/:circleSlug", component: () => import("../pages/learningPath/CirclePage.vue"), props: true, }, { - path: "/learn/:learningPathSlug/:circleSlug/evaluate/:learningUnitSlug", + path: "/course/:courseSlug/learn/:circleSlug/evaluate/:learningUnitSlug", component: () => import("../pages/learningPath/SelfEvaluationPage.vue"), props: true, }, { - path: "/learn/:learningPathSlug/:circleSlug/:contentSlug", + path: "/course/:courseSlug/learn/:circleSlug/:contentSlug", component: () => import("../pages/learningPath/LearningContentPage.vue"), props: true, }, { - path: "/learn/:learningPathSlug/cockpit", + path: "/course/:courseSlug/cockpit", component: () => import("../pages/cockpit/CockpitPage.vue"), props: true, }, diff --git a/client/src/stores/circle.ts b/client/src/stores/circle.ts index 90436dfb..57d5a559 100644 --- a/client/src/stores/circle.ts +++ b/client/src/stores/circle.ts @@ -37,8 +37,9 @@ export const useCircleStore = defineStore({ }, getters: {}, actions: { - async loadCircle(learningPathSlug: string, circleSlug: string): Promise { + async loadCircle(courseSlug: string, circleSlug: string): Promise { this.circle = undefined; + const learningPathSlug = courseSlug + "-lp"; const learningPathStore = useLearningPathStore(); await learningPathStore.loadLearningPath(learningPathSlug); if (learningPathStore.learningPath) { @@ -54,11 +55,11 @@ export const useCircleStore = defineStore({ return this.circle; }, async loadLearningContent( - learningPathSlug: string, + courseSlug: string, circleSlug: string, learningContentSlug: string ) { - const circle = await this.loadCircle(learningPathSlug, circleSlug); + const circle = await this.loadCircle(courseSlug, circleSlug); const result = circle.flatLearningContents.find((learningContent) => { return learningContent.slug.endsWith(learningContentSlug); }); @@ -70,11 +71,11 @@ export const useCircleStore = defineStore({ return result; }, async loadSelfEvaluation( - learningPathSlug: string, + courseSlug: string, circleSlug: string, learningUnitSlug: string ) { - const circle = await this.loadCircle(learningPathSlug, circleSlug); + const circle = await this.loadCircle(courseSlug, circleSlug); const learningUnit = circle.flatLearningUnits.find((child) => { return child.slug.endsWith(learningUnitSlug); }); diff --git a/server/vbv_lernwelt/competence/models.py b/server/vbv_lernwelt/competence/models.py index 81abb841..b44304da 100644 --- a/server/vbv_lernwelt/competence/models.py +++ b/server/vbv_lernwelt/competence/models.py @@ -30,7 +30,7 @@ class CompetenceProfilePage(CourseBasePage): super(CompetenceProfilePage, self).full_clean(*args, **kwargs) def get_frontend_url(self): - return f"/competence/{self.slug}" + return f"/course/{self.get_parent().slug}/competence" class CompetencePage(CourseBasePage): diff --git a/server/vbv_lernwelt/course/creators/test_course.py b/server/vbv_lernwelt/course/creators/test_course.py index 2cd3c019..632e2821 100644 --- a/server/vbv_lernwelt/course/creators/test_course.py +++ b/server/vbv_lernwelt/course/creators/test_course.py @@ -80,6 +80,8 @@ def create_test_course_with_categories(apps=None, schema_editor=None): parent=site.root_page, course=course, ) + course.slug = course_page.slug + course.save() def create_test_learning_path(user=None, skip_locales=True): diff --git a/server/vbv_lernwelt/course/creators/versicherungsvermittlerin.py b/server/vbv_lernwelt/course/creators/versicherungsvermittlerin.py index 66fb771e..7855bdde 100644 --- a/server/vbv_lernwelt/course/creators/versicherungsvermittlerin.py +++ b/server/vbv_lernwelt/course/creators/versicherungsvermittlerin.py @@ -57,3 +57,5 @@ def create_versicherungsvermittlerin_with_categories( parent=site.root_page, course=course, ) + course.slug = course_page.slug + course.save() diff --git a/server/vbv_lernwelt/course/migrations/0006_course_slug.py b/server/vbv_lernwelt/course/migrations/0006_course_slug.py new file mode 100644 index 00000000..c5362f1a --- /dev/null +++ b/server/vbv_lernwelt/course/migrations/0006_course_slug.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.13 on 2022-12-02 09:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0005_alter_coursesessionuser_expert'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='slug', + field=models.SlugField(allow_unicode=True, blank=True, max_length=255, unique=True, verbose_name='Slug'), + ), + ] diff --git a/server/vbv_lernwelt/course/models.py b/server/vbv_lernwelt/course/models.py index 9109f257..b84fe36f 100644 --- a/server/vbv_lernwelt/course/models.py +++ b/server/vbv_lernwelt/course/models.py @@ -14,6 +14,9 @@ class Course(models.Model): category_name = models.CharField( _("Kategorie-Name"), max_length=255, default="Kategorie" ) + slug = models.SlugField( + _("Slug"), max_length=255, unique=True, blank=True, allow_unicode=True + ) class Meta: verbose_name = _("Lehrgang") diff --git a/server/vbv_lernwelt/course/serializers.py b/server/vbv_lernwelt/course/serializers.py index 7aec84cc..8306bc42 100644 --- a/server/vbv_lernwelt/course/serializers.py +++ b/server/vbv_lernwelt/course/serializers.py @@ -65,7 +65,7 @@ class CourseSessionSerializer(serializers.ModelSerializer): def get_experts(self, obj): expert_relations = CourseSessionUser.objects.filter( expert__in=Circle.objects.descendant_of(obj.course.coursepage) - ) + ).distinct() expert_result = [] for er in expert_relations: for circle in er.expert.all(): diff --git a/server/vbv_lernwelt/learnpath/models.py b/server/vbv_lernwelt/learnpath/models.py index 8a57f875..ac2db8c8 100644 --- a/server/vbv_lernwelt/learnpath/models.py +++ b/server/vbv_lernwelt/learnpath/models.py @@ -43,7 +43,7 @@ class LearningPath(CourseBasePage): return f"{self.title}" def get_frontend_url(self): - return f"/learn/{self.slug}" + return f"/course/{self.get_parent().slug}/learn" class Topic(CourseBasePage):