Updating the certificate app for testing
Conflicts: lms/djangoapps/certificates/models.py
This commit is contained in:
committed by
Carlos Andrés Rocha
parent
d636d729c6
commit
22ed22d956
@@ -1,22 +1,71 @@
|
||||
from django.utils.simplejson import dumps
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from certificates.models import GeneratedCertificate
|
||||
from courseware import grades, courses
|
||||
from django.contrib.auth.models import User
|
||||
from django.test.client import RequestFactory
|
||||
from profilehooks import profile
|
||||
import cProfile
|
||||
from pprint import pprint
|
||||
from capa.xqueue_interface import XQueueInterface
|
||||
from capa.xqueue_interface import make_xheader
|
||||
from django.conf import settings
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
help = """
|
||||
This command finds all GeneratedCertificate objects that do not have a
|
||||
certificate generated. These come into being when a user requests a
|
||||
certificate, or when grade_all_students is called (for pre-generating
|
||||
certificates).
|
||||
|
||||
This command finds all users that have not been graded
|
||||
for a single course.
|
||||
It returns a json formatted list of users and their user ids
|
||||
"""
|
||||
|
||||
# @profile
|
||||
def _grade(self,student, request, course):
|
||||
grades.grade(student, request, course)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
users = GeneratedCertificate.objects.filter(
|
||||
download_url=None)
|
||||
user_output = [{'user_id':user.user_id, 'name':user.name}
|
||||
for user in users]
|
||||
self.stdout.write(dumps(user_output) + "\n")
|
||||
factory = RequestFactory()
|
||||
course_id = 'BerkeleyX/CS169.1x/2012_Fall'
|
||||
course = courses.get_course_by_id(course_id)
|
||||
if settings.XQUEUE_INTERFACE.get('basic_auth') is not None:
|
||||
requests_auth = HTTPBasicAuth(
|
||||
*settings.XQUEUE_INTERFACE['basic_auth'])
|
||||
else:
|
||||
requests_auth = None
|
||||
|
||||
xqueue_interface = XQueueInterface(
|
||||
settings.XQUEUE_INTERFACE['url'],
|
||||
settings.XQUEUE_INTERFACE['django_auth'],
|
||||
requests_auth,
|
||||
)
|
||||
|
||||
header = make_xheader('/certificate', 'foo', 'test-pull')
|
||||
print "Sending test message to queue"
|
||||
xqueue_interface.send_to_queue(header, { 'test': 'foo' })
|
||||
|
||||
#enrolled_students = User.objects.filter(
|
||||
# courseenrollment__course_id=course_id).prefetch_related(
|
||||
# "groups").order_by('username')
|
||||
#generated_certificates = GeneratedCertificate.objects.filter(
|
||||
# course_id=course_id)
|
||||
#request = factory.get('/')
|
||||
#student = User.objects.get(username='03199618')
|
||||
#print "total students: {0}".format(len(enrolled_students))
|
||||
#count = 0
|
||||
#for student in enrolled_students:
|
||||
# count += 1
|
||||
# if count % 1000 == 0:
|
||||
# print "{0}/{1}".format(count, len(enrolled_students))
|
||||
# grades.grade(student, request, course)
|
||||
|
||||
#for student in enrolled_students:
|
||||
# g = grades.grade(student, request, course)
|
||||
# if g['grade'] is not None:
|
||||
# print str(student)
|
||||
# pprint(g)
|
||||
# break
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from django.conf import settings as settings
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
|
||||
@@ -14,131 +12,66 @@ we save these generated certificates (for later verification), we
|
||||
also record the UUID so that if we regenerate the certificate it
|
||||
will have the same UUID.
|
||||
|
||||
If certificates are being generated on the fly, a GeneratedCertificate
|
||||
should be created with the user, certificate_id, and enabled set
|
||||
when a student requests a certificate. When the certificate has been
|
||||
generated, the download_url should be set.
|
||||
Certificates are generated in batches by a cron job, when a
|
||||
certificate is available for download the GeneratedCertificate
|
||||
table is updated with information that will be displayed
|
||||
on the course overview page.
|
||||
|
||||
Certificates can also be pre-generated. In this case, the user,
|
||||
certificate_id, and download_url are all set before the user does
|
||||
anything. When the user requests the certificate, only enabled
|
||||
needs to be set to true.
|
||||
|
||||
'''
|
||||
|
||||
|
||||
class GeneratedCertificate(models.Model):
|
||||
user = models.ForeignKey(User, db_index=True)
|
||||
# This is the name at the time of request
|
||||
name = models.CharField(blank=True, max_length=255)
|
||||
user = models.ForeignKey(User)
|
||||
course_id = models.CharField(max_length=255, default=False)
|
||||
certificate_id = models.CharField(max_length=32, default=False)
|
||||
graded_certificate_id = models.CharField(max_length=32, default=False)
|
||||
|
||||
certificate_id = models.CharField(max_length=32, null=True, default=None)
|
||||
graded_certificate_id = models.CharField(max_length=32, null=True, default=None)
|
||||
download_url = models.CharField(max_length=128, default=False)
|
||||
graded_download_url = models.CharField(max_length=128, default=False)
|
||||
grade = models.CharField(max_length=5, default=False)
|
||||
key = models.CharField(max_length=32, default=False)
|
||||
|
||||
download_url = models.CharField(max_length=128, null=True)
|
||||
graded_download_url = models.CharField(max_length=128, null=True)
|
||||
|
||||
grade = models.CharField(max_length=5, null=True)
|
||||
|
||||
# enabled should only be true if the student has earned a grade in the course
|
||||
# The student must have a grade and request a certificate for enabled to be True
|
||||
# enabled should only be true if the student has earned a passing grade
|
||||
# in the course.
|
||||
enabled = models.BooleanField(default=False)
|
||||
|
||||
|
||||
class RevokedCertificate(models.Model):
|
||||
"""
|
||||
This model is for when a GeneratedCertificate must be regenerated. This model
|
||||
contains all the same fields, to store a record of what the GeneratedCertificate
|
||||
was before it was revoked (at which time all of it's information can change when
|
||||
it is regenerated).
|
||||
|
||||
GeneratedCertificate may be deleted once they are revoked, and then created again.
|
||||
For this reason, the only link between a GeneratedCertificate and RevokedCertificate
|
||||
is that they share the same user.
|
||||
"""
|
||||
####-------------------New Fields--------------------####
|
||||
explanation = models.TextField(blank=True)
|
||||
|
||||
####---------Fields from GeneratedCertificate---------####
|
||||
user = models.ForeignKey(User, db_index=True)
|
||||
# This is the name at the time of request
|
||||
name = models.CharField(blank=True, max_length=255)
|
||||
|
||||
certificate_id = models.CharField(max_length=32, null=True, default=None)
|
||||
graded_certificate_id = models.CharField(max_length=32, null=True, default=None)
|
||||
|
||||
download_url = models.CharField(max_length=128, null=True)
|
||||
graded_download_url = models.CharField(max_length=128, null=True)
|
||||
|
||||
grade = models.CharField(max_length=5, null=True)
|
||||
|
||||
enabled = models.BooleanField(default=False)
|
||||
|
||||
|
||||
def revoke_certificate(certificate, explanation):
|
||||
"""
|
||||
This method takes a GeneratedCertificate. It records its information from the certificate
|
||||
into a RevokedCertificate, and then marks the certificate as needing regenerating.
|
||||
When the new certificiate is regenerated it will have new IDs and download URLS.
|
||||
|
||||
Once this method has been called, it is safe to delete the certificate, or modify the
|
||||
certificate's name or grade until it has been generated again.
|
||||
"""
|
||||
revoked = RevokedCertificate(user=certificate.user,
|
||||
name=certificate.name,
|
||||
certificate_id=certificate.certificate_id,
|
||||
graded_certificate_id=certificate.graded_certificate_id,
|
||||
download_url=certificate.download_url,
|
||||
graded_download_url=certificate.graded_download_url,
|
||||
grade=certificate.grade,
|
||||
enabled=certificate.enabled)
|
||||
|
||||
revoked.explanation = explanation
|
||||
|
||||
certificate.certificate_id = None
|
||||
certificate.graded_certificate_id = None
|
||||
certificate.download_url = None
|
||||
certificate.graded_download_url = None
|
||||
|
||||
certificate.save()
|
||||
revoked.save()
|
||||
|
||||
|
||||
def certificate_state_for_student(student, grade):
|
||||
def certificate_state_for_student(student):
|
||||
'''
|
||||
This returns a dictionary with a key for state, and other information. The state is one of the
|
||||
following:
|
||||
This returns a dictionary with a key for state, and other information.
|
||||
The state is one of the following:
|
||||
|
||||
unavailable - A student is not eligible for a certificate.
|
||||
requestable - A student is eligible to request a certificate
|
||||
generating - A student has requested a certificate, but it is not generated yet.
|
||||
downloadable - The certificate has been requested and is available for download.
|
||||
unavailable - A student is not eligible for a certificate.
|
||||
generating - A student has requested a certificate,
|
||||
but it is not generated yet.
|
||||
downloadable - The certificate has been requested and is
|
||||
available for download.
|
||||
|
||||
If the state is "downloadable", the dictionary also contains "download_url" and "graded_download_url".
|
||||
If the state is "downloadable", the dictionary also contains
|
||||
"download_url" and "graded_download_url".
|
||||
|
||||
'''
|
||||
|
||||
if grade:
|
||||
#TODO: Remove the following after debugging
|
||||
if settings.DEBUG_SURVEY:
|
||||
return {'state': 'requestable'}
|
||||
|
||||
try:
|
||||
generated_certificate = GeneratedCertificate.objects.get(user=student)
|
||||
if generated_certificate.enabled:
|
||||
if generated_certificate.download_url:
|
||||
return {'state': 'downloadable',
|
||||
'download_url': generated_certificate.download_url,
|
||||
'graded_download_url': generated_certificate.graded_download_url}
|
||||
else:
|
||||
return {'state': 'generating'}
|
||||
try:
|
||||
generated_certificate = GeneratedCertificate.objects.get(
|
||||
user=student)
|
||||
if generated_certificate.enabled:
|
||||
if generated_certificate.download_url:
|
||||
return {
|
||||
'state': 'downloadable',
|
||||
'download_url':
|
||||
generated_certificate.download_url,
|
||||
'graded_download_url':
|
||||
generated_certificate.graded_download_url
|
||||
}
|
||||
else:
|
||||
# If enabled=False, it may have been pre-generated but not yet requested
|
||||
# Our output will be the same as if the GeneratedCertificate did not exist
|
||||
pass
|
||||
except GeneratedCertificate.DoesNotExist:
|
||||
return {'state': 'generating'}
|
||||
else:
|
||||
# If enabled=False, there is no certificate available
|
||||
# Our output will be the same as if the
|
||||
# GeneratedCertificate did not exist
|
||||
pass
|
||||
return {'state': 'requestable'}
|
||||
else:
|
||||
# No grade, no certificate. No exceptions
|
||||
return {'state': 'unavailable'}
|
||||
except GeneratedCertificate.DoesNotExist:
|
||||
pass
|
||||
return {'state': 'unavailable'}
|
||||
|
||||
@@ -1,146 +1,31 @@
|
||||
import json
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.mail import send_mail
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import redirect
|
||||
|
||||
import courseware.grades as grades
|
||||
from certificates.models import GeneratedCertificate, certificate_state_for_student, revoke_certificate
|
||||
from mitxmako.shortcuts import render_to_response, render_to_string
|
||||
from student.models import UserProfile
|
||||
#TODO: Finish migrating these changes from stable
|
||||
# from student.survey_questions import exit_survey_list_for_student
|
||||
# from student.views import student_took_survey, record_exit_survey
|
||||
from certificates.models import GeneratedCertificate
|
||||
from pprint import pprint
|
||||
|
||||
log = logging.getLogger("mitx.certificates")
|
||||
|
||||
|
||||
@login_required
|
||||
def certificate_request(request):
|
||||
''' Attempt to send a certificate. '''
|
||||
if not settings.END_COURSE_ENABLED:
|
||||
raise Http404
|
||||
def update_certificate(request):
|
||||
"""
|
||||
Will update GeneratedCertificate for a new certificate or
|
||||
modify an existing certificate entry.
|
||||
"""
|
||||
|
||||
if request.method == "POST":
|
||||
honor_code_verify = request.POST.get('cert_request_honor_code_verify', 'false')
|
||||
name_verify = request.POST.get('cert_request_name_verify', 'false')
|
||||
id_verify = request.POST.get('cert_request_id_verify', 'false')
|
||||
error = ''
|
||||
|
||||
def return_error(error):
|
||||
return HttpResponse(json.dumps({'success': False,
|
||||
'error': error}))
|
||||
|
||||
if honor_code_verify != 'true':
|
||||
error += 'Please verify that you have followed the honor code to receive a certificate. '
|
||||
|
||||
if name_verify != 'true':
|
||||
error += 'Please verify that your name is correct to receive a certificate. '
|
||||
|
||||
if id_verify != 'true':
|
||||
error += 'Please certify that you understand the unique ID on the certificate. '
|
||||
|
||||
if len(error) > 0:
|
||||
return return_error(error)
|
||||
|
||||
survey_response = record_exit_survey(request, internal_request=True)
|
||||
if not survey_response['success']:
|
||||
return return_error(survey_response['error'])
|
||||
|
||||
grade = None
|
||||
student_gradesheet = grades.grade(request.user, request, course)
|
||||
grade = student_gradesheet['grade']
|
||||
|
||||
if not grade:
|
||||
return return_error('You have not earned a grade in this course. ')
|
||||
|
||||
generate_certificate(request.user, grade)
|
||||
|
||||
return HttpResponse(json.dumps({'success': True}))
|
||||
|
||||
else:
|
||||
#This is not a POST, we should render the page with the form
|
||||
|
||||
student_gradesheet = grades.grade(request.user, request, course)
|
||||
certificate_state = certificate_state_for_student(request.user, grade_sheet['grade'])
|
||||
|
||||
if certificate_state['state'] != "requestable":
|
||||
return redirect("/profile")
|
||||
|
||||
user_info = UserProfile.objects.get(user=request.user)
|
||||
|
||||
took_survey = student_took_survey(user_info)
|
||||
if settings.DEBUG_SURVEY:
|
||||
took_survey = False
|
||||
survey_list = []
|
||||
if not took_survey:
|
||||
survey_list = exit_survey_list_for_student(request.user)
|
||||
|
||||
context = {'certificate_state': certificate_state,
|
||||
'took_survey': took_survey,
|
||||
'survey_list': survey_list,
|
||||
'name': user_info.name}
|
||||
|
||||
return render_to_response('cert_request.html', context)
|
||||
|
||||
|
||||
# This method should only be called if the user has a grade and has requested a certificate
|
||||
def generate_certificate(user, grade):
|
||||
# Make sure to see the comments in models.GeneratedCertificate to read about the valid
|
||||
# states for a GeneratedCertificate object
|
||||
if grade and user.is_active:
|
||||
generated_certificate = None
|
||||
|
||||
try:
|
||||
generated_certificate = GeneratedCertificate.objects.get(user=user)
|
||||
except GeneratedCertificate.DoesNotExist:
|
||||
generated_certificate = GeneratedCertificate(user=user)
|
||||
|
||||
generated_certificate.enabled = True
|
||||
if generated_certificate.graded_download_url and (generated_certificate.grade != grade):
|
||||
log.critical(u"A graded certificate has been pre-generated with the grade "
|
||||
"of {gen_grade} but requested by user id {userid} with grade "
|
||||
"{req_grade}! The download URLs were {graded_dl_url} and "
|
||||
"{ungraded_dl_url}".format(
|
||||
gen_grade=generated_certificate.grade,
|
||||
req_grade=grade,
|
||||
graded_dl_url=generated_certificate.graded_download_url,
|
||||
ungraded_dl_url=generated_certificate.download_url,
|
||||
userid=user.id))
|
||||
revoke_certificate(generated_certificate, "The grade on this certificate may be inaccurate.")
|
||||
|
||||
user_name = UserProfile.objects.get(user=user).name
|
||||
if generated_certificate.download_url and (generated_certificate.name != user_name):
|
||||
log.critical(u"A Certificate has been pre-generated with the name of "
|
||||
"{gen_name} but current name is {user_name} (user id is "
|
||||
"{userid})! The download URLs were {graded_dl_url} and "
|
||||
"{ungraded_dl_url}".format(
|
||||
gen_name=generated_certificate.name.encode('utf-8'),
|
||||
user_name=user_name.encode('utf-8'),
|
||||
graded_dl_url=generated_certificate.graded_download_url,
|
||||
ungraded_dl_url=generated_certificate.download_url,
|
||||
userid=user.id))
|
||||
revoke_certificate(generated_certificate, "The name on this certificate may be inaccurate.")
|
||||
|
||||
generated_certificate.grade = grade
|
||||
generated_certificate.name = user_name
|
||||
generated_certificate.save()
|
||||
|
||||
certificate_id = generated_certificate.certificate_id
|
||||
|
||||
log.debug("Generating certificate for " + str(user.username) + " with ID: " + str(certificate_id))
|
||||
|
||||
# TODO: If the certificate was pre-generated, send the email that it is ready to download
|
||||
if certificate_state_for_student(user, grade)['state'] == "downloadable":
|
||||
subject = render_to_string('emails/certificate_ready_subject.txt', {})
|
||||
subject = ''.join(subject.splitlines())
|
||||
message = render_to_string('emails/certificate_ready.txt', {})
|
||||
|
||||
res = send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email, ])
|
||||
|
||||
else:
|
||||
log.warning("Asked to generate a certificate for student " + str(user.username) + " but with a grade of " + str(grade) + " and active status " + str(user.is_active))
|
||||
pprint(request)
|
||||
# user = request.POST.get('user')
|
||||
# try:
|
||||
# generated_certificate = GeneratedCertificate.objects.get(
|
||||
# key=key)
|
||||
# except GeneratedCertificate.DoesNotExist:
|
||||
# generated_certificate = GeneratedCertificate(user=user)
|
||||
#
|
||||
# enabled = request.POST.get('enabled')
|
||||
# enabled = True if enabled == 'True' else False
|
||||
# generated_certificate.grade = request.POST.get('grade')
|
||||
# generated_certificate.download_url = request.POST.get('download_url')
|
||||
# generated_certificate.graded_download_url = request.POST.get(
|
||||
# 'graded_download_url')
|
||||
# generated_certificate.course_id = request.POST.get('course_id')
|
||||
# generated_certificate.enabled = enabled
|
||||
# generated_certificate.save()
|
||||
|
||||
Reference in New Issue
Block a user