Skip to content

Conversation

@farhan
Copy link
Contributor

@farhan farhan commented Oct 10, 2025

@farhan farhan force-pushed the farhan/move-video-sharing-utils branch from 3be4b69 to 2ac1c33 Compare October 10, 2025 14:02
@ttqureshi
Copy link
Member

ttqureshi commented Oct 15, 2025

I have tested the video sharing functionality. And this refactoring doesn't have any impact on how the video sharing works.
I enabled the Social Sharing checkbox in the video block configuration settings:
image
This setting rendered the template context for video sharing buttong as shown below:
image
image

I am now looking into the test case failures in this PR.

@farhan farhan force-pushed the farhan/move-video-sharing-utils branch 2 times, most recently from 77cc7a3 to 0eedb35 Compare October 16, 2025 12:51
@farhan farhan force-pushed the farhan/move-video-sharing-utils branch from 0eedb35 to 245402e Compare October 16, 2025 12:57
@farhan farhan requested a review from kdmccormick October 17, 2025 14:06
Copy link
Member

@kdmccormick kdmccormick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functionality and testing both look great 👍🏻

Just some more code nits to work through and then we can get this merged.

Could you re-label the commit as a refactor:? The chore: label is supposed to be for really trivial things, like a patch version bump.

COURSE_VIDEO_SHARING_NONE = 'all-off'


class VideoSharingUtils:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern (empty class with static methods) is a common way to organize functions in java, but it is not idiomatic in python. The module already serves the purpose of organizing the functions. So, please remove the VideoSharingUtils class and declare these as regular functions. If you'd like, you could rename the module to sharing.py -- this makes it clear, even without the class, that all the functions within are related to sharing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out, following pythonic way of doing things make sense, going to implement.

I gave it a try to this pattern as it could bring following benefits:

  1. utils.py sounds extensible to me to accommodate other util methods in future.
  2. Defined empty class VideoSharingUtils to group the related util methods
  3. VideoSharingUtils.get_public_video_url() felt more explicit and self-documenting when importing, making it immediately clear what functionality you're using

Copy link
Member

@kdmccormick kdmccormick Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only use of a module named utils.py, in my opinion, is for helper functions which cannot be grouped together in any better way. But, you have a good way to group these together (they are sharing-related), so you may as well make that the name of the module.

VideoSharingUtils.get_public_video_url() felt more explicit and self-documenting when importing

This is fair, and you can still achieve it without the class:

from openedx.core.djangoapps.video_config import sharing as video_sharing

video_sharing.get_public_video_url(...)

return fr'{settings.LMS_ROOT_URL}/videos/{str(usage_id)}'

@staticmethod
def is_public_sharing_enabled(video_block):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by taking an instance of VideoBlock as a parameter, this function is making assumptions about the fields of VideoBlock, which it should not be once the VideoBlock is defined outside of the platform.

could this function take just (usage_key: UsageKey, public_access: bool) instead?

please annotate the return type as well.

Comment on lines 246 to 247
elif service_name == 'video_config':
return VideoConfigService()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not special-case the video-config like this. The field-data service is weird, and isn't the best pattern to follow. Check out the user or teams_configuration xblock services to see a more standard way of injecting the service into the runtime.

render_template=render_template or mock_render_template,
mixins=(InheritanceMixin, XModuleMixin),
services={'field-data': field_data},
services={'field-data': field_data, 'video_config': video_config},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this addition is necessary in order to get tests to pass, then this is fine.

But, if tests pass without the service, then please remove it. It's best to keep the test runtime as simple as possible. For example, there's not even a user service in this runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, without this injection tests are failing.

FAILED lms/djangoapps/courseware/tests/test_video_mongo.py::VideoBlockTest::test_get_context - xblock.exceptions.NoSuchServiceError: Service 'video_config' is not available.

Detailed logs

_________________________________________________________________________________ VideoBlockTest.test_get_context _________________________________________________________________________________

self = <lms.djangoapps.courseware.tests.test_video_mongo.VideoBlockTest testMethod=test_get_context>

    def test_get_context(self):
        """"
        Test get_context.
    
        This test is located here and not in xmodule.tests because get_context calls editable_metadata_fields.
        Which, in turn, uses settings.LANGUAGES from django setttings.
        """
        correct_tabs = [
            {
                'name': "Basic",
                'template': "video/transcripts.html",
                'current': True
            },
            {
                'name': 'Advanced',
                'template': 'tabs/metadata-edit-tab.html'
            }
        ]
>       rendered_context = self.block.get_context()

lms/djangoapps/courseware/tests/test_video_mongo.py:1829: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
xmodule/video_block/video_block.py:849: in get_context
    _context = MakoTemplateBlockBase.get_context(self)
xmodule/mako_block.py:56: in get_context
    'editable_metadata_fields': self.editable_metadata_fields
xmodule/video_block/video_block.py:647: in editable_metadata_fields
    editable_fields['public_access']['url'] = self.get_public_video_url()
xmodule/video_block/video_block.py:523: in get_public_video_url
    video_config_service = self.runtime.service(self, 'video_config')
xmodule/x_module.py:1505: in service
    service = super().service(block=block, service_name=service_name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <xmodule.tests.TestDescriptorSystem object at 0xffff67fea350>
block = <VideoBlockWithMixins @E3CB license=None, name=None, parent=None, tags=[], display_name='Video', course_edit_method='S...{}, youtube_id_0_75='', youtube_id_1_0='3_yD_cEKoCk', youtube_id_1_25='', youtube_id_1_5='', youtube_is_available=True>
service_name = 'video_config'

    def service(self, block, service_name):
        """Return a service, or None.
    
        Services are objects implementing arbitrary other interfaces.  They are
        requested by agreed-upon names, see [XXX TODO] for a list of possible
        services.  The object returned depends on the service requested.
    
        XBlocks must announce their intention to request services with the
        `XBlock.needs` or `XBlock.wants` decorators.  Use `needs` if you assume
        that the service is available, or `wants` if your code is flexible and
        can accept a None from this method.
    
        Runtimes can override this method if they have different techniques for
        finding and delivering services.
    
        Arguments:
            block (XBlock): this block's class will be examined for service
                decorators.
            service_name (str): the name of the service requested.
    
        Returns:
            An object implementing the requested service, or None.
    
        """
        declaration = block.service_declaration(service_name)
        if declaration is None:
            raise NoSuchServiceError(f"Service {service_name!r} was not requested.")
        service = self._services.get(service_name)
        if service is None and declaration == "need":
>           raise NoSuchServiceError(f"Service {service_name!r} is not available.")
E           xblock.exceptions.NoSuchServiceError: Service 'video_config' is not available.

/mnt/xblock/xblock/runtime.py:1101: NoSuchServiceError

Comment on lines 78 to 82
Args:
video_block: The video XBlock instance
Returns:
Course video sharing option or None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this information is redundant. you can omit it and add a type annotation instead.

@farhan farhan added the create-sandbox open-craft-grove should create a sandbox environment from this PR label Oct 20, 2025
@farhan farhan force-pushed the farhan/move-video-sharing-utils branch from e5e2cc1 to 4ef9a61 Compare October 20, 2025 07:44
@open-craft-grove
Copy link

Sandbox deployment successful 🚀
🎓 LMS
📝 Studio
ℹ️ Configuration information unavailable

@open-craft-grove
Copy link

Sandbox deployment failed 💥
Please check the settings and requirements.
Retry deployment by pushing a new commit or updating the requirements/settings in the pull request's description.
📜 Failure Logs
ℹ️ Configuration information unavailable

@farhan farhan closed this Oct 20, 2025
@farhan farhan reopened this Oct 20, 2025
@open-craft-grove
Copy link

Sandbox deployment failed 💥
Please check the settings and requirements.
Retry deployment by pushing a new commit or updating the requirements/settings in the pull request's description.
📜 Failure Logs
ℹ️ Configuration information unavailable

1 similar comment
@open-craft-grove
Copy link

Sandbox deployment failed 💥
Please check the settings and requirements.
Retry deployment by pushing a new commit or updating the requirements/settings in the pull request's description.
📜 Failure Logs
ℹ️ Configuration information unavailable

@farhan farhan closed this Oct 20, 2025
@farhan farhan reopened this Oct 20, 2025
@farhan farhan force-pushed the farhan/move-video-sharing-utils branch from c2dc35f to d21ff53 Compare October 20, 2025 12:24
@open-craft-grove
Copy link

Sandbox deployment successful 🚀
🎓 LMS
📝 Studio
ℹ️ Configuration information unavailable

@farhan farhan force-pushed the farhan/move-video-sharing-utils branch from 52f9dd9 to 08fd86e Compare October 20, 2025 13:22
@open-craft-grove
Copy link

Sandbox deployment successful 🚀
🎓 LMS
📝 Studio
ℹ️ Configuration information unavailable

@open-craft-grove
Copy link

Sandbox deployment successful 🚀
🎓 LMS
📝 Studio
ℹ️ Configuration information unavailable

2 similar comments
@open-craft-grove
Copy link

Sandbox deployment successful 🚀
🎓 LMS
📝 Studio
ℹ️ Configuration information unavailable

@open-craft-grove
Copy link

Sandbox deployment successful 🚀
🎓 LMS
📝 Studio
ℹ️ Configuration information unavailable

@farhan farhan requested a review from kdmccormick October 21, 2025 10:34
@open-craft-grove
Copy link

Sandbox deployment successful 🚀
🎓 LMS
📝 Studio
ℹ️ Configuration information unavailable

@farhan
Copy link
Contributor Author

farhan commented Oct 21, 2025

@kdmccormick The PR is ready for another review

I’ve aligned the service injection with the standard patterns, but please feel free to share any further guidance if needed.
I’ve also tested the sharing feature on the sandbox with all the recent changes.

Copy link
Member

@kdmccormick kdmccormick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@open-craft-grove
Copy link

Sandbox deployment failed 💥
Please check the settings and requirements.
Retry deployment by pushing a new commit or updating the requirements/settings in the pull request's description.
📜 Failure Logs
ℹ️ Grove Config, Tutor Config, Tutor Requirements

@open-craft-grove
Copy link

Sandbox deployment failed 💥
Please check the settings and requirements.
Retry deployment by pushing a new commit or updating the requirements/settings in the pull request's description.
📜 Failure Logs
ℹ️ Grove Config, Tutor Config, Tutor Requirements

@farhan farhan force-pushed the farhan/move-video-sharing-utils branch from 4971f25 to 0cd62bf Compare November 11, 2025 07:55
@open-craft-grove
Copy link

Sandbox deployment successful 🚀
🎓 LMS
📝 Studio
ℹ️ Grove Config, Tutor Config, Tutor Requirements

@farhan farhan merged commit 7b953a5 into master Nov 11, 2025
53 checks passed
@farhan farhan deleted the farhan/move-video-sharing-utils branch November 11, 2025 12:59
@farhan farhan changed the title chore: move video sharing methods to video_config app refactor: Introduce VideoConfig service, move video sharing methods in it Nov 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

create-sandbox open-craft-grove should create a sandbox environment from this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move video sharing feature into new VideoConfig service

5 participants