chore: introduce course configuration "feature" flags in client

This commit is contained in:
Livio Bieri 2024-02-28 14:14:23 +01:00
parent 51609591e1
commit e4329194ee
10 changed files with 59 additions and 109 deletions

View File

@ -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>

View File

@ -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
];

View File

@ -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'];

View File

@ -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!

View File

@ -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>

View File

@ -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")
}}

View File

@ -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"

View File

@ -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,
}
)

View File

@ -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):

View File

@ -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")