From c02c009c5c009a7ecc6e37cd26992d628e27ef8d Mon Sep 17 00:00:00 2001 From: farhan Date: Thu, 8 Jan 2026 12:17:50 +0500 Subject: [PATCH 1/6] chore: enable video block --- openedx/envs/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openedx/envs/common.py b/openedx/envs/common.py index 59313c8382fa..70b939771b49 100644 --- a/openedx/envs/common.py +++ b/openedx/envs/common.py @@ -2114,7 +2114,7 @@ def add_optional_apps(optional_apps, installed_apps): # .. toggle_warning: Not production-ready until relevant subtask https://github.com/openedx/edx-platform/issues/34827 is done. # .. toggle_creation_date: 2024-11-10 # .. toggle_target_removal_date: 2025-06-01 -USE_EXTRACTED_VIDEO_BLOCK = False +USE_EXTRACTED_VIDEO_BLOCK = True ############################## Marketing Site ############################## From f940ea8e7d9a1805c54481b7f5dabd4ff12416b6 Mon Sep 17 00:00:00 2001 From: farhan Date: Fri, 9 Jan 2026 19:49:48 +0500 Subject: [PATCH 2/6] fix: fix test cases for extracted video xblock --- .../courseware/tests/test_video_handlers.py | 29 ++++++++++++++----- .../courseware/tests/test_video_mongo.py | 19 ++++++++---- openedx/envs/common.py | 1 + xmodule/modulestore/tests/test_api.py | 5 +++- xmodule/tests/test_video.py | 24 ++++++++++----- 5 files changed, 58 insertions(+), 20 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_video_handlers.py b/lms/djangoapps/courseware/tests/test_video_handlers.py index 6353e2aec79f..d0a5de4a91f7 100644 --- a/lms/djangoapps/courseware/tests/test_video_handlers.py +++ b/lms/djangoapps/courseware/tests/test_video_handlers.py @@ -10,6 +10,7 @@ import pytest import ddt import freezegun +from django.conf import settings from django.core.files.base import ContentFile from django.utils.timezone import now from django.test import RequestFactory @@ -48,6 +49,13 @@ Привіт, edX вітає вас. """) +if settings.USE_EXTRACTED_VIDEO_BLOCK: + path_video_handlers = 'xblocks_contrib.video.video_handlers' + path_transcripts_utils = 'xblocks_contrib.video.video_transcripts_utils' +else: + path_video_handlers = 'xmodule.video_block.video_handlers' + path_transcripts_utils = 'openedx.core.djangoapps.video_config.transcripts_utils' + def _create_srt_file(content=None): """ @@ -206,10 +214,17 @@ def test_handle_ajax(self): {'demoo�': 'sample'} ] for sample in data: - response = self.clients[self.users[0].username].post( - self.get_url('save_user_state'), - sample, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + if settings.USE_EXTRACTED_VIDEO_BLOCK: + handler_url = self.get_url('save_user_state', handler_name='ajax_handler') + response = self.clients[self.users[0].username].post( + handler_url, + sample, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + else: + response = self.clients[self.users[0].username].post( + self.get_url('save_user_state'), + sample, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') assert response.status_code == 200 assert self.block.speed is None @@ -320,7 +335,7 @@ def test_multiple_available_translations(self, mock_get_video_transcript_content assert sorted(json.loads(response.body.decode('utf-8'))) == sorted(['en', 'uk']) @patch('openedx.core.djangoapps.video_config.transcripts_utils.get_video_transcript_content') - @patch('openedx.core.djangoapps.video_config.transcripts_utils.get_available_transcript_languages') + @patch(f'{path_transcripts_utils}.get_available_transcript_languages') @ddt.data( ( ['en', 'uk', 'ro'], @@ -504,7 +519,7 @@ def test_download_transcript_not_exist(self): assert response.status == '404 Not Found' @patch( - 'xmodule.video_block.video_handlers.get_transcript', + f'{path_video_handlers}.get_transcript', return_value=('Subs!', 'test_filename.srt', 'application/x-subrip; charset=utf-8') ) def test_download_srt_exist(self, __): @@ -515,7 +530,7 @@ def test_download_srt_exist(self, __): assert response.headers['Content-Language'] == 'en' @patch( - 'xmodule.video_block.video_handlers.get_transcript', + f'{path_video_handlers}.get_transcript', return_value=('Subs!', 'txt', 'text/plain; charset=utf-8') ) def test_download_txt_exist(self, __): diff --git a/lms/djangoapps/courseware/tests/test_video_mongo.py b/lms/djangoapps/courseware/tests/test_video_mongo.py index f4d2847cff0f..6be2fe84bbff 100644 --- a/lms/djangoapps/courseware/tests/test_video_mongo.py +++ b/lms/djangoapps/courseware/tests/test_video_mongo.py @@ -49,7 +49,7 @@ from xmodule.tests.helpers import mock_render_template, override_descriptor_system # pylint: disable=unused-import from xmodule.tests.test_import import DummyModuleStoreRuntime from xmodule.tests.test_video import VideoBlockTestBase -from xmodule.video_block import VideoBlock, bumper_utils, video_utils +from xmodule.video_block import VideoBlock, video_utils from openedx.core.djangoapps.video_config.transcripts_utils import Transcript, save_to_store, subs_filename from xmodule.video_block.video_block import EXPORT_IMPORT_COURSE_DIR, EXPORT_IMPORT_STATIC_DIR from xmodule.x_module import PUBLIC_VIEW, STUDENT_VIEW @@ -66,6 +66,15 @@ from .test_video_xml import SOURCE_XML, PUBLIC_SOURCE_XML from common.test.utils import assert_dict_contains_subset +if settings.USE_EXTRACTED_VIDEO_BLOCK: + from xblocks_contrib.video import bumper_utils + bumper_utils_path = 'xblocks_contrib.video.bumper_utils' + video_block_path = 'xblocks_contrib.video.video' +else: + from xmodule.video_block import bumper_utils + bumper_utils_path = 'xmodule.video_block.bumper_utils' + video_block_path = 'xmodule.video_block.video_block' + TRANSCRIPT_FILE_SRT_DATA = """ 1 00:00:14,370 --> 00:00:16,530 @@ -930,7 +939,7 @@ def helper_get_html_with_edx_video_id(self, data): # pylint: disable=invalid-name @patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template) - @patch('xmodule.video_block.video_block.rewrite_video_url') + @patch(f'{video_block_path}.rewrite_video_url') def test_get_html_cdn_source(self, mocked_get_video, mock_render_django_template): """ Test if sources got from CDN @@ -2323,7 +2332,7 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests # Use temporary FEATURES in this test without affecting the original FEATURES = dict(settings.FEATURES) - @patch('xmodule.video_block.bumper_utils.get_bumper_settings') + @patch(f'{bumper_utils_path}.get_bumper_settings') def test_is_bumper_enabled(self, get_bumper_settings): """ Check that bumper is (not)shown if ENABLE_VIDEO_BUMPER is (False)True @@ -2348,8 +2357,8 @@ def test_is_bumper_enabled(self, get_bumper_settings): assert not bumper_utils.is_bumper_enabled(self.block) @patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template) - @patch('xmodule.video_block.bumper_utils.is_bumper_enabled') - @patch('xmodule.video_block.bumper_utils.get_bumper_settings') + @patch(f'{bumper_utils_path}.is_bumper_enabled') + @patch(f'{bumper_utils_path}.get_bumper_settings') @patch('edxval.api.get_urls_for_profiles') def test_bumper_metadata( self, get_url_for_profiles, get_bumper_settings, is_bumper_enabled, mock_render_django_template diff --git a/openedx/envs/common.py b/openedx/envs/common.py index 70b939771b49..64a9e659c3cd 100644 --- a/openedx/envs/common.py +++ b/openedx/envs/common.py @@ -2116,6 +2116,7 @@ def add_optional_apps(optional_apps, installed_apps): # .. toggle_target_removal_date: 2025-06-01 USE_EXTRACTED_VIDEO_BLOCK = True + ############################## Marketing Site ############################## EDXMKTG_LOGGED_IN_COOKIE_NAME = 'edxloggedin' diff --git a/xmodule/modulestore/tests/test_api.py b/xmodule/modulestore/tests/test_api.py index 03dd79d4ffa4..61aa4ef40177 100644 --- a/xmodule/modulestore/tests/test_api.py +++ b/xmodule/modulestore/tests/test_api.py @@ -26,7 +26,10 @@ def test_get_root_module_name(): Ensure the module name function works with different xblocks. """ assert get_root_module_name(LtiConsumerXBlock) == 'lti_consumer' - assert get_root_module_name(VideoBlock) == 'xmodule' + if settings.USE_EXTRACTED_VIDEO_BLOCK: + assert get_root_module_name(VideoBlock) == 'xblocks_contrib' + else: + assert get_root_module_name(VideoBlock) == 'xmodule' assert get_root_module_name(DoneXBlock) == 'done' diff --git a/xmodule/tests/test_video.py b/xmodule/tests/test_video.py index aa03c15851cb..42f2d176cf7d 100644 --- a/xmodule/tests/test_video.py +++ b/xmodule/tests/test_video.py @@ -82,6 +82,17 @@ ["ur", "Urdu"] ) +if settings.USE_EXTRACTED_VIDEO_BLOCK: + path_video_block = 'xblocks_contrib.video.video' + get_available_transcript_languages_path = ( + 'xblocks_contrib.video.video_transcripts_utils.get_available_transcript_languages' + ) +else: + path_video_block = 'xmodule.video_block.video_block' + get_available_transcript_languages_path = ( + 'openedx.core.djangoapps.video_config.transcripts_utils.get_available_transcript_languages' + ) + def instantiate_block(**field_data): """ @@ -320,7 +331,7 @@ def test_parse_xml(self): @XBlockAside.register_temp_plugin(AsideTestType, "test_aside") @patch('xmodule.video_block.video_block.VideoBlock.load_file') - @patch('xmodule.video_block.video_block.is_pointer_tag') + @patch(f'{path_video_block}.is_pointer_tag') @ddt.data(True, False) def test_parse_xml_with_asides(self, video_xml_has_aside, mock_is_pointer_tag, mock_load_file): """Test that `parse_xml` parses asides from the video xml""" @@ -642,7 +653,7 @@ def test_import_with_float_times(self): 'data': '' }) - @patch('xmodule.video_block.video_block.edxval_api') + @patch(f'{path_video_block}.edxval_api') def test_import_val_data(self, mock_val_api): """ Test that `parse_xml` works method works as expected. @@ -687,7 +698,7 @@ def mock_val_import(xml, edx_video_id, resource_fs, static_dir, external_transcr course_id='test_course_id' ) - @patch('xmodule.video_block.video_block.edxval_api') + @patch(f'{path_video_block}.edxval_api') def test_import_val_data_invalid(self, mock_val_api): mock_val_api.ValCannotCreateError = _MockValCannotCreateError mock_val_api.import_from_xml = Mock(side_effect=mock_val_api.ValCannotCreateError) @@ -715,7 +726,7 @@ def setUp(self): self.file_system = OSFS(self.temp_dir) self.addCleanup(shutil.rmtree, self.temp_dir) - @patch('xmodule.video_block.video_block.edxval_api') + @patch(f'{path_video_block}.edxval_api') def test_export_to_xml(self, mock_val_api): """ Test that we write the correct XML on export. @@ -815,7 +826,7 @@ def test_export_to_xml_without_video_id(self): expected = etree.XML(xml_string, parser=parser) self.assertXmlEqual(expected, xml) - @patch('xmodule.video_block.video_block.edxval_api') + @patch(f'{path_video_block}.edxval_api') def test_export_to_xml_val_error(self, mock_val_api): # Export should succeed without VAL data if video does not exist mock_val_api.ValVideoNotFoundError = _MockValVideoNotFoundError @@ -948,8 +959,7 @@ def test_student_view_data(self, field_data, expected_student_view_data): 'openedx.core.djangoapps.video_config.services.VideoConfigService.is_hls_playback_enabled', Mock(return_value=True) ) - @patch('openedx.core.djangoapps.video_config.transcripts_utils.get_available_transcript_languages', - Mock(return_value=['es'])) + @patch(get_available_transcript_languages_path, Mock(return_value=['es'])) @patch('edxval.api.get_video_info_for_course_and_profiles', Mock(return_value={})) @patch('openedx.core.djangoapps.video_config.transcripts_utils.get_video_transcript_content') @patch('edxval.api.get_video_info') From df6cff155afc8e5b0934ecc048befa1fadb2cb2f Mon Sep 17 00:00:00 2001 From: farhan Date: Thu, 8 Jan 2026 12:56:17 +0500 Subject: [PATCH 3/6] chore: adds tagging app config --- cms/envs/common.py | 2 +- cms/lib/xblock/tagging/apps.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 cms/lib/xblock/tagging/apps.py diff --git a/cms/envs/common.py b/cms/envs/common.py index ceaa86756caa..9f03c28524a3 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -803,7 +803,7 @@ def make_lms_template_path(settings): 'statici18n', # Tagging - 'cms.lib.xblock.tagging', + 'cms.lib.xblock.tagging.apps.TaggingConfig', # Enables default site and redirects 'django_sites_extensions', diff --git a/cms/lib/xblock/tagging/apps.py b/cms/lib/xblock/tagging/apps.py new file mode 100644 index 000000000000..123e706eea46 --- /dev/null +++ b/cms/lib/xblock/tagging/apps.py @@ -0,0 +1,12 @@ +"""Expand commentComment on line R1Code has comments. Press enter to view. +Django app configuration for the XBlock tagging app +""" +from django.apps import AppConfig + + +class TaggingConfig(AppConfig): + """ + Django app configuration for the XBlock tagging app + """ + name = 'cms.lib.xblock.tagging' + verbose_name = 'XBlock Tagging' From 46107b532191dc6d50e6ec7863ae0e6eab189804 Mon Sep 17 00:00:00 2001 From: farhan Date: Fri, 16 Jan 2026 13:45:11 +0500 Subject: [PATCH 4/6] chore: test commit to change xblocks-contrib path --- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/doc.txt | 2 +- requirements/edx/testing.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index d59b577a21dc..1bb0346d4d04 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -1267,7 +1267,7 @@ xblock-utils==4.0.0 # via # edx-sga # xblock-poll -xblocks-contrib==0.10.2 +git+https://github.com/openedx/xblocks-contrib.git@farhan/video-xblock-extraction#egg=xblocks-contrib # via -r requirements/edx/bundled.in xmlsec==1.3.14 # via diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index b24c0fb4bebe..7774210737a1 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -2299,7 +2299,7 @@ xblock-utils==4.0.0 # -r requirements/edx/testing.txt # edx-sga # xblock-poll -xblocks-contrib==0.10.2 +git+https://github.com/openedx/xblocks-contrib.git@farhan/video-xblock-extraction#egg=xblocks-contrib # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index c721a39e06fb..7beac278c0f0 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -1609,7 +1609,7 @@ xblock-utils==4.0.0 # -r requirements/edx/base.txt # edx-sga # xblock-poll -xblocks-contrib==0.10.2 +git+https://github.com/openedx/xblocks-contrib.git@farhan/video-xblock-extraction#egg=xblocks-contrib # via -r requirements/edx/base.txt xmlsec==1.3.14 # via diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 20cf145c3c2a..99dca18f37c8 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -1699,7 +1699,7 @@ xblock-utils==4.0.0 # -r requirements/edx/base.txt # edx-sga # xblock-poll -xblocks-contrib==0.10.2 +git+https://github.com/openedx/xblocks-contrib.git@farhan/video-xblock-extraction#egg=xblocks-contrib # via -r requirements/edx/base.txt xmlsec==1.3.14 # via From 983fce3cc009c6c0e616f9677e7f35af6fd1b0b5 Mon Sep 17 00:00:00 2001 From: farhan Date: Mon, 19 Jan 2026 21:24:16 +0500 Subject: [PATCH 5/6] chore: change the way of fixing video block paths --- lms/djangoapps/courseware/tests/test_video_mongo.py | 4 +--- xmodule/tests/test_video.py | 12 +++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_video_mongo.py b/lms/djangoapps/courseware/tests/test_video_mongo.py index 6be2fe84bbff..1a72a7e5b18a 100644 --- a/lms/djangoapps/courseware/tests/test_video_mongo.py +++ b/lms/djangoapps/courseware/tests/test_video_mongo.py @@ -69,11 +69,9 @@ if settings.USE_EXTRACTED_VIDEO_BLOCK: from xblocks_contrib.video import bumper_utils bumper_utils_path = 'xblocks_contrib.video.bumper_utils' - video_block_path = 'xblocks_contrib.video.video' else: from xmodule.video_block import bumper_utils bumper_utils_path = 'xmodule.video_block.bumper_utils' - video_block_path = 'xmodule.video_block.video_block' TRANSCRIPT_FILE_SRT_DATA = """ 1 @@ -939,7 +937,7 @@ def helper_get_html_with_edx_video_id(self, data): # pylint: disable=invalid-name @patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template) - @patch(f'{video_block_path}.rewrite_video_url') + @patch(f'{VideoBlock.__module__}.rewrite_video_url') def test_get_html_cdn_source(self, mocked_get_video, mock_render_django_template): """ Test if sources got from CDN diff --git a/xmodule/tests/test_video.py b/xmodule/tests/test_video.py index 42f2d176cf7d..b85d9f723a70 100644 --- a/xmodule/tests/test_video.py +++ b/xmodule/tests/test_video.py @@ -83,12 +83,10 @@ ) if settings.USE_EXTRACTED_VIDEO_BLOCK: - path_video_block = 'xblocks_contrib.video.video' get_available_transcript_languages_path = ( 'xblocks_contrib.video.video_transcripts_utils.get_available_transcript_languages' ) else: - path_video_block = 'xmodule.video_block.video_block' get_available_transcript_languages_path = ( 'openedx.core.djangoapps.video_config.transcripts_utils.get_available_transcript_languages' ) @@ -331,7 +329,7 @@ def test_parse_xml(self): @XBlockAside.register_temp_plugin(AsideTestType, "test_aside") @patch('xmodule.video_block.video_block.VideoBlock.load_file') - @patch(f'{path_video_block}.is_pointer_tag') + @patch(f'{VideoBlock.__module__}.is_pointer_tag') @ddt.data(True, False) def test_parse_xml_with_asides(self, video_xml_has_aside, mock_is_pointer_tag, mock_load_file): """Test that `parse_xml` parses asides from the video xml""" @@ -653,7 +651,7 @@ def test_import_with_float_times(self): 'data': '' }) - @patch(f'{path_video_block}.edxval_api') + @patch(f'{VideoBlock.__module__}.edxval_api') def test_import_val_data(self, mock_val_api): """ Test that `parse_xml` works method works as expected. @@ -698,7 +696,7 @@ def mock_val_import(xml, edx_video_id, resource_fs, static_dir, external_transcr course_id='test_course_id' ) - @patch(f'{path_video_block}.edxval_api') + @patch(f'{VideoBlock.__module__}.edxval_api') def test_import_val_data_invalid(self, mock_val_api): mock_val_api.ValCannotCreateError = _MockValCannotCreateError mock_val_api.import_from_xml = Mock(side_effect=mock_val_api.ValCannotCreateError) @@ -726,7 +724,7 @@ def setUp(self): self.file_system = OSFS(self.temp_dir) self.addCleanup(shutil.rmtree, self.temp_dir) - @patch(f'{path_video_block}.edxval_api') + @patch(f'{VideoBlock.__module__}.edxval_api') def test_export_to_xml(self, mock_val_api): """ Test that we write the correct XML on export. @@ -826,7 +824,7 @@ def test_export_to_xml_without_video_id(self): expected = etree.XML(xml_string, parser=parser) self.assertXmlEqual(expected, xml) - @patch(f'{path_video_block}.edxval_api') + @patch(f'{VideoBlock.__module__}.edxval_api') def test_export_to_xml_val_error(self, mock_val_api): # Export should succeed without VAL data if video does not exist mock_val_api.ValVideoNotFoundError = _MockValVideoNotFoundError From 074943892aa1f74fec12772ce1f2f8809378b761 Mon Sep 17 00:00:00 2001 From: farhan Date: Fri, 23 Jan 2026 20:36:44 +0500 Subject: [PATCH 6/6] chore: update xblocks-contrib path --- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/doc.txt | 2 +- requirements/edx/testing.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 1bb0346d4d04..4e4ede9c39da 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -1267,7 +1267,7 @@ xblock-utils==4.0.0 # via # edx-sga # xblock-poll -git+https://github.com/openedx/xblocks-contrib.git@farhan/video-xblock-extraction#egg=xblocks-contrib +git+https://github.com/openedx/xblocks-contrib.git@farhan/update-shifted-video-block-code#egg=xblocks-contrib # via -r requirements/edx/bundled.in xmlsec==1.3.14 # via diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 7774210737a1..fd2b962e4e2d 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -2299,7 +2299,7 @@ xblock-utils==4.0.0 # -r requirements/edx/testing.txt # edx-sga # xblock-poll -git+https://github.com/openedx/xblocks-contrib.git@farhan/video-xblock-extraction#egg=xblocks-contrib +git+https://github.com/openedx/xblocks-contrib.git@farhan/update-shifted-video-block-code#egg=xblocks-contrib # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 7beac278c0f0..679b2398c407 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -1609,7 +1609,7 @@ xblock-utils==4.0.0 # -r requirements/edx/base.txt # edx-sga # xblock-poll -git+https://github.com/openedx/xblocks-contrib.git@farhan/video-xblock-extraction#egg=xblocks-contrib +git+https://github.com/openedx/xblocks-contrib.git@farhan/update-shifted-video-block-code#egg=xblocks-contrib # via -r requirements/edx/base.txt xmlsec==1.3.14 # via diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 99dca18f37c8..feaba5f6b04b 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -1699,7 +1699,7 @@ xblock-utils==4.0.0 # -r requirements/edx/base.txt # edx-sga # xblock-poll -git+https://github.com/openedx/xblocks-contrib.git@farhan/video-xblock-extraction#egg=xblocks-contrib +git+https://github.com/openedx/xblocks-contrib.git@farhan/update-shifted-video-block-code#egg=xblocks-contrib # via -r requirements/edx/base.txt xmlsec==1.3.14 # via