queue.py 6.81 KB
Newer Older
1 2
from certificates.models import GeneratedCertificate
from certificates.models import certificate_status_for_student
3
from certificates.models import CertificateStatuses as status
4
from certificates.models import CertificateWhitelist
5

6 7 8 9 10 11 12
from courseware import grades, courses
from django.test.client import RequestFactory
from capa.xqueue_interface import XQueueInterface
from capa.xqueue_interface import make_xheader, make_hashkey
from django.conf import settings
from requests.auth import HTTPBasicAuth
from student.models import UserProfile
13

14 15
import json
import random
16 17 18 19
import logging


logger = logging.getLogger(__name__)
20 21


John Jarvis committed
22
class XQueueCertInterface(object):
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
    """
    XQueueCertificateInterface provides an
    interface to the xqueue server for
    managing student certificates.

    Instantiating an object will create a new
    connection to the queue server.

    See models.py for valid state transitions,
    summary of methods:

       add_cert:   Add a new certificate.  Puts a single
                   request on the queue for the student/course.
                   Once the certificate is generated a post
                   will be made to the update_certificate
                   view which will save the certificate
                   download URL.

       regen_cert: Regenerate an existing certificate.
                   For a user that already has a certificate
                   this will delete the existing one and
                   generate a new cert.


       del_cert:   Delete an existing certificate
                   For a user that already has a certificate
                   this will delete his cert.

    """
52

53
    def __init__(self, request=None):
54

John Jarvis committed
55 56 57
        # Get basic auth (username/password) for
        # xqueue connection if it's in the settings

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
        if settings.XQUEUE_INTERFACE.get('basic_auth') is not None:
            requests_auth = HTTPBasicAuth(
                    *settings.XQUEUE_INTERFACE['basic_auth'])
        else:
            requests_auth = None

        if request is None:
            factory = RequestFactory()
            self.request = factory.get('/')
        else:
            self.request = request

        self.xqueue_interface = XQueueInterface(
                settings.XQUEUE_INTERFACE['url'],
                settings.XQUEUE_INTERFACE['django_auth'],
                requests_auth,
                )
75 76
        self.whitelist = CertificateWhitelist.objects.all()
        self.restricted = UserProfile.objects.filter(allow_certificate=False)
77 78 79 80 81 82 83 84 85 86

    def regen_cert(self, student, course_id):
        """
        Arguments:
          student - User.object
          course_id - courseenrollment.course_id (string)

        Removes certificate for a student, will change
        the certificate status to 'regenerating'.

87
        Certificate must be in the 'error' or 'downloadable' state
88 89 90 91 92 93

        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
94

95
        otherwise it will return the current state
96 97 98

        """

99
        raise NotImplementedError
100

101
    def del_cert(self, student, course_id):
102

103
        """
104 105 106 107 108 109 110
        Arguments:
          student - User.object
          course_id - courseenrollment.course_id (string)

        Removes certificate for a student, will change
        the certificate status to 'deleting'.

111 112
        Certificate must be in the 'error' or 'downloadable' state
        otherwise it will return the current state
113 114 115

        """

116
        raise NotImplementedError
117

118
    def add_cert(self, student, course_id):
119 120 121 122 123 124
        """

        Arguments:
          student - User.object
          course_id - courseenrollment.course_id (string)

125
        Request a new certificate for a student.
126
        Will change the certificate status to 'generating'.
127

128
        Certificate must be in the 'unavailable', 'error',
129
        'deleted' or 'generating' state.
130

131 132 133 134 135
        If a student has a passing grade or is in the whitelist
        table for the course a request will made for a new cert.

        If a student has allow_certificate set to False in the
        userprofile table the status will change to 'restricted'
136

John Jarvis committed
137

138 139 140 141
        If a student does not have a passing grade the status
        will change to status.notpassing

        Returns the student's status
142 143 144

        """

145 146
        VALID_STATUSES = [ status.generating,
                status.unavailable, status.deleted, status.error,
147
                status.notpassing]
148 149 150

        cert_status = certificate_status_for_student(
                              student, course_id)['status']
John Jarvis committed
151

152 153 154
        if cert_status in VALID_STATUSES:
            # grade the student
            course = courses.get_course_by_id(course_id)
155
            profile = UserProfile.objects.get(user=student)
156 157


158 159
            cert, created = GeneratedCertificate.objects.get_or_create(
                   user=student, course_id=course_id)
160

161
            grade = grades.grade(student, self.request, course)
162
            is_whitelisted = self.whitelist.filter(
163 164
                user=student, course_id=course_id, whitelist=True).exists()

165
            if is_whitelisted or grade['grade'] is not None:
166 167 168 169

                # check to see whether the student is on the
                # the embargoed country restricted list
                if self.restricted.filter(user=student).exists():
170
                    cert.status = status.restricted
171 172 173
                    cert.save()
                    return cert.status

174 175
                cert_status = status.generating
                key = make_hashkey(random.random())
176

177 178 179 180 181
                cert.status = cert_status
                cert.grade = grade['percent']
                cert.user = student
                cert.course_id = course_id
                cert.key = key
182
                cert.name = profile.name
183 184 185 186 187 188 189

                contents = {
                    'action': 'create',
                    'username': student.username,
                    'course_id': course_id,
                    'name': profile.name,
                }
190

191
                self._send_to_xqueue(contents, key)
192
                cert.save()
193 194
            else:
                cert_status = status.notpassing
195
                cert.grade = grade['percent']
196 197 198 199 200
                cert.status = cert_status
                cert.user = student
                cert.course_id = course_id
                cert.name = profile.name
                cert.save()
201

202
        return cert_status
203

204
    def _send_to_xqueue(self, contents, key):
205 206

        xheader = make_xheader(
John Jarvis committed
207
            'https://{0}/update_certificate?{1}'.format(
208
                settings.SITE_NAME, key), key, settings.CERT_QUEUE)
209 210 211 212 213 214

        (error, msg) = self.xqueue_interface.send_to_queue(
                header=xheader, body=json.dumps(contents))
        if error:
            logger.critical('Unable to add a request to the queue')
            raise Exception('Unable to send queue message')