Files
edx-platform/lms/djangoapps/ccx/overrides.py
Carlos de la Guardia 9ddee93401 MIT: CCX. Code Quality fixes
add doc strings; fix pep8 warning
address some minor issues brought up by the code review process
2015-04-10 23:30:26 -04:00

185 lines
5.6 KiB
Python

"""
API related to providing field overrides for individual students. This is used
by the individual custom courses feature.
"""
import json
import threading
from contextlib import contextmanager
from django.db import transaction, IntegrityError
from courseware.field_overrides import FieldOverrideProvider # pylint: disable=import-error
from ccx import ACTIVE_CCX_KEY # pylint: disable=import-error
from .models import CcxMembership, CcxFieldOverride
class CustomCoursesForEdxOverrideProvider(FieldOverrideProvider):
"""
A concrete implementation of
:class:`~courseware.field_overrides.FieldOverrideProvider` which allows for
overrides to be made on a per user basis.
"""
def get(self, block, name, default):
"""
Just call the get_override_for_ccx method if there is a ccx
"""
ccx = get_current_ccx()
if ccx:
return get_override_for_ccx(ccx, block, name, default)
return default
class _CcxContext(threading.local):
"""
A threading local used to implement the `with_ccx` context manager, that
keeps track of the CCX currently set as the context.
"""
ccx = None
request = None
_CCX_CONTEXT = _CcxContext()
@contextmanager
def ccx_context(ccx):
"""
A context manager which can be used to explicitly set the CCX that is in
play for field overrides. This mechanism overrides the standard mechanism
of looking in the user's session to see if they are enrolled in a CCX and
viewing that CCX.
"""
prev = _CCX_CONTEXT.ccx
_CCX_CONTEXT.ccx = ccx
yield
_CCX_CONTEXT.ccx = prev
def get_current_ccx():
"""
Return the ccx that is active for this request.
"""
return _CCX_CONTEXT.ccx
def get_current_request():
"""
Return the active request, so that we can get context information in places
where it is limited, like in the tabs.
"""
request = _CCX_CONTEXT.request
return request
def get_override_for_ccx(ccx, block, name, default=None):
"""
Gets the value of the overridden field for the `ccx`. `block` and `name`
specify the block and the name of the field. If the field is not
overridden for the given ccx, returns `default`.
"""
if not hasattr(block, '_ccx_overrides'):
block._ccx_overrides = {} # pylint: disable=protected-access
overrides = block._ccx_overrides.get(ccx.id) # pylint: disable=protected-access
if overrides is None:
overrides = _get_overrides_for_ccx(ccx, block)
block._ccx_overrides[ccx.id] = overrides # pylint: disable=protected-access
return overrides.get(name, default)
def _get_overrides_for_ccx(ccx, block):
"""
Returns a dictionary mapping field name to overriden value for any
overrides set on this block for this CCX.
"""
overrides = {}
query = CcxFieldOverride.objects.filter(
ccx=ccx,
location=block.location
)
for override in query:
field = block.fields[override.field]
value = field.from_json(json.loads(override.value))
overrides[override.field] = value
return overrides
@transaction.commit_on_success
def override_field_for_ccx(ccx, block, name, value):
"""
Overrides a field for the `ccx`. `block` and `name` specify the block
and the name of the field on that block to override. `value` is the
value to set for the given field.
"""
field = block.fields[name]
value = json.dumps(field.to_json(value))
try:
override = CcxFieldOverride.objects.create(
ccx=ccx,
location=block.location,
field=name,
value=value)
except IntegrityError:
transaction.commit()
override = CcxFieldOverride.objects.get(
ccx=ccx,
location=block.location,
field=name)
override.value = value
override.save()
if hasattr(block, '_ccx_overrides'):
del block._ccx_overrides[ccx.id] # pylint: disable=protected-access
def clear_override_for_ccx(ccx, block, name):
"""
Clears a previously set field override for the `ccx`. `block` and `name`
specify the block and the name of the field on that block to clear.
This function is idempotent--if no override is set, nothing action is
performed.
"""
try:
CcxFieldOverride.objects.get(
ccx=ccx,
location=block.location,
field=name).delete()
if hasattr(block, '_ccx_overrides'):
del block._ccx_overrides[ccx.id] # pylint: disable=protected-access
except CcxFieldOverride.DoesNotExist:
pass
class CcxMiddleware(object):
"""
Checks to see if current session is examining a CCX and sets the CCX as
the current CCX for the override machinery if so.
"""
def process_request(self, request):
"""
Do the check.
"""
ccx_id = request.session.get(ACTIVE_CCX_KEY, None)
if ccx_id is not None:
try:
membership = CcxMembership.objects.get(
student=request.user, active=True, ccx__id__exact=ccx_id
)
_CCX_CONTEXT.ccx = membership.ccx
except CcxMembership.DoesNotExist:
# if there is no membership, be sure to unset the active ccx
_CCX_CONTEXT.ccx = None
request.session.pop(ACTIVE_CCX_KEY)
_CCX_CONTEXT.request = request
def process_response(self, request, response): # pylint: disable=unused-argument
"""
Clean up afterwards.
"""
_CCX_CONTEXT.ccx = None
_CCX_CONTEXT.request = None
return response