openedx.core.djangoapps.content.learning_sequences package

Contents

openedx.core.djangoapps.content.learning_sequences package#

Subpackages#

Submodules#

openedx.core.djangoapps.content.learning_sequences.apps module#

class openedx.core.djangoapps.content.learning_sequences.apps.LearningSequencesConfig(app_name, app_module)#

Bases: AppConfig

name = 'openedx.core.djangoapps.content.learning_sequences'#
ready()#

Override this method in subclasses to run code when Django starts.

verbose_name = 'Learning Sequences and Outlines'#

openedx.core.djangoapps.content.learning_sequences.data module#

Public data structures for this app.

Guidelines:

  1. Make these data structures immutable (frozen=True) wherever possible, as it simplifies debugging.

  2. This module should not import any other part of the app. This is the module that everything else imports, not the other way around. Dependencies should be kept to an absolute minimum–the Python stdlib, attr, opaque keys, and some Django primitives.

  3. Keep the data classes dumb. Business logic should go into the api package modules that operate on this data. Do not attach complex objects with methods as attributes to data classes, as this makes them more difficult to mock out and make guarantees about behavior.

  4. These data classes can perform validation, but only if that validation is entirely self-contained. They MUST NOT make database calls, network requests, or use API functions from other apps. They should not trigger expensive computation.

TODO: Validate all datetimes to be UTC.

class openedx.core.djangoapps.content.learning_sequences.data.ContentErrorData(message: str, usage_key: UsageKey | None = None)#

Bases: object

A human-readable description of something wrong with the content, to ease the debugging of content issues–especially ones where content had to be skipped because it was somehow malformed. The messages should be comprehensible to course teams and support staff.

Errors can refer to things that are not anywhere in the outline, such as when things don’t show up where we expect then to be and we omit them from the outline (unknown tag types, sequences where we expect sections, etc.)

message: str#
usage_key: UsageKey | None#
class openedx.core.djangoapps.content.learning_sequences.data.CourseLearningSequenceData(usage_key: UsageKey, title: str, visibility: VisibilityData = VisibilityData(hide_from_toc=False, visible_to_staff_only=False), exam: ExamData = ExamData(is_practice_exam=False, is_proctored_enabled=False, is_time_limited=False), inaccessible_after_due: bool = False, user_partition_groups: Dict[int, FrozenSet[int]] = NOTHING)#

Bases: object

A Learning Sequence (a.k.a. subsection) from a Course.

It’s possible that at some point we’ll want a LearningSequenceData superclass to encapsulate the minimum set of data that is shared between learning sequences in Courses vs. Pathways vs. Libraries. Such an object would likely not have visibility as that holds course-specific concepts.

class openedx.core.djangoapps.content.learning_sequences.data.CourseOutlineData(course_key: CourseKey, title: str, published_at: datetime, published_version: str, days_early_for_beta: int | None, sections: List[CourseSectionData], self_paced: bool, course_visibility: CourseVisibility, entrance_exam_id: str | None)#

Bases: object

Course Outline information without any user-specific data.

exception DoesNotExist#

Bases: ObjectDoesNotExist

MAX_SEQUENCE_COUNT = 1000#
course_visibility: CourseVisibility#
not_deprecated(_attribute, value)#

Only non-deprecated course keys (e.g. course-v1:) are supported. The older style of “Org/Course/Run” slash-separated keys will not work.

remove(usage_keys)#

Create a new CourseOutlineData by removing a set of UsageKeys.

The UsageKeys can be for Sequences or Sections/Chapters. Removing a Section will remove all Sequences in that Section. It is not an error to pass in UsageKeys that do not exist in the outline.

validate_days_early_for_beta(attribute, value)#

Ensure that days_early_for_beta isn’t negative.

class openedx.core.djangoapps.content.learning_sequences.data.CourseSectionData(usage_key: UsageKey, title: str, visibility: VisibilityData = VisibilityData(hide_from_toc=False, visible_to_staff_only=False), sequences: List[CourseLearningSequenceData] = NOTHING, user_partition_groups: Dict[int, FrozenSet[int]] = NOTHING)#

Bases: object

A Section in a Course (sometimes called a Chapter).

class openedx.core.djangoapps.content.learning_sequences.data.CourseVisibility(*values)#

Bases: Enum

