Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't use OLX when copying/duplicating blocks [FC-0049] #34386

Merged
merged 14 commits into from
Apr 2, 2024

Conversation

pomegranited
Copy link
Contributor

@pomegranited pomegranited commented Mar 19, 2024

Description

Refactors the block/library copy/paste and duplicate functions to ensure that content tags are copy/pasted/duplicated to use signals/events to trigger copying tags instead of using the OLX as intermediate storage.

Supporting information

Closes openedx/modular-learning#203

Testing instructions

USE latest master:

  1. Create a course with units that contain all the different types of blocks, including a library content block.
  2. Add tags to your units, and to the actual blocks.
  3. Copy the tagged unit to staged content clipboard.

USE this branch:

  1. Run migrations

  2. Paste the block you copied into your course.

    Ensure the paste works without errors, and creates the expected content.
    Note that no tags will be pasted though.

  3. Copy your tagged unit again.

  4. Paste the tagged unit.

    Check that the tags pasted as expected in the parent and all the different types of blocks.

  5. Duplicate a subsection/unit.
    Check that the tags duplicated as expected in the parent and child blocks.

Deadline

Need to get this merged before Redwood is cut to avoid OLX incompatibilities in exported courses or staged content.

Private-ref: FAL-3688

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Mar 19, 2024
@openedx-webhooks
Copy link

openedx-webhooks commented Mar 19, 2024

Thanks for the pull request, @pomegranited! Please note that it may take us up to several weeks or months to complete a review and merge your PR.

Feel free to add as much of the following information to the ticket as you can:

  • supporting documentation
  • Open edX discussion forum threads
  • timeline information ("this must be merged by XX date", and why that is)
  • partner information ("this is a course on edx.org")
  • any other information that can help Product understand the context for the PR

All technical communication about the code itself will be done via the GitHub pull request interface. As a reminder, our process documentation is here.

Please let us know once your PR is ready for our review and all tests are green.

instead of using TaggedBlockMixin.studio_post_duplicate

Adds api.copy_tags method and tests to support this.
to avoid duplicating logic.

This required updating get_all_object_tags to accept ordinary ContentKey
object_ids, not just Course or Library keys.
Adds a new JSON field to StagedContent to store a dict of serialized
object tags from the copied block and its children, if any.

These tags are deserialized and applied to the new block(s) on paste.

To support this, we had to make 2 changes to XBlockSerializer:

* XBlockSerializer now serializes the block's (and it's child block's)
  tags into a dict, keyed off the block usage key. These serialized tags
  are stored in StagedContent.tags
* XBlockSerializer now inserts a url_name attribute when serializing
  blocks that don't provide one.
  When pasting serialized blocks, the url_name is used to reconstruct
  the original usage keys of any serialized children, so we can identify
  their tags.

Other related changes:

* Updates import_staged_content_from_user_clipboard's helper method to
  reconstruct child usage keys, so we can determine which tags apply to
  the pasted block(s).
* Updates ClipboardPasteTestCase to test copying child tags
* Removes TaggedBlockMixin, and moves its object tag serialization logic
  into content_tagging.api
Copy link
Member

@yusuf-musleh yusuf-musleh left a comment

Choose a reason for hiding this comment

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

@pomegranited Great work on this! After looking into the code, it's alot more clear to me why this approach is much cleaner than the previous one storing tags in the OLX.

