Files
edx-platform/xmodule/randomize_block.py
Kyle McCormick 355779983e build: commit builtinblocks Webpack config and stub out xmodule_assets (#32685)
The Webpack configuration file for built-in XBlock JS used to be
generated at build time and git-ignored. It lived at
common/static/xmodule/webpack.xmodule.config.js. It was generated
because the JS that it referred to was also generated at build-time, and
the filenames of those JS modules were not static.

Now that its contents have been made entirely static [1], there is no
reason we need to continue generating this Webpack configuration file.
So, we check it into edx-platform under the name
./webpack.builtinblocks.config.js. We choose to put it in the repo's
root directory because the paths contained in the config file are
relative to the repo's root.

This allows us to behead both the xmodule/static_content.py
(`xmodule_assets`) script andthe  `process_xmodule_assets` paver task, a
major step in removing the need for Python in the edx-platform asset
build [2]. It also allows us to delete the `HTMLSnippet` class and all
associated attributes, which were exclusively used by
xmodule/static_content.py..

We leave `xmodule_assets` and  `process_xmodule_assets` in as stubs for
now in order to avoid breaking external code (like Tutor) which calls
Paver; the entire pavelib/assets.py function will be eventually removed
soon anyway [3]. Further, to avoid extraneous refactoring, we keep one
method of `HTMLSnippet` around on a few of its former subclasses:
`get_html`. This method was originally part of the XModule framework;
now, it is left over on a few classes as a simple internal helper
method.

References:
1. https://github.com/openedx/edx-platform/pull/32480
2. https://github.com/openedx/edx-platform/issues/31800
3. https://github.com/openedx/edx-platform/issues/31895

Part of: https://github.com/openedx/edx-platform/issues/32481
2023-07-27 14:32:29 +00:00

124 lines
3.5 KiB
Python

# lint-amnesty, pylint: disable=missing-module-docstring
import logging
import random
from django.utils.functional import cached_property
from lxml import etree
from web_fragments.fragment import Fragment
from xblock.fields import Integer, Scope
from xmodule.mako_block import MakoTemplateBlockBase
from xmodule.seq_block import SequenceMixin
from xmodule.xml_block import XmlMixin
from xmodule.x_module import (
ResourceTemplates,
STUDENT_VIEW,
XModuleMixin,
XModuleToXBlockMixin,
)
log = logging.getLogger('edx.' + __name__)
class RandomizeBlock(
SequenceMixin,
MakoTemplateBlockBase,
XmlMixin,
XModuleToXBlockMixin,
ResourceTemplates,
XModuleMixin,
):
"""
Chooses a random child xblock. Chooses the same one every time for each student.
Example:
<randomize>
<problem url_name="problem1" />
<problem url_name="problem2" />
<problem url_name="problem3" />
</randomize>
User notes:
- If you're randomizing amongst graded blocks, each of them MUST be worth the same
number of points. Otherwise, the earth will be overrun by monsters from the
deeps. You have been warned.
Technical notes:
- There is more dark magic in this code than I'd like. The whole varying-children +
grading interaction is a tangle between super and subclasses of descriptors and
blocks.
"""
choice = Integer(help="Which random child was chosen", scope=Scope.user_state)
resources_dir = None
filename_extension = "xml"
show_in_read_only_mode = True
@cached_property
def child(self):
""" Return XBlock instance of selected choice """
num_choices = len(self.get_children())
if self.choice is not None and self.choice > num_choices:
# Oops. Children changed. Reset.
self.choice = None
if self.choice is None:
# choose one based on the system seed, or randomly if that's not available
if num_choices > 0:
if self.runtime.seed is not None:
self.choice = self.runtime.seed % num_choices
else:
self.choice = random.randrange(0, num_choices)
if self.choice is None:
return None
child = self.get_children()[self.choice]
if self.choice is not None:
log.debug("children of randomize block (should be only 1): %s", child)
return child
def get_child_blocks(self):
"""
For grading--return just the chosen child.
"""
if self.child is None:
return []
return [self.child]
def student_view(self, context):
"""
The student view.
"""
if self.child is None:
# raise error instead? In fact, could complain on block load...
return Fragment(content="<div>Nothing to randomize between</div>")
return self.child.render(STUDENT_VIEW, context)
def get_html(self):
return self.studio_view(None).content
def get_icon_class(self):
return self.child.get_icon_class() if self.child else 'other'
def definition_to_xml(self, resource_fs):
xml_object = etree.Element('randomize')
for child in self.get_children():
self.runtime.add_block_as_child_node(child, xml_object)
return xml_object
def has_dynamic_children(self):
"""
Grading needs to know that only one of the children is actually "real". This
makes it use block.get_child_blocks().
"""
return True