PRIVATE = 'private'#
PUBLIC = 'public'#
PUBLIC_OUTLINE = 'public_outline'#
class openedx.core.djangoapps.content.learning_sequences.data.ExamData(is_practice_exam: bool = False, is_proctored_enabled: bool = False, is_time_limited: bool = False)#

Bases: object

XBlock attributes that describe exams

is_practice_exam: bool#
is_proctored_enabled: bool#
is_time_limited: bool#
exception openedx.core.djangoapps.content.learning_sequences.data.ObjectDoesNotExist#

Bases: Exception

Imitating Django model conventions, we put a subclass of this in some of our data classes to indicate when something is not found.

class openedx.core.djangoapps.content.learning_sequences.data.ScheduleData(course_start: datetime | None, course_end: datetime | None, sections: Dict[UsageKey, ScheduleItemData], sequences: Dict[UsageKey, ScheduleItemData])#

Bases: object

Overall course schedule data.

course_end: datetime | None#
course_start: datetime | None#
sections: Dict[UsageKey, ScheduleItemData]#
sequences: Dict[UsageKey, ScheduleItemData]#
class openedx.core.djangoapps.content.learning_sequences.data.ScheduleItemData(usage_key: UsageKey, start: datetime | None, effective_start: datetime | None, due: datetime | None)#

Bases: object

Scheduling specific data (start/end/due dates) for a single item.

due: datetime | None#
effective_start: datetime | None#
start: datetime | None#
usage_key: UsageKey#
class openedx.core.djangoapps.content.learning_sequences.data.SpecialExamAttemptData(sequences: Dict[UsageKey, Dict])#

Bases: object

Overall special exam attempt data.

sequences: Dict[UsageKey, Dict]#
class openedx.core.djangoapps.content.learning_sequences.data.UserCourseOutlineData(course_key: CourseKey, title: str, published_at: datetime, published_version: str, days_early_for_beta: int | None, sections: List[CourseSectionData], self_paced: bool, course_visibility: CourseVisibility, entrance_exam_id: str | None, base_outline: CourseOutlineData, user: User, at_time: datetime, accessible_sequences: FrozenSet[UsageKey])#

Bases: CourseOutlineData

A course outline that has been customized for a specific user and time.

This is a subclass of CourseOutlineData that has been trimmed to only show those things that a user is allowed to know exists. That being said, this class is a pretty dumb container that doesn’t understand anything about how to derive that trimmed-down state. It’s the responsibility of functions in the learning_sequences.api package to figure out how to derive the correct values to instantiate this class.

accessible_sequences: FrozenSet[UsageKey]#
at_time: datetime#
base_outline: CourseOutlineData#
user: User#
class openedx.core.djangoapps.content.learning_sequences.data.UserCourseOutlineDetailsData(outline: UserCourseOutlineData, schedule: ScheduleData, special_exam_attempts: SpecialExamAttemptData)#

Bases: object

Class that has a user’s course outline plus useful details (like schedules). Will eventually expand to include other systems like Completion.

outline: UserCourseOutlineData#
schedule: ScheduleData#
special_exam_attempts: SpecialExamAttemptData#
class openedx.core.djangoapps.content.learning_sequences.data.VisibilityData(hide_from_toc: bool = False, visible_to_staff_only: bool = False)#

Bases: object

XBlock attributes that help determine item visibility.

hide_from_toc: bool#
visible_to_staff_only: bool#
openedx.core.djangoapps.content.learning_sequences.data.user_partition_groups_not_empty(instance, attribute, value)#

It’s not valid to have a user_partition_groups key with no groups.

For any User Partition, users must be in one group. Associating a piece of content with a user partition but no groups within that partition means that the content would never be accessible to anyone who is not staff, which is likely just an error. There _is_ a use case for this kind of hidden content, but we do that with visible_to_staff_only.

openedx.core.djangoapps.content.learning_sequences.models module#

Models for Learning Sequences and Course Outline generation.

Conventions:

1. Only things in the api package should ever import this file. Do NOT import from views.py or anywhere else. Even if that means we have to give up some DRF niceties.

2. The vast majority of what our public API promises should be efficiently queryable with these models. We might occasionally reach into other systems built for fast course-level queries (e.g. grading, scheduling), but we should never touch ModuleStore or Block Transformers.

