Exemplo n.º 1
0
 def editor_saved(self, user, old_metadata, old_content):
     """
     Used to update video values during `self`:save method from CMS.
     old_metadata: dict, values of fields of `self` with scope=settings which were explicitly set by user.
     old_content, same as `old_metadata` but for scope=content.
     Due to nature of code flow in item.py::_save_item, before current function is called,
     fields of `self` instance have been already updated, but not yet saved.
     To obtain values, which were changed by user input,
     one should compare own_metadata(self) and old_medatada.
     Video player has two tabs, and due to nature of sync between tabs,
     metadata from Basic tab is always sent when video player is edited and saved first time, for example:
     {'youtube_id_1_0': u'3_yD_cEKoCk', 'display_name': u'Video', 'sub': u'3_yD_cEKoCk', 'html5_sources': []},
     that's why these fields will always present in old_metadata after first save. This should be fixed.
     At consequent save requests html5_sources are always sent too, disregard of their change by user.
     That means that html5_sources are always in list of fields that were changed (`metadata` param in save_item).
     This should be fixed too.
     """
     metadata_was_changed_by_user = old_metadata != own_metadata(self)
     if metadata_was_changed_by_user:
         manage_video_subtitles_save(
             self,
             user,
             old_metadata if old_metadata else None,
             generate_translation=True
         )
Exemplo n.º 2
0
 def editor_saved(self, user, old_metadata, old_content):
     """
     Used to update video values during `self`:save method from CMS.
     old_metadata: dict, values of fields of `self` with scope=settings which were explicitly set by user.
     old_content, same as `old_metadata` but for scope=content.
     Due to nature of code flow in item.py::_save_item, before current function is called,
     fields of `self` instance have been already updated, but not yet saved.
     To obtain values, which were changed by user input,
     one should compare own_metadata(self) and old_medatada.
     Video player has two tabs, and due to nature of sync between tabs,
     metadata from Basic tab is always sent when video player is edited and saved first time, for example:
     {'youtube_id_1_0': u'3_yD_cEKoCk', 'display_name': u'Video', 'sub': u'3_yD_cEKoCk', 'html5_sources': []},
     that's why these fields will always present in old_metadata after first save. This should be fixed.
     At consequent save requests html5_sources are always sent too, disregard of their change by user.
     That means that html5_sources are always in list of fields that were changed (`metadata` param in save_item).
     This should be fixed too.
     """
     metadata_was_changed_by_user = old_metadata != own_metadata(self)
     if metadata_was_changed_by_user:
         manage_video_subtitles_save(
             self,
             user,
             old_metadata if old_metadata else None,
             generate_translation=True
         )
Exemplo n.º 3
0
 def editor_saved(self, user, old_metadata, old_content):
     """
     Used to update video subtitles.
     """
     manage_video_subtitles_save(self,
                                 user,
                                 old_metadata if old_metadata else None,
                                 generate_translation=True)
Exemplo n.º 4
0
 def editor_saved(self, user, old_metadata, old_content):
     """
     Used to update video subtitles.
     """
     manage_video_subtitles_save(
         self,
         user,
         old_metadata if old_metadata else None,
         generate_translation=True
     )
Exemplo n.º 5
0
    def editor_saved(self, user, old_metadata, old_content):
        """
        Used to update video values during `self`:save method from CMS.
        old_metadata: dict, values of fields of `self` with scope=settings which were explicitly set by user.
        old_content, same as `old_metadata` but for scope=content.
        Due to nature of code flow in item.py::_save_item, before current function is called,
        fields of `self` instance have been already updated, but not yet saved.
        To obtain values, which were changed by user input,
        one should compare own_metadata(self) and old_medatada.
        Video player has two tabs, and due to nature of sync between tabs,
        metadata from Basic tab is always sent when video player is edited and saved first time, for example:
        {'youtube_id_1_0': u'3_yD_cEKoCk', 'display_name': u'Video', 'sub': u'3_yD_cEKoCk', 'html5_sources': []},
        that's why these fields will always present in old_metadata after first save. This should be fixed.
        At consequent save requests html5_sources are always sent too, disregard of their change by user.
        That means that html5_sources are always in list of fields that were changed (`metadata` param in save_item).
        This should be fixed too.
        """
        metadata_was_changed_by_user = old_metadata != own_metadata(self)

        # There is an edge case when old_metadata and own_metadata are same and we are importing transcript from youtube
        # then there is a syncing issue where html5_subs are not syncing with youtube sub, We can make sync better by
        # checking if transcript is present for the video and if any html5_ids transcript is not present then trigger
        # the manage_video_subtitles_save to create the missing transcript with particular html5_id.
        if not metadata_was_changed_by_user and self.sub and hasattr(self, 'html5_sources'):
            html5_ids = get_html5_ids(self.html5_sources)
            for subs_id in html5_ids:
                try:
                    Transcript.asset(self.location, subs_id)
                except NotFoundError:
                    # If a transcript does not not exist with particular html5_id then there is no need to check other
                    # html5_ids because we have to create a new transcript with this missing html5_id by turning on
                    # metadata_was_changed_by_user flag.
                    metadata_was_changed_by_user = True
                    break

        if metadata_was_changed_by_user:
            self.edx_video_id = self.edx_video_id and self.edx_video_id.strip()

            # We want to override `youtube_id_1_0` with val youtube profile in the first place when someone adds/edits
            # an `edx_video_id` or its underlying YT val profile. Without this, override will only happen when a user
            # saves the video second time. This is because of the syncing of basic and advanced video settings which
            # also syncs val youtube id from basic tab's `Video Url` to advanced tab's `Youtube ID`.
            if self.edx_video_id and edxval_api:
                val_youtube_id = edxval_api.get_url_for_profile(self.edx_video_id, 'youtube')
                if val_youtube_id and self.youtube_id_1_0 != val_youtube_id:
                    self.youtube_id_1_0 = val_youtube_id

            manage_video_subtitles_save(
                self,
                user,
                old_metadata if old_metadata else None,
                generate_translation=True
            )
