learning_sequence doc tweaks and new type annotation style (#27311)
* docs: Add a statement comment. Update learning sequences README. * refactor: Add Python3.6+-style type annotations to attrs classes where possible.
This commit is contained in:
@@ -67,6 +67,7 @@ def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=
|
||||
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded
|
||||
from cms.djangoapps.contentstore.tasks import update_outline_from_modulestore_task, update_search_index
|
||||
if key_supports_outlines(course_key):
|
||||
# Push the course outline to learning_sequences asynchronously.
|
||||
update_outline_from_modulestore_task.delay(str(course_key))
|
||||
|
||||
# Finally call into the course search subsystem
|
||||
|
||||
@@ -9,17 +9,12 @@ users through the LMS, though it is also available to Studio for pushing data
|
||||
into the system. The first API this app implements is computing the Course
|
||||
Outline.
|
||||
|
||||
.. important::
|
||||
This package should _not_ depend on the modulestore directly.
|
||||
|
||||
---------------
|
||||
Direction: Keep
|
||||
---------------
|
||||
|
||||
This package is being actively developed, but in a very early state. We're not
|
||||
going to start feeding data into it (by triggering off of publish) until it's a
|
||||
little more complete, so that it's easier to make drastic changes to the data
|
||||
model if necessary. During development, you can seed data into it by using the
|
||||
``update_course_outline`` management command.
|
||||
The primary path which feeds course outline data into learning sequence models
|
||||
is a signal handler upon a Studio course publish. However, you can also seed
|
||||
data into it by using the ``update_course_outline`` management command.
|
||||
|
||||
-----
|
||||
Usage
|
||||
@@ -49,7 +44,7 @@ rules will have to be reimplmented–as we've already seen with mobile APIs?
|
||||
a single sequence.
|
||||
3. Block Transformers are extremely powerful, but also complex and slow.
|
||||
Optimizing them to suit our use case would be a lot of work and result in
|
||||
even more complexity. Sequence and outline related metadata is much smaller,
|
||||
even more complexity. Sequence- and outline-related metadata is much smaller,
|
||||
and we can make simplifying assumptions like treating the outline as a tree
|
||||
and not a DAG (i.e. each Sequence being in only one Section).
|
||||
|
||||
@@ -59,7 +54,8 @@ How to Extend?
|
||||
|
||||
This app is experimenting with some new conventions, so please read the decision
|
||||
docs (``docs/decisions``). Many of the modules also have a long top-level
|
||||
docstring explaining what they should be used for–please read these. Many of the
|
||||
docstring explaining for what they should be used – please read these docstrings.
|
||||
Many of the
|
||||
conventions are there to promote predictable behavior and are dramatically less
|
||||
effective if broken even in little ways.
|
||||
|
||||
@@ -71,7 +67,7 @@ The public data types are in ``api/data.py``. Database persistence is in
|
||||
dumb. All real business logic should happen in a module within the ``api``
|
||||
package. Currently, all the existing API logic is handled in ``api/outlines.py``
|
||||
and re-exported at the top level in ``api/__init__.py``. If your new
|
||||
functionality is outlines related, please follow this convention. Otherwise, you
|
||||
functionality is outline-related, please follow this convention. Otherwise, you
|
||||
can create a new module in ``api/`` (e.g. ``api/sequences.py``) to hold your
|
||||
logic, and re-export that via the top level ``api/__init__.py``.
|
||||
|
||||
@@ -79,8 +75,8 @@ I want to add a new rule affecting how sequences show up in the outline.
|
||||
========================================================================
|
||||
|
||||
You probably want to create or modify an OutlineProcessor (``api/processors``).
|
||||
This is not yet a pluggable interface, though it was designed to make it easy to
|
||||
turn into one. Please see the docstrings in ``api/processors/base.py`` for
|
||||
This interface is not yet pluggable, though it was designed to make it easy to
|
||||
become pluggable. Please see the docstrings in ``api/processors/base.py`` for
|
||||
details on how to write one.
|
||||
|
||||
I want to pull data from ModuleStore or Block Structures.
|
||||
|
||||
@@ -18,9 +18,6 @@ Guidelines:
|
||||
or use API functions from other apps. They should not trigger expensive
|
||||
computation.
|
||||
|
||||
Note: we're using old-style syntax for attrs because we need to support Python
|
||||
3.5, but we can move to the PEP-526 style once we move to Python 3.6+.
|
||||
|
||||
TODO: Validate all datetimes to be UTC.
|
||||
"""
|
||||
import logging
|
||||
@@ -50,7 +47,7 @@ class ObjectDoesNotExist(Exception):
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class ContentErrorData:
|
||||
"""
|
||||
A human-readable description of something wrong with the content, to ease
|
||||
@@ -62,11 +59,11 @@ class ContentErrorData:
|
||||
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 = attr.ib(type=str)
|
||||
usage_key = attr.ib(type=Optional[UsageKey], default=None)
|
||||
message: str
|
||||
usage_key: Optional[UsageKey] = None
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class VisibilityData:
|
||||
"""
|
||||
XBlock attributes that help determine item visibility.
|
||||
@@ -75,29 +72,29 @@ class VisibilityData:
|
||||
# lets you define a Sequence that is reachable by direct URL but not shown
|
||||
# in Course navigation. It was used for things like supplementary tutorials
|
||||
# that were not considered a part of the normal course path.
|
||||
hide_from_toc = attr.ib(type=bool, default=False)
|
||||
hide_from_toc: bool = False
|
||||
|
||||
# Restrict visibility to course staff, regardless of start date. This is
|
||||
# often used to hide content that either still being built out, or is a
|
||||
# scratch space of content that will eventually be copied over to other
|
||||
# sequences.
|
||||
visible_to_staff_only = attr.ib(type=bool, default=False)
|
||||
visible_to_staff_only: bool = False
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class ExamData:
|
||||
"""
|
||||
XBlock attributes that describe exams
|
||||
"""
|
||||
is_practice_exam = attr.ib(type=bool, default=False)
|
||||
is_proctored_enabled = attr.ib(type=bool, default=False)
|
||||
is_time_limited = attr.ib(type=bool, default=False)
|
||||
is_practice_exam: bool = False
|
||||
is_proctored_enabled: bool = False
|
||||
is_time_limited: bool = False
|
||||
|
||||
def __bool__(self):
|
||||
return self.is_practice_exam or self.is_proctored_enabled or self.is_time_limited
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class CourseLearningSequenceData:
|
||||
"""
|
||||
A Learning Sequence (a.k.a. subsection) from a Course.
|
||||
@@ -107,22 +104,22 @@ class CourseLearningSequenceData:
|
||||
learning sequences in Courses vs. Pathways vs. Libraries. Such an object
|
||||
would likely not have `visibility` as that holds course-specific concepts.
|
||||
"""
|
||||
usage_key = attr.ib(type=UsageKey)
|
||||
title = attr.ib(type=str)
|
||||
visibility = attr.ib(type=VisibilityData, default=VisibilityData())
|
||||
exam = attr.ib(type=ExamData, default=ExamData())
|
||||
inaccessible_after_due = attr.ib(type=bool, default=False)
|
||||
usage_key: UsageKey
|
||||
title: str
|
||||
visibility: VisibilityData = VisibilityData()
|
||||
exam: ExamData = ExamData()
|
||||
inaccessible_after_due: bool = False
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class CourseSectionData:
|
||||
"""
|
||||
A Section in a Course (sometimes called a Chapter).
|
||||
"""
|
||||
usage_key = attr.ib(type=UsageKey)
|
||||
title = attr.ib(type=str)
|
||||
visibility = attr.ib(type=VisibilityData)
|
||||
sequences = attr.ib(type=List[CourseLearningSequenceData])
|
||||
usage_key: UsageKey
|
||||
title: str
|
||||
visibility: VisibilityData
|
||||
sequences: List[CourseLearningSequenceData]
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@@ -247,41 +244,41 @@ class CourseOutlineData:
|
||||
)
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class ScheduleItemData:
|
||||
"""
|
||||
Scheduling specific data (start/end/due dates) for a single item.
|
||||
"""
|
||||
usage_key = attr.ib(type=UsageKey)
|
||||
usage_key: UsageKey
|
||||
|
||||
# Start date that is specified for this item
|
||||
start = attr.ib(type=Optional[datetime])
|
||||
start: Optional[datetime]
|
||||
|
||||
# Effective release date that it's available (may be affected by parents)
|
||||
effective_start = attr.ib(type=Optional[datetime])
|
||||
due = attr.ib(type=Optional[datetime])
|
||||
effective_start: Optional[datetime]
|
||||
due: Optional[datetime]
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class ScheduleData:
|
||||
"""
|
||||
Overall course schedule data.
|
||||
"""
|
||||
course_start = attr.ib(type=Optional[datetime])
|
||||
course_end = attr.ib(type=Optional[datetime])
|
||||
sections = attr.ib(type=Dict[UsageKey, ScheduleItemData])
|
||||
sequences = attr.ib(type=Dict[UsageKey, ScheduleItemData])
|
||||
course_start: Optional[datetime]
|
||||
course_end: Optional[datetime]
|
||||
sections: Dict[UsageKey, ScheduleItemData]
|
||||
sequences: Dict[UsageKey, ScheduleItemData]
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class SpecialExamAttemptData:
|
||||
"""
|
||||
Overall special exam attempt data.
|
||||
"""
|
||||
sequences = attr.ib(type=Dict[UsageKey, Dict])
|
||||
sequences: Dict[UsageKey, Dict]
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class UserCourseOutlineData(CourseOutlineData):
|
||||
"""
|
||||
A course outline that has been customized for a specific user and time.
|
||||
@@ -298,16 +295,16 @@ class UserCourseOutlineData(CourseOutlineData):
|
||||
# to reach up into parts of a Course that the user is not normally allowed
|
||||
# to know the existence of (e.g. Sequences marked `visible_to_staff_only`),
|
||||
# we can use this attribute.
|
||||
base_outline = attr.ib(type=CourseOutlineData)
|
||||
base_outline: CourseOutlineData
|
||||
|
||||
# Django User representing who we've customized this outline for. This may
|
||||
# be the AnonymousUser.
|
||||
user = attr.ib(type=User)
|
||||
user: User
|
||||
|
||||
# UTC TZ time representing the time for which this user course outline was
|
||||
# created. It is possible to create UserCourseOutlineData for a time in the
|
||||
# future (i.e. "What will this user be able to see next week?")
|
||||
at_time = attr.ib(type=datetime)
|
||||
at_time: datetime
|
||||
|
||||
# What Sequences is this `user` allowed to access? Anything in the `outline`
|
||||
# is something that the `user` is allowed to know exists, but they might not
|
||||
@@ -317,15 +314,15 @@ class UserCourseOutlineData(CourseOutlineData):
|
||||
# * If anonymous course access is enabled in "public_outline" mode,
|
||||
# unauthenticated users (AnonymousUser) will see the course outline but
|
||||
# not be able to access anything inside.
|
||||
accessible_sequences = attr.ib(type=Set[UsageKey])
|
||||
accessible_sequences: Set[UsageKey]
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@attr.s(frozen=True, auto_attribs=True)
|
||||
class UserCourseOutlineDetailsData:
|
||||
"""
|
||||
Class that has a user's course outline plus useful details (like schedules).
|
||||
Will eventually expand to include other systems like Completion.
|
||||
"""
|
||||
outline = attr.ib(type=UserCourseOutlineData)
|
||||
schedule = attr.ib(type=ScheduleData)
|
||||
special_exam_attempts = attr.ib(type=SpecialExamAttemptData)
|
||||
outline: UserCourseOutlineData
|
||||
schedule: ScheduleData
|
||||
special_exam_attempts: SpecialExamAttemptData
|
||||
|
||||
Reference in New Issue
Block a user