3. It’s okay for some basic validation to happen at the model layer. Constraints like uniqueness should absolutely be enforced at this layer. But business logic should happen in the api package.

4. Try to avoid blob-like entites (e.g. JSON fields) as much as possible and push things into normalized tables.

5. In general, keep models as a thin, dumb persistence layer. Let the api package decide when and where it’s safe to cache things.

6. Models and data.py datastructures don’t have to map 1:1, but the convention is that the data struct has a “…Data” appended to it. For instance, LearningContext -> LearningContextData. This is because the Python name for dataclasses (what attrs is close to), and for better backwards compatibility if we want to adopt this convention elsewhere.

7. Strongly separate things that are intrinsic to Learning Sequences as a whole vs. things that only apply to Sequences in the context of a Course. We have other uses for sequences (e.g. Content Libraries, Pathways) and we want to keep that separated.

8. Your app _may_ make foreign keys to models in this app, but you should limit yourself to the LearningContext and LearningSequence models. Other tables are not guaranteed to stick around, and values may be deleted unexpectedly.

class openedx.core.djangoapps.content.learning_sequences.models.ContentError(*args, **kwargs)#

Bases: Model

Human readable content errors.

If something got here, it means that we were able to make _something_ (or there would be no LearningContext at all), but something about the published state of the content is wrong and should be flagged to course and support teams. In many cases, this will be some malformed course structure that gets uploaded via OLX import–a process that is more forgiving than Studio’s UI.

It’s a little weird to store errors in such a freeform manner like this. It would be more efficient and flexible in terms of i18n if we were to store error codes, and leave the message generation to the time of display. The problem with that is that we don’t know up front what the parameterization for such errors would be. The current error messages being created are fairly complicated and include references to multiple attributes of multiple pieces of content with supporting breadcrumbs. Other future errors might just be about display_name string length.

So instead of trying to model all that internally, I’m just allowing for freeform messages. It is quite possible that at some point we will come up with a more comprehensive taxonomy of error messages, at which point we could do a backfill to regenerate this data in a more normalized way.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

id#

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

message#

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

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

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.

publish_report_id#
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.learning_sequences.models.CourseContentVisibilityMixin(*args, **kwargs)#

Bases: Model

This mixin stores XBlock information that affects outline level visibility for a single LearningSequence or Section in a course.

We keep the XBlock field names here, even if they’re somewhat misleading. Please read the comments carefully for each field.

class Meta#

Bases: object

abstract = False#
hide_from_toc#

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

visible_to_staff_only#

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.learning_sequences.models.CourseContext(*args, **kwargs)#

Bases: TimeStampedModel

A model containing course specific information e.g course_visibility

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

course_visibility#

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

created#

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

days_early_for_beta#

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

entrance_exam_id#

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

get_course_visibility_display(*, field=<django.db.models.fields.CharField: course_visibility>)#
get_next_by_created(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=True, **kwargs)#
get_next_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=True, **kwargs)#
get_previous_by_created(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=False, **kwargs)#
get_previous_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=False, **kwargs)#
learning_context#

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_context_id#
modified#

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

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

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.

sections#

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.

self_paced#

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.learning_sequences.models.CourseSection(*args, **kwargs)#

Bases: CourseContentVisibilityMixin, TimeStampedModel

Course Section data, mapping to the ‘chapter’ block type.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

course_context#

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.

course_context_id#
coursesectionsequence_set#

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.

created#

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(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=True, **kwargs)#
get_next_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=True, **kwargs)#
get_previous_by_created(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=False, **kwargs)#
get_previous_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=False, **kwargs)#
hide_from_toc#

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.

modified#

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

new_user_partition_groups: models.ManyToManyField[UserPartitionGroup, models.Model]#

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.

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

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

sectionpartitiongroup_set#

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.

title#

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

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.

visible_to_staff_only#

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.learning_sequences.models.CourseSectionSequence(*args, **kwargs)#

Bases: CourseContentVisibilityMixin, TimeStampedModel

This is a join+ordering table, with entries that could get wiped out and recreated with every course publish. Do NOT make a ForeignKey against this table before implementing smarter replacement logic when publishing happens, or you’ll see deletes all the time.

CourseContentVisibilityMixin is applied here (and not in LearningSequence) because CourseContentVisibilityMixin describes attributes that are part of how a LearningSequence is used within a course, and may not apply to other kinds of LearningSequences.

