from django.conf import settings as settings from django.contrib.auth.models import User 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 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 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 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) 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 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) 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): ''' 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. 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'} 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: pass return {'state': 'requestable'} else: # No grade, no certificate. No exceptions return {'state': 'unavailable'}