grades.py 4.64 KB
Newer Older
1
import random
2
import logging
3 4

from django.conf import settings
5

6
from courseware.course_settings import course_settings
7 8
from xmodule import graders
from xmodule.graders import Score
9
from models import StudentModule
10 11 12

_log = logging.getLogger("mitx.courseware")

13 14

def grade_sheet(student, course, student_module_cache):
15 16
    """
    This pulls a summary of all problems in the course. It returns a dictionary with two datastructures:
17

18 19 20
    - 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.
21

22
    - grade_summary is the output from the course grader. More information on the format is in the docstring for CourseGrader.
23

24 25 26 27 28
    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
    """
29
    totaled_scores = {}
30 31
    chapters = []
    for c in course.get_children():
32
        sections = []
33 34 35 36 37 38 39
        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

40
            graded = s.metadata.get('graded', False)
41 42 43 44
            scores = []
            for module in yield_descendents(s):
                (correct, total) = get_score(student, module, student_module_cache)

45 46 47
                if correct is None and total is None:
                    continue

48 49 50 51 52 53 54 55 56 57
                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

58
                scores.append(Score(correct, total, graded, module.metadata.get('display_name')))
59

60
            section_total, graded_total = graders.aggregate_scores(scores, s.metadata.get('display_name'))
61
            #Add the graded total to totaled_scores
62
            format = s.metadata.get('format', "")
63
            if format and graded_total.possible > 0:
64 65 66 67 68
                format_scores = totaled_scores.get(format, [])
                format_scores.append(graded_total)
                totaled_scores[format] = format_scores

            sections.append({
69
                'section': s.metadata.get('display_name'),
70 71 72
                'scores': scores,
                'section_total': section_total,
                'format': format,
73
                'due': s.metadata.get("due", ""),
74 75 76
                'graded': graded,
            })

77 78
        chapters.append({'course': course.metadata.get('display_name'),
                         'chapter': c.metadata.get('display_name'),
79 80
                         'sections': sections})

81
    grader = course_settings.GRADER
82
    grade_summary = grader.grade(totaled_scores)
83

84 85 86 87 88
    return {'courseware_summary': chapters,
            'grade_summary': grade_summary}


def get_score(user, problem, cache):
89 90 91 92
    """
    Return the score for a user on a problem

    user: a Student object
93 94
    problem: an XModule
    cache: A StudentModuleCache
95
    """
96
    correct = 0.0
97

98
    # If the ID is not in the cache, add the item
99
    instance_module = cache.lookup(problem.category, problem.id)
100
    if instance_module is None:
101
        instance_module = StudentModule(module_type=problem.category,
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
                                        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
124 125

    return (correct, total)