def test_get_web_certificate_url(self): """ Test the get_certificate_url with a web cert course """ expected_url = reverse( 'certificates:render_cert_by_uuid', kwargs=dict(certificate_uuid=self.uuid) ) cert_url = certs_api.get_certificate_url( user_id=self.student.id, course_id=self.web_cert_course.id, uuid=self.uuid ) self.assertEqual(expected_url, cert_url) expected_url = reverse( 'certificates:html_view', kwargs={ "user_id": str(self.student.id), "course_id": unicode(self.web_cert_course.id), } ) cert_url = certs_api.get_certificate_url( user_id=self.student.id, course_id=self.web_cert_course.id ) self.assertEqual(expected_url, cert_url)
def test_get_web_certificate_url(self): """ Test the get_certificate_url with a web cert course """ expected_url = reverse( 'certificates:render_cert_by_uuid', kwargs=dict(certificate_uuid=self.uuid) ) cert_url = certs_api.get_certificate_url( user_id=self.student.id, course_id=self.web_cert_course.id, uuid=self.uuid ) self.assertEqual(expected_url, cert_url) expected_url = reverse( 'certificates:render_cert_by_uuid', kwargs=dict(certificate_uuid=self.uuid) ) cert_url = certs_api.get_certificate_url( user_id=self.student.id, course_id=self.web_cert_course.id, uuid=self.uuid ) self.assertEqual(expected_url, cert_url)
def test_html_view_microsite_configuration_missing(self): test_configuration_string = """{ "default": { "accomplishment_class_append": "accomplishment-certificate", "platform_name": "edX", "company_about_url": "http://www.edx.org/about-us", "company_privacy_url": "http://www.edx.org/edx-privacy-policy", "company_tos_url": "http://www.edx.org/edx-terms-service", "company_verified_certificate_url": "http://www.edx.org/verified-certificate", "document_stylesheet_url_application": "/static/certificates/sass/main-ltr.css", "logo_src": "/static/certificates/images/logo-edx.svg", "logo_url": "http://www.edx.org", "company_about_description": "This should not survive being overwritten by static content" }, "honor": { "certificate_type": "Honor Code" } }""" config = self._certificate_html_view_configuration(configuration_string=test_configuration_string) self.assertEquals(config.configuration, test_configuration_string) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) self.assertIn('edX', response.content) self.assertNotIn('platform_microsite', response.content) self.assertNotIn('http://www.microsite.org', response.content) self.assertNotIn('This should not survive being overwritten by static content', response.content)
def linkedin_add_to_profile_url(self): """ Returns a URL to add a certificate to a LinkedIn profile (will autofill fields). Requires LinkedIn sharing to be enabled, either via a site configuration or a LinkedInAddToProfileConfiguration object being enabled. """ if self.effective_user.is_anonymous: return linkedin_config = LinkedInAddToProfileConfiguration.current() if linkedin_config.is_enabled(): try: user_certificate = GeneratedCertificate.eligible_certificates.get( user=self.effective_user, course_id=self.course_key) except GeneratedCertificate.DoesNotExist: return cert_url = self.request.build_absolute_uri( get_certificate_url(course_id=self.course_key, uuid=user_certificate.verify_uuid)) return linkedin_config.add_to_profile_url( self.overview.display_name, user_certificate.mode, cert_url, certificate=user_certificate, )
def _attach_run_mode_certificate_url(self, run_mode): certificate_data = certificate_api.certificate_downloadable_status(self.user, self.course_key) certificate_uuid = certificate_data.get('uuid') run_mode['certificate_url'] = certificate_api.get_certificate_url( course_id=self.course_key, uuid=certificate_uuid, ) if certificate_uuid else None
def test_get_pdf_certificate_url(self): """ Test the get_certificate_url with a pdf cert course """ cert_url = get_certificate_url(user_id=self.student.id, course_id=self.pdf_cert_course.id, uuid=self.uuid) assert 'www.gmail.com' == cert_url
def supplement_program_data(program_data, user): """Supplement program course codes with CourseOverview and CourseEnrollment data. Arguments: program_data (dict): Representation of a program. user (User): The user whose enrollments to inspect. """ for organization in program_data['organizations']: # TODO cache the results of the get_organization_by_short_name call # so we don't have to hit database that frequently org_obj = get_organization_by_short_name(organization['key']) if org_obj and org_obj.get('logo'): organization['img'] = org_obj['logo'].url for course_code in program_data['course_codes']: for run_mode in course_code['run_modes']: course_key = CourseKey.from_string(run_mode['course_key']) course_overview = CourseOverview.get_from_id(course_key) run_mode['course_url'] = reverse('course_root', args=[course_key]) run_mode['course_image_url'] = course_overview.course_image_url run_mode['start_date'] = course_overview.start_datetime_text() run_mode['end_date'] = course_overview.end_datetime_text() end_date = course_overview.end or datetime.datetime.max.replace( tzinfo=pytz.UTC) run_mode['is_course_ended'] = end_date < timezone.now() run_mode['is_enrolled'] = CourseEnrollment.is_enrolled( user, course_key) enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace( tzinfo=pytz.UTC) enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace( tzinfo=pytz.UTC) is_enrollment_open = enrollment_start <= timezone.now( ) < enrollment_end run_mode['is_enrollment_open'] = is_enrollment_open if not is_enrollment_open: # Only render this enrollment open date if the enrollment open is in the future run_mode['enrollment_open_date'] = strftime_localized( enrollment_start, 'SHORT_DATE') # TODO: Currently unavailable on LMS. run_mode['marketing_url'] = '' certificate_data = certificate_api.certificate_downloadable_status( user, course_key) certificate_uuid = certificate_data.get('uuid') if certificate_uuid: run_mode[ 'certificate_url'] = certificate_api.get_certificate_url( course_id=course_key, uuid=certificate_uuid, ) return program_data
def _attach_course_run_certificate_url(self, run_mode): certificate_data = certificate_api.certificate_downloadable_status(self.user, self.course_run_key) certificate_uuid = certificate_data.get('uuid') run_mode['certificate_url'] = certificate_api.get_certificate_url( user_id=self.user.id, # Providing user_id allows us to fall back to PDF certificates # if web certificates are not configured for a given course. course_id=self.course_run_key, uuid=certificate_uuid, ) if certificate_uuid else None
def test_get_pdf_certificate_url(self): """ Test the get_certificate_url with a pdf cert course """ cert_url = certs_api.get_certificate_url( user_id=self.student.id, course_id=self.pdf_cert_course.id, uuid=self.uuid) self.assertEqual('www.gmail.com', cert_url)
def test_linked_student_to_web_view_credential(self, enrollment_mode): cert = self._create_certificate(enrollment_mode) test_url = get_certificate_url(course_id=self.course.id, uuid=cert.verify_uuid) response = self.client.get(reverse('dashboard')) self.assertContains(response, u'View Test_Certificate') self.assertContains(response, test_url)
def test_html_view_site_configuration_missing(self): test_url = get_certificate_url(user_id=self.user.id, course_id=six.text_type(self.course.id)) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url) self.assertIn('edX', response.content) self.assertNotIn('My Platform Site', response.content) self.assertNotIn( 'This should not survive being overwritten by static content', response.content)
def test_html_view_for_site(self): test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url) self.assertIn('awarded this My Platform Site Honor Code Certificate of Completion', response.content) self.assertIn('My Platform Site offers interactive online classes and MOOCs.', response.content) self.assertIn('About My Platform Site', response.content)
def test_get_web_certificate_url(self): """ Test the get_certificate_url with a web cert course """ certificates = [ { 'id': 1, 'name': 'Test Certificate Name', 'description': 'Test Certificate Description', 'course_title': 'tes_course_title', 'signatories': [], 'version': 1, 'is_active': True } ] self.web_cert_course.certificates = {'certificates': certificates} self.web_cert_course.save() self.store.update_item(self.web_cert_course, self.student.id) expected_url = reverse( 'certificates:render_cert_by_uuid', kwargs=dict(certificate_uuid=self.uuid) ) cert_url = certs_api.get_certificate_url( user_id=self.student.id, course_id=self.web_cert_course.id, uuid=self.uuid ) self.assertEqual(expected_url, cert_url) expected_url = reverse( 'certificates:html_view', kwargs={ "user_id": str(self.student.id), "course_id": unicode(self.web_cert_course.id), } ) cert_url = certs_api.get_certificate_url( user_id=self.student.id, course_id=self.web_cert_course.id ) self.assertEqual(expected_url, cert_url)
def test_get_pdf_certificate_url(self): """ Test the get_certificate_url with a pdf cert course """ cert_url = certs_api.get_certificate_url( user_id=self.student.id, course_id=self.pdf_cert_course.id, uuid=self.uuid ) self.assertEqual('www.gmail.com', cert_url)
def test_html_view_for_site(self): test_url = get_certificate_url(user_id=self.user.id, course_id=six.text_type(self.course.id), uuid=self.cert.verify_uuid) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url) self.assertContains( response, 'awarded this My Platform Site Honor Code Certificate of Completion', ) self.assertContains( response, 'My Platform Site offers interactive online classes and MOOCs.') self.assertContains(response, 'About My Platform Site')
def supplement_program_data(program_data, user): """Supplement program course codes with CourseOverview and CourseEnrollment data. Arguments: program_data (dict): Representation of a program. user (User): The user whose enrollments to inspect. """ for organization in program_data['organizations']: # TODO cache the results of the get_organization_by_short_name call # so we don't have to hit database that frequently org_obj = get_organization_by_short_name(organization['key']) if org_obj and org_obj.get('logo'): organization['img'] = org_obj['logo'].url for course_code in program_data['course_codes']: for run_mode in course_code['run_modes']: course_key = CourseKey.from_string(run_mode['course_key']) course_overview = CourseOverview.get_from_id(course_key) run_mode['course_url'] = reverse('course_root', args=[course_key]) run_mode['course_image_url'] = course_overview.course_image_url run_mode['start_date'] = course_overview.start_datetime_text() run_mode['end_date'] = course_overview.end_datetime_text() end_date = course_overview.end or datetime.datetime.max.replace(tzinfo=pytz.UTC) run_mode['is_course_ended'] = end_date < timezone.now() run_mode['is_enrolled'] = CourseEnrollment.is_enrolled(user, course_key) enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace(tzinfo=pytz.UTC) enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=pytz.UTC) is_enrollment_open = enrollment_start <= timezone.now() < enrollment_end run_mode['is_enrollment_open'] = is_enrollment_open if not is_enrollment_open: # Only render this enrollment open date if the enrollment open is in the future run_mode['enrollment_open_date'] = strftime_localized(enrollment_start, 'SHORT_DATE') # TODO: Currently unavailable on LMS. run_mode['marketing_url'] = '' certificate_data = certificate_api.certificate_downloadable_status(user, course_key) certificate_uuid = certificate_data.get('uuid') if certificate_uuid: run_mode['certificate_url'] = certificate_api.get_certificate_url( course_id=course_key, uuid=certificate_uuid, ) return program_data
def test_html_view_for_microsite(self): test_configuration_string = """{ "default": { "accomplishment_class_append": "accomplishment-certificate", "platform_name": "edX", "company_about_url": "http://www.edx.org/about-us", "company_privacy_url": "http://www.edx.org/edx-privacy-policy", "company_tos_url": "http://www.edx.org/edx-terms-service", "company_verified_certificate_url": "http://www.edx.org/verified-certificate", "document_stylesheet_url_application": "/static/certificates/sass/main-ltr.css", "logo_src": "/static/certificates/images/logo-edx.svg", "logo_url": "http://www.edx.org" }, "microsites": { "test-site": { "accomplishment_class_append": "accomplishment-certificate", "platform_name": "platform_microsite", "company_about_url": "http://www.microsite.org/about-us", "company_privacy_url": "http://www.microsite.org/edx-privacy-policy", "company_tos_url": "http://www.microsite.org/microsite-terms-service", "company_verified_certificate_url": "http://www.microsite.org/verified-certificate", "document_stylesheet_url_application": "/static/certificates/sass/main-ltr.css", "logo_src": "/static/certificates/images/logo-microsite.svg", "logo_url": "http://www.microsite.org", "company_about_description": "This is special microsite aware company_about_description content", "company_about_title": "Microsite title" } }, "honor": { "certificate_type": "Honor Code" } }""" config = self._certificate_html_view_configuration( configuration_string=test_configuration_string) self.assertEquals(config.configuration, test_configuration_string) test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) self.assertIn('platform_microsite', response.content) # logo url is taken from microsite configuration setting self.assertIn('http://test_site.localhost', response.content) self.assertIn( 'This is special microsite aware company_about_description content', response.content) self.assertIn('Microsite title', response.content)
def test_html_view_for_microsite(self): test_configuration_string = """{ "default": { "accomplishment_class_append": "accomplishment-certificate", "platform_name": "edX", "company_about_url": "http://www.edx.org/about-us", "company_privacy_url": "http://www.edx.org/edx-privacy-policy", "company_tos_url": "http://www.edx.org/edx-terms-service", "company_verified_certificate_url": "http://www.edx.org/verified-certificate", "document_stylesheet_url_application": "/static/certificates/sass/main-ltr.css", "logo_src": "/static/certificates/images/logo-edx.svg", "logo_url": "http://www.edx.org" }, "microsites": { "test-site": { "accomplishment_class_append": "accomplishment-certificate", "platform_name": "platform_microsite", "company_about_url": "http://www.microsite.org/about-us", "company_privacy_url": "http://www.microsite.org/edx-privacy-policy", "company_tos_url": "http://www.microsite.org/microsite-terms-service", "company_verified_certificate_url": "http://www.microsite.org/verified-certificate", "document_stylesheet_url_application": "/static/certificates/sass/main-ltr.css", "logo_src": "/static/certificates/images/logo-microsite.svg", "logo_url": "http://www.microsite.org", "company_about_description": "This is special microsite aware company_about_description content", "company_about_title": "Microsite title" } }, "honor": { "certificate_type": "Honor Code" } }""" config = self._certificate_html_view_configuration(configuration_string=test_configuration_string) self.assertEquals(config.configuration, test_configuration_string) test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) self.assertIn('platform_microsite', response.content) # logo url is taken from microsite configuration setting self.assertIn('http://test_site.localhost', response.content) self.assertIn('This is special microsite aware company_about_description content', response.content) self.assertIn('Microsite title', response.content)
def add_certificate_footer(self): x, y = 11, 0.9 sub_y = y - 0.25 font_size = 0.15 font_sub_size = 0.12 if not self.is_english: date = trans_digits(_date(self.cert.modified_date, 'd F, Y')) else: date = self.cert.modified_date.strftime('%d %B, %Y') self.draw_bidi_text(self._('COURSE CERTIFICATE'), x, y, size=font_size) date_str = self._('Issued {date}').format(date=date) self.draw_bidi_text(date_str, x, sub_y, bold=True, size=font_sub_size) # Verification x = x - 3.3 self.draw_bidi_text( self._('Verify the authenticity of this certificate at'), x, y, size=font_size) cert_url = get_certificate_url(course_id=self.course_id, uuid=self.cert.verify_uuid) url = self.path_builder(cert_url) cert_uuid = self.cert.verify_uuid qr_y = (y + sub_y) / 2.0 self.add_qr_code(url, x, qr_y) self.draw_bidi_text(cert_uuid, x, sub_y, size=font_sub_size) # Underline the UUID uuid_width = stringWidth(cert_uuid, self.font, self.font_size) xu = self.bidi_x_axis(x * inch) yu = (sub_y - 0.02) * inch length = uuid_width if self.is_english else -uuid_width self.ctx.setLineWidth(0.25) self.ctx.line(xu, yu, xu + length, yu) # Link the UUID rect = (xu, yu, xu + length, yu + (0.5 * inch)) self.ctx.linkURL(url, rect, relative=1)
def test_new_cert_requests_into_xqueue_returns_generating(self): with mock_passing_grade(): with self._mock_queue(): certs_api.generate_user_certificates(self.student, self.course.id) # Verify that the certificate has status 'generating' cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id) self.assertEqual(cert.status, CertificateStatuses.generating) self.assert_event_emitted( 'edx.certificate.created', user_id=self.student.id, course_id=unicode(self.course.id), certificate_url=certs_api.get_certificate_url(self.student.id, self.course.id), certificate_id=cert.verify_uuid, enrollment_mode=cert.mode, generation_mode='batch' )
def test_new_cert_requests_into_xqueue_returns_generating(self): with mock_passing_grade(): with self._mock_queue(): certs_api.generate_user_certificates(self.student, self.course.id) # Verify that the certificate has status 'generating' cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id) self.assertEqual(cert.status, CertificateStatuses.generating) self.assert_event_emitted( 'edx.certificate.created', user_id=self.student.id, course_id=six.text_type(self.course.id), certificate_url=certs_api.get_certificate_url(self.student.id, self.course.id), certificate_id=cert.verify_uuid, enrollment_mode=cert.mode, generation_mode='batch' )
def _update_social_context(request, context, course, user, user_certificate, platform_name): """ Updates context dictionary with info required for social sharing. """ share_settings = configuration_helpers.get_value( "SOCIAL_SHARING_SETTINGS", settings.SOCIAL_SHARING_SETTINGS) context['facebook_share_enabled'] = share_settings.get( 'CERTIFICATE_FACEBOOK', False) context['facebook_app_id'] = configuration_helpers.get_value( "FACEBOOK_APP_ID", settings.FACEBOOK_APP_ID) context['facebook_share_text'] = share_settings.get( 'CERTIFICATE_FACEBOOK_TEXT', _(u"I completed the {course_title} course on {platform_name}.").format( course_title=context['accomplishment_copy_course_name'], platform_name=platform_name)) context['twitter_share_enabled'] = share_settings.get( 'CERTIFICATE_TWITTER', False) context['twitter_share_text'] = share_settings.get( 'CERTIFICATE_TWITTER_TEXT', _(u"I completed a course at {platform_name}. Take a look at my certificate." ).format(platform_name=platform_name)) share_url = request.build_absolute_uri( get_certificate_url(course_id=course.id, uuid=user_certificate.verify_uuid)) context['share_url'] = share_url twitter_url = '' if context.get('twitter_share_enabled', False): twitter_url = 'https://twitter.com/intent/tweet?text={twitter_share_text}&url={share_url}'.format( twitter_share_text=smart_str(context['twitter_share_text']), share_url=urllib.quote_plus(smart_str(share_url))) context['twitter_url'] = twitter_url context['linked_in_url'] = None # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they # can add the certificate information to their profile. linkedin_config = LinkedInAddToProfileConfiguration.current() linkedin_share_enabled = share_settings.get('CERTIFICATE_LINKEDIN', linkedin_config.enabled) if linkedin_share_enabled: context['linked_in_url'] = linkedin_config.add_to_profile_url( course.id, course.display_name, user_certificate.mode, smart_str(share_url))
def _update_social_context(request, context, course, user, user_certificate, platform_name): """ Updates context dictionary with info required for social sharing. """ share_settings = configuration_helpers.get_value("SOCIAL_SHARING_SETTINGS", settings.SOCIAL_SHARING_SETTINGS) context['facebook_share_enabled'] = share_settings.get('CERTIFICATE_FACEBOOK', False) context['facebook_app_id'] = configuration_helpers.get_value("FACEBOOK_APP_ID", settings.FACEBOOK_APP_ID) context['facebook_share_text'] = share_settings.get( 'CERTIFICATE_FACEBOOK_TEXT', _(u"I completed the {course_title} course on {platform_name}.").format( course_title=context['accomplishment_copy_course_name'], platform_name=platform_name ) ) context['twitter_share_enabled'] = share_settings.get('CERTIFICATE_TWITTER', False) context['twitter_share_text'] = share_settings.get( 'CERTIFICATE_TWITTER_TEXT', _(u"I completed a course at {platform_name}. Take a look at my certificate.").format( platform_name=platform_name ) ) share_url = request.build_absolute_uri(get_certificate_url(course_id=course.id, uuid=user_certificate.verify_uuid)) context['share_url'] = share_url twitter_url = '' if context.get('twitter_share_enabled', False): twitter_url = 'https://twitter.com/intent/tweet?text={twitter_share_text}&url={share_url}'.format( twitter_share_text=smart_str(context['twitter_share_text']), share_url=urllib.quote_plus(smart_str(share_url)) ) context['twitter_url'] = twitter_url context['linked_in_url'] = None # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they # can add the certificate information to their profile. linkedin_config = LinkedInAddToProfileConfiguration.current() linkedin_share_enabled = share_settings.get('CERTIFICATE_LINKEDIN', linkedin_config.enabled) if linkedin_share_enabled: context['linked_in_url'] = linkedin_config.add_to_profile_url( course.id, course.display_name, user_certificate.mode, smart_str(share_url) )
def test_changes_on_webview(self): # Prepare attributes to check for CourseDetails.update_about_item(self.course, 'short_description', 'Edraak Test Description', self.user.id) # Creating a certificate self._add_course_certificates(count=1, signatory_count=2) self._create_edraak_test_template() test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) # Getting certificate as HTML response = self.client.get(test_url) # Verifying contents self.assertContains(response, 'Edraak Template') self.assertContains(response, 'course_description: Edraak Test Description')
def supplement_program_data(program_data, user): """Supplement program course codes with CourseOverview and CourseEnrollment data. Arguments: program_data (dict): Representation of a program. user (User): The user whose enrollments to inspect. """ for course_code in program_data['course_codes']: for run_mode in course_code['run_modes']: course_key = CourseKey.from_string(run_mode['course_key']) course_overview = CourseOverview.get_from_id(course_key) run_mode['course_url'] = reverse('course_root', args=[course_key]) run_mode['course_image_url'] = course_overview.course_image_url human_friendly_format = '%x' start_date = course_overview.start or DEFAULT_START_DATE end_date = course_overview.end or datetime.datetime.max.replace(tzinfo=pytz.UTC) run_mode['start_date'] = start_date.strftime(human_friendly_format) run_mode['end_date'] = end_date.strftime(human_friendly_format) run_mode['is_enrolled'] = CourseEnrollment.is_enrolled(user, course_key) enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace(tzinfo=pytz.UTC) enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=pytz.UTC) is_enrollment_open = enrollment_start <= timezone.now() < enrollment_end run_mode['is_enrollment_open'] = is_enrollment_open # TODO: Currently unavailable on LMS. run_mode['marketing_url'] = '' certificate_data = certificate_api.certificate_downloadable_status(user, course_key) certificate_uuid = certificate_data.get('uuid') if certificate_uuid: run_mode['certificate_url'] = certificate_api.get_certificate_url( course_id=course_key, uuid=certificate_uuid, ) return program_data
def supplement_program_data(program_data, user): """Supplement program course codes with CourseOverview and CourseEnrollment data. Arguments: program_data (dict): Representation of a program. user (User): The user whose enrollments to inspect. """ for course_code in program_data["course_codes"]: for run_mode in course_code["run_modes"]: course_key = CourseKey.from_string(run_mode["course_key"]) course_overview = CourseOverview.get_from_id(course_key) run_mode["course_url"] = reverse("course_root", args=[course_key]) run_mode["course_image_url"] = course_overview.course_image_url human_friendly_format = "%x" start_date = course_overview.start or DEFAULT_START_DATE end_date = course_overview.end or datetime.datetime.max.replace(tzinfo=pytz.UTC) run_mode["start_date"] = start_date.strftime(human_friendly_format) run_mode["end_date"] = end_date.strftime(human_friendly_format) run_mode["is_enrolled"] = CourseEnrollment.is_enrolled(user, course_key) enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace(tzinfo=pytz.UTC) enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=pytz.UTC) is_enrollment_open = enrollment_start <= timezone.now() < enrollment_end run_mode["is_enrollment_open"] = is_enrollment_open # TODO: Currently unavailable on LMS. run_mode["marketing_url"] = "" certificate_data = certificate_api.certificate_downloadable_status(user, course_key) certificate_uuid = certificate_data.get("uuid") if certificate_uuid: run_mode["certificate_url"] = certificate_api.get_certificate_url( course_id=course_key, uuid=certificate_uuid ) return program_data
def test_course_metadata(self, logged_in, enrollment_mode, enable_anonymous, is_microfrontend_enabled_for_user): is_microfrontend_enabled_for_user.return_value = True check_public_access = mock.Mock() check_public_access.return_value = enable_anonymous with mock.patch( 'lms.djangoapps.courseware.access_utils.check_public_access', check_public_access): if not logged_in: self.client.logout() if enrollment_mode == 'verified': cert = GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, status='downloadable', mode='verified', ) if enrollment_mode: CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode) response = self.client.get(self.url) assert response.status_code == 200 if enrollment_mode: enrollment = response.data['enrollment'] assert enrollment_mode == enrollment['mode'] assert enrollment['is_active'] assert len(response.data['tabs']) == 6 found = False for tab in response.data['tabs']: if tab['type'] == 'external_link': assert tab[ 'url'] != 'http://hidden.com', "Hidden tab is not hidden" if tab['url'] == 'http://zombo.com': found = True assert found, 'external link not in course tabs' assert not response.data['user_has_passing_grade'] if enrollment_mode == 'audit': assert response.data['verify_identity_url'] is None assert response.data['verification_status'] == 'none' # lint-amnesty, pylint: disable=literal-comparison assert response.data['linkedin_add_to_profile_url'] is None else: assert response.data['certificate_data'][ 'cert_status'] == 'earned_but_not_available' expected_verify_identity_url = IDVerificationService.get_verify_location( course_id=self.course.id) # The response contains an absolute URL so this is only checking the path of the final assert expected_verify_identity_url in response.data[ 'verify_identity_url'] assert response.data['verification_status'] == 'none' # lint-amnesty, pylint: disable=literal-comparison request = RequestFactory().request() cert_url = get_certificate_url(course_id=self.course.id, uuid=cert.verify_uuid) linkedin_url_params = { 'name': '{platform_name} Verified Certificate for {course_name}' .format( platform_name=settings.PLATFORM_NAME, course_name=self.course.display_name, ), 'certUrl': request.build_absolute_uri(cert_url), # default value from the LinkedInAddToProfileConfigurationFactory company_identifier 'organizationId': 1337, 'certId': cert.verify_uuid, 'issueYear': cert.created_date.year, 'issueMonth': cert.created_date.month, } expected_linkedin_url = ( 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&{params}' .format(params=urlencode(linkedin_url_params))) assert response.data[ 'linkedin_add_to_profile_url'] == expected_linkedin_url elif enable_anonymous and not logged_in: # multiple checks use this handler check_public_access.assert_called() assert response.data['enrollment']['mode'] is None assert response.data['course_access']['has_access'] else: assert not response.data['course_access']['has_access']
def _cert_info(user, enrollment, cert_status): """ Implements the logic for cert_info -- split out for testing. TODO: replace with a method that lives in the certificates app and combines this logic with lms.djangoapps.certificates.api.can_show_certificate_message and lms.djangoapps.courseware.views.get_cert_data Arguments: user (User): A user. enrollment (CourseEnrollment): A course enrollment. cert_status (dict): dictionary containing information about certificate status for the user Returns: dictionary containing: 'status': one of 'generating', 'downloadable', 'notpassing', 'restricted', 'auditing', 'processing', 'unverified', 'unavailable', or 'certificate_earned_but_not_available' 'show_survey_button': bool 'can_unenroll': if status allows for unenrollment The dictionary may also contain: 'linked_in_url': url to add cert to LinkedIn profile 'survey_url': url, only if course_overview.end_of_course_survey_url is not None 'show_cert_web_view': bool if html web certs are enabled and there is an active web cert 'cert_web_view_url': url if html web certs are enabled and there is an active web cert 'download_url': url to download a cert 'grade': if status is in 'generating', 'downloadable', 'notpassing', 'restricted', 'auditing', or 'unverified' """ # simplify the status for the template using this lookup table template_state = { CertificateStatuses.generating: 'generating', CertificateStatuses.downloadable: 'downloadable', CertificateStatuses.notpassing: 'notpassing', CertificateStatuses.restricted: 'restricted', CertificateStatuses.auditing: 'auditing', CertificateStatuses.audit_passing: 'auditing', CertificateStatuses.audit_notpassing: 'auditing', CertificateStatuses.unverified: 'unverified', } certificate_earned_but_not_available_status = 'certificate_earned_but_not_available' default_status = 'processing' default_info = { 'status': default_status, 'show_survey_button': False, 'can_unenroll': True, } if cert_status is None or enrollment is None: return default_info course_overview = enrollment.course_overview if enrollment else None status = template_state.get(cert_status['status'], default_status) is_hidden_status = status in ('processing', 'generating', 'notpassing', 'auditing') if _is_certificate_earned_but_not_available(course_overview, status): status = certificate_earned_but_not_available_status if (course_overview.certificates_display_behavior == CertificatesDisplayBehaviors.EARLY_NO_INFO and is_hidden_status): return default_info if not CourseMode.is_eligible_for_certificate(enrollment.mode, status=status): return default_info if course_overview and access.is_beta_tester(user, course_overview.id): # Beta testers are not eligible for a course certificate return default_info status_dict = { 'status': status, 'mode': cert_status.get('mode', None), 'linked_in_url': None, 'can_unenroll': status not in DISABLE_UNENROLL_CERT_STATES, } if status != default_status and course_overview.end_of_course_survey_url is not None: status_dict.update({ 'show_survey_button': True, 'survey_url': process_survey_link(course_overview.end_of_course_survey_url, user) }) else: status_dict['show_survey_button'] = False if status == 'downloadable': # showing the certificate web view button if certificate is downloadable state and feature flags are enabled. if has_html_certificates_enabled(course_overview): if course_overview.has_any_active_web_certificate: status_dict.update({ 'show_cert_web_view': True, 'cert_web_view_url': get_certificate_url(course_id=course_overview.id, uuid=cert_status['uuid']) }) elif cert_status['download_url']: status_dict['download_url'] = cert_status['download_url'] else: # don't show download certificate button if we don't have an active certificate for course status_dict['status'] = 'unavailable' elif 'download_url' not in cert_status: log.warning( "User %s has a downloadable cert for %s, but no download url", user.username, course_overview.id) return default_info else: status_dict['download_url'] = cert_status['download_url'] # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they # can add the certificate information to their profile. linkedin_config = LinkedInAddToProfileConfiguration.current() if linkedin_config.is_enabled(): status_dict[ 'linked_in_url'] = linkedin_config.add_to_profile_url( course_overview.display_name, cert_status.get('mode'), cert_status['download_url'], ) if status in { 'generating', 'downloadable', 'notpassing', 'restricted', 'auditing', 'unverified' }: cert_grade_percent = -1 persisted_grade_percent = -1 persisted_grade = CourseGradeFactory().read(user, course=course_overview, create_if_needed=False) if persisted_grade is not None: persisted_grade_percent = persisted_grade.percent if 'grade' in cert_status: cert_grade_percent = float(cert_status['grade']) if cert_grade_percent == -1 and persisted_grade_percent == -1: # Note: as of 11/20/2012, we know there are students in this state-- cs169.1x, # who need to be regraded (we weren't tracking 'notpassing' at first). # We can add a log.warning here once we think it shouldn't happen. return default_info grades_input = [cert_grade_percent, persisted_grade_percent] max_grade = (None if all(grade is None for grade in grades_input) else max(filter(lambda x: x is not None, grades_input))) status_dict['grade'] = str(max_grade) # If the grade is passing, the status is one of these statuses, and request certificate # is enabled for a course then we need to provide the option to the learner cert_gen_enabled = (has_self_generated_certificates_enabled( course_overview.id) or auto_certificate_generation_enabled()) passing_grade = persisted_grade and persisted_grade.passed if (status_dict['status'] != CertificateStatuses.downloadable and cert_gen_enabled and passing_grade and course_overview.has_any_active_web_certificate): status_dict['status'] = CertificateStatuses.requesting return status_dict
def test_enrolled_course_metadata(self, logged_in, enrollment_mode): check_public_access = mock.Mock() check_public_access.return_value = ACCESS_DENIED with mock.patch('lms.djangoapps.courseware.access_utils.check_public_access', check_public_access): if not logged_in: self.client.logout() if enrollment_mode == 'verified': cert = GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, status='downloadable', mode='verified', ) if enrollment_mode: CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode) response = self.client.get(self.url) assert response.status_code == 200 enrollment = response.data['enrollment'] assert enrollment_mode == enrollment['mode'] assert enrollment['is_active'] assert not response.data['user_has_passing_grade'] assert response.data['celebrations']['first_section'] assert not response.data['celebrations']['weekly_goal'] # This import errors in cms if it is imported at the top level from lms.djangoapps.course_goals.api import get_course_goal selected_goal = get_course_goal(self.user, self.course.id) if selected_goal: assert response.data['course_goals']['selected_goal'] == { 'days_per_week': selected_goal.days_per_week, 'subscribed_to_reminders': selected_goal.subscribed_to_reminders, } if enrollment_mode == 'audit': assert response.data['verify_identity_url'] is None assert response.data['verification_status'] == 'none' assert response.data['linkedin_add_to_profile_url'] is None else: assert response.data['certificate_data']['cert_status'] == 'earned_but_not_available' expected_verify_identity_url = IDVerificationService.get_verify_location( course_id=self.course.id ) # The response contains an absolute URL so this is only checking the path of the final assert expected_verify_identity_url in response.data['verify_identity_url'] assert response.data['verification_status'] == 'none' request = RequestFactory().request() cert_url = get_certificate_url(course_id=self.course.id, uuid=cert.verify_uuid) linkedin_url_params = { 'name': '{platform_name} Verified Certificate for {course_name}'.format( platform_name=settings.PLATFORM_NAME, course_name=self.course.display_name, ), 'certUrl': request.build_absolute_uri(cert_url), # default value from the LinkedInAddToProfileConfigurationFactory company_identifier 'organizationId': 1337, 'certId': cert.verify_uuid, 'issueYear': cert.created_date.year, 'issueMonth': cert.created_date.month, } expected_linkedin_url = ( 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&{params}'.format( params=urlencode(linkedin_url_params) ) ) assert response.data['linkedin_add_to_profile_url'] == expected_linkedin_url
def test_cert_url_empty_with_invalid_certificate(self): """ Test certificate url is empty if html view is not enabled and certificate is not yet generated """ url = certs_api.get_certificate_url(self.student.id, self.course.id) self.assertEqual(url, "")
def supplement_program_data(program_data, user): """Supplement program course codes with CourseOverview and CourseEnrollment data. Arguments: program_data (dict): Representation of a program. user (User): The user whose enrollments to inspect. """ for organization in program_data['organizations']: # TODO: Cache the results of the get_organization_by_short_name call so # the database is hit less frequently. org_obj = get_organization_by_short_name(organization['key']) if org_obj and org_obj.get('logo'): organization['img'] = org_obj['logo'].url for course_code in program_data['course_codes']: for run_mode in course_code['run_modes']: course_key = CourseKey.from_string(run_mode['course_key']) course_overview = CourseOverview.get_from_id(course_key) course_url = reverse('course_root', args=[course_key]) course_image_url = course_overview.course_image_url start_date_string = course_overview.start_datetime_text() end_date_string = course_overview.end_datetime_text() end_date = course_overview.end or datetime.datetime.max.replace(tzinfo=pytz.UTC) is_course_ended = end_date < timezone.now() is_enrolled = CourseEnrollment.is_enrolled(user, course_key) enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace(tzinfo=pytz.UTC) enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=pytz.UTC) is_enrollment_open = enrollment_start <= timezone.now() < enrollment_end enrollment_open_date = None if is_enrollment_open else strftime_localized(enrollment_start, 'SHORT_DATE') certificate_data = certificate_api.certificate_downloadable_status(user, course_key) certificate_uuid = certificate_data.get('uuid') certificate_url = certificate_api.get_certificate_url( course_id=course_key, uuid=certificate_uuid, ) if certificate_uuid else None required_mode_slug = run_mode['mode_slug'] enrolled_mode_slug, _ = CourseEnrollment.enrollment_mode_for_user(user, course_key) is_mode_mismatch = required_mode_slug != enrolled_mode_slug is_upgrade_required = is_enrolled and is_mode_mismatch # Requires that the ecommerce service be in use. required_mode = CourseMode.mode_for_course(course_key, required_mode_slug) ecommerce = EcommerceService() sku = getattr(required_mode, 'sku', None) if ecommerce.is_enabled(user) and sku: upgrade_url = ecommerce.checkout_page_url(required_mode.sku) if is_upgrade_required else None else: upgrade_url = None run_mode.update({ 'certificate_url': certificate_url, 'course_image_url': course_image_url, 'course_url': course_url, 'end_date': end_date_string, 'enrollment_open_date': enrollment_open_date, 'is_course_ended': is_course_ended, 'is_enrolled': is_enrolled, 'is_enrollment_open': is_enrollment_open, 'marketing_url': get_run_marketing_url(course_key, user), 'start_date': start_date_string, 'upgrade_url': upgrade_url, }) return program_data
def supplement_program_data(program_data, user): """Supplement program course codes with CourseOverview and CourseEnrollment data. Arguments: program_data (dict): Representation of a program. user (User): The user whose enrollments to inspect. """ for organization in program_data['organizations']: # TODO: Cache the results of the get_organization_by_short_name call so # the database is hit less frequently. org_obj = get_organization_by_short_name(organization['key']) if org_obj and org_obj.get('logo'): organization['img'] = org_obj['logo'].url for course_code in program_data['course_codes']: for run_mode in course_code['run_modes']: course_key = CourseKey.from_string(run_mode['course_key']) course_overview = CourseOverview.get_from_id(course_key) course_url = reverse('course_root', args=[course_key]) course_image_url = course_overview.course_image_url start_date_string = course_overview.start_datetime_text() end_date_string = course_overview.end_datetime_text() end_date = course_overview.end or datetime.datetime.max.replace( tzinfo=pytz.UTC) is_course_ended = end_date < timezone.now() is_enrolled = CourseEnrollment.is_enrolled(user, course_key) enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace( tzinfo=pytz.UTC) enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace( tzinfo=pytz.UTC) is_enrollment_open = enrollment_start <= timezone.now( ) < enrollment_end enrollment_open_date = None if is_enrollment_open else strftime_localized( enrollment_start, 'SHORT_DATE') certificate_data = certificate_api.certificate_downloadable_status( user, course_key) certificate_uuid = certificate_data.get('uuid') certificate_url = certificate_api.get_certificate_url( course_id=course_key, uuid=certificate_uuid, ) if certificate_uuid else None required_mode_slug = run_mode['mode_slug'] enrolled_mode_slug, _ = CourseEnrollment.enrollment_mode_for_user( user, course_key) is_mode_mismatch = required_mode_slug != enrolled_mode_slug is_upgrade_required = is_enrolled and is_mode_mismatch # Requires that the ecommerce service be in use. required_mode = CourseMode.mode_for_course(course_key, required_mode_slug) ecommerce = EcommerceService() sku = getattr(required_mode, 'sku', None) if ecommerce.is_enabled(user) and sku: upgrade_url = ecommerce.checkout_page_url( required_mode.sku) if is_upgrade_required else None else: upgrade_url = None run_mode.update({ 'certificate_url': certificate_url, 'course_image_url': course_image_url, 'course_url': course_url, 'end_date': end_date_string, 'enrollment_open_date': enrollment_open_date, 'is_course_ended': is_course_ended, 'is_enrolled': is_enrolled, 'is_enrollment_open': is_enrollment_open, # TODO: Not currently available on LMS. 'marketing_url': None, 'start_date': start_date_string, 'upgrade_url': upgrade_url, }) return program_data
def test_cert_url_empty_with_invalid_certificate(self): """ Test certificate url is empty if html view is not enabled and certificate is not yet generated """ url = get_certificate_url(self.user.id, self.course_run_key) assert url == ''
def _update_certificate_context(context, course, user_certificate, platform_name, preview_mode=None): """ Build up the certificate web view context using the provided values (Helper method to keep the view clean) """ # Populate dynamic output values using the course/certificate data loaded above certificate_type = context.get('certificate_type') # Override the defaults with any mode-specific static values context['certificate_id_number'] = user_certificate.verify_uuid context['certificate_verify_url'] = "{prefix}{uuid}{suffix}".format( prefix=context.get('certificate_verify_url_prefix'), uuid=user_certificate.verify_uuid, suffix=context.get('certificate_verify_url_suffix')) context['certificate_absolute_url'] = get_certificate_url( course_id=course.id, uuid=user_certificate.verify_uuid) # Translators: The format of the date includes the full name of the month if preview_mode: date = datetime.now() else: date = display_date_for_certificate(course, user_certificate) context['certificate_date_issued'] = _('{month} {day}, {year}').format( month=strftime_localized(date, "%B"), day=date.day, year=date.year) # Translators: This text represents the verification of the certificate context['document_meta_description'] = _( 'This is a valid {platform_name} certificate for {user_name}, ' 'who participated in {partner_short_name} {course_number}').format( platform_name=platform_name, user_name=context['accomplishment_copy_name'], partner_short_name=context['organization_short_name'], course_number=context['course_number']) # Translators: This text is bound to the HTML 'title' element of the page and appears in the browser title bar context['document_title'] = _( "{partner_short_name} {course_number} Certificate | {platform_name}" ).format(partner_short_name=context['organization_short_name'], course_number=context['course_number'], platform_name=platform_name) # Translators: This text fragment appears after the student's name (displayed in a large font) on the certificate # screen. The text describes the accomplishment represented by the certificate information displayed to the user context['accomplishment_copy_description_full'] = _( "successfully completed, received a passing grade, and was " "awarded this {platform_name} {certificate_type} " "Certificate of Completion in ").format( platform_name=platform_name, certificate_type=context.get("certificate_type")) certificate_type_description = get_certificate_description( user_certificate.mode, certificate_type, platform_name) if certificate_type_description: context['certificate_type_description'] = certificate_type_description # Translators: This text describes the purpose (and therefore, value) of a course certificate context['certificate_info_description'] = _( "{platform_name} acknowledges achievements through " "certificates, which are awarded for course activities " "that {platform_name} students complete.").format( platform_name=platform_name, tos_url=context.get('company_tos_url'), verified_cert_url=context.get('company_verified_certificate_url'))
def _cert_info(user, course_overview, cert_status): """ Implements the logic for cert_info -- split out for testing. Arguments: user (User): A user. course_overview (CourseOverview): A course. """ # simplify the status for the template using this lookup table template_state = { CertificateStatuses.generating: 'generating', CertificateStatuses.downloadable: 'downloadable', CertificateStatuses.notpassing: 'notpassing', CertificateStatuses.restricted: 'restricted', CertificateStatuses.auditing: 'auditing', CertificateStatuses.audit_passing: 'auditing', CertificateStatuses.audit_notpassing: 'auditing', CertificateStatuses.unverified: 'unverified', } certificate_earned_but_not_available_status = 'certificate_earned_but_not_available' default_status = 'processing' default_info = { 'status': default_status, 'show_survey_button': False, 'can_unenroll': True, } if cert_status is None: return default_info status = template_state.get(cert_status['status'], default_status) is_hidden_status = status in ('unavailable', 'processing', 'generating', 'notpassing', 'auditing') if ( not certificates_viewable_for_course(course_overview) and (status in CertificateStatuses.PASSED_STATUSES) and course_overview.certificate_available_date ): status = certificate_earned_but_not_available_status if ( course_overview.certificates_display_behavior == 'early_no_info' and is_hidden_status ): return default_info status_dict = { 'status': status, 'mode': cert_status.get('mode', None), 'linked_in_url': None, 'can_unenroll': status not in DISABLE_UNENROLL_CERT_STATES, } if status != default_status and course_overview.end_of_course_survey_url is not None: status_dict.update({ 'show_survey_button': True, 'survey_url': process_survey_link(course_overview.end_of_course_survey_url, user)}) else: status_dict['show_survey_button'] = False if status == 'downloadable': # showing the certificate web view button if certificate is downloadable state and feature flags are enabled. if has_html_certificates_enabled(course_overview): if course_overview.has_any_active_web_certificate: status_dict.update({ 'show_cert_web_view': True, 'cert_web_view_url': get_certificate_url(course_id=course_overview.id, uuid=cert_status['uuid']) }) else: # don't show download certificate button if we don't have an active certificate for course status_dict['status'] = 'unavailable' elif 'download_url' not in cert_status: log.warning( u"User %s has a downloadable cert for %s, but no download url", user.username, course_overview.id ) return default_info else: status_dict['download_url'] = cert_status['download_url'] # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they # can add the certificate information to their profile. linkedin_config = LinkedInAddToProfileConfiguration.current() # posting certificates to LinkedIn is not currently # supported in White Labels if linkedin_config.enabled and not theming_helpers.is_request_in_themed_site(): status_dict['linked_in_url'] = linkedin_config.add_to_profile_url( course_overview.id, course_overview.display_name, cert_status.get('mode'), cert_status['download_url'] ) if status in {'generating', 'downloadable', 'notpassing', 'restricted', 'auditing', 'unverified'}: cert_grade_percent = -1 persisted_grade_percent = -1 persisted_grade = CourseGradeFactory().read(user, course=course_overview, create_if_needed=False) if persisted_grade is not None: persisted_grade_percent = persisted_grade.percent if 'grade' in cert_status: cert_grade_percent = float(cert_status['grade']) if cert_grade_percent == -1 and persisted_grade_percent == -1: # Note: as of 11/20/2012, we know there are students in this state-- cs169.1x, # who need to be regraded (we weren't tracking 'notpassing' at first). # We can add a log.warning here once we think it shouldn't happen. return default_info status_dict['grade'] = text_type(max(cert_grade_percent, persisted_grade_percent)) return status_dict
def _cert_info(user, course_overview, cert_status): """ Implements the logic for cert_info -- split out for testing. Arguments: user (User): A user. course_overview (CourseOverview): A course. """ # simplify the status for the template using this lookup table template_state = { CertificateStatuses.generating: 'generating', CertificateStatuses.downloadable: 'downloadable', CertificateStatuses.notpassing: 'notpassing', CertificateStatuses.restricted: 'restricted', CertificateStatuses.auditing: 'auditing', CertificateStatuses.audit_passing: 'auditing', CertificateStatuses.audit_notpassing: 'auditing', CertificateStatuses.unverified: 'unverified', } certificate_earned_but_not_available_status = 'certificate_earned_but_not_available' default_status = 'processing' default_info = { 'status': default_status, 'show_survey_button': False, 'can_unenroll': True, } if cert_status is None: return default_info status = template_state.get(cert_status['status'], default_status) is_hidden_status = status in ('unavailable', 'processing', 'generating', 'notpassing', 'auditing') if ( not certificates_viewable_for_course(course_overview) and (status in CertificateStatuses.PASSED_STATUSES) and course_overview.certificate_available_date ): status = certificate_earned_but_not_available_status if ( course_overview.certificates_display_behavior == 'early_no_info' and is_hidden_status ): return default_info status_dict = { 'status': status, 'mode': cert_status.get('mode', None), 'linked_in_url': None, 'can_unenroll': status not in DISABLE_UNENROLL_CERT_STATES, } if status != default_status and course_overview.end_of_course_survey_url is not None: status_dict.update({ 'show_survey_button': True, 'survey_url': process_survey_link(course_overview.end_of_course_survey_url, user)}) else: status_dict['show_survey_button'] = False if status == 'downloadable': # showing the certificate web view button if certificate is downloadable state and feature flags are enabled. if has_html_certificates_enabled(course_overview): if course_overview.has_any_active_web_certificate: status_dict.update({ 'show_cert_web_view': True, 'cert_web_view_url': get_certificate_url(course_id=course_overview.id, uuid=cert_status['uuid']) }) else: # don't show download certificate button if we don't have an active certificate for course status_dict['status'] = 'unavailable' elif 'download_url' not in cert_status: log.warning( u"User %s has a downloadable cert for %s, but no download url", user.username, course_overview.id ) return default_info else: status_dict['download_url'] = cert_status['download_url'] # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they # can add the certificate information to their profile. linkedin_config = LinkedInAddToProfileConfiguration.current() # posting certificates to LinkedIn is not currently # supported in White Labels if linkedin_config.enabled and not theming_helpers.is_request_in_themed_site(): status_dict['linked_in_url'] = linkedin_config.add_to_profile_url( course_overview.id, course_overview.display_name, cert_status.get('mode'), cert_status['download_url'] ) if status in {'generating', 'downloadable', 'notpassing', 'restricted', 'auditing', 'unverified'}: cert_grade_percent = -1 persisted_grade_percent = -1 persisted_grade = CourseGradeFactory().read(user, course=course_overview, create_if_needed=False) if persisted_grade is not None: persisted_grade_percent = persisted_grade.percent if 'grade' in cert_status: cert_grade_percent = float(cert_status['grade']) if cert_grade_percent == -1 and persisted_grade_percent == -1: # Note: as of 11/20/2012, we know there are students in this state-- cs169.1x, # who need to be regraded (we weren't tracking 'notpassing' at first). # We can add a log.warning here once we think it shouldn't happen. return default_info grades_input = [cert_grade_percent, persisted_grade_percent] max_grade = None if all(grade is None for grade in grades_input) else max(filter(lambda x: x is not None, grades_input)) status_dict['grade'] = text_type(max_grade) return status_dict
def generate_user_cert(request, course_id): """Start generating a new certificate for the user. Certificate generation is allowed if: * The user has passed the course, and * The user does not already have a pending/completed certificate. Note that if an error occurs during certificate generation (for example, if the queue is down), then we simply mark the certificate generation task status as "error" and re-run the task with a management command. To students, the certificate will appear to be "generating" until it is re-run. Args: request (HttpRequest): The POST request to this view. course_id (unicode): The identifier for the course. Returns: HttpResponse: 200 on success, 400 if a new certificate cannot be generated. """ if not request.user.is_authenticated(): log.info(u"Anon user trying to generate certificate for %s", course_id) return HttpResponseBadRequest( _('You must be signed in to {platform_name} to create a certificate.' ).format(platform_name=configuration_helpers.get_value( 'PLATFORM_NAME', settings.PLATFORM_NAME))) student = request.user course_key = CourseKey.from_string(course_id) course = modulestore().get_course(course_key, depth=2) if not course: return HttpResponseBadRequest(_("Course is not valid")) if not is_course_passed(course, None, student, request): return HttpResponseBadRequest( _("Your certificate will be available when you pass the course.")) certificate_status = certs_api.certificate_downloadable_status( student, course.id) if certificate_status["is_downloadable"]: return HttpResponseBadRequest( _("Certificate has already been created.")) elif certificate_status["is_generating"]: return HttpResponseBadRequest(_("Certificate is being created.")) else: # If the certificate is not already in-process or completed, # then create a new certificate generation task. # If the certificate cannot be added to the queue, this will # mark the certificate with "error" status, so it can be re-run # with a management command. From the user's perspective, # it will appear that the certificate task was submitted successfully. base_url = settings.LMS_ROOT_URL MandrillClient().send_mail( MandrillClient.COURSE_COMPLETION_TEMPLATE, student.email, { 'course_name': course.display_name, 'course_url': get_course_link(course_id=course.id), 'full_name': student.first_name + " " + student.last_name, 'certificate_url': base_url + get_certificate_url(user_id=student.id, course_id=course.id), 'course_library_url': base_url + '/courses', }) certs_api.generate_user_certificates(student, course.id, course=course, generation_mode='self') _track_successful_certificate_generation(student.id, course.id) return HttpResponse()