Skip to content

Commit cf6c55a

Browse files
committed
feat: get youtube videos edx_video_id for a course
1 parent 2bdc341 commit cf6c55a

4 files changed

Lines changed: 131 additions & 0 deletions

File tree

cms/djangoapps/contentstore/video_storage_handlers.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
update_video_status
4343
)
4444
from fs.osfs import OSFS
45+
from opaque_keys import InvalidKeyError
4546
from opaque_keys.edx.keys import CourseKey
4647
from path import Path as path
4748
from pytz import UTC
@@ -960,3 +961,32 @@ def _update_pagination_context(request):
960961

961962
request.session['VIDEOS_PER_PAGE'] = videos_per_page
962963
return JsonResponse()
964+
965+
966+
def get_course_youtube_edx_video_ids(course_id):
967+
"""
968+
Get a list of youtube edx_video_ids
969+
"""
970+
error_msg = "Invalid course_key: '%s'." % course_id
971+
try:
972+
course_key = CourseKey.from_string(course_id)
973+
course = modulestore().get_course(course_key)
974+
except InvalidKeyError:
975+
return JsonResponse({'error': error_msg}, status=500)
976+
blocks = []
977+
block_yt_field = 'youtube_id_1_0'
978+
block_edx_id_field = 'edx_video_id'
979+
if hasattr(course, 'get_children'):
980+
for section in course.get_children():
981+
for subsection in section.get_children():
982+
for vertical in subsection.get_children():
983+
for block in vertical.get_children():
984+
blocks.append(block)
985+
986+
edx_video_ids = []
987+
for block in blocks:
988+
if hasattr(block, block_yt_field) and getattr(block, block_yt_field):
989+
if getattr(block, block_edx_id_field):
990+
edx_video_ids.append(getattr(block, block_edx_id_field))
991+
992+
return JsonResponse({'edx_video_ids': edx_video_ids}, status=200)

cms/djangoapps/contentstore/views/tests/test_videos.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from unittest.mock import Mock, patch
1313

1414
import dateutil.parser
15+
from common.djangoapps.student.tests.factories import UserFactory
1516
import ddt
1617
import pytz
1718
from django.test import TestCase
@@ -37,6 +38,8 @@
3738
ENABLE_DEVSTACK_VIDEO_UPLOADS,
3839
)
3940
from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel
41+
from xmodule.modulestore.django import modulestore
42+
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
4043
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
4144

