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
993c24b7
Commit
993c24b7
authored
Jan 08, 2013
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
WIP: Model data caching work
parent
507d2021
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
427 additions
and
313 deletions
+427
-313
common/lib/xmodule/xmodule/course_module.py
+1
-1
lms/djangoapps/courseware/courses.py
+30
-8
lms/djangoapps/courseware/grades.py
+57
-41
lms/djangoapps/courseware/management/commands/check_course.py
+2
-2
lms/djangoapps/courseware/model_data.py
+257
-79
lms/djangoapps/courseware/models.py
+0
-103
lms/djangoapps/courseware/module_render.py
+22
-27
lms/djangoapps/courseware/tests/test_model_data.py
+36
-27
lms/djangoapps/courseware/tests/tests.py
+5
-5
lms/djangoapps/courseware/views.py
+13
-16
lms/templates/courseware/info.html
+4
-4
No files found.
common/lib/xmodule/xmodule/course_module.py
View file @
993c24b7
...
@@ -358,7 +358,7 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -358,7 +358,7 @@ class CourseDescriptor(SequenceDescriptor):
all_descriptors - This contains a list of all xmodules that can
all_descriptors - This contains a list of all xmodules that can
effect grading a student. This is used to efficiently fetch
effect grading a student. This is used to efficiently fetch
all the xmodule state for a
StudentModule
Cache without walking
all the xmodule state for a
ModelData
Cache without walking
the descriptor tree again.
the descriptor tree again.
...
...
lms/djangoapps/courseware/courses.py
View file @
993c24b7
...
@@ -19,10 +19,10 @@ from xmodule.contentstore.content import StaticContent
...
@@ -19,10 +19,10 @@ from xmodule.contentstore.content import StaticContent
from
xmodule.modulestore.xml
import
XMLModuleStore
from
xmodule.modulestore.xml
import
XMLModuleStore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.x_module
import
XModule
from
xmodule.x_module
import
XModule
from
courseware.model_data
import
ModelDataCache
from
static_replace
import
replace_urls
,
try_staticfiles_lookup
from
static_replace
import
replace_urls
,
try_staticfiles_lookup
from
courseware.access
import
has_access
from
courseware.access
import
has_access
import
branding
import
branding
from
courseware.models
import
StudentModuleCache
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -148,12 +148,23 @@ def get_course_about_section(course, section_key):
...
@@ -148,12 +148,23 @@ def get_course_about_section(course, section_key):
request
=
get_request_for_thread
()
request
=
get_request_for_thread
()
loc
=
course
.
location
.
_replace
(
category
=
'about'
,
name
=
section_key
)
loc
=
course
.
location
.
_replace
(
category
=
'about'
,
name
=
section_key
)
course_module
=
get_module
(
request
.
user
,
request
,
loc
,
None
,
course
.
id
,
not_found_ok
=
True
,
wrap_xmodule_display
=
True
)
# Use an empty cache
model_data_cache
=
ModelDataCache
([],
course
.
id
,
request
.
user
)
about_module
=
get_module
(
request
.
user
,
request
,
loc
,
model_data_cache
,
course
.
id
,
not_found_ok
=
True
,
wrap_xmodule_display
=
True
)
html
=
''
html
=
''
if
course
_module
is
not
None
:
if
about
_module
is
not
None
:
html
=
course
_module
.
get_html
()
html
=
about
_module
.
get_html
()
return
html
return
html
...
@@ -174,7 +185,7 @@ def get_course_about_section(course, section_key):
...
@@ -174,7 +185,7 @@ def get_course_about_section(course, section_key):
def
get_course_info_section
(
request
,
c
ache
,
c
ourse
,
section_key
):
def
get_course_info_section
(
request
,
course
,
section_key
):
"""
"""
This returns the snippet of html to be rendered on the course info page,
This returns the snippet of html to be rendered on the course info page,
given the key for the section.
given the key for the section.
...
@@ -188,11 +199,22 @@ def get_course_info_section(request, cache, course, section_key):
...
@@ -188,11 +199,22 @@ def get_course_info_section(request, cache, course, section_key):
loc
=
Location
(
course
.
location
.
tag
,
course
.
location
.
org
,
course
.
location
.
course
,
'course_info'
,
section_key
)
loc
=
Location
(
course
.
location
.
tag
,
course
.
location
.
org
,
course
.
location
.
course
,
'course_info'
,
section_key
)
course_module
=
get_module
(
request
.
user
,
request
,
loc
,
cache
,
course
.
id
,
wrap_xmodule_display
=
True
)
# Use an empty cache
model_data_cache
=
ModelDataCache
([],
course
.
id
,
request
.
user
)
info_module
=
get_module
(
request
.
user
,
request
,
loc
,
model_data_cache
,
course
.
id
,
wrap_xmodule_display
=
True
)
html
=
''
html
=
''
if
course
_module
is
not
None
:
if
info
_module
is
not
None
:
html
=
course
_module
.
get_html
()
html
=
info
_module
.
get_html
()
return
html
return
html
...
...
lms/djangoapps/courseware/grades.py
View file @
993c24b7
...
@@ -8,7 +8,8 @@ from collections import defaultdict
...
@@ -8,7 +8,8 @@ from collections import defaultdict
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
models
import
StudentModuleCache
from
.model_data
import
ModelDataCache
,
LmsKeyValueStore
from
xblock.core
import
Scope
from
module_render
import
get_module
from
module_render
import
get_module
from
xmodule
import
graders
from
xmodule
import
graders
from
xmodule.capa_module
import
CapaModule
from
xmodule.capa_module
import
CapaModule
...
@@ -57,37 +58,39 @@ def yield_problems(request, course, student):
...
@@ -57,37 +58,39 @@ def yield_problems(request, course, student):
the list, but there may be others as well).
the list, but there may be others as well).
"""
"""
grading_context
=
course
.
grading_context
grading_context
=
course
.
grading_context
student_module_cache
=
StudentModuleCache
(
course
.
id
,
student
,
grading_context
[
'all_descriptors'
])
descriptor_locations
=
(
descriptor
.
location
.
url
()
for
descriptor
in
grading_context
[
'all_descriptors'
])
existing_student_modules
=
set
(
StudentModule
.
objects
.
filter
(
module_state_key__in
=
descriptor_locations
)
.
values_list
(
'module_state_key'
,
flat
=
True
))
sections_to_list
=
[]
for
section_format
,
sections
in
grading_context
[
'graded_sections'
]
.
iteritems
():
for
section_format
,
sections
in
grading_context
[
'graded_sections'
]
.
iteritems
():
for
section
in
sections
:
for
section
in
sections
:
section_descriptor
=
section
[
'section_descriptor'
]
section_descriptor
=
section
[
'section_descriptor'
]
# If the student hasn't seen a single problem in the section, skip it.
# If the student hasn't seen a single problem in the section, skip it.
skip
=
True
for
moduledescriptor
in
section
[
'xmoduledescriptors'
]:
for
moduledescriptor
in
section
[
'xmoduledescriptors'
]:
if
student_module_cache
.
lookup
(
if
moduledescriptor
.
location
.
url
()
in
existing_student_modules
:
course
.
id
,
moduledescriptor
.
category
,
moduledescriptor
.
location
.
url
()):
sections_to_list
.
append
(
section_descriptor
)
skip
=
False
break
break
if
skip
:
model_data_cache
=
ModelDataCache
(
sections_to_list
,
course
.
id
,
student
)
continue
for
section_descriptor
in
sections_to_list
:
section_module
=
get_module
(
student
,
request
,
section_module
=
get_module
(
student
,
request
,
section_descriptor
.
location
,
model_data_cache
,
section_descriptor
.
location
,
student_module_cache
,
course
.
id
)
course
.
id
)
if
section_module
is
None
:
if
section_module
is
None
:
# student doesn't have access to this module, or something else
# student doesn't have access to this module, or something else
# went wrong.
# went wrong.
# log.debug("couldn't get module for student {0} for section location {1}"
# log.debug("couldn't get module for student {0} for section location {1}"
# .format(student.username, section_descriptor.location))
# .format(student.username, section_descriptor.location))
continue
continue
for
problem
in
yield_module_descendents
(
section_module
):
for
problem
in
yield_module_descendents
(
section_module
):
if
isinstance
(
problem
,
CapaModule
):
if
isinstance
(
problem
,
CapaModule
):
yield
problem
yield
problem
def
answer_distributions
(
request
,
course
):
def
answer_distributions
(
request
,
course
):
"""
"""
...
@@ -116,7 +119,7 @@ def answer_distributions(request, course):
...
@@ -116,7 +119,7 @@ def answer_distributions(request, course):
return
counts
return
counts
def
grade
(
student
,
request
,
course
,
student_module
_cache
=
None
,
keep_raw_scores
=
False
):
def
grade
(
student
,
request
,
course
,
model_data
_cache
=
None
,
keep_raw_scores
=
False
):
"""
"""
This grades a student as quickly as possible. It retuns the
This grades a student as quickly as possible. It retuns the
output from the course grader, augmented with the final letter
output from the course grader, augmented with the final letter
...
@@ -138,8 +141,8 @@ def grade(student, request, course, student_module_cache=None, keep_raw_scores=F
...
@@ -138,8 +141,8 @@ def grade(student, request, course, student_module_cache=None, keep_raw_scores=F
grading_context
=
course
.
grading_context
grading_context
=
course
.
grading_context
raw_scores
=
[]
raw_scores
=
[]
if
student_module
_cache
==
None
:
if
model_data
_cache
==
None
:
student_module_cache
=
StudentModuleCache
(
course
.
id
,
student
,
grading_context
[
'all_descriptors'
]
)
model_data_cache
=
ModelDataCache
(
grading_context
[
'all_descriptors'
],
course
.
id
,
student
)
totaled_scores
=
{}
totaled_scores
=
{}
# This next complicated loop is just to collect the totaled_scores, which is
# This next complicated loop is just to collect the totaled_scores, which is
...
@@ -153,8 +156,14 @@ def grade(student, request, course, student_module_cache=None, keep_raw_scores=F
...
@@ -153,8 +156,14 @@ def grade(student, request, course, student_module_cache=None, keep_raw_scores=F
should_grade_section
=
False
should_grade_section
=
False
# If we haven't seen a single problem in the section, we don't have to grade it at all! We can assume 0%
# If we haven't seen a single problem in the section, we don't have to grade it at all! We can assume 0%
for
moduledescriptor
in
section
[
'xmoduledescriptors'
]:
for
moduledescriptor
in
section
[
'xmoduledescriptors'
]:
if
student_module_cache
.
lookup
(
# Create a fake key to pull out a StudentModule object from the ModelDataCache
course
.
id
,
moduledescriptor
.
category
,
moduledescriptor
.
location
.
url
()):
key
=
LmsKeyValueStore
.
Key
(
Scope
.
student_state
,
student
.
id
,
moduledescriptor
.
location
,
None
)
if
model_data_cache
.
find
(
key
):
should_grade_section
=
True
should_grade_section
=
True
break
break
...
@@ -165,11 +174,11 @@ def grade(student, request, course, student_module_cache=None, keep_raw_scores=F
...
@@ -165,11 +174,11 @@ def grade(student, request, course, student_module_cache=None, keep_raw_scores=F
# TODO: We need the request to pass into here. If we could forgo that, our arguments
# TODO: We need the request to pass into here. If we could forgo that, our arguments
# would be simpler
# would be simpler
return
get_module
(
student
,
request
,
descriptor
.
location
,
return
get_module
(
student
,
request
,
descriptor
.
location
,
student_module
_cache
,
course
.
id
)
model_data
_cache
,
course
.
id
)
for
module_descriptor
in
yield_dynamic_descriptor_descendents
(
section_descriptor
,
create_module
):
for
module_descriptor
in
yield_dynamic_descriptor_descendents
(
section_descriptor
,
create_module
):
(
correct
,
total
)
=
get_score
(
course
.
id
,
student
,
module_descriptor
,
create_module
,
student_module
_cache
)
(
correct
,
total
)
=
get_score
(
course
.
id
,
student
,
module_descriptor
,
create_module
,
model_data
_cache
)
if
correct
is
None
and
total
is
None
:
if
correct
is
None
and
total
is
None
:
continue
continue
...
@@ -240,7 +249,7 @@ def grade_for_percentage(grade_cutoffs, percentage):
...
@@ -240,7 +249,7 @@ def grade_for_percentage(grade_cutoffs, percentage):
# TODO: This method is not very good. It was written in the old course style and
# TODO: This method is not very good. It was written in the old course style and
# then converted over and performance is not good. Once the progress page is redesigned
# then converted over and performance is not good. Once the progress page is redesigned
# to not have the progress summary this method should be deleted (so it won't be copied).
# to not have the progress summary this method should be deleted (so it won't be copied).
def
progress_summary
(
student
,
request
,
course
,
student_module
_cache
):
def
progress_summary
(
student
,
request
,
course
,
model_data
_cache
):
"""
"""
This pulls a summary of all problems in the course.
This pulls a summary of all problems in the course.
...
@@ -254,7 +263,7 @@ def progress_summary(student, request, course, student_module_cache):
...
@@ -254,7 +263,7 @@ def progress_summary(student, request, course, student_module_cache):
Arguments:
Arguments:
student: A User object for the student to grade
student: A User object for the student to grade
course: A Descriptor containing the course to grade
course: A Descriptor containing the course to grade
student_module_cache: A StudentModule
Cache initialized with all
model_data_cache: A ModelData
Cache initialized with all
instance_modules for the student
instance_modules for the student
If the student does not have access to load the course module, this function
If the student does not have access to load the course module, this function
...
@@ -266,7 +275,7 @@ def progress_summary(student, request, course, student_module_cache):
...
@@ -266,7 +275,7 @@ def progress_summary(student, request, course, student_module_cache):
# TODO: We need the request to pass into here. If we could forgo that, our arguments
# TODO: We need the request to pass into here. If we could forgo that, our arguments
# would be simpler
# would be simpler
course_module
=
get_module
(
student
,
request
,
course_module
=
get_module
(
student
,
request
,
course
.
location
,
student_module
_cache
,
course
.
location
,
model_data
_cache
,
course
.
id
)
course
.
id
)
if
not
course_module
:
if
not
course_module
:
# This student must not have access to the course.
# This student must not have access to the course.
...
@@ -294,7 +303,7 @@ def progress_summary(student, request, course, student_module_cache):
...
@@ -294,7 +303,7 @@ def progress_summary(student, request, course, student_module_cache):
for
module_descriptor
in
yield_dynamic_descriptor_descendents
(
section_module
.
descriptor
,
module_creator
):
for
module_descriptor
in
yield_dynamic_descriptor_descendents
(
section_module
.
descriptor
,
module_creator
):
course_id
=
course
.
id
course_id
=
course
.
id
(
correct
,
total
)
=
get_score
(
course_id
,
student
,
module_descriptor
,
module_creator
,
student_module
_cache
)
(
correct
,
total
)
=
get_score
(
course_id
,
student
,
module_descriptor
,
module_creator
,
model_data
_cache
)
if
correct
is
None
and
total
is
None
:
if
correct
is
None
and
total
is
None
:
continue
continue
...
@@ -324,7 +333,7 @@ def progress_summary(student, request, course, student_module_cache):
...
@@ -324,7 +333,7 @@ def progress_summary(student, request, course, student_module_cache):
return
chapters
return
chapters
def
get_score
(
course_id
,
user
,
problem_descriptor
,
module_creator
,
student_module
_cache
):
def
get_score
(
course_id
,
user
,
problem_descriptor
,
module_creator
,
model_data
_cache
):
"""
"""
Return the score for a user on a problem, as a tuple (correct, total).
Return the score for a user on a problem, as a tuple (correct, total).
e.g. (5,7) if you got 5 out of 7 points.
e.g. (5,7) if you got 5 out of 7 points.
...
@@ -336,7 +345,7 @@ def get_score(course_id, user, problem_descriptor, module_creator, student_modul
...
@@ -336,7 +345,7 @@ def get_score(course_id, user, problem_descriptor, module_creator, student_modul
problem_descriptor: an XModuleDescriptor
problem_descriptor: an XModuleDescriptor
module_creator: a function that takes a descriptor, and returns the corresponding XModule for this user.
module_creator: a function that takes a descriptor, and returns the corresponding XModule for this user.
Can return None if user doesn't have access, or if something else went wrong.
Can return None if user doesn't have access, or if something else went wrong.
cache: A
StudentModule
Cache
cache: A
ModelData
Cache
"""
"""
if
not
user
.
is_authenticated
():
if
not
user
.
is_authenticated
():
return
(
None
,
None
)
return
(
None
,
None
)
...
@@ -345,16 +354,23 @@ def get_score(course_id, user, problem_descriptor, module_creator, student_modul
...
@@ -345,16 +354,23 @@ def get_score(course_id, user, problem_descriptor, module_creator, student_modul
# These are not problems, and do not have a score
# These are not problems, and do not have a score
return
(
None
,
None
)
return
(
None
,
None
)
instance_module
=
student_module_cache
.
lookup
(
# Create a fake KeyValueStore key to pull out the StudentModule
course_id
,
problem_descriptor
.
category
,
problem_descriptor
.
location
.
url
())
key
=
LmsKeyValueStore
.
Key
(
Scope
.
student_state
,
user
.
id
,
problem_descriptor
.
location
,
None
)
student_module
=
model_data_cache
.
find
(
key
)
if
instance_module
is
not
None
and
instance
_module
.
max_grade
is
not
None
:
if
student_module
is
not
None
and
student
_module
.
max_grade
is
not
None
:
correct
=
instance_module
.
grade
if
instance
_module
.
grade
is
not
None
else
0
correct
=
student_module
.
grade
if
student
_module
.
grade
is
not
None
else
0
total
=
instance
_module
.
max_grade
total
=
student
_module
.
max_grade
else
:
else
:
# If the problem was not in the cache, or hasn't been graded yet,
# If the problem was not in the cache, or hasn't been graded yet,
# we need to instantiate the problem.
# we need to instantiate the problem.
# Otherwise, the max score (cached in
instance
_module) won't be available
# Otherwise, the max score (cached in
student
_module) won't be available
problem
=
module_creator
(
problem_descriptor
)
problem
=
module_creator
(
problem_descriptor
)
if
problem
is
None
:
if
problem
is
None
:
return
(
None
,
None
)
return
(
None
,
None
)
...
@@ -371,7 +387,7 @@ def get_score(course_id, user, problem_descriptor, module_creator, student_modul
...
@@ -371,7 +387,7 @@ def get_score(course_id, user, problem_descriptor, module_creator, student_modul
weight
=
getattr
(
problem_descriptor
,
'weight'
,
None
)
weight
=
getattr
(
problem_descriptor
,
'weight'
,
None
)
if
weight
is
not
None
:
if
weight
is
not
None
:
if
total
==
0
:
if
total
==
0
:
log
.
exception
(
"Cannot reweight a problem with zero total points. Problem: "
+
str
(
instance
_module
))
log
.
exception
(
"Cannot reweight a problem with zero total points. Problem: "
+
str
(
student
_module
))
return
(
correct
,
total
)
return
(
correct
,
total
)
correct
=
correct
*
weight
/
total
correct
=
correct
*
weight
/
total
total
=
weight
total
=
weight
...
...
lms/djangoapps/courseware/management/commands/check_course.py
View file @
993c24b7
...
@@ -13,7 +13,7 @@ import xmodule
...
@@ -13,7 +13,7 @@ import xmodule
import
mitxmako.middleware
as
middleware
import
mitxmako.middleware
as
middleware
middleware
.
MakoMiddleware
()
middleware
.
MakoMiddleware
()
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
courseware.model
s
import
StudentModule
Cache
from
courseware.model
_data
import
ModelData
Cache
from
courseware.module_render
import
get_module
from
courseware.module_render
import
get_module
...
@@ -83,7 +83,7 @@ class Command(BaseCommand):
...
@@ -83,7 +83,7 @@ class Command(BaseCommand):
# TODO (cpennington): Get coursename in a legitimate way
# TODO (cpennington): Get coursename in a legitimate way
course_location = 'i4x://edx/6002xs12/course/6.002_Spring_2012'
course_location = 'i4x://edx/6002xs12/course/6.002_Spring_2012'
student_module_cache =
StudentModule
Cache.cache_for_descriptor_descendents(
student_module_cache =
ModelData
Cache.cache_for_descriptor_descendents(
course_id,
course_id,
sample_user, modulestore().get_item(course_location))
sample_user, modulestore().get_item(course_location))
course = get_module(sample_user, None, course_location, student_module_cache)
course = get_module(sample_user, None, course_location, student_module_cache)
...
...
lms/djangoapps/courseware/model_data.py
View file @
993c24b7
import
json
import
json
from
collections
import
namedtuple
from
collections
import
namedtuple
,
defaultdict
from
itertools
import
chain
from
.models
import
(
from
.models
import
(
StudentModule
,
StudentModule
,
XModuleContentField
,
XModuleContentField
,
...
@@ -16,6 +17,235 @@ class InvalidWriteError(Exception):
...
@@ -16,6 +17,235 @@ class InvalidWriteError(Exception):
pass
pass
def
chunks
(
items
,
chunk_size
):
items
=
list
(
items
)
return
(
items
[
i
:
i
+
chunk_size
]
for
i
in
xrange
(
0
,
len
(
items
),
chunk_size
))
class
ModelDataCache
(
object
):
"""
A cache of django model objects needed to supply the data
for a module and its decendents
"""
def
__init__
(
self
,
descriptors
,
course_id
,
user
,
select_for_update
=
False
):
'''
Find any courseware.models objects that are needed by any descriptor
in descriptors. Attempts to minimize the number of queries to the database.
Note: Only modules that have store_state = True or have shared
state will have a StudentModule.
Arguments
descriptors: An array of XModuleDescriptors.
course_id: The id of the current course
user: The user for which to cache data
select_for_update: Flag indicating whether the rows should be locked until end of transaction
'''
self
.
cache
=
{}
self
.
descriptors
=
descriptors
self
.
select_for_update
=
select_for_update
self
.
course_id
=
course_id
self
.
user
=
user
if
user
.
is_authenticated
():
for
scope
,
fields
in
self
.
_fields_to_cache
()
.
items
():
for
field_object
in
self
.
_retrieve_fields
(
scope
,
fields
):
self
.
cache
[
self
.
_cache_key_from_field_object
(
scope
,
field_object
)]
=
field_object
@classmethod
def
cache_for_descriptor_descendents
(
cls
,
course_id
,
user
,
descriptor
,
depth
=
None
,
descriptor_filter
=
lambda
descriptor
:
True
,
select_for_update
=
False
):
"""
course_id: the course in the context of which we want StudentModules.
user: the django user for whom to load modules.
descriptor: An XModuleDescriptor
depth is the number of levels of descendent modules to load StudentModules for, in addition to
the supplied descriptor. If depth is None, load all descendent StudentModules
descriptor_filter is a function that accepts a descriptor and return wether the StudentModule
should be cached
select_for_update: Flag indicating whether the rows should be locked until end of transaction
"""
def
get_child_descriptors
(
descriptor
,
depth
,
descriptor_filter
):
if
descriptor_filter
(
descriptor
):
descriptors
=
[
descriptor
]
else
:
descriptors
=
[]
if
depth
is
None
or
depth
>
0
:
new_depth
=
depth
-
1
if
depth
is
not
None
else
depth
for
child
in
descriptor
.
get_children
():
descriptors
.
extend
(
get_child_descriptors
(
child
,
new_depth
,
descriptor_filter
))
return
descriptors
descriptors
=
get_child_descriptors
(
descriptor
,
depth
,
descriptor_filter
)
return
ModelDataCache
(
descriptors
,
course_id
,
user
,
select_for_update
)
def
_query
(
self
,
model_class
,
**
kwargs
):
"""
Queries model_class with **kwargs, optionally adding select_for_update if
self.select_for_update is set
"""
query
=
model_class
.
objects
if
self
.
select_for_update
:
query
=
query
.
select_for_update
()
query
=
query
.
filter
(
**
kwargs
)
return
query
def
_chunked_query
(
self
,
model_class
,
chunk_field
,
items
,
chunk_size
=
500
,
**
kwargs
):
"""
Queries model_class with `chunk_field` set to chunks of size `chunk_size`,
and all other parameters from `**kwargs`
This works around a limitation in sqlite3 on the number of parameters
that can be put into a single query
"""
res
=
chain
.
from_iterable
(
self
.
_query
(
model_class
,
**
dict
([(
chunk_field
,
chunk
)]
+
kwargs
.
items
()))
for
chunk
in
chunks
(
items
,
chunk_size
)
)
return
res
def
_retrieve_fields
(
self
,
scope
,
fields
):
"""
Queries the database for all of the fields in the specified scope
"""
if
scope
in
(
Scope
.
children
,
Scope
.
parent
):
return
[]
elif
scope
==
Scope
.
student_state
:
return
self
.
_chunked_query
(
StudentModule
,
'module_state_key__in'
,
(
descriptor
.
location
.
url
()
for
descriptor
in
self
.
descriptors
),
course_id
=
self
.
course_id
,
student
=
self
.
user
,
)
elif
scope
==
Scope
.
content
:
return
self
.
_chunked_query
(
XModuleContentField
,
'definition_id__in'
,
(
descriptor
.
location
.
url
()
for
descriptor
in
self
.
descriptors
),
field_name__in
=
set
(
field
.
name
for
field
in
fields
),
)
elif
scope
==
Scope
.
settings
:
return
self
.
_chunked_query
(
XModuleSettingsField
,
'usage_id__in'
,
(
'
%
s-
%
s'
%
(
self
.
course_id
,
descriptor
.
location
.
url
())
for
descriptor
in
self
.
descriptors
),
field_name__in
=
set
(
field
.
name
for
field
in
fields
),
)
elif
scope
==
Scope
.
student_preferences
:
return
self
.
_chunked_query
(
XModuleStudentPrefsField
,
'module_type__in'
,
set
(
descriptor
.
location
.
category
for
descriptor
in
self
.
descriptors
),
student
=
self
.
user
,
field_name__in
=
set
(
field
.
name
for
field
in
fields
),
)
elif
scope
==
Scope
.
student_info
:
self
.
_query
(
XModuleStudentInfoField
,
student
=
self
.
user
,
field_name__in
=
set
(
field
.
name
for
field
in
fields
),
)
else
:
raise
InvalidScopeError
(
scope
)
def
_fields_to_cache
(
self
):
"""
Returns a map of scopes to fields in that scope that should be cached
"""
scope_map
=
defaultdict
(
set
)
for
descriptor
in
self
.
descriptors
:
for
field
in
(
descriptor
.
module_class
.
fields
+
descriptor
.
module_class
.
lms
.
fields
):
scope_map
[
field
.
scope
]
.
add
(
field
)
return
scope_map
def
_cache_key_from_kvs_key
(
self
,
key
):
if
key
.
scope
==
Scope
.
student_state
:
return
(
key
.
scope
,
key
.
block_scope_id
.
url
())
elif
key
.
scope
==
Scope
.
content
:
return
(
key
.
scope
,
key
.
block_scope_id
.
url
(),
key
.
field_name
)
elif
key
.
scope
==
Scope
.
settings
:
return
(
key
.
scope
,
'
%
s-
%
s'
%
(
self
.
course_id
,
key
.
block_scope_id
.
url
()),
key
.
field_name
)
elif
key
.
scope
==
Scope
.
student_preferences
:
return
(
key
.
scope
,
key
.
block_scope_id
,
key
.
field_name
)
elif
key
.
scope
==
Scope
.
student_info
:
return
(
key
.
scope
,
key
.
field_name
)
def
_cache_key_from_field_object
(
self
,
scope
,
field_object
):
if
scope
==
Scope
.
student_state
:
return
(
scope
,
field_object
.
module_state_key
)
elif
scope
==
Scope
.
content
:
return
(
scope
,
field_object
.
definition_id
,
field_object
.
field_name
)
elif
scope
==
Scope
.
settings
:
return
(
scope
,
field_object
.
usage_id
,
field_object
.
field_name
)
elif
scope
==
Scope
.
student_preferences
:
return
(
scope
,
field_object
.
module_type
,
field_object
.
field_name
)
elif
scope
==
Scope
.
student_info
:
return
(
scope
,
field_object
.
field_name
)
def
find
(
self
,
key
):
'''
Look for a model data object using an LmsKeyValueStore.Key object
key: An `LmsKeyValueStore.Key` object selecting the object to find
returns the found object, or None if the object doesn't exist
'''
return
self
.
cache
.
get
(
self
.
_cache_key_from_kvs_key
(
key
))
def
find_or_create
(
self
,
key
):
'''
Find a model data object in this cache, or create it if it doesn't
exist
'''
field_object
=
self
.
find
(
key
)
if
field_object
is
not
None
:
return
field_object
if
key
.
scope
==
Scope
.
student_state
:
field_object
,
_
=
StudentModule
.
objects
.
get_or_create
(
course_id
=
self
.
course_id
,
student
=
self
.
user
,
module_type
=
key
.
block_scope_id
.
category
,
module_state_key
=
key
.
block_scope_id
.
url
(),
defaults
=
{
'state'
:
json
.
dumps
({})},
)
elif
key
.
scope
==
Scope
.
content
:
field_object
,
_
=
XModuleContentField
.
objects
.
get_or_create
(
field_name
=
key
.
field_name
,
definition_id
=
key
.
block_scope_id
.
url
()
)
elif
key
.
scope
==
Scope
.
settings
:
field_object
,
_
=
XModuleSettingsField
.
objects
.
get_or_create
(
field_name
=
key
.
field_name
,
usage_id
=
'
%
s-
%
s'
%
(
self
.
course_id
,
key
.
block_scope_id
.
url
()),
)
elif
key
.
scope
==
Scope
.
student_preferences
:
field_object
,
_
=
XModuleStudentPrefsField
.
objects
.
get_or_create
(
field_name
=
key
.
field_name
,
module_type
=
key
.
block_scope_id
,
student
=
self
.
user
,
)
elif
key
.
scope
==
Scope
.
student_info
:
field_object
,
_
=
XModuleStudentInfoField
.
objects
.
get_or_create
(
field_name
=
key
.
field_name
,
student
=
self
.
user
,
)
cache_key
=
self
.
_cache_key_from_kvs_key
(
key
)
self
.
cache
[
cache_key
]
=
field_object
return
field_object
class
LmsKeyValueStore
(
KeyValueStore
):
class
LmsKeyValueStore
(
KeyValueStore
):
"""
"""
This KeyValueStore will read data from descriptor_model_data if it exists,
This KeyValueStore will read data from descriptor_model_data if it exists,
...
@@ -37,32 +267,9 @@ class LmsKeyValueStore(KeyValueStore):
...
@@ -37,32 +267,9 @@ class LmsKeyValueStore(KeyValueStore):
If the key isn't found in the expected table during a read or a delete, then a KeyError will be raised
If the key isn't found in the expected table during a read or a delete, then a KeyError will be raised
"""
"""
def
__init__
(
self
,
course_id
,
user
,
descriptor_model_data
,
student_module_cache
):
def
__init__
(
self
,
descriptor_model_data
,
model_data_cache
):
self
.
_course_id
=
course_id
self
.
_user
=
user
self
.
_descriptor_model_data
=
descriptor_model_data
self
.
_descriptor_model_data
=
descriptor_model_data
self
.
_student_module_cache
=
student_module_cache
self
.
_model_data_cache
=
model_data_cache
def
_student_module
(
self
,
key
):
student_module
=
self
.
_student_module_cache
.
lookup
(
self
.
_course_id
,
key
.
block_scope_id
.
category
,
key
.
block_scope_id
.
url
()
)
return
student_module
def
_field_object
(
self
,
key
):
if
key
.
scope
==
Scope
.
content
:
return
XModuleContentField
,
{
'field_name'
:
key
.
field_name
,
'definition_id'
:
key
.
block_scope_id
}
elif
key
.
scope
==
Scope
.
settings
:
return
XModuleSettingsField
,
{
'field_name'
:
key
.
field_name
,
'usage_id'
:
'
%
s-
%
s'
%
(
self
.
_course_id
,
key
.
block_scope_id
)
}
elif
key
.
scope
==
Scope
.
student_preferences
:
return
XModuleStudentPrefsField
,
{
'field_name'
:
key
.
field_name
,
'student'
:
self
.
_user
,
'module_type'
:
key
.
block_scope_id
}
elif
key
.
scope
==
Scope
.
student_info
:
return
XModuleStudentInfoField
,
{
'field_name'
:
key
.
field_name
,
'student'
:
self
.
_user
}
raise
InvalidScopeError
(
key
.
scope
)
def
get
(
self
,
key
):
def
get
(
self
,
key
):
if
key
.
field_name
in
self
.
_descriptor_model_data
:
if
key
.
field_name
in
self
.
_descriptor_model_data
:
...
@@ -71,74 +278,45 @@ class LmsKeyValueStore(KeyValueStore):
...
@@ -71,74 +278,45 @@ class LmsKeyValueStore(KeyValueStore):
if
key
.
scope
==
Scope
.
parent
:
if
key
.
scope
==
Scope
.
parent
:
return
None
return
None
if
key
.
scope
==
Scope
.
student_state
:
field_object
=
self
.
_model_data_cache
.
find
(
key
)
student_module
=
self
.
_student_module
(
key
)
if
field_object
is
None
:
if
student_module
is
None
:
raise
KeyError
(
key
.
field_name
)
return
json
.
loads
(
student_module
.
state
)[
key
.
field_name
]
scope_field_cls
,
search_kwargs
=
self
.
_field_object
(
key
)
try
:
return
json
.
loads
(
scope_field_cls
.
objects
.
get
(
**
search_kwargs
)
.
value
)
except
scope_field_cls
.
DoesNotExist
:
raise
KeyError
(
key
.
field_name
)
raise
KeyError
(
key
.
field_name
)
if
key
.
scope
==
Scope
.
student_state
:
return
json
.
loads
(
field_object
.
state
)[
key
.
field_name
]
else
:
return
json
.
loads
(
field_object
.
value
)
def
set
(
self
,
key
,
value
):
def
set
(
self
,
key
,
value
):
if
key
.
field_name
in
self
.
_descriptor_model_data
:
if
key
.
field_name
in
self
.
_descriptor_model_data
:
raise
InvalidWriteError
(
"Not allowed to overwrite descriptor model data"
,
key
.
field_name
)
raise
InvalidWriteError
(
"Not allowed to overwrite descriptor model data"
,
key
.
field_name
)
field_object
=
self
.
_model_data_cache
.
find_or_create
(
key
)
if
key
.
scope
==
Scope
.
student_state
:
if
key
.
scope
==
Scope
.
student_state
:
student_module
=
self
.
_student_module
(
key
)
state
=
json
.
loads
(
field_object
.
state
)
if
student_module
is
None
:
student_module
=
StudentModule
(
course_id
=
self
.
_course_id
,
student
=
self
.
_user
,
module_type
=
key
.
block_scope_id
.
category
,
module_state_key
=
key
.
block_scope_id
.
url
(),
state
=
json
.
dumps
({})
)
self
.
_student_module_cache
.
append
(
student_module
)
state
=
json
.
loads
(
student_module
.
state
)
state
[
key
.
field_name
]
=
value
state
[
key
.
field_name
]
=
value
student_module
.
state
=
json
.
dumps
(
state
)
field_object
.
state
=
json
.
dumps
(
state
)
student_module
.
save
()
else
:
return
field_object
.
value
=
json
.
dumps
(
value
)
scope_field_cls
,
search_kwargs
=
self
.
_field_object
(
key
)
field_object
.
save
()
json_value
=
json
.
dumps
(
value
)
field
,
created
=
scope_field_cls
.
objects
.
select_for_update
()
.
get_or_create
(
defaults
=
{
'value'
:
json_value
},
**
search_kwargs
)
if
not
created
:
field
.
value
=
json_value
field
.
save
()
def
delete
(
self
,
key
):
def
delete
(
self
,
key
):
if
key
.
field_name
in
self
.
_descriptor_model_data
:
if
key
.
field_name
in
self
.
_descriptor_model_data
:
raise
InvalidWriteError
(
"Not allowed to deleted descriptor model data"
,
key
.
field_name
)
raise
InvalidWriteError
(
"Not allowed to deleted descriptor model data"
,
key
.
field_name
)
if
key
.
scope
==
Scope
.
student_state
:
field_object
=
self
.
_model_data_cache
.
find
(
key
)
student_module
=
self
.
_student_module
(
key
)
if
field_object
is
None
:
if
student_module
is
None
:
raise
KeyError
(
key
.
field_name
)
state
=
json
.
loads
(
student_module
.
state
)
del
state
[
key
.
field_name
]
student_module
.
state
=
json
.
dumps
(
state
)
student_module
.
save
()
return
scope_field_cls
,
search_kwargs
=
self
.
_field_object
(
key
)
print
scope_field_cls
,
search_kwargs
query
=
scope_field_cls
.
objects
.
filter
(
**
search_kwargs
)
if
not
query
.
exists
():
raise
KeyError
(
key
.
field_name
)
raise
KeyError
(
key
.
field_name
)
query
.
delete
()
if
key
.
scope
==
Scope
.
student_state
:
state
=
json
.
loads
(
field_object
.
state
)
del
state
[
key
.
field_name
]
field_object
.
state
=
json
.
dumps
(
state
)
field_object
.
save
()
else
:
field_object
.
delete
()
LmsUsage
=
namedtuple
(
'LmsUsage'
,
'id, def_id'
)
LmsUsage
=
namedtuple
(
'LmsUsage'
,
'id, def_id'
)
...
...
lms/djangoapps/courseware/models.py
View file @
993c24b7
...
@@ -198,106 +198,3 @@ class XModuleStudentInfoField(models.Model):
...
@@ -198,106 +198,3 @@ class XModuleStudentInfoField(models.Model):
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
unicode
(
repr
(
self
))
return
unicode
(
repr
(
self
))
# TODO (cpennington): Remove these once the LMS switches to using XModuleDescriptors
class
StudentModuleCache
(
object
):
"""
A cache of StudentModules for a specific student
"""
def
__init__
(
self
,
course_id
,
user
,
descriptors
,
select_for_update
=
False
):
'''
Find any StudentModule objects that are needed by any descriptor
in descriptors. Avoids making multiple queries to the database.
Note: Only modules that have store_state = True or have shared
state will have a StudentModule.
Arguments
user: The user for which to fetch maching StudentModules
descriptors: An array of XModuleDescriptors.
select_for_update: Flag indicating whether the rows should be locked until end of transaction
'''
if
user
.
is_authenticated
():
module_ids
=
self
.
_get_module_state_keys
(
descriptors
)
# This works around a limitation in sqlite3 on the number of parameters
# that can be put into a single query
self
.
cache
=
[]
chunk_size
=
500
for
id_chunk
in
[
module_ids
[
i
:
i
+
chunk_size
]
for
i
in
xrange
(
0
,
len
(
module_ids
),
chunk_size
)]:
if
select_for_update
:
self
.
cache
.
extend
(
StudentModule
.
objects
.
select_for_update
()
.
filter
(
course_id
=
course_id
,
student
=
user
,
module_state_key__in
=
id_chunk
)
)
else
:
self
.
cache
.
extend
(
StudentModule
.
objects
.
filter
(
course_id
=
course_id
,
student
=
user
,
module_state_key__in
=
id_chunk
)
)
else
:
self
.
cache
=
[]
@classmethod
def
cache_for_descriptor_descendents
(
cls
,
course_id
,
user
,
descriptor
,
depth
=
None
,
descriptor_filter
=
lambda
descriptor
:
True
,
select_for_update
=
False
):
"""
course_id: the course in the context of which we want StudentModules.
user: the django user for whom to load modules.
descriptor: An XModuleDescriptor
depth is the number of levels of descendent modules to load StudentModules for, in addition to
the supplied descriptor. If depth is None, load all descendent StudentModules
descriptor_filter is a function that accepts a descriptor and return wether the StudentModule
should be cached
select_for_update: Flag indicating whether the rows should be locked until end of transaction
"""
def
get_child_descriptors
(
descriptor
,
depth
,
descriptor_filter
):
if
descriptor_filter
(
descriptor
):
descriptors
=
[
descriptor
]
else
:
descriptors
=
[]
if
depth
is
None
or
depth
>
0
:
new_depth
=
depth
-
1
if
depth
is
not
None
else
depth
for
child
in
descriptor
.
get_children
():
descriptors
.
extend
(
get_child_descriptors
(
child
,
new_depth
,
descriptor_filter
))
return
descriptors
descriptors
=
get_child_descriptors
(
descriptor
,
depth
,
descriptor_filter
)
return
StudentModuleCache
(
course_id
,
user
,
descriptors
,
select_for_update
)
def
_get_module_state_keys
(
self
,
descriptors
):
'''
Get a list of the state_keys needed for StudentModules
required for this module descriptor
descriptor_filter is a function that accepts a descriptor and return wether the StudentModule
should be cached
'''
return
[
descriptor
.
location
.
url
()
for
descriptor
in
descriptors
if
descriptor
.
stores_state
]
def
lookup
(
self
,
course_id
,
module_type
,
module_state_key
):
'''
Look for a student module with the given course_id, type, and id in the cache.
cache -- list of student modules
returns first found object, or None
'''
for
o
in
self
.
cache
:
if
(
o
.
course_id
==
course_id
and
o
.
module_type
==
module_type
and
o
.
module_state_key
==
module_state_key
):
return
o
return
None
def
append
(
self
,
student_module
):
self
.
cache
.
append
(
student_module
)
lms/djangoapps/courseware/module_render.py
View file @
993c24b7
...
@@ -17,7 +17,7 @@ from capa.xqueue_interface import XQueueInterface
...
@@ -17,7 +17,7 @@ from capa.xqueue_interface import XQueueInterface
from
capa.chem
import
chemcalc
from
capa.chem
import
chemcalc
from
courseware.access
import
has_access
from
courseware.access
import
has_access
from
mitxmako.shortcuts
import
render_to_string
from
mitxmako.shortcuts
import
render_to_string
from
models
import
StudentModule
,
StudentModuleCache
from
models
import
StudentModule
from
psychometrics.psychoanalyze
import
make_psychometrics_data_update_handler
from
psychometrics.psychoanalyze
import
make_psychometrics_data_update_handler
from
static_replace
import
replace_urls
from
static_replace
import
replace_urls
from
xmodule.errortracker
import
exc_info_to_str
from
xmodule.errortracker
import
exc_info_to_str
...
@@ -28,7 +28,7 @@ from xmodule.x_module import ModuleSystem
...
@@ -28,7 +28,7 @@ from xmodule.x_module import ModuleSystem
from
xmodule.error_module
import
ErrorDescriptor
,
NonStaffErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
,
NonStaffErrorDescriptor
from
xblock.runtime
import
DbModel
from
xblock.runtime
import
DbModel
from
xmodule_modifiers
import
replace_course_urls
,
replace_static_urls
,
add_histogram
,
wrap_xmodule
from
xmodule_modifiers
import
replace_course_urls
,
replace_static_urls
,
add_histogram
,
wrap_xmodule
from
.model_data
import
LmsKeyValueStore
,
LmsUsage
from
.model_data
import
LmsKeyValueStore
,
LmsUsage
,
ModelDataCache
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
statsd
import
statsd
from
statsd
import
statsd
...
@@ -60,7 +60,7 @@ def make_track_function(request):
...
@@ -60,7 +60,7 @@ def make_track_function(request):
return
f
return
f
def
toc_for_course
(
user
,
request
,
course
,
active_chapter
,
active_section
):
def
toc_for_course
(
user
,
request
,
course
,
active_chapter
,
active_section
,
model_data_cache
):
'''
'''
Create a table of contents from the module store
Create a table of contents from the module store
...
@@ -80,11 +80,11 @@ def toc_for_course(user, request, course, active_chapter, active_section):
...
@@ -80,11 +80,11 @@ def toc_for_course(user, request, course, active_chapter, active_section):
NOTE: assumes that if we got this far, user has access to course. Returns
NOTE: assumes that if we got this far, user has access to course. Returns
None if this is not the case.
None if this is not the case.
model_data_cache must include data from the course module and 2 levels of its descendents
'''
'''
student_module_cache
=
StudentModuleCache
.
cache_for_descriptor_descendents
(
course_module
=
get_module
(
user
,
request
,
course
.
location
,
model_data_cache
,
course
.
id
)
course
.
id
,
user
,
course
,
depth
=
2
)
course_module
=
get_module
(
user
,
request
,
course
.
location
,
student_module_cache
,
course
.
id
)
if
course_module
is
None
:
if
course_module
is
None
:
return
None
return
None
...
@@ -115,7 +115,7 @@ def toc_for_course(user, request, course, active_chapter, active_section):
...
@@ -115,7 +115,7 @@ def toc_for_course(user, request, course, active_chapter, active_section):
return
chapters
return
chapters
def
get_module
(
user
,
request
,
location
,
student_module
_cache
,
course_id
,
def
get_module
(
user
,
request
,
location
,
model_data
_cache
,
course_id
,
position
=
None
,
not_found_ok
=
False
,
wrap_xmodule_display
=
True
,
position
=
None
,
not_found_ok
=
False
,
wrap_xmodule_display
=
True
,
grade_bucket_type
=
None
):
grade_bucket_type
=
None
):
"""
"""
...
@@ -128,7 +128,7 @@ def get_module(user, request, location, student_module_cache, course_id,
...
@@ -128,7 +128,7 @@ def get_module(user, request, location, student_module_cache, course_id,
- request : current django HTTPrequest. Note: request.user isn't used for anything--all auth
- request : current django HTTPrequest. Note: request.user isn't used for anything--all auth
and such works based on user.
and such works based on user.
- location : A Location-like object identifying the module to load
- location : A Location-like object identifying the module to load
-
student_module_cache : a StudentModule
Cache
-
model_data_cache : a ModelData
Cache
- course_id : the course_id in the context of which to load module
- course_id : the course_id in the context of which to load module
- position : extra information from URL for user-specified
- position : extra information from URL for user-specified
position within module
position within module
...
@@ -138,7 +138,7 @@ def get_module(user, request, location, student_module_cache, course_id,
...
@@ -138,7 +138,7 @@ def get_module(user, request, location, student_module_cache, course_id,
if possible. If not possible, return None.
if possible. If not possible, return None.
"""
"""
try
:
try
:
return
_get_module
(
user
,
request
,
location
,
student_module
_cache
,
course_id
,
position
,
wrap_xmodule_display
)
return
_get_module
(
user
,
request
,
location
,
model_data
_cache
,
course_id
,
position
,
wrap_xmodule_display
)
except
ItemNotFoundError
:
except
ItemNotFoundError
:
if
not
not_found_ok
:
if
not
not_found_ok
:
log
.
exception
(
"Error in get_module"
)
log
.
exception
(
"Error in get_module"
)
...
@@ -149,7 +149,7 @@ def get_module(user, request, location, student_module_cache, course_id,
...
@@ -149,7 +149,7 @@ def get_module(user, request, location, student_module_cache, course_id,
return
None
return
None
def
_get_module
(
user
,
request
,
location
,
student_module
_cache
,
course_id
,
def
_get_module
(
user
,
request
,
location
,
model_data
_cache
,
course_id
,
position
=
None
,
wrap_xmodule_display
=
True
,
grade_bucket_type
=
None
):
position
=
None
,
wrap_xmodule_display
=
True
,
grade_bucket_type
=
None
):
"""
"""
Actually implement get_module. See docstring there for details.
Actually implement get_module. See docstring there for details.
...
@@ -204,11 +204,11 @@ def _get_module(user, request, location, student_module_cache, course_id,
...
@@ -204,11 +204,11 @@ def _get_module(user, request, location, student_module_cache, course_id,
Delegate to get_module. It does an access check, so may return None
Delegate to get_module. It does an access check, so may return None
"""
"""
return
get_module
(
user
,
request
,
location
,
return
get_module
(
user
,
request
,
location
,
student_module
_cache
,
course_id
,
position
)
model_data
_cache
,
course_id
,
position
)
def
xblock_model_data
(
descriptor
):
def
xblock_model_data
(
descriptor
):
return
DbModel
(
return
DbModel
(
LmsKeyValueStore
(
course_id
,
user
,
descriptor
.
_model_data
,
student_module
_cache
),
LmsKeyValueStore
(
descriptor
.
_model_data
,
model_data
_cache
),
descriptor
.
module_class
,
descriptor
.
module_class
,
user
.
id
,
user
.
id
,
LmsUsage
(
location
,
location
)
LmsUsage
(
location
,
location
)
...
@@ -218,18 +218,13 @@ def _get_module(user, request, location, student_module_cache, course_id,
...
@@ -218,18 +218,13 @@ def _get_module(user, request, location, student_module_cache, course_id,
if
event
.
get
(
'event_name'
)
!=
'grade'
:
if
event
.
get
(
'event_name'
)
!=
'grade'
:
return
return
student_module
=
student_module_cache
.
lookup
(
student_module
,
created
=
StudentModule
.
objects
.
get_or_create
(
course_id
,
descriptor
.
location
.
category
,
descriptor
.
location
.
url
()
course_id
=
course_id
,
student
=
user
,
module_type
=
descriptor
.
location
.
category
,
module_state_key
=
descriptor
.
location
.
url
(),
defaults
=
{
'state'
:
'{}'
},
)
)
if
student_module
is
None
:
student_module
=
StudentModule
(
course_id
=
course_id
,
student
=
user
,
module_type
=
descriptor
.
location
.
category
,
module_state_key
=
descriptor
.
location
.
url
(),
state
=
json
.
dumps
({})
)
student_module_cache
.
append
(
student_module
)
student_module
.
grade
=
event
.
get
(
'value'
)
student_module
.
grade
=
event
.
get
(
'value'
)
student_module
.
max_grade
=
event
.
get
(
'max_value'
)
student_module
.
max_grade
=
event
.
get
(
'max_value'
)
student_module
.
save
()
student_module
.
save
()
...
@@ -335,9 +330,9 @@ def xqueue_callback(request, course_id, userid, id, dispatch):
...
@@ -335,9 +330,9 @@ def xqueue_callback(request, course_id, userid, id, dispatch):
# Retrieve target StudentModule
# Retrieve target StudentModule
user
=
User
.
objects
.
get
(
id
=
userid
)
user
=
User
.
objects
.
get
(
id
=
userid
)
student_module_cache
=
StudentModule
Cache
.
cache_for_descriptor_descendents
(
course_id
,
model_data_cache
=
ModelData
Cache
.
cache_for_descriptor_descendents
(
course_id
,
user
,
modulestore
()
.
get_instance
(
course_id
,
id
),
depth
=
0
,
select_for_update
=
True
)
user
,
modulestore
()
.
get_instance
(
course_id
,
id
),
depth
=
0
,
select_for_update
=
True
)
instance
=
get_module
(
user
,
request
,
id
,
student_module
_cache
,
course_id
,
grade_bucket_type
=
'xqueue'
)
instance
=
get_module
(
user
,
request
,
id
,
model_data
_cache
,
course_id
,
grade_bucket_type
=
'xqueue'
)
if
instance
is
None
:
if
instance
is
None
:
log
.
debug
(
"No module {0} for user {1}--access denied?"
.
format
(
id
,
user
))
log
.
debug
(
"No module {0} for user {1}--access denied?"
.
format
(
id
,
user
))
raise
Http404
raise
Http404
...
@@ -394,10 +389,10 @@ def modx_dispatch(request, dispatch, location, course_id):
...
@@ -394,10 +389,10 @@ def modx_dispatch(request, dispatch, location, course_id):
return
HttpResponse
(
json
.
dumps
({
'success'
:
file_too_big_msg
}))
return
HttpResponse
(
json
.
dumps
({
'success'
:
file_too_big_msg
}))
p
[
fileinput_id
]
=
inputfiles
p
[
fileinput_id
]
=
inputfiles
student_module_cache
=
StudentModule
Cache
.
cache_for_descriptor_descendents
(
course_id
,
model_data_cache
=
ModelData
Cache
.
cache_for_descriptor_descendents
(
course_id
,
request
.
user
,
modulestore
()
.
get_instance
(
course_id
,
location
))
request
.
user
,
modulestore
()
.
get_instance
(
course_id
,
location
))
instance
=
get_module
(
request
.
user
,
request
,
location
,
student_module
_cache
,
course_id
,
grade_bucket_type
=
'ajax'
)
instance
=
get_module
(
request
.
user
,
request
,
location
,
model_data
_cache
,
course_id
,
grade_bucket_type
=
'ajax'
)
if
instance
is
None
:
if
instance
is
None
:
# Either permissions just changed, or someone is trying to be clever
# Either permissions just changed, or someone is trying to be clever
# and load something they shouldn't have access to.
# and load something they shouldn't have access to.
...
...
lms/djangoapps/courseware/tests/test_model_data.py
View file @
993c24b7
...
@@ -5,18 +5,26 @@ from django.contrib.auth.models import User
...
@@ -5,18 +5,26 @@ from django.contrib.auth.models import User
from
functools
import
partial
from
functools
import
partial
from
courseware.model_data
import
LmsKeyValueStore
,
InvalidWriteError
,
InvalidScopeError
from
courseware.model_data
import
LmsKeyValueStore
,
InvalidWriteError
,
InvalidScopeError
,
ModelDataCache
from
courseware.models
import
StudentModule
,
XModuleContentField
,
XModuleSettingsField
,
XModuleStudentInfoField
,
XModuleStudentPrefsField
,
StudentModuleCache
from
courseware.models
import
StudentModule
,
XModuleContentField
,
XModuleSettingsField
,
XModuleStudentInfoField
,
XModuleStudentPrefsField
from
xblock.core
import
Scope
,
BlockScope
from
xblock.core
import
Scope
,
BlockScope
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
django.test
import
TestCase
from
django.test
import
TestCase
def
mock_descriptor
():
def
mock_field
(
scope
,
name
):
field
=
Mock
()
field
.
scope
=
scope
field
.
name
=
name
return
field
def
mock_descriptor
(
fields
=
[],
lms_fields
=
[]):
descriptor
=
Mock
()
descriptor
=
Mock
()
descriptor
.
stores_state
=
True
descriptor
.
stores_state
=
True
descriptor
.
location
=
location
(
'def_id'
)
descriptor
.
location
=
location
(
'def_id'
)
descriptor
.
module_class
.
fields
=
fields
descriptor
.
module_class
.
lms
.
fields
=
lms_fields
return
descriptor
return
descriptor
location
=
partial
(
Location
,
'i4x'
,
'edX'
,
'test_course'
,
'problem'
)
location
=
partial
(
Location
,
'i4x'
,
'edX'
,
'test_course'
,
'problem'
)
...
@@ -85,7 +93,7 @@ class TestDescriptorFallback(TestCase):
...
@@ -85,7 +93,7 @@ class TestDescriptorFallback(TestCase):
'field_a'
:
'content'
,
'field_a'
:
'content'
,
'field_b'
:
'settings'
,
'field_b'
:
'settings'
,
}
}
self
.
kvs
=
LmsKeyValueStore
(
course_id
,
UserFactory
.
build
(),
self
.
desc_md
,
None
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
None
)
def
test_get_from_descriptor
(
self
):
def
test_get_from_descriptor
(
self
):
self
.
assertEquals
(
'content'
,
self
.
kvs
.
get
(
content_key
(
'field_a'
)))
self
.
assertEquals
(
'content'
,
self
.
kvs
.
get
(
content_key
(
'field_a'
)))
...
@@ -103,13 +111,11 @@ class TestDescriptorFallback(TestCase):
...
@@ -103,13 +111,11 @@ class TestDescriptorFallback(TestCase):
self
.
assertEquals
(
'settings'
,
self
.
desc_md
[
'field_b'
])
self
.
assertEquals
(
'settings'
,
self
.
desc_md
[
'field_b'
])
class
TestStudentStateFields
(
TestCase
):
pass
class
TestInvalidScopes
(
TestCase
):
class
TestInvalidScopes
(
TestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
self
.
desc_md
=
{}
self
.
desc_md
=
{}
self
.
kvs
=
LmsKeyValueStore
(
course_id
,
UserFactory
.
build
(),
self
.
desc_md
,
None
)
self
.
mdc
=
ModelDataCache
([
mock_descriptor
([
mock_field
(
Scope
.
student_state
,
'a_field'
)])],
course_id
,
self
.
user
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
mdc
)
def
test_invalid_scopes
(
self
):
def
test_invalid_scopes
(
self
):
for
scope
in
(
Scope
(
student
=
True
,
block
=
BlockScope
.
DEFINITION
),
for
scope
in
(
Scope
(
student
=
True
,
block
=
BlockScope
.
DEFINITION
),
...
@@ -123,12 +129,10 @@ class TestInvalidScopes(TestCase):
...
@@ -123,12 +129,10 @@ class TestInvalidScopes(TestCase):
class
TestStudentModuleStorage
(
TestCase
):
class
TestStudentModuleStorage
(
TestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
student_module
=
StudentModuleFactory
.
create
(
state
=
json
.
dumps
({
'a_field'
:
'a_value'
}))
self
.
user
=
student_module
.
student
self
.
desc_md
=
{}
self
.
desc_md
=
{}
self
.
smc
=
StudentModuleCache
(
course_id
,
self
.
user
,
[
mock_descriptor
()]
)
self
.
mdc
=
Mock
(
)
self
.
kvs
=
LmsKeyValueStore
(
course_id
,
self
.
user
,
self
.
desc_md
,
self
.
smc
)
self
.
mdc
.
find
.
return_value
.
state
=
json
.
dumps
({
'a_field'
:
'a_value'
}
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
mdc
)
def
test_get_existing_field
(
self
):
def
test_get_existing_field
(
self
):
"Test that getting an existing field in an existing StudentModule works"
"Test that getting an existing field in an existing StudentModule works"
...
@@ -154,7 +158,7 @@ class TestStudentModuleStorage(TestCase):
...
@@ -154,7 +158,7 @@ class TestStudentModuleStorage(TestCase):
"Test that deleting an existing field removes it from the StudentModule"
"Test that deleting an existing field removes it from the StudentModule"
self
.
kvs
.
delete
(
student_state_key
(
'a_field'
))
self
.
kvs
.
delete
(
student_state_key
(
'a_field'
))
self
.
assertEquals
(
1
,
StudentModule
.
objects
.
all
()
.
count
())
self
.
assertEquals
(
1
,
StudentModule
.
objects
.
all
()
.
count
())
self
.
assertEquals
({},
json
.
loads
(
StudentModule
.
objects
.
all
()[
0
]
.
state
)
)
self
.
assertEquals
({},
self
.
mdc
.
find
.
return_value
.
state
)
def
test_delete_missing_field
(
self
):
def
test_delete_missing_field
(
self
):
"Test that deleting a missing field from an existing StudentModule raises a KeyError"
"Test that deleting a missing field from an existing StudentModule raises a KeyError"
...
@@ -167,8 +171,8 @@ class TestMissingStudentModule(TestCase):
...
@@ -167,8 +171,8 @@ class TestMissingStudentModule(TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
self
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
desc_md
=
{}
self
.
desc_md
=
{}
self
.
smc
=
StudentModuleCache
(
course_id
,
self
.
user
,
[
mock_descriptor
()]
)
self
.
mdc
=
ModelDataCache
([
mock_descriptor
()],
course_id
,
self
.
user
)
self
.
kvs
=
LmsKeyValueStore
(
course_id
,
self
.
user
,
self
.
desc_md
,
self
.
sm
c
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
md
c
)
def
test_get_field_from_missing_student_module
(
self
):
def
test_get_field_from_missing_student_module
(
self
):
"Test that getting a field from a missing StudentModule raises a KeyError"
"Test that getting a field from a missing StudentModule raises a KeyError"
...
@@ -176,12 +180,12 @@ class TestMissingStudentModule(TestCase):
...
@@ -176,12 +180,12 @@ class TestMissingStudentModule(TestCase):
def
test_set_field_in_missing_student_module
(
self
):
def
test_set_field_in_missing_student_module
(
self
):
"Test that setting a field in a missing StudentModule creates the student module"
"Test that setting a field in a missing StudentModule creates the student module"
self
.
assertEquals
(
0
,
len
(
self
.
sm
c
.
cache
))
self
.
assertEquals
(
0
,
len
(
self
.
md
c
.
cache
))
self
.
assertEquals
(
0
,
StudentModule
.
objects
.
all
()
.
count
())
self
.
assertEquals
(
0
,
StudentModule
.
objects
.
all
()
.
count
())
self
.
kvs
.
set
(
student_state_key
(
'a_field'
),
'a_value'
)
self
.
kvs
.
set
(
student_state_key
(
'a_field'
),
'a_value'
)
self
.
assertEquals
(
1
,
len
(
self
.
sm
c
.
cache
))
self
.
assertEquals
(
1
,
len
(
self
.
md
c
.
cache
))
self
.
assertEquals
(
1
,
StudentModule
.
objects
.
all
()
.
count
())
self
.
assertEquals
(
1
,
StudentModule
.
objects
.
all
()
.
count
())
student_module
=
StudentModule
.
objects
.
all
()[
0
]
student_module
=
StudentModule
.
objects
.
all
()[
0
]
...
@@ -201,8 +205,8 @@ class TestSettingsStorage(TestCase):
...
@@ -201,8 +205,8 @@ class TestSettingsStorage(TestCase):
settings
=
SettingsFactory
.
create
()
settings
=
SettingsFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
desc_md
=
{}
self
.
desc_md
=
{}
self
.
smc
=
StudentModuleCache
(
course_id
,
self
.
user
,
[]
)
self
.
mdc
=
ModelDataCache
([
mock_descriptor
()],
course_id
,
self
.
user
)
self
.
kvs
=
LmsKeyValueStore
(
course_id
,
self
.
user
,
self
.
desc_md
,
self
.
sm
c
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
md
c
)
def
test_get_existing_field
(
self
):
def
test_get_existing_field
(
self
):
"Test that getting an existing field in an existing SettingsField works"
"Test that getting an existing field in an existing SettingsField works"
...
@@ -242,8 +246,8 @@ class TestContentStorage(TestCase):
...
@@ -242,8 +246,8 @@ class TestContentStorage(TestCase):
content
=
ContentFactory
.
create
()
content
=
ContentFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
desc_md
=
{}
self
.
desc_md
=
{}
self
.
smc
=
StudentModuleCache
(
course_id
,
self
.
user
,
[]
)
self
.
mdc
=
ModelDataCache
([
mock_descriptor
()],
course_id
,
self
.
user
)
self
.
kvs
=
LmsKeyValueStore
(
course_id
,
self
.
user
,
self
.
desc_md
,
self
.
sm
c
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
md
c
)
def
test_get_existing_field
(
self
):
def
test_get_existing_field
(
self
):
"Test that getting an existing field in an existing ContentField works"
"Test that getting an existing field in an existing ContentField works"
...
@@ -283,8 +287,11 @@ class TestStudentPrefsStorage(TestCase):
...
@@ -283,8 +287,11 @@ class TestStudentPrefsStorage(TestCase):
student_pref
=
StudentPrefsFactory
.
create
()
student_pref
=
StudentPrefsFactory
.
create
()
self
.
user
=
student_pref
.
student
self
.
user
=
student_pref
.
student
self
.
desc_md
=
{}
self
.
desc_md
=
{}
self
.
smc
=
StudentModuleCache
(
course_id
,
self
.
user
,
[])
self
.
mdc
=
ModelDataCache
([
mock_descriptor
([
self
.
kvs
=
LmsKeyValueStore
(
course_id
,
self
.
user
,
self
.
desc_md
,
self
.
smc
)
mock_field
(
Scope
.
student_preferences
,
'student_pref_field'
),
mock_field
(
Scope
.
student_preferences
,
'not_student_pref_field'
),
])],
course_id
,
self
.
user
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
mdc
)
def
test_get_existing_field
(
self
):
def
test_get_existing_field
(
self
):
"Test that getting an existing field in an existing StudentPrefsField works"
"Test that getting an existing field in an existing StudentPrefsField works"
...
@@ -309,7 +316,6 @@ class TestStudentPrefsStorage(TestCase):
...
@@ -309,7 +316,6 @@ class TestStudentPrefsStorage(TestCase):
def
test_delete_existing_field
(
self
):
def
test_delete_existing_field
(
self
):
"Test that deleting an existing field removes it"
"Test that deleting an existing field removes it"
print
list
(
XModuleStudentPrefsField
.
objects
.
all
())
self
.
kvs
.
delete
(
student_prefs_key
(
'student_pref_field'
))
self
.
kvs
.
delete
(
student_prefs_key
(
'student_pref_field'
))
self
.
assertEquals
(
0
,
XModuleStudentPrefsField
.
objects
.
all
()
.
count
())
self
.
assertEquals
(
0
,
XModuleStudentPrefsField
.
objects
.
all
()
.
count
())
...
@@ -325,8 +331,11 @@ class TestStudentInfoStorage(TestCase):
...
@@ -325,8 +331,11 @@ class TestStudentInfoStorage(TestCase):
student_info
=
StudentInfoFactory
.
create
()
student_info
=
StudentInfoFactory
.
create
()
self
.
user
=
student_info
.
student
self
.
user
=
student_info
.
student
self
.
desc_md
=
{}
self
.
desc_md
=
{}
self
.
smc
=
StudentModuleCache
(
course_id
,
self
.
user
,
[])
self
.
mdc
=
ModelDataCache
([
mock_descriptor
([
self
.
kvs
=
LmsKeyValueStore
(
course_id
,
self
.
user
,
self
.
desc_md
,
self
.
smc
)
mock_field
(
Scope
.
student_info
,
'student_info_field'
),
mock_field
(
Scope
.
student_info
,
'not_student_info_field'
),
])],
course_id
,
self
.
user
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
mdc
)
def
test_get_existing_field
(
self
):
def
test_get_existing_field
(
self
):
"Test that getting an existing field in an existing StudentInfoField works"
"Test that getting an existing field in an existing StudentInfoField works"
...
...
lms/djangoapps/courseware/tests/tests.py
View file @
993c24b7
...
@@ -23,7 +23,7 @@ import xmodule.modulestore.django
...
@@ -23,7 +23,7 @@ import xmodule.modulestore.django
# Need access to internal func to put users in the right group
# Need access to internal func to put users in the right group
from
courseware
import
grades
from
courseware
import
grades
from
courseware.access
import
_course_staff_group_name
from
courseware.access
import
_course_staff_group_name
from
courseware.model
s
import
StudentModule
Cache
from
courseware.model
_data
import
ModelData
Cache
from
student.models
import
Registration
from
student.models
import
Registration
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
...
@@ -705,27 +705,27 @@ class TestCourseGrader(PageLoader):
...
@@ -705,27 +705,27 @@ class TestCourseGrader(PageLoader):
self
.
factory
=
RequestFactory
()
self
.
factory
=
RequestFactory
()
def
get_grade_summary
(
self
):
def
get_grade_summary
(
self
):
student_module_cache
=
StudentModule
Cache
.
cache_for_descriptor_descendents
(
model_data_cache
=
ModelData
Cache
.
cache_for_descriptor_descendents
(
self
.
graded_course
.
id
,
self
.
student_user
,
self
.
graded_course
)
self
.
graded_course
.
id
,
self
.
student_user
,
self
.
graded_course
)
fake_request
=
self
.
factory
.
get
(
reverse
(
'progress'
,
fake_request
=
self
.
factory
.
get
(
reverse
(
'progress'
,
kwargs
=
{
'course_id'
:
self
.
graded_course
.
id
}))
kwargs
=
{
'course_id'
:
self
.
graded_course
.
id
}))
return
grades
.
grade
(
self
.
student_user
,
fake_request
,
return
grades
.
grade
(
self
.
student_user
,
fake_request
,
self
.
graded_course
,
student_module
_cache
)
self
.
graded_course
,
model_data
_cache
)
def
get_homework_scores
(
self
):
def
get_homework_scores
(
self
):
return
self
.
get_grade_summary
()[
'totaled_scores'
][
'Homework'
]
return
self
.
get_grade_summary
()[
'totaled_scores'
][
'Homework'
]
def
get_progress_summary
(
self
):
def
get_progress_summary
(
self
):
student_module_cache
=
StudentModule
Cache
.
cache_for_descriptor_descendents
(
model_data_cache
=
ModelData
Cache
.
cache_for_descriptor_descendents
(
self
.
graded_course
.
id
,
self
.
student_user
,
self
.
graded_course
)
self
.
graded_course
.
id
,
self
.
student_user
,
self
.
graded_course
)
fake_request
=
self
.
factory
.
get
(
reverse
(
'progress'
,
fake_request
=
self
.
factory
.
get
(
reverse
(
'progress'
,
kwargs
=
{
'course_id'
:
self
.
graded_course
.
id
}))
kwargs
=
{
'course_id'
:
self
.
graded_course
.
id
}))
progress_summary
=
grades
.
progress_summary
(
self
.
student_user
,
fake_request
,
progress_summary
=
grades
.
progress_summary
(
self
.
student_user
,
fake_request
,
self
.
graded_course
,
student_module
_cache
)
self
.
graded_course
,
model_data
_cache
)
return
progress_summary
return
progress_summary
def
check_grade_percent
(
self
,
percent
):
def
check_grade_percent
(
self
,
percent
):
...
...
lms/djangoapps/courseware/views.py
View file @
993c24b7
...
@@ -23,7 +23,7 @@ from courseware import grades
...
@@ -23,7 +23,7 @@ from courseware import grades
from
courseware.access
import
has_access
from
courseware.access
import
has_access
from
courseware.courses
import
(
get_course_with_access
,
get_courses_by_university
)
from
courseware.courses
import
(
get_course_with_access
,
get_courses_by_university
)
import
courseware.tabs
as
tabs
import
courseware.tabs
as
tabs
from
courseware.model
s
import
StudentModule
Cache
from
courseware.model
_data
import
ModelData
Cache
from
module_render
import
toc_for_course
,
get_module
from
module_render
import
toc_for_course
,
get_module
from
student.models
import
UserProfile
from
student.models
import
UserProfile
...
@@ -81,7 +81,7 @@ def courses(request):
...
@@ -81,7 +81,7 @@ def courses(request):
return
render_to_response
(
"courses.html"
,
{
'universities'
:
universities
})
return
render_to_response
(
"courses.html"
,
{
'universities'
:
universities
})
def
render_accordion
(
request
,
course
,
chapter
,
section
):
def
render_accordion
(
request
,
course
,
chapter
,
section
,
model_data_cache
):
''' Draws navigation bar. Takes current position in accordion as
''' Draws navigation bar. Takes current position in accordion as
parameter.
parameter.
...
@@ -92,7 +92,7 @@ def render_accordion(request, course, chapter, section):
...
@@ -92,7 +92,7 @@ def render_accordion(request, course, chapter, section):
Returns the html string'''
Returns the html string'''
# grab the table of contents
# grab the table of contents
toc
=
toc_for_course
(
request
.
user
,
request
,
course
,
chapter
,
section
)
toc
=
toc_for_course
(
request
.
user
,
request
,
course
,
chapter
,
section
,
model_data_cache
)
context
=
dict
([(
'toc'
,
toc
),
context
=
dict
([(
'toc'
,
toc
),
(
'course_id'
,
course
.
id
),
(
'course_id'
,
course
.
id
),
...
@@ -191,24 +191,21 @@ def index(request, course_id, chapter=None, section=None,
...
@@ -191,24 +191,21 @@ def index(request, course_id, chapter=None, section=None,
return
redirect
(
reverse
(
'about_course'
,
args
=
[
course
.
id
]))
return
redirect
(
reverse
(
'about_course'
,
args
=
[
course
.
id
]))
try
:
try
:
student_module_cache
=
StudentModule
Cache
.
cache_for_descriptor_descendents
(
model_data_cache
=
ModelData
Cache
.
cache_for_descriptor_descendents
(
course
.
id
,
request
.
user
,
course
,
depth
=
2
)
course
.
id
,
request
.
user
,
course
,
depth
=
2
)
# Has this student been in this course before?
course_module
=
get_module
(
request
.
user
,
request
,
course
.
location
,
model_data_cache
,
course
.
id
)
first_time
=
student_module_cache
.
lookup
(
course_id
,
'course'
,
course
.
location
.
url
())
is
None
course_module
=
get_module
(
request
.
user
,
request
,
course
.
location
,
student_module_cache
,
course
.
id
)
if
course_module
is
None
:
if
course_module
is
None
:
log
.
warning
(
'If you see this, something went wrong: if we got this'
log
.
warning
(
'If you see this, something went wrong: if we got this'
' far, should have gotten a course module for this user'
)
' far, should have gotten a course module for this user'
)
return
redirect
(
reverse
(
'about_course'
,
args
=
[
course
.
id
]))
return
redirect
(
reverse
(
'about_course'
,
args
=
[
course
.
id
]))
if
chapter
is
None
:
if
chapter
is
None
:
return
redirect_to_course_position
(
course_module
,
first_time
)
return
redirect_to_course_position
(
course_module
,
course_module
.
first_time_user
)
context
=
{
context
=
{
'csrf'
:
csrf
(
request
)[
'csrf_token'
],
'csrf'
:
csrf
(
request
)[
'csrf_token'
],
'accordion'
:
render_accordion
(
request
,
course
,
chapter
,
section
),
'accordion'
:
render_accordion
(
request
,
course
,
chapter
,
section
,
model_data_cache
),
'COURSE_TITLE'
:
course
.
title
,
'COURSE_TITLE'
:
course
.
title
,
'course'
:
course
,
'course'
:
course
,
'init'
:
''
,
'init'
:
''
,
...
@@ -224,7 +221,7 @@ def index(request, course_id, chapter=None, section=None,
...
@@ -224,7 +221,7 @@ def index(request, course_id, chapter=None, section=None,
raise
Http404
raise
Http404
chapter_module
=
get_module
(
request
.
user
,
request
,
chapter_descriptor
.
location
,
chapter_module
=
get_module
(
request
.
user
,
request
,
chapter_descriptor
.
location
,
student_module
_cache
,
course_id
)
model_data
_cache
,
course_id
)
if
chapter_module
is
None
:
if
chapter_module
is
None
:
# User may be trying to access a chapter that isn't live yet
# User may be trying to access a chapter that isn't live yet
raise
Http404
raise
Http404
...
@@ -235,11 +232,11 @@ def index(request, course_id, chapter=None, section=None,
...
@@ -235,11 +232,11 @@ def index(request, course_id, chapter=None, section=None,
# Specifically asked-for section doesn't exist
# Specifically asked-for section doesn't exist
raise
Http404
raise
Http404
section_
student_module_cache
=
StudentModule
Cache
.
cache_for_descriptor_descendents
(
section_
model_data_cache
=
ModelData
Cache
.
cache_for_descriptor_descendents
(
course_id
,
request
.
user
,
section_descriptor
)
course_id
,
request
.
user
,
section_descriptor
)
section_module
=
get_module
(
request
.
user
,
request
,
section_module
=
get_module
(
request
.
user
,
request
,
section_descriptor
.
location
,
section_descriptor
.
location
,
section_
student_module
_cache
,
course_id
,
position
)
section_
model_data
_cache
,
course_id
,
position
)
if
section_module
is
None
:
if
section_module
is
None
:
# User may be trying to be clever and access something
# User may be trying to be clever and access something
# they don't have access to.
# they don't have access to.
...
@@ -491,12 +488,12 @@ def progress(request, course_id, student_id=None):
...
@@ -491,12 +488,12 @@ def progress(request, course_id, student_id=None):
# additional DB lookup (this kills the Progress page in particular).
# additional DB lookup (this kills the Progress page in particular).
student
=
User
.
objects
.
prefetch_related
(
"groups"
)
.
get
(
id
=
student
.
id
)
student
=
User
.
objects
.
prefetch_related
(
"groups"
)
.
get
(
id
=
student
.
id
)
student_module_cache
=
StudentModule
Cache
.
cache_for_descriptor_descendents
(
model_data_cache
=
ModelData
Cache
.
cache_for_descriptor_descendents
(
course_id
,
student
,
course
)
course_id
,
student
,
course
)
courseware_summary
=
grades
.
progress_summary
(
student
,
request
,
course
,
courseware_summary
=
grades
.
progress_summary
(
student
,
request
,
course
,
student_module
_cache
)
model_data
_cache
)
grade_summary
=
grades
.
grade
(
student
,
request
,
course
,
student_module
_cache
)
grade_summary
=
grades
.
grade
(
student
,
request
,
course
,
model_data
_cache
)
if
courseware_summary
is
None
:
if
courseware_summary
is
None
:
#This means the student didn't have access to the course (which the instructor requested)
#This means the student didn't have access to the course (which the instructor requested)
...
...
lms/templates/courseware/info.html
View file @
993c24b7
...
@@ -27,20 +27,20 @@ $(document).ready(function(){
...
@@ -27,20 +27,20 @@ $(document).ready(function(){
% if user.is_authenticated():
% if user.is_authenticated():
<section
class=
"updates"
>
<section
class=
"updates"
>
<h1>
Course Updates
&
News
</h1>
<h1>
Course Updates
&
News
</h1>
${get_course_info_section(request, c
ache, c
ourse, 'updates')}
${get_course_info_section(request, course, 'updates')}
</section>
</section>
<section
aria-label=
"Handout Navigation"
class=
"handouts"
>
<section
aria-label=
"Handout Navigation"
class=
"handouts"
>
<h1>
${course.info_sidebar_name}
</h1>
<h1>
${course.info_sidebar_name}
</h1>
${get_course_info_section(request, c
ache, c
ourse, 'handouts')}
${get_course_info_section(request, course, 'handouts')}
</section>
</section>
% else:
% else:
<section
class=
"updates"
>
<section
class=
"updates"
>
<h1>
Course Updates
&
News
</h1>
<h1>
Course Updates
&
News
</h1>
${get_course_info_section(request, c
ache, c
ourse, 'guest_updates')}
${get_course_info_section(request, course, 'guest_updates')}
</section>
</section>
<section
aria-label=
"Handout Navigation"
class=
"handouts"
>
<section
aria-label=
"Handout Navigation"
class=
"handouts"
>
<h1>
Course Handouts
</h1>
<h1>
Course Handouts
</h1>
${get_course_info_section(request, c
ache, c
ourse, 'guest_handouts')}
${get_course_info_section(request, course, 'guest_handouts')}
</section>
</section>
% endif
% endif
</div>
</div>
...
...
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