Exemplo n.º 6
0
    def editor_saved(self, user, old_metadata, old_content):
        """
        Used to update video values during `self`:save method from CMS.
        old_metadata: dict, values of fields of `self` with scope=settings which were explicitly set by user.
        old_content, same as `old_metadata` but for scope=content.
        Due to nature of code flow in item.py::_save_item, before current function is called,
        fields of `self` instance have been already updated, but not yet saved.
        To obtain values, which were changed by user input,
        one should compare own_metadata(self) and old_medatada.
        Video player has two tabs, and due to nature of sync between tabs,
        metadata from Basic tab is always sent when video player is edited and saved first time, for example:
        {'youtube_id_1_0': u'3_yD_cEKoCk', 'display_name': u'Video', 'sub': u'3_yD_cEKoCk', 'html5_sources': []},
        that's why these fields will always present in old_metadata after first save. This should be fixed.
        At consequent save requests html5_sources are always sent too, disregard of their change by user.
        That means that html5_sources are always in list of fields that were changed (`metadata` param in save_item).
        This should be fixed too.
        """
        
        self.runtime.modulestore.create_keyword_video('test',self.display_name,self.key_word,self.location)
        metadata_was_changed_by_user = old_metadata != own_metadata(self)

        # There is an edge case when old_metadata and own_metadata are same and we are importing transcript from youtube
        # then there is a syncing issue where html5_subs are not syncing with youtube sub, We can make sync better by
        # checking if transcript is present for the video and if any html5_ids transcript is not present then trigger
        # the manage_video_subtitles_save to create the missing transcript with particular html5_id.
        if not metadata_was_changed_by_user and self.sub and hasattr(self, 'html5_sources'):
            html5_ids = get_html5_ids(self.html5_sources)
            for subs_id in html5_ids:
                try:
                    Transcript.asset(self.location, subs_id)
                except NotFoundError:
                    # If a transcript does not not exist with particular html5_id then there is no need to check other
                    # html5_ids because we have to create a new transcript with this missing html5_id by turning on
                    # metadata_was_changed_by_user flag.
                    metadata_was_changed_by_user = True
                    break

        if metadata_was_changed_by_user:
            manage_video_subtitles_save(
                self,
                user,
                old_metadata if old_metadata else None,
                generate_translation=True
            )
Exemplo n.º 7
0
def json_update_videos(request, locations):
    """
    Updates the captions of a given list of videos and returns the status of the
    videos in json format

    request: the incoming request to update the videos
    locations: list of locations of videos to be updated
    """
    results = []
    for key_string in locations:
        key = UsageKey.from_string(key_string)
        try:
            #update transcripts
            item = modulestore().get_item(key)
            download_youtube_subs(item.youtube_id_1_0, item, settings)

            # Once transcripts downloaded, show subs are present from youtube
            item.sub = item.youtube_id_1_0
            manage_video_subtitles_save(item, request.user)

            #get new status
            videos = {'youtube': item.youtube_id_1_0}
            html5 = {}
            for url in item.html5_sources:
                name = os.path.splitext(url.split('/')[-1])[0]
                html5[name] = 'html5'
            videos['html5'] = html5
            captions_dict = get_transcripts_presence(videos, item)
            captions_dict.update({'location': key_string})
            results.append(captions_dict)

        except GetTranscriptsFromYouTubeException as e:
            log.debug(e)
            results.append({'location': key_string, 'command': e})

    return JsonResponse(results)
