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.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
"""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
|
||||
class Command(BaseCommand):
|
||||
|
||||
help = """
|
||||
Find all students that need certificates
|
||||
for courses that have finished and
|
||||
put their cert requests on the queue
|
||||
Find all students that need certificates for courses that have finished and
|
||||
put their cert requests on the queue.
|
||||
|
||||
Use the --noop option to test without actually
|
||||
putting certificates on the queue to be generated.
|
||||
If --user is given, only grade and certify the requested username.
|
||||
|
||||
Use the --noop option to test without actually putting certificates on the
|
||||
queue to be generated.
|
||||
"""
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
@@ -80,6 +81,7 @@ class Command(BaseCommand):
|
||||
enrolled_students = User.objects.filter(
|
||||
courseenrollment__course_id=course_id).prefetch_related(
|
||||
"groups").order_by('username')
|
||||
|
||||
xq = XQueueCertInterface()
|
||||
total = enrolled_students.count()
|
||||
count = 0
|
||||
|
||||
@@ -52,15 +52,15 @@ Eligibility:
|
||||
|
||||
|
||||
class CertificateStatuses(object):
|
||||
unavailable = 'unavailable'
|
||||
generating = 'generating'
|
||||
regenerating = 'regenerating'
|
||||
deleting = 'deleting'
|
||||
deleted = 'deleted'
|
||||
deleted = 'deleted'
|
||||
deleting = 'deleting'
|
||||
downloadable = 'downloadable'
|
||||
notpassing = 'notpassing'
|
||||
restricted = 'restricted'
|
||||
error = 'error'
|
||||
error = 'error'
|
||||
generating = 'generating'
|
||||
notpassing = 'notpassing'
|
||||
regenerating = 'regenerating'
|
||||
restricted = 'restricted'
|
||||
unavailable = 'unavailable'
|
||||
|
||||
|
||||
class CertificateWhitelist(models.Model):
|
||||
|
||||
@@ -75,28 +75,34 @@ class XQueueCertInterface(object):
|
||||
self.whitelist = CertificateWhitelist.objects.all()
|
||||
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:
|
||||
student - User.object
|
||||
student - User.object
|
||||
course_id - courseenrollment.course_id (string)
|
||||
|
||||
Removes certificate for a student, will change
|
||||
the certificate status to 'regenerating'.
|
||||
WARNING: this command will leave the old certificate, if one exists,
|
||||
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
|
||||
# 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
|
||||
|
||||
raise NotImplementedError
|
||||
return self.add_cert(student, course_id, course)
|
||||
|
||||
def del_cert(self, student, course_id):
|
||||
|
||||
@@ -134,7 +140,6 @@ class XQueueCertInterface(object):
|
||||
If a student has allow_certificate set to False in the
|
||||
userprofile table the status will change to 'restricted'
|
||||
|
||||
|
||||
If a student does not have a passing grade the status
|
||||
will change to status.notpassing
|
||||
|
||||
@@ -143,11 +148,12 @@ class XQueueCertInterface(object):
|
||||
"""
|
||||
|
||||
VALID_STATUSES = [status.generating,
|
||||
status.unavailable, status.deleted, status.error,
|
||||
status.notpassing]
|
||||
status.unavailable,
|
||||
status.deleted,
|
||||
status.error,
|
||||
status.notpassing]
|
||||
|
||||
cert_status = certificate_status_for_student(
|
||||
student, course_id)['status']
|
||||
cert_status = certificate_status_for_student(student, course_id)['status']
|
||||
|
||||
if cert_status in VALID_STATUSES:
|
||||
# grade the student
|
||||
|
||||
Reference in New Issue
Block a user