feat: paste tags when pasting xblocks with tag data (#34270)
This commit is contained in:
@@ -13,7 +13,8 @@ from openedx_tagging.core.tagging.models import ObjectTag, Taxonomy
|
||||
from organizations.models import Organization
|
||||
|
||||
from .models import TaxonomyOrg
|
||||
from .types import ObjectTagByObjectIdDict, TaxonomyDict
|
||||
from .types import ContentKey, ObjectTagByObjectIdDict, TagValuesByTaxonomyExportIdDict, TaxonomyDict
|
||||
from .utils import check_taxonomy_context_key_org, get_context_key_from_key
|
||||
|
||||
|
||||
def create_taxonomy(
|
||||
@@ -161,16 +162,42 @@ def get_all_object_tags(
|
||||
|
||||
for object_id, block_tags in groupby(all_object_tags, lambda x: x.object_id):
|
||||
grouped_object_tags[object_id] = {}
|
||||
for taxonomy_id, taxonomy_tags in groupby(block_tags, lambda x: x.tag.taxonomy_id):
|
||||
for taxonomy_id, taxonomy_tags in groupby(block_tags, lambda x: x.tag.taxonomy_id if x.tag else 0):
|
||||
object_tags_list = list(taxonomy_tags)
|
||||
grouped_object_tags[object_id][taxonomy_id] = object_tags_list
|
||||
|
||||
if taxonomy_id not in taxonomies:
|
||||
assert object_tags_list[0].tag
|
||||
assert object_tags_list[0].tag.taxonomy
|
||||
taxonomies[taxonomy_id] = object_tags_list[0].tag.taxonomy
|
||||
|
||||
return grouped_object_tags, taxonomies
|
||||
|
||||
|
||||
def set_object_tags(
|
||||
content_key: ContentKey,
|
||||
object_tags: TagValuesByTaxonomyExportIdDict,
|
||||
) -> None:
|
||||
"""
|
||||
Sets the tags for the given content object.
|
||||
"""
|
||||
context_key = get_context_key_from_key(content_key)
|
||||
|
||||
for taxonomy_export_id, tags_values in object_tags.items():
|
||||
taxonomy = oel_tagging.get_taxonomy_by_export_id(taxonomy_export_id)
|
||||
if not taxonomy:
|
||||
continue
|
||||
|
||||
if not check_taxonomy_context_key_org(taxonomy, context_key):
|
||||
continue
|
||||
|
||||
oel_tagging.tag_object(
|
||||
object_id=str(content_key),
|
||||
taxonomy=taxonomy,
|
||||
tags=tags_values,
|
||||
)
|
||||
|
||||
|
||||
# Expose the oel_tagging APIs
|
||||
|
||||
get_taxonomy = oel_tagging.get_taxonomy
|
||||
@@ -181,3 +208,4 @@ delete_object_tags = oel_tagging.delete_object_tags
|
||||
resync_object_tags = oel_tagging.resync_object_tags
|
||||
get_object_tags = oel_tagging.get_object_tags
|
||||
tag_object = oel_tagging.tag_object
|
||||
add_tag_to_taxonomy = oel_tagging.add_tag_to_taxonomy
|
||||
|
||||
@@ -20,10 +20,9 @@ from common.djangoapps.student.roles import (
|
||||
)
|
||||
|
||||
from .models import TaxonomyOrg
|
||||
from .utils import get_context_key_from_key_string, TaggingRulesCache
|
||||
from .utils import check_taxonomy_context_key_org, get_context_key_from_key_string, rules_cache
|
||||
|
||||
|
||||
rules_cache = TaggingRulesCache()
|
||||
UserType = Union[django.contrib.auth.models.User, django.contrib.auth.models.AnonymousUser]
|
||||
|
||||
|
||||
@@ -288,19 +287,12 @@ def can_change_object_tag(
|
||||
"""
|
||||
if oel_tagging.can_change_object_tag(user, perm_obj):
|
||||
if perm_obj and perm_obj.taxonomy and perm_obj.object_id:
|
||||
# can_change_object_tag_objectid already checked that object_id is valid and has an org,
|
||||
# so these statements will not fail. But we need to assert to keep the type checker happy.
|
||||
try:
|
||||
context_key = get_context_key_from_key_string(perm_obj.object_id)
|
||||
assert context_key.org
|
||||
except (ValueError, AssertionError):
|
||||
except ValueError:
|
||||
return False # pragma: no cover
|
||||
|
||||
is_all_org, taxonomy_orgs = TaxonomyOrg.get_organizations(perm_obj.taxonomy)
|
||||
if not is_all_org:
|
||||
# Ensure the object_id's org is among the allowed taxonomy orgs
|
||||
object_org = rules_cache.get_orgs([context_key.org])
|
||||
return bool(object_org) and object_org[0] in taxonomy_orgs
|
||||
return check_taxonomy_context_key_org(perm_obj.taxonomy, context_key)
|
||||
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -10,7 +10,9 @@ from opaque_keys.edx.locator import LibraryLocatorV2
|
||||
from openedx_tagging.core.tagging.models import ObjectTag, Taxonomy
|
||||
|
||||
ContentKey = Union[LibraryLocatorV2, CourseKey, UsageKey]
|
||||
ContextKey = Union[LibraryLocatorV2, CourseKey]
|
||||
|
||||
ObjectTagByTaxonomyIdDict = Dict[int, List[ObjectTag]]
|
||||
ObjectTagByObjectIdDict = Dict[str, ObjectTagByTaxonomyIdDict]
|
||||
TaxonomyDict = Dict[int, Taxonomy]
|
||||
TagValuesByTaxonomyExportIdDict = Dict[str, List[str]]
|
||||
|
||||
@@ -7,11 +7,13 @@ from edx_django_utils.cache import RequestCache
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from opaque_keys.edx.locator import LibraryLocatorV2
|
||||
from openedx_tagging.core.tagging.models import Taxonomy
|
||||
from organizations.models import Organization
|
||||
|
||||
from openedx.core.djangoapps.content_libraries.api import get_libraries_for_user
|
||||
|
||||
from .types import ContentKey
|
||||
from .types import ContentKey, ContextKey
|
||||
from .models import TaxonomyOrg
|
||||
|
||||
|
||||
def get_content_key_from_string(key_str: str) -> ContentKey:
|
||||
@@ -30,11 +32,10 @@ def get_content_key_from_string(key_str: str) -> ContentKey:
|
||||
raise ValueError("object_id must be a CourseKey, LibraryLocatorV2 or a UsageKey") from usage_key_error
|
||||
|
||||
|
||||
def get_context_key_from_key_string(key_str: str) -> CourseKey | LibraryLocatorV2:
|
||||
def get_context_key_from_key(content_key: ContentKey) -> ContextKey:
|
||||
"""
|
||||
Get context key from an key string
|
||||
Returns the context key from a given content key.
|
||||
"""
|
||||
content_key = get_content_key_from_string(key_str)
|
||||
# If the content key is a CourseKey or a LibraryLocatorV2, return it
|
||||
if isinstance(content_key, (CourseKey, LibraryLocatorV2)):
|
||||
return content_key
|
||||
@@ -48,6 +49,31 @@ def get_context_key_from_key_string(key_str: str) -> CourseKey | LibraryLocatorV
|
||||
raise ValueError("context must be a CourseKey or a LibraryLocatorV2")
|
||||
|
||||
|
||||
def get_context_key_from_key_string(key_str: str) -> ContextKey:
|
||||
"""
|
||||
Get context key from an key string
|
||||
"""
|
||||
content_key = get_content_key_from_string(key_str)
|
||||
return get_context_key_from_key(content_key)
|
||||
|
||||
|
||||
def check_taxonomy_context_key_org(taxonomy: Taxonomy, context_key: ContextKey) -> bool:
|
||||
"""
|
||||
Returns True if the given taxonomy can tag a object with the given context_key.
|
||||
"""
|
||||
if not context_key.org:
|
||||
return False
|
||||
|
||||
is_all_org, taxonomy_orgs = TaxonomyOrg.get_organizations(taxonomy)
|
||||
|
||||
if is_all_org:
|
||||
return True
|
||||
|
||||
# Ensure the object_id's org is among the allowed taxonomy orgs
|
||||
object_org = rules_cache.get_orgs([context_key.org])
|
||||
return bool(object_org) and object_org[0] in taxonomy_orgs
|
||||
|
||||
|
||||
class TaggingRulesCache:
|
||||
"""
|
||||
Caches data required for computing rules for the duration of the request.
|
||||
@@ -57,7 +83,7 @@ class TaggingRulesCache:
|
||||
"""
|
||||
Initializes the request cache.
|
||||
"""
|
||||
self.request_cache = RequestCache('openedx.core.djangoapps.content_tagging.rules')
|
||||
self.request_cache = RequestCache('openedx.core.djangoapps.content_tagging.utils')
|
||||
|
||||
def get_orgs(self, org_names: list[str] | None = None) -> list[Organization]:
|
||||
"""
|
||||
@@ -102,3 +128,6 @@ class TaggingRulesCache:
|
||||
return [
|
||||
library_orgs[org_name] for org_name in org_names if org_name in library_orgs
|
||||
]
|
||||
|
||||
|
||||
rules_cache = TaggingRulesCache()
|
||||
|
||||
@@ -89,6 +89,11 @@ class XBlockSerializer:
|
||||
with filesystem.open(file_path, 'rb') as fh:
|
||||
data = fh.read()
|
||||
self.static_files.append(StaticFile(name=unit_file.name, data=data, url=None))
|
||||
|
||||
# Serialize and add tag data if any
|
||||
if isinstance(block, TaggedBlockMixin):
|
||||
block.add_tags_to_node(olx_node)
|
||||
|
||||
if block.has_children:
|
||||
self._serialize_children(block, olx_node)
|
||||
return olx_node
|
||||
|
||||
Reference in New Issue
Block a user