""" Helper functions for the course complete event that was originally included with the Badging MVP. """ import hashlib import logging from django.urls import reverse from django.utils.text import slugify from django.utils.translation import ugettext_lazy as _ from lms.djangoapps.badges.models import BadgeAssertion, BadgeClass, CourseCompleteImageConfiguration from lms.djangoapps.badges.utils import requires_badges_enabled, site_prefix from xmodule.modulestore.django import modulestore LOGGER = logging.getLogger(__name__) # NOTE: As these functions are carry-overs from the initial badging implementation, they are used in # migrations. Please check the badge migrations when changing any of these functions. def course_slug(course_key, mode): """ Legacy: Not to be used as a model for constructing badge slugs. Included for compatibility with the original badge type, awarded on course completion. Slug ought to be deterministic and limited in size so it's not too big for Badgr. Badgr's max slug length is 255. """ # Seven digits should be enough to realistically avoid collisions. That's what git services use. digest = hashlib.sha256( "{}{}".format(str(course_key), str(mode)).encode('utf-8') ).hexdigest()[:7] base_slug = slugify(str(course_key) + f'_{mode}_')[:248] return base_slug + digest def badge_description(course, mode): """ Returns a description for the earned badge. """ if course.end: return _('Completed the course "{course_name}" ({course_mode}, {start_date} - {end_date})').format( start_date=course.start.date(), end_date=course.end.date(), course_name=course.display_name, course_mode=mode, ) else: return _('Completed the course "{course_name}" ({course_mode})').format( course_name=course.display_name, course_mode=mode, ) def evidence_url(user_id, course_key): """ Generates a URL to the user's Certificate HTML view, along with a GET variable that will signal the evidence visit event. """ course_id = str(course_key) # avoid circular import problems from lms.djangoapps.certificates.models import GeneratedCertificate cert = GeneratedCertificate.eligible_certificates.get(user__id=int(user_id), course_id=course_id) return site_prefix() + reverse( 'certificates:render_cert_by_uuid', kwargs={'certificate_uuid': cert.verify_uuid}) + '?evidence_visit=1' def criteria(course_key): """ Constructs the 'criteria' URL from the course about page. """ about_path = reverse('about_course', kwargs={'course_id': str(course_key)}) return f'{site_prefix()}{about_path}' def get_completion_badge(course_id, user): """ Given a course key and a user, find the user's enrollment mode and get the Course Completion badge. """ from common.djangoapps.student.models import CourseEnrollment badge_classes = CourseEnrollment.objects.filter( user=user, course_id=course_id ).order_by('-is_active') if not badge_classes: return None mode = badge_classes[0].mode course = modulestore().get_course(course_id) if not course.issue_badges: return None return BadgeClass.get_badge_class( slug=course_slug(course_id, mode), issuing_component='', criteria=criteria(course_id), description=badge_description(course, mode), course_id=course_id, mode=mode, display_name=course.display_name, image_file_handle=CourseCompleteImageConfiguration.image_for_mode(mode) ) @requires_badges_enabled def course_badge_check(user, course_key): """ Takes a GeneratedCertificate instance, and checks to see if a badge exists for this course, creating it if not, should conditions be right. """ if not modulestore().get_course(course_key).issue_badges: LOGGER.info("Course is not configured to issue badges.") return badge_class = get_completion_badge(course_key, user) if not badge_class: # We're not configured to make a badge for this course mode. return if BadgeAssertion.objects.filter(user=user, badge_class=badge_class): LOGGER.info("Completion badge already exists for this user on this course.") # Badge already exists. Skip. return evidence = evidence_url(user.id, course_key) badge_class.award(user, evidence_url=evidence)