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
674b68b7
Commit
674b68b7
authored
Apr 24, 2015
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add implementation of get_many and set_many to DjangoXBlockUserStateClient
parent
1ec4ae7c
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
143 additions
and
13 deletions
+143
-13
lms/djangoapps/courseware/user_state_client.py
+143
-13
No files found.
lms/djangoapps/courseware/user_state_client.py
View file @
674b68b7
...
...
@@ -3,10 +3,20 @@ An implementation of :class:`XBlockUserStateClient`, which stores XBlock Scope.u
data in a Django ORM model.
"""
from
xblock_user_state.interface
import
DjangoXBlockUserStateClient
import
itertools
from
operator
import
attrgetter
try
:
import
simplejson
as
json
except
ImportError
:
import
json
class
DjangoXBlockUserStateClient
(
DjangoXBlockUserStateClient
):
from
xblock.fields
import
Scope
from
xblock_user_state.interface
import
XBlockUserStateClient
from
courseware.models
import
StudentModule
class
DjangoXBlockUserStateClient
(
XBlockUserStateClient
):
"""
An interface that uses the Django ORM StudentModule as a backend.
"""
...
...
@@ -29,7 +39,11 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
"""
pass
def
get
(
username
,
block_key
,
scope
=
Scope
.
user_state
,
fields
=
None
):
def
__init__
(
self
,
user
):
self
.
_student_module_cache
=
{}
self
.
user
=
user
def
get
(
self
,
username
,
block_key
,
scope
=
Scope
.
user_state
,
fields
=
None
):
"""
Retrieve the stored XBlock state for a single xblock usage.
...
...
@@ -45,6 +59,7 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
Raises:
DoesNotExist if no entry is found.
"""
assert
self
.
user
.
username
==
username
try
:
_usage_key
,
state
=
next
(
self
.
get_many
(
username
,
[
block_key
],
scope
,
fields
=
fields
))
except
StopIteration
:
...
...
@@ -52,19 +67,68 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
return
state
def
set
(
username
,
block_key
,
state
,
scope
=
Scope
.
user_state
):
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
.
block_key (UsageKey): The UsageKey identifying which xblock state to
update
.
state (dict): A dictionary mapping field names to values
scope (Scope): The scope to load data from
"""
assert
self
.
user
.
username
==
username
self
.
set_many
(
username
,
{
block_key
:
state
},
scope
)
def
get_many
(
username
,
block_keys
,
scope
=
Scope
.
user_state
,
fields
=
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.
"""
assert
self
.
user
.
username
==
username
return
self
.
delete_many
(
username
,
[
block_key
],
scope
,
fields
=
fields
)
def
_get_field_objects
(
self
,
username
,
block_keys
):
"""
Retrieve the :class:`~StudentModule`s for the supplied ``username`` and ``block_keys``.
Arguments:
username (str): The name of the user to load `StudentModule`s for.
block_keys (list of :class:`~UsageKey`): The set of XBlocks to load data for.
"""
course_key_func
=
attrgetter
(
'course_key'
)
by_course
=
itertools
.
groupby
(
sorted
(
block_keys
,
key
=
course_key_func
),
course_key_func
,
)
for
course_key
,
usage_keys
in
by_course
:
not_cached
=
[]
for
usage_key
in
usage_keys
:
if
usage_key
in
self
.
_student_module_cache
:
yield
self
.
_student_module_cache
[
usage_key
]
else
:
not_cached
.
append
(
usage_key
)
query
=
StudentModule
.
objects
.
chunked_filter
(
'module_state_key__in'
,
not_cached
,
student__username
=
username
,
course_id
=
course_key
,
)
for
student_module
in
query
:
usage_key
=
student_module
.
module_state_key
.
map_into_course
(
student_module
.
course_id
)
self
.
_student_module_cache
[
usage_key
]
=
student_module
yield
(
student_module
,
usage_key
)
def
get_many
(
self
,
username
,
block_keys
,
scope
=
Scope
.
user_state
,
fields
=
None
):
"""
Retrieve the stored XBlock state for a single xblock usage.
...
...
@@ -78,11 +142,19 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
(UsageKey, field_state) tuples for each specified UsageKey in block_keys.
field_state is a dict mapping field names to values.
"""
assert
self
.
user
.
username
==
username
if
scope
!=
Scope
.
user_state
:
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
NotImplementedError
()
raise
ValueError
(
"Only Scope.user_state is supported, not {}"
.
format
(
scope
))
def
set_many
(
username
,
block_keys_to_state
,
scope
=
Scope
.
user_state
):
modules
=
self
.
_get_field_objects
(
username
,
block_keys
)
for
module
,
usage_key
in
modules
:
if
module
.
state
is
None
:
state
=
{}
else
:
state
=
json
.
loads
(
module
.
state
)
yield
(
usage_key
,
state
)
def
set_many
(
self
,
username
,
block_keys_to_state
,
scope
=
Scope
.
user_state
):
"""
Set fields for a particular XBlock.
...
...
@@ -94,17 +166,75 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
:meth:`delete` or :meth:`delete_many`.
scope (Scope): The scope to load data from
"""
assert
self
.
user
.
username
==
username
if
scope
!=
Scope
.
user_state
:
raise
ValueError
(
"Only Scope.user_state is supported"
)
field_objects
=
self
.
_get_field_objects
(
username
,
block_keys_to_state
.
keys
())
for
field_object
in
field_objects
:
usage_key
=
field_object
.
module_state_key
.
map_into_course
(
field_object
.
course_id
)
current_state
=
json
.
loads
(
field_object
.
state
)
current_state
.
update
(
block_keys_to_state
.
pop
(
usage_key
))
field_object
.
state
=
json
.
dumps
(
current_state
)
field_object
.
save
()
for
usage_key
,
state
in
block_keys_to_state
.
items
():
student_module
,
created
=
StudentModule
.
objects
.
get_or_create
(
student
=
self
.
user
,
course_id
=
usage_key
.
course_key
,
module_state_key
=
usage_key
,
defaults
=
{
'state'
:
json
.
dumps
(
state
),
'module_type'
:
usage_key
.
block_type
,
},
)
if
not
created
:
if
student_module
.
state
is
None
:
current_state
=
{}
else
:
current_state
=
json
.
loads
(
student_module
.
state
)
current_state
.
update
(
state
)
student_module
.
state
=
json
.
dumps
(
current_state
)
# We just read this object, so we know that we can do an update
student_module
.
save
(
force_update
=
True
)
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.
"""
assert
self
.
user
.
username
==
username
if
scope
!=
Scope
.
user_state
:
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
NotImplementedError
()
def
get_history
(
username
,
block_key
,
scope
=
Scope
.
user_state
):
student_modules
=
self
.
_get_field_objects
(
username
,
block_keys
)
for
student_module
,
_
in
student_modules
:
if
fields
is
None
:
field_object
.
state
=
"{}"
else
:
current_state
=
json
.
loads
(
field_object
.
state
)
for
field
in
fields
:
if
field
in
current_state
:
del
current_state
[
field
]
student_module
.
state
=
json
.
dumps
(
current_state
)
# We just read this object, so we know that we can do an update
student_module
.
save
(
force_update
=
True
)
def
get_history
(
self
,
username
,
block_key
,
scope
=
Scope
.
user_state
):
"""We don't guarantee that history for many blocks will be fast."""
assert
self
.
user
.
username
==
username
if
scope
!=
Scope
.
user_state
:
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
NotImplementedError
()
def
iter_all_for_block
(
block_key
,
scope
=
Scope
.
user_state
,
batch_size
=
None
):
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
...
...
@@ -114,7 +244,7 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
NotImplementedError
()
def
iter_all_for_course
(
course_key
,
block_type
=
None
,
scope
=
Scope
.
user_state
,
batch_size
=
None
):
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
...
...
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