Add courseSessionId query param to url and switch to it
This commit is contained in:
parent
8b4b00170a
commit
6048129507
|
|
@ -3,10 +3,12 @@ import type { CourseSession, DueDate } from "@/types";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
import { useTranslation } from "i18next-vue";
|
import { useTranslation } from "i18next-vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
dueDate: DueDate;
|
dueDate: DueDate;
|
||||||
singleLine?: boolean;
|
singleLine?: boolean;
|
||||||
|
showCourseSession?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -24,6 +26,16 @@ if (!courseSession) {
|
||||||
|
|
||||||
const isExpert = courseSessionsStore.hasCockpit(courseSession);
|
const isExpert = courseSessionsStore.hasCockpit(courseSession);
|
||||||
const url = isExpert ? props.dueDate.url_expert : props.dueDate.url;
|
const url = isExpert ? props.dueDate.url_expert : props.dueDate.url;
|
||||||
|
|
||||||
|
const courseSessionTitle = computed(() => {
|
||||||
|
if (props.dueDate.course_session) {
|
||||||
|
return (
|
||||||
|
courseSessionsStore.getCourseSessionById(props.dueDate.course_session)?.title ??
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -54,7 +66,12 @@ const url = isExpert ? props.dueDate.url_expert : props.dueDate.url;
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-small text-gray-900">
|
<div class="text-small text-gray-900">
|
||||||
<div>{{ $t("a.Circle") }} «{{ props.dueDate.circle?.title }}»</div>
|
<div>
|
||||||
|
<span v-if="props.showCourseSession ?? courseSessionTitle">
|
||||||
|
{{ courseSessionTitle }}:
|
||||||
|
</span>
|
||||||
|
{{ $t("a.Circle") }} «{{ props.dueDate.circle?.title }}»
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ const props = defineProps<{
|
||||||
showTopBorder: boolean;
|
showTopBorder: boolean;
|
||||||
showBottomBorder: boolean;
|
showBottomBorder: boolean;
|
||||||
showAllDueDatesLink: boolean;
|
showAllDueDatesLink: boolean;
|
||||||
|
showCourseSession: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const allDueDates = computed(() => {
|
const allDueDates = computed(() => {
|
||||||
|
|
@ -28,7 +29,10 @@ const dueDatesDisplayed = computed(() => {
|
||||||
:key="dueDate.id"
|
:key="dueDate.id"
|
||||||
:class="{ 'first:border-t': props.showTopBorder, 'border-b': true }"
|
:class="{ 'first:border-t': props.showTopBorder, 'border-b': true }"
|
||||||
>
|
>
|
||||||
<DueDateSingle :due-date="dueDate"></DueDateSingle>
|
<DueDateSingle
|
||||||
|
:due-date="dueDate"
|
||||||
|
:show-course-session="props.showCourseSession"
|
||||||
|
></DueDateSingle>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div v-if="allDueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
<div v-if="allDueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
:show-top-border="props.showTopBorder"
|
:show-top-border="props.showTopBorder"
|
||||||
show-all-due-dates-link
|
show-all-due-dates-link
|
||||||
show-bottom-border
|
show-bottom-border
|
||||||
|
:show-course-session="false"
|
||||||
></DueDatesList>
|
></DueDatesList>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const logout = () => {
|
||||||
userStore.handleLogout();
|
userStore.handleLogout();
|
||||||
};
|
};
|
||||||
const selectCourseSession = (courseSession: CourseSession) => {
|
const selectCourseSession = (courseSession: CourseSession) => {
|
||||||
courseSessionsStore.switchCourseSession(courseSession);
|
courseSessionsStore.switchCourseSessionById(courseSession.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ async function loadAdditionalAppointments() {
|
||||||
:show-all-due-dates-link="false"
|
:show-all-due-dates-link="false"
|
||||||
:max-count="numAppointmentsToShow"
|
:max-count="numAppointmentsToShow"
|
||||||
data-cy="appointments-list"
|
data-cy="appointments-list"
|
||||||
|
:show-course-session="true"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="canLoadMore"
|
v-if="canLoadMore"
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ const getNextStepLink = (courseSession: CourseSession) => {
|
||||||
:show-top-border="false"
|
:show-top-border="false"
|
||||||
:show-all-due-dates-link="true"
|
:show-all-due-dates-link="true"
|
||||||
:show-bottom-border="true"
|
:show-bottom-border="true"
|
||||||
|
:show-course-session="true"
|
||||||
></DueDatesList>
|
></DueDatesList>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ export const redirectToLoginIfRequired: NavigationGuard = (to) => {
|
||||||
if (loginRequired(to) && !userStore.loggedIn) {
|
if (loginRequired(to) && !userStore.loggedIn) {
|
||||||
const appEnv = import.meta.env.VITE_APP_ENVIRONMENT || "local";
|
const appEnv = import.meta.env.VITE_APP_ENVIRONMENT || "local";
|
||||||
const ssoLogin = appEnv.startsWith("prod") || appEnv.startsWith("stage");
|
const ssoLogin = appEnv.startsWith("prod") || appEnv.startsWith("stage");
|
||||||
return ssoLogin ? `/login?next=${to.fullPath}` : `/login-local?next=${to.fullPath}`;
|
return ssoLogin
|
||||||
|
? `/login?next=${encodeURIComponent(to.fullPath)}`
|
||||||
|
: `/login-local?next=${encodeURIComponent(to.fullPath)}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -46,15 +48,52 @@ export const expertRequired: NavigationGuard = (to: RouteLocationNormalized) =>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function handleCourseSessions(to: RouteLocationNormalized) {
|
export async function handleCurrentCourseSession(to: RouteLocationNormalized) {
|
||||||
// register after login hooks
|
// register after login hooks
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
const userStore = useUserStore();
|
||||||
if (to.params.courseSlug) {
|
if (userStore.loggedIn) {
|
||||||
courseSessionsStore._currentCourseSlug = to.params.courseSlug as string;
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
} else {
|
if (to.params.courseSlug) {
|
||||||
courseSessionsStore._currentCourseSlug = "";
|
courseSessionsStore._currentCourseSlug = to.params.courseSlug as string;
|
||||||
}
|
} else {
|
||||||
if (!courseSessionsStore.loaded) {
|
courseSessionsStore._currentCourseSlug = "";
|
||||||
await courseSessionsStore.loadCourseSessionsData();
|
}
|
||||||
|
if (!courseSessionsStore.loaded) {
|
||||||
|
await courseSessionsStore.loadCourseSessionsData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalized) {
|
||||||
|
/**
|
||||||
|
* switch to course session with id from query param `courseSessionId` if it
|
||||||
|
* is present and valid.
|
||||||
|
*/
|
||||||
|
// register after login hooks
|
||||||
|
const userStore = useUserStore();
|
||||||
|
if (userStore.loggedIn) {
|
||||||
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
|
if (!courseSessionsStore.loaded) {
|
||||||
|
await courseSessionsStore.loadCourseSessionsData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to.query.courseSessionId) {
|
||||||
|
const { courseSessionId, ...restOfQuery } = to.query;
|
||||||
|
const switchSuccessful = courseSessionsStore.switchCourseSessionById(
|
||||||
|
courseSessionId.toString()
|
||||||
|
);
|
||||||
|
if (switchSuccessful) {
|
||||||
|
return {
|
||||||
|
path: to.path,
|
||||||
|
query: restOfQuery,
|
||||||
|
replace: true,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// courseSessionId is invalid for current user -> redirect to home
|
||||||
|
return {
|
||||||
|
path: "/",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import DashboardPage from "@/pages/DashboardPage.vue";
|
import DashboardPage from "@/pages/DashboardPage.vue";
|
||||||
import LoginPage from "@/pages/LoginPage.vue";
|
import LoginPage from "@/pages/LoginPage.vue";
|
||||||
import {
|
import {
|
||||||
handleCourseSessions,
|
handleCourseSessionAsQueryParam,
|
||||||
|
handleCurrentCourseSession,
|
||||||
redirectToLoginIfRequired,
|
redirectToLoginIfRequired,
|
||||||
updateLoggedIn,
|
updateLoggedIn,
|
||||||
} from "@/router/guards";
|
} from "@/router/guards";
|
||||||
|
|
@ -214,7 +215,8 @@ router.beforeEach(updateLoggedIn);
|
||||||
router.beforeEach(redirectToLoginIfRequired);
|
router.beforeEach(redirectToLoginIfRequired);
|
||||||
|
|
||||||
// register after login hooks
|
// register after login hooks
|
||||||
router.beforeEach(handleCourseSessions);
|
router.beforeEach(handleCurrentCourseSession);
|
||||||
|
router.beforeEach(handleCourseSessionAsQueryParam);
|
||||||
|
|
||||||
router.beforeEach(addToHistory);
|
router.beforeEach(addToHistory);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,13 +91,31 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchCourseSession(courseSession: CourseSession) {
|
function _switchCourseSession(courseSession: CourseSession) {
|
||||||
log.debug("switchCourseSession", courseSession);
|
log.debug("switchCourseSession", courseSession);
|
||||||
selectedCourseSessionMap.value.set(courseSession.course.slug, courseSession.id);
|
selectedCourseSessionMap.value.set(courseSession.course.slug, courseSession.id);
|
||||||
// Emit event so that the App can re-render with the new courseSession
|
// Emit event so that the App can re-render with the new courseSession
|
||||||
eventBus.emit("switchedCourseSession", courseSession.id);
|
eventBus.emit("switchedCourseSession", courseSession.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCourseSessionById(courseSessionId: number | string) {
|
||||||
|
return allCourseSessions.value.find((cs) => {
|
||||||
|
return courseSessionId.toString() === cs.id.toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchCourseSessionById(courseSessionId: number | string) {
|
||||||
|
const courseSession = allCourseSessions.value.find((cs) => {
|
||||||
|
return courseSessionId.toString() === cs.id.toString();
|
||||||
|
});
|
||||||
|
if (courseSession) {
|
||||||
|
_switchCourseSession(courseSession);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function courseSessionForCourse(courseSlug: string) {
|
function courseSessionForCourse(courseSlug: string) {
|
||||||
if (courseSlug) {
|
if (courseSlug) {
|
||||||
const courseSession = selectedCourseSessionForCourse(courseSlug);
|
const courseSession = selectedCourseSessionForCourse(courseSlug);
|
||||||
|
|
@ -268,7 +286,8 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
||||||
uniqueCourseSessionsByCourse,
|
uniqueCourseSessionsByCourse,
|
||||||
allCurrentCourseSessions,
|
allCurrentCourseSessions,
|
||||||
courseSessionForCourse,
|
courseSessionForCourse,
|
||||||
switchCourseSession,
|
getCourseSessionById,
|
||||||
|
switchCourseSessionById,
|
||||||
hasCockpit,
|
hasCockpit,
|
||||||
hasCourseSessionPreview,
|
hasCourseSessionPreview,
|
||||||
currentCourseSessionHasCockpit,
|
currentCourseSessionHasCockpit,
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,9 @@ class CourseSessionUserAdmin(admin.ModelAdmin):
|
||||||
"user_first_name",
|
"user_first_name",
|
||||||
"course_session",
|
"course_session",
|
||||||
"role",
|
"role",
|
||||||
"created_at",
|
"circles",
|
||||||
"updated_at",
|
# "created_at",
|
||||||
|
# "updated_at",
|
||||||
]
|
]
|
||||||
search_fields = [
|
search_fields = [
|
||||||
"user__first_name",
|
"user__first_name",
|
||||||
|
|
@ -66,6 +67,9 @@ class CourseSessionUserAdmin(admin.ModelAdmin):
|
||||||
user_last_name.short_description = "Last Name"
|
user_last_name.short_description = "Last Name"
|
||||||
user_last_name.admin_order_field = "user__last_name"
|
user_last_name.admin_order_field = "user__last_name"
|
||||||
|
|
||||||
|
def circles(self, obj):
|
||||||
|
return ", ".join([c.title for c in obj.expert.all()])
|
||||||
|
|
||||||
fieldsets = [
|
fieldsets = [
|
||||||
(None, {"fields": ("user", "course_session", "role")}),
|
(None, {"fields": ("user", "course_session", "role")}),
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,10 @@ class CourseSessionAttendanceCourse(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.due_date.manual_override_fields:
|
if not self.due_date.manual_override_fields:
|
||||||
self.due_date.url = self.learning_content.get_frontend_url()
|
self.due_date.url = self.learning_content.get_frontend_url(
|
||||||
self.due_date.url_expert = f"/course/{self.due_date.course_session.course.slug}/cockpit/attendance?id={self.learning_content_id}"
|
course_session_id=self.course_session.id
|
||||||
|
)
|
||||||
|
self.due_date.url_expert = f"/course/{self.due_date.course_session.course.slug}/cockpit/attendance?id={self.learning_content_id}&courseSessionId={self.course_session.id}"
|
||||||
self.due_date.title = self.learning_content.title
|
self.due_date.title = self.learning_content.title
|
||||||
self.due_date.page = self.learning_content.page_ptr
|
self.due_date.page = self.learning_content.page_ptr
|
||||||
self.due_date.assignment_type_translation_key = (
|
self.due_date.assignment_type_translation_key = (
|
||||||
|
|
@ -123,7 +125,9 @@ class CourseSessionAssignment(models.Model):
|
||||||
if self.learning_content_id:
|
if self.learning_content_id:
|
||||||
title = self.learning_content.title
|
title = self.learning_content.title
|
||||||
page = self.learning_content.page_ptr
|
page = self.learning_content.page_ptr
|
||||||
url = self.learning_content.get_frontend_url()
|
url = self.learning_content.get_frontend_url(
|
||||||
|
course_session_id=self.course_session.id
|
||||||
|
)
|
||||||
assignment_type = self.learning_content.assignment_type
|
assignment_type = self.learning_content.assignment_type
|
||||||
assignment_type_translation_keys = {
|
assignment_type_translation_keys = {
|
||||||
AssignmentType.CASEWORK.value: "learningContentTypes.casework",
|
AssignmentType.CASEWORK.value: "learningContentTypes.casework",
|
||||||
|
|
@ -131,7 +135,7 @@ class CourseSessionAssignment(models.Model):
|
||||||
AssignmentType.REFLECTION.value: "learningContentTypes.reflection",
|
AssignmentType.REFLECTION.value: "learningContentTypes.reflection",
|
||||||
}
|
}
|
||||||
|
|
||||||
url_expert = f"/course/{self.course_session.course.slug}/cockpit/assignment/{self.learning_content_id}"
|
url_expert = f"/course/{self.course_session.course.slug}/cockpit/assignment/{self.learning_content_id}?courseSessionId={self.course_session.id}"
|
||||||
|
|
||||||
if assignment_type in (
|
if assignment_type in (
|
||||||
AssignmentType.CASEWORK.value,
|
AssignmentType.CASEWORK.value,
|
||||||
|
|
@ -212,10 +216,10 @@ class CourseSessionEdoniqTest(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.deadline.manual_override_fields:
|
if not self.deadline.manual_override_fields:
|
||||||
self.deadline.url = self.learning_content.get_frontend_url()
|
self.deadline.url = self.learning_content.get_frontend_url(
|
||||||
self.deadline.url_expert = (
|
course_session_id=self.course_session.id
|
||||||
f"/course/{self.course_session.course.slug}/cockpit/"
|
|
||||||
)
|
)
|
||||||
|
self.deadline.url_expert = f"/course/{self.course_session.course.slug}/cockpit?courseSessionId={self.course_session.id}"
|
||||||
self.deadline.title = self.learning_content.title
|
self.deadline.title = self.learning_content.title
|
||||||
self.deadline.page = self.learning_content.page_ptr
|
self.deadline.page = self.learning_content.page_ptr
|
||||||
self.deadline.assignment_type_translation_key = (
|
self.deadline.assignment_type_translation_key = (
|
||||||
|
|
|
||||||
|
|
@ -4,56 +4,30 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
def set_url_expert_course_session_assignments(apps):
|
def set_url_expert_course_session_assignments(apps):
|
||||||
Course = apps.get_model("course", "Course") # noqa
|
# need to load concrete model, so that wagtail page has `specific` instance method...
|
||||||
|
from vbv_lernwelt.course_session.models import CourseSessionAssignment
|
||||||
CourseSessionAssignment = apps.get_model( # noqa
|
|
||||||
"course_session", "CourseSessionAssignment"
|
|
||||||
)
|
|
||||||
|
|
||||||
for assignment in CourseSessionAssignment.objects.all():
|
for assignment in CourseSessionAssignment.objects.all():
|
||||||
for due_date in [
|
# trigger save to update due_date foreign key fields
|
||||||
assignment.submission_deadline,
|
assignment.save()
|
||||||
assignment.evaluation_deadline,
|
|
||||||
]:
|
|
||||||
if due_date and due_date.page:
|
|
||||||
course_slug = due_date.course_session.course.slug
|
|
||||||
content_id = due_date.page.id
|
|
||||||
due_date.url_expert = (
|
|
||||||
f"/course/{course_slug}/cockpit/assignment/{content_id}"
|
|
||||||
)
|
|
||||||
due_date.save()
|
|
||||||
|
|
||||||
|
|
||||||
def set_url_expert_course_session_edoniq_test(apps):
|
def set_url_expert_course_session_edoniq_test(apps):
|
||||||
CourseSessionEdoniqTest = apps.get_model( # noqa
|
# need to load concrete model, so that wagtail page has `specific` instance method...
|
||||||
"course_session", "CourseSessionEdoniqTest"
|
from vbv_lernwelt.course_session.models import CourseSessionEdoniqTest
|
||||||
)
|
|
||||||
|
|
||||||
for edoniq_test in CourseSessionEdoniqTest.objects.all():
|
for edoniq_test in CourseSessionEdoniqTest.objects.all():
|
||||||
due_date = edoniq_test.deadline
|
# trigger save to update due_date foreign key fields
|
||||||
|
edoniq_test.save()
|
||||||
if due_date:
|
|
||||||
course_slug = due_date.course_session.course.slug
|
|
||||||
due_date.url_expert = f"/course/{course_slug}/cockpit/"
|
|
||||||
due_date.save()
|
|
||||||
|
|
||||||
|
|
||||||
def set_url_expert_course_session_attendances(apps):
|
def set_url_expert_course_session_attendances(apps):
|
||||||
Course = apps.get_model("course", "Course") # noqa
|
# need to load concrete model, so that wagtail page has `specific` instance method...
|
||||||
|
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
||||||
CourseSessionAttendanceCourse = apps.get_model( # noqa
|
|
||||||
"course_session", "CourseSessionAttendanceCourse"
|
|
||||||
)
|
|
||||||
|
|
||||||
for attendance in CourseSessionAttendanceCourse.objects.all():
|
for attendance in CourseSessionAttendanceCourse.objects.all():
|
||||||
due_date = attendance.due_date
|
# trigger save to update due_date foreign key fields
|
||||||
if due_date and due_date.page:
|
attendance.save()
|
||||||
course_slug = due_date.course_session.course.slug
|
|
||||||
content_id = due_date.page.id
|
|
||||||
due_date.url_expert = (
|
|
||||||
f"/course/{course_slug}/cockpit/attendance?id={content_id}"
|
|
||||||
)
|
|
||||||
due_date.save()
|
|
||||||
|
|
||||||
|
|
||||||
def set_url_expert_default(apps, schema_editor):
|
def set_url_expert_default(apps, schema_editor):
|
||||||
|
|
|
||||||
|
|
@ -268,14 +268,19 @@ class LearningContent(CourseBasePage):
|
||||||
<span style="margin-left: 8px;">{self.get_admin_display_title()}</span>
|
<span style="margin-left: 8px;">{self.get_admin_display_title()}</span>
|
||||||
</span>"""
|
</span>"""
|
||||||
|
|
||||||
def get_frontend_url(self):
|
def get_frontend_url(self, course_session_id=None):
|
||||||
r = re.compile(
|
r = re.compile(
|
||||||
r"^(?P<coursePart>.+?)-lp-circle-(?P<circlePart>.+?)-lc-(?P<lcPart>.+)$"
|
r"^(?P<coursePart>.+?)-lp-circle-(?P<circlePart>.+?)-lc-(?P<lcPart>.+)$"
|
||||||
)
|
)
|
||||||
m = r.match(self.slug)
|
m = r.match(self.slug)
|
||||||
if m is None:
|
if m is None:
|
||||||
return "ERROR: could not parse slug"
|
return "ERROR: could not parse slug"
|
||||||
return f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}/{m.group('lcPart')}"
|
url = f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}/{m.group('lcPart')}"
|
||||||
|
|
||||||
|
if course_session_id:
|
||||||
|
url += f"?courseSessionId={course_session_id}"
|
||||||
|
|
||||||
|
return url
|
||||||
|
|
||||||
def get_parent_circle(self):
|
def get_parent_circle(self):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue