forked from openedx/edx-platform
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrandomize_block.py
123 lines (96 loc) · 3.53 KB
/
randomize_block.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# 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