def generate_course_certificate(user, course_key, generation_mode): """ Generate a course certificate for this user, in this course run. If the certificate has a passing status, also emit a certificate event. Note that the certificate could be either an allowlist certificate or a "regular" course certificate; the content will be the same either way. Args: user: user for whom to generate a certificate course_key: course run key for which to generate a certificate generation_mode: Used when emitting an events. Options are "self" (implying the user generated the cert themself) and "batch" for everything else. """ cert = _generate_certificate(user, course_key) if CertificateStatuses.is_passing_status(cert.status): # Emit a certificate event event_data = { 'user_id': user.id, 'course_id': str(course_key), 'certificate_id': cert.verify_uuid, 'enrollment_mode': cert.mode, 'generation_mode': generation_mode } emit_certificate_event(event_name='created', user=user, course_id=course_key, event_data=event_data) return cert
def generate_user_certificates(student, course_key, insecure=False, generation_mode='batch', forced_grade=None): """ It will add the add-cert request into the xqueue. A new record will be created to track the certificate generation task. If an error occurs while adding the certificate to the queue, the task will have status 'error'. It also emits `edx.certificate.created` event for analytics. This method has not yet been updated (it predates the certificates revamp). If modifying this method, see also generate_user_certificates() in generation_handler.py (which is very similar but is not called from a celery task). In the future these methods will be unified. Args: student (User) course_key (CourseKey) Keyword Arguments: insecure - (Boolean) generation_mode - who has requested certificate generation. Its value should `batch` in case of django command and `self` if student initiated the request. forced_grade - a string indicating to replace grade parameter. if present grading will be skipped. """ beta_testers_queryset = list_with_level(course_key, 'beta') if beta_testers_queryset.filter(username=student.username): log.info(f"Canceling Certificate Generation task for user {student.id} : {course_key}. User is a Beta Tester.") return xqueue = XQueueCertInterface() if insecure: xqueue.use_https = False course_overview = get_course_overview(course_key) generate_pdf = not has_html_certificates_enabled(course_overview) cert = xqueue.add_cert( student, course_key, generate_pdf=generate_pdf, forced_grade=forced_grade ) log.info(f"Queued Certificate Generation task for {student.id} : {course_key}") # If cert_status is not present in certificate valid_statuses (for example unverified) then # add_cert returns None and raises AttributeError while accessing cert attributes. if cert is None: return if CertificateStatuses.is_passing_status(cert.status): emit_certificate_event('created', student, course_key, course_overview, { 'user_id': student.id, 'course_id': str(course_key), 'certificate_id': cert.verify_uuid, 'enrollment_mode': cert.mode, 'generation_mode': generation_mode }) return cert.status
def _track_certificate_events(request, course, user, user_certificate): """ Tracks web certificate view related events. """ # Badge Request Event Tracking Logic course_key = course.location.course_key if 'evidence_visit' in request.GET: badge_class = get_completion_badge(course_key, user) if not badge_class: log.warning( 'Visit to evidence URL for badge, but badges not configured for course "%s"', course_key) badges = [] else: badges = badge_class.get_for_user(user) if badges: # There should only ever be one of these. badge = badges[0] tracker.emit( 'edx.badge.assertion.evidence_visited', { 'badge_name': badge.badge_class.display_name, 'badge_slug': badge.badge_class.slug, 'badge_generator': badge.backend, 'issuing_component': badge.badge_class.issuing_component, 'user_id': user.id, 'course_id': str(course_key), 'enrollment_mode': badge.badge_class.mode, 'assertion_id': badge.id, 'assertion_image_url': badge.image_url, 'assertion_json_url': badge.assertion_url, 'issuer': badge.data.get('issuer'), }) else: log.warning( "Could not find badge for %s on course %s.", user.id, course_key, ) # track certificate evidence_visited event for analytics when certificate_user and accessing_user are different if request.user and request.user.id != user.id: emit_certificate_event('evidence_visited', user, str(course.id), event_data={ 'certificate_id': user_certificate.verify_uuid, 'enrollment_mode': user_certificate.mode, 'social_network': CertificateSocialNetworks.linkedin })
def _revoke_certificate(self, status, grade=None, source=None): """ Revokes a course certificate from a learner, updating the certificate's status as specified by the value of the `status` argument. This will prevent the learner from being able to access their certificate in the associated course run. We remove the `download_uuid` and the `download_url` as well, but this is only important to PDF certificates. Invalidating a certificate fires the `COURSE_CERT_REVOKED` signal. This kicks off a task to determine if there are any program certificates that also need to be revoked from the learner. If the certificate had a status of `downloadable` before being revoked then we will also emit an `edx.certificate.revoked` event for tracking purposes. Args: status (CertificateStatus) - certificate status to set for the `GeneratedCertificate` record grade (float) - snapshot of the learner's current grade as a decimal source (String) - source requesting invalidation of the certificate for tracking purposes """ previous_certificate_status = self.status self.error_reason = '' self.download_uuid = '' self.download_url = '' self.grade = grade or '' self.status = status self.save() COURSE_CERT_REVOKED.send_robust( sender=self.__class__, user=self.user, course_key=self.course_id, mode=self.mode, status=self.status, ) if previous_certificate_status == CertificateStatuses.downloadable: # imported here to avoid a circular import issue from lms.djangoapps.certificates.utils import emit_certificate_event event_data = { 'user_id': self.user.id, 'course_id': str(self.course_id), 'certificate_id': self.verify_uuid, 'enrollment_mode': self.mode, 'source': source or '', } emit_certificate_event('revoked', self.user, str(self.course_id), event_data=event_data)
def generate_allowlist_certificate(user, course_key): """ Generate an allowlist certificate for this user, in this course run. This method should be called from a task. """ cert = _generate_certificate(user, course_key) if CertificateStatuses.is_passing_status(cert.status): # Emit a certificate event. Note that the two options for generation_mode are "self" (implying the user # generated the cert themself) and "batch" for everything else. event_data = { 'user_id': user.id, 'course_id': str(course_key), 'certificate_id': cert.verify_uuid, 'enrollment_mode': cert.mode, 'generation_mode': 'batch' } emit_certificate_event(event_name='created', user=user, course_id=course_key, event_data=event_data) return cert
def generate_user_certificates(student, course_key, course=None, insecure=False, generation_mode='batch', forced_grade=None): """ It will add the add-cert request into the xqueue. A new record will be created to track the certificate generation task. If an error occurs while adding the certificate to the queue, the task will have status 'error'. It also emits `edx.certificate.created` event for analytics. This method has not yet been updated (it predates the certificates revamp). If modifying this method, see also generate_user_certificates() in generation.py (which is very similar but is called from a celery task). In the future these methods will be unified. Args: student (User) course_key (CourseKey) Keyword Arguments: course (Course): Optionally provide the course object; if not provided it will be loaded. insecure - (Boolean) generation_mode - who has requested certificate generation. Its value should `batch` in case of django command and `self` if student initiated the request. forced_grade - a string indicating to replace grade parameter. if present grading will be skipped. """ if is_using_certificate_allowlist_and_is_on_allowlist(student, course_key): # Note that this will launch an asynchronous task, and so cannot return the certificate status. This is a # change from the older certificate code that tries to immediately create a cert. log.info( f'{course_key} is using allowlist certificates, and the user {student.id} is on its allowlist. ' f'Attempt will be made to regenerate an allowlist certificate.') return generate_allowlist_certificate_task(student, course_key) if not course: course = modulestore().get_course(course_key, depth=0) beta_testers_queryset = list_with_level(course, 'beta') if beta_testers_queryset.filter(username=student.username): message = 'Cancelling course certificate generation for user [{}] against course [{}], user is a Beta Tester.' log.info(message.format(student.username, course_key)) return xqueue = XQueueCertInterface() if insecure: xqueue.use_https = False generate_pdf = not has_html_certificates_enabled(course) cert = xqueue.add_cert(student, course_key, course=course, generate_pdf=generate_pdf, forced_grade=forced_grade) message = 'Queued Certificate Generation task for {user} : {course}' log.info(message.format(user=student.id, course=course_key)) # If cert_status is not present in certificate valid_statuses (for example unverified) then # add_cert returns None and raises AttributeError while accessing cert attributes. if cert is None: return if CertificateStatuses.is_passing_status(cert.status): emit_certificate_event( 'created', student, course_key, course, { 'user_id': student.id, 'course_id': str(course_key), 'certificate_id': cert.verify_uuid, 'enrollment_mode': cert.mode, 'generation_mode': generation_mode }) return cert.status