Commit cbec692c by Justin Riley

randomize: fix support for suspending problems

Problems can now be suspended by adding a 'suspended' xml attribute to
the <randomize> tag. To suspend a problem simply specify a comma
separated list of problem url_name's in the 'suspended' xml attribute.
Previously setting any attribute at the <problem> level was causing the
system to drop grades for any student that had successfully completed a
suspended problem. This new approach for suspended problem fixes that
issue.
parent 919777b6
...@@ -51,19 +51,26 @@ class RandomizeModule(RandomizeFields, XModule): ...@@ -51,19 +51,26 @@ class RandomizeModule(RandomizeFields, XModule):
xml_attrs = self.descriptor.xml_attributes or [] xml_attrs = self.descriptor.xml_attributes or []
use_randrange = self._str_to_bool(xml_attrs.get('use_randrange', '')) use_randrange = self._str_to_bool(xml_attrs.get('use_randrange', ''))
no_repeats = self._str_to_bool(xml_attrs.get('no_repeats', '')) no_repeats = self._str_to_bool(xml_attrs.get('no_repeats', ''))
self.pick_choice(use_randrange=use_randrange, no_repeats=no_repeats) suspended = xml_attrs.get('suspended', '').split(',')
suspended = [i.strip() for i in suspended if i.strip()]
self.pick_choice(use_randrange=use_randrange, no_repeats=no_repeats,
suspended=suspended)
def _str_to_bool(self, v): def _str_to_bool(self, v):
return v.lower() == 'true' return v.lower() == 'true'
def pick_choice(self, use_randrange=None, no_repeats=None): def pick_choice(self, use_randrange=False, no_repeats=False,
choices = self.get_choices(no_repeats=no_repeats) suspended=None):
num_choices = len(choices) choices = self.get_choices(no_repeats=no_repeats, suspended=suspended)
if self.choice is not None and self.choice not in choices: all_choices = self.get_choices()
# Children changed. Reset. current_child = all_choices.get(self.choice)
self.choice = None if current_child and self.choice not in choices:
if current_child.location.name not in suspended:
# Children changed. Reset.
self.choice = None
if self.choice is None: if self.choice is None:
num_choices = len(choices)
# choose one based on the system seed, or randomly if that's not # choose one based on the system seed, or randomly if that's not
# available # available
if num_choices > 0: if num_choices > 0:
...@@ -77,15 +84,25 @@ class RandomizeModule(RandomizeFields, XModule): ...@@ -77,15 +84,25 @@ class RandomizeModule(RandomizeFields, XModule):
log.debug('using randrange for %s' % str(self.location)) log.debug('using randrange for %s' % str(self.location))
self.choice = choice self.choice = choice
else: else:
log.debug('error in randomize: num_choices = %s' % num_choices) # we're in a bad state - possible solution below tries to
# restore last failed problem from history (raise exc for now)
# try:
# last_failed = json.loads(self.history or '[]')[-1]
# self.choice = last_failed
# current_child = all_choices[last_failed]
# except IndexError:
# raise Exception('No choices left!!!')
raise Exception('No choices left!!!')
if self.choice is not None: if self.choice is not None:
self.child_descriptor = choices[self.choice] if current_child:
self.child_descriptor = current_child
else:
self.child_descriptor = choices[self.choice]
# Now get_children() should return a list with one element # Now get_children() should return a list with one element
log.debug("choice=%s in %s, children of randomize module " children = self.get_children()
"(should be only 1): %s", self.choice, assert len(children) == 1
str(self.location), self.get_children()) self.child = children[0]
self.child = self.get_children()[0]
if no_repeats: if no_repeats:
child_loc = self.child.location.url() child_loc = self.child.location.url()
history = json.loads(self.history or '[]') history = json.loads(self.history or '[]')
...@@ -96,13 +113,14 @@ class RandomizeModule(RandomizeFields, XModule): ...@@ -96,13 +113,14 @@ class RandomizeModule(RandomizeFields, XModule):
self.child_descriptor = None self.child_descriptor = None
self.child = None self.child = None
def get_choices(self, no_repeats=None): def get_choices(self, no_repeats=None, suspended=None):
children = self.descriptor.get_children() children = self.descriptor.get_children()
if self.choice is None and no_repeats: if self.choice is None and no_repeats:
history = json.loads(self.history or '[]') history = json.loads(self.history or '[]')
children = [c for c in children if c.location.url() not in history] children = [c for c in children if c.location.url() not in history]
children = [c for c in children if not if suspended:
self._str_to_bool(c.xml_attributes.get('suspended', ''))] children = [c for c in children if c.location.name not in
suspended]
return OrderedDict([(c.location.url(), c) for c in children]) return OrderedDict([(c.location.url(), c) for c in children])
def get_choice_index(self, choice=None, choices=None): def get_choice_index(self, choice=None, choices=None):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment