diff --git a/lms/djangoapps/certificates/management/commands/find_unicode_certs.py b/lms/djangoapps/certificates/management/commands/find_unicode_certs.py deleted file mode 100644 index 49497622c6..0000000000 --- a/lms/djangoapps/certificates/management/commands/find_unicode_certs.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.core.management.base import BaseCommand -from certificates.models import certificate_status_for_student -from certificates.queue import XQueueCertInterface -from django.contrib.auth.models import User -from student.models import UserProfile - - -class Command(BaseCommand): - - help = """ - Looks for names that have unicode characters - and queues them up for a certificate request - """ - - def handle(self, *args, **options): - - # TODO this is only temporary for CS169 certs - - course_id = 'BerkeleyX/CS169.1x/2012_Fall' - - enrolled_students = User.objects.filter( - courseenrollment__course_id=course_id).prefetch_related( - "groups").order_by('username') - xq = XQueueCertInterface() - print "Looking for unusual names.." - for student in enrolled_students: - if certificate_status_for_student( - student, course_id)['status'] == 'unavailable': - continue - name = UserProfile.objects.get(user=student).name - for c in name: - if ord(c) >= 0x200: - ret = xq.add_cert(student, course_id) - if ret == 'generating': - print 'generating for {0}'.format(student) - break diff --git a/lms/djangoapps/certificates/management/commands/gen_cert_report.py b/lms/djangoapps/certificates/management/commands/gen_cert_report.py new file mode 100644 index 0000000000..6c175dcc4c --- /dev/null +++ b/lms/djangoapps/certificates/management/commands/gen_cert_report.py @@ -0,0 +1,95 @@ +from django.core.management.base import BaseCommand +from certificates.models import certificate_status_for_student +from django.contrib.auth.models import User +from optparse import make_option +from django.conf import settings +from xmodule.course_module import CourseDescriptor +from xmodule.modulestore.django import modulestore +from collections import Counter + + +class Command(BaseCommand): + + help = """ + + Generate a certificate status report for all courses that have ended. + This command does not do anything other than report the current + certificate status. + + unavailable - A student is not eligible for a certificate. + generating - A request has been made to generate a certificate, + but it has not been generated yet. + regenerating - A request has been made to regenerate a certificate, + but it has not been generated yet. + deleting - A request has been made to delete a certificate. + + deleted - The certificate has been deleted. + downloadable - The certificate is available for download. + notpassing - The student was graded but is not passing + + """ + + option_list = BaseCommand.option_list + ( + make_option('-c', '--course', + metavar='COURSE_ID', + dest='course', + default=None, + help='Only generate for COURSE_ID'), + ) + + def handle(self, *args, **options): + + # Find all courses that have ended + + if options['course']: + ended_courses = [options['course']] + else: + ended_courses = [] + for course_id in [course # all courses in COURSE_LISTINGS + for sub in settings.COURSE_LISTINGS + for course in settings.COURSE_LISTINGS[sub]]: + + course_loc = CourseDescriptor.id_to_location(course_id) + course = modulestore().get_instance(course_id, course_loc) + if course.has_ended(): + ended_courses.append(course_id) + + total_enrolled = {} + cert_statuses = {} + + for course_id in ended_courses: + + # find students who are enrolled + print "Looking up certificate states for {0}".format(course_id) + enrolled_students = User.objects.filter( + courseenrollment__course_id=course_id).prefetch_related( + "groups").order_by('username') + total_enrolled[course_id] = enrolled_students.count() + + # tally up certificate statuses for every student + # enrolled in the course + cert_statuses[course_id] = Counter( + [certificate_status_for_student( + student, course_id)['status'] + for student in enrolled_students]) + + # all states we have seen far all courses + status_headings = set( + [status for course in cert_statuses + for status in cert_statuses[course]]) + + # print the heading for the report + print "{0:>20}{1:>10}".format("course ID", "enrolled"), + print ' '.join(["{:>12}".format(heading) + for heading in status_headings]) + + # print the report + for course_id in total_enrolled: + print "{0:>20}{1:>10}".format( + course_id[0:18], total_enrolled[course_id]), + for heading in status_headings: + if heading in cert_statuses[course_id]: + print "{:>12}".format(cert_statuses[course_id][heading]), + else: + print " " * 12, + print diff --git a/lms/djangoapps/certificates/management/commands/ungenerated_certs.py b/lms/djangoapps/certificates/management/commands/ungenerated_certs.py index 82e86c2097..080918c0cc 100644 --- a/lms/djangoapps/certificates/management/commands/ungenerated_certs.py +++ b/lms/djangoapps/certificates/management/commands/ungenerated_certs.py @@ -2,29 +2,92 @@ from django.core.management.base import BaseCommand from certificates.models import certificate_status_for_student from certificates.queue import XQueueCertInterface from django.contrib.auth.models import User +from optparse import make_option +from django.conf import settings +from xmodule.course_module import CourseDescriptor +from xmodule.modulestore.django import modulestore +from certificates.models import CertificateStatuses +import datetime class Command(BaseCommand): help = """ - Find all students that have need certificates - and put certificate requests on the queue + Find all students that need certificates + for courses that have finished and + put their cert requests on the queue - This is only for BerkeleyX/CS169.1x/2012_Fall + Use the --noop option to test without actually + putting certificates on the queue to be generated. """ + option_list = BaseCommand.option_list + ( + make_option('-n', '--noop', + action='store_true', + dest='noop', + default=False, + help="Don't add certificate requests to the queue"), + make_option('-c', '--course', + metavar='COURSE_ID', + dest='course', + default=False, + help='Grade and generate certificates for a specific course'), + + ) + def handle(self, *args, **options): - # TODO This is only temporary for CS169 certs + # Will only generate a certificate if the current + # status is in this state - course_id = 'BerkeleyX/CS169.1x/2012_Fall' - enrolled_students = User.objects.filter( - courseenrollment__course_id=course_id).prefetch_related( - "groups").order_by('username') - xq = XQueueCertInterface() - for student in enrolled_students: - if certificate_status_for_student( - student, course_id)['status'] == 'unavailable': - ret = xq.add_cert(student, course_id) - if ret == 'generating': - print 'generating for {0}'.format(student) + VALID_STATUSES = [ + CertificateStatuses.unavailable + ] + + # Print update after this many students + + STATUS_INTERVAL = 500 + + if options['course']: + ended_courses = [options['course']] + else: + # Find all courses that have ended + ended_courses = [] + for course_id in [course # all courses in COURSE_LISTINGS + for sub in settings.COURSE_LISTINGS + for course in settings.COURSE_LISTINGS[sub]]: + course_loc = CourseDescriptor.id_to_location(course_id) + course = modulestore().get_instance(course_id, course_loc) + if course.has_ended(): + ended_courses.append(course_id) + + for course_id in ended_courses: + print "Fetching enrolled students for {0}".format(course_id) + enrolled_students = User.objects.filter( + courseenrollment__course_id=course_id).prefetch_related( + "groups").order_by('username') + xq = XQueueCertInterface() + total = enrolled_students.count() + count = 0 + start = datetime.datetime.now() + for student in enrolled_students: + count += 1 + if count % STATUS_INTERVAL == 0: + # Print a status update with an approximation of + # how much time is left based on how long the last + # interval took + diff = datetime.datetime.now() - start + timeleft = diff * (total - count) / STATUS_INTERVAL + hours, remainder = divmod(timeleft.seconds, 3600) + minutes, seconds = divmod(remainder, 60) + print "{0}/{1} completed ~{2:02}:{3:02}m remaining".format( + count, total, hours, minutes) + start = datetime.datetime.now() + + if certificate_status_for_student( + student, course_id)['status'] in VALID_STATUSES: + if not options['noop']: + # Add the certificate request to the queue + ret = xq.add_cert(student, course_id) + if ret == 'generating': + print '{0} - {1}'.format(student, ret)