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
86eca26a
Commit
86eca26a
authored
Jun 28, 2017
by
Sanford Student
Committed by
Alex Dusenbery
Jun 29, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
EDUCATOR-778: prefetched visible blocks
parent
b673ceee
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
85 additions
and
42 deletions
+85
-42
lms/djangoapps/grades/models.py
+52
-8
lms/djangoapps/grades/new/course_grade_factory.py
+33
-23
lms/djangoapps/grades/tests/test_tasks.py
+0
-11
No files found.
lms/djangoapps/grades/models.py
View file @
86eca26a
...
@@ -181,6 +181,7 @@ class VisibleBlocks(models.Model):
...
@@ -181,6 +181,7 @@ class VisibleBlocks(models.Model):
in the blocks_json field. A hash of this json array is used for lookup
in the blocks_json field. A hash of this json array is used for lookup
purposes.
purposes.
"""
"""
CACHE_NAMESPACE
=
u"grades.models.VisibleBlocks"
blocks_json
=
models
.
TextField
()
blocks_json
=
models
.
TextField
()
hashed
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
)
hashed
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
)
course_id
=
CourseKeyField
(
blank
=
False
,
max_length
=
255
,
db_index
=
True
)
course_id
=
CourseKeyField
(
blank
=
False
,
max_length
=
255
,
db_index
=
True
)
...
@@ -207,27 +208,37 @@ class VisibleBlocks(models.Model):
...
@@ -207,27 +208,37 @@ class VisibleBlocks(models.Model):
@classmethod
@classmethod
def
bulk_read
(
cls
,
course_key
):
def
bulk_read
(
cls
,
course_key
):
"""
"""
Reads all visible block records for the given course.
Reads and returns all visible block records for the given course from
the cache. The cache is initialize with the visible blocks for this
course if no entry currently exists.has no entry for this course,
the cache is updated.
Arguments:
Arguments:
course_key: The course identifier for the desired records
course_key: The course identifier for the desired records
"""
"""
return
cls
.
objects
.
filter
(
course_id
=
course_key
)
prefetched
=
get_cache
(
cls
.
CACHE_NAMESPACE
)
.
get
(
cls
.
_cache_key
(
course_key
))
if
not
prefetched
:
prefetched
=
cls
.
_initialize_cache
(
course_key
)
return
prefetched
@classmethod
@classmethod
def
bulk_create
(
cls
,
block_record_lists
):
def
bulk_create
(
cls
,
course_key
,
block_record_lists
):
"""
"""
Bulk creates VisibleBlocks for the given iterator of
Bulk creates VisibleBlocks for the given iterator of
BlockRecordList objects.
BlockRecordList objects and updates the VisibleBlocks cache
for the block records' course with the new VisibleBlocks.
Returns the newly created visible blocks.
"""
"""
return
cls
.
objects
.
bulk_create
([
created
=
cls
.
objects
.
bulk_create
([
VisibleBlocks
(
VisibleBlocks
(
blocks_json
=
brl
.
json_value
,
blocks_json
=
brl
.
json_value
,
hashed
=
brl
.
hash_value
,
hashed
=
brl
.
hash_value
,
course_id
=
brl
.
course_key
,
course_id
=
course_key
,
)
)
for
brl
in
block_record_lists
for
brl
in
block_record_lists
])
])
cls
.
_update_cache
(
course_key
,
created
)
return
created
@classmethod
@classmethod
def
bulk_get_or_create
(
cls
,
block_record_lists
,
course_key
):
def
bulk_get_or_create
(
cls
,
block_record_lists
,
course_key
):
...
@@ -236,9 +247,42 @@ class VisibleBlocks(models.Model):
...
@@ -236,9 +247,42 @@ class VisibleBlocks(models.Model):
BlockRecordList objects for the given course_key, but
BlockRecordList objects for the given course_key, but
only for those that aren't already created.
only for those that aren't already created.
"""
"""
existent_records
=
{
record
.
hashed
:
record
for
record
in
cls
.
bulk_read
(
course_key
)}
existent_records
=
cls
.
bulk_read
(
course_key
)
non_existent_brls
=
{
brl
for
brl
in
block_record_lists
if
brl
.
hash_value
not
in
existent_records
}
non_existent_brls
=
{
brl
for
brl
in
block_record_lists
if
brl
.
hash_value
not
in
existent_records
}
cls
.
bulk_create
(
non_existent_brls
)
cls
.
bulk_create
(
course_key
,
non_existent_brls
)
@classmethod
def
_initialize_cache
(
cls
,
course_key
):
"""
Prefetches visible blocks for the given course and stores in the cache.
Returns a dictionary mapping hashes of these block records to the
block record objects.
"""
prefetched
=
{
record
.
hashed
:
record
for
record
in
cls
.
objects
.
filter
(
course_id
=
course_key
)}
get_cache
(
cls
.
CACHE_NAMESPACE
)[
cls
.
_cache_key
(
course_key
)]
=
prefetched
return
prefetched
@classmethod
def
_update_cache
(
cls
,
course_key
,
visible_blocks
):
"""
Adds a specific set of visible blocks to the request cache.
This assumes that prefetch has already been called.
"""
get_cache
(
cls
.
CACHE_NAMESPACE
)[
cls
.
_cache_key
(
course_key
)]
.
update
(
{
visible_block
.
hashed
:
visible_block
for
visible_block
in
visible_blocks
}
)
@classmethod
def
clear_cache
(
cls
,
course_key
):
"""
Clears the cache of all contents for a given course.
"""
cache
=
get_cache
(
cls
.
CACHE_NAMESPACE
)
cache
.
pop
(
cls
.
_cache_key
(
course_key
),
None
)
@classmethod
def
_cache_key
(
cls
,
course_key
):
return
u"visible_blocks_cache.{}"
.
format
(
course_key
)
class
PersistentSubsectionGrade
(
DeleteGradesMixin
,
TimeStampedModel
):
class
PersistentSubsectionGrade
(
DeleteGradesMixin
,
TimeStampedModel
):
...
...
lms/djangoapps/grades/new/course_grade_factory.py
View file @
86eca26a
from
collections
import
namedtuple
from
collections
import
namedtuple
from
contextlib
import
contextmanager
from
logging
import
getLogger
from
logging
import
getLogger
import
dogstats_wrapper
as
dog_stats_api
import
dogstats_wrapper
as
dog_stats_api
...
@@ -6,7 +7,7 @@ from openedx.core.djangoapps.signals.signals import COURSE_GRADE_CHANGED
...
@@ -6,7 +7,7 @@ from openedx.core.djangoapps.signals.signals import COURSE_GRADE_CHANGED
from
..config
import
assume_zero_if_absent
,
should_persist_grades
from
..config
import
assume_zero_if_absent
,
should_persist_grades
from
..config.waffle
import
WRITE_ONLY_IF_ENGAGED
,
waffle
from
..config.waffle
import
WRITE_ONLY_IF_ENGAGED
,
waffle
from
..models
import
PersistentCourseGrade
from
..models
import
PersistentCourseGrade
,
VisibleBlocks
from
.course_data
import
CourseData
from
.course_data
import
CourseData
from
.course_grade
import
CourseGrade
,
ZeroCourseGrade
from
.course_grade
import
CourseGrade
,
ZeroCourseGrade
...
@@ -75,6 +76,14 @@ class CourseGradeFactory(object):
...
@@ -75,6 +76,14 @@ class CourseGradeFactory(object):
course_data
=
CourseData
(
user
,
course
,
collected_block_structure
,
course_structure
,
course_key
)
course_data
=
CourseData
(
user
,
course
,
collected_block_structure
,
course_structure
,
course_key
)
return
self
.
_update
(
user
,
course_data
,
read_only
=
False
)
return
self
.
_update
(
user
,
course_data
,
read_only
=
False
)
@contextmanager
def
_course_transaction
(
self
,
course_key
):
"""
Provides a transaction context in which GradeResults are created.
"""
yield
VisibleBlocks
.
clear_cache
(
course_key
)
def
iter
(
def
iter
(
self
,
self
,
users
,
users
,
...
@@ -100,28 +109,29 @@ class CourseGradeFactory(object):
...
@@ -100,28 +109,29 @@ class CourseGradeFactory(object):
course_data
=
CourseData
(
course_data
=
CourseData
(
user
=
None
,
course
=
course
,
collected_block_structure
=
collected_block_structure
,
course_key
=
course_key
,
user
=
None
,
course
=
course
,
collected_block_structure
=
collected_block_structure
,
course_key
=
course_key
,
)
)
for
user
in
users
:
stats_tags
=
[
u'action:{}'
.
format
(
course_data
.
course_key
)]
with
dog_stats_api
.
timer
(
with
self
.
_course_transaction
(
course_data
.
course_key
):
'lms.grades.CourseGradeFactory.iter'
,
for
user
in
users
:
tags
=
[
u'action:{}'
.
format
(
course_data
.
course_key
)]
with
dog_stats_api
.
timer
(
'lms.grades.CourseGradeFactory.iter'
,
tags
=
stats_tags
):
):
yield
self
.
_iter_grade_result
(
user
,
course_data
,
force_update
)
try
:
method
=
CourseGradeFactory
()
.
update
if
force_update
else
CourseGradeFactory
()
.
create
def
_iter_grade_result
(
self
,
user
,
course_data
,
force_update
):
course_grade
=
method
(
try
:
user
,
course_data
.
course
,
course_data
.
collected_structure
,
course_key
=
course_key
,
method
=
CourseGradeFactory
()
.
update
if
force_update
else
CourseGradeFactory
()
.
create
)
course_grade
=
method
(
yield
self
.
GradeResult
(
user
,
course_grade
,
None
)
user
,
course_data
.
course
,
course_data
.
collected_structure
,
course_key
=
course_data
.
course_key
,
)
except
Exception
as
exc
:
# pylint: disable=broad-except
return
self
.
GradeResult
(
user
,
course_grade
,
None
)
# Keep marching on even if this student couldn't be graded for
except
Exception
as
exc
:
# pylint: disable=broad-except
# some reason, but log it for future reference.
# Keep marching on even if this student couldn't be graded for
log
.
exception
(
# some reason, but log it for future reference.
'Cannot grade student
%
s in course
%
s because of exception:
%
s'
,
log
.
exception
(
user
.
id
,
'Cannot grade student
%
s in course
%
s because of exception:
%
s'
,
course_data
.
course_key
,
user
.
id
,
exc
.
message
course_data
.
course_key
,
)
exc
.
message
yield
self
.
GradeResult
(
user
,
None
,
exc
)
)
return
self
.
GradeResult
(
user
,
None
,
exc
)
@staticmethod
@staticmethod
def
_create_zero
(
user
,
course_data
):
def
_create_zero
(
user
,
course_data
):
...
...
lms/djangoapps/grades/tests/test_tasks.py
View file @
86eca26a
...
@@ -407,17 +407,6 @@ class ComputeGradesForCourseTest(HasCourseWithProblemsMixin, ModuleStoreTestCase
...
@@ -407,17 +407,6 @@ class ComputeGradesForCourseTest(HasCourseWithProblemsMixin, ModuleStoreTestCase
)
)
@ddt.data
(
*
xrange
(
1
,
12
,
3
))
@ddt.data
(
*
xrange
(
1
,
12
,
3
))
def
test_database_calls
(
self
,
batch_size
):
per_user_queries
=
15
*
min
(
batch_size
,
6
)
# No more than 6 due to offset
with
self
.
assertNumQueries
(
6
+
per_user_queries
):
with
check_mongo_calls
(
1
):
compute_grades_for_course_v2
.
delay
(
course_key
=
six
.
text_type
(
self
.
course
.
id
),
batch_size
=
batch_size
,
offset
=
6
,
)
@ddt.data
(
*
xrange
(
1
,
12
,
3
))
def
test_compute_all_grades_for_course
(
self
,
batch_size
):
def
test_compute_all_grades_for_course
(
self
,
batch_size
):
self
.
set_up_course
()
self
.
set_up_course
()
result
=
compute_all_grades_for_course
.
delay
(
result
=
compute_all_grades_for_course
.
delay
(
...
...
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