Commit 61a38ec8 by Bill Filler

fix comments from review

parent 1cac8216
...@@ -292,7 +292,7 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule): ...@@ -292,7 +292,7 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
self._update_position(context, len(display_items)) self._update_position(context, len(display_items))
prereq_met = True prereq_met = True
if self._find_gating_milestone(): if self._is_prereq_required():
if self.runtime.user_is_staff: if self.runtime.user_is_staff:
banner_text = _('This subsection is unlocked for learners when they meet the prerequisite requirements.') banner_text = _('This subsection is unlocked for learners when they meet the prerequisite requirements.')
else: else:
...@@ -323,13 +323,16 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule): ...@@ -323,13 +323,16 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
return fragment return fragment
def _find_gating_milestone(self): def _is_prereq_required(self):
""" """
Checks whether a gating milestone exists for this Section Checks whether a prerequiste is required for this Section
Returns:
milestone if a prereq is required, False otherwise
""" """
gating_service = self.runtime.service(self, 'gating') gating_service = self.runtime.service(self, 'gating')
if gating_service: if gating_service:
milestone = gating_service.get_gating_milestone( milestone = gating_service.is_prereq_required(
self.course_id, self.location, 'requires' self.course_id, self.location, 'requires'
) )
return milestone return milestone
......
...@@ -148,7 +148,7 @@ def get_prerequisites(course_key): ...@@ -148,7 +148,7 @@ def get_prerequisites(course_key):
milestones_by_block_id = {} milestones_by_block_id = {}
block_ids = [] block_ids = []
for milestone in course_content_milestones: for milestone in course_content_milestones:
prereq_content_key = milestone['namespace'].replace(GATING_NAMESPACE_QUALIFIER, '') prereq_content_key = _get_gating_block_id(milestone)
block_id = UsageKey.from_string(prereq_content_key).block_id block_id = UsageKey.from_string(prereq_content_key).block_id
block_ids.append(block_id) block_ids.append(block_id)
milestones_by_block_id[block_id] = milestone milestones_by_block_id[block_id] = milestone
...@@ -273,7 +273,7 @@ def get_required_content(course_key, gated_content_key): ...@@ -273,7 +273,7 @@ def get_required_content(course_key, gated_content_key):
milestone = get_gating_milestone(course_key, gated_content_key, 'requires') milestone = get_gating_milestone(course_key, gated_content_key, 'requires')
if milestone: if milestone:
return ( return (
milestone.get('namespace', '').replace(GATING_NAMESPACE_QUALIFIER, ''), _get_gating_block_id(milestone),
milestone.get('requirements', {}).get('min_score') milestone.get('requirements', {}).get('min_score')
) )
else: else:
...@@ -317,7 +317,7 @@ def is_prereq_met(content_id, user_id, recalc_on_unmet=False): ...@@ -317,7 +317,7 @@ def is_prereq_met(content_id, user_id, recalc_on_unmet=False):
Returns: Returns:
tuple: True|False, tuple: True|False,
prereq_meta_info = { 'url': prereq_url, 'display_name': prereq_name} prereq_meta_info = { 'url': prereq_url|None, 'display_name': prereq_name|None}
""" """
course_id = content_id.course_key course_id = content_id.course_key
...@@ -329,9 +329,10 @@ def is_prereq_met(content_id, user_id, recalc_on_unmet=False): ...@@ -329,9 +329,10 @@ def is_prereq_met(content_id, user_id, recalc_on_unmet=False):
user_id user_id
) )
prereq_met = not unfulfilled_milestones prereq_met = not unfulfilled_milestones
prereq_meta_info = {'url': None, 'display_name': None}
if prereq_met or not recalc_on_unmet: if prereq_met or not recalc_on_unmet:
return prereq_met, {} return prereq_met, prereq_meta_info
milestone = unfulfilled_milestones[0] milestone = unfulfilled_milestones[0]
student = User.objects.get(id=user_id) student = User.objects.get(id=user_id)
...@@ -341,7 +342,7 @@ def is_prereq_met(content_id, user_id, recalc_on_unmet=False): ...@@ -341,7 +342,7 @@ def is_prereq_met(content_id, user_id, recalc_on_unmet=False):
course_structure = get_course_blocks(student, store.make_course_usage_key(course_id)) course_structure = get_course_blocks(student, store.make_course_usage_key(course_id))
course = store.get_course(course_id, depth=0) course = store.get_course(course_id, depth=0)
subsection_grade_factory = SubsectionGradeFactory(student, course, course_structure) subsection_grade_factory = SubsectionGradeFactory(student, course, course_structure)
subsection_usage_key = UsageKey.from_string(milestone['namespace'].replace(GATING_NAMESPACE_QUALIFIER, '')) subsection_usage_key = UsageKey.from_string(_get_gating_block_id(milestone))
if subsection_usage_key in course_structure: if subsection_usage_key in course_structure:
# this will force a recalcuation of the subsection grade # this will force a recalcuation of the subsection grade
...@@ -376,6 +377,12 @@ def update_milestone(milestone, subsection_grade, prereq_milestone, user_id): ...@@ -376,6 +377,12 @@ def update_milestone(milestone, subsection_grade, prereq_milestone, user_id):
milestones_helpers.remove_user_milestone({'id': user_id}, prereq_milestone) milestones_helpers.remove_user_milestone({'id': user_id}, prereq_milestone)
return False return False
def _get_gating_block_id(milestone):
"""
Return the block id of the gating milestone
"""
return milestone.get('namespace', '').replace(GATING_NAMESPACE_QUALIFIER, '')
def _get_minimum_required_percentage(milestone): def _get_minimum_required_percentage(milestone):
""" """
Returns the minimum percentage requirement for the given milestone. Returns the minimum percentage requirement for the given milestone.
......
""" """
A wrapper class around requested methods exposed in api.py A wrapper class to communicate with Gating api
""" """
import types
from . import api as gating_api from . import api as gating_api
class GatingService(object): # pylint: disable=too-few-public-methods class GatingService(object):
""" """
An xBlock service for xBlocks to talk to the Gating api. An XBlock service to talk to the Gating api.
NOTE: This is a Singleton class. We should only have one instance of it!
""" """
_instance = None def is_prereq_met(self, content_id, user_id, recalc_on_unmet=False):
"""
Returns true if the prequiste has been met for a given milestone
REQUESTED_FUNCTIONS = [ Arguments:
'get_gating_milestone_meta_info', content_id (BlockUsageLocator): BlockUsageLocator for the content
'get_gating_milestone', user_id: The id of the user
'is_prereq_met' recalc_on_unmet: Recalculate the grade if prereq has not yet been met
]
def __new__(cls, *args, **kwargs): Returns:
""" tuple: True|False,
This is the class factory to make sure this is a Singleton prereq_meta_info = { 'url': prereq_url|None, 'display_name': prereq_name|None}
""" """
if not cls._instance: return gating_api.is_prereq_met(content_id, user_id, recalc_on_unmet)
cls._instance = super(GatingService, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self): def is_prereq_required(self, course_key, content_key, relationship):
"""
Class initializer, which just inspects the libraries and exposes the same functions
listed in REQUESTED_FUNCTIONS
""" """
self._bind_to_requested_functions() Returns the prerequiste if one is required
def _bind_to_requested_functions(self): Arguments:
""" course_key (str|CourseKey): The course key
bind module functions. Since we use underscores to mean private methods, let's exclude those. content_key (str|UsageKey): The content usage key
relationship (str): The relationship type (e.g. 'requires')
Returns:
dict or None: The gating milestone dict or None
""" """
for attr_name in self.REQUESTED_FUNCTIONS: return gating_api.get_gating_milestone(course_key, content_key, relationship)
attr = getattr(gating_api, attr_name, None)
if isinstance(attr, types.FunctionType) and not attr_name.startswith('_'):
if not hasattr(self, attr_name):
setattr(self, attr_name, attr)
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