offline_gradecalc.py 3.29 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
    '''

    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

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

    for student in enrolled_students:
Kristin Stephens committed
51 52 53 54
        request = DummyRequest()
        request.user = student
        request.session = {}

55 56 57 58 59
        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
60
        print "%s done" % student  	# print statement used because this is run by a management command
61 62 63

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

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

70 71 72 73 74 75 76 77 78 79 80

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
81

82 83 84 85 86 87 88 89
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
90

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

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