Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
112aa71f
Commit
112aa71f
authored
Jun 06, 2016
by
Awais
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adding logic to check if cert is invalidate then user cannot see the regen button.
ECOM-4217
parent
6eef8ee9
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
302 additions
and
5 deletions
+302
-5
lms/djangoapps/certificates/api.py
+21
-0
lms/djangoapps/certificates/models.py
+19
-0
lms/djangoapps/certificates/tests/test_api.py
+117
-1
lms/djangoapps/certificates/tests/test_models.py
+52
-1
lms/djangoapps/courseware/tests/test_views.py
+81
-1
lms/djangoapps/courseware/views/views.py
+8
-1
lms/templates/courseware/progress.html
+4
-1
No files found.
lms/djangoapps/certificates/api.py
View file @
112aa71f
...
...
@@ -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.
...
...
lms/djangoapps/certificates/models.py
View file @
112aa71f
...
...
@@ -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
...
...
lms/djangoapps/certificates/tests/test_api.py
View file @
112aa71f
...
...
@@ -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
()
...
...
@@ -209,6 +213,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. """
@classmethod
...
...
lms/djangoapps/certificates/tests/test_models.py
View file @
112aa71f
...
...
@@ -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
)
)
lms/djangoapps/courseware/tests/test_views.py
View file @
112aa71f
...
...
@@ -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
...
...
@@ -1414,6 +1417,83 @@ 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
=
views
.
progress
(
self
.
request
,
course_id
=
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
=
views
.
progress
(
self
.
request
,
course_id
=
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
=
views
.
progress
(
self
.
request
,
course_id
=
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
):
...
...
lms/djangoapps/courseware/views/views.py
View file @
112aa71f
...
...
@@ -747,10 +747,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 don't show the generate button.
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.
...
...
lms/templates/courseware/progress.html
View file @
112aa71f
...
...
@@ -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"
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment