Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review PR. DO NOT MERGE #2

Open
wants to merge 25 commits into
base: original-commit
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f57414b
coverage should be executable
cewing May 14, 2015
fc415c0
sketch out constructor and _to_string method
cewing May 15, 2015
f782eb4
the key will have a 'ccx' property
cewing May 15, 2015
14b0d99
build tests for the constructor (and for _to_string)
cewing May 15, 2015
07bcd58
fix malformed entry point syntax
cewing May 16, 2015
d98a161
add an alternate constructor that will build a ccx locator from a cou…
cewing May 16, 2015
474011c
add more tests
cewing May 16, 2015
9f87cfe
not using sphinx, don't include it in requirements.
cewing May 16, 2015
cc5c2fe
adding travis configuration
cewing May 16, 2015
00022d5
incorporate travis badge in readme
cewing May 16, 2015
88398a6
add coveralls integration
cewing May 16, 2015
65ac21c
incorporate quality checking
cewing May 16, 2015
4f3b2f6
code quality
cewing May 16, 2015
eaa0af9
fix issues identified in quick code review
cewing May 19, 2015
da385e0
implement a block usage locator that can use CCXLocator instead of Co…
cewing May 19, 2015
d1c6b2e
prevent deprecated keys from interoperating with CCX locators of any …
cewing May 21, 2015
f5b3c1d
fix initializer as recommended
cewing May 30, 2015
fc0c5ea
add convenience method that returns a course locator equivalent to th…
cewing May 30, 2015
582735c
fix tests now that we only accept kwargs
cewing May 30, 2015
5c73556
fix error in method return value
cewing May 30, 2015
827f293
implement the methods responsible for constructing asset and usage ke…
cewing May 30, 2015
fe661bd
of course, ccx keys do not have the deprecated attribute, maybe they …
cewing May 30, 2015
633b28f
ccx locators are *never* deprecated
cewing May 30, 2015
81d071a
call the method
cewing May 30, 2015
e6b0370
add a method to the `CCXBlockUsageLocator` class that will return a n…
cewing Jun 1, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pep8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pep8]
ignore=E501
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
language: python
python:
- "2.7"
before_install:
- git fetch origin master:refs/remotes/origin/master
install:
- pip install -r test-requirements.txt
- pip install coveralls
script:
- scripts/coverage
after_success:
- coveralls
9 changes: 7 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ Part of `edX code`_.

.. _`edX code`: http://code.edx.org/

ccx-keys
========
ccx-keys |build-status| |coverage-status|
=========================================

Ccx-keys provides implementations of the Opaque Key concept specific to Custom
Courses for EdX. For more on opaque keys see `the opaque-keys package`_,
`its documentation`_, or the `edx-platform wiki documentation`_ regarding
opaque keys.

.. |build-status| image:: https://travis-ci.org/jazkarta/ccx-keys.png
:target: https://travis-ci.org/jazkarta/ccx-keys
.. |coverage-status| image:: https://coveralls.io/repos/jazkarta/ccx-keys/badge.svg
:target: https://coveralls.io/r/jazkarta/ccx-keys

.. _`the opaque-keys package`: https://github.com/edx/opaque-keys
.. _`its documentation`: http://opaque-keys.readthedocs.org/en/latest/
.. _`edx-platform wiki documentation`: https://github.com/edx/edx-platform/wiki/Opaque-Keys-(Locators)
7 changes: 6 additions & 1 deletion ccx_keys/key.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# -*- coding: utf-8 -*-
from abc import abstractproperty
from opaque_keys.edx.keys import CourseKey


class CCXKey(CourseKey):
pass

@abstractproperty
def ccx(self):
"""The id of the ccx for this key"""
raise NotImplementedError()
174 changes: 171 additions & 3 deletions ccx_keys/locator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,174 @@
# -*- coding: utf-8 -*-
from opaque_keys.edx.locator import CourseLocator
import re

from opaque_keys import InvalidKeyError
from opaque_keys.edx.locator import (
AssetLocator,
BlockUsageLocator,
CourseLocator,
)
from opaque_keys.edx.keys import UsageKey

class CCXLocator(CourseLocator):
pass
from ccx_keys.key import CCXKey


class CCXLocator(CourseLocator, CCXKey):
"""Concrete implementation of an Opaque Key for CCX courses"""

CANONICAL_NAMESPACE = 'ccx-v1'
KEY_FIELDS = CourseLocator.KEY_FIELDS + ('ccx', )
__slots__ = KEY_FIELDS
CHECKED_INIT = False
CCX_PREFIX = 'ccx'
deprecated = False

# pep8 and pylint don't agree on the indentation in this block; let's make
# pep8 happy and ignore pylint as that's easier to do.
# pylint: disable=bad-continuation
URL_RE_SOURCE = r"""
((?P<org>{ALLOWED_ID_CHARS}+)\+(?P<course>{ALLOWED_ID_CHARS}+)(\+(?P<run>{ALLOWED_ID_CHARS}+))?{SEP})??
({BRANCH_PREFIX}@(?P<branch>{ALLOWED_ID_CHARS}+){SEP})?
({VERSION_PREFIX}@(?P<version_guid>[A-F0-9]+){SEP})?
({CCX_PREFIX}@(?P<ccx>\d+){SEP})
({BLOCK_TYPE_PREFIX}@(?P<block_type>{ALLOWED_ID_CHARS}+){SEP})?
({BLOCK_PREFIX}@(?P<block_id>{ALLOWED_ID_CHARS}+))?
""".format(
ALLOWED_ID_CHARS=CourseLocator.ALLOWED_ID_CHARS,
BRANCH_PREFIX=CourseLocator.BRANCH_PREFIX,
VERSION_PREFIX=CourseLocator.VERSION_PREFIX,
BLOCK_TYPE_PREFIX=CourseLocator.BLOCK_TYPE_PREFIX,
BLOCK_PREFIX=CourseLocator.BLOCK_PREFIX,
CCX_PREFIX=CCX_PREFIX,
SEP=r'(\+(?=.)|$)', # Separator: requires a non-trailing '+' or end of string
)

URL_RE = re.compile(
'^' + URL_RE_SOURCE + '$', re.IGNORECASE | re.VERBOSE | re.UNICODE
)

def __init__(self, **kwargs):
"""constructor for a ccx locator"""
# for a ccx locator we require a ccx id to be passed.
if 'ccx' not in kwargs:
raise InvalidKeyError(self.__class__, "ccx must be set")

if kwargs.get('deprecated', False):
raise InvalidKeyError(self.__class__, "cannot be deprecated")

super(CCXLocator, self).__init__(**kwargs)

@classmethod
def from_course_locator(cls, course_locator, ccx):
"""Construct a CCXLocator given a CourseLocator and a ccx id"""
new_obj = cls(
org=course_locator.org,
course=course_locator.course,
run=course_locator.run,
branch=course_locator.branch,
version_guid=course_locator.version_guid,
deprecated=course_locator.deprecated,
ccx=ccx,
)
return new_obj

def to_course_locator(self):
return CourseLocator(
org=self.org,
course=self.course,
run=self.run,
branch=self.branch,
version_guid=self.version_guid
)

def _to_string(self):
"""
Return a string representing this location.
"""
string = super(CCXLocator, self)._to_string()
# append the identifier for the ccx to the existing course string
string += u"+{prefix}@{ccx}".format(
prefix=self.CCX_PREFIX, ccx=self.ccx
)
return string

def _to_deprecated_string(self):
""" CCXLocators are never deprecated. """
raise NotImplementedError

@classmethod
def _from_deprecated_string(cls, serialized):
""" CCXLocators are never deprecated. """
raise NotImplementedError

def make_usage_key(self, block_type, block_id):
return CCXBlockUsageLocator(
course_key=self,
block_type=block_type,
block_id=block_id,
deprecated=False
)

def make_asset_key(self, asset_type, path):
return AssetLocator(
self.to_course_locator(),
asset_type,
path,
deprecated=False
)


class CCXBlockUsageLocator(BlockUsageLocator, UsageKey):
"""Concrete implementation of a usage key for blocks in CCXs"""

CANONICAL_NAMESPACE = 'ccx-block-v1'

URL_RE = re.compile(
'^' + CCXLocator.URL_RE_SOURCE + '$', re.IGNORECASE | re.VERBOSE | re.UNICODE
)

def replace(self, **kwargs):
# BlockUsageLocator allows for the replacement of 'KEY_FIELDS' in 'self.course_key'.
# This includes the deprecated 'KEY_FIELDS' of CourseLocator `'revision'` and `'version'`.
course_key_kwargs = {}
for key in CCXLocator.KEY_FIELDS:
if key in kwargs:
course_key_kwargs[key] = kwargs.pop(key)
if len(course_key_kwargs) > 0:
kwargs['course_key'] = self.course_key.replace(**course_key_kwargs)

return super(CCXBlockUsageLocator, self).replace(**kwargs)

@classmethod
def _from_string(cls, serialized):
"""
Requests CCXLocator to deserialize its part and then adds the local
deserialization of block
"""
# Allow access to _from_string protected method
course_key = CCXLocator._from_string(serialized) # pylint: disable=protected-access
parsed_parts = cls.parse_url(serialized)
block_id = parsed_parts.get('block_id', None)
if block_id is None:
raise InvalidKeyError(cls, serialized)
return cls(course_key, parsed_parts.get('block_type'), block_id)

@property
def ccx(self):
"""Returns the ccx for this object's course_key."""
return self.course_key.ccx

def to_block_locator(self):
return BlockUsageLocator(
course_key=self.course_key.to_course_locator(),
block_type=self.block_type,
block_id=self.block_id
)

def _to_deprecated_string(self):
""" CCXBlockUsageLocators are never deprecated. """
raise NotImplementedError

@classmethod
def _from_deprecated_string(cls, serialized):
""" CCXBlockUsageLocators are never deprecated."""
raise NotImplementedError
Loading