offline_gradecalc.py 3.18 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
from instructor.utils import DummyRequest
17 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


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

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

    enc = MyEncoder()

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

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

50 51 52 53 54
        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
55
        print "%s done" % student  	# print statement used because this is run by a management command
56 57 58

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

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

65 66 67 68 69 70 71 72 73 74 75

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
76

77 78 79 80 81 82 83 84
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
85

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

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