offline_gradecalc.py 3.2 KB
Newer Older
1 2
"""
======== Offline calculation of grades =============================================================
3

4 5 6 7 8
Computing grades of a large number of students can take a long time.  These routines allow grades to
be computed offline, by a batch process (eg cronjob).

The grades are stored in the OfflineComputedGrade table of the courseware model.
"""
9 10 11 12 13 14
import json
import time

from json import JSONEncoder
from courseware import grades, models
from courseware.courses import get_course_by_id
15
from django.contrib.auth.models import User
16

17
from instructor.utils import DummyRequest
18

19

20 21 22 23 24 25 26 27 28 29 30
class MyEncoder(JSONEncoder):

    def _iterencode(self, obj, markers=None):
        if isinstance(obj, tuple) and hasattr(obj, '_asdict'):
            gen = self._iterencode_dict(obj._asdict(), markers)
        else:
            gen = JSONEncoder._iterencode(self, obj, markers)
        for chunk in gen:
            yield chunk


31
def offline_grade_calculation(course_key):
32
    '''
Calen Pennington committed
33
    Compute grades for all students for a specified course, and save results to the DB.
34 35 36
    '''

    tstart = time.time()
37
    enrolled_students = User.objects.filter(
38
        courseenrollment__course_id=course_key,
39 40
        courseenrollment__is_active=1
    ).prefetch_related("groups").order_by('username')
41 42 43

    enc = MyEncoder()

44 45
    print "{} enrolled students".format(len(enrolled_students))
    course = get_course_by_id(course_key)
46 47

    for student in enrolled_students:
Kristin Stephens committed
48 49 50 51
        request = DummyRequest()
        request.user = student
        request.session = {}

52 53
        gradeset = grades.grade(student, request, course, keep_raw_scores=True)
        gs = enc.encode(gradeset)
54
        ocg, _created = models.OfflineComputedGrade.objects.get_or_create(user=student, course_id=course_key)
55 56
        ocg.gradeset = gs
        ocg.save()
Calen Pennington committed
57
        print "%s done" % student  	# print statement used because this is run by a management command
58 59 60

    tend = time.time()
    dt = tend - tstart
Calen Pennington committed
61

62
    ocgl = models.OfflineComputedGradeLog(course_id=course_key, seconds=dt, nstudents=len(enrolled_students))
63 64 65
    ocgl.save()
    print ocgl
    print "All Done!"
Calen Pennington committed
66

67

68
def offline_grades_available(course_key):
69 70 71 72
    '''
    Returns False if no offline grades available for specified course.
    Otherwise returns latest log field entry about the available pre-computed grades.
    '''
73
    ocgl = models.OfflineComputedGradeLog.objects.filter(course_id=course_key)
74 75 76 77
    if not ocgl:
        return False
    return ocgl.latest('created')

Calen Pennington committed
78

79 80 81 82 83 84 85 86
def student_grades(student, request, course, keep_raw_scores=False, use_offline=False):
    '''
    This is the main interface to get grades.  It has the same parameters as grades.grade, as well
    as use_offline.  If use_offline is True then this will look for an offline computed gradeset in the DB.
    '''

    if not use_offline:
        return grades.grade(student, request, course, keep_raw_scores=keep_raw_scores)
Calen Pennington committed
87

88 89 90
    try:
        ocg = models.OfflineComputedGrade.objects.get(user=student, course_id=course.id)
    except models.OfflineComputedGrade.DoesNotExist:
91 92 93 94 95
        return dict(
            raw_scores=[],
            section_breakdown=[],
            msg='Error: no offline gradeset available for {}, {}'.format(student, course.id)
        )
96

Calen Pennington committed
97
    return json.loads(ocg.gradeset)