feat: create course home api DRF (#33173)

This commit is contained in:
ruzniaievdm
2023-09-06 17:05:25 +03:00
committed by GitHub
parent c5d1807c81
commit e480a79d9c
13 changed files with 550 additions and 90 deletions

View File

@@ -1,8 +1,10 @@
"""
Serializers for v1 contentstore API.
"""
from .home import CourseHomeSerializer
from .course_details import CourseDetailsSerializer
from .course_team import CourseTeamSerializer
from .course_rerun import CourseRerunSerializer
from .grading import CourseGradingModelSerializer, CourseGradingSerializer
from .proctoring import (
LimitedProctoredExamSettingsSerializer,

View File

@@ -0,0 +1,19 @@
"""
Common API Serializers
"""
from rest_framework import serializers
from openedx.core.lib.api.serializers import CourseKeyField
class CourseCommonSerializer(serializers.Serializer):
"""Serializer for course renders"""
course_key = CourseKeyField()
display_name = serializers.CharField()
lms_link = serializers.CharField()
number = serializers.CharField()
org = serializers.CharField()
rerun_link = serializers.CharField()
run = serializers.CharField()
url = serializers.CharField()

View File

@@ -0,0 +1,15 @@
"""
API Serializers for course rerun
"""
from rest_framework import serializers
class CourseRerunSerializer(serializers.Serializer):
""" Serializer for course rerun """
allow_unicode_course_id = serializers.BooleanField()
course_creator_status = serializers.CharField()
display_name = serializers.CharField()
number = serializers.CharField()
org = serializers.CharField()
run = serializers.CharField()

View File

@@ -0,0 +1,62 @@
"""
API Serializers for course home
"""
from rest_framework import serializers
from openedx.core.lib.api.serializers import CourseKeyField
from .common import CourseCommonSerializer
class UnsucceededCourseSerializer(serializers.Serializer):
"""Serializer for unsucceeded course"""
display_name = serializers.CharField()
course_key = CourseKeyField()
org = serializers.CharField()
number = serializers.CharField()
run = serializers.CharField()
is_failed = serializers.BooleanField()
is_in_progress = serializers.BooleanField()
dismiss_link = serializers.CharField()
class LibraryViewSerializer(serializers.Serializer):
"""Serializer for library view"""
display_name = serializers.CharField()
library_key = serializers.CharField()
url = serializers.CharField()
org = serializers.CharField()
number = serializers.CharField()
can_edit = serializers.BooleanField()
class CourseHomeSerializer(serializers.Serializer):
"""Serializer for course home"""
allow_course_reruns = serializers.BooleanField()
allow_to_create_new_org = serializers.BooleanField()
allow_unicode_course_id = serializers.BooleanField()
allowed_organizations = serializers.ListSerializer(
child=serializers.CharField(),
allow_empty=True
)
archived_courses = CourseCommonSerializer(required=False, many=True)
can_create_organizations = serializers.BooleanField()
course_creator_status = serializers.CharField()
courses = CourseCommonSerializer(required=False, many=True)
in_process_course_actions = UnsucceededCourseSerializer(many=True, required=False, allow_null=True)
libraries = LibraryViewSerializer(many=True, required=False, allow_null=True)
libraries_enabled = serializers.BooleanField()
library_authoring_mfe_url = serializers.CharField()
optimization_enabled = serializers.BooleanField()
redirect_to_library_authoring_mfe = serializers.BooleanField()
request_course_creator_url = serializers.CharField()
rerun_creator_status = serializers.BooleanField()
show_new_library_button = serializers.BooleanField()
split_studio_home = serializers.BooleanField()
studio_name = serializers.CharField()
studio_short_name = serializers.CharField()
studio_request_email = serializers.CharField()
tech_support_email = serializers.CharField()
platform_name = serializers.CharField()
user_is_active = serializers.BooleanField()

View File

@@ -4,23 +4,11 @@ API Serializers for course settings
from rest_framework import serializers
from openedx.core.lib.api.serializers import CourseKeyField
class PossiblePreRequisiteCourseSerializer(serializers.Serializer):
""" Serializer for possible pre requisite course """
course_key = CourseKeyField()
display_name = serializers.CharField()
lms_link = serializers.CharField()
number = serializers.CharField()
org = serializers.CharField()
rerun_link = serializers.CharField()
run = serializers.CharField()
url = serializers.CharField()
from .common import CourseCommonSerializer
class CourseSettingsSerializer(serializers.Serializer):
""" Serializer for course settings """
"""Serializer for course settings"""
about_page_editable = serializers.BooleanField()
can_show_certificate_available_date_field = serializers.BooleanField()
course_display_name = serializers.CharField()
@@ -38,7 +26,7 @@ class CourseSettingsSerializer(serializers.Serializer):
marketing_enabled = serializers.BooleanField()
mfe_proctored_exam_settings_url = serializers.CharField(required=False, allow_null=True, allow_blank=True)
platform_name = serializers.CharField()
possible_pre_requisite_courses = PossiblePreRequisiteCourseSerializer(required=False, many=True)
possible_pre_requisite_courses = CourseCommonSerializer(required=False, many=True)
short_description_editable = serializers.BooleanField()
show_min_grade_warning = serializers.BooleanField()
sidebar_html_enabled = serializers.BooleanField()

View File

@@ -1,8 +1,7 @@
""" Contenstore API v1 URLs. """
from django.urls import path
from django.urls import re_path
from django.conf import settings
from django.urls import re_path, path
from openedx.core.constants import COURSE_ID_PATTERN
@@ -10,7 +9,9 @@ from .views import (
CourseDetailsView,
CourseTeamView,
CourseGradingView,
CourseRerunView,
CourseSettingsView,
HomePageView,
ProctoredExamSettingsView,
ProctoringErrorsView,
xblock,
@@ -25,6 +26,11 @@ app_name = 'v1'
VIDEO_ID_PATTERN = r'(?:(?P<edx_video_id>[-\w]+))'
urlpatterns = [
path(
'home',
HomePageView.as_view(),
name="home"
),
re_path(
fr'^proctored_exam_settings/{COURSE_ID_PATTERN}$',
ProctoredExamSettingsView.as_view(),
@@ -92,4 +98,9 @@ urlpatterns = [
HelpUrlsView.as_view(),
name="help_urls"
),
re_path(
fr'^course_rerun/{COURSE_ID_PATTERN}$',
CourseRerunView.as_view(),
name="course_rerun"
),
]

View File

@@ -3,8 +3,10 @@ Views for v1 contentstore API.
"""
from .course_details import CourseDetailsView
from .course_team import CourseTeamView
from .course_rerun import CourseRerunView
from .grading import CourseGradingView
from .proctoring import ProctoredExamSettingsView, ProctoringErrorsView
from .home import HomePageView
from .settings import CourseSettingsView
from .xblock import XblockView
from .assets import AssetsView

View File

@@ -0,0 +1,76 @@
""" API Views for course rerun """
import edx_api_doc_tools as apidocs
from opaque_keys.edx.keys import CourseKey
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from cms.djangoapps.contentstore.utils import get_course_rerun_context
from cms.djangoapps.contentstore.rest_api.v1.serializers import CourseRerunSerializer
from common.djangoapps.student.roles import GlobalStaff
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, verify_course_exists, view_auth_classes
from xmodule.modulestore.django import modulestore
@view_auth_classes(is_authenticated=True)
class CourseRerunView(DeveloperErrorViewMixin, APIView):
"""
View for course rerun.
"""
@apidocs.schema(
parameters=[
apidocs.string_parameter("course_id", apidocs.ParameterLocation.PATH, description="Course ID"),
],
responses={
200: CourseRerunSerializer,
401: "The requester is not authenticated.",
403: "The requester cannot access the specified course.",
404: "The requested course does not exist.",
},
)
@verify_course_exists()
def get(self, request: Request, course_id: str):
"""
Get an object containing course rerun.
**Example Request**
GET /api/contentstore/v1/course_rerun/{course_id}
**Response Values**
If the request is successful, an HTTP 200 "OK" response is returned.
The HTTP 200 response contains a single dict that contains keys that
are the course's rerun.
**Example Response**
```json
{
"allow_unicode_course_id": False,
"course_creator_status": "granted",
"number": "101",
"display_name": "new edx course",
"org": "edx",
"run": "2023",
}
```
"""
if not GlobalStaff().has_user(request.user):
self.permission_denied(request)
course_key = CourseKey.from_string(course_id)
with modulestore().bulk_operations(course_key):
course_block = modulestore().get_course(course_key)
course_rerun_context = get_course_rerun_context(course_key, course_block, request.user)
course_rerun_context.update({
'org': course_key.org,
'number': course_key.course,
'run': course_key.run,
})
serializer = CourseRerunSerializer(course_rerun_context)
return Response(serializer.data)

View File

@@ -0,0 +1,120 @@
""" API Views for course home """
import edx_api_doc_tools as apidocs
from django.conf import settings
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from openedx.core.lib.api.view_utils import view_auth_classes
from ....utils import get_home_context
from ..serializers import CourseHomeSerializer
@view_auth_classes(is_authenticated=True)
class HomePageView(APIView):
"""
View for getting all courses and libraries available to the logged in user.
"""
@apidocs.schema(
parameters=[
apidocs.string_parameter(
"org",
apidocs.ParameterLocation.QUERY,
description="Query param to filter by course org",
)],
responses={
200: CourseHomeSerializer,
401: "The requester is not authenticated.",
},
)
def get(self, request: Request):
"""
Get an object containing all courses and libraries on home page.
**Example Request**
GET /api/contentstore/v1/home
**Response Values**
If the request is successful, an HTTP 200 "OK" response is returned.
The HTTP 200 response contains a single dict that contains keys that
are the course's home.
**Example Response**
```json
{
"allow_course_reruns": true,
"allow_to_create_new_org": true,
"allow_unicode_course_id": false,
"allowed_organizations": [],
"archived_courses": [
{
"course_key": "course-v1:edX+P315+2T2023",
"display_name": "Quantum Entanglement",
"lms_link": "//localhost:18000/courses/course-v1:edX+P315+2T2023",
"number": "P315",
"org": "edX",
"rerun_link": "/course_rerun/course-v1:edX+P315+2T2023",
"run": "2T2023"
"url": "/course/course-v1:edX+P315+2T2023"
},
],
"can_create_organizations": true,
"course_creator_status": "granted",
"courses": [
{
"course_key": "course-v1:edX+E2E-101+course",
"display_name": "E2E Test Course",
"lms_link": "//localhost:18000/courses/course-v1:edX+E2E-101+course",
"number": "E2E-101",
"org": "edX",
"rerun_link": "/course_rerun/course-v1:edX+E2E-101+course",
"run": "course",
"url": "/course/course-v1:edX+E2E-101+course"
},
],
"in_process_course_actions": [],
"libraries": [
{
"display_name": "My First Library",
"library_key": "library-v1:new+CPSPR",
"url": "/library/library-v1:new+CPSPR",
"org": "new",
"number": "CPSPR",
"can_edit": true
}
],
"libraries_enabled": true,
"library_authoring_mfe_url": "//localhost:3001/course/course-v1:edX+P315+2T2023",
"optimization_enabled": true,
"redirect_to_library_authoring_mfe": false,
"request_course_creator_url": "/request_course_creator",
"rerun_creator_status": true,
"show_new_library_button": true,
"split_studio_home": false,
"studio_name": "Studio",
"studio_short_name": "Studio",
"studio_request_email": "",
"tech_support_email": "technical@example.com",
"platform_name": "Your Platform Name Here"
"user_is_active": true,
}
```
"""
home_context = get_home_context(request)
home_context.update({
'allow_to_create_new_org': settings.FEATURES.get('ENABLE_CREATOR_GROUP', True) and request.user.is_staff,
'studio_name': settings.STUDIO_NAME,
'studio_short_name': settings.STUDIO_SHORT_NAME,
'studio_request_email': settings.FEATURES.get('STUDIO_REQUEST_EMAIL', ''),
'tech_support_email': settings.TECH_SUPPORT_EMAIL,
'platform_name': settings.PLATFORM_NAME,
'user_is_active': request.user.is_active,
})
serializer = CourseHomeSerializer(home_context)
return Response(serializer.data)

View File

@@ -0,0 +1,36 @@
"""
Unit tests for course rerun.
"""
from django.urls import reverse
from rest_framework import status
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.rest_api.v1.mixins import PermissionAccessMixin
class CourseRerunViewTest(CourseTestCase, PermissionAccessMixin):
"""
Tests for CourseRerunView.
"""
def setUp(self):
super().setUp()
self.url = reverse(
"cms.djangoapps.contentstore:v1:course_rerun",
kwargs={"course_id": self.course.id},
)
def test_course_rerun_response(self):
"""Check successful response content"""
response = self.client.get(self.url)
expected_response = {
"allow_unicode_course_id": False,
"course_creator_status": "granted",
"display_name": self.course.display_name,
"number": self.course.id.course,
"org": self.course.id.org,
"run": self.course.id.run,
}
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)

View File

@@ -0,0 +1,87 @@
"""
Unit tests for home page view.
"""
import ddt
from django.conf import settings
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_switch
from rest_framework import status
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.views.course import ENABLE_GLOBAL_STAFF_OPTIMIZATION
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from xmodule.modulestore.tests.factories import CourseFactory
@ddt.ddt
class HomePageViewTest(CourseTestCase):
"""
Tests for HomePageView.
"""
def setUp(self):
super().setUp()
self.url = reverse("cms.djangoapps.contentstore:v1:home")
def test_home_page_response(self):
"""Check successful response content"""
response = self.client.get(self.url)
expected_response = {
"allow_course_reruns": True,
"allow_to_create_new_org": False,
"allow_unicode_course_id": False,
"allowed_organizations": [],
"archived_courses": [],
"can_create_organizations": True,
"course_creator_status": "granted",
"courses": [{
"course_key": str(self.course.id),
"display_name": self.course.display_name,
"lms_link": None,
"number": self.course.number,
"org": self.course.org,
"rerun_link": None,
"run": self.course.id.run,
"url": None,
}],
"in_process_course_actions": [],
"libraries": [],
"libraries_enabled": True,
"library_authoring_mfe_url": settings.LIBRARY_AUTHORING_MICROFRONTEND_URL,
"optimization_enabled": False,
"redirect_to_library_authoring_mfe": False,
"request_course_creator_url": "/request_course_creator",
"rerun_creator_status": True,
"show_new_library_button": True,
"split_studio_home": False,
"studio_name": settings.STUDIO_NAME,
"studio_short_name": settings.STUDIO_SHORT_NAME,
"studio_request_email": "",
"tech_support_email": "technical@example.com",
"platform_name": settings.PLATFORM_NAME,
"user_is_active": True,
}
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)
@override_waffle_switch(ENABLE_GLOBAL_STAFF_OPTIMIZATION, True)
def test_org_query_if_passed(self):
"""Test home page when org filter passed as a query param"""
foo_course = self.store.make_course_key('foo-org', 'bar-number', 'baz-run')
test_course = CourseFactory.create(
org=foo_course.org,
number=foo_course.course,
run=foo_course.run
)
CourseOverviewFactory.create(id=test_course.id, org='foo-org')
response = self.client.get(self.url, {"org": "foo-org"})
self.assertEqual(len(response.data['courses']), 1)
self.assertEqual(response.status_code, status.HTTP_200_OK)
@override_waffle_switch(ENABLE_GLOBAL_STAFF_OPTIMIZATION, True)
def test_org_query_if_empty(self):
"""Test home page with an empty org query param"""
response = self.client.get(self.url)
self.assertEqual(len(response.data['courses']), 0)
self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@@ -24,6 +24,7 @@ from pytz import UTC
from xblock.fields import Scope
from cms.djangoapps.contentstore.toggles import exam_setting_view_enabled
from common.djangoapps.course_action_state.models import CourseRerunUIStateManager
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.edxmako.services import MakoService
from common.djangoapps.student import auth
@@ -59,6 +60,7 @@ from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from openedx.features.content_type_gating.partitions import CONTENT_TYPE_GATING_SCHEME
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML
from cms.djangoapps.contentstore.toggles import (
split_library_view_on_dashboard,
use_new_advanced_settings_page,
use_new_course_outline_page,
use_new_export_page,
@@ -68,12 +70,13 @@ from cms.djangoapps.contentstore.toggles import (
use_new_home_page,
use_new_import_page,
use_new_schedule_details_page,
use_new_text_editor,
use_new_unit_page,
use_new_updates_page,
use_new_video_editor,
use_new_video_uploads_page,
use_new_custom_pages,
)
from cms.djangoapps.contentstore.toggles import use_new_text_editor, use_new_video_editor
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from xmodule.library_tools import LibraryToolsService
from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order
@@ -1387,6 +1390,109 @@ def get_help_urls():
return help_tokens
def get_home_context(request):
"""
Utils is used to get context of course grading.
It is used for both DRF and django views.
"""
from cms.djangoapps.contentstore.views.course import (
get_allowed_organizations,
get_allowed_organizations_for_libraries,
get_courses_accessible_to_user,
user_can_create_organizations,
_accessible_libraries_iter,
_get_course_creator_status,
_format_library_for_view,
_process_courses_list,
ENABLE_GLOBAL_STAFF_OPTIMIZATION,
)
from cms.djangoapps.contentstore.views.library import (
LIBRARY_AUTHORING_MICROFRONTEND_URL,
LIBRARIES_ENABLED,
should_redirect_to_library_authoring_mfe,
user_can_create_library,
)
optimization_enabled = GlobalStaff().has_user(request.user) and ENABLE_GLOBAL_STAFF_OPTIMIZATION.is_enabled()
org = request.GET.get('org', '') if optimization_enabled else None
courses_iter, in_process_course_actions = get_courses_accessible_to_user(request, org)
user = request.user
libraries = []
if not split_library_view_on_dashboard() and LIBRARIES_ENABLED:
libraries = _accessible_libraries_iter(request.user)
def format_in_process_course_view(uca):
"""
Return a dict of the data which the view requires for each unsucceeded course
"""
return {
'display_name': uca.display_name,
'course_key': str(uca.course_key),
'org': uca.course_key.org,
'number': uca.course_key.course,
'run': uca.course_key.run,
'is_failed': uca.state == CourseRerunUIStateManager.State.FAILED,
'is_in_progress': uca.state == CourseRerunUIStateManager.State.IN_PROGRESS,
'dismiss_link': reverse_course_url(
'course_notifications_handler',
uca.course_key,
kwargs={
'action_state_id': uca.id,
},
) if uca.state == CourseRerunUIStateManager.State.FAILED else ''
}
split_archived = settings.FEATURES.get('ENABLE_SEPARATE_ARCHIVED_COURSES', False)
active_courses, archived_courses = _process_courses_list(courses_iter, in_process_course_actions, split_archived)
in_process_course_actions = [format_in_process_course_view(uca) for uca in in_process_course_actions]
home_context = {
'courses': active_courses,
'split_studio_home': split_library_view_on_dashboard(),
'archived_courses': archived_courses,
'in_process_course_actions': in_process_course_actions,
'libraries_enabled': LIBRARIES_ENABLED,
'redirect_to_library_authoring_mfe': should_redirect_to_library_authoring_mfe(),
'library_authoring_mfe_url': LIBRARY_AUTHORING_MICROFRONTEND_URL,
'libraries': [_format_library_for_view(lib, request) for lib in libraries],
'show_new_library_button': user_can_create_library(user) and not should_redirect_to_library_authoring_mfe(),
'user': user,
'request_course_creator_url': reverse('request_course_creator'),
'course_creator_status': _get_course_creator_status(user),
'rerun_creator_status': GlobalStaff().has_user(user),
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False),
'allow_course_reruns': settings.FEATURES.get('ALLOW_COURSE_RERUNS', True),
'optimization_enabled': optimization_enabled,
'active_tab': 'courses',
'allowed_organizations': get_allowed_organizations(user),
'allowed_organizations_for_libraries': get_allowed_organizations_for_libraries(user),
'can_create_organizations': user_can_create_organizations(user),
}
return home_context
def get_course_rerun_context(course_key, course_block, user):
"""
Utils is used to get context of course rerun.
It is used for both DRF and django views.
"""
from cms.djangoapps.contentstore.views.course import _get_course_creator_status
course_rerun_context = {
'source_course_key': course_key,
'display_name': course_block.display_name,
'user': user,
'course_creator_status': _get_course_creator_status(user),
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False)
}
return course_rerun_context
class StudioPermissionsService:
"""
Service that can provide information about a user's permissions.

View File

@@ -31,6 +31,7 @@ from organizations.api import add_organization_course, ensure_organization
from organizations.exceptions import InvalidOrganizationException
from rest_framework.exceptions import ValidationError
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import create_xblock_info
from cms.djangoapps.course_creators.views import add_user_with_status_unrequested, get_course_creator_status
from cms.djangoapps.course_creators.models import CourseCreator
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
@@ -100,6 +101,7 @@ from ..utils import (
add_instructor,
get_course_settings,
get_course_grading,
get_home_context,
get_lms_link_for_item,
get_proctored_exam_settings_url,
get_course_outline_url,
@@ -108,6 +110,7 @@ from ..utils import (
get_advanced_settings_url,
get_grading_url,
get_schedule_details_url,
get_course_rerun_context,
initialize_permissions,
remove_all_instructors,
reverse_course_url,
@@ -118,15 +121,7 @@ from ..utils import (
update_course_discussions_settings,
)
from .component import ADVANCED_COMPONENT_TYPES
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import (
create_xblock_info,
)
from .library import (
LIBRARIES_ENABLED,
LIBRARY_AUTHORING_MICROFRONTEND_URL,
user_can_create_library,
should_redirect_to_library_authoring_mfe
)
from .library import LIBRARIES_ENABLED
log = logging.getLogger(__name__)
User = get_user_model()
@@ -334,13 +329,8 @@ def course_rerun_handler(request, course_key_string):
with modulestore().bulk_operations(course_key):
course_block = get_course_and_check_access(course_key, request.user, depth=3)
if request.method == 'GET':
return render_to_response('course-create-rerun.html', {
'source_course_key': course_key,
'display_name': course_block.display_name,
'user': request.user,
'course_creator_status': _get_course_creator_status(request.user),
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False)
})
course_rerun_context = get_course_rerun_context(course_key, course_block, request.user)
return render_to_response('course-create-rerun.html', course_rerun_context)
@login_required
@@ -551,62 +541,8 @@ def course_listing(request):
if use_new_home_page():
return redirect(get_studio_home_url())
optimization_enabled = GlobalStaff().has_user(request.user) and ENABLE_GLOBAL_STAFF_OPTIMIZATION.is_enabled()
org = request.GET.get('org', '') if optimization_enabled else None
courses_iter, in_process_course_actions = get_courses_accessible_to_user(request, org)
user = request.user
libraries = []
if not split_library_view_on_dashboard() and LIBRARIES_ENABLED:
libraries = _accessible_libraries_iter(request.user)
def format_in_process_course_view(uca):
"""
Return a dict of the data which the view requires for each unsucceeded course
"""
return {
'display_name': uca.display_name,
'course_key': str(uca.course_key),
'org': uca.course_key.org,
'number': uca.course_key.course,
'run': uca.course_key.run,
'is_failed': uca.state == CourseRerunUIStateManager.State.FAILED,
'is_in_progress': uca.state == CourseRerunUIStateManager.State.IN_PROGRESS,
'dismiss_link': reverse_course_url(
'course_notifications_handler',
uca.course_key,
kwargs={
'action_state_id': uca.id,
},
) if uca.state == CourseRerunUIStateManager.State.FAILED else ''
}
split_archived = settings.FEATURES.get('ENABLE_SEPARATE_ARCHIVED_COURSES', False)
active_courses, archived_courses = _process_courses_list(courses_iter, in_process_course_actions, split_archived)
in_process_course_actions = [format_in_process_course_view(uca) for uca in in_process_course_actions]
return render_to_response('index.html', {
'courses': active_courses,
'split_studio_home': split_library_view_on_dashboard(),
'archived_courses': archived_courses,
'in_process_course_actions': in_process_course_actions,
'libraries_enabled': LIBRARIES_ENABLED,
'redirect_to_library_authoring_mfe': should_redirect_to_library_authoring_mfe(),
'library_authoring_mfe_url': LIBRARY_AUTHORING_MICROFRONTEND_URL,
'libraries': [_format_library_for_view(lib, request) for lib in libraries],
'show_new_library_button': user_can_create_library(user) and not should_redirect_to_library_authoring_mfe(),
'user': user,
'request_course_creator_url': reverse('request_course_creator'),
'course_creator_status': _get_course_creator_status(user),
'rerun_creator_status': GlobalStaff().has_user(user),
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False),
'allow_course_reruns': settings.FEATURES.get('ALLOW_COURSE_RERUNS', True),
'optimization_enabled': optimization_enabled,
'active_tab': 'courses',
'allowed_organizations': get_allowed_organizations(user),
'allowed_organizations_for_libraries': get_allowed_organizations_for_libraries(user),
'can_create_organizations': user_can_create_organizations(user),
})
home_context = get_home_context(request)
return render_to_response('index.html', home_context)
@login_required