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
...
@@ -3,10 +3,20 @@ An implementation of :class:`XBlockUserStateClient`, which stores XBlock Scope.u
data in a Django ORM model.
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.
An interface that uses the Django ORM StudentModule as a backend.
"""
"""
...
@@ -29,7 +39,11 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
...
@@ -29,7 +39,11 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
"""
"""
pass
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.
Retrieve the stored XBlock state for a single xblock usage.
...
@@ -45,6 +59,7 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
...
@@ -45,6 +59,7 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
Raises:
Raises:
DoesNotExist if no entry is found.
DoesNotExist if no entry is found.
"""
"""
assert
self
.
user
.
username
==
username
try
:
try
:
_usage_key
,
state
=
next
(
self
.
get_many
(
username
,
[
block_key
],
scope
,
fields
=
fields
))
_usage_key
,
state
=
next
(
self
.
get_many
(
username
,
[
block_key
],
scope
,
fields
=
fields
))
except
StopIteration
:
except
StopIteration
:
...
@@ -52,19 +67,68 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
...
@@ -52,19 +67,68 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
return
state
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.
Set fields for a particular XBlock.
Arguments:
Arguments:
username: The name of the user whose state should be retrieved
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
state (dict): A dictionary mapping field names to values
scope (Scope): The scope to load data from
scope (Scope): The scope to load data from
"""
"""
assert
self
.
user
.
username
==
username
self
.
set_many
(
username
,
{
block_key
:
state
},
scope
)
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.
Retrieve the stored XBlock state for a single xblock usage.
...
@@ -78,11 +142,19 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
...
@@ -78,11 +142,19 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
(UsageKey, field_state) tuples for each specified UsageKey in block_keys.
(UsageKey, field_state) tuples for each specified UsageKey in block_keys.
field_state is a dict mapping field names to values.
field_state is a dict mapping field names to values.
"""
"""
assert
self
.
user
.
username
==
username
if
scope
!=
Scope
.
user_state
:
if
scope
!=
Scope
.
user_state
:
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
ValueError
(
"Only Scope.user_state is supported, not {}"
.
format
(
scope
))
raise
NotImplementedError
()
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.
Set fields for a particular XBlock.
...
@@ -94,17 +166,75 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
...
@@ -94,17 +166,75 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
:meth:`delete` or :meth:`delete_many`.
:meth:`delete` or :meth:`delete_many`.
scope (Scope): The scope to load data from
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
:
if
scope
!=
Scope
.
user_state
:
raise
ValueError
(
"Only Scope.user_state is supported"
)
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."""
"""We don't guarantee that history for many blocks will be fast."""
assert
self
.
user
.
username
==
username
if
scope
!=
Scope
.
user_state
:
if
scope
!=
Scope
.
user_state
:
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
NotImplementedError
()
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
You get no ordering guarantees. Fetching will happen in batch_size
increments. If you're using this method, you should be running in an
increments. If you're using this method, you should be running in an
...
@@ -114,7 +244,7 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
...
@@ -114,7 +244,7 @@ class DjangoXBlockUserStateClient(DjangoXBlockUserStateClient):
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
NotImplementedError
()
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
You get no ordering guarantees. Fetching will happen in batch_size
increments. If you're using this method, you should be running in an
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