Do NOT make a foreign key against this table, as the values are deleted and re-created on course publish.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

course_context#

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.

course_context_id#
created#

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

exam#

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

In the example:

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

get_next_by_created(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=True, **kwargs)#
get_next_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=True, **kwargs)#
get_previous_by_created(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=False, **kwargs)#
get_previous_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=False, **kwargs)#
hide_from_toc#

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.

inaccessible_after_due#

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

modified#

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

new_user_partition_groups: models.ManyToManyField[UserPartitionGroup, models.Model]#

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.

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

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

section#

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.

section_id#
sectionsequencepartitiongroup_set#

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.

sequence#

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.

sequence_id#
visible_to_staff_only#

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.learning_sequences.models.CourseSequenceExam(*args, **kwargs)#

Bases: TimeStampedModel

This model stores XBlock information that affects outline level information pertaining to special exams

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

course_section_sequence#

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.

course_section_sequence_id#
created#

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(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=True, **kwargs)#
get_next_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=True, **kwargs)#
get_previous_by_created(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=False, **kwargs)#
get_previous_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, 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.

is_practice_exam#

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

is_proctored_enabled#

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

is_time_limited#

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

modified#

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

objects = <django.db.models.manager.Manager object>#
class openedx.core.djangoapps.content.learning_sequences.models.LearningContext(*args, **kwargs)#

Bases: TimeStampedModel

These are used to group Learning Sequences so that many of them can be pulled at once. We use this instead of a foreign key to CourseOverview because this table can contain things that are not courses.

It is okay to make a foreign key against this table.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

context_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.

course_context#

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

In the example:

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

created#

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(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=True, **kwargs)#
get_next_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=True, **kwargs)#
get_next_by_published_at(*, field=<django.db.models.fields.DateTimeField: published_at>, is_next=True, **kwargs)#
get_previous_by_created(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=False, **kwargs)#
get_previous_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=False, **kwargs)#
get_previous_by_published_at(*, field=<django.db.models.fields.DateTimeField: published_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.

modified#

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

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

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

In the example:

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

published_at#

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

published_version#

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

sequences#

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.

title#

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.learning_sequences.models.LearningSequence(*args, **kwargs)#

Bases: TimeStampedModel

The reason why this model doesn’t have a direct foreign key to CourseSection is because we eventually want to have LearningSequences that exist outside of courses. Attributes that apply directly to all LearningSequences (usage_key, title, learning_context, etc.) will apply here, but anything that is specific to how a LearningContext is rendered for a course (e.g. permissions, staff visibility, is_entrance_exam) wil live in CourseSectionSequence.

It is okay to make a foreign key against this table.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

coursesectionsequence_set#

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.

created#

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(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=True, **kwargs)#
get_next_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, is_next=True, **kwargs)#
get_previous_by_created(*, field=<model_utils.fields.AutoCreatedField: created>, is_next=False, **kwargs)#
get_previous_by_modified(*, field=<model_utils.fields.AutoLastModifiedField: modified>, 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.

learning_context#

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.

learning_context_id#
modified#

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

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

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

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.learning_sequences.models.PublishReport(*args, **kwargs)#

Bases: Model

A report about the content that generated this LearningContext publish.

All these fields could be derived with aggregate SQL functions, but it would be slower and make the admin code more complex. Since we only write at publish time, keeping things in sync is less of a concern.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

content_errors#

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.

id#

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

learning_context#

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_context_id#
num_errors#

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

num_sections#

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

num_sequences#

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

objects = <django.db.models.manager.Manager object>#
class openedx.core.djangoapps.content.learning_sequences.models.SectionPartitionGroup(*args, **kwargs)#

Bases: Model

Model which maps user partition groups to course sections. Used for the user_partition_groups ManyToManyField field in the CourseSection model above. Adds a cascading delete which will delete these many-to-many relations whenever a UserPartitionGroup or CourseSection object is deleted.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

course_section#

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.

course_section_id#
id#

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

objects = <django.db.models.manager.Manager object>#
user_partition_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.

user_partition_group_id#
class openedx.core.djangoapps.content.learning_sequences.models.SectionSequencePartitionGroup(*args, **kwargs)#

Bases: Model

Model which maps user partition groups to course section sequences. Used for the user_partition_groups ManyToManyField field in the CourseSectionSequence model above. Adds a cascading delete which will delete these many-to-many relations whenever a UserPartitionGroup or CourseSectionSequence object is deleted.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

course_section_sequence#

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.

course_section_sequence_id#
id#

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

objects = <django.db.models.manager.Manager object>#
user_partition_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.

user_partition_group_id#
class openedx.core.djangoapps.content.learning_sequences.models.UserPartitionGroup(*args, **kwargs)#

Bases: Model

Each row represents a Group in a UserPartition.

UserPartitions is a pluggable interface. Some IDs are static (with values less than 100). Others are dynamic, picking a range between 100 and 2^31-1. That means that technically, we could use IntegerField instead of BigIntegerField, but a) that limit isn’t actually enforced as far as I can tell; and b) it’s not _that_ much extra storage, so I’m using BigInteger instead (2^63-1).

