import random import logging from django.conf import settings from courseware.course_settings import course_settings from xmodule import graders from xmodule.graders import Score from models import StudentModule _log = logging.getLogger("mitx.courseware") def grade_sheet(student, course, student_module_cache): """ This pulls a summary of all problems in the course. It returns a dictionary with two datastructures: - courseware_summary is a summary of all sections with problems in the course. It is organized as an array of chapters, each containing an array of sections, each containing an array of scores. This contains information for graded and ungraded problems, and is good for displaying a course summary with due dates, etc. - grade_summary is the output from the course grader. More information on the format is in the docstring for CourseGrader. Arguments: student: A User object for the student to grade course: An XModule containing the course to grade student_module_cache: A StudentModuleCache initialized with all instance_modules for the student """ totaled_scores = {} chapters = [] for c in course.get_children(): sections = [] for s in c.get_children(): def yield_descendents(module): yield module for child in module.get_display_items(): for module in yield_descendents(child): yield module graded = s.metadata.get('graded', False) scores = [] for module in yield_descendents(s): (correct, total) = get_score(student, module, student_module_cache) if correct is None and total is None: continue if settings.GENERATE_PROFILE_SCORES: if total > 1: correct = random.randrange(max(total - 2, 1), total + 1) else: correct = total if not total > 0: #We simply cannot grade a problem that is 12/0, because we might need it as a percentage graded = False scores.append(Score(correct, total, graded, module.metadata.get('display_name'))) section_total, graded_total = graders.aggregate_scores(scores, s.metadata.get('display_name')) #Add the graded total to totaled_scores format = s.metadata.get('format', "") if format and graded_total.possible > 0: format_scores = totaled_scores.get(format, []) format_scores.append(graded_total) totaled_scores[format] = format_scores sections.append({ 'section': s.metadata.get('display_name'), 'scores': scores, 'section_total': section_total, 'format': format, 'due': s.metadata.get("due", ""), 'graded': graded, }) chapters.append({'course': course.metadata.get('display_name'), 'chapter': c.metadata.get('display_name'), 'sections': sections}) grader = course_settings.GRADER grade_summary = grader.grade(totaled_scores) return {'courseware_summary': chapters, 'grade_summary': grade_summary} def get_score(user, problem, cache): """ Return the score for a user on a problem user: a Student object problem: an XModule cache: A StudentModuleCache """ correct = 0.0 # If the ID is not in the cache, add the item instance_module = cache.lookup(problem.category, problem.id) if instance_module is None: instance_module = StudentModule(module_type=problem.category, module_state_key=problem.id, student=user, state=None, grade=0, max_grade=problem.max_score(), done='i') cache.append(instance_module) instance_module.save() # If this problem is ungraded/ungradable, bail if instance_module.max_grade is None: return (None, None) correct = instance_module.grade if instance_module.grade is not None else 0 total = instance_module.max_grade if correct is not None and total is not None: #Now we re-weight the problem, if specified weight = getattr(problem, 'weight', 1) if weight != 1: correct = correct * weight / total total = weight return (correct, total)