Just had a small nit (in addition to addressing the failing tests). I've tested out the functionality as described, everything worked really well. I did face an error when I tried duplicating a unit that contained a library block (but I think you mentioned that you're still getting to that), here's the error message to give you some insights:

 2024-03-25 06:18:18,154 ERROR 128 [django.request] [user None] [ip None] log.py:241 - Internal Server Error: /xblock/
 Traceback (most recent call last):
   File "/openedx/edx-platform/xmodule/library_content_block.py", line 185, in source_library_key
     return LibraryLocator.from_string(self.source_library_id)
   File "/openedx/venv/lib/python3.8/site-packages/opaque_keys/__init__.py", line 183, in from_string
     raise InvalidKeyError(cls, serialized)
 opaque_keys.InvalidKeyError: <class 'opaque_keys.edx.locator.LibraryLocator'>: None

 During handling of the above exception, another exception occurred:

 Traceback (most recent call last):
   File "/openedx/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
     response = get_response(request)
   File "/openedx/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
     response = wrapped_callback(request, *callback_args, **callback_kwargs)
   File "/openedx/venv/lib/python3.8/site-packages/django/views/decorators/http.py", line 43, in inner
     return func(request, *args, **kwargs)
   File "/openedx/venv/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 23, in _wrapper_view
     return view_func(request, *args, **kwargs)
   File "/openedx/edx-platform/common/djangoapps/util/json_request.py", line 57, in parse_json_into_request
     return view_function(request, *args, **kwargs)
   File "/openedx/edx-platform/cms/djangoapps/contentstore/views/block.py", line 140, in xblock_handler
     return handle_xblock(request, usage_key_string)
   File "/openedx/edx-platform/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py", line 227, in handle_xblock
     dest_usage_key = duplicate_block(
   File "/openedx/edx-platform/cms/djangoapps/contentstore/utils.py", line 1137, in duplicate_block
     dupe = duplicate_block(dest_block.location, child, user=user, is_child=True)
   File "/openedx/edx-platform/cms/djangoapps/contentstore/utils.py", line 1130, in duplicate_block
     children_handled = dest_block.studio_post_duplicate(store, source_item)
   File "/openedx/edx-platform/xmodule/library_content_block.py", line 599, in studio_post_duplicate
     self._validate_sync_permissions()
   File "/openedx/edx-platform/xmodule/library_content_block.py", line 518, in _validate_sync_permissions
     if self.source_library_key:
   File "/openedx/edx-platform/xmodule/library_content_block.py", line 187, in source_library_key
     return LibraryLocatorV2.from_string(self.source_library_id)
   File "/openedx/venv/lib/python3.8/site-packages/opaque_keys/__init__.py", line 183, in from_string
     raise InvalidKeyError(cls, serialized)
 opaque_keys.InvalidKeyError: <class 'opaque_keys.edx.locator.LibraryLocatorV2'>: None
 [25/Mar/2024 06:18:18] "POST /xblock/ HTTP/1.1" 500 305190

cms/djangoapps/contentstore/helpers.py Show resolved Hide resolved
to avoid circular imports when using these APIs.
and use taxonomy.id instead of taxonomy.external_id.

To achieve this without adding functions to content_tagging.api, these
related changes were made:

* get_all_object_tags: now returns a list of tag values instead of
  ObjectTag instances, grouped by object_id and Taxonomy.id
* set_object_tags: renamed to `set_all_object_tags`, to clarify its
  relationship to get_all_object_tags
* set_all_object_tags: now accepts a list of tag values grouped by
  Taxonomy.id, instead of by Taxonomy.external_id
* tag_object: added a content_tagging version of this oel_tagging method
  which checks that the given taxonomy is enabled for the given
  object_id's org before applying the tag.
* types.ObjectTagByObjectIdDict changed to be TagValuesByObjectIdDict,
  types.ObjectTagByTaxonomyIdDict changed to be TagValuesByTaxonomyIdDict
@pomegranited pomegranited marked this pull request as ready for review March 26, 2024 07:00
@pomegranited
Copy link
Contributor Author

pomegranited commented Mar 26, 2024

Hi @yusuf-musleh , this PR is ready for a full review now.

The error you found happens when the library content block doesn't have its source_library_id set, which users have to do in the Studio/course authoring MFE:

Traceback (most recent call last):
   File "/openedx/edx-platform/xmodule/library_content_block.py", line 185, in source_library_key
     return LibraryLocator.from_string(self.source_library_id)
   File "/openedx/venv/lib/python3.8/site-packages/opaque_keys/__init__.py", line 183, in from_string
     raise InvalidKeyError(cls, serialized)
 opaque_keys.InvalidKeyError: <class 'opaque_keys.edx.locator.LibraryLocator'>: None

The Course Authoring MFE doesn't let me edit content library blocks, so I'm not able to verify that providing a valid library key here fixes the issue.

But I'm pretty sure it doesn't have anything to do with tagging or the changes I've made here? But I can submit another PR to raise a better error here, if it's important to fix right away.

Copy link
Member

@yusuf-musleh yusuf-musleh left a comment

Choose a reason for hiding this comment

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

@pomegranited Looks good to me 👍 great work!

The error you found happens when the library content block doesn't have its source_library_id set, which users have to do in the Studio/course authoring MFE

Thanks for looking into it, you're right the error seems unrelated to the changes here, and I think it's fine leaving it for another ticket/PR.

  • I tested this: I following the testing instructions and confirmed everything is working well as expected
  • I read through the code
  • I checked for accessibility issues
  • Includes documentation
  • I made sure any change in configuration variables is reflected in the corresponding client's configuration-secure repository.

Copy link
Contributor

@bradenmacdonald bradenmacdonald left a comment

Choose a reason for hiding this comment

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

Looks great - just a couple small questions

cms/djangoapps/contentstore/helpers.py Outdated Show resolved Hide resolved
openedx/core/djangoapps/content_staging/models.py Outdated Show resolved Hide resolved
@pomegranited
Copy link
Contributor Author

Thank you for your review @bradenmacdonald ! I think I've addressed everything, but let me know if not?

@bradenmacdonald bradenmacdonald merged commit 7ad2256 into openedx:master Apr 2, 2024
68 checks passed
@bradenmacdonald bradenmacdonald deleted the jill/no-tags-in-olx branch April 2, 2024 16:59
@openedx-webhooks
Copy link

@pomegranited 🎉 Your pull request was merged! Please take a moment to answer a two question survey so we can improve your experience in the future.

@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX staging environment in preparation for a release to production.

@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX production environment.

1 similar comment
@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX production environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
open-source-contribution PR author is not from Axim or 2U
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[Tagging] Don't put tags in OLX when duplicating/copy-pasting
5 participants