From d05b62a2831d582aa6cb596f713fe72abf9bbd62 Mon Sep 17 00:00:00 2001 From: Andy Armstrong Date: Tue, 5 Sep 2017 16:18:46 -0400 Subject: [PATCH] Only show passing certificates on a learner profile LEARNER-2486 --- lms/djangoapps/certificates/models.py | 2 - .../tests/views/test_learner_profile.py | 47 +++++++++++++++++-- .../views/learner_achievements.py | 21 ++++++--- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index 3759456ee2..a4ad99b394 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -513,8 +513,6 @@ def certificate_status(generated_certificate): certificate generation yet. generating - A request has been made to generate a certificate, but it has not been generated yet. - regenerating - A request has been made to regenerate a certificate, - but it has not been generated yet. deleting - A request has been made to delete a certificate. deleted - The certificate has been deleted. diff --git a/openedx/features/learner_profile/tests/views/test_learner_profile.py b/openedx/features/learner_profile/tests/views/test_learner_profile.py index fcac442cd1..4ca735e040 100644 --- a/openedx/features/learner_profile/tests/views/test_learner_profile.py +++ b/openedx/features/learner_profile/tests/views/test_learner_profile.py @@ -9,6 +9,8 @@ from course_modes.models import CourseMode from django.conf import settings from django.core.urlresolvers import reverse from django.test.client import RequestFactory +from lms.djangoapps.certificates.api import is_passing_status +from opaque_keys.edx.locator import CourseLocator from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag from openedx.features.learner_profile.views.learner_profile import learner_profile_context from student.tests.factories import CourseEnrollmentFactory, UserFactory @@ -118,15 +120,15 @@ class LearnerProfileViewTest(UrlResetMixin, ModuleStoreTestCase): response = self.client.get(path=profile_path) self.assertEqual(404, response.status_code) - def _create_certificate(self, enrollment_mode): + def _create_certificate(self, course_key=None, enrollment_mode=CourseMode.HONOR, status='downloadable'): """Simulate that the user has a generated certificate. """ CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id, mode=enrollment_mode) return GeneratedCertificateFactory( user=self.user, - course_id=self.course.id, + course_id=course_key or self.course.id, mode=enrollment_mode, download_url=self.DOWNLOAD_URL, - status="downloadable" + status=status, ) @ddt.data(CourseMode.HONOR, CourseMode.PROFESSIONAL, CourseMode.VERIFIED) @@ -136,13 +138,50 @@ class LearnerProfileViewTest(UrlResetMixin, ModuleStoreTestCase): Verify that certificates are displayed with the correct card mode. """ # Add new certificate - cert = self._create_certificate(cert_mode) + cert = self._create_certificate(enrollment_mode=cert_mode) cert.save() response = self.client.get('/u/{username}'.format(username=self.user.username)) self.assertContains(response, 'card certificate-card mode-{cert_mode}'.format(cert_mode=cert_mode)) + @ddt.data( + ['downloadable', True], + ['notpassing', False], + ) + @ddt.unpack + @override_waffle_flag(SHOW_ACHIEVEMENTS_FLAG, active=True) + def test_certificate_status_visibility(self, status, is_passed_status): + """ + Verify that certificates are only displayed for passing status. + """ + # Add new certificate + cert = self._create_certificate(status=status) + cert.save() + + # Ensure that this test is actually using both passing and non-passing certs. + self.assertEqual(is_passing_status(cert.status), is_passed_status) + + response = self.client.get('/u/{username}'.format(username=self.user.username)) + + if is_passed_status: + self.assertContains(response, 'card certificate-card mode-{cert_mode}'.format(cert_mode=cert.mode)) + else: + self.assertNotContains(response, 'card certificate-card mode-{cert_mode}'.format(cert_mode=cert.mode)) + + @override_waffle_flag(SHOW_ACHIEVEMENTS_FLAG, active=True) + def test_certificate_for_missing_course(self): + """ + Verify that a certificate is not shown for a missing course. + """ + # Add new certificate + cert = self._create_certificate(course_key=CourseLocator.from_string('course-v1:edX+INVALID+1')) + cert.save() + + response = self.client.get('/u/{username}'.format(username=self.user.username)) + + self.assertNotContains(response, 'card certificate-card mode-{cert_mode}'.format(cert_mode=cert.mode)) + @ddt.data(True, False) @override_waffle_flag(SHOW_ACHIEVEMENTS_FLAG, active=True) def test_no_certificate_visibility(self, own_profile): diff --git a/openedx/features/learner_profile/views/learner_achievements.py b/openedx/features/learner_profile/views/learner_achievements.py index f605e192df..19c944ffc2 100644 --- a/openedx/features/learner_profile/views/learner_achievements.py +++ b/openedx/features/learner_profile/views/learner_achievements.py @@ -2,9 +2,9 @@ Views to render a learner's achievements. """ -from courseware.courses import get_course_overview_with_access from django.template.loader import render_to_string from lms.djangoapps.certificates import api as certificate_api +from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.plugin_api.views import EdxFragmentView from web_fragments.fragment import Fragment @@ -34,9 +34,18 @@ class LearnerAchievementsFragmentView(EdxFragmentView): Returns a user's certificates sorted by course name. """ course_certificates = certificate_api.get_certificates_for_user(username) + passing_certificates = [] for course_certificate in course_certificates: - course_key = course_certificate['course_key'] - course_overview = get_course_overview_with_access(request.user, 'load', course_key) - course_certificate['course'] = course_overview - course_certificates.sort(key=lambda certificate: certificate['course'].display_name_with_default) - return course_certificates + if course_certificate.get('is_passing', False): + course_key = course_certificate['course_key'] + try: + course_overview = CourseOverview.get_from_id(course_key) + course_certificate['course'] = course_overview + passing_certificates.append(course_certificate) + except CourseOverview.DoesNotExist: + # This is unlikely to fail as the course should exist. + # Ideally the cert should have all the information that + # it needs. This might be solved by the Credentials API. + pass + passing_certificates.sort(key=lambda certificate: certificate['course'].display_name_with_default) + return passing_certificates