Skip to content
Snippets Groups Projects
Commit 0bd1f4cc authored by Awais's avatar Awais Committed by Ahsan Ulhaq
Browse files

Adding logic to check if cert is invalidate then user cannot see the regen button.

ECOM-4217
parent c7e6de0d
No related merge requests found
......@@ -21,6 +21,7 @@ from util.organizations_helpers import get_course_organizations
from certificates.models import (
CertificateGenerationConfiguration,
CertificateGenerationCourseSetting,
CertificateInvalidation,
CertificateStatuses,
CertificateTemplate,
CertificateTemplateAsset,
......@@ -273,6 +274,26 @@ def set_cert_generation_enabled(course_key, is_enabled):
log.info(u"Disabled self-generated certificates for course '%s'.", unicode(course_key))
def is_certificate_invalid(student, course_key):
"""Check that whether the student in the course has been invalidated
for receiving certificates.
Arguments:
student (user object): logged-in user
course_key (CourseKey): The course identifier.
Returns:
Boolean denoting whether the student in the course is invalidated
to receive certificates
"""
is_invalid = False
certificate = GeneratedCertificate.certificate_for_student(student, course_key)
if certificate is not None:
is_invalid = CertificateInvalidation.has_certificate_invalidation(student, course_key)
return is_invalid
def cert_generation_enabled(course_key):
"""Check whether certificate generation is enabled for a course.
......
......@@ -436,6 +436,25 @@ class CertificateInvalidation(TimeStampedModel):
})
return data
@classmethod
def has_certificate_invalidation(cls, student, course_key):
"""Check that whether the student in the course has been invalidated
for receiving certificates.
Arguments:
student (user): logged-in user
course_key (CourseKey): The course associated with the certificate.
Returns:
Boolean denoting whether the student in the course is invalidated
to receive certificates
"""
return cls.objects.filter(
generated_certificate__course_id=course_key,
active=True,
generated_certificate__user=student
).exists()
@receiver(COURSE_CERT_AWARDED, sender=GeneratedCertificate)
def handle_course_cert_awarded(sender, user, course_key, **kwargs): # pylint: disable=unused-argument
......
......@@ -13,6 +13,7 @@ from opaque_keys.edx.locator import CourseLocator
from config_models.models import cache
from course_modes.models import CourseMode
from course_modes.tests.factories import CourseModeFactory
from courseware.tests.factories import GlobalStaffFactory
from microsite_configuration import microsite
from student.models import CourseEnrollment
from student.tests.factories import UserFactory
......@@ -32,7 +33,10 @@ from certificates.models import (
certificate_status_for_student,
)
from certificates.queue import XQueueCertInterface, XQueueAddToQueueError
from certificates.tests.factories import GeneratedCertificateFactory
from certificates.tests.factories import (
CertificateInvalidationFactory,
GeneratedCertificateFactory
)
FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy()
......@@ -208,6 +212,118 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes
)
@attr('shard_1')
@ddt.ddt
class CertificateisInvalid(WebCertificateTestMixin, ModuleStoreTestCase):
"""Tests for the `is_certificate_invalid` helper function. """
def setUp(self):
super(CertificateisInvalid, self).setUp()
self.student = UserFactory()
self.course = CourseFactory.create(
org='edx',
number='verified',
display_name='Verified Course'
)
self.global_staff = GlobalStaffFactory()
self.request_factory = RequestFactory()
def test_method_with_no_certificate(self):
""" Test the case when there is no certificate for a user for a specific course. """
course = CourseFactory.create(
org='edx',
number='honor',
display_name='Course 1'
)
# Also check query count for 'is_certificate_invalid' method.
with self.assertNumQueries(1):
self.assertFalse(
certs_api.is_certificate_invalid(self.student, course.id)
)
@ddt.data(
CertificateStatuses.generating,
CertificateStatuses.downloadable,
CertificateStatuses.notpassing,
CertificateStatuses.error,
CertificateStatuses.unverified,
CertificateStatuses.deleted,
CertificateStatuses.unavailable,
)
def test_method_with_invalidated_cert(self, status):
""" Verify that if certificate is marked as invalid than method will return
True. """
generated_cert = self._generate_cert(status)
self._invalidate_certificate(generated_cert, True)
self.assertTrue(
certs_api.is_certificate_invalid(self.student, self.course.id)
)
@ddt.data(
CertificateStatuses.generating,
CertificateStatuses.downloadable,
CertificateStatuses.notpassing,
CertificateStatuses.error,
CertificateStatuses.unverified,
CertificateStatuses.deleted,
CertificateStatuses.unavailable,
)
def test_method_with_inactive_invalidated_cert(self, status):
""" Verify that if certificate is valid but it's invalidated status is
false than method will return false. """
generated_cert = self._generate_cert(status)
self._invalidate_certificate(generated_cert, False)
self.assertFalse(
certs_api.is_certificate_invalid(self.student, self.course.id)
)
@ddt.data(
CertificateStatuses.generating,
CertificateStatuses.downloadable,
CertificateStatuses.notpassing,
CertificateStatuses.error,
CertificateStatuses.unverified,
CertificateStatuses.deleted,
CertificateStatuses.unavailable,
)
def test_method_with_all_statues(self, status):
""" Verify method return True if certificate has valid status but it is
marked as invalid in CertificateInvalidation table. """
certificate = self._generate_cert(status)
CertificateInvalidationFactory.create(
generated_certificate=certificate,
invalidated_by=self.global_staff,
active=True
)
# Also check query count for 'is_certificate_invalid' method.
with self.assertNumQueries(2):
self.assertTrue(
certs_api.is_certificate_invalid(self.student, self.course.id)
)
def _invalidate_certificate(self, certificate, active):
""" Dry method to mark certificate as invalid. """
CertificateInvalidationFactory.create(
generated_certificate=certificate,
invalidated_by=self.global_staff,
active=active
)
# Invalidate user certificate
certificate.invalidate()
self.assertFalse(certificate.is_valid())
def _generate_cert(self, status):
""" Dry method to generate certificate. """
return GeneratedCertificateFactory.create(
user=self.student,
course_id=self.course.id,
status=status,
mode='verified'
)
@attr('shard_1')
class CertificateGetTests(SharedModuleStoreTestCase):
"""Tests for the `test_get_certificate_for_user` helper function. """
......
......@@ -15,11 +15,15 @@ from certificates.models import (
ExampleCertificateSet,
CertificateHtmlViewConfiguration,
CertificateTemplateAsset,
CertificateInvalidation,
GeneratedCertificate,
CertificateStatuses,
CertificateGenerationHistory,
)
from certificates.tests.factories import GeneratedCertificateFactory
from certificates.tests.factories import (
CertificateInvalidationFactory,
GeneratedCertificateFactory
)
from instructor_task.tests.factories import InstructorTaskFactory
from opaque_keys.edx.locator import CourseLocator
from student.tests.factories import AdminFactory, UserFactory
......@@ -300,3 +304,50 @@ class TestCertificateGenerationHistory(TestCase):
certificate_generation_history.get_task_name(),
expected
)
@attr('shard_1')
class CertificateInvalidationTest(SharedModuleStoreTestCase):
"""
Test for the Certificate Invalidation model.
"""
def setUp(self):
super(CertificateInvalidationTest, self).setUp()
self.course = CourseFactory()
self.user = UserFactory()
self.course_id = self.course.id # pylint: disable=no-member
self.certificate = GeneratedCertificateFactory.create(
status=CertificateStatuses.downloadable,
user=self.user,
course_id=self.course_id
)
def test_is_certificate_invalid_method(self):
""" Verify that method return false if certificate is valid. """
self.assertFalse(
CertificateInvalidation.has_certificate_invalidation(self.user, self.course_id)
)
def test_is_certificate_invalid_with_invalid_cert(self):
""" Verify that method return true if certificate is invalid. """
invalid_cert = CertificateInvalidationFactory.create(
generated_certificate=self.certificate,
invalidated_by=self.user
)
# Invalidate user certificate
self.certificate.invalidate()
self.assertTrue(
CertificateInvalidation.has_certificate_invalidation(self.user, self.course_id)
)
# mark the entry as in-active.
invalid_cert.active = False
invalid_cert.save()
# After making the certificate valid method will return false.
self.assertFalse(
CertificateInvalidation.has_certificate_invalidation(self.user, self.course_id)
)
......@@ -32,7 +32,10 @@ import courseware.views.views as views
import shoppingcart
from certificates import api as certs_api
from certificates.models import CertificateStatuses, CertificateGenerationConfiguration
from certificates.tests.factories import GeneratedCertificateFactory
from certificates.tests.factories import (
CertificateInvalidationFactory,
GeneratedCertificateFactory
)
from commerce.models import CommerceConfiguration
from course_modes.models import CourseMode
from course_modes.tests.factories import CourseModeFactory
......@@ -1387,6 +1390,89 @@ class ProgressPageTests(ModuleStoreTestCase):
cert_button_hidden,
'Request Certificate' not in resp.content)
@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True})
@patch('courseware.grades.grade', Mock(return_value={'grade': 'Pass', 'percent': 0.75, 'section_breakdown': [],
'grade_breakdown': []}))
def test_page_with_invalidated_certificate_with_html_view(self):
"""
Verify that for html certs if certificate is marked as invalidated than
re-generate button should not appear on progress page.
"""
generated_certificate = self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)
CertificateGenerationConfiguration(enabled=True).save()
certs_api.set_cert_generation_enabled(self.course.id, True)
# Course certificate configurations
certificates = [
{
'id': 1,
'name': 'dummy',
'description': 'dummy description',
'course_title': 'dummy title',
'signatories': [],
'version': 1,
'is_active': True
}
]
self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save()
self.store.update_item(self.course, self.user.id)
resp = self.client.get(
reverse('progress', args=[unicode(self.course.id)])
)
self.assertContains(resp, u"View Certificate")
self.assert_invalidate_certificate(generated_certificate)
@patch('courseware.grades.grade', Mock(return_value={'grade': 'Pass', 'percent': 0.75, 'section_breakdown': [],
'grade_breakdown': []}))
def test_page_with_invalidated_certificate_with_pdf(self):
"""
Verify that for pdf certs if certificate is marked as invalidated than
re-generate button should not appear on progress page.
"""
generated_certificate = self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)
CertificateGenerationConfiguration(enabled=True).save()
certs_api.set_cert_generation_enabled(self.course.id, True)
resp = self.client.get(
reverse('progress', args=[unicode(self.course.id)])
)
self.assertContains(resp, u'Download Your Certificate')
self.assert_invalidate_certificate(generated_certificate)
def assert_invalidate_certificate(self, certificate):
""" Dry method to mark certificate as invalid. And assert the response. """
CertificateInvalidationFactory.create(
generated_certificate=certificate,
invalidated_by=self.user
)
# Invalidate user certificate
certificate.invalidate()
resp = self.client.get(
reverse('progress', args=[unicode(self.course.id)])
)
self.assertNotContains(resp, u'Request Certificate')
self.assertContains(resp, u'Your certificate has been invalidated.')
self.assertContains(resp, u'Please contact your course team if you have any questions.')
self.assertNotContains(resp, u'View Your Certificate')
self.assertNotContains(resp, u'Download Your Certificate')
def generate_certificate(self, url, mode):
""" Dry method to generate certificate. """
return GeneratedCertificateFactory.create(
user=self.user,
course_id=self.course.id,
status=CertificateStatuses.downloadable,
download_url=url,
mode=mode
)
@attr('shard_1')
class VerifyCourseKeyDecoratorTests(TestCase):
......
......@@ -753,10 +753,17 @@ def _progress(request, course_key, student_id):
'passed': is_course_passed(course, grade_summary),
'show_generate_cert_btn': show_generate_cert_btn,
'credit_course_requirements': _credit_course_requirements(course_key, student),
'missing_required_verification': missing_required_verification
'missing_required_verification': missing_required_verification,
'certificate_invalidated': False,
}
if show_generate_cert_btn:
# If current certificate is invalidated by instructor
# then show the certificate invalidated message.
context.update({
'certificate_invalidated': certs_api.is_certificate_invalid(student, course_key)
})
cert_status = certs_api.certificate_downloadable_status(student, course_key)
context.update(cert_status)
# showing the certificate web view button if feature flags are enabled.
......
......@@ -55,7 +55,10 @@ from django.utils.http import urlquote_plus
<div class="auto-cert-message" id="course-success">
<div class="has-actions">
<% post_url = reverse('generate_user_cert', args=[unicode(course.id)]) %>
% if is_downloadable:
## If current certificate is invalidated by instructor then don't show the generate button.
% if certificate_invalidated:
<p class="copy">${_("Your certificate has been invalidated. Please contact your course team if you have any questions.")}</p>
%elif is_downloadable:
<div class="msg-content">
<h2 class="title">${_("Your certificate is available")}</h2>
<p class="copy">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment