Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .course_waffle_flags import CourseWaffleFlagsSerializer # noqa: F401
from .grading import CourseGradingModelSerializer, CourseGradingSerializer # noqa: F401
from .group_configurations import CourseGroupConfigurationsSerializer # noqa: F401
from .home import CourseHomeTabSerializer, LibraryTabSerializer, StudioHomeSerializer # noqa: F401
from .home import LibraryTabSerializer, StudioHomeSerializer # noqa: F401
from .proctoring import (
LimitedProctoredExamSettingsSerializer, # noqa: F401
ProctoredExamConfigurationSerializer, # noqa: F401
Expand Down
6 changes: 0 additions & 6 deletions cms/djangoapps/contentstore/rest_api/v1/serializers/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ class LibraryViewSerializer(serializers.Serializer):
migrated_to_collection_title = serializers.CharField(required=False)


class CourseHomeTabSerializer(serializers.Serializer):
archived_courses = CourseCommonSerializer(required=False, many=True)
courses = CourseCommonSerializer(required=False, many=True)
in_process_course_actions = UnsucceededCourseSerializer(many=True, required=False, allow_null=True)


class LibraryTabSerializer(serializers.Serializer):
libraries = LibraryViewSerializer(many=True, required=False, allow_null=True)

Expand Down
5 changes: 0 additions & 5 deletions cms/djangoapps/contentstore/rest_api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
CourseVideosView,
CourseWaffleFlagsView,
HelpUrlsView,
HomePageCoursesView,
HomePageLibrariesView,
HomePageView,
ProctoredExamSettingsView,
Comment thread
irfanuddinahmad marked this conversation as resolved.
Expand All @@ -40,10 +39,6 @@
HomePageView.as_view(),
name="home"
),
path(
'home/courses',
HomePageCoursesView.as_view(),
name="courses"),
path(
'home/libraries',
HomePageLibrariesView.as_view(),
Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/rest_api/v1/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .grading import CourseGradingView # noqa: F401
from .group_configurations import CourseGroupConfigurationsView # noqa: F401
from .help_urls import HelpUrlsView # noqa: F401
from .home import HomePageCoursesView, HomePageLibrariesView, HomePageView # noqa: F401
from .home import HomePageLibrariesView, HomePageView # noqa: F401
from .proctoring import ProctoredExamSettingsView, ProctoringErrorsView # noqa: F401
from .settings import CourseSettingsView # noqa: F401
from .textbooks import CourseTextbooksView # noqa: F401
Expand Down
79 changes: 2 additions & 77 deletions cms/djangoapps/contentstore/rest_api/v1/views/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

from openedx.core.lib.api.view_utils import view_auth_classes

from ....utils import get_course_context, get_home_context, get_library_context
from ..serializers import CourseHomeTabSerializer, LibraryTabSerializer, StudioHomeSerializer
from ....utils import get_home_context, get_library_context
from ..serializers import LibraryTabSerializer, StudioHomeSerializer


@view_auth_classes(is_authenticated=True)
Expand Down Expand Up @@ -99,81 +99,6 @@ def get(self, request: Request):
return Response(serializer.data)


@view_auth_classes(is_authenticated=True)
class HomePageCoursesView(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: CourseHomeTabSerializer,
401: "The requester is not authenticated.",
},
)
def get(self, request: Request):
"""
Get an object containing all courses.

**Example Request**

GET /api/contentstore/v1/home/courses

**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
{
"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"
},
],
"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": [],
}
```
"""

active_courses, archived_courses, in_process_course_actions = get_course_context(request)
courses_context = {
"courses": active_courses,
"archived_courses": archived_courses,
"in_process_course_actions": in_process_course_actions,
}
serializer = CourseHomeTabSerializer(courses_context)
return Response(serializer.data)


@view_auth_classes(is_authenticated=True)
class HomePageLibrariesView(APIView):
"""
Expand Down
151 changes: 1 addition & 150 deletions cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
"""
Unit tests for home page view.
"""
from collections import OrderedDict
from datetime import datetime, timedelta

import ddt
import pytz
from django.conf import settings
from django.test import override_settings
from django.urls import reverse
Expand All @@ -18,14 +14,13 @@
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.modulestore_migrator import api as migrator_api
from cms.djangoapps.modulestore_migrator.data import CompositionLevel, RepeatHandlingStrategy
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from openedx.core.djangoapps.content_libraries import api as lib_api


@ddt.ddt
class HomePageViewTest(CourseTestCase):
"""
Tests for HomePageCoursesView.
Tests for HomePageView.
"""

def setUp(self):
Expand Down Expand Up @@ -100,150 +95,6 @@ def test_taxonomy_list_link(self):
)


@ddt.ddt
class HomePageCoursesViewTest(CourseTestCase):
"""
Tests for HomePageView.
"""

def setUp(self):
super().setUp()
self.url = reverse("cms.djangoapps.contentstore:v1:courses")
self.course_overview = CourseOverviewFactory.create(
id=self.course.id,
org=self.course.org,
display_name=self.course.display_name,
display_number_with_default=self.course.number,
)
self.non_staff_client, _ = self.create_non_staff_authed_user_client()

def test_home_page_response(self):
"""Check successful response content"""
response = self.client.get(self.url)
course_id = str(self.course.id)

expected_response = {
"archived_courses": [],
"courses": [{
"course_key": course_id,
"display_name": self.course.display_name,
"lms_link": f'{settings.LMS_ROOT_URL}/courses/{course_id}/jump_to/{self.course.location}',
"number": self.course.number,
"org": self.course.org,
"rerun_link": f'/course_rerun/{course_id}',
"run": self.course.id.run,
"url": f'/course/{course_id}',
}],
"in_process_course_actions": [],
}

self.assertEqual(response.status_code, status.HTTP_200_OK) # noqa: PT009
self.assertDictEqual(expected_response, response.data) # noqa: PT009

def test_home_page_response_with_api_v2(self):
"""Check successful response content with api v2 modifications.

When the feature flag is enabled, the courses are exclusively fetched from the CourseOverview model, so
the values in the courses' list are OrderedDicts instead of the default dictionaries.
"""
course_id = str(self.course.id)
expected_response = {
"archived_courses": [],
"courses": [
OrderedDict([
("course_key", course_id),
("display_name", self.course.display_name),
("lms_link", f'{settings.LMS_ROOT_URL}/courses/{course_id}/jump_to/{self.course.location}'),
("number", self.course.number),
("org", self.course.org),
("rerun_link", f'/course_rerun/{course_id}'),
("run", self.course.id.run),
("url", f'/course/{course_id}'),
]),
],
"in_process_course_actions": [],
}

response = self.client.get(self.url)

self.assertEqual(response.status_code, status.HTTP_200_OK) # noqa: PT009
self.assertDictEqual(expected_response, response.data) # noqa: PT009

@ddt.data(
("active_only", "true", 2, 0),
("archived_only", "true", 0, 1),
("search", "sample", 1, 0),
("search", "demo", 0, 1),
("order", "org", 2, 1),
("order", "display_name", 2, 1),
("order", "number", 2, 1),
("order", "run", 2, 1)
)
@ddt.unpack
def test_filter_and_ordering_courses(
self,
filter_key,
filter_value,
expected_active_length,
expected_archived_length
):
"""Test home page with org filter and ordering for a staff user.

The test creates an active/archived course, and then filters/orders them using the query parameters.
"""
archived_course_key = self.store.make_course_key("demo-org", "demo-number", "demo-run")
CourseOverviewFactory.create(
display_name="Course (Demo)",
id=archived_course_key,
org=archived_course_key.org,
end=(datetime.now() - timedelta(days=365)).replace(tzinfo=pytz.UTC),
)
active_course_key = self.store.make_course_key("sample-org", "sample-number", "sample-run")
CourseOverviewFactory.create(
display_name="Course (Sample)",
id=active_course_key,
org=active_course_key.org,
)

response = self.client.get(self.url, {filter_key: filter_value})

self.assertEqual(response.status_code, status.HTTP_200_OK) # noqa: PT009
self.assertEqual(len(response.data["archived_courses"]), expected_archived_length) # noqa: PT009
self.assertEqual(len(response.data["courses"]), expected_active_length) # noqa: PT009

@ddt.data(
("active_only", "true"),
("archived_only", "true"),
("search", "sample"),
("order", "org"),
)
@ddt.unpack
def test_filter_and_ordering_no_courses_staff(self, filter_key, filter_value):
"""Test home page with org filter and ordering when there are no courses for a staff user."""
self.course_overview.delete()

response = self.client.get(self.url, {filter_key: filter_value})

self.assertEqual(len(response.data["courses"]), 0) # noqa: PT009
self.assertEqual(response.status_code, status.HTTP_200_OK) # noqa: PT009

@ddt.data(
("active_only", "true"),
("archived_only", "true"),
("search", "sample"),
("order", "org"),
)
@ddt.unpack
def test_home_page_response_no_courses_non_staff(self, filter_key, filter_value):
"""Test home page with org filter and ordering when there are no courses for a non-staff user."""
self.course_overview.delete()

response = self.non_staff_client.get(self.url, {filter_key: filter_value})

self.assertEqual(len(response.data["courses"]), 0) # noqa: PT009
self.assertEqual(response.status_code, status.HTTP_200_OK) # noqa: PT009


@ddt.ddt
class HomePageLibrariesViewTest(LibraryTestCase):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class HomePageCoursesViewV2Test(CourseTestCase):
def setUp(self):
super().setUp()
self.api_v2_url = reverse("cms.djangoapps.contentstore:v2:courses")
self.api_v1_url = reverse("cms.djangoapps.contentstore:v1:courses")
self.active_course = CourseOverviewFactory.create(
id=self.course.id,
org=self.course.org,
Expand Down
Loading