Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
4182f874
Commit
4182f874
authored
Mar 09, 2015
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add the candidate XBlockUserStateClient interface
parent
4312c0e7
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
254 additions
and
0 deletions
+254
-0
lms/djangoapps/xblock_user_state/__init__.py
+0
-0
lms/djangoapps/xblock_user_state/interface.py
+254
-0
No files found.
lms/djangoapps/xblock_user_state/__init__.py
0 → 100644
View file @
4182f874
lms/djangoapps/xblock_user_state/interface.py
0 → 100644
View file @
4182f874
"""
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
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment