Update documentation, comments, and docstrings throughout the codebase to reflect the migration from setup.py to pyproject.toml: - Transformer class docstrings: changed to reference "entry point name in the package configuration" for better future-proofing - Block structure module docs: updated to reference pyproject.toml - Test file comments: updated entry point references - Config files (tox.ini, pytest.ini): updated references - Documentation (extension_points.rst, course apps ADRs): updated to reference pyproject.toml with inclusive language for external packages - Requirements documentation (github.in): updated with inclusive language - edxmako README: modernized install command to use pip install Historical ADRs and references to external packages that may still use setup.py were intentionally left unchanged or updated with inclusive language acknowledging both pyproject.toml and setup.py. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
"""
|
|
Visibility Transformer implementation.
|
|
"""
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
from pytz import utc
|
|
|
|
from openedx.core.djangoapps.content.block_structure.transformer import BlockStructureTransformer
|
|
from xmodule.seq_block import SequenceBlock # lint-amnesty, pylint: disable=wrong-import-order
|
|
|
|
from .utils import collect_merged_boolean_field, collect_merged_date_field
|
|
|
|
MAXIMUM_DATE = utc.localize(datetime.max)
|
|
|
|
|
|
class HiddenContentTransformer(BlockStructureTransformer):
|
|
"""
|
|
A transformer that enforces the hide_after_due field on
|
|
blocks by removing children blocks from the block structure for
|
|
which the user does not have access. The hide_after_due
|
|
field on a block is percolated down to its descendants, so that
|
|
all blocks enforce the hidden content settings from their ancestors.
|
|
|
|
For a block with multiple parents, access is denied only if
|
|
access is denied from all its parents.
|
|
|
|
Staff users are exempted from hidden content rules.
|
|
|
|
IMPORTANT: Must be run _after_ the DateOverrideTransformer from edx-when
|
|
in case the 'due' date on a block has been shifted for a user.
|
|
"""
|
|
WRITE_VERSION = 4
|
|
READ_VERSION = 4
|
|
MERGED_HIDE_AFTER_DUE = 'merged_hide_after_due'
|
|
MERGED_END_DATE = 'merged_end_date'
|
|
|
|
@classmethod
|
|
def name(cls):
|
|
"""
|
|
Unique identifier for the transformer's class.
|
|
This must match the entry point name in the package configuration.
|
|
"""
|
|
return "hidden_content"
|
|
|
|
@classmethod
|
|
def _get_merged_hide_after_due(cls, block_structure, block_key):
|
|
"""
|
|
Returns whether the block with the given block_key in the
|
|
given block_structure should be hidden after due date per
|
|
computed value from ancestry chain.
|
|
"""
|
|
return block_structure.get_transformer_block_field(
|
|
block_key, cls, cls.MERGED_HIDE_AFTER_DUE, False
|
|
)
|
|
|
|
@classmethod
|
|
def _get_merged_end_date(cls, block_structure, block_key):
|
|
"""
|
|
Returns the merged value for the end date for the block with
|
|
the given block_key in the given block_structure.
|
|
"""
|
|
return block_structure.get_transformer_block_field(
|
|
block_key, cls, cls.MERGED_END_DATE
|
|
)
|
|
|
|
@classmethod
|
|
def collect(cls, block_structure):
|
|
"""
|
|
Collects any information that's necessary to execute this
|
|
transformer's transform method.
|
|
"""
|
|
collect_merged_boolean_field(
|
|
block_structure,
|
|
transformer=cls,
|
|
xblock_field_name='hide_after_due',
|
|
merged_field_name=cls.MERGED_HIDE_AFTER_DUE,
|
|
)
|
|
collect_merged_date_field(
|
|
block_structure,
|
|
transformer=cls,
|
|
xblock_field_name='end',
|
|
merged_field_name=cls.MERGED_END_DATE,
|
|
default_date=MAXIMUM_DATE
|
|
)
|
|
|
|
block_structure.request_xblock_fields('self_paced', 'due')
|
|
|
|
def transform(self, usage_info, block_structure):
|
|
# Users with staff access bypass the Visibility check.
|
|
if usage_info.has_staff_access:
|
|
return [block_structure.create_universal_filter()]
|
|
|
|
block_structure.remove_block_traversal(lambda block_key: self._is_block_hidden(block_structure, block_key))
|
|
|
|
def _is_block_hidden(self, block_structure, block_key):
|
|
"""
|
|
Returns whether the block with the given block_key should
|
|
be hidden, given the current time.
|
|
"""
|
|
hide_after_due = self._get_merged_hide_after_due(block_structure, block_key)
|
|
self_paced = block_structure[block_structure.root_block_usage_key].self_paced
|
|
if self_paced:
|
|
hidden_date = self._get_merged_end_date(block_structure, block_key)
|
|
else:
|
|
# Important Note:
|
|
# A small subtlety of grabbing the due date here is that this transformer relies on the
|
|
# DateOverrideTransformer (located in edx-when repo) to first set any overrides (one
|
|
# example is a user receiving an extension on an assignment).
|
|
hidden_date = block_structure.get_xblock_field(block_key, 'due', None) or MAXIMUM_DATE
|
|
return not SequenceBlock.verify_current_content_visibility(hidden_date, hide_after_due)
|