Skip to content

Commit

Permalink
feat!: add new content authoring event signals
Browse files Browse the repository at this point in the history
  • Loading branch information
rpenido authored and navinkarkera committed Jul 25, 2023
1 parent 0212767 commit c540709
Showing 10 changed files with 628 additions and 74 deletions.
45 changes: 41 additions & 4 deletions docs/hooks/events.rst
Original file line number Diff line number Diff line change
@@ -192,18 +192,55 @@ Content Authoring Events
- *Type*
- *Date added*

* - `COURSE_CATALOG_INFO_CHANGED <https://github.com/openedx/openedx-events/blob/main/openedx_events/content_authoring/signals.py#L23>`_
* - `COURSE_CATALOG_INFO_CHANGED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L25>`_
- org.openedx.content_authoring.course.catalog_info.changed.v1
- `2022-08-24 <https://github.com/openedx/edx-platform/blob/a8598fa1fac5e26ac212aa588e8527e727581742/cms/djangoapps/contentstore/signals/handlers.py#L111>`_

* - `XBLOCK_PUBLISHED <https://github.com/openedx/openedx-events/blob/main/openedx_events/content_authoring/signals.py#L30>`_
* - `XBLOCK_PUBLISHED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L63>`_
- org.openedx.content_authoring.xblock.published.v1
- `2022-12-06 <https://github.com/openedx/edx-platform/blob/master/xmodule/modulestore/mixed.py#L926>`_

* - `XBLOCK_DELETED <https://github.com/openedx/openedx-events/blob/main/openedx_events/content_authoring/signals.py#L42>`_
* - `XBLOCK_DELETED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L75>`_
- org.openedx.content_authoring.xblock.deleted.v1
- `2022-12-06 <https://github.com/openedx/edx-platform/blob/master/xmodule/modulestore/mixed.py#L804>`_

* - `XBLOCK_DUPLICATED <https://github.com/openedx/openedx-events/blob/main/openedx_events/content_authoring/signals.py#L54>`_
* - `XBLOCK_DUPLICATED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L87>`_
- org.openedx.content_authoring.xblock.duplicated.v1
- `2022-12-06 <https://github.com/openedx/edx-platform/blob/master/cms/djangoapps/contentstore/views/item.py#L965>`_

* - `XBLOCK_CREATED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L36>`_
- org.openedx.content_authoring.xblock.created.v1
- 2023-07-20

* - `XBLOCK_UPDATED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L47>`_
- org.openedx.content_authoring.xblock.updated.v1
- 2023-07-20

* - `COURSE_CREATED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L123>`_
- org.openedx.content_authoring.course.created.v1
- 2023-07-20

* - `CONTENT_LIBRARY_CREATED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L134>`_
- org.openedx.content_authoring.content_library.created.v1
- 2023-07-20

* - `CONTENT_LIBRARY_UPDATED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L145>`_
- org.openedx.content_authoring.content_library.updated.v1
- 2023-07-20

* - `CONTENT_LIBRARY_DELETED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L156>`_
- org.openedx.content_authoring.content_library.deleted.v1
- 2023-07-20

* - `LIBRARY_BLOCK_CREATED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L167>`_
- org.openedx.content_authoring.content_library.created.v1
- 2023-07-20

* - `LIBRARY_BLOCK_UPDATED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L178>`_
- org.openedx.content_authoring.content_library.updated.v1
- 2023-07-20

* - `LIBRARY_BLOCK_DELETED <https://github.com/openedx/openedx-events/blob/c0eb4ba1a3d7d066d58e5c87920b8ccb0645f769/openedx_events/content_authoring/signals.py#L189>`_
- org.openedx.content_authoring.content_library.deleted.v1
- 2023-07-20

104 changes: 83 additions & 21 deletions openedx/core/djangoapps/content_libraries/api.py
Original file line number Diff line number Diff line change
@@ -76,6 +76,15 @@
LibraryUsageLocatorV2,
LibraryLocator as LibraryLocatorV1
)
from openedx_events.content_authoring.data import ContentLibraryData, LibraryBlockData
from openedx_events.content_authoring.signals import (
CONTENT_LIBRARY_CREATED,
CONTENT_LIBRARY_DELETED,
CONTENT_LIBRARY_UPDATED,
LIBRARY_BLOCK_CREATED,
LIBRARY_BLOCK_DELETED,
LIBRARY_BLOCK_UPDATED,
)

from organizations.models import Organization
from xblock.core import XBlock
@@ -90,14 +99,7 @@
ContentLibraryPermission,
ContentLibraryBlockImportTask,
)
from openedx.core.djangoapps.content_libraries.signals import (
CONTENT_LIBRARY_CREATED,
CONTENT_LIBRARY_UPDATED,
CONTENT_LIBRARY_DELETED,
LIBRARY_BLOCK_CREATED,
LIBRARY_BLOCK_UPDATED,
LIBRARY_BLOCK_DELETED,
)

from openedx.core.djangoapps.xblock.api import (
get_block_display_name,
get_learning_context_impl,
@@ -452,7 +454,11 @@ def create_library(
)
except IntegrityError:
raise LibraryAlreadyExists(slug) # lint-amnesty, pylint: disable=raise-missing-from
CONTENT_LIBRARY_CREATED.send(sender=None, library_key=ref.library_key)
CONTENT_LIBRARY_CREATED.send_event(
content_library=ContentLibraryData(
library_key=ref.library_key
)
)
return ContentLibraryMetadata(
key=ref.library_key,
bundle_uuid=bundle.uuid,
@@ -602,7 +608,11 @@ def update_library(
assert isinstance(description, str)
fields["description"] = description
update_bundle(ref.bundle_uuid, **fields)
CONTENT_LIBRARY_UPDATED.send(sender=None, library_key=ref.library_key)
CONTENT_LIBRARY_UPDATED.send_event(
content_library=ContentLibraryData(
library_key=ref.library_key
)
)


def delete_library(library_key):
@@ -617,7 +627,11 @@ def delete_library(library_key):
# system, which is a better state than having a reference to a library with
# no backing blockstore bundle.
ref.delete()
CONTENT_LIBRARY_DELETED.send(sender=None, library_key=ref.library_key)
CONTENT_LIBRARY_DELETED.send_event(
content_library=ContentLibraryData(
library_key=ref.library_key
)
)
try:
delete_bundle(bundle_uuid)
except:
@@ -754,7 +768,12 @@ def set_library_block_olx(usage_key, new_olx_str):
write_draft_file(draft.uuid, metadata.def_key.olx_path, new_olx_str.encode('utf-8'))
# Clear the bundle cache so everyone sees the new block immediately:
BundleCache(metadata.def_key.bundle_uuid, draft_name=DRAFT_NAME).clear()
LIBRARY_BLOCK_UPDATED.send(sender=None, library_key=usage_key.context_key, usage_key=usage_key)
LIBRARY_BLOCK_UPDATED.send_event(
library_block=LibraryBlockData(
library_key=usage_key.context_key,
usage_key=usage_key
)
)


def create_library_block(library_key, block_type, definition_id):
@@ -803,7 +822,12 @@ def create_library_block(library_key, block_type, definition_id):
# Clear the bundle cache so everyone sees the new block immediately:
BundleCache(ref.bundle_uuid, draft_name=DRAFT_NAME).clear()
# Now return the metadata about the new block:
LIBRARY_BLOCK_CREATED.send(sender=None, library_key=ref.library_key, usage_key=usage_key)
LIBRARY_BLOCK_CREATED.send_event(
library_block=LibraryBlockData(
library_key=ref.library_key,
usage_key=usage_key
)
)
return get_library_block(usage_key)


@@ -856,7 +880,12 @@ def delete_library_block(usage_key, remove_from_parent=True):
pass
# Clear the bundle cache so everyone sees the deleted block immediately:
lib_bundle.cache.clear()
LIBRARY_BLOCK_DELETED.send(sender=None, library_key=lib_bundle.library_key, usage_key=usage_key)
LIBRARY_BLOCK_DELETED.send_event(
library_block=LibraryBlockData(
library_key=lib_bundle.library_key,
usage_key=usage_key
)
)


def create_library_block_child(parent_usage_key, block_type, definition_id):
@@ -880,7 +909,12 @@ def create_library_block_child(parent_usage_key, block_type, definition_id):
parent_block.runtime.add_child_include(parent_block, include_data)
parent_block.save()
ref = ContentLibrary.objects.get_by_key(parent_usage_key.context_key)
LIBRARY_BLOCK_UPDATED.send(sender=None, library_key=ref.library_key, usage_key=metadata.usage_key)
LIBRARY_BLOCK_UPDATED.send_event(
library_block=LibraryBlockData(
library_key=ref.library_key,
usage_key=metadata.usage_key
)
)
return metadata


@@ -930,7 +964,12 @@ def add_library_block_static_asset_file(usage_key, file_name, file_content):
file_metadata = blockstore_cache.get_bundle_file_metadata_with_cache(
bundle_uuid=def_key.bundle_uuid, path=file_path, draft_name=DRAFT_NAME,
)
LIBRARY_BLOCK_UPDATED.send(sender=None, library_key=lib_bundle.library_key, usage_key=usage_key)
LIBRARY_BLOCK_UPDATED.send_event(
library_block=LibraryBlockData(
library_key=lib_bundle.library_key,
usage_key=usage_key
)
)
return LibraryXBlockStaticFile(path=file_metadata.path, url=file_metadata.url, size=file_metadata.size)


@@ -951,7 +990,12 @@ def delete_library_block_static_asset_file(usage_key, file_name):
write_draft_file(draft.uuid, file_path, contents=None)
# Clear the bundle cache so everyone sees the new file immediately:
lib_bundle.cache.clear()
LIBRARY_BLOCK_UPDATED.send(sender=None, library_key=lib_bundle.library_key, usage_key=usage_key)
LIBRARY_BLOCK_UPDATED.send_event(
library_block=LibraryBlockData(
library_key=lib_bundle.library_key,
usage_key=usage_key
)
)


def get_allowed_block_types(library_key): # pylint: disable=unused-argument
@@ -1044,7 +1088,11 @@ def create_bundle_link(library_key, link_id, target_opaque_key, version=None):
set_draft_link(draft.uuid, link_id, target_bundle_uuid, version)
# Clear the cache:
LibraryBundle(library_key, ref.bundle_uuid, draft_name=DRAFT_NAME).cache.clear()
CONTENT_LIBRARY_UPDATED.send(sender=None, library_key=library_key)
CONTENT_LIBRARY_UPDATED.send_event(
content_library=ContentLibraryData(
library_key=library_key
)
)


def update_bundle_link(library_key, link_id, version=None, delete=False):
@@ -1068,7 +1116,11 @@ def update_bundle_link(library_key, link_id, version=None, delete=False):
set_draft_link(draft.uuid, link_id, link.bundle_uuid, version)
# Clear the cache:
LibraryBundle(library_key, ref.bundle_uuid, draft_name=DRAFT_NAME).cache.clear()
CONTENT_LIBRARY_UPDATED.send(sender=None, library_key=library_key)
CONTENT_LIBRARY_UPDATED.send_event(
content_library=ContentLibraryData(
library_key=library_key
)
)


def publish_changes(library_key):
@@ -1084,7 +1136,12 @@ def publish_changes(library_key):
return # If there is no draft, no action is needed.
LibraryBundle(library_key, ref.bundle_uuid).cache.clear()
LibraryBundle(library_key, ref.bundle_uuid, draft_name=DRAFT_NAME).cache.clear()
CONTENT_LIBRARY_UPDATED.send(sender=None, library_key=library_key, update_blocks=True)
CONTENT_LIBRARY_UPDATED.send_event(
content_library=ContentLibraryData(
library_key=library_key,
update_blocks=True
)
)


def revert_changes(library_key):
@@ -1100,7 +1157,12 @@ def revert_changes(library_key):
else:
return # If there is no draft, no action is needed.
LibraryBundle(library_key, ref.bundle_uuid, draft_name=DRAFT_NAME).cache.clear()
CONTENT_LIBRARY_UPDATED.send(sender=None, library_key=library_key, update_blocks=True)
CONTENT_LIBRARY_UPDATED.send_event(
content_library=ContentLibraryData(
library_key=library_key,
update_blocks=True
)
)


# Import from Courseware
71 changes: 57 additions & 14 deletions openedx/core/djangoapps/content_libraries/libraries_index.py
Original file line number Diff line number Diff line change
@@ -9,16 +9,17 @@
from search.elastic import _translate_hits, RESERVED_CHARACTERS
from search.search_engine_base import SearchEngine
from opaque_keys.edx.locator import LibraryUsageLocatorV2

from openedx.core.djangoapps.content_libraries.constants import DRAFT_NAME
from openedx.core.djangoapps.content_libraries.signals import (
from openedx_events.content_authoring.data import ContentLibraryData, LibraryBlockData
from openedx_events.content_authoring.signals import (
CONTENT_LIBRARY_CREATED,
CONTENT_LIBRARY_UPDATED,
CONTENT_LIBRARY_DELETED,
CONTENT_LIBRARY_UPDATED,
LIBRARY_BLOCK_CREATED,
LIBRARY_BLOCK_UPDATED,
LIBRARY_BLOCK_DELETED,
LIBRARY_BLOCK_UPDATED,
)

from openedx.core.djangoapps.content_libraries.constants import DRAFT_NAME
from openedx.core.djangoapps.content_libraries.library_bundle import LibraryBundle
from openedx.core.djangoapps.content_libraries.models import ContentLibrary
from openedx.core.lib.blockstore_api import get_bundle
@@ -242,17 +243,21 @@ def get_item_definition(cls, item):

@receiver(CONTENT_LIBRARY_CREATED)
@receiver(CONTENT_LIBRARY_UPDATED)
@receiver(LIBRARY_BLOCK_CREATED)
@receiver(LIBRARY_BLOCK_UPDATED)
@receiver(LIBRARY_BLOCK_DELETED)
def index_library(sender, library_key, **kwargs): # pylint: disable=unused-argument
def index_library(**kwargs):
"""
Index library when created or updated, or when its blocks are modified.
"""
content_library = kwargs.get('content_library', None)
if not content_library or not isinstance(content_library, ContentLibraryData):
log.error('Received null or incorrect data for event')
return

library_key = content_library.library_key
update_blocks = content_library.update_blocks
if ContentLibraryIndexer.indexing_is_enabled():
try:
ContentLibraryIndexer.index_items([library_key])
if kwargs.get('update_blocks', False):
if update_blocks:
blocks = LibraryBlockIndexer.get_items(filter_terms={
'library_key': str(library_key)
})
@@ -262,12 +267,38 @@ def index_library(sender, library_key, **kwargs): # pylint: disable=unused-argu
log.exception(e)


@receiver(LIBRARY_BLOCK_CREATED)
@receiver(LIBRARY_BLOCK_DELETED)
@receiver(LIBRARY_BLOCK_UPDATED)
def index_library_block(**kwargs):
"""
Index library when its blocks are created, modified, or deleted.
"""
library_block = kwargs.get('library_block', None)
if not library_block or not isinstance(library_block, LibraryBlockData):
log.error('Received null or incorrect data for event')
return

library_key = library_block.library_key
if ContentLibraryIndexer.indexing_is_enabled():
try:
ContentLibraryIndexer.index_items([library_key])
except ElasticConnectionError as e:
log.exception(e)


@receiver(CONTENT_LIBRARY_DELETED)
def remove_library_index(sender, library_key, **kwargs): # pylint: disable=unused-argument
def remove_library_index(**kwargs):
"""
Remove from index when library is deleted
"""
content_library = kwargs.get('content_library', None)
if not content_library or not isinstance(content_library, ContentLibraryData):
log.error('Received null or incorrect data for event')
return

if ContentLibraryIndexer.indexing_is_enabled():
library_key = content_library.library_key
try:
ContentLibraryIndexer.remove_items([library_key])
blocks = LibraryBlockIndexer.get_items(filter_terms={
@@ -280,10 +311,16 @@ def remove_library_index(sender, library_key, **kwargs): # pylint: disable=unus

@receiver(LIBRARY_BLOCK_CREATED)
@receiver(LIBRARY_BLOCK_UPDATED)
def index_block(sender, usage_key, **kwargs): # pylint: disable=unused-argument
def index_block(**kwargs):
"""
Index block metadata when created
Index block metadata when created or updated
"""
library_block = kwargs.get('library_block', None)
if not library_block or not isinstance(library_block, LibraryBlockData):
log.error('Received null or incorrect data for event')
return

usage_key = library_block.usage_key
if LibraryBlockIndexer.indexing_is_enabled():
try:
LibraryBlockIndexer.index_items([usage_key])
@@ -292,10 +329,16 @@ def index_block(sender, usage_key, **kwargs): # pylint: disable=unused-argument


@receiver(LIBRARY_BLOCK_DELETED)
def remove_block_index(sender, usage_key, **kwargs): # pylint: disable=unused-argument
def remove_block_index(**kwargs):
"""
Remove the block from the index when deleted
"""
library_block = kwargs.get('library_block', None)
if not library_block or not isinstance(library_block, LibraryBlockData):
log.error('Received null or incorrect data for LIBRARY_BLOCK_DELETED')
return

usage_key = library_block.usage_key
if LibraryBlockIndexer.indexing_is_enabled():
try:
LibraryBlockIndexer.remove_items([usage_key])
Loading

0 comments on commit c540709

Please sign in to comment.