def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable=too-many-statements """ Display the instructor dashboard for a course. """ try: course_key = CourseKey.from_string(course_id) except InvalidKeyError: log.error("Unable to find course with course key %s while loading the Instructor Dashboard.", course_id) return HttpResponseServerError() course = get_course_by_id(course_key, depth=0) access = { 'admin': request.user.is_staff, 'instructor': bool(has_access(request.user, 'instructor', course)), 'finance_admin': CourseFinanceAdminRole(course_key).has_user(request.user), 'sales_admin': CourseSalesAdminRole(course_key).has_user(request.user), 'staff': bool(has_access(request.user, 'staff', course)), 'forum_admin': has_forum_access(request.user, course_key, FORUM_ROLE_ADMINISTRATOR), 'data_researcher': request.user.has_perm(permissions.CAN_RESEARCH, course_key), } if not request.user.has_perm(permissions.VIEW_DASHBOARD, course_key): raise Http404() sections = [] if access['staff']: sections_content = [ _section_course_info(course, access), _section_membership(course, access), _section_cohort_management(course, access), _section_student_admin(course, access), ] if legacy_discussion_experience_enabled(course_key): sections_content.append(_section_discussions_management(course, access)) sections.extend(sections_content) if access['data_researcher']: sections.append(_section_data_download(course, access)) analytics_dashboard_message = None if show_analytics_dashboard_message(course_key) and (access['staff'] or access['instructor']): # Construct a URL to the external analytics dashboard analytics_dashboard_url = f'{settings.ANALYTICS_DASHBOARD_URL}/courses/{str(course_key)}' link_start = HTML("<a href=\"{}\" rel=\"noopener\" target=\"_blank\">").format(analytics_dashboard_url) analytics_dashboard_message = _( "To gain insights into student enrollment and participation {link_start}" "visit {analytics_dashboard_name}, our new course analytics product{link_end}." ) analytics_dashboard_message = Text(analytics_dashboard_message).format( link_start=link_start, link_end=HTML("</a>"), analytics_dashboard_name=settings.ANALYTICS_DASHBOARD_NAME) # Temporarily show the "Analytics" section until we have a better way of linking to Insights sections.append(_section_analytics(course, access)) # Check if there is corresponding entry in the CourseMode Table related to the Instructor Dashboard course course_mode_has_price = False # lint-amnesty, pylint: disable=unused-variable paid_modes = CourseMode.paid_modes_for_course(course_key) if len(paid_modes) == 1: course_mode_has_price = True elif len(paid_modes) > 1: log.error( "Course %s has %s course modes with payment options. Course must only have " "one paid course mode to enable eCommerce options.", str(course_key), len(paid_modes) ) if access['instructor'] and is_enabled_for_course(course_key): sections.insert(3, _section_extensions(course)) # Gate access to course email by feature flag & by course-specific authorization if ( is_bulk_email_feature_enabled(course_key) and not is_bulk_email_disabled_for_course(course_key) and (access['staff'] or access['instructor']) ): sections.append(_section_send_email(course, access)) # Gate access to Special Exam tab depending if either timed exams or proctored exams # are enabled in the course user_has_access = any([ request.user.is_staff, CourseStaffRole(course_key).has_user(request.user), CourseInstructorRole(course_key).has_user(request.user) ]) course_has_special_exams = course.enable_proctored_exams or course.enable_timed_exams can_see_special_exams = course_has_special_exams and user_has_access and settings.FEATURES.get( 'ENABLE_SPECIAL_EXAMS', False) if can_see_special_exams: sections.append(_section_special_exams(course, access)) # Certificates panel # This is used to generate example certificates # and enable self-generated certificates for a course. # Note: This is hidden for all CCXs certs_enabled = CertificateGenerationConfiguration.current().enabled and not hasattr(course_key, 'ccx') if certs_enabled and access['admin']: sections.append(_section_certificates(course)) openassessment_blocks = modulestore().get_items( course_key, qualifiers={'category': 'openassessment'} ) # filter out orphaned openassessment blocks openassessment_blocks = [ block for block in openassessment_blocks if block.parent is not None ] if len(openassessment_blocks) > 0 and access['staff']: sections.append(_section_open_response_assessment(request, course, openassessment_blocks, access)) disable_buttons = not CourseEnrollment.objects.is_small_course(course_key) certificate_allowlist = certs_api.get_allowlist(course_key) generate_certificate_exceptions_url = reverse( 'generate_certificate_exceptions', kwargs={'course_id': str(course_key), 'generate_for': ''} ) generate_bulk_certificate_exceptions_url = reverse( 'generate_bulk_certificate_exceptions', kwargs={'course_id': str(course_key)} ) certificate_exception_view_url = reverse( 'certificate_exception_view', kwargs={'course_id': str(course_key)} ) certificate_invalidation_view_url = reverse( 'certificate_invalidation_view', kwargs={'course_id': str(course_key)} ) certificate_invalidations = CertificateInvalidation.get_certificate_invalidations(course_key) context = { 'course': course, 'studio_url': get_studio_url(course, 'course'), 'sections': sections, 'disable_buttons': disable_buttons, 'analytics_dashboard_message': analytics_dashboard_message, 'certificate_allowlist': certificate_allowlist, 'certificate_invalidations': certificate_invalidations, 'generate_certificate_exceptions_url': generate_certificate_exceptions_url, 'generate_bulk_certificate_exceptions_url': generate_bulk_certificate_exceptions_url, 'certificate_exception_view_url': certificate_exception_view_url, 'certificate_invalidation_view_url': certificate_invalidation_view_url, '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)
def get_exclude_list_of_fields(cls, course_key): """ Returns a list of fields to exclude from the Studio Advanced settings based on a feature flag (i.e. enabled or disabled). """ # Copy the filtered list to avoid permanently changing the class attribute. exclude_list = list(cls.FIELDS_EXCLUDE_LIST) # Do not show giturl if feature is not enabled. if not toggles.EXPORT_GIT.is_enabled(): exclude_list.append('giturl') # Do not show edxnotes if the feature is disabled. if not settings.FEATURES.get('ENABLE_EDXNOTES'): exclude_list.append('edxnotes') # Do not show video auto advance if the feature is disabled if not settings.FEATURES.get('ENABLE_OTHER_COURSE_SETTINGS'): exclude_list.append('other_course_settings') # Do not show video_upload_pipeline if the feature is disabled. if not settings.FEATURES.get('ENABLE_VIDEO_UPLOAD_PIPELINE'): exclude_list.append('video_upload_pipeline') # Do not show video auto advance if the feature is disabled if not settings.FEATURES.get('ENABLE_AUTOADVANCE_VIDEOS'): exclude_list.append('video_auto_advance') # Do not show social sharing url field if the feature is disabled. if (not hasattr(settings, 'SOCIAL_SHARING_SETTINGS') or not getattr(settings, 'SOCIAL_SHARING_SETTINGS', {}).get("CUSTOM_COURSE_URLS")): exclude_list.append('social_sharing_url') # Do not show teams configuration if feature is disabled. if not settings.FEATURES.get('ENABLE_TEAMS'): exclude_list.append('teams_configuration') if not settings.FEATURES.get('ENABLE_VIDEO_BUMPER'): exclude_list.append('video_bumper') # Do not show enable_ccx if feature is not enabled. if not settings.FEATURES.get('CUSTOM_COURSES_EDX'): exclude_list.append('enable_ccx') exclude_list.append('ccx_connector') # Do not show "Issue Open Badges" in Studio Advanced Settings # if the feature is disabled. if not settings.FEATURES.get('ENABLE_OPENBADGES'): exclude_list.append('issue_badges') # If the XBlockStudioConfiguration table is not being used, there is no need to # display the "Allow Unsupported XBlocks" setting. if not XBlockStudioConfigurationFlag.is_enabled(): exclude_list.append('allow_unsupported_xblocks') # Do not show "Course Visibility For Unenrolled Learners" in Studio Advanced Settings # if the enable_anonymous_access flag is not enabled if not COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled( course_key=course_key): exclude_list.append('course_visibility') # Do not show "Proctortrack Exam Escalation Contact" if Proctortrack is not # an available proctoring backend. if not settings.PROCTORING_BACKENDS or settings.PROCTORING_BACKENDS.get( 'proctortrack') is None: exclude_list.append('proctoring_escalation_email') if not legacy_discussion_experience_enabled(course_key): exclude_list.append('discussion_blackouts') exclude_list.append('allow_anonymous') exclude_list.append('allow_anonymous_to_peers') exclude_list.append('discussion_topics') return exclude_list