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

feat: Copy tags when sync library [FC-0062] #35596

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions openedx/core/djangoapps/content_libraries/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.content_libraries import api as library_api
from openedx.core.djangoapps.xblock.api import load_block
from openedx.core.djangoapps.content_tagging import api as tagging_api
from openedx.core.lib import ensure_cms
from xmodule.capa_block import ProblemBlock
from xmodule.library_content_block import ANY_CAPA_TYPE_VALUE, LibraryContentBlock
Expand Down Expand Up @@ -143,6 +144,9 @@ def generate_block_key(source_key, dest_parent_key):
new_block.save()
store.update_item(new_block, user_id)

# Copy/Sync tags of this block
tagging_api.copy_tags(source_key, new_block_key)

if new_block.has_children:
# Delete existing children in the new block, which can be reimported again if they still exist in the
# source library
Expand Down
1 change: 1 addition & 0 deletions openedx/core/djangoapps/content_tagging/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,4 @@ def tag_object(
resync_object_tags = oel_tagging.resync_object_tags
get_object_tags = oel_tagging.get_object_tags
add_tag_to_taxonomy = oel_tagging.add_tag_to_taxonomy
copy_tags = oel_tagging.copy_tags
17 changes: 17 additions & 0 deletions openedx/core/djangoapps/content_tagging/rest_api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from rest_framework import serializers, fields

from openedx_tagging.core.tagging.rest_api.v1.serializers import (
ObjectTagMinimalSerializer,
TaxonomyListQueryParamsSerializer,
TaxonomySerializer,
)
Expand Down Expand Up @@ -94,3 +95,19 @@ class Meta:
model = TaxonomySerializer.Meta.model
fields = TaxonomySerializer.Meta.fields + ["orgs", "all_orgs"]
read_only_fields = ["orgs", "all_orgs"]


class ObjectTagOrgMinimalSerializer(ObjectTagMinimalSerializer):
ChrisChV marked this conversation as resolved.
Show resolved Hide resolved
"""
Serializer for Object Tags
bradenmacdonald marked this conversation as resolved.
Show resolved Hide resolved
"""

def get_can_delete_objecttag(self, instance):
"""
Verify if the user can delete the object tag.
"""
if instance.is_copied:
# The user can't delete copied tags.
return False

return super().get_can_delete_objecttag(instance)
Original file line number Diff line number Diff line change
Expand Up @@ -1850,6 +1850,31 @@ def test_get_tags(self):
assert status.is_success(response3.status_code)
assert response3.data[str(self.courseA)]["taxonomies"] == expected_tags

def test_get_copied_tags(self):
self.client.force_authenticate(user=self.staffB)

object_id_1 = str(self.courseA)
object_id_2 = str(self.courseB)
tagging_api.tag_object(object_id=object_id_1, taxonomy=self.t1, tags=["android"])
tagging_api.tag_object(object_id=object_id_2, taxonomy=self.t1, tags=["anvil"])
tagging_api.copy_tags(object_id_1, object_id_2)

expected_tags = [{
'name': self.t1.name,
'taxonomy_id': self.t1.pk,
'can_tag_object': True,
'export_id': self.t1.export_id,
'tags': [
{'value': 'android', 'lineage': ['ALPHABET', 'android'], 'can_delete_objecttag': False},
{'value': 'anvil', 'lineage': ['ALPHABET', 'anvil'], 'can_delete_objecttag': True}
]
}]

get_url = OBJECT_TAGS_URL.format(object_id=self.courseB)
response = self.client.get(get_url, format="json")
assert status.is_success(response.status_code)
assert response.data[str(object_id_2)]["taxonomies"] == expected_tags

@ddt.data(
('staff', 'courseA', 8),
('staff', 'libraryA', 8),
Expand Down
8 changes: 7 additions & 1 deletion openedx/core/djangoapps/content_tagging/rest_api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@
)
from ...rules import get_admin_orgs
from .filters import ObjectTagTaxonomyOrgFilterBackend, UserOrgFilterBackend
from .serializers import TaxonomyOrgListQueryParamsSerializer, TaxonomyOrgSerializer, TaxonomyUpdateOrgBodySerializer
from .serializers import (
ObjectTagOrgMinimalSerializer,
TaxonomyOrgListQueryParamsSerializer,
TaxonomyOrgSerializer,
TaxonomyUpdateOrgBodySerializer,
)


class TaxonomyOrgView(TaxonomyView):
Expand Down Expand Up @@ -148,6 +153,7 @@ class ObjectTagOrgView(ObjectTagView):

Refer to ObjectTagView docstring for usage details.
"""
minimal_serilizer_class = ObjectTagOrgMinimalSerializer
filter_backends = [ObjectTagTaxonomyOrgFilterBackend]

def update(self, request, *args, **kwargs) -> Response:
Expand Down
2 changes: 1 addition & 1 deletion requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ libsass==0.10.0
click==8.1.6

# pinning this version to avoid updates while the library is being developed
openedx-learning==0.13.1
openedx-learning==0.14.0

# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
openai<=0.28.1
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ openedx-filters==1.10.0
# -r requirements/edx/kernel.in
# lti-consumer-xblock
# ora2
openedx-learning==0.13.1
openedx-learning @ git+https://github.com/open-craft/openedx-learning.git@chris/FAL-3616-copy-tags
ChrisChV marked this conversation as resolved.
Show resolved Hide resolved
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1373,7 +1373,7 @@ openedx-filters==1.10.0
# -r requirements/edx/testing.txt
# lti-consumer-xblock
# ora2
openedx-learning==0.13.1
openedx-learning @ git+https://github.com/open-craft/openedx-learning.git@chris/FAL-3616-copy-tags
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,7 @@ openedx-filters==1.10.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
openedx-learning==0.13.1
openedx-learning @ git+https://github.com/open-craft/openedx-learning.git@chris/FAL-3616-copy-tags
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/kernel.in
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ openedx-calc # Library supporting mathematical calculatio
openedx-django-require
openedx-events # Open edX Events from Hooks Extension Framework (OEP-50)
openedx-filters # Open edX Filters from Hooks Extension Framework (OEP-50)
openedx-learning # Open edX Learning core (experimental)
git+https://github.com/open-craft/openedx-learning.git@chris/FAL-3616-copy-tags#egg=openedx-learning # Open edX Learning core (experimental)
openedx-mongodbproxy
openedx-django-wiki
path
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,7 @@ openedx-filters==1.10.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
openedx-learning==0.13.1
openedx-learning @ git+https://github.com/open-craft/openedx-learning.git@chris/FAL-3616-copy-tags
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
Expand Down
16 changes: 14 additions & 2 deletions xmodule/tests/test_library_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangolib.testing.utils import skip_unless_cms
from openedx.core.djangoapps.content_libraries.tests.base import ContentLibrariesRestApiTest
from openedx.core.djangoapps.content_tagging import api as tagging_api
from openedx.core.djangoapps.content_tagging.tests.test_api import TestTaxonomyMixin
from xmodule.library_tools import LibraryToolsService
from xmodule.modulestore.tests.factories import CourseFactory, LibraryFactory
from xmodule.modulestore.tests.utils import MixedSplitTestCase


@skip_unless_cms
@ddt.ddt
class ContentLibraryToolsTest(MixedSplitTestCase, ContentLibrariesRestApiTest):
class ContentLibraryToolsTest(TestTaxonomyMixin, MixedSplitTestCase, ContentLibrariesRestApiTest):
"""
Tests for LibraryToolsService.

Expand Down Expand Up @@ -104,7 +106,8 @@ def test_update_children_for_v2_lib(self):
library = self._create_library(
slug="cool-v2-lib", title="The best Library", description="Spectacular description"
)
self._add_block_to_library(library["id"], "unit", "unit1_id")
block = self._add_block_to_library(library["id"], "unit", "unit1_id")
tagging_api.tag_object(block["id"], self.taxonomy_all_orgs, [self.tag_all_orgs.value])

course = CourseFactory.create(modulestore=self.store, user_id=self.user.id)
CourseInstructorRole(course.id).add_users(self.user)
Expand All @@ -126,6 +129,15 @@ def test_update_children_for_v2_lib(self):

assert len(content_block.children) == 1

# Verify that tags are copied to children
child_key = str(content_block.children[0])
tags = tagging_api.get_object_tags(child_key)
assert len(tags) == 1
object_tag = tags[0]
assert object_tag.value == self.tag_all_orgs.value
assert object_tag.taxonomy == self.taxonomy_all_orgs
assert object_tag.object_id == child_key

def test_update_children_for_v1_lib(self):
"""
Test update_children with V1 library as a source.
Expand Down