Commit fef004f4 by Bridger Maxwell

Changed certificate schema to allow for pre-generated certificates.

parent b05271c9
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'GeneratedCertificate.enabled'
db.add_column('certificates_generatedcertificate', 'enabled',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting field 'GeneratedCertificate.enabled'
db.delete_column('certificates_generatedcertificate', 'enabled')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
},
'certificates.generatedcertificate': {
'Meta': {'object_name': 'GeneratedCertificate'},
'certificate_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
complete_apps = ['certificates']
\ No newline at end of file
...@@ -3,6 +3,8 @@ from django.db import models ...@@ -3,6 +3,8 @@ from django.db import models
''' '''
Certificates are created for a student and an offering of a course.
When a certificate is generated, a unique ID is generated so that When a certificate is generated, a unique ID is generated so that
the certificate can be verified later. The ID is a UUID4, so that the certificate can be verified later. The ID is a UUID4, so that
it can't be easily guessed and so that it is unique. Even though it can't be easily guessed and so that it is unique. Even though
...@@ -10,7 +12,15 @@ we save these generated certificates (for later verification), we ...@@ -10,7 +12,15 @@ we save these generated certificates (for later verification), we
also record the UUID so that if we regenerate the certificate it also record the UUID so that if we regenerate the certificate it
will have the same UUID. will have the same UUID.
Certificates are created for a student and an offering of a course. 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 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.
''' '''
...@@ -18,4 +28,8 @@ class GeneratedCertificate(models.Model): ...@@ -18,4 +28,8 @@ class GeneratedCertificate(models.Model):
user = models.ForeignKey(User, db_index=True) user = models.ForeignKey(User, db_index=True)
certificate_id = models.CharField(max_length=32) certificate_id = models.CharField(max_length=32)
download_url = models.CharField(max_length=128, null=True) download_url = models.CharField(max_length=128, null=True)
\ No newline at end of file
# 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 = models.BooleanField(default=False)
\ No newline at end of file
...@@ -50,16 +50,20 @@ def certificate_request(request): ...@@ -50,16 +50,20 @@ def certificate_request(request):
'error': error })) 'error': error }))
# TODO: This method should be executed as an asynchronous task # This method should only be called if the user has a grade and has requested a certificate
def generate_certificate(user, grade, destination_email): def generate_certificate(user, grade, destination_email):
# Make sure to see the comments in models.GeneratedCertificate to read about the valid
# states for a GeneratedCertificate object
generated_certificate = None generated_certificate = None
try: try:
generated_certificate = GeneratedCertificate.objects.get(user = user) generated_certificate = GeneratedCertificate.objects.get(user = user)
except GeneratedCertificate.DoesNotExist: except GeneratedCertificate.DoesNotExist:
generated_certificate = GeneratedCertificate(user = user, certificate_id = uuid.uuid4().hex) generated_certificate = GeneratedCertificate(user = user, certificate_id = uuid.uuid4().hex)
generated_certificate.save()
generated_certificate.enabled = True
generated_certificate.save()
certificate_id = generated_certificate.certificate_id certificate_id = generated_certificate.certificate_id
log.debug("Generating certificate for " + str(user.username) + " with ID: " + certificate_id) log.debug("Generating certificate for " + str(user.username) + " with ID: " + certificate_id)
......
...@@ -88,15 +88,19 @@ def profile(request, student_id = None): ...@@ -88,15 +88,19 @@ def profile(request, student_id = None):
if settings.END_COURSE_ENABLED: if settings.END_COURSE_ENABLED:
took_survey = student_took_survey(user_info) took_survey = student_took_survey(user_info)
generated_certificate = None # certificate_requested determines if the student has requested a certificate
certificate_download_url = None
certificate_requested = False certificate_requested = False
# certificate_download_url determines if the certificate has been generated
certificate_download_url = None
if grade_sheet['grade']: if grade_sheet['grade']:
try: try:
generated_certificate = GeneratedCertificate.objects.get(user = student) generated_certificate = GeneratedCertificate.objects.get(user = student)
certificate_requested = True #If enabled=False, it may have been pre-generated but not yet requested
certificate_download_url = generated_certificate.download_url if generated_certificate.enabled:
certificate_requested = True
certificate_download_url = generated_certificate.download_url
except GeneratedCertificate.DoesNotExist: except GeneratedCertificate.DoesNotExist:
#They haven't submited the request form #They haven't submited the request form
certificate_requested = False certificate_requested = False
......
...@@ -156,6 +156,7 @@ $(function() { ...@@ -156,6 +156,7 @@ $(function() {
} }
$("#cert_request").html("<h1>Certificate Request Received</h1><p>A certificate will be sent to the specified email in a short time.</p>"); $("#cert_request").html("<h1>Certificate Request Received</h1><p>A certificate will be sent to the specified email in a short time.</p>");
$(".cert_request_link").text("Certificate is being generated...");
} else { } else {
$("#cert_request_error").html(data.error); $("#cert_request_error").html(data.error);
} }
...@@ -216,7 +217,7 @@ $(function() { ...@@ -216,7 +217,7 @@ $(function() {
%if not certificate_requested: %if not certificate_requested:
<a rel="leanModal" class="cert_request_link" href="#cert_request">Request Certificate</a> <a rel="leanModal" class="cert_request_link" href="#cert_request">Request Certificate</a>
%elif certificate_download_url: %elif certificate_download_url:
<a href="${certificate_download_url}">Download Certificate</a> <a href="${certificate_download_url}" target="_blank">Download Certificate</a>
%else: %else:
<a href="#">Certificate is being generated...</a> <a href="#">Certificate is being generated...</a>
%endif %endif
......
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