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: Language auto tagging library blocks #34056

Merged
Show file tree
Hide file tree
Changes from all 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
57 changes: 54 additions & 3 deletions openedx/core/djangoapps/content_tagging/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,32 @@
Automatic tagging of content
"""

import crum
import logging

from django.dispatch import receiver
from openedx_events.content_authoring.data import CourseData, XBlockData
from openedx_events.content_authoring.signals import COURSE_CREATED, XBLOCK_CREATED, XBLOCK_DELETED, XBLOCK_UPDATED
from openedx_events.content_authoring.data import (
CourseData,
XBlockData,
LibraryBlockData,
)
from openedx_events.content_authoring.signals import (
COURSE_CREATED,
XBLOCK_CREATED,
XBLOCK_DELETED,
XBLOCK_UPDATED,
LIBRARY_BLOCK_CREATED,
LIBRARY_BLOCK_UPDATED,
LIBRARY_BLOCK_DELETED,
)

from .tasks import delete_course_tags
from .tasks import (
delete_xblock_tags,
update_course_tags,
update_xblock_tags
update_xblock_tags,
update_library_block_tags,
delete_library_block_tags,
)
from .toggles import CONTENT_TAGGING_AUTO

Expand Down Expand Up @@ -74,3 +89,39 @@ def delete_tag_xblock(**kwargs):
delete_course_tags.delay(str(xblock_info.usage_key.course_key))

delete_xblock_tags.delay(str(xblock_info.usage_key))


@receiver(LIBRARY_BLOCK_CREATED)
@receiver(LIBRARY_BLOCK_UPDATED)
def auto_tag_library_block(**kwargs):
"""
Automatically tag Library Blocks based on metadata
"""
if not CONTENT_TAGGING_AUTO.is_enabled():
return

library_block_data = kwargs.get("library_block", None)
if not library_block_data or not isinstance(library_block_data, LibraryBlockData):
log.error("Received null or incorrect data for event")
return

current_request = crum.get_current_request()
update_library_block_tags.delay(
str(library_block_data.usage_key), current_request.LANGUAGE_CODE
)


@receiver(LIBRARY_BLOCK_DELETED)
def delete_tag_library_block(**kwargs):
"""
Delete tags associated with a Library XBlock whenever the block is deleted.
"""
library_block_data = kwargs.get("library_block", None)
if not library_block_data or not isinstance(library_block_data, LibraryBlockData):
log.error("Received null or incorrect data for event")
return

try:
delete_library_block_tags(str(library_block_data.usage_key))
except Exception as err: # pylint: disable=broad-except
log.error(f"Failed to delete library block tags: {err}")
46 changes: 46 additions & 0 deletions openedx/core/djangoapps/content_tagging/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.contrib.auth import get_user_model
from edx_django_utils.monitoring import set_code_owner_attribute
from opaque_keys.edx.keys import LearningContextKey, UsageKey
from opaque_keys.edx.locator import LibraryUsageLocatorV2
from openedx_tagging.core.tagging.models import Taxonomy

from xmodule.modulestore.django import modulestore
Expand Down Expand Up @@ -152,3 +153,48 @@ def delete_xblock_tags(usage_key_str: str) -> bool:
except Exception as e: # pylint: disable=broad-except
log.error("Error deleting tags for XBlock with id: %s. %s", usage_key, e)
return False


@shared_task(base=LoggedTask)
@set_code_owner_attribute
def update_library_block_tags(usage_key_str: str, language_code: str) -> bool:
"""
Updates the automatically-managed tags for a content library block
whenever it is created/updated

Params:
usage_key_str (str): identifier of the Library Block
langauge_code (str): the preferred language code of the user
"""
try:
usage_key = LibraryUsageLocatorV2.from_string(usage_key_str)

log.info("Updating tags for Library Block with id: %s", usage_key)

_set_initial_language_tag(usage_key, language_code)
return True
except Exception as e: # pylint: disable=broad-except
log.error("Error updating tags for XBlock with id: %s. %s", usage_key, e)
return False


@shared_task(base=LoggedTask)
@set_code_owner_attribute
def delete_library_block_tags(usage_key_str: str) -> bool:
"""
Delete the tags for a Library Block (when the Library Block itself is deleted).

Params:
usage_key_str (str): identifier of the Library Block
"""
try:
usage_key = LibraryUsageLocatorV2.from_string(usage_key_str)

log.info("Deleting tags for Library Block with id: %s", usage_key)

_delete_tags(usage_key)

return True
except Exception as e: # pylint: disable=broad-except
log.error("Error deleting tags for Library Block with id: %s. %s", usage_key, e)
return False
70 changes: 68 additions & 2 deletions openedx/core/djangoapps/content_tagging/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@

from unittest.mock import patch

from django.test import override_settings
from django.test import override_settings, LiveServerTestCase
from django.http import HttpRequest
from edx_toggles.toggles.testutils import override_waffle_flag
from openedx_tagging.core.tagging.models import Tag, Taxonomy
from organizations.models import Organization

from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangolib.testing.utils import skip_unless_cms
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase
from openedx.core.lib.blockstore_api import create_collection
from openedx.core.djangoapps.content_libraries.api import create_library, create_library_block, delete_library_block
from openedx.core.lib.blockstore_api.tests.base import BlockstoreAppTestMixin

from .. import api
from ..models.base import TaxonomyOrg
Expand Down Expand Up @@ -51,7 +55,12 @@ def setUp(self):

@skip_unless_cms # Auto-tagging is only available in the CMS
@override_waffle_flag(CONTENT_TAGGING_AUTO, active=True)
class TestAutoTagging(LanguageTaxonomyTestMixin, ModuleStoreTestCase): # type: ignore[misc]
class TestAutoTagging( # type: ignore[misc]
LanguageTaxonomyTestMixin,
ModuleStoreTestCase,
BlockstoreAppTestMixin,
LiveServerTestCase
):
"""
Test if the Course and XBlock tags are automatically created
"""
Expand Down Expand Up @@ -237,3 +246,60 @@ def test_waffle_disabled_create_delete_xblock(self):

# Still no tags
assert self._check_tag(usage_key_str, LANGUAGE_TAXONOMY_ID, None)

def test_create_delete_library_block(self):
# Create collection and library
collection = create_collection("Test library collection")
library = create_library(
collection_uuid=collection.uuid,
org=self.orgA,
slug="lib_a",
title="Library Org A",
description="This is a library from Org A",
)

fake_request = HttpRequest()
fake_request.LANGUAGE_CODE = "pt-br"
with patch('crum.get_current_request', return_value=fake_request):
# Create Library Block
library_block = create_library_block(library.key, "problem", "Problem1")

usage_key_str = str(library_block.usage_key)

# Check if the tags are created in the Library Block with the user's preferred language
assert self._check_tag(usage_key_str, LANGUAGE_TAXONOMY_ID, 'Português (Brasil)')

# Delete the XBlock
delete_library_block(library_block.usage_key)

# Check if the tags are deleted
assert self._check_tag(usage_key_str, LANGUAGE_TAXONOMY_ID, None)

@override_waffle_flag(CONTENT_TAGGING_AUTO, active=False)
def test_waffle_disabled_create_delete_library_block(self):
# Create collection and library
collection = create_collection("Test library collection 2")
library = create_library(
collection_uuid=collection.uuid,
org=self.orgA,
slug="lib_a2",
title="Library Org A 2",
description="This is a library from Org A 2",
)

fake_request = HttpRequest()
fake_request.LANGUAGE_CODE = "pt-br"
with patch('crum.get_current_request', return_value=fake_request):
# Create Library Block
library_block = create_library_block(library.key, "problem", "Problem2")

usage_key_str = str(library_block.usage_key)

# No tags created
assert self._check_tag(usage_key_str, LANGUAGE_TAXONOMY_ID, None)

# Delete the XBlock
delete_library_block(library_block.usage_key)

# Still no tags
assert self._check_tag(usage_key_str, LANGUAGE_TAXONOMY_ID, None)
Loading