diff --git a/lms/djangoapps/courseware/user_state_client.py b/lms/djangoapps/courseware/user_state_client.py index a60284cfbd..bb18d1d7ab 100644 --- a/lms/djangoapps/courseware/user_state_client.py +++ b/lms/djangoapps/courseware/user_state_client.py @@ -13,7 +13,7 @@ except ImportError: from django.contrib.auth.models import User from xblock.fields import Scope, ScopeBase -from xblock_user_state.interface import XBlockUserStateClient +from edx_user_state_client.interface import XBlockUserStateClient from courseware.models import StudentModule, StudentModuleHistory from contracts import contract, new_contract from opaque_keys.edx.keys import UsageKey diff --git a/lms/djangoapps/xblock_user_state/__init__.py b/lms/djangoapps/xblock_user_state/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lms/djangoapps/xblock_user_state/interface.py b/lms/djangoapps/xblock_user_state/interface.py deleted file mode 100644 index 2c7254d00f..0000000000 --- a/lms/djangoapps/xblock_user_state/interface.py +++ /dev/null @@ -1,255 +0,0 @@ -""" -A baseclass for a generic client for accessing XBlock Scope.user_state field data. -""" - -from abc import abstractmethod - -from contracts import contract, new_contract, ContractsMeta -from opaque_keys.edx.keys import UsageKey -from xblock.fields import Scope, ScopeBase - -new_contract('UsageKey', UsageKey) - - -class XBlockUserStateClient(object): - """ - First stab at an interface for accessing XBlock User State. This will have - use StudentModule as a backing store in the default case. - - Scope/Goals: - 1. Mediate access to all student-specific state stored by XBlocks. - a. This includes "preferences" and "user_info" (i.e. UserScope.ONE) - b. This includes XBlock Asides. - c. This may later include user_state_summary (i.e. UserScope.ALL). - d. This may include group state in the future. - e. This may include other key types + UserScope.ONE (e.g. Definition) - 2. Assume network service semantics. - At some point, this will probably be calling out to an external service. - Even if it doesn't, we want to be able to implement circuit breakers, so - that a failure in StudentModule doesn't bring down the whole site. - This also implies that the client is running as a user, and whatever is - backing it is smart enough to do authorization checks. - 3. This does not yet cover export-related functionality. - - Open Questions: - 1. Is it sufficient to just send the block_key in and extract course + - version info from it? - 2. Do we want to use the username as the identifier? Privacy implications? - Ease of debugging? - 3. Would a get_many_by_type() be useful? - """ - - __metaclass__ = ContractsMeta - - class ServiceUnavailable(Exception): - """ - This error is raised if the service backing this client is currently unavailable. - """ - pass - - class PermissionDenied(Exception): - """ - This error is raised if the caller is not allowed to access the requested data. - """ - pass - - class DoesNotExist(Exception): - """ - This error is raised if the caller has requested data that does not exist. - """ - pass - - @contract( - username="basestring", - block_key=UsageKey, - scope=ScopeBase, - fields="seq(basestring)|set(basestring)|None", - returns="dict(basestring: *)" - ) - def get(self, username, block_key, scope=Scope.user_state, fields=None): - """ - Retrieve the stored XBlock state for a single xblock usage. - - Arguments: - username: The name of the user whose state should be retrieved - block_key (UsageKey): The UsageKey identifying which xblock state to load. - scope (Scope): The scope to load data from - fields: A list of field values to retrieve. If None, retrieve all stored fields. - - Returns - dict: A dictionary mapping field names to values - """ - return next(self.get_many(username, [block_key], scope, fields=fields))[1] - - @contract( - username="basestring", - block_key=UsageKey, - state="dict(basestring: *)", - scope=ScopeBase, - returns=None, - ) - def set(self, username, block_key, state, scope=Scope.user_state): - """ - Set fields for a particular XBlock. - - Arguments: - username: The name of the user whose state should be retrieved - block_key (UsageKey): The UsageKey identifying which xblock state to load. - state (dict): A dictionary mapping field names to values - scope (Scope): The scope to store data to - """ - self.set_many(username, {block_key: state}, scope) - - @contract( - username="basestring", - block_key=UsageKey, - scope=ScopeBase, - fields="seq(basestring)|set(basestring)|None", - returns=None, - ) - def delete(self, username, block_key, scope=Scope.user_state, fields=None): - """ - Delete the stored XBlock state for a single xblock usage. - - Arguments: - username: The name of the user whose state should be deleted - block_key (UsageKey): The UsageKey identifying which xblock state to delete. - scope (Scope): The scope to delete data from - fields: A list of fields to delete. If None, delete all stored fields. - """ - return self.delete_many(username, [block_key], scope, fields=fields) - - @contract( - username="basestring", - block_key=UsageKey, - scope=ScopeBase, - fields="seq(basestring)|set(basestring)|None", - returns="dict(basestring: datetime)", - ) - def get_mod_date(self, username, block_key, scope=Scope.user_state, fields=None): - """ - Get the last modification date for fields from the specified blocks. - - Arguments: - username: The name of the user whose state should queried - block_key (UsageKey): The UsageKey identifying which xblock modification dates to retrieve. - scope (Scope): The scope to retrieve from. - fields: A list of fields to query. If None, query all fields. - Specific implementations are free to return the same modification date - for all fields, if they don't store changes individually per field. - Implementations may omit fields for which data has not been stored. - - Returns: list a dict of {field_name: modified_date} for each selected field. - """ - results = self.get_mod_date_many(username, [block_key], scope, fields=fields) - return { - field: date for (_, field, date) in results - } - - @contract( - username="basestring", - block_keys="seq(UsageKey)|set(UsageKey)", - scope=ScopeBase, - fields="seq(basestring)|set(basestring)|None", - ) - @abstractmethod - def get_many(self, username, block_keys, scope=Scope.user_state, fields=None): - """ - Retrieve the stored XBlock state for a single xblock usage. - - Arguments: - username: The name of the user whose state should be retrieved - block_keys ([UsageKey]): A list of UsageKeys identifying which xblock states to load. - scope (Scope): The scope to load data from - fields: A list of field values to retrieve. If None, retrieve all stored fields. - - Yields: - (UsageKey, field_state) tuples for each specified UsageKey in block_keys. - field_state is a dict mapping field names to values. - """ - raise NotImplementedError() - - @contract( - username="basestring", - block_keys_to_state="dict(UsageKey: dict(basestring: *))", - scope=ScopeBase, - returns=None, - ) - @abstractmethod - def set_many(self, username, block_keys_to_state, scope=Scope.user_state): - """ - Set fields for a particular XBlock. - - Arguments: - username: The name of the user whose state should be retrieved - block_keys_to_state (dict): A dict mapping UsageKeys to state dicts. - Each state dict maps field names to values. These state dicts - are overlaid over the stored state. To delete fields, use - :meth:`delete` or :meth:`delete_many`. - scope (Scope): The scope to load data from - """ - raise NotImplementedError() - - @contract( - username="basestring", - block_keys="seq(UsageKey)|set(UsageKey)", - scope=ScopeBase, - fields="seq(basestring)|set(basestring)|None", - returns=None, - ) - @abstractmethod - def delete_many(self, username, block_keys, scope=Scope.user_state, fields=None): - """ - Delete the stored XBlock state for a many xblock usages. - - Arguments: - username: The name of the user whose state should be deleted - block_key (UsageKey): The UsageKey identifying which xblock state to delete. - scope (Scope): The scope to delete data from - fields: A list of fields to delete. If None, delete all stored fields. - """ - raise NotImplementedError() - - @contract( - username="basestring", - block_keys="seq(UsageKey)|set(UsageKey)", - scope=ScopeBase, - fields="seq(basestring)|set(basestring)|None", - ) - @abstractmethod - def get_mod_date_many(self, username, block_keys, scope=Scope.user_state, fields=None): - """ - Get the last modification date for fields from the specified blocks. - - Arguments: - username: The name of the user whose state should be queried - block_key (UsageKey): The UsageKey identifying which xblock modification dates to retrieve. - scope (Scope): The scope to retrieve from. - fields: A list of fields to query. If None, delete all stored fields. - Specific implementations are free to return the same modification date - for all fields, if they don't store changes individually per field. - Implementations may omit fields for which data has not been stored. - - Yields: tuples of (block, field_name, modified_date) for each selected field. - """ - raise NotImplementedError() - - def get_history(self, username, block_key, scope=Scope.user_state): - """We don't guarantee that history for many blocks will be fast.""" - raise NotImplementedError() - - def iter_all_for_block(self, block_key, scope=Scope.user_state, batch_size=None): - """ - You get no ordering guarantees. Fetching will happen in batch_size - increments. If you're using this method, you should be running in an - async task. - """ - raise NotImplementedError() - - def iter_all_for_course(self, course_key, block_type=None, scope=Scope.user_state, batch_size=None): - """ - You get no ordering guarantees. Fetching will happen in batch_size - increments. If you're using this method, you should be running in an - async task. - """ - raise NotImplementedError() diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt index 3db9e7883e..5bd5507d26 100644 --- a/requirements/edx/github.txt +++ b/requirements/edx/github.txt @@ -53,6 +53,7 @@ git+https://github.com/edx/edx-lint.git@ed8c8d2a0267d4d42f43642d193e25f8bd575d9b -e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive -e git+https://github.com/edx/edx-reverification-block.git@a286e89c73e1b788e35ac5b08a54b71a9fa63cfd#egg=edx-reverification-block git+https://github.com/edx/ecommerce-api-client.git@1.0.0#egg=ecommerce-api-client==1.0.0 +-e git+https://github.com/edx/edx-user-state-client.git@64a8b603f42669bb7fdca03d364d4e8d3d6ad67d#egg=edx-user-state-client # Third Party XBlocks -e git+https://github.com/mitodl/edx-sga@172a90fd2738f8142c10478356b2d9ed3e55334a#egg=edx-sga