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
0e8b303d
Commit
0e8b303d
authored
Jul 10, 2015
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Cache CcxFieldOverrides on a per-request basis
parent
63ddbd9b
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
50 additions
and
57 deletions
+50
-57
lms/djangoapps/ccx/overrides.py
+37
-31
lms/djangoapps/ccx/tests/test_field_override_performance.py
+3
-3
lms/djangoapps/ccx/tests/test_overrides.py
+2
-2
lms/djangoapps/ccx/tests/test_views.py
+8
-21
No files found.
lms/djangoapps/ccx/overrides.py
View file @
0e8b303d
...
@@ -84,35 +84,40 @@ def get_override_for_ccx(ccx, block, name, default=None):
...
@@ -84,35 +84,40 @@ def get_override_for_ccx(ccx, block, name, default=None):
specify the block and the name of the field. If the field is not
specify the block and the name of the field. If the field is not
overridden for the given ccx, returns `default`.
overridden for the given ccx, returns `default`.
"""
"""
if
not
hasattr
(
block
,
'_ccx_overrides'
):
overrides
=
_get_overrides_for_ccx
(
ccx
)
block
.
_ccx_overrides
=
{}
# pylint: disable=protected-access
overrides
=
block
.
_ccx_overrides
.
get
(
ccx
.
id
)
# pylint: disable=protected-access
if
isinstance
(
block
.
location
,
CCXBlockUsageLocator
):
if
overrides
is
None
:
non_ccx_key
=
block
.
location
.
to_block_locator
()
overrides
=
_get_overrides_for_ccx
(
ccx
,
block
)
else
:
block
.
_ccx_overrides
[
ccx
.
id
]
=
overrides
# pylint: disable=protected-access
non_ccx_key
=
block
.
location
return
overrides
.
get
(
name
,
default
)
block_overrides
=
overrides
.
get
(
non_ccx_key
,
{})
if
name
in
block_overrides
:
return
block
.
fields
[
name
]
.
from_json
(
block_overrides
[
name
])
else
:
return
default
def
_get_overrides_for_ccx
(
ccx
,
block
):
def
_get_overrides_for_ccx
(
ccx
):
"""
"""
Returns a dictionary mapping field name to overriden value for any
Returns a dictionary mapping field name to overriden value for any
overrides set on this block for this CCX.
overrides set on this block for this CCX.
"""
"""
overrides
=
{}
overrides
_cache
=
request_cache
.
get_cache
(
'ccx-overrides'
)
# block as passed in may have a location specific to a CCX, we must strip
# that for this query
if
ccx
not
in
overrides_cache
:
location
=
block
.
location
overrides
=
{}
if
isinstance
(
block
.
location
,
CCXBlockUsageLocator
):
query
=
CcxFieldOverride
.
objects
.
filter
(
location
=
block
.
location
.
to_block_locator
()
ccx
=
ccx
,
query
=
CcxFieldOverride
.
objects
.
filter
(
)
ccx
=
ccx
,
location
=
location
for
override
in
query
:
)
block_overrides
=
overrides
.
setdefault
(
override
.
location
,
{}
)
for
override
in
query
:
block_overrides
[
override
.
field
]
=
json
.
loads
(
override
.
value
)
field
=
block
.
fields
[
override
.
field
]
value
=
field
.
from_json
(
json
.
loads
(
override
.
value
))
overrides_cache
[
ccx
]
=
overrides
overrides
[
override
.
field
]
=
value
return
overrides
return
overrides
_cache
[
ccx
]
@transaction.commit_on_success
@transaction.commit_on_success
...
@@ -123,23 +128,25 @@ def override_field_for_ccx(ccx, block, name, value):
...
@@ -123,23 +128,25 @@ def override_field_for_ccx(ccx, block, name, value):
value to set for the given field.
value to set for the given field.
"""
"""
field
=
block
.
fields
[
name
]
field
=
block
.
fields
[
name
]
value
=
json
.
dumps
(
field
.
to_json
(
value
))
value_json
=
field
.
to_json
(
value
)
serialized_value
=
json
.
dumps
(
value_json
)
try
:
try
:
override
=
CcxFieldOverride
.
objects
.
create
(
override
=
CcxFieldOverride
.
objects
.
create
(
ccx
=
ccx
,
ccx
=
ccx
,
location
=
block
.
location
,
location
=
block
.
location
,
field
=
name
,
field
=
name
,
value
=
value
)
value
=
serialized_value
)
except
IntegrityError
:
except
IntegrityError
:
transaction
.
commit
()
transaction
.
commit
()
override
=
CcxFieldOverride
.
objects
.
get
(
override
=
CcxFieldOverride
.
objects
.
get
(
ccx
=
ccx
,
ccx
=
ccx
,
location
=
block
.
location
,
location
=
block
.
location
,
field
=
name
)
field
=
name
override
.
value
=
value
)
override
.
value
=
serialized_value
override
.
save
()
override
.
save
()
if
hasattr
(
block
,
'_ccx_overrides'
):
_get_overrides_for_ccx
(
ccx
)
.
setdefault
(
block
.
location
,
{})[
name
]
=
value_json
del
block
.
_ccx_overrides
[
ccx
.
id
]
# pylint: disable=protected-access
def
clear_override_for_ccx
(
ccx
,
block
,
name
):
def
clear_override_for_ccx
(
ccx
,
block
,
name
):
...
@@ -155,8 +162,7 @@ def clear_override_for_ccx(ccx, block, name):
...
@@ -155,8 +162,7 @@ def clear_override_for_ccx(ccx, block, name):
location
=
block
.
location
,
location
=
block
.
location
,
field
=
name
)
.
delete
()
field
=
name
)
.
delete
()
if
hasattr
(
block
,
'_ccx_overrides'
):
_get_overrides_for_ccx
(
ccx
)
.
setdefault
(
block
.
location
,
{})
.
pop
(
name
)
del
block
.
_ccx_overrides
[
ccx
.
id
]
# pylint: disable=protected-access
except
CcxFieldOverride
.
DoesNotExist
:
except
CcxFieldOverride
.
DoesNotExist
:
pass
pass
lms/djangoapps/ccx/tests/test_field_override_performance.py
View file @
0e8b303d
...
@@ -250,9 +250,9 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
...
@@ -250,9 +250,9 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
(
'ccx'
,
1
,
True
,
False
):
(
23
,
4
,
9
),
(
'ccx'
,
1
,
True
,
False
):
(
23
,
4
,
9
),
(
'ccx'
,
2
,
True
,
False
):
(
68
,
19
,
54
),
(
'ccx'
,
2
,
True
,
False
):
(
68
,
19
,
54
),
(
'ccx'
,
3
,
True
,
False
):
(
263
,
84
,
215
),
(
'ccx'
,
3
,
True
,
False
):
(
263
,
84
,
215
),
(
'ccx'
,
1
,
True
,
True
):
(
33
,
4
,
13
),
(
'ccx'
,
1
,
True
,
True
):
(
25
,
4
,
13
),
(
'ccx'
,
2
,
True
,
True
):
(
68
,
19
,
84
),
(
'ccx'
,
2
,
True
,
True
):
(
70
,
19
,
84
),
(
'ccx'
,
3
,
True
,
True
):
(
26
3
,
84
,
335
),
(
'ccx'
,
3
,
True
,
True
):
(
26
5
,
84
,
335
),
(
'no_overrides'
,
1
,
False
,
False
):
(
23
,
4
,
9
),
(
'no_overrides'
,
1
,
False
,
False
):
(
23
,
4
,
9
),
(
'no_overrides'
,
2
,
False
,
False
):
(
68
,
19
,
54
),
(
'no_overrides'
,
2
,
False
,
False
):
(
68
,
19
,
54
),
(
'no_overrides'
,
3
,
False
,
False
):
(
263
,
84
,
215
),
(
'no_overrides'
,
3
,
False
,
False
):
(
263
,
84
,
215
),
...
...
lms/djangoapps/ccx/tests/test_overrides.py
View file @
0e8b303d
...
@@ -100,7 +100,7 @@ class TestFieldOverrides(ModuleStoreTestCase):
...
@@ -100,7 +100,7 @@ class TestFieldOverrides(ModuleStoreTestCase):
"""
"""
ccx_start
=
datetime
.
datetime
(
2014
,
12
,
25
,
00
,
00
,
tzinfo
=
pytz
.
UTC
)
ccx_start
=
datetime
.
datetime
(
2014
,
12
,
25
,
00
,
00
,
tzinfo
=
pytz
.
UTC
)
chapter
=
self
.
ccx
.
course
.
get_children
()[
0
]
chapter
=
self
.
ccx
.
course
.
get_children
()[
0
]
with
self
.
assertNumQueries
(
4
):
with
self
.
assertNumQueries
(
3
):
override_field_for_ccx
(
self
.
ccx
,
chapter
,
'start'
,
ccx_start
)
override_field_for_ccx
(
self
.
ccx
,
chapter
,
'start'
,
ccx_start
)
dummy
=
chapter
.
start
dummy
=
chapter
.
start
...
@@ -110,7 +110,7 @@ class TestFieldOverrides(ModuleStoreTestCase):
...
@@ -110,7 +110,7 @@ class TestFieldOverrides(ModuleStoreTestCase):
"""
"""
ccx_start
=
datetime
.
datetime
(
2014
,
12
,
25
,
00
,
00
,
tzinfo
=
pytz
.
UTC
)
ccx_start
=
datetime
.
datetime
(
2014
,
12
,
25
,
00
,
00
,
tzinfo
=
pytz
.
UTC
)
chapter
=
self
.
ccx
.
course
.
get_children
()[
0
]
chapter
=
self
.
ccx
.
course
.
get_children
()[
0
]
with
self
.
assertNumQueries
(
4
):
with
self
.
assertNumQueries
(
3
):
override_field_for_ccx
(
self
.
ccx
,
chapter
,
'start'
,
ccx_start
)
override_field_for_ccx
(
self
.
ccx
,
chapter
,
'start'
,
ccx_start
)
dummy1
=
chapter
.
start
dummy1
=
chapter
.
start
dummy2
=
chapter
.
start
dummy2
=
chapter
.
start
...
...
lms/djangoapps/ccx/tests/test_views.py
View file @
0e8b303d
...
@@ -21,6 +21,7 @@ from django.test.utils import override_settings
...
@@ -21,6 +21,7 @@ from django.test.utils import override_settings
from
django.test
import
RequestFactory
from
django.test
import
RequestFactory
from
edxmako.shortcuts
import
render_to_response
# pylint: disable=import-error
from
edxmako.shortcuts
import
render_to_response
# pylint: disable=import-error
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
request_cache.middleware
import
RequestCache
from
student.roles
import
CourseCcxCoachRole
# pylint: disable=import-error
from
student.roles
import
CourseCcxCoachRole
# pylint: disable=import-error
from
student.tests.factories
import
(
# pylint: disable=import-error
from
student.tests.factories
import
(
# pylint: disable=import-error
AdminFactory
,
AdminFactory
,
...
@@ -503,26 +504,6 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -503,26 +504,6 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
role
.
add_users
(
coach
)
role
.
add_users
(
coach
)
ccx
=
CcxFactory
(
course_id
=
course
.
id
,
coach
=
self
.
coach
)
ccx
=
CcxFactory
(
course_id
=
course
.
id
,
coach
=
self
.
coach
)
# Apparently the test harness doesn't use LmsFieldStorage, and I'm not
# sure if there's a way to poke the test harness to do so. So, we'll
# just inject the override field storage in this brute force manner.
OverrideFieldData
.
provider_classes
=
None
# pylint: disable=protected-access
for
block
in
iter_blocks
(
course
):
block
.
_field_data
=
OverrideFieldData
.
wrap
(
coach
,
course
,
block
.
_field_data
)
new_cache
=
{
'tabs'
:
[],
'discussion_topics'
:
[]}
if
'grading_policy'
in
block
.
_field_data_cache
:
new_cache
[
'grading_policy'
]
=
block
.
_field_data_cache
[
'grading_policy'
]
block
.
_field_data_cache
=
new_cache
def
cleanup_provider_classes
():
"""
After everything is done, clean up by un-doing the change to the
OverrideFieldData object that is done during the wrap method.
"""
OverrideFieldData
.
provider_classes
=
None
self
.
addCleanup
(
cleanup_provider_classes
)
# override course grading policy and make last section invisible to students
# override course grading policy and make last section invisible to students
override_field_for_ccx
(
ccx
,
course
,
'grading_policy'
,
{
override_field_for_ccx
(
ccx
,
course
,
'grading_policy'
,
{
'GRADER'
:
[
'GRADER'
:
[
...
@@ -561,9 +542,13 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -561,9 +542,13 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
client
.
login
(
username
=
coach
.
username
,
password
=
"test"
)
self
.
client
.
login
(
username
=
coach
.
username
,
password
=
"test"
)
self
.
addCleanup
(
RequestCache
.
clear_request_cache
)
@patch
(
'ccx.views.render_to_response'
,
intercept_renderer
)
@patch
(
'ccx.views.render_to_response'
,
intercept_renderer
)
def
test_gradebook
(
self
):
def
test_gradebook
(
self
):
self
.
course
.
enable_ccx
=
True
self
.
course
.
enable_ccx
=
True
RequestCache
.
clear_request_cache
()
url
=
reverse
(
url
=
reverse
(
'ccx_gradebook'
,
'ccx_gradebook'
,
kwargs
=
{
'course_id'
:
self
.
ccx_key
}
kwargs
=
{
'course_id'
:
self
.
ccx_key
}
...
@@ -580,6 +565,8 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -580,6 +565,8 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
def
test_grades_csv
(
self
):
def
test_grades_csv
(
self
):
self
.
course
.
enable_ccx
=
True
self
.
course
.
enable_ccx
=
True
RequestCache
.
clear_request_cache
()
url
=
reverse
(
url
=
reverse
(
'ccx_grades_csv'
,
'ccx_grades_csv'
,
kwargs
=
{
'course_id'
:
self
.
ccx_key
}
kwargs
=
{
'course_id'
:
self
.
ccx_key
}
...
@@ -591,11 +578,11 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -591,11 +578,11 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
response
.
content
.
strip
()
.
split
(
'
\n
'
)
response
.
content
.
strip
()
.
split
(
'
\n
'
)
)
)
data
=
dict
(
zip
(
headers
,
row
))
data
=
dict
(
zip
(
headers
,
row
))
self
.
assertTrue
(
'HW 04'
not
in
data
)
self
.
assertEqual
(
data
[
'HW 01'
],
'0.75'
)
self
.
assertEqual
(
data
[
'HW 01'
],
'0.75'
)
self
.
assertEqual
(
data
[
'HW 02'
],
'0.5'
)
self
.
assertEqual
(
data
[
'HW 02'
],
'0.5'
)
self
.
assertEqual
(
data
[
'HW 03'
],
'0.25'
)
self
.
assertEqual
(
data
[
'HW 03'
],
'0.25'
)
self
.
assertEqual
(
data
[
'HW Avg'
],
'0.5'
)
self
.
assertEqual
(
data
[
'HW Avg'
],
'0.5'
)
self
.
assertTrue
(
'HW 04'
not
in
data
)
@patch
(
'courseware.views.render_to_response'
,
intercept_renderer
)
@patch
(
'courseware.views.render_to_response'
,
intercept_renderer
)
def
test_student_progress
(
self
):
def
test_student_progress
(
self
):
...
...
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