openedx.core.djangoapps.content_libraries package

Contents

openedx.core.djangoapps.content_libraries package#

Subpackages#

Submodules#

openedx.core.djangoapps.content_libraries.apps module#

Django AppConfig for Content Libraries Implementation

class openedx.core.djangoapps.content_libraries.apps.ContentLibrariesConfig(app_name, app_module)#

Bases: AppConfig

Django AppConfig for Content Libraries Implementation

name = 'openedx.core.djangoapps.content_libraries'#
plugin_app = {'settings_config': {'cms.djangoapp': {}}, 'url_config': {'cms.djangoapp': {'namespace': 'content_libraries'}}}#
ready()#

Import signal handler’s module to ensure they are registered.

verbose_name = 'Content Libraries (Learning-Core-based)'#

openedx.core.djangoapps.content_libraries.auth module#

Content Library LTI authentication.

This module offers an authentication backend to support LTI launches within content libraries.

class openedx.core.djangoapps.content_libraries.auth.LtiAuthenticationBackend#

Bases: ModelBackend

Authenticate based on content library LTI profile.

The backend assumes the profile was previously created and its presence is enough to assume the launch claims are valid.

authenticate(request, iss=None, aud=None, sub=None, **kwargs)#

Authenticate if the user in the request has an LTI profile.

openedx.core.djangoapps.content_libraries.constants module#

Constants used for the content libraries.

openedx.core.djangoapps.content_libraries.library_context module#

Definition of “Library” as a learning context.

class openedx.core.djangoapps.content_libraries.library_context.LibraryContextImpl(**kwargs)#

Bases: LearningContext

Implements content libraries as a learning context.

This is the new content libraries based on Learning Core, not the old content libraries based on modulestore.

block_exists(usage_key: LibraryUsageLocatorV2)#

Does the block for this usage_key exist in this Library?

Note that this applies to all versions, i.e. you can put a usage key for a piece of content that has been soft-deleted (removed from Drafts), and it will still return True here. That’s because for the purposes of permission checking, we just want to know whether that block has ever existed in this Library, because we could be looking at any older version of it.

can_edit_block(user: User | AnonymousUser, usage_key: UsageKeyV2) bool#

Assuming a block with the specified ID (usage_key) exists, does the specified user have permission to edit it (make changes to the fields / authored data store)?

May raise ContentLibraryNotFound if the library does not exist.

can_view_block(user: User | AnonymousUser, usage_key: UsageKeyV2) bool#

Does the specified usage key exist in its context, and if so, does the specified user have permission to view it and interact with it (call handlers, save user state, etc.)?

May raise ContentLibraryNotFound if the library does not exist.

can_view_block_for_editing(user: User | AnonymousUser, usage_key: UsageKeyV2) bool#

Assuming a block with the specified ID (usage_key) exists, does the specified user have permission to view its fields and OLX details (but not necessarily to make changes to it)?

May raise ContentLibraryNotFound if the library does not exist.

send_block_updated_event(usage_key: UsageKeyV2)#

Send a “block updated” event for the library block with the given usage_key.

send_container_updated_events(usage_key: UsageKeyV2)#

Send “container updated” events for containers that contains the library block with the given usage_key.

openedx.core.djangoapps.content_libraries.models module#

Content Libraries Models#

This module contains the models for new Content Libraries.

LTI 1.3 Models#

Content Libraries serves learning-core-based content through LTI 1.3 launches. The interface supports resource link launches and grading services. Two use cases justify the current data model to support LTI launches. They are:

  1. Authentication and authorization. This use case demands management of user lifecycle to authorize access to content and grade submission, and it introduces a model to own the authentication business logic related to LTI.

  2. Grade and assignments. When AGS is supported, content libraries store additional information concerning the launched resource so that, once the grading sub-system submits the score, it can retrieve them to propagate the score update into the LTI platform’s grade book.

Relationship with LMS’s lti_provider` models#

The data model above is similar to the one provided by the current LTI 1.1 implementation for modulestore and courseware content. But, Content Libraries is orthogonal. Its use-case is to offer standalone, embedded content from a specific backend (learning core). As such, it decouples from LTI 1.1. and the logic assume no relationship or impact across the two applications. The same reasoning applies to steps beyond the data model, such as at the XBlock runtime, authentication, and score handling, etc.

class openedx.core.djangoapps.content_libraries.models.ContentLibrary(*args, **kwargs)#

Bases: Model

A Content Library is a collection of content (XBlocks and/or static assets)

All actual content is stored in Learning Core, and any data that we’d want to transfer to another instance if this library were exported and then re-imported on another Open edX instance should be kept in Learning Core. This model in Studio should only be used to track settings specific to this Open edX instance, like who has permission to edit this content library.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

property allow_lti#

True if there is at least one LTI tool configuration associated if this library.

allow_public_learning#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

allow_public_read#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

classmethod authorize_lti_launch(library_key, *, issuer, client_id=None)#

Check if the given Issuer and Client ID are authorized to launch content from this library.

authorized_lti_configs#

Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.

In the example:

class Pizza(Model):
    toppings = ManyToManyField(Topping, related_name='pizzas')

Pizza.toppings and Topping.pizzas are ManyToManyDescriptor instances.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

get_license_display(*, field=<django.db.models.fields.CharField: license>)#
id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

import_tasks#

Accessor to the related objects manager on the reverse side of a many-to-one relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Parent.children is a ReverseManyToOneDescriptor instance.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

learning_package#

Accessor to the related object on the forward side of a one-to-one relation.

In the example:

class Restaurant(Model):
    place = OneToOneField(Place, related_name='restaurant')

Restaurant.place is a ForwardOneToOneDescriptor instance.

learning_package_id#
property library_key#

Get the LibraryLocatorV2 opaque key for this library

license#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

objects: ClassVar[ContentLibraryManager] = <openedx.core.djangoapps.content_libraries.models.ContentLibraryManager object>#
org#

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

org_id#
permission_grants#

Accessor to the related objects manager on the reverse side of a many-to-one relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Parent.children is a ReverseManyToOneDescriptor instance.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

slug#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

class openedx.core.djangoapps.content_libraries.models.ContentLibraryBlockImportTask(*args, **kwargs)#

Bases: Model

Model of a task to import blocks from an external source (e.g. modulestore).

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

TASK_CREATED = 'created'#
TASK_FAILED = 'failed'#
TASK_PENDING = 'pending'#
TASK_RUNNING = 'running'#
TASK_STATE_CHOICES = (('created', 'Task was created, but not queued to run.'), ('pending', 'Task was created and queued to run.'), ('running', 'Task is running.'), ('failed', 'Task finished, but some blocks failed to import.'), ('successful', 'Task finished successfully.'))#
TASK_SUCCESSFUL = 'successful'#
course_id#

DO NOT REUSE THIS CLASS. Provided for backwards compatibility only!

A placeholder class that provides a way to set the attribute on the model.

created_at#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

classmethod execute(import_task_id)#

A context manager to manage a task that is being executed.

get_next_by_created_at(*, field=<django.db.models.fields.DateTimeField: created_at>, is_next=True, **kwargs)#
get_next_by_updated_at(*, field=<django.db.models.fields.DateTimeField: updated_at>, is_next=True, **kwargs)#
get_previous_by_created_at(*, field=<django.db.models.fields.DateTimeField: created_at>, is_next=False, **kwargs)#
get_previous_by_updated_at(*, field=<django.db.models.fields.DateTimeField: updated_at>, is_next=False, **kwargs)#
get_state_display(*, field=<django.db.models.fields.CharField: state>)#
id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

library#

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

library_id#
objects = <django.db.models.manager.Manager object>#
progress#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

save_progress(progress)#
state#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

updated_at#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

class openedx.core.djangoapps.content_libraries.models.ContentLibraryManager(*args, **kwargs)#

Bases: Manager

Custom manager for ContentLibrary class.

get_by_key(library_key) ContentLibrary#

Get the ContentLibrary for the given LibraryLocatorV2 key.

class openedx.core.djangoapps.content_libraries.models.ContentLibraryPermission(*args, **kwargs)#

Bases: Model

Row recording permissions for a content library

ACCESS_LEVEL_CHOICES = (('admin', 'Administer users and author content'), ('author', 'Author content'), ('read', 'Read-only'))#
ADMIN_LEVEL = 'admin'#
AUTHOR_LEVEL = 'author'#
exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

READ_LEVEL = 'read'#
access_level#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

get_access_level_display(*, field=<django.db.models.fields.CharField: access_level>)#
group#

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

group_id#
id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

library#

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

library_id#
objects = <django.db.models.manager.Manager object>#
save(*args, **kwargs)#

Validate any constraints on the model.

We can remove this and replace it with a proper database constraint once we’re upgraded to Django 2.2+

user#

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

user_id#
class openedx.core.djangoapps.content_libraries.models.LtiGradedResource(*args, **kwargs)#

Bases: Model

A content libraries resource launched through LTI with AGS enabled.

Essentially, an instance of this model represents a successful LTI AGS launch. This model links the profile that launched the resource with the resource itself, allowing identifcation of the link through its usage key string and user id.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

ags_lineitem#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

objects = <openedx.core.djangoapps.content_libraries.models.LtiGradedResourceManager object>#
profile#

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

profile_id#
resource_id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

resource_title#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

update_score(weighted_earned, weighted_possible, timestamp)#

Use LTI’s score service to update the LTI platform’s gradebook.

This method synchronously send a request to the LTI platform to update the assignment score.

usage_key#

DO NOT REUSE THIS CLASS. Provided for backwards compatibility only!

A placeholder class that provides a way to set the attribute on the model.

class openedx.core.djangoapps.content_libraries.models.LtiGradedResourceManager(*args, **kwargs)#

Bases: Manager

A custom manager for the graded resources model.

get_from_user_id(user_id, **kwds)#

Retrieve a resource for a given user id holding an lti profile.

upsert_from_ags_launch(user, block, resource_endpoint, resource_link)#

Update or create a graded resource at AGS launch.

class openedx.core.djangoapps.content_libraries.models.LtiProfile(*args, **kwargs)#

Bases: Model

Content Libraries LTI’s profile for Open edX users.

Unless Anonymous, this should be a unique representation of the LTI subject (as per the client token sub identify claim) that initiated an LTI launch through Content Libraries.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

client_id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

created_at#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

get_next_by_created_at(*, field=<django.db.models.fields.DateTimeField: created_at>, is_next=True, **kwargs)#
get_previous_by_created_at(*, field=<django.db.models.fields.DateTimeField: created_at>, is_next=False, **kwargs)#
id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

lti_resources#

Accessor to the related objects manager on the reverse side of a many-to-one relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Parent.children is a ReverseManyToOneDescriptor instance.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

objects = <openedx.core.djangoapps.content_libraries.models.LtiProfileManager object>#
platform_id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

save(*args, **kwds)#

Get or create an edx user on save.

subject_id#

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

property subject_url#

An local URL that is known to uniquely identify this profile.

We take advantage of the fact that platform id is required to be an URL and append paths with the reamaining keys to it.

user#

Accessor to the related object on the forward side of a one-to-one relation.

In the example:

class Restaurant(Model):
    place = OneToOneField(Place, related_name='restaurant')

Restaurant.place is a ForwardOneToOneDescriptor instance.

user_id#
class openedx.core.djangoapps.content_libraries.models.LtiProfileManager(*args, **kwargs)#

Bases: Manager

Custom manager of LtiProfile mode.

get_from_claims(*, iss, aud, sub)#

Get the an instance from a LTI launch claims.

get_or_create_from_claims(*, iss, aud, sub)#

Get or create an instance from a LTI launch claims.

openedx.core.djangoapps.content_libraries.permissions module#

Permissions for Content Libraries (v2, Learning-Core-based)

openedx.core.djangoapps.content_libraries.signal_handlers module#

Content library signal handlers.

openedx.core.djangoapps.content_libraries.signal_handlers.library_collection_deleted(sender, instance, **kwargs)#

Raises LIBRARY_COLLECTION_DELETED for the deleted Collection.

openedx.core.djangoapps.content_libraries.signal_handlers.library_collection_entities_changed(sender, instance, action, pk_set, **kwargs)#

Sends a CONTENT_OBJECT_ASSOCIATIONS_CHANGED event for components added/removed/cleared from a collection.

openedx.core.djangoapps.content_libraries.signal_handlers.library_collection_entity_deleted(sender, instance, **kwargs)#

Sends a CONTENT_OBJECT_ASSOCIATIONS_CHANGED event for components removed from a collection.

openedx.core.djangoapps.content_libraries.signal_handlers.library_collection_entity_saved(sender, instance, created, **kwargs)#

Sends a CONTENT_OBJECT_ASSOCIATIONS_CHANGED event for components added to a collection.

openedx.core.djangoapps.content_libraries.signal_handlers.library_collection_saved(sender, instance, created, **kwargs)#

Raises LIBRARY_COLLECTION_CREATED if the Collection is new, or LIBRARY_COLLECTION_UPDATED if updated an existing Collection.

openedx.core.djangoapps.content_libraries.signal_handlers.score_changed_handler(sender, **kwargs)#

Match the score event to an LTI resource and update.

openedx.core.djangoapps.content_libraries.tasks module#

Celery tasks for Content Libraries.

Architecture note:

Several functions in this file manage the copying/updating of blocks in modulestore and learning core. These operations should only be performed within the context of CMS. However, due to existing edx-platform code structure, we’ve had to define the functions in shared source tree (openedx/) and the tasks are registered in both LMS and CMS.

To ensure that we’re not accidentally importing things from learning core in the LMS context, we use ensure_cms throughout this module.

A longer-term solution to this issue would be to move the content_libraries app to cms: openedx/edx-platform#33428

class openedx.core.djangoapps.content_libraries.tasks.LibrarySyncChildrenTask#

Bases: UserTask

Base class for tasks which operate upon library_content children.

classmethod generate_name(arguments_dict) str#

Create a name for this particular import task instance.

Should be both: a. semi human-friendly b. something we can query in order to determine whether the dest block has a task in progress

Parameters:

arguments_dict (dict) – The arguments given to the task function

ignore_result = False#

If enabled the worker won’t store task state and return values for this task. Defaults to the :setting:`task_ignore_result` setting.

priority = None#

Default task priority.

rate_limit = None#

None (no rate limit), ‘100/s’ (hundred tasks a second), ‘100/m’ (hundred tasks a minute),`’100/h’` (hundred tasks an hour)

Type:

Rate limit for this task type. Examples

reject_on_worker_lost = None#

Even if acks_late is enabled, the worker will acknowledge tasks when the worker process executing them abruptly exits or is signaled (e.g., :sig:`KILL`/:sig:`INT`, etc).

Setting this to true allows the message to be re-queued instead, so that the task will execute again by the same worker, or another worker.

Warning: Enabling this can cause message loops; make sure you know what you’re doing.

request_stack = <celery.utils.threads._LocalStack object>#

Task request stack, the current request will be the topmost.

serializer = 'json'#

The name of a serializer that are registered with kombu.serialization.registry. Default is ‘json’.

store_errors_even_if_ignored = True#

When enabled errors will be stored even if the task is otherwise configured to ignore results.

track_started = True#

If enabled the task will report its status as ‘started’ when the task is executed by a worker. Disabled by default as the normal behavior is to not report that level of granularity. Tasks are either pending, finished, or waiting to be retried.

Having a ‘started’ status can be useful for when there are long running tasks and there’s a need to report what task is currently running.

The application default can be overridden using the :setting:`task_track_started` setting.

typing = True#

Enable argument checking. You can set this to false if you don’t want the signature to be checked when calling the task. Defaults to app.strict_typing.

openedx.core.djangoapps.content_libraries.tasks.wait_for_post_publish_events(publish_log: PublishLog, library_key: LibraryLocatorV2)#

After publishing some changes, trigger the required event handlers (e.g. update the search index). Try to wait for that to complete before returning, up to some reasonable timeout, and then finish anything remaining asynchonrously.

openedx.core.djangoapps.content_libraries.tasks.wait_for_post_revert_events(draft_change_log: DraftChangeLog, library_key: LibraryLocatorV2)#

After discard all changes in a library, trigger the required event handlers (e.g. update the search index). Try to wait for that to complete before returning, up to some reasonable timeout, and then finish anything remaining asynchonrously.

openedx.core.djangoapps.content_libraries.urls module#

Module contents#