Commit ba42c022 by Bridger Maxwell

Moved the generation of the 'grading context' to a lazy property in course descriptor.

parent 2348f71c
def lazyproperty(fn):
"""
Use this decorator for lazy generation of properties that
are expensive to compute. From http://stackoverflow.com/a/3013910/86828
Example:
class Test(object):
@lazyproperty
def a(self):
print 'generating "a"'
return range(5)
Interactive Session:
>>> t = Test()
>>> t.__dict__
{}
>>> t.a
generating "a"
[0, 1, 2, 3, 4]
>>> t.__dict__
{'_lazy_a': [0, 1, 2, 3, 4]}
>>> t.a
[0, 1, 2, 3, 4]
"""
attr_name = '_lazy_' + fn.__name__
@property
def _lazyprop(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, fn(self))
return getattr(self, attr_name)
return _lazyprop
\ No newline at end of file
......@@ -3,6 +3,7 @@ import time
import dateutil.parser
import logging
from util.decorators import lazyproperty
from xmodule.graders import load_grading_policy
from xmodule.modulestore import Location
from xmodule.seq_module import SequenceDescriptor, SequenceModule
......@@ -17,9 +18,6 @@ class CourseDescriptor(SequenceDescriptor):
def __init__(self, system, definition=None, **kwargs):
super(CourseDescriptor, self).__init__(system, definition, **kwargs)
self._grader = None
self._grade_cutoffs = None
msg = None
try:
self.start = time.strptime(self.metadata["start"], "%Y-%m-%dT%H:%M")
......@@ -42,17 +40,14 @@ class CourseDescriptor(SequenceDescriptor):
@property
def grader(self):
self.__load_grading_policy()
return self._grader
return self.__grading_policy['GRADER']
@property
def grade_cutoffs(self):
self.__load_grading_policy()
return self._grade_cutoffs
return self.__grading_policy['GRADE_CUTOFFS']
def __load_grading_policy(self):
if not self._grader or not self._grade_cutoffs:
@lazyproperty
def __grading_policy(self):
policy_string = ""
try:
......@@ -63,8 +58,64 @@ class CourseDescriptor(SequenceDescriptor):
grading_policy = load_grading_policy(policy_string)
self._grader = grading_policy['GRADER']
self._grade_cutoffs = grading_policy['GRADE_CUTOFFS']
return grading_policy
@lazyproperty
def grading_context(self):
"""
This returns a dictionary with keys necessary for quickly grading
a student. They are used by grades.grade()
The grading context has two keys:
graded_sections - This contains the sections that are graded, as
well as all possible children modules that can effect the
grading. This allows some sections to be skipped if the student
hasn't seen any part of it.
The format is a dictionary keyed by section-type. The values are
arrays of dictionaries containing
"section_descriptor" : The section descriptor
"xmoduledescriptors" : An array of xmoduledescriptors that
could possibly be in the section, for any student
all_descriptors - This contains a list of all xmodules that can
effect grading a student. This is used to efficiently fetch
all the xmodule state for a StudentModuleCache without walking
the descriptor tree again.
"""
all_descriptors = []
graded_sections = {}
def yield_descriptor_descendents(module_descriptor):
for child in module_descriptor.get_children():
yield child
for module_descriptor in yield_descriptor_descendents(child):
yield module_descriptor
for c in self.get_children():
sections = []
for s in c.get_children():
if s.metadata.get('graded', False):
xmoduledescriptors = []
for module in yield_descriptor_descendents(s):
# TODO: Only include modules that have a score here
xmoduledescriptors.append(module)
section_description = { 'section_descriptor' : s, 'xmoduledescriptors' : xmoduledescriptors}
section_format = s.metadata.get('format', "")
graded_sections[ section_format ] = graded_sections.get( section_format, [] ) + [section_description]
all_descriptors.extend(xmoduledescriptors)
all_descriptors.append(s)
return { 'graded_sections' : graded_sections,
'all_descriptors' : all_descriptors,}
@staticmethod
......
......@@ -3,6 +3,7 @@ import logging
from django.conf import settings
from models import StudentModuleCache
from module_render import get_module, get_instance_module
from xmodule import graders
from xmodule.graders import Score
......@@ -10,54 +11,28 @@ from models import StudentModule
_log = logging.getLogger("mitx.courseware")
def get_graded_sections(course_descriptor):
"""
Arguments:
course_descriptor: a CourseDescriptor object for the course to be graded
Returns:
A dictionary keyed by section-type. The values are arrays of dictionaries containing
"section_descriptor" : The section descriptor
"xmoduledescriptors" : An array of xmoduledescriptors that could possibly be in the section, for any student
"""
all_descriptors = []
graded_sections = {}
for c in course_descriptor.get_children():
sections = []
for s in c.get_children():
if s.metadata.get('graded', False):
xmoduledescriptors = []
for module in yield_descriptor_descendents(s):
# TODO: Only include modules that have a score here
xmoduledescriptors.append(module)
section_description = { 'section_descriptor' : s, 'xmoduledescriptors' : xmoduledescriptors}
section_format = s.metadata.get('format', "")
graded_sections[ section_format ] = graded_sections.get( section_format, [] ) + [section_description]
all_descriptors.extend(xmoduledescriptors)
all_descriptors.append(s)
return graded_sections, all_descriptors
def yield_descriptor_descendents(module_descriptor):
for child in module_descriptor.get_children():
yield child
for module_descriptor in yield_descriptor_descendents(child):
yield module_descriptor
def yield_module_descendents(module):
for child in module.get_display_items():
yield child
for module in yield_module_descendents(child):
yield module
def fast_grade(student, request, course_graded_sections, grader, student_module_cache):
def grade(student, request, course, student_module_cache=None):
"""
This grades a student as quickly as possible. It reutns the
output from the course grader. More information on the format
is in the docstring for CourseGrader.
"""
grading_context = course.grading_context
if student_module_cache == None:
student_module_cache = StudentModuleCache(student, descriptors=grading_context['all_descriptors'])
totaled_scores = {}
for section_format, sections in course_graded_sections.iteritems():
# This next complicated loop is just to collect the totaled_scores, which is
# passed to the grader
for section_format, sections in grading_context['graded_sections'].iteritems():
format_scores = []
for section in sections:
section_descriptor = section['section_descriptor']
......@@ -72,6 +47,8 @@ def fast_grade(student, request, course_graded_sections, grader, student_module_
if should_grade_section:
scores = []
# TODO: We need the request to pass into here. If we could forgo that, our arguments
# would be simpler
section_module = get_module(student, request, section_descriptor.location, student_module_cache)
# TODO: We may be able to speed this up by only getting a list of children IDs from section_module
......@@ -106,11 +83,11 @@ def fast_grade(student, request, course_graded_sections, grader, student_module_
totaled_scores[section_format] = format_scores
grade_summary = grade_summary = grader.grade(totaled_scores)
grade_summary = course.grader.grade(totaled_scores)
return grade_summary
def grade_sheet(student, course, grader, student_module_cache):
def progress_summary(student, course, grader, student_module_cache):
"""
This pulls a summary of all problems in the course.
......
......@@ -78,20 +78,16 @@ def gradebook(request, course_id):
raise Http404
course = check_course(course_id)
sections, all_descriptors = grades.get_graded_sections(course)
student_objects = User.objects.all()[:100]
student_info = []
#TODO: Only select students who are in the course
for student in student_objects:
student_module_cache = StudentModuleCache(student, descriptors=all_descriptors)
student_info.append({
'username': student.username,
'id': student.id,
'email': student.email,
'grade_summary': grades.fast_grade(student, request, sections, course.grader, student_module_cache),
'grade_summary': grades.grade(student, request, course),
'realname': UserProfile.objects.get(user=student).name
})
......@@ -117,9 +113,8 @@ def profile(request, course_id, student_id=None):
student_module_cache = StudentModuleCache(request.user, course)
course_module = get_module(request.user, request, course.location, student_module_cache)
courseware_summary = grades.grade_sheet(student, course_module, course.grader, student_module_cache)
sections, _ = grades.get_graded_sections(course)
grade_summary = grades.fast_grade(request.user, request, sections, course.grader, student_module_cache)
courseware_summary = grades.progress_summary(student, course_module, course.grader, student_module_cache)
grade_summary = grades.grade(request.user, request, course, student_module_cache)
context = {'name': user_info.name,
'username': student.username,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment