From 22ed22d956cbd51332db89a45b4c96819d71cb23 Mon Sep 17 00:00:00 2001 From: John Jarvis Date: Tue, 30 Oct 2012 18:35:03 -0400 Subject: [PATCH] Updating the certificate app for testing Conflicts: lms/djangoapps/certificates/models.py --- .../management/commands/ungenerated_certs.py | 69 ++++++-- lms/djangoapps/certificates/models.py | 155 +++++------------ lms/djangoapps/certificates/views.py | 163 +++--------------- 3 files changed, 127 insertions(+), 260 deletions(-) diff --git a/lms/djangoapps/certificates/management/commands/ungenerated_certs.py b/lms/djangoapps/certificates/management/commands/ungenerated_certs.py index 7d384103ab..947af913dd 100644 --- a/lms/djangoapps/certificates/management/commands/ungenerated_certs.py +++ b/lms/djangoapps/certificates/management/commands/ungenerated_certs.py @@ -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 + + + diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index 5815db64ca..eacd5b7977 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -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'} diff --git a/lms/djangoapps/certificates/views.py b/lms/djangoapps/certificates/views.py index 472215ab5c..d701794b6d 100644 --- a/lms/djangoapps/certificates/views.py +++ b/lms/djangoapps/certificates/views.py @@ -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()