Exemplo n.º 8
0
def json_update_videos(request, locations):
    """
    Updates the captions of a given list of videos and returns the status of the
    videos in json format

    request: the incoming request to update the videos
    locations: list of locations of videos to be updated
    """
    results = []
    for key_string in locations:
        key = UsageKey.from_string(key_string)
        try:
            #update transcripts
            item = modulestore().get_item(key)
            download_youtube_subs(item.youtube_id_1_0, item, settings)

            # Once transcripts downloaded, show subs are present from youtube
            item.sub = item.youtube_id_1_0
            manage_video_subtitles_save(item, request.user)

            #get new status
            videos = {'youtube': item.youtube_id_1_0}
            html5 = {}
            for url in item.html5_sources:
                name = os.path.splitext(url.split('/')[-1])[0]
                html5[name] = 'html5'
            videos['html5'] = html5
            captions_dict = get_transcripts_presence(videos, item)
            captions_dict.update({'location': key_string})
            results.append(captions_dict)

        except GetTranscriptsFromYouTubeException as e:
            log.debug(e)
            results.append({'location': key_string, 'command': e})

    return JsonResponse(results)
Exemplo n.º 9
0
def _save_item(request,
               usage_loc,
               item_location,
               data=None,
               children=None,
               metadata=None,
               nullout=None,
               grader_type=None,
               publish=None):
    """
    Saves xblock w/ its fields. Has special processing for grader_type, publish, and nullout and Nones in metadata.
    nullout means to truly set the field to None whereas nones in metadata mean to unset them (so they revert
    to default).

    The item_location is still the old-style location whereas usage_loc is a BlockUsageLocator
    """
    store = get_modulestore(item_location)

    try:
        existing_item = store.get_item(item_location)
    except ItemNotFoundError:
        if item_location.category in CREATE_IF_NOT_FOUND:
            # New module at this location, for pages that are not pre-created.
            # Used for course info handouts.
            store.create_and_save_xmodule(item_location)
            existing_item = store.get_item(item_location)
        else:
            raise
    except InvalidLocationError:
        log.error("Can't find item by location.")
        return JsonResponse(
            {"error": "Can't find item by location: " + str(item_location)},
            404)

    old_metadata = own_metadata(existing_item)

    if publish:
        if publish == 'make_private':
            _xmodule_recurse(existing_item,
                             lambda i: modulestore().unpublish(i.location))
        elif publish == 'create_draft':
            # This recursively clones the existing item location to a draft location (the draft is
            # implicit, because modulestore is a Draft modulestore)
            _xmodule_recurse(
                existing_item,
                lambda i: modulestore().convert_to_draft(i.location))

    if data:
        # TODO Allow any scope.content fields not just "data" (exactly like the get below this)
        existing_item.data = data
    else:
        data = existing_item.get_explicitly_set_fields_by_scope(Scope.content)

    if children is not None:
        children_ids = [
            loc_mapper().translate_locator_to_location(
                BlockUsageLocator(child_locator)).url()
            for child_locator in children
        ]
        existing_item.children = children_ids

    # also commit any metadata which might have been passed along
    if nullout is not None or metadata is not None:
        # the postback is not the complete metadata, as there's system metadata which is
        # not presented to the end-user for editing. So let's use the original (existing_item) and
        # 'apply' the submitted metadata, so we don't end up deleting system metadata.
        if nullout is not None:
            for metadata_key in nullout:
                setattr(existing_item, metadata_key, None)

        # update existing metadata with submitted metadata (which can be partial)
        # IMPORTANT NOTE: if the client passed 'null' (None) for a piece of metadata that means 'remove it'. If
        # the intent is to make it None, use the nullout field
        if metadata is not None:
            for metadata_key, value in metadata.items():
                field = existing_item.fields[metadata_key]

                if value is None:
                    field.delete_from(existing_item)
                else:
                    try:
                        value = field.from_json(value)
                    except ValueError:
                        return JsonResponse({"error": "Invalid data"}, 400)
                    field.write_to(existing_item, value)

        if existing_item.category == 'video':
            manage_video_subtitles_save(existing_item,
                                        request.user,
                                        old_metadata,
                                        generate_translation=True)

    # commit to datastore
    store.update_item(existing_item, request.user.id)

    result = {
        'id': unicode(usage_loc),
        'data': data,
        'metadata': own_metadata(existing_item)
    }

    if grader_type is not None:
        result.update(
            CourseGradingModel.update_section_grader_type(
                existing_item, grader_type, request.user))

    # Make public after updating the xblock, in case the caller asked
    # for both an update and a publish.
    if publish and publish == 'make_public':

        def _publish(block):
            # This is super gross, but prevents us from publishing something that
            # we shouldn't. Ideally, all modulestores would have a consistant
            # interface for publishing. However, as of now, only the DraftMongoModulestore
            # does, so we have to check for the attribute explicitly.
            store = get_modulestore(block.location)
            if hasattr(store, 'publish'):
                store.publish(block.location, request.user.id)

        _xmodule_recurse(existing_item, _publish)

    # Note that children aren't being returned until we have a use case.
    return JsonResponse(result)
