diff --git a/common/djangoapps/student/management/commands/get_grades.py b/common/djangoapps/student/management/commands/get_grades.py index e24fef26e3..bbbbd3453d 100644 --- a/common/djangoapps/student/management/commands/get_grades.py +++ b/common/djangoapps/student/management/commands/get_grades.py @@ -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 diff --git a/lms/djangoapps/instructor/management/commands/compute_grades.py b/lms/djangoapps/instructor/management/commands/compute_grades.py index d1c66d51d2..41289f1a61 100644 --- a/lms/djangoapps/instructor/management/commands/compute_grades.py +++ b/lms/djangoapps/instructor/management/commands/compute_grades.py @@ -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) diff --git a/lms/djangoapps/instructor/management/commands/dump_grades.py b/lms/djangoapps/instructor/management/commands/dump_grades.py index 9da068cedf..7312de05a6 100644 --- a/lms/djangoapps/instructor/management/commands/dump_grades.py +++ b/lms/djangoapps/instructor/management/commands/dump_grades.py @@ -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'])) diff --git a/lms/djangoapps/instructor/offline_gradecalc.py b/lms/djangoapps/instructor/offline_gradecalc.py index 5b95899005..02b46534e4 100644 --- a/lms/djangoapps/instructor/offline_gradecalc.py +++ b/lms/djangoapps/instructor/offline_gradecalc.py @@ -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) diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py index 53c28793a5..4f0053b997 100644 --- a/lms/djangoapps/instructor/views/legacy.py +++ b/lms/djangoapps/instructor/views/legacy.py @@ -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=
%s' % 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 += "{text}".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 += "{text}".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,