From 533b971d835d403e4ecc67c506d2ba62963e6e26 Mon Sep 17 00:00:00 2001 From: AsadAzam Date: Mon, 7 Mar 2022 12:15:45 +0500 Subject: [PATCH] feat: added live course app plugin (#29998) --- lms/envs/common.py | 3 - openedx/core/djangoapps/course_live/apps.py | 14 ++++ .../djangoapps/course_live/config/__init__.py | 0 .../djangoapps/course_live/config/waffle.py | 26 ++++++++ openedx/core/djangoapps/course_live/models.py | 12 ++++ .../core/djangoapps/course_live/plugins.py | 66 +++++++++++++++++++ setup.py | 3 + 7 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 openedx/core/djangoapps/course_live/apps.py create mode 100644 openedx/core/djangoapps/course_live/config/__init__.py create mode 100644 openedx/core/djangoapps/course_live/config/waffle.py create mode 100644 openedx/core/djangoapps/course_live/plugins.py diff --git a/lms/envs/common.py b/lms/envs/common.py index ad6c6b75ec..5f289a301b 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3212,9 +3212,6 @@ INSTALLED_APPS = [ # For save for later 'lms.djangoapps.save_for_later', - - # Course Live App - 'openedx.core.djangoapps.course_live', ] ######################### CSRF ######################################### diff --git a/openedx/core/djangoapps/course_live/apps.py b/openedx/core/djangoapps/course_live/apps.py new file mode 100644 index 0000000000..2a83c86aa2 --- /dev/null +++ b/openedx/core/djangoapps/course_live/apps.py @@ -0,0 +1,14 @@ +""" +Configure the django app +""" +from django.apps import AppConfig + + +class CourseLiveConfig(AppConfig): + """ + Configuration class for Course Live. + """ + + name = "openedx.core.djangoapps.course_live" + + plugin_app = {} diff --git a/openedx/core/djangoapps/course_live/config/__init__.py b/openedx/core/djangoapps/course_live/config/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openedx/core/djangoapps/course_live/config/waffle.py b/openedx/core/djangoapps/course_live/config/waffle.py new file mode 100644 index 0000000000..02065787f7 --- /dev/null +++ b/openedx/core/djangoapps/course_live/config/waffle.py @@ -0,0 +1,26 @@ +""" +This module contains various configuration settings via +waffle switches for the live app. +""" + +from edx_toggles.toggles import LegacyWaffleFlagNamespace + +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag + + +WAFFLE_NAMESPACE = LegacyWaffleFlagNamespace(name='course_live') + +# .. toggle_name: course_live.enable_course_live +# .. toggle_implementation: CourseWaffleFlag +# .. toggle_default: False +# .. toggle_description: Waffle flag to enable the course live app plugin +# .. toggle_use_cases: temporary, open_edx +# .. toggle_creation_date: 2022-03-02 +# .. toggle_target_removal_date: 2022-06-02 +# .. toggle_warnings: When the flag is ON, the course live app will be visible in the course authoring mfe +# .. toggle_tickets: TNL-9603 +ENABLE_COURSE_LIVE = CourseWaffleFlag( + waffle_namespace=WAFFLE_NAMESPACE, + flag_name='enable_course_live', + module_name=__name__, +) diff --git a/openedx/core/djangoapps/course_live/models.py b/openedx/core/djangoapps/course_live/models.py index ef18eb56ae..dd8153634a 100644 --- a/openedx/core/djangoapps/course_live/models.py +++ b/openedx/core/djangoapps/course_live/models.py @@ -36,6 +36,18 @@ class CourseLiveConfiguration(TimeStampedModel): def __str__(self): return f"Configuration(course_key='{self.course_key}', provider='{self.provider_type}', enabled={self.enabled})" + @classmethod + def is_enabled(cls, course_key) -> bool: + """ + Check if there is an active configuration for a given course key + + Default to False, if no configuration exists + """ + configuration = cls.get(course_key) + if not configuration: + return False + return configuration.enabled + @classmethod def get(cls, course_key): """ diff --git a/openedx/core/djangoapps/course_live/plugins.py b/openedx/core/djangoapps/course_live/plugins.py new file mode 100644 index 0000000000..5a7d01ccfb --- /dev/null +++ b/openedx/core/djangoapps/course_live/plugins.py @@ -0,0 +1,66 @@ +""" +Course app configuration for live. +""" +from typing import Dict, Optional + +from django.contrib.auth import get_user_model +from django.utils.translation import gettext_noop as _ +from opaque_keys.edx.keys import CourseKey + +from openedx.core.djangoapps.course_apps.plugins import CourseApp +from openedx.core.djangoapps.course_live.config.waffle import ENABLE_COURSE_LIVE +from .models import CourseLiveConfiguration + +User = get_user_model() + + +class LiveCourseApp(CourseApp): + """ + Course App config for Live. + """ + + app_id = "live" + name = _("Live") + description = _("Enable in-platform video conferencing by configuring live") + documentation_links = { + # TODO: add the actual documentation link once it exists + "learn_more_configuration": '', + } + + @classmethod + def is_available(cls, course_key: CourseKey) -> bool: + """ + Live is available based on ENABLE_COURSE_LIVE flag + """ + return ENABLE_COURSE_LIVE.is_enabled(course_key) + + @classmethod + def is_enabled(cls, course_key: CourseKey) -> bool: + """ + Live enable/disable status is stored in a separate model. + """ + return CourseLiveConfiguration.is_enabled(course_key) + + @classmethod + def set_enabled(cls, course_key: CourseKey, enabled: bool, user: 'User') -> bool: + """ + Set live enabled status in CourseLiveConfiguration model. + """ + configuration = CourseLiveConfiguration.get(course_key) + if configuration is None: + raise ValueError("Can't enable/disable live for course before it is configured.") + configuration.enabled = enabled + configuration.save() + return configuration.enabled + + @classmethod + def get_allowed_operations(cls, course_key: CourseKey, user: Optional[User] = None) -> Dict[str, bool]: + """ + Return allowed operations for live app. + """ + # Can only enable live for a course if live is configured. + can_enable = CourseLiveConfiguration.get(course_key) is not None + return { + "enable": can_enable, + "configure": True, + } diff --git a/setup.py b/setup.py index 0cdcaa04f3..159a0f94e9 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ setup( "textbooks = lms.djangoapps.courseware.plugins:TextbooksCourseApp", "wiki = lms.djangoapps.course_wiki.plugins.course_app:WikiCourseApp", "custom_pages = lms.djangoapps.courseware.plugins:CustomPagesCourseApp", + "live = openedx.core.djangoapps.course_live.plugins:LiveCourseApp", ], "openedx.course_tool": [ "calendar_sync_toggle = openedx.features.calendar_sync.plugins:CalendarSyncToggleTool", @@ -91,6 +92,7 @@ setup( "announcements = openedx.features.announcements.apps:AnnouncementsConfig", "ace_common = openedx.core.djangoapps.ace_common.apps:AceCommonConfig", "credentials = openedx.core.djangoapps.credentials.apps:CredentialsConfig", + "course_live = openedx.core.djangoapps.course_live.apps:CourseLiveConfig", "content_libraries = openedx.core.djangoapps.content_libraries.apps:ContentLibrariesConfig", "discussion = lms.djangoapps.discussion.apps:DiscussionConfig", "discussions = openedx.core.djangoapps.discussions.apps:DiscussionsConfig", @@ -109,6 +111,7 @@ setup( "cms.djangoapp": [ "announcements = openedx.features.announcements.apps:AnnouncementsConfig", "ace_common = openedx.core.djangoapps.ace_common.apps:AceCommonConfig", + "course_live = openedx.core.djangoapps.course_live.apps:CourseLiveConfig", "content_libraries = openedx.core.djangoapps.content_libraries.apps:ContentLibrariesConfig", # Importing an LMS app into the Studio process is not a good # practice. We're ignoring this for Discussions here because its