Commit 60855242 by Will Daly

Refactored capa module HTML rendering, isolating logic for

determining which buttons to show
parent 42043d2c
...@@ -136,7 +136,7 @@ class CapaModule(XModule): ...@@ -136,7 +136,7 @@ class CapaModule(XModule):
self.close_date = self.display_due_date self.close_date = self.display_due_date
max_attempts = self.metadata.get('attempts', None) max_attempts = self.metadata.get('attempts', None)
if max_attempts: if max_attempts is not None:
self.max_attempts = int(max_attempts) self.max_attempts = int(max_attempts)
else: else:
self.max_attempts = None self.max_attempts = None
...@@ -247,6 +247,78 @@ class CapaModule(XModule): ...@@ -247,6 +247,78 @@ class CapaModule(XModule):
'progress': Progress.to_js_status_str(self.get_progress()) 'progress': Progress.to_js_status_str(self.get_progress())
}) })
def check_button_name(self):
"""
Determine the name for the "check" button.
Usually it is just "Check", but if this is the student's
final attempt, change the name to "Final Check"
"""
if self.max_attempts is not None:
final_check = (self.attempts >= self.max_attempts - 1)
else:
final_check = False
return "Final Check" if final_check else "Check"
def should_show_check_button(self):
"""
Return True/False to indicate whether to show the "Check" button.
"""
submitted_without_reset = (self.is_completed() and self.rerandomize == "always")
# If the problem is closed (past due / too many attempts)
# then we do NOT show the "check" button
# Also, do not show the "check" button if we're waiting
# for the user to reset a randomized problem
if self.closed() or submitted_without_reset:
return False
else:
return True
def should_show_reset_button(self):
"""
Return True/False to indicate whether to show the "Reset" button.
"""
survey_question = (self.max_attempts == 0)
if self.rerandomize in ["always", "onreset"]:
# If the problem is closed (and not a survey question with max_attempts==0),
# then do NOT show the reset button.
# If the problem hasn't been submitted yet, then do NOT show
# the reset button.
if (self.closed() and not survey_question) or not self.is_completed():
return False
else:
return True
# Only randomized problems need a "reset" button
else:
return False
def should_show_save_button(self):
"""
Return True/False to indicate whether to show the "Save" button.
"""
# If the user has forced the save button to display,
# then show it as long as the problem is not closed
# (past due / too many attempts)
if self.force_save_button == "true":
return not self.closed()
else:
survey_question = (self.max_attempts == 0)
needs_reset = self.is_completed() and self.rerandomize == "always"
# If the problem is closed (and not a survey question with max_attempts==0),
# then do NOT show the reset button
# If we're waiting for the user to reset a randomized problem
# then do NOT show the reset button
if (self.closed() and not survey_question) or needs_reset:
return False
else:
return True
def get_problem_html(self, encapsulate=True): def get_problem_html(self, encapsulate=True):
'''Return html for the problem. Adds check, reset, save buttons '''Return html for the problem. Adds check, reset, save buttons
as necessary based on the problem config and state.''' as necessary based on the problem config and state.'''
...@@ -313,57 +385,14 @@ class CapaModule(XModule): ...@@ -313,57 +385,14 @@ class CapaModule(XModule):
'weight': self.descriptor.weight, 'weight': self.descriptor.weight,
} }
# We using strings as truthy values, because the terminology of the
# check button is context-specific.
# Put a "Check" button if unlimited attempts or still some left
if self.max_attempts is None or self.attempts < self.max_attempts - 1:
check_button = "Check"
else:
# Will be final check so let user know that
check_button = "Final Check"
reset_button = True
save_button = True
# If we're after deadline, or user has exhausted attempts,
# question is read-only.
if self.closed():
check_button = False
reset_button = False
save_button = False
# If attempts=0 then show just check and reset buttons; this is for survey questions using capa
if self.max_attempts==0:
check_button = False
reset_button = True
save_button = True
# User submitted a problem, and hasn't reset. We don't want
# more submissions.
if self.lcp.done and self.rerandomize == "always":
check_button = False
save_button = False
# Only show the reset button if pressing it will show different values
if self.rerandomize not in ["always", "onreset"]:
reset_button = False
# User hasn't submitted an answer yet -- we don't want resets
if not self.lcp.done:
reset_button = False
# We may not need a "save" button if infinite number of attempts and
# non-randomized. The problem author can force it. It's a bit weird for
# randomization to control this; should perhaps be cleaned up.
if (self.force_save_button == "false") and (self.max_attempts is None and self.rerandomize != "always"):
save_button = False
context = {'problem': content, context = {'problem': content,
'id': self.id, 'id': self.id,
'check_button': check_button,
'reset_button': reset_button, # Pass in the name of the check button or False
'save_button': save_button, # if we do not need a check button
'check_button': self.check_button_name() if self.should_show_check_button() else False,
'reset_button': self.should_show_reset_button(),
'save_button': self.should_show_save_button(),
'answer_available': self.answer_available(), 'answer_available': self.answer_available(),
'ajax_url': self.system.ajax_url, 'ajax_url': self.system.ajax_url,
'attempts_used': self.attempts, 'attempts_used': self.attempts,
...@@ -732,6 +761,7 @@ class CapaModule(XModule): ...@@ -732,6 +761,7 @@ class CapaModule(XModule):
# in next line) # in next line)
self.lcp.seed = None self.lcp.seed = None
self.lcp = LoncapaProblem(self.definition['data'], self.lcp = LoncapaProblem(self.definition['data'],
self.location.html_id(), self.lcp.get_state(), self.location.html_id(), self.lcp.get_state(),
system=self.system) system=self.system)
......
...@@ -3,6 +3,7 @@ import json ...@@ -3,6 +3,7 @@ import json
from mock import Mock, MagicMock, patch from mock import Mock, MagicMock, patch
from pprint import pprint from pprint import pprint
import unittest import unittest
import random
import xmodule import xmodule
import capa import capa
...@@ -618,3 +619,164 @@ class CapaModuleTest(unittest.TestCase): ...@@ -618,3 +619,164 @@ class CapaModuleTest(unittest.TestCase):
# Expect that we succeed # Expect that we succeed
self.assertTrue('success' in result and result['success']) self.assertTrue('success' in result and result['success'])
def test_check_button_name(self):
# If last attempt, button name changes to "Final Check"
# Just in case, we also check what happens if we have
# more attempts than allowed.
attempts = random.randint(1, 10)
module = CapaFactory.create(attempts=attempts-1, max_attempts=attempts)
self.assertEqual(module.check_button_name(), "Final Check")
module = CapaFactory.create(attempts=attempts, max_attempts=attempts)
self.assertEqual(module.check_button_name(), "Final Check")
module = CapaFactory.create(attempts=attempts + 1, max_attempts=attempts)
self.assertEqual(module.check_button_name(), "Final Check")
# Otherwise, button name is "Check"
module = CapaFactory.create(attempts=attempts-2, max_attempts=attempts)
self.assertEqual(module.check_button_name(), "Check")
module = CapaFactory.create(attempts=attempts-3, max_attempts=attempts)
self.assertEqual(module.check_button_name(), "Check")
# If no limit on attempts, then always show "Check"
module = CapaFactory.create(attempts=attempts-3)
self.assertEqual(module.check_button_name(), "Check")
module = CapaFactory.create(attempts=0)
self.assertEqual(module.check_button_name(), "Check")
def test_should_show_check_button(self):
attempts = random.randint(1,10)
# If we're after the deadline, do NOT show check button
module = CapaFactory.create(due=self.yesterday_str)
self.assertFalse(module.should_show_check_button())
# If user is out of attempts, do NOT show the check button
module = CapaFactory.create(attempts=attempts, max_attempts=attempts)
self.assertFalse(module.should_show_check_button())
# If survey question (max_attempts = 0), do NOT show the check button
module = CapaFactory.create(max_attempts=0)
self.assertFalse(module.should_show_check_button())
# If user submitted a problem but hasn't reset,
# do NOT show the check button
# Note: we can only reset when rerandomize="always"
module = CapaFactory.create(rerandomize="always")
module.lcp.done = True
self.assertFalse(module.should_show_check_button())
# Otherwise, DO show the check button
module = CapaFactory.create()
self.assertTrue(module.should_show_check_button())
# If the user has submitted the problem
# and we do NOT have a reset button, then we can show the check button
# Setting rerandomize to "never" ensures that the reset button
# is not shown
module = CapaFactory.create(rerandomize="never")
module.lcp.done = True
self.assertTrue(module.should_show_check_button())
def test_should_show_reset_button(self):
attempts = random.randint(1,10)
# If we're after the deadline, do NOT show the reset button
module = CapaFactory.create(due=self.yesterday_str)
module.lcp.done = True
self.assertFalse(module.should_show_reset_button())
# If the user is out of attempts, do NOT show the reset button
module = CapaFactory.create(attempts=attempts, max_attempts=attempts)
module.lcp.done = True
self.assertFalse(module.should_show_reset_button())
# If we're NOT randomizing, then do NOT show the reset button
module = CapaFactory.create(rerandomize="never")
module.lcp.done = True
self.assertFalse(module.should_show_reset_button())
# If the user hasn't submitted an answer yet,
# then do NOT show the reset button
module = CapaFactory.create()
module.lcp.done = False
self.assertFalse(module.should_show_reset_button())
# Otherwise, DO show the reset button
module = CapaFactory.create()
module.lcp.done = True
self.assertTrue(module.should_show_reset_button())
# If survey question for capa (max_attempts = 0),
# DO show the reset button
module = CapaFactory.create(max_attempts=0)
module.lcp.done = True
self.assertTrue(module.should_show_reset_button())
def test_should_show_save_button(self):
attempts = random.randint(1,10)
# If we're after the deadline, do NOT show the save button
module = CapaFactory.create(due=self.yesterday_str)
module.lcp.done = True
self.assertFalse(module.should_show_save_button())
# If the user is out of attempts, do NOT show the save button
module = CapaFactory.create(attempts=attempts, max_attempts=attempts)
module.lcp.done = True
self.assertFalse(module.should_show_save_button())
# If user submitted a problem but hasn't reset, do NOT show the save button
module = CapaFactory.create(rerandomize="always")
module.lcp.done = True
self.assertFalse(module.should_show_save_button())
# Otherwise, DO show the save button
module = CapaFactory.create()
module.lcp.done = False
self.assertTrue(module.should_show_save_button())
# If we're not randomizing, then we can re-save
module = CapaFactory.create(rerandomize="never")
module.lcp.done = True
self.assertTrue(module.should_show_save_button())
# If survey question for capa (max_attempts = 0),
# DO show the save button
module = CapaFactory.create(max_attempts=0)
module.lcp.done = False
self.assertTrue(module.should_show_save_button())
def test_should_show_save_button_force_save_button(self):
# If we're after the deadline, do NOT show the save button
# even though we're forcing a save
module = CapaFactory.create(due=self.yesterday_str,
force_save_button="true")
module.lcp.done = True
self.assertFalse(module.should_show_save_button())
# If the user is out of attempts, do NOT show the save button
attempts = random.randint(1,10)
module = CapaFactory.create(attempts=attempts,
max_attempts=attempts,
force_save_button="true")
module.lcp.done = True
self.assertFalse(module.should_show_save_button())
# Otherwise, if we force the save button,
# then show it even if we would ordinarily
# require a reset first
module = CapaFactory.create(force_save_button="true",
rerandomize="always")
module.lcp.done = True
self.assertTrue(module.should_show_save_button())
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