offline_gradecalc.py 3.23 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
# ======== Offline calculation of grades =============================================================================
#
# 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.

import json
import time

from json import JSONEncoder
from courseware import grades, models
from courseware.courses import get_course_by_id
14
from django.contrib.auth.models import User
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29


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


def offline_grade_calculation(course_id):
    '''
Calen Pennington committed
30
    Compute grades for all students for a specified course, and save results to the DB.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    '''

    tstart = time.time()
    enrolled_students = User.objects.filter(courseenrollment__course_id=course_id).prefetch_related("groups").order_by('username')

    enc = MyEncoder()

    class DummyRequest(object):
        META = {}
        def __init__(self):
            return
        def get_host(self):
            return 'edx.mit.edu'
        def is_secure(self):
            return False

    request = DummyRequest()

    print "%d enrolled students" % len(enrolled_students)
    course = get_course_by_id(course_id)

    for student in enrolled_students:
        gradeset = grades.grade(student, request, course, keep_raw_scores=True)
        gs = enc.encode(gradeset)
        ocg, created = models.OfflineComputedGrade.objects.get_or_create(user=student, course_id=course_id)
        ocg.gradeset = gs
        ocg.save()
Calen Pennington committed
58
        print "%s done" % student  	# print statement used because this is run by a management command
59 60 61

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

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

68 69 70 71 72 73 74 75 76 77 78

def offline_grades_available(course_id):
    '''
    Returns False if no offline grades available for specified course.
    Otherwise returns latest log field entry about the available pre-computed grades.
    '''
    ocgl = models.OfflineComputedGradeLog.objects.filter(course_id=course_id)
    if not ocgl:
        return False
    return ocgl.latest('created')

Calen Pennington committed
79

80 81 82 83 84 85 86 87
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
88

89 90 91
    try:
        ocg = models.OfflineComputedGrade.objects.get(user=student, course_id=course.id)
    except models.OfflineComputedGrade.DoesNotExist:
Calen Pennington committed
92
        return dict(raw_scores=[], section_breakdown=[],
93 94
                    msg='Error: no offline gradeset available for %s, %s' % (student, course.id))

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