From 6c6cf0ad0b7ec51311dc613eef1e0d1b7a1427aa Mon Sep 17 00:00:00 2001 From: Abdurrehman <93309234+MAbdurrehman12@users.noreply.github.com> Date: Tue, 7 Dec 2021 15:54:00 +0500 Subject: [PATCH] feat: external plugin integration in instructor dashboard (#29376) * feat: external plugin integration fixes --- cms/envs/production.py | 8 ++++---- lms/djangoapps/instructor/constants.py | 9 +++++++++ .../tests/views/test_instructor_dashboard.py | 16 ++++++++++++++++ lms/djangoapps/instructor/views/__init__.py | 4 ---- lms/djangoapps/instructor/views/api.py | 2 +- .../instructor/views/instructor_dashboard.py | 11 +++++++++++ lms/envs/production.py | 8 ++++---- .../instructor_dashboard_2.html | 2 +- 8 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 lms/djangoapps/instructor/constants.py diff --git a/cms/envs/production.py b/cms/envs/production.py index baf4e2e437..d67233a8b9 100644 --- a/cms/envs/production.py +++ b/cms/envs/production.py @@ -545,16 +545,16 @@ SYSTEM_WIDE_ROLE_CLASSES = ENV_TOKENS.get('SYSTEM_WIDE_ROLE_CLASSES') or SYSTEM_ ######################## Setting for content libraries ######################## MAX_BLOCKS_PER_CONTENT_LIBRARY = ENV_TOKENS.get('MAX_BLOCKS_PER_CONTENT_LIBRARY', MAX_BLOCKS_PER_CONTENT_LIBRARY) +########################## Derive Any Derived Settings ####################### + +derive_settings(__name__) + ####################### Plugin Settings ########################## # This is at the bottom because it is going to load more settings after base settings are loaded add_plugins(__name__, ProjectType.CMS, SettingsType.PRODUCTION) -########################## Derive Any Derived Settings ####################### - -derive_settings(__name__) - ############# CORS headers for cross-domain requests ################# if FEATURES.get('ENABLE_CORS_HEADERS'): CORS_ALLOW_CREDENTIALS = True diff --git a/lms/djangoapps/instructor/constants.py b/lms/djangoapps/instructor/constants.py new file mode 100644 index 0000000000..972946e2c0 --- /dev/null +++ b/lms/djangoapps/instructor/constants.py @@ -0,0 +1,9 @@ +""" +Constants used by Instructor. +""" + +# this is the UserPreference key for the user's recipient invoice copy +INVOICE_KEY = 'pref-invoice-copy' + +# external plugins (if any) will use this constant to return context to instructor dashboard +INSTRUCTOR_DASHBOARD_PLUGIN_VIEW_NAME = 'instructor_dashboard' diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py index 70763e100c..8908d011f0 100644 --- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py +++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py @@ -23,6 +23,7 @@ from common.djangoapps.student.tests.factories import AdminFactory, CourseAccess from common.djangoapps.student.tests.factories import StaffFactory from common.djangoapps.student.tests.factories import UserFactory from common.test.utils import XssTestMixin +from lms.djangoapps.courseware.courses import get_studio_url from lms.djangoapps.courseware.tabs import get_course_tab_list from lms.djangoapps.courseware.tests.factories import StudentModuleFactory from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase @@ -582,6 +583,21 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT # assert we don't get a 500 error assert 200 == response.status_code + @patch("lms.djangoapps.instructor.views.instructor_dashboard.get_plugins_view_context") + def test_external_plugin_integration(self, mock_get_plugins_view_context): + """ + Tests that whether context from plugins is being reflected/added in instructor dashboard. + """ + test_studio_url = get_studio_url(self.course, 'course') + + context = { + 'studio_url': test_studio_url + } + mock_get_plugins_view_context.return_value = context + + response = self.client.get(self.url) + self.assertContains(response, test_studio_url) + @ddt.ddt class TestInstructorDashboardPerformance(ModuleStoreTestCase, LoginEnrollmentTestCase, XssTestMixin): diff --git a/lms/djangoapps/instructor/views/__init__.py b/lms/djangoapps/instructor/views/__init__.py index 2b14fd387e..e69de29bb2 100644 --- a/lms/djangoapps/instructor/views/__init__.py +++ b/lms/djangoapps/instructor/views/__init__.py @@ -1,4 +0,0 @@ -""" -This is the UserPreference key for the user's recipient invoice copy -""" -INVOICE_KEY = 'pref-invoice-copy' diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 248a8dd5cc..d47638cda4 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -87,6 +87,7 @@ from lms.djangoapps.discussion.django_comment_client.utils import ( ) from lms.djangoapps.instructor import enrollment from lms.djangoapps.instructor.access import ROLES, allow_access, list_with_level, revoke_access, update_forum_role +from lms.djangoapps.instructor.constants import INVOICE_KEY from lms.djangoapps.instructor.enrollment import ( enroll_email, get_email_params, @@ -95,7 +96,6 @@ from lms.djangoapps.instructor.enrollment import ( send_mail_to_student, unenroll_email, ) -from lms.djangoapps.instructor.views import INVOICE_KEY from lms.djangoapps.instructor.views.instructor_task_helpers import extract_email_features, extract_task_features from lms.djangoapps.instructor_analytics import basic as instructor_analytics_basic, csvs as instructor_analytics_csvs from lms.djangoapps.instructor_task import api as task_api diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index be88a9ca0b..c3e15fa824 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -22,6 +22,7 @@ from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.http import require_POST from edx_proctoring.api import does_backend_support_onboarding from edx_when.api import is_enabled_for_course +from edx_django_utils.plugins import get_plugins_view_context from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from xblock.field_data import DictFieldData @@ -52,10 +53,12 @@ from lms.djangoapps.courseware.courses import get_studio_url from lms.djangoapps.courseware.module_render import get_module_by_usage_id from lms.djangoapps.discussion.django_comment_client.utils import has_forum_access from lms.djangoapps.grades.api import is_writable_gradebook_enabled +from lms.djangoapps.instructor.constants import INSTRUCTOR_DASHBOARD_PLUGIN_VIEW_NAME from openedx.core.djangoapps.course_groups.cohorts import DEFAULT_COHORT_NAME, get_course_cohorts, is_course_cohorted from openedx.core.djangoapps.discussions.config.waffle_utils import legacy_discussion_experience_enabled from openedx.core.djangoapps.discussions.utils import available_division_schemes from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_ADMINISTRATOR, CourseDiscussionSettings +from openedx.core.djangoapps.plugins.constants import ProjectType from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.verified_track_content.models import VerifiedTrackCohortedCourse from openedx.core.djangolib.markup import HTML, Text @@ -255,6 +258,14 @@ def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable 'xqa_server': settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"), } + context_from_plugins = get_plugins_view_context( + ProjectType.LMS, + INSTRUCTOR_DASHBOARD_PLUGIN_VIEW_NAME, + context + ) + + context.update(context_from_plugins) + return render_to_response('instructor/instructor_dashboard_2/instructor_dashboard_2.html', context) diff --git a/lms/envs/production.py b/lms/envs/production.py index 5e2737d925..820b427f94 100644 --- a/lms/envs/production.py +++ b/lms/envs/production.py @@ -953,6 +953,10 @@ DASHBOARD_COURSE_LIMIT = ENV_TOKENS.get('DASHBOARD_COURSE_LIMIT', None) ######################## Setting for content libraries ######################## MAX_BLOCKS_PER_CONTENT_LIBRARY = ENV_TOKENS.get('MAX_BLOCKS_PER_CONTENT_LIBRARY', MAX_BLOCKS_PER_CONTENT_LIBRARY) +########################## Derive Any Derived Settings ####################### + +derive_settings(__name__) + ############################### Plugin Settings ############################### # This is at the bottom because it is going to load more settings after base settings are loaded @@ -960,10 +964,6 @@ MAX_BLOCKS_PER_CONTENT_LIBRARY = ENV_TOKENS.get('MAX_BLOCKS_PER_CONTENT_LIBRARY' # Load production.py in plugins add_plugins(__name__, ProjectType.LMS, SettingsType.PRODUCTION) -########################## Derive Any Derived Settings ####################### - -derive_settings(__name__) - ############## Settings for Completion API ######################### # Once a user has watched this percentage of a video, mark it as complete: diff --git a/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html b/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html index b795f0eae6..c756bca275 100644 --- a/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html +++ b/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html @@ -130,7 +130,7 @@ from openedx.core.djangolib.markup import HTML <% is_hidden = section_data.get('is_hidden', False) %> % endfor