4245
from ..videos import (
@@ -1662,3 +1665,82 @@ def test_storage_bucket(self):
16621665

16631666
self.assertIn("https://vem_test_bucket.s3.amazonaws.com:443/test_root/", upload_url)
16641667
self.assertIn(edx_video_id, upload_url)
1668+
1669+
1670+
class CourseYoutubeEdxVideoIds(ModuleStoreTestCase):
1671+
"""
1672+
This test checks youtube videos in a course
1673+
"""
1674+
VIEW_NAME = 'youtube_edx_video_ids'
1675+
1676+
def setUp(self):
1677+
super().setUp()
1678+
self.course = CourseFactory.create()
1679+
self.course_with_no_youtube_videos = CourseFactory.create()
1680+
self.store = modulestore()
1681+
self.user = UserFactory()
1682+
self.client.login(username=self.user.username, password='Password1234')
1683+
1684+
def get_url_for_course_key(self, course_key, kwargs=None):
1685+
"""Return video handler URL for the given course"""
1686+
return reverse_course_url(self.VIEW_NAME, course_key, kwargs) # lint-amnesty, pylint: disable=no-member
1687+
1688+
def test_course_with_youtube_videos(self):
1689+
course_key = self.course.id
1690+
1691+
with self.store.bulk_operations(course_key):
1692+
chapter_loc = self.store.create_child(
1693+
self.user.id, self.course.location, 'chapter', 'test_chapter'
1694+
).location
1695+
seq_loc = self.store.create_child(
1696+
self.user.id, chapter_loc, 'sequential', 'test_seq'
1697+
).location
1698+
vert_loc = self.store.create_child(self.user.id, seq_loc, 'vertical', 'test_vert').location
1699+
self.store.create_child(
1700+
self.user.id,
1701+
vert_loc,
1702+
'problem',
1703+
'test_problem',
1704+
fields={"data": "<problem>Test</problem>"}
1705+
)
1706+
self.store.create_child(
1707+
self.user.id, vert_loc, 'video', fields={
1708+
"youtube_is_available": False,
1709+
"name": "sample_video",
1710+
"edx_video_id": "youtube_193_84709099",
1711+
}
1712+
)
1713+
1714+
response = self.client.get(self.get_url_for_course_key(course_key))
1715+
self.assertEqual(response.status_code, 200)
1716+
1717+
edx_video_ids = json.loads(response.content.decode('utf-8'))['edx_video_ids']
1718+
self.assertEqual(len(edx_video_ids), 1)
1719+
1720+
def test_course_with_no_youtube_videos(self):
1721+
course_key = self.course_with_no_youtube_videos.id
1722+
1723+
with self.store.bulk_operations(course_key):
1724+
chapter_loc = self.store.create_child(
1725+
self.user.id, self.course_with_no_youtube_videos.location, 'chapter', 'test_chapter'
1726+
).location
1727+
seq_loc = self.store.create_child(
1728+
self.user.id, chapter_loc, 'sequential', 'test_seq'
1729+
).location
1730+
vert_loc = self.store.create_child(self.user.id, seq_loc, 'vertical', 'test_vert').location
1731+
self.store.create_child(
1732+
self.user.id, vert_loc, 'problem', 'test_problem', fields={"data": "<problem>Test</problem>"}
1733+
)
1734+
self.store.create_child(
1735+
self.user.id, vert_loc, 'video', fields={
1736+
"youtube_id_1_0": None,
1737+
"name": "sample_video",
1738+
"edx_video_id": "no_youtube_193_84709099",
1739+
}
1740+
)
1741+
1742+
response = self.client.get(self.get_url_for_course_key(course_key))
1743+
1744+
edx_video_ids = json.loads(response.content.decode('utf-8'))['edx_video_ids']
1745+
self.assertEqual(response.status_code, 200)
1746+
self.assertEqual(len(edx_video_ids), 0)

cms/djangoapps/contentstore/views/videos.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
storage_service_key as storage_service_key_source_function,
2828
send_video_status_update as send_video_status_update_source_function,
2929
is_status_update_request as is_status_update_request_source_function,
30+
get_course_youtube_edx_video_ids,
3031
)
3132

3233
from common.djangoapps.util.json_request import expect_json
@@ -41,6 +42,7 @@
4142
'get_video_features',
4243
'transcript_preferences_handler',
4344
'generate_video_upload_link_handler',
45+
'get_course_youtube_edx_videos_ids',
4446
]
4547

4648
LOGGER = logging.getLogger(__name__)
@@ -236,3 +238,18 @@ def is_status_update_request(request_data):
236238
Exposes helper method without breaking existing bindings/dependencies
237239
"""
238240
return is_status_update_request_source_function(request_data)
241+
242+
243+
@api_view(['GET'])
244+
@view_auth_classes()
245+
@require_GET
246+
def get_course_youtube_edx_videos_ids(request, course_key_string):
247+
"""
248+
Get an object containing course videos.
249+
**Example Request**
250+
GET /api/contentstore/v1/videos/youtube_ids{course_id}
251+
**Response Values**
252+
If the request is successful, an HTTP 200 "OK" response is returned.
253+
The HTTP 200 response contains a list of youtube edx_video_ids for a given course.
254+
"""
255+
return get_course_youtube_edx_video_ids(course_key_string)

cms/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@
194194
path('api/val/v0/', include('edxval.urls')),
195195
path('api/tasks/v0/', include('user_tasks.urls')),
196196
path('accessibility', contentstore_views.accessibility, name='accessibility'),
197+
re_path(fr'api/youtube/courses/{COURSELIKE_KEY_PATTERN}/edx-video-ids$',
198+
contentstore_views.get_course_youtube_edx_videos_ids, name='youtube_edx_video_ids'),
197199
]
198200

199201
if not settings.DISABLE_DEPRECATED_SIGNIN_URL:

0 commit comments

Comments
 (0)