Commit 8ef8fc82 by Joe Blaylock

Certificates: Add regeneration command

* Adds regenerate_user command, which lets you run certificates for one
  particular user in one particular class, regardless of whether they
  have a pre-existing certificate.
parent ae8c9710
"""Django management command to force certificate regeneration for one user"""
from optparse import make_option
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError
from certificates.queue import XQueueCertInterface
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.django import modulestore
class Command(BaseCommand):
help = """Put a request on the queue to recreate the certificate for a particular user in a particular course."""
option_list = BaseCommand.option_list + (
make_option('-n', '--noop',
action='store_true',
dest='noop',
default=False,
help="Don't grade or add certificate requests to the queue"),
make_option('-c', '--course',
metavar='COURSE_ID',
dest='course',
default=False,
help='The course id (e.g., mit/6-002x/circuits-and-electronics) for which the student named in'
'<username> should be graded'),
make_option('-u', '--user',
metavar='USERNAME',
dest='username',
default=False,
help='The username or email address for whom grading and certification should be requested'),
)
def handle(self, *args, **options):
user = options['username']
course_id = options['course']
if not (course_id and user):
raise CommandError('both course id and student username are required')
student = None
print "Fetching enrollment for student {0} in {1}".format(user, course_id)
if '@' in user:
student = User.objects.get(email=user, courseenrollment__course_id=course_id)
else:
student = User.objects.get(username=user, courseenrollment__course_id=course_id)
print "Fetching course data for {0}".format(course_id)
course = modulestore().get_instance(course_id, CourseDescriptor.id_to_location(course_id), depth=2)
if not options['noop']:
# Add the certificate request to the queue
xq = XQueueCertInterface()
ret = xq.regen_cert(student, course_id, course=course)
print '{0} - {1}'.format(student, ret)
else:
print "noop option given, skipping work queueing..."
...@@ -14,12 +14,13 @@ from pytz import UTC ...@@ -14,12 +14,13 @@ from pytz import UTC
class Command(BaseCommand): class Command(BaseCommand):
help = """ help = """
Find all students that need certificates Find all students that need certificates for courses that have finished and
for courses that have finished and put their cert requests on the queue.
put their cert requests on the queue
Use the --noop option to test without actually If --user is given, only grade and certify the requested username.
putting certificates on the queue to be generated.
Use the --noop option to test without actually putting certificates on the
queue to be generated.
""" """
option_list = BaseCommand.option_list + ( option_list = BaseCommand.option_list + (
...@@ -80,6 +81,7 @@ class Command(BaseCommand): ...@@ -80,6 +81,7 @@ class Command(BaseCommand):
enrolled_students = User.objects.filter( enrolled_students = User.objects.filter(
courseenrollment__course_id=course_id).prefetch_related( courseenrollment__course_id=course_id).prefetch_related(
"groups").order_by('username') "groups").order_by('username')
xq = XQueueCertInterface() xq = XQueueCertInterface()
total = enrolled_students.count() total = enrolled_students.count()
count = 0 count = 0
......
...@@ -52,15 +52,15 @@ Eligibility: ...@@ -52,15 +52,15 @@ Eligibility:
class CertificateStatuses(object): class CertificateStatuses(object):
unavailable = 'unavailable' deleted = 'deleted'
generating = 'generating' deleting = 'deleting'
regenerating = 'regenerating'
deleting = 'deleting'
deleted = 'deleted'
downloadable = 'downloadable' downloadable = 'downloadable'
notpassing = 'notpassing' error = 'error'
restricted = 'restricted' generating = 'generating'
error = 'error' notpassing = 'notpassing'
regenerating = 'regenerating'
restricted = 'restricted'
unavailable = 'unavailable'
class CertificateWhitelist(models.Model): class CertificateWhitelist(models.Model):
......
...@@ -75,28 +75,34 @@ class XQueueCertInterface(object): ...@@ -75,28 +75,34 @@ class XQueueCertInterface(object):
self.whitelist = CertificateWhitelist.objects.all() self.whitelist = CertificateWhitelist.objects.all()
self.restricted = UserProfile.objects.filter(allow_certificate=False) self.restricted = UserProfile.objects.filter(allow_certificate=False)
def regen_cert(self, student, course_id): def regen_cert(self, student, course_id, course=None):
""" """(Re-)Make certificate for a particular student in a particular course
Arguments: Arguments:
student - User.object student - User.object
course_id - courseenrollment.course_id (string) course_id - courseenrollment.course_id (string)
Removes certificate for a student, will change WARNING: this command will leave the old certificate, if one exists,
the certificate status to 'regenerating'. laying around in AWS taking up space. If this is a problem,
take pains to clean up storage before running this command.
Certificate must be in the 'error' or 'downloadable' state
If the student has a passing grade a certificate
request will be put on the queue
If the student is not passing his state will change
to status.notpassing
otherwise it will return the current state Change the certificate status to unavailable (if it exists) and request
grading. Passing grades will put a certificate request on the queue.
Return the status object.
""" """
# TODO: when del_cert is implemented and plumbed through certificates
raise NotImplementedError # repo also, do a deletion followed by a creation r/t a simple
# recreation. XXX: this leaves orphan cert files laying around in
# AWS. See note in the docstring too.
try:
certificate = GeneratedCertificate.objects.get(user=student, course_id=course_id)
certificate.status = status.unavailable
certificate.save()
except GeneratedCertificate.DoesNotExist:
pass
return self.add_cert(student, course_id, course)
def del_cert(self, student, course_id): def del_cert(self, student, course_id):
...@@ -134,7 +140,6 @@ class XQueueCertInterface(object): ...@@ -134,7 +140,6 @@ class XQueueCertInterface(object):
If a student has allow_certificate set to False in the If a student has allow_certificate set to False in the
userprofile table the status will change to 'restricted' userprofile table the status will change to 'restricted'
If a student does not have a passing grade the status If a student does not have a passing grade the status
will change to status.notpassing will change to status.notpassing
...@@ -143,11 +148,12 @@ class XQueueCertInterface(object): ...@@ -143,11 +148,12 @@ class XQueueCertInterface(object):
""" """
VALID_STATUSES = [status.generating, VALID_STATUSES = [status.generating,
status.unavailable, status.deleted, status.error, status.unavailable,
status.notpassing] status.deleted,
status.error,
status.notpassing]
cert_status = certificate_status_for_student( cert_status = certificate_status_for_student(student, course_id)['status']
student, course_id)['status']
if cert_status in VALID_STATUSES: if cert_status in VALID_STATUSES:
# grade the student # grade the student
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment