chore: introduce course configuration "feature" flags in client
This commit is contained in:
parent
51609591e1
commit
e4329194ee
|
|
@ -23,7 +23,6 @@ import {
|
|||
getMediaCenterUrl,
|
||||
} from "@/utils/utils";
|
||||
import { useCockpitStore } from "@/stores/cockpit";
|
||||
import { VV_COURSE_IDS } from "@/constants";
|
||||
|
||||
log.debug("MainNavigationBar created");
|
||||
|
||||
|
|
@ -100,13 +99,13 @@ const hasNotificationsMenu = computed(() => {
|
|||
});
|
||||
|
||||
const hasMentorManagementMenu = computed(() => {
|
||||
if (courseSessionsStore.currentCourseSessionHasCockpit) {
|
||||
if (courseSessionsStore.currentCourseSessionHasCockpit || !inCourse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// learning mentor management is only available for VV courses
|
||||
const currentCourseId = courseSessionsStore.currentCourseSession?.course.id || "";
|
||||
return inCourse() && VV_COURSE_IDS.includes(currentCourseId);
|
||||
return (
|
||||
courseSessionsStore.currentCourseSession?.course.configuration
|
||||
.enable_learning_mentor ?? false
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,3 @@ export const itCheckboxDefaultIconCheckedTailwindClass =
|
|||
|
||||
export const itCheckboxDefaultIconUncheckedTailwindClass =
|
||||
"bg-[url(/static/icons/icon-checkbox-unchecked.svg)] hover:bg-[url(/static/icons/icon-checkbox-unchecked-hover.svg)]";
|
||||
|
||||
export const VV_COURSE_IDS = [
|
||||
"-4", // vv-de
|
||||
"-10", // vv-fr
|
||||
"-11", // vv-it
|
||||
];
|
||||
|
|
|
|||
|
|
@ -455,6 +455,7 @@ export type CourseStatisticsType = {
|
|||
|
||||
export type DashboardConfigType = {
|
||||
__typename?: 'DashboardConfigType';
|
||||
course_configuration: CourseConfigurationObjectType;
|
||||
dashboard_type: DashboardType;
|
||||
id: Scalars['ID']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ type DashboardConfigType {
|
|||
name: String!
|
||||
slug: String!
|
||||
dashboard_type: DashboardType!
|
||||
course_configuration: CourseConfigurationObjectType!
|
||||
}
|
||||
|
||||
enum DashboardType {
|
||||
|
|
@ -208,6 +209,12 @@ enum DashboardType {
|
|||
SIMPLE_DASHBOARD
|
||||
}
|
||||
|
||||
type CourseConfigurationObjectType {
|
||||
enable_circle_documents: Boolean!
|
||||
enable_learning_mentor: Boolean!
|
||||
enable_competence_certificates: Boolean!
|
||||
}
|
||||
|
||||
type LearningPathObjectType implements CoursePageInterface {
|
||||
id: ID!
|
||||
title: String!
|
||||
|
|
@ -241,12 +248,6 @@ type CourseObjectType {
|
|||
action_competences: [ActionCompetenceObjectType!]!
|
||||
}
|
||||
|
||||
type CourseConfigurationObjectType {
|
||||
enable_circle_documents: Boolean!
|
||||
enable_learning_mentor: Boolean!
|
||||
enable_competence_certificates: Boolean!
|
||||
}
|
||||
|
||||
type ActionCompetenceObjectType implements CoursePageInterface {
|
||||
competence_id: String!
|
||||
id: ID!
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {
|
|||
} from "@/pages/competence/utils";
|
||||
import { useSelfEvaluationFeedbackSummaries } from "@/services/selfEvaluationFeedback";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import { VV_COURSE_IDS } from "@/constants";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
|
|
@ -67,12 +66,7 @@ const isFeedbackEvaluationVisible = computed(
|
|||
false
|
||||
);
|
||||
|
||||
// FIXME 22.02.24: To-be-tackled NEXT in a separate PR (shippable member comp.navi)
|
||||
// -> Do not use the VV_COURSE_ID anymore (discuss with @chrigu) -> We do this next.
|
||||
const currentCourseSession = useCurrentCourseSession();
|
||||
const hasCompetenceCertificates = computed(() => {
|
||||
return !VV_COURSE_IDS.includes(currentCourseSession.value.course.id);
|
||||
});
|
||||
|
||||
const isLoaded = computed(
|
||||
() =>
|
||||
|
|
@ -83,7 +77,10 @@ const isLoaded = computed(
|
|||
<template>
|
||||
<div v-if="isLoaded" class="container-large lg:mt-4">
|
||||
<!-- Competence certificates -->
|
||||
<section v-if="hasCompetenceCertificates" class="mb-4 bg-white p-8">
|
||||
<section
|
||||
v-if="currentCourseSession.course.configuration.enable_competence_certificates"
|
||||
class="mb-4 bg-white p-8"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<h3>{{ $t("a.Kompetenznachweise") }}</h3>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import * as log from "loglevel";
|
||||
import { computed, onMounted } from "vue";
|
||||
import { onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { VV_COURSE_IDS } from "@/constants";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
|
||||
log.debug("CompetenceParentPage created");
|
||||
|
|
@ -29,12 +28,7 @@ function routeInSelfEvaluationAndFeedback() {
|
|||
return route.path.endsWith("/self-evaluation-and-feedback");
|
||||
}
|
||||
|
||||
// FIXME 22.02.24: To-be-tackled NEXT in a separate PR (shippable member comp.navi)
|
||||
// -> Do not use the VV_COURSE_ID anymore (discuss with @chrigu) -> We do this next.
|
||||
const currentCourseSession = useCurrentCourseSession();
|
||||
const isVVCourse = computed(() => {
|
||||
return VV_COURSE_IDS.includes(currentCourseSession.value.course.id);
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
log.debug("CompetenceParentPage mounted", props.courseSlug);
|
||||
|
|
@ -54,7 +48,9 @@ onMounted(async () => {
|
|||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="!isVVCourse"
|
||||
v-if="
|
||||
currentCourseSession.course.configuration.enable_competence_certificates
|
||||
"
|
||||
class="border-t-2 border-t-transparent lg:ml-12"
|
||||
:class="{ 'border-b-2 border-b-blue-900': routeInCompetenceCertificate() }"
|
||||
>
|
||||
|
|
@ -76,7 +72,7 @@ onMounted(async () => {
|
|||
class="block py-3"
|
||||
>
|
||||
{{
|
||||
isVVCourse
|
||||
currentCourseSession.course.configuration.enable_learning_mentor
|
||||
? $t("a.Selbst- und Fremdeinschätzungen")
|
||||
: $t("a.Selbsteinschätzungen")
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import type {
|
|||
} from "@/gql/graphql";
|
||||
import CompetenceSummaryBox from "@/components/dashboard/CompetenceSummaryBox.vue";
|
||||
import AssignmentProgressSummaryBox from "@/components/dashboard/AssignmentProgressSummaryBox.vue";
|
||||
import { VV_COURSE_IDS } from "@/constants";
|
||||
|
||||
const dashboardStore = useDashboardStore();
|
||||
|
||||
|
|
@ -48,11 +47,12 @@ const competenceCriteriaUrl = computed(() => {
|
|||
return `/course/${courseSlug.value}/competence/self-evaluation-and-feedback?courseSessionId=${courseSessionProgress.value?.session_to_continue_id}`;
|
||||
});
|
||||
|
||||
const isVVCourse = computed(() => {
|
||||
const showCompetenceCertificates = computed(() => {
|
||||
if (!dashboardStore.currentDashboardConfig) {
|
||||
return false;
|
||||
}
|
||||
return VV_COURSE_IDS.includes(dashboardStore.currentDashboardConfig.id);
|
||||
return dashboardStore.currentDashboardConfig.course_configuration
|
||||
.enable_competence_certificates;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ const isVVCourse = computed(() => {
|
|||
</div>
|
||||
<div class="grid auto-rows-fr grid-cols-1 gap-8 xl:grid-cols-2">
|
||||
<AssignmentProgressSummaryBox
|
||||
v-if="!isVVCourse"
|
||||
v-if="showCompetenceCertificates"
|
||||
:total-assignments="assignment.total_count"
|
||||
:achieved-points-count="assignment.points_achieved_count"
|
||||
:max-points-count="assignment.points_max_count"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
import graphene
|
||||
from graphql import GraphQLError
|
||||
|
||||
from vbv_lernwelt.assignment.models import (
|
||||
AssignmentCompletion,
|
||||
AssignmentCompletionStatus,
|
||||
)
|
||||
from vbv_lernwelt.core.admin import User
|
||||
from vbv_lernwelt.course.graphql.types import CourseConfigurationObjectType
|
||||
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||
from vbv_lernwelt.dashboard.graphql.types.competence import competences
|
||||
|
|
@ -24,7 +22,6 @@ from vbv_lernwelt.iam.permissions import (
|
|||
can_view_course_session,
|
||||
can_view_course_session_group_statistics,
|
||||
can_view_course_session_progress,
|
||||
can_view_course,
|
||||
)
|
||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||
|
||||
|
|
@ -34,10 +31,6 @@ class DashboardQuery(graphene.ObjectType):
|
|||
CourseStatisticsType, course_id=graphene.ID(required=True)
|
||||
)
|
||||
|
||||
course_configuration = graphene.Field(
|
||||
CourseConfigurationObjectType, course_id=graphene.ID(required=True)
|
||||
)
|
||||
|
||||
course_progress = graphene.Field(
|
||||
CourseProgressType, course_id=graphene.ID(required=True)
|
||||
)
|
||||
|
|
@ -46,14 +39,6 @@ class DashboardQuery(graphene.ObjectType):
|
|||
graphene.NonNull(DashboardConfigType), required=True
|
||||
)
|
||||
|
||||
def resolve_course_configuration(root, info, course_id: str): # noqa
|
||||
course = Course.objects.get(id=course_id)
|
||||
|
||||
if not can_view_course(user=info.context.user, course=course):
|
||||
raise GraphQLError("You do not have access to this course.")
|
||||
|
||||
return course.configuration
|
||||
|
||||
def resolve_course_statistics(root, info, course_id: str): # noqa
|
||||
user = info.context.user
|
||||
course = Course.objects.get(id=course_id)
|
||||
|
|
@ -89,6 +74,7 @@ class DashboardQuery(graphene.ObjectType):
|
|||
"name": c["title"],
|
||||
"slug": c["slug"],
|
||||
"dashboard_type": DashboardType.SIMPLE_DASHBOARD,
|
||||
"course_configuration": c.configuration,
|
||||
}
|
||||
for c in courses
|
||||
]
|
||||
|
|
@ -191,6 +177,7 @@ def get_user_statistics_dashboards(user: User) -> Tuple[List[Dict[str, str]], Se
|
|||
"name": course.title,
|
||||
"slug": course.slug,
|
||||
"dashboard_type": DashboardType.STATISTICS_DASHBOARD,
|
||||
"course_configuration": course.configuration,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -209,6 +196,7 @@ def get_learning_mentor_dashboards(user: User) -> List[Dict[str, str]]:
|
|||
"name": course.title,
|
||||
"slug": course.slug,
|
||||
"dashboard_type": DashboardType.SIMPLE_DASHBOARD,
|
||||
"course_configuration": course.configuration,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -256,6 +244,7 @@ def get_user_course_session_dashboards(
|
|||
"name": course.title,
|
||||
"slug": course.slug,
|
||||
"dashboard_type": resolved_dashboard_type,
|
||||
"course_configuration": course.configuration,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import graphene
|
||||
from graphene import Enum
|
||||
|
||||
from vbv_lernwelt.course.graphql.types import CourseConfigurationObjectType
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.dashboard.graphql.types.assignment import (
|
||||
assignments,
|
||||
|
|
@ -59,6 +60,7 @@ class DashboardConfigType(graphene.ObjectType):
|
|||
name = graphene.String(required=True)
|
||||
slug = graphene.String(required=True)
|
||||
dashboard_type = graphene.Field(DashboardType, required=True)
|
||||
course_configuration = graphene.Field(CourseConfigurationObjectType, required=True)
|
||||
|
||||
|
||||
class ProgressDashboardCompetenceType(graphene.ObjectType):
|
||||
|
|
|
|||
|
|
@ -150,65 +150,14 @@ class DashboardTestCase(GraphQLTestCase):
|
|||
self.assertEqual(assignment["points_max_count"], 50)
|
||||
self.assertEqual(assignment["points_achieved_count"], 20)
|
||||
|
||||
def test_course_configuration_denied(self):
|
||||
# GIVEN
|
||||
role_less_user = create_user("sepp@blatter.fifa")
|
||||
self.client.force_login(role_less_user)
|
||||
|
||||
course, _ = create_course("Course 1")
|
||||
|
||||
# WHEN
|
||||
query = """query($course_id: ID!) {
|
||||
course_configuration(course_id: $course_id) {
|
||||
enable_circle_documents
|
||||
enable_learning_mentor
|
||||
enable_competence_certificates
|
||||
}
|
||||
}
|
||||
"""
|
||||
response = self.query(query, variables={"course_id": str(course.id)})
|
||||
|
||||
# THEN
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
response.json()["errors"][0]["message"],
|
||||
"You do not have access to this course.",
|
||||
)
|
||||
|
||||
def test_course_configuration(self):
|
||||
# GIVEN
|
||||
member = create_user("sepp@blatter.fifa")
|
||||
self.client.force_login(member)
|
||||
|
||||
course, _ = create_course("Course 1")
|
||||
add_course_session_user(
|
||||
course_session=create_course_session(course=course, title="Whatever"),
|
||||
role=CourseSessionUser.Role.MEMBER,
|
||||
user=member,
|
||||
)
|
||||
|
||||
# WHEN
|
||||
query = """query($course_id: ID!) {
|
||||
course_configuration(course_id: $course_id) {
|
||||
enable_circle_documents
|
||||
enable_learning_mentor
|
||||
enable_competence_certificates
|
||||
}
|
||||
}
|
||||
"""
|
||||
response = self.query(query, variables={"course_id": str(course.id)})
|
||||
|
||||
# THEN
|
||||
self.assertResponseNoErrors(response)
|
||||
|
||||
course_configuration = response.json()["data"]["course_configuration"]
|
||||
self.assertEqual(course_configuration["enable_circle_documents"], True)
|
||||
self.assertEqual(course_configuration["enable_learning_mentor"], True)
|
||||
self.assertEqual(course_configuration["enable_competence_certificates"], True)
|
||||
|
||||
def test_dashboard_config(self):
|
||||
# GIVEN
|
||||
course_1, _ = create_course("Test Course 1")
|
||||
course_1.configuration.enable_learning_mentor = False
|
||||
course_1.configuration.enable_competence_certificates = False
|
||||
course_1.configuration.enable_circle_documents = False
|
||||
course_1.configuration.save()
|
||||
|
||||
course_2, _ = create_course("Test Course 2")
|
||||
course_3, _ = create_course("Test Course 3")
|
||||
|
||||
|
|
@ -249,6 +198,11 @@ class DashboardTestCase(GraphQLTestCase):
|
|||
name
|
||||
slug
|
||||
dashboard_type
|
||||
course_configuration {
|
||||
enable_circle_documents
|
||||
enable_learning_mentor
|
||||
enable_competence_certificates
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
|
@ -269,6 +223,13 @@ class DashboardTestCase(GraphQLTestCase):
|
|||
self.assertEqual(course_1_config["slug"], course_1.slug)
|
||||
self.assertEqual(course_1_config["dashboard_type"], "PROGRESS_DASHBOARD")
|
||||
|
||||
course_1_course_configuration = course_1_config["course_configuration"]
|
||||
self.assertFalse(course_1_course_configuration["enable_circle_documents"])
|
||||
self.assertFalse(course_1_course_configuration["enable_learning_mentor"])
|
||||
self.assertFalse(
|
||||
course_1_course_configuration["enable_competence_certificates"]
|
||||
)
|
||||
|
||||
course_2_config = find_dashboard_config_by_course_id(
|
||||
dashboard_config, course_2.id
|
||||
)
|
||||
|
|
@ -277,6 +238,11 @@ class DashboardTestCase(GraphQLTestCase):
|
|||
self.assertEqual(course_2_config["slug"], course_2.slug)
|
||||
self.assertEqual(course_2_config["dashboard_type"], "STATISTICS_DASHBOARD")
|
||||
|
||||
course_2_course_configuration = course_2_config["course_configuration"]
|
||||
self.assertTrue(course_2_course_configuration["enable_circle_documents"])
|
||||
self.assertTrue(course_2_course_configuration["enable_learning_mentor"])
|
||||
self.assertTrue(course_2_course_configuration["enable_competence_certificates"])
|
||||
|
||||
course_3_config = find_dashboard_config_by_course_id(
|
||||
dashboard_config, course_3.id
|
||||
)
|
||||
|
|
@ -285,6 +251,11 @@ class DashboardTestCase(GraphQLTestCase):
|
|||
self.assertEqual(course_3_config["slug"], course_3.slug)
|
||||
self.assertEqual(course_3_config["dashboard_type"], "SIMPLE_DASHBOARD")
|
||||
|
||||
course_3_course_configuration = course_3_config["course_configuration"]
|
||||
self.assertTrue(course_3_course_configuration["enable_circle_documents"])
|
||||
self.assertTrue(course_3_course_configuration["enable_learning_mentor"])
|
||||
self.assertTrue(course_3_course_configuration["enable_competence_certificates"])
|
||||
|
||||
def test_dashboard_config_mentor(self):
|
||||
# GIVEN
|
||||
course_1, _ = create_course("Test Course 1")
|
||||
|
|
|
|||
Loading…
Reference in New Issue