From cf6c55a9aa10de43f0d244fa12c71187c66ad71b Mon Sep 17 00:00:00 2001 From: hajorg Date: Wed, 12 Jun 2024 14:55:12 +0100 Subject: [PATCH] feat: get youtube videos edx_video_id for a course --- .../contentstore/video_storage_handlers.py | 30 +++++++ .../contentstore/views/tests/test_videos.py | 82 +++++++++++++++++++ cms/djangoapps/contentstore/views/videos.py | 17 ++++ cms/urls.py | 2 + 4 files changed, 131 insertions(+) diff --git a/cms/djangoapps/contentstore/video_storage_handlers.py b/cms/djangoapps/contentstore/video_storage_handlers.py index b84d7f0f7c31..6827b295b0a8 100644 --- a/cms/djangoapps/contentstore/video_storage_handlers.py +++ b/cms/djangoapps/contentstore/video_storage_handlers.py @@ -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 @@ -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) diff --git a/cms/djangoapps/contentstore/views/tests/test_videos.py b/cms/djangoapps/contentstore/views/tests/test_videos.py index 5d96e6797d9d..2d17229f59c1 100644 --- a/cms/djangoapps/contentstore/views/tests/test_videos.py +++ b/cms/djangoapps/contentstore/views/tests/test_videos.py @@ -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 @@ -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 ( @@ -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": "Test"} + ) + 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": "Test"} + ) + 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) diff --git a/cms/djangoapps/contentstore/views/videos.py b/cms/djangoapps/contentstore/views/videos.py index e1bdfa1cde1c..2eac141b9c9e 100644 --- a/cms/djangoapps/contentstore/views/videos.py +++ b/cms/djangoapps/contentstore/views/videos.py @@ -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 @@ -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__) @@ -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) diff --git a/cms/urls.py b/cms/urls.py index e21d07083eea..1c5f191b2728 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -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: