Skip to content

Commit

Permalink
Merge pull request openedx#34977 from openedx/hajorg/au-2070-youtube-…
Browse files Browse the repository at this point in the history
…videos-id

feat: get youtube videos edx_video_id for a course
  • Loading branch information
hajorg committed Jun 21, 2024
2 parents 2bdc341 + cf6c55a commit ad368de
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 0 deletions.
30 changes: 30 additions & 0 deletions cms/djangoapps/contentstore/video_storage_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
update_video_status
)
from fs.osfs import OSFS
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from path import Path as path
from pytz import UTC
Expand Down Expand Up @@ -960,3 +961,32 @@ def _update_pagination_context(request):

request.session['VIDEOS_PER_PAGE'] = videos_per_page
return JsonResponse()


def get_course_youtube_edx_video_ids(course_id):
"""
Get a list of youtube edx_video_ids
"""
error_msg = "Invalid course_key: '%s'." % course_id
try:
course_key = CourseKey.from_string(course_id)
course = modulestore().get_course(course_key)
except InvalidKeyError:
return JsonResponse({'error': error_msg}, status=500)
blocks = []
block_yt_field = 'youtube_id_1_0'
block_edx_id_field = 'edx_video_id'
if hasattr(course, 'get_children'):
for section in course.get_children():
for subsection in section.get_children():
for vertical in subsection.get_children():
for block in vertical.get_children():
blocks.append(block)

edx_video_ids = []
for block in blocks:
if hasattr(block, block_yt_field) and getattr(block, block_yt_field):
if getattr(block, block_edx_id_field):
edx_video_ids.append(getattr(block, block_edx_id_field))

return JsonResponse({'edx_video_ids': edx_video_ids}, status=200)
82 changes: 82 additions & 0 deletions cms/djangoapps/contentstore/views/tests/test_videos.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from unittest.mock import Mock, patch

import dateutil.parser
from common.djangoapps.student.tests.factories import UserFactory
import ddt
import pytz
from django.test import TestCase
Expand All @@ -37,6 +38,8 @@
ENABLE_DEVSTACK_VIDEO_UPLOADS,
)
from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order

from ..videos import (
Expand Down Expand Up @@ -1662,3 +1665,82 @@ def test_storage_bucket(self):

self.assertIn("https://vem_test_bucket.s3.amazonaws.com:443/test_root/", upload_url)
self.assertIn(edx_video_id, upload_url)


class CourseYoutubeEdxVideoIds(ModuleStoreTestCase):
"""
This test checks youtube videos in a course
"""
VIEW_NAME = 'youtube_edx_video_ids'

def setUp(self):
super().setUp()
self.course = CourseFactory.create()
self.course_with_no_youtube_videos = CourseFactory.create()
self.store = modulestore()
self.user = UserFactory()
self.client.login(username=self.user.username, password='Password1234')

def get_url_for_course_key(self, course_key, kwargs=None):
"""Return video handler URL for the given course"""
return reverse_course_url(self.VIEW_NAME, course_key, kwargs) # lint-amnesty, pylint: disable=no-member

def test_course_with_youtube_videos(self):
course_key = self.course.id

with self.store.bulk_operations(course_key):
chapter_loc = self.store.create_child(
self.user.id, self.course.location, 'chapter', 'test_chapter'
).location
seq_loc = self.store.create_child(
self.user.id, chapter_loc, 'sequential', 'test_seq'
).location
vert_loc = self.store.create_child(self.user.id, seq_loc, 'vertical', 'test_vert').location
self.store.create_child(
self.user.id,
vert_loc,
'problem',
'test_problem',
fields={"data": "<problem>Test</problem>"}
)
self.store.create_child(
self.user.id, vert_loc, 'video', fields={
"youtube_is_available": False,
"name": "sample_video",
"edx_video_id": "youtube_193_84709099",
}
)

response = self.client.get(self.get_url_for_course_key(course_key))
self.assertEqual(response.status_code, 200)

edx_video_ids = json.loads(response.content.decode('utf-8'))['edx_video_ids']
self.assertEqual(len(edx_video_ids), 1)

def test_course_with_no_youtube_videos(self):
course_key = self.course_with_no_youtube_videos.id

with self.store.bulk_operations(course_key):
chapter_loc = self.store.create_child(
self.user.id, self.course_with_no_youtube_videos.location, 'chapter', 'test_chapter'
).location
seq_loc = self.store.create_child(
self.user.id, chapter_loc, 'sequential', 'test_seq'
).location
vert_loc = self.store.create_child(self.user.id, seq_loc, 'vertical', 'test_vert').location
self.store.create_child(
self.user.id, vert_loc, 'problem', 'test_problem', fields={"data": "<problem>Test</problem>"}
)
self.store.create_child(
self.user.id, vert_loc, 'video', fields={
"youtube_id_1_0": None,
"name": "sample_video",
"edx_video_id": "no_youtube_193_84709099",
}
)

response = self.client.get(self.get_url_for_course_key(course_key))

edx_video_ids = json.loads(response.content.decode('utf-8'))['edx_video_ids']
self.assertEqual(response.status_code, 200)
self.assertEqual(len(edx_video_ids), 0)
17 changes: 17 additions & 0 deletions cms/djangoapps/contentstore/views/videos.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
storage_service_key as storage_service_key_source_function,
send_video_status_update as send_video_status_update_source_function,
is_status_update_request as is_status_update_request_source_function,
get_course_youtube_edx_video_ids,
)

from common.djangoapps.util.json_request import expect_json
Expand All @@ -41,6 +42,7 @@
'get_video_features',
'transcript_preferences_handler',
'generate_video_upload_link_handler',
'get_course_youtube_edx_videos_ids',
]

LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -236,3 +238,18 @@ def is_status_update_request(request_data):
Exposes helper method without breaking existing bindings/dependencies
"""
return is_status_update_request_source_function(request_data)


@api_view(['GET'])
@view_auth_classes()
@require_GET
def get_course_youtube_edx_videos_ids(request, course_key_string):
"""
Get an object containing course videos.
**Example Request**
GET /api/contentstore/v1/videos/youtube_ids{course_id}
**Response Values**
If the request is successful, an HTTP 200 "OK" response is returned.
The HTTP 200 response contains a list of youtube edx_video_ids for a given course.
"""
return get_course_youtube_edx_video_ids(course_key_string)
2 changes: 2 additions & 0 deletions cms/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@
path('api/val/v0/', include('edxval.urls')),
path('api/tasks/v0/', include('user_tasks.urls')),
path('accessibility', contentstore_views.accessibility, name='accessibility'),
re_path(fr'api/youtube/courses/{COURSELIKE_KEY_PATTERN}/edx-video-ids$',
contentstore_views.get_course_youtube_edx_videos_ids, name='youtube_edx_video_ids'),
]

if not settings.DISABLE_DEPRECATED_SIGNIN_URL:
Expand Down

0 comments on commit ad368de

Please sign in to comment.