Fix grades management commands
This commit is contained in:
@@ -3,6 +3,9 @@ from certificates.models import GeneratedCertificate
|
||||
from django.test.client import RequestFactory
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
import os
|
||||
from opaque_keys import InvalidKeyError
|
||||
from xmodule.modulestore.keys import CourseKey
|
||||
from xmodule.modulestore.locations import SlashSeparatedCourseKey
|
||||
from django.contrib.auth.models import User
|
||||
from optparse import make_option
|
||||
import datetime
|
||||
@@ -62,24 +65,37 @@ class Command(BaseCommand):
|
||||
options['output']))
|
||||
|
||||
STATUS_INTERVAL = 100
|
||||
course_id = options['course']
|
||||
print "Fetching enrolled students for {0}".format(course_id)
|
||||
|
||||
# parse out the course into a coursekey
|
||||
if options['course']:
|
||||
try:
|
||||
course_key = CourseKey.from_string(options['course'])
|
||||
# if it's not a new-style course key, parse it from an old-style
|
||||
# course key
|
||||
except InvalidKeyError:
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(options['course'])
|
||||
|
||||
print "Fetching enrolled students for {0}".format(course_key)
|
||||
enrolled_students = User.objects.filter(
|
||||
courseenrollment__course_id=course_id)
|
||||
courseenrollment__course_id=course_key
|
||||
)
|
||||
factory = RequestMock()
|
||||
request = factory.get('/')
|
||||
|
||||
total = enrolled_students.count()
|
||||
print "Total enrolled: {0}".format(total)
|
||||
course = courses.get_course_by_id(course_id)
|
||||
course = courses.get_course_by_id(course_key)
|
||||
total = enrolled_students.count()
|
||||
start = datetime.datetime.now()
|
||||
rows = []
|
||||
header = None
|
||||
print "Fetching certificate data"
|
||||
cert_grades = {cert.user.username: cert.grade
|
||||
for cert in list(GeneratedCertificate.objects.filter(
|
||||
course_id=course_id).prefetch_related('user'))}
|
||||
cert_grades = {
|
||||
cert.user.username: cert.grade
|
||||
for cert in list(
|
||||
GeneratedCertificate.objects.filter(course_id=course_key).prefetch_related('user')
|
||||
)
|
||||
}
|
||||
print "Grading students"
|
||||
for count, student in enumerate(enrolled_students):
|
||||
count += 1
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
from instructor.offline_gradecalc import offline_grade_calculation
|
||||
from courseware.courses import get_course_by_id
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from opaque_keys import InvalidKeyError
|
||||
from xmodule.modulestore.keys import CourseKey
|
||||
from xmodule.modulestore.locations import SlashSeparatedCourseKey
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
@@ -25,19 +28,24 @@ class Command(BaseCommand):
|
||||
else:
|
||||
print self.help
|
||||
return
|
||||
|
||||
course_key = None
|
||||
# parse out the course id into a coursekey
|
||||
try:
|
||||
course = get_course_by_id(course_id)
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
# if it's not a new-style course key, parse it from an old-style
|
||||
# course key
|
||||
except InvalidKeyError:
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
||||
try:
|
||||
course = get_course_by_id(course_key)
|
||||
except Exception as err:
|
||||
if course_id in modulestore().courses:
|
||||
course = modulestore().courses[course_id]
|
||||
else:
|
||||
print "-----------------------------------------------------------------------------"
|
||||
print "Sorry, cannot find course %s" % course_id
|
||||
print "Please provide a course ID or course data directory name, eg content-mit-801rq"
|
||||
return
|
||||
print "-----------------------------------------------------------------------------"
|
||||
print "Sorry, cannot find course with id {}".format(course_id)
|
||||
print "Got exception {}".format(err)
|
||||
print "Please provide a course ID or course data directory name, eg content-mit-801rq"
|
||||
return
|
||||
|
||||
print "-----------------------------------------------------------------------------"
|
||||
print "Computing grades for %s" % (course.id)
|
||||
print "Computing grades for {}".format(course_id)
|
||||
|
||||
offline_grade_calculation(course.id)
|
||||
offline_grade_calculation(course_key)
|
||||
|
||||
@@ -7,6 +7,9 @@ import csv
|
||||
|
||||
from instructor.views.legacy import get_student_grade_summary_data
|
||||
from courseware.courses import get_course_by_id
|
||||
from opaque_keys import InvalidKeyError
|
||||
from xmodule.modulestore.keys import CourseKey
|
||||
from xmodule.modulestore.locations import SlashSeparatedCourseKey
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
@@ -39,20 +42,26 @@ class Command(BaseCommand):
|
||||
get_raw_scores = args[2].lower() == 'raw'
|
||||
|
||||
request = DummyRequest()
|
||||
# parse out the course into a coursekey
|
||||
try:
|
||||
course = get_course_by_id(course_id)
|
||||
except Exception:
|
||||
if course_id in modulestore().courses:
|
||||
course = modulestore().courses[course_id]
|
||||
else:
|
||||
print "-----------------------------------------------------------------------------"
|
||||
print "Sorry, cannot find course %s" % course_id
|
||||
print "Please provide a course ID or course data directory name, eg content-mit-801rq"
|
||||
return
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
# if it's not a new-style course key, parse it from an old-style
|
||||
# course key
|
||||
except InvalidKeyError:
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
||||
|
||||
try:
|
||||
course = get_course_by_id(course_key)
|
||||
except Exception as err:
|
||||
print "-----------------------------------------------------------------------------"
|
||||
print "Sorry, cannot find course with id {}".format(course_id)
|
||||
print "Got exception {}".format(err)
|
||||
print "Please provide a course ID or course data directory name, eg content-mit-801rq"
|
||||
return
|
||||
|
||||
print "-----------------------------------------------------------------------------"
|
||||
print "Dumping grades from %s to file %s (get_raw_scores=%s)" % (course.id, fn, get_raw_scores)
|
||||
datatable = get_student_grade_summary_data(request, course, course.id, get_raw_scores=get_raw_scores)
|
||||
print "Dumping grades from {} to file {} (get_raw_scores={})".format(course.id, fn, get_raw_scores)
|
||||
datatable = get_student_grade_summary_data(request, course, get_raw_scores=get_raw_scores)
|
||||
|
||||
fp = open(fn, 'w')
|
||||
|
||||
@@ -63,4 +72,4 @@ class Command(BaseCommand):
|
||||
writer.writerow(encoded_row)
|
||||
|
||||
fp.close()
|
||||
print "Done: %d records dumped" % len(datatable['data'])
|
||||
print "Done: {} records dumped".format(len(datatable['data']))
|
||||
|
||||
@@ -26,21 +26,21 @@ class MyEncoder(JSONEncoder):
|
||||
yield chunk
|
||||
|
||||
|
||||
def offline_grade_calculation(course_id):
|
||||
def offline_grade_calculation(course_key):
|
||||
'''
|
||||
Compute grades for all students for a specified course, and save results to the DB.
|
||||
'''
|
||||
|
||||
tstart = time.time()
|
||||
enrolled_students = User.objects.filter(
|
||||
courseenrollment__course_id=course_id,
|
||||
courseenrollment__course_id=course_key,
|
||||
courseenrollment__is_active=1
|
||||
).prefetch_related("groups").order_by('username')
|
||||
|
||||
enc = MyEncoder()
|
||||
|
||||
print "%d enrolled students" % len(enrolled_students)
|
||||
course = get_course_by_id(course_id)
|
||||
print "{} enrolled students".format(len(enrolled_students))
|
||||
course = get_course_by_id(course_key)
|
||||
|
||||
for student in enrolled_students:
|
||||
request = DummyRequest()
|
||||
@@ -49,7 +49,7 @@ def offline_grade_calculation(course_id):
|
||||
|
||||
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, created = models.OfflineComputedGrade.objects.get_or_create(user=student, course_id=course_key)
|
||||
ocg.gradeset = gs
|
||||
ocg.save()
|
||||
print "%s done" % student # print statement used because this is run by a management command
|
||||
@@ -57,18 +57,18 @@ def offline_grade_calculation(course_id):
|
||||
tend = time.time()
|
||||
dt = tend - tstart
|
||||
|
||||
ocgl = models.OfflineComputedGradeLog(course_id=course_id, seconds=dt, nstudents=len(enrolled_students))
|
||||
ocgl = models.OfflineComputedGradeLog(course_id=course_key, seconds=dt, nstudents=len(enrolled_students))
|
||||
ocgl.save()
|
||||
print ocgl
|
||||
print "All Done!"
|
||||
|
||||
|
||||
def offline_grades_available(course_id):
|
||||
def offline_grades_available(course_key):
|
||||
'''
|
||||
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)
|
||||
ocgl = models.OfflineComputedGradeLog.objects.filter(course_id=course_key)
|
||||
if not ocgl:
|
||||
return False
|
||||
return ocgl.latest('created')
|
||||
@@ -86,7 +86,10 @@ def student_grades(student, request, course, keep_raw_scores=False, use_offline=
|
||||
try:
|
||||
ocg = models.OfflineComputedGrade.objects.get(user=student, course_id=course.id)
|
||||
except models.OfflineComputedGrade.DoesNotExist:
|
||||
return dict(raw_scores=[], section_breakdown=[],
|
||||
msg='Error: no offline gradeset available for %s, %s' % (student, course.id))
|
||||
return dict(
|
||||
raw_scores=[],
|
||||
section_breakdown=[],
|
||||
msg='Error: no offline gradeset available for {}, {}'.format(student, course.id)
|
||||
)
|
||||
|
||||
return json.loads(ocg.gradeset)
|
||||
|
||||
@@ -226,19 +226,19 @@ def instructor_dashboard(request, course_id):
|
||||
|
||||
if action == 'Dump list of enrolled students' or action == 'List enrolled students':
|
||||
log.debug(action)
|
||||
datatable = get_student_grade_summary_data(request, course, course_key, get_grades=False, use_offline=use_offline)
|
||||
datatable = get_student_grade_summary_data(request, course, get_grades=False, use_offline=use_offline)
|
||||
datatable['title'] = _('List of students enrolled in {course_key}').format(course_key=course_key.to_deprecated_string())
|
||||
track.views.server_track(request, "list-students", {}, page="idashboard")
|
||||
|
||||
elif 'Dump Grades' in action:
|
||||
log.debug(action)
|
||||
datatable = get_student_grade_summary_data(request, course, course_key, get_grades=True, use_offline=use_offline)
|
||||
datatable = get_student_grade_summary_data(request, course, get_grades=True, use_offline=use_offline)
|
||||
datatable['title'] = _('Summary Grades of students enrolled in {course_key}').format(course_key=course_key.to_deprecated_string())
|
||||
track.views.server_track(request, "dump-grades", {}, page="idashboard")
|
||||
|
||||
elif 'Dump all RAW grades' in action:
|
||||
log.debug(action)
|
||||
datatable = get_student_grade_summary_data(request, course, course_key, get_grades=True,
|
||||
datatable = get_student_grade_summary_data(request, course, get_grades=True,
|
||||
get_raw_scores=True, use_offline=use_offline)
|
||||
datatable['title'] = _('Raw Grades of students enrolled in {course_key}').format(course_key=course_key)
|
||||
track.views.server_track(request, "dump-grades-raw", {}, page="idashboard")
|
||||
@@ -246,12 +246,12 @@ def instructor_dashboard(request, course_id):
|
||||
elif 'Download CSV of all student grades' in action:
|
||||
track.views.server_track(request, "dump-grades-csv", {}, page="idashboard")
|
||||
return return_csv('grades_{0}.csv'.format(course_key.to_deprecated_string()),
|
||||
get_student_grade_summary_data(request, course, course_key, use_offline=use_offline))
|
||||
get_student_grade_summary_data(request, course, use_offline=use_offline))
|
||||
|
||||
elif 'Download CSV of all RAW grades' in action:
|
||||
track.views.server_track(request, "dump-grades-csv-raw", {}, page="idashboard")
|
||||
return return_csv('grades_{0}_raw.csv'.format(course_key.to_deprecated_string()),
|
||||
get_student_grade_summary_data(request, course, course_key, get_raw_scores=True, use_offline=use_offline))
|
||||
get_student_grade_summary_data(request, course, get_raw_scores=True, use_offline=use_offline))
|
||||
|
||||
elif 'Download CSV of answer distributions' in action:
|
||||
track.views.server_track(request, "dump-answer-dist-csv", {}, page="idashboard")
|
||||
@@ -539,7 +539,7 @@ def instructor_dashboard(request, course_id):
|
||||
|
||||
elif action == 'List assignments available for this course':
|
||||
log.debug(action)
|
||||
allgrades = get_student_grade_summary_data(request, course, course_key, get_grades=True, use_offline=use_offline)
|
||||
allgrades = get_student_grade_summary_data(request, course, get_grades=True, use_offline=use_offline)
|
||||
|
||||
assignments = [[x] for x in allgrades['assignments']]
|
||||
datatable = {'header': [_('Assignment Name')]}
|
||||
@@ -549,7 +549,7 @@ def instructor_dashboard(request, course_id):
|
||||
msg += 'assignments=<pre>%s</pre>' % assignments
|
||||
|
||||
elif action == 'List enrolled students matching remote gradebook':
|
||||
stud_data = get_student_grade_summary_data(request, course, course_key, get_grades=False, use_offline=use_offline)
|
||||
stud_data = get_student_grade_summary_data(request, course, get_grades=False, use_offline=use_offline)
|
||||
msg2, rg_stud_data = _do_remote_gradebook(request.user, course, 'get-membership')
|
||||
datatable = {'header': ['Student email', 'Match?']}
|
||||
rg_students = [x['email'] for x in rg_stud_data['retdata']]
|
||||
@@ -568,7 +568,7 @@ def instructor_dashboard(request, course_id):
|
||||
if not aname:
|
||||
msg += "<font color='red'>{text}</font>".format(text=_("Please enter an assignment name"))
|
||||
else:
|
||||
allgrades = get_student_grade_summary_data(request, course, course_key, get_grades=True, use_offline=use_offline)
|
||||
allgrades = get_student_grade_summary_data(request, course, get_grades=True, use_offline=use_offline)
|
||||
if aname not in allgrades['assignments']:
|
||||
msg += "<font color='red'>{text}</font>".format(
|
||||
text=_("Invalid assignment name '{name}'").format(name=aname)
|
||||
@@ -1354,8 +1354,8 @@ class GradeTable(object):
|
||||
return self.components.keys()
|
||||
|
||||
|
||||
def get_student_grade_summary_data(request, course, course_key, get_grades=True, get_raw_scores=False, use_offline=False):
|
||||
'''
|
||||
def get_student_grade_summary_data(request, course, get_grades=True, get_raw_scores=False, use_offline=False):
|
||||
"""
|
||||
Return data arrays with student identity and grades for specified course.
|
||||
|
||||
course = CourseDescriptor
|
||||
@@ -1370,8 +1370,8 @@ def get_student_grade_summary_data(request, course, course_key, get_grades=True,
|
||||
data = list (one per student) of lists of data corresponding to the fields
|
||||
|
||||
If get_raw_scores=True, then instead of grade summaries, the raw grades for all graded modules are returned.
|
||||
|
||||
'''
|
||||
"""
|
||||
course_key = course.id
|
||||
enrolled_students = User.objects.filter(
|
||||
courseenrollment__course_id=course_key,
|
||||
courseenrollment__is_active=1,
|
||||
|
||||
Reference in New Issue
Block a user