Commit 9e76c612 by Joe Blaylock

Merge pull request #1286 from edx/jrbl/certs-single-user

Certificates: Add regeneration command
parents b00d59ed 8ef8fc82
"""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'.
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
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.
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.
"""
raise NotImplementedError
# 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
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
......
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