Mask grades on progress page according to "Show Correctness" setting.
This commit is contained in:
committed by
Tim Krones
parent
e8a36957b1
commit
6ca8a702ae
@@ -25,8 +25,9 @@ from capa.responsetypes import StudentInputError, ResponseError, LoncapaProblemE
|
||||
from capa.util import convert_files_to_filenames, get_inner_html_from_xpath
|
||||
from xblock.fields import Boolean, Dict, Float, Integer, Scope, String, XMLString
|
||||
from xblock.scorable import ScorableXBlockMixin, Score
|
||||
from xmodule.capa_base_constants import RANDOMIZATION, SHOWANSWER, SHOW_CORRECTNESS
|
||||
from xmodule.capa_base_constants import RANDOMIZATION, SHOWANSWER
|
||||
from xmodule.exceptions import NotFoundError
|
||||
from xmodule.graders import ShowCorrectness
|
||||
from .fields import Date, Timedelta
|
||||
from .progress import Progress
|
||||
|
||||
@@ -120,11 +121,11 @@ class CapaFields(object):
|
||||
help=_("Defines when to show whether a learner's answer to the problem is correct. "
|
||||
"Configured on the subsection."),
|
||||
scope=Scope.settings,
|
||||
default=SHOW_CORRECTNESS.ALWAYS,
|
||||
default=ShowCorrectness.ALWAYS,
|
||||
values=[
|
||||
{"display_name": _("Always"), "value": SHOW_CORRECTNESS.ALWAYS},
|
||||
{"display_name": _("Never"), "value": SHOW_CORRECTNESS.NEVER},
|
||||
{"display_name": _("Past Due"), "value": SHOW_CORRECTNESS.PAST_DUE},
|
||||
{"display_name": _("Always"), "value": ShowCorrectness.ALWAYS},
|
||||
{"display_name": _("Never"), "value": ShowCorrectness.NEVER},
|
||||
{"display_name": _("Past Due"), "value": ShowCorrectness.PAST_DUE},
|
||||
],
|
||||
)
|
||||
showanswer = String(
|
||||
@@ -921,17 +922,11 @@ class CapaMixin(ScorableXBlockMixin, CapaFields):
|
||||
|
||||
Limits access to the correct/incorrect flags, messages, and problem score.
|
||||
"""
|
||||
if self.show_correctness == SHOW_CORRECTNESS.NEVER:
|
||||
return False
|
||||
elif self.runtime.user_is_staff:
|
||||
# This is after the 'never' check because admins can see correctness
|
||||
# unless the problem explicitly prevents it
|
||||
return True
|
||||
elif self.show_correctness == SHOW_CORRECTNESS.PAST_DUE:
|
||||
return self.is_past_due()
|
||||
|
||||
# else: self.show_correctness == SHOW_CORRECTNESS.ALWAYS
|
||||
return True
|
||||
return ShowCorrectness.correctness_available(
|
||||
show_correctness=self.show_correctness,
|
||||
due_date=self.close_date,
|
||||
has_staff_access=self.runtime.user_is_staff,
|
||||
)
|
||||
|
||||
def update_score(self, data):
|
||||
"""
|
||||
|
||||
@@ -4,15 +4,6 @@ Constants for capa_base problems
|
||||
"""
|
||||
|
||||
|
||||
class SHOW_CORRECTNESS(object): # pylint: disable=invalid-name
|
||||
"""
|
||||
Constants for when to show correctness
|
||||
"""
|
||||
ALWAYS = "always"
|
||||
PAST_DUE = "past_due"
|
||||
NEVER = "never"
|
||||
|
||||
|
||||
class SHOWANSWER(object):
|
||||
"""
|
||||
Constants for when to show answer
|
||||
|
||||
@@ -10,9 +10,10 @@ import logging
|
||||
import random
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime # Used by pycontracts. pylint: disable=unused-import
|
||||
from datetime import datetime
|
||||
|
||||
from contracts import contract
|
||||
from pytz import UTC
|
||||
|
||||
log = logging.getLogger("edx.courseware")
|
||||
|
||||
@@ -462,3 +463,38 @@ def _min_or_none(itr):
|
||||
return min(itr)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
class ShowCorrectness(object):
|
||||
"""
|
||||
Helper class for determining whether correctness is currently hidden for a block.
|
||||
|
||||
When correctness is hidden, this limits the user's access to the correct/incorrect flags, messages, problem scores,
|
||||
and aggregate subsection and course grades.
|
||||
"""
|
||||
|
||||
"""
|
||||
Constants used to indicate when to show correctness
|
||||
"""
|
||||
ALWAYS = "always"
|
||||
PAST_DUE = "past_due"
|
||||
NEVER = "never"
|
||||
|
||||
@classmethod
|
||||
def correctness_available(cls, show_correctness='', due_date=None, has_staff_access=False):
|
||||
"""
|
||||
Returns whether correctness is available now, for the given attributes.
|
||||
"""
|
||||
if show_correctness == cls.NEVER:
|
||||
return False
|
||||
elif has_staff_access:
|
||||
# This is after the 'never' check because course staff can see correctness
|
||||
# unless the sequence/problem explicitly prevents it
|
||||
return True
|
||||
elif show_correctness == cls.PAST_DUE:
|
||||
# Is it now past the due date?
|
||||
return (due_date is None or
|
||||
due_date < datetime.now(UTC))
|
||||
|
||||
# else: show_correctness == cls.ALWAYS
|
||||
return True
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
Grading tests
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import ddt
|
||||
import unittest
|
||||
|
||||
from pytz import UTC
|
||||
from xmodule import graders
|
||||
from xmodule.graders import ProblemScore, AggregatedScore, aggregate_scores
|
||||
from xmodule.graders import (
|
||||
AggregatedScore, ProblemScore, ShowCorrectness, aggregate_scores
|
||||
)
|
||||
|
||||
|
||||
class GradesheetTest(unittest.TestCase):
|
||||
@@ -315,3 +317,86 @@ class GraderTest(unittest.TestCase):
|
||||
with self.assertRaises(ValueError) as error:
|
||||
graders.grader_from_conf([invalid_conf])
|
||||
self.assertIn(expected_error_message, error.exception.message)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShowCorrectnessTest(unittest.TestCase):
|
||||
"""
|
||||
Tests the correctness_available method
|
||||
"""
|
||||
def setUp(self):
|
||||
super(ShowCorrectnessTest, self).setUp()
|
||||
|
||||
now = datetime.now(UTC)
|
||||
day_delta = timedelta(days=1)
|
||||
self.yesterday = now - day_delta
|
||||
self.today = now
|
||||
self.tomorrow = now + day_delta
|
||||
|
||||
def test_show_correctness_default(self):
|
||||
"""
|
||||
Test that correctness is visible by default.
|
||||
"""
|
||||
self.assertTrue(ShowCorrectness.correctness_available())
|
||||
|
||||
@ddt.data(
|
||||
(ShowCorrectness.ALWAYS, True),
|
||||
(ShowCorrectness.ALWAYS, False),
|
||||
# Any non-constant values behave like "always"
|
||||
('', True),
|
||||
('', False),
|
||||
('other-value', True),
|
||||
('other-value', False),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_show_correctness_always(self, show_correctness, has_staff_access):
|
||||
"""
|
||||
Test that correctness is visible when show_correctness is turned on.
|
||||
"""
|
||||
self.assertTrue(ShowCorrectness.correctness_available(
|
||||
show_correctness=show_correctness,
|
||||
has_staff_access=has_staff_access
|
||||
))
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_show_correctness_never(self, has_staff_access):
|
||||
"""
|
||||
Test that show_correctness="never" hides correctness from learners and course staff.
|
||||
"""
|
||||
self.assertFalse(ShowCorrectness.correctness_available(
|
||||
show_correctness=ShowCorrectness.NEVER,
|
||||
has_staff_access=has_staff_access
|
||||
))
|
||||
|
||||
@ddt.data(
|
||||
# Correctness not visible to learners if due date in the future
|
||||
('tomorrow', False, False),
|
||||
# Correctness is visible to learners if due date in the past
|
||||
('yesterday', False, True),
|
||||
# Correctness is visible to learners if due date in the past (just)
|
||||
('today', False, True),
|
||||
# Correctness is visible to learners if there is no due date
|
||||
(None, False, True),
|
||||
# Correctness is visible to staff if due date in the future
|
||||
('tomorrow', True, True),
|
||||
# Correctness is visible to staff if due date in the past
|
||||
('yesterday', True, True),
|
||||
# Correctness is visible to staff if there is no due date
|
||||
(None, True, True),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_show_correctness_past_due(self, due_date_str, has_staff_access, expected_result):
|
||||
"""
|
||||
Test show_correctness="past_due" to ensure:
|
||||
* correctness is always visible to course staff
|
||||
* correctness is always visible to everyone if there is no due date
|
||||
* correctness is visible to learners after the due date, when there is a due date.
|
||||
"""
|
||||
if due_date_str is None:
|
||||
due_date = None
|
||||
else:
|
||||
due_date = getattr(self, due_date_str)
|
||||
self.assertEquals(
|
||||
ShowCorrectness.correctness_available(ShowCorrectness.PAST_DUE, due_date, has_staff_access),
|
||||
expected_result
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user