Exemplo n.º 10
0
def _save_item(request, usage_loc, item_location, data=None, children=None, metadata=None, nullout=None,
               grader_type=None, publish=None):
    """
    Saves xblock w/ its fields. Has special processing for grader_type, publish, and nullout and Nones in metadata.
    nullout means to truly set the field to None whereas nones in metadata mean to unset them (so they revert
    to default).

    The item_location is still the old-style location whereas usage_loc is a BlockUsageLocator
    """
    store = get_modulestore(item_location)

    try:
        existing_item = store.get_item(item_location)
    except ItemNotFoundError:
        if item_location.category in CREATE_IF_NOT_FOUND:
            # New module at this location, for pages that are not pre-created.
            # Used for course info handouts.
            store.create_and_save_xmodule(item_location)
            existing_item = store.get_item(item_location)
        else:
            raise
    except InvalidLocationError:
        log.error("Can't find item by location.")
        return JsonResponse({"error": "Can't find item by location: " + str(item_location)}, 404)

    old_metadata = own_metadata(existing_item)

    if publish:
        if publish == 'make_private':
            _xmodule_recurse(existing_item, lambda i: modulestore().unpublish(i.location))
        elif publish == 'create_draft':
            # This clones the existing item location to a draft location (the draft is
            # implicit, because modulestore is a Draft modulestore)
            modulestore().convert_to_draft(item_location)

    if data:
        # TODO Allow any scope.content fields not just "data" (exactly like the get below this)
        existing_item.data = data
    else:
        data = existing_item.get_explicitly_set_fields_by_scope(Scope.content)

    if children is not None:
        children_ids = [
            loc_mapper().translate_locator_to_location(BlockUsageLocator(child_locator)).url()
            for child_locator
            in children
        ]
        existing_item.children = children_ids

    # also commit any metadata which might have been passed along
    if nullout is not None or metadata is not None:
        # the postback is not the complete metadata, as there's system metadata which is
        # not presented to the end-user for editing. So let's use the original (existing_item) and
        # 'apply' the submitted metadata, so we don't end up deleting system metadata.
        if nullout is not None:
            for metadata_key in nullout:
                setattr(existing_item, metadata_key, None)

        # update existing metadata with submitted metadata (which can be partial)
        # IMPORTANT NOTE: if the client passed 'null' (None) for a piece of metadata that means 'remove it'. If
        # the intent is to make it None, use the nullout field
        if metadata is not None:
            for metadata_key, value in metadata.items():
                field = existing_item.fields[metadata_key]

                if value is None:
                    field.delete_from(existing_item)
                else:
                    try:
                        value = field.from_json(value)
                    except ValueError:
                        return JsonResponse({"error": "Invalid data"}, 400)
                    field.write_to(existing_item, value)

        if existing_item.category == 'video':
            manage_video_subtitles_save(existing_item, request.user, old_metadata, generate_translation=True)

    # commit to datastore
    store.update_item(existing_item, request.user.id)

    result = {
        'id': unicode(usage_loc),
        'data': data,
        'metadata': own_metadata(existing_item)
    }

    if grader_type is not None:
        result.update(CourseGradingModel.update_section_grader_type(existing_item, grader_type, request.user))

    # Make public after updating the xblock, in case the caller asked
    # for both an update and a publish.
    if publish and publish == 'make_public':
        def _publish(block):
            # This is super gross, but prevents us from publishing something that
            # we shouldn't. Ideally, all modulestores would have a consistant
            # interface for publishing. However, as of now, only the DraftMongoModulestore
            # does, so we have to check for the attribute explicitly.
            store = get_modulestore(block.location)
            if hasattr(store, 'publish'):
                store.publish(block.location, request.user.id)

        _xmodule_recurse(
            existing_item,
            _publish
        )

    # Note that children aren't being returned until we have a use case.
    return JsonResponse(result)