It’s a pluggable interface (entry points: openedx.user_partition_scheme, openedx.dynamic_partition_generator), so there’s no “UserPartition” model. We need to actually link this against the values passed back from the partitions service in order to map them to names and descriptions.

Any CourseSection or CourseSectionSequence may be associated with any number of UserPartitionGroups. An individual _user_ may only be in one Group for any given User Partition, but a piece of _content_ can be associated with multiple groups. So for instance, for the Enrollment Track user partition, a piece of content may be associated with both “Verified” and “Masters” tracks, while a user may only be in one or the other.

UserPartitionGroups are not associated with LearningSequence directly because User Partitions often carry course-level assumptions (e.g. Enrollment Track) that don’t make sense outside of a Course.

exception DoesNotExist#

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned#

Bases: MultipleObjectsReturned

group_id#

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 = <django.db.models.manager.Manager object>#
partition_id#

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

sec_user_partition_groups#

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.

secseq_user_partition_groups#

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.

sectionpartitiongroup_set#

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.

sectionsequencepartitiongroup_set#

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.

openedx.core.djangoapps.content.learning_sequences.services module#

Learning Sequences Runtime Service

class openedx.core.djangoapps.content.learning_sequences.services.LearningSequencesRuntimeService#

Bases: object

Provides functions of the public API as a class injected into edx-proctoring

get_user_course_outline(course_key, user, at_time)#

Returns UserCourseOutlineData

get_user_course_outline_details(course_key, user, at_time)#

Returns UserCourseOutlineDetailsData

openedx.core.djangoapps.content.learning_sequences.urls module#

openedx.core.djangoapps.content.learning_sequences.views module#

The views.py for this app is intentionally thin, and only exists to translate user input/output to and from the business logic in the api package.

class openedx.core.djangoapps.content.learning_sequences.views.CourseOutlineView(**kwargs)#

Bases: APIView

Display all CourseOutline information for a given user.

class UserCourseOutlineDataSerializer(*args, **kwargs)#

Bases: BaseSerializer

Read-only serializer for CourseOutlineData for this endpoint.

This serializer was purposefully declared inline with the CourseOutlineView to discourage reuse/magic. Our goal is to make it extremely obvious how things are being serialized, and not have surprise regressions because a shared serializer in another module was modified to fix an issue in one of its three use cases.

The data structures in api/data.py send back try to separate the data by lifecycle (e.g. CourseOutlineData vs UserCourseOutlineData) and by logical system (e.g. ScheduleData) to promote performance and pluggability. But for the REST API, we’re just trying to collapse those into the simplest, most convenient output possible.

We also remove any references to “usage_keys” at this layer. UsageKeys are a critical part of the internals of edx-platform, so the in-process API uses them, but we translate them to “ids” for REST API clients.

to_representation(user_course_outline_details)#

Convert to something DRF knows how to serialize (so no custom types)

This is intentionally dumb and lists out every field to make API additions/changes more obvious.

authentication_classes = (<class 'edx_rest_framework_extensions.auth.jwt.authentication.JwtAuthentication'>, <class 'edx_rest_framework_extensions.auth.session.authentication.SessionAuthenticationAllowInactiveUser'>)#
get(request, course_key_str, format=None)#

The CourseOutline, customized for a given user.

TODO: Swagger docs of API. For an exemplar to imitate, see: openedx/edx-platform

Module contents#