Commit 2e0a6dc8 by Bill Filler

expose GatingService to Xblock, add needed apis to Gating API, use GatingService…

expose GatingService to Xblock, add needed apis to Gating API, use GatingService in seq_module instead of Milestone service
parent 24a50948
......@@ -159,6 +159,7 @@ class ProctoringFields(object):
@XBlock.wants('proctoring')
@XBlock.wants('verification')
@XBlock.wants('milestones')
@XBlock.wants('gating')
@XBlock.wants('credit')
@XBlock.needs('user')
@XBlock.needs('bookmarks')
......@@ -267,16 +268,16 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
return banner_text, hidden_content_html
def _is_content_gated(self):
def _get_gating_milestone(self):
"""
Checks whether the content is gated for learners.
"""
milestones_service = self.runtime.service(self, 'milestones')
if milestones_service:
content_milestones = milestones_service.get_course_content_milestones(
gating_service = self.runtime.service(self, 'gating')
if gating_service:
milestone = gating_service.get_gating_milestone(
self.course_id, self.location, 'requires'
)
return content_milestones
return milestone
return False
......@@ -284,13 +285,22 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
"""
Evaluate if the user has completed the prerequiste
"""
prereq_met = False
milestones_service = self.runtime.service(self, 'milestones')
if milestones_service:
gating_service = self.runtime.service(self, 'gating')
if gating_service:
# if it's complete then a user milestone record will exist
prereq_met = milestones_service.user_has_milestone({'id': self.runtime.user_id}, milestone)
return gating_service.is_prereq_met(self.runtime.user_id, milestone)
return False
def _get_milestone_meta_info(self, milestone):
"""
Return information to display about the gating milstone
"""
gating_service = self.runtime.service(self, 'gating')
if gating_service:
return gating_service.get_gating_milestone_meta_info(self.course_id, milestone)
return prereq_met
return {}
def _can_user_view_content(self, course):
"""
......@@ -317,19 +327,13 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
self._update_position(context, len(display_items))
gate_content = False
milestones = self._is_content_gated()
if milestones:
milestone = self._get_gating_milestone()
if milestone:
if self.runtime.user_is_staff:
banner_text = _('This subsection is unlocked for learners when they meet the prerequisite requirements.')
elif not self._is_prereq_met(milestones[0]):
elif not self._is_prereq_met(milestone):
gate_content = True
required_grade = milestones[0]['requirements']['min_score']
# figure out correct way to formulate this url
preqreq_url = '../../../jump_to/' + milestones[0]['namespace'].replace('.gating', '')
# figure out how to get the display name of the gating section
preqreq_section_name = 'Test Section'
else:
print('\nBF!! Hurray the prequesite has been met!!')
milestone_meta_info = self._get_milestone_meta_info(milestone)
fragment = Fragment()
params = {
......@@ -344,9 +348,9 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
'banner_text': banner_text,
'disable_navigation': not self.is_user_authenticated(context),
'gate_content': gate_content,
'required_grade': required_grade if gate_content else None,
'prereq_url': preqreq_url if gate_content else None,
'prereq_section_name' : preqreq_section_name if gate_content else None
'required_grade': milestone['requirements']['min_score'] if gate_content else None,
'prereq_url': milestone_meta_info['url'] if gate_content else None,
'prereq_section_name': milestone_meta_info['display_name'] if gate_content else None
}
fragment.add_content(self.system.render_template("seq_module.html", params))
......
......@@ -51,6 +51,7 @@ from openedx.core.djangoapps.crawlers.models import CrawlersConfig
from openedx.core.djangoapps.credit.services import CreditService
from openedx.core.djangoapps.monitoring_utils import set_custom_metrics_for_course_key, set_monitoring_transaction_name
from openedx.core.djangoapps.util.user_utils import SystemUser
from openedx.core.lib.gating.services import GatingService
from openedx.core.lib.license import wrap_with_license
from openedx.core.lib.url_utils import quote_slashes, unquote_slashes
from openedx.core.lib.xblock_utils import request_token as xblock_request_token
......@@ -755,6 +756,7 @@ def get_module_system_for_user(
'milestones': milestones_helpers.get_service(),
'credit': CreditService(),
'bookmarks': BookmarksService(user=user),
'gating': GatingService(),
},
get_user_role=lambda: get_user_role(user, course_id),
descriptor_runtime=descriptor._runtime, # pylint: disable=protected-access
......
......@@ -3,6 +3,7 @@ API for the gating djangoapp
"""
import logging
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from milestones import api as milestones_api
from opaque_keys.edx.keys import UsageKey
......@@ -299,3 +300,32 @@ def get_gated_content(course, user):
{'id': user.id}
)
]
def is_prereq_met(user_id, milestone):
"""
Returns true if the prequiste has been met for a given milestone
Arguments:
user_id: The id of the user
milestone (Milestone) : The milestone to check
"""
return milestones_api.user_has_milestone({'id': user_id}, milestone)
def get_gating_milestone_meta_info(course_id, milestone):
"""
Returns dict containing meta information about given milestone
Arguments:
course_id: The id of the course
milestone (Milestone): The gating milestone
Returns:
dict of {'url': gating_milestone_url, 'display_name': gating_milestone_display_name}
"""
prereq_content_key = milestone['namespace'].replace(GATING_NAMESPACE_QUALIFIER, '')
gating_milestone_url = reverse('jump_to', kwargs={'course_id': course_id, 'location': prereq_content_key})
block_id = UsageKey.from_string(prereq_content_key).block_id
blocks = modulestore().get_items(course_id, qualifiers={'name': [block_id]})
if blocks:
gating_milestone_display_name = blocks[0].display_name
return {'url': gating_milestone_url, 'display_name': gating_milestone_display_name}
"""
A wrapper class around requested methods exposed in api.py
"""
import types
from . import api as gating_api
class GatingService(object): # pylint: disable=too-few-public-methods
"""
An xBlock service for xBlocks to talk to the Gating api.
NOTE: This is a Singleton class. We should only have one instance of it!
"""
_instance = None
REQUESTED_FUNCTIONS = [
'get_gating_milestone_meta_info',
'get_gating_milestone',
'is_prereq_met'
]
def __new__(cls, *args, **kwargs):
"""
This is the class factory to make sure this is a Singleton
"""
if not cls._instance:
cls._instance = super(GatingService, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
"""
Class initializer, which just inspects the libraries and exposes the same functions
listed in REQUESTED_FUNCTIONS
"""
self._bind_to_requested_functions()
def _bind_to_requested_functions(self):
"""
bind module functions. Since we use underscores to mean private methods, let's exclude those.
"""
for attr_name in self.REQUESTED_FUNCTIONS:
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