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
ab713181
Commit
ab713181
authored
Sep 11, 2017
by
Alex Dusenbery
Committed by
Alex Dusenbery
Sep 19, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
EDUCATOR-1316 | Refactor courseware.views.views._get_cert_data and related functions.
parent
fd07dea0
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
444 additions
and
378 deletions
+444
-378
common/djangoapps/student/views.py
+1
-1
lms/djangoapps/certificates/api.py
+61
-82
lms/djangoapps/certificates/tests/test_webview_views.py
+1
-1
lms/djangoapps/certificates/views/webview.py
+16
-14
lms/djangoapps/courseware/tests/test_views.py
+105
-145
lms/djangoapps/courseware/views/views.py
+116
-125
lms/djangoapps/instructor/views/instructor_dashboard.py
+1
-1
lms/djangoapps/instructor_task/tests/test_tasks_helper.py
+1
-1
openedx/core/djangoapps/certificates/api.py
+46
-0
openedx/core/djangoapps/certificates/apps.py
+13
-0
openedx/core/djangoapps/certificates/tests/test_api.py
+83
-8
No files found.
common/djangoapps/student/views.py
View file @
ab713181
...
@@ -376,7 +376,7 @@ def _cert_info(user, course_overview, cert_status, course_mode): # pylint: disa
...
@@ -376,7 +376,7 @@ def _cert_info(user, course_overview, cert_status, course_mode): # pylint: disa
if
status
==
'ready'
:
if
status
==
'ready'
:
# showing the certificate web view button if certificate is ready state and feature flags are enabled.
# showing the certificate web view button if certificate is ready state and feature flags are enabled.
if
has_html_certificates_enabled
(
course_overview
.
id
,
course_overview
):
if
has_html_certificates_enabled
(
course_overview
):
if
course_overview
.
has_any_active_web_certificate
:
if
course_overview
.
has_any_active_web_certificate
:
status_dict
.
update
({
status_dict
.
update
({
'show_cert_web_view'
:
True
,
'show_cert_web_view'
:
True
,
...
...
lms/djangoapps/certificates/api.py
View file @
ab713181
...
@@ -150,7 +150,12 @@ def generate_user_certificates(student, course_key, course=None, insecure=False,
...
@@ -150,7 +150,12 @@ def generate_user_certificates(student, course_key, course=None, insecure=False,
xqueue
=
XQueueCertInterface
()
xqueue
=
XQueueCertInterface
()
if
insecure
:
if
insecure
:
xqueue
.
use_https
=
False
xqueue
.
use_https
=
False
generate_pdf
=
not
has_html_certificates_enabled
(
course_key
,
course
)
if
not
course
:
course
=
modulestore
()
.
get_course
(
course_key
,
depth
=
0
)
generate_pdf
=
not
has_html_certificates_enabled
(
course
)
cert
=
xqueue
.
add_cert
(
cert
=
xqueue
.
add_cert
(
student
,
student
,
course_key
,
course_key
,
...
@@ -198,7 +203,11 @@ def regenerate_user_certificates(student, course_key, course=None,
...
@@ -198,7 +203,11 @@ def regenerate_user_certificates(student, course_key, course=None,
if
insecure
:
if
insecure
:
xqueue
.
use_https
=
False
xqueue
.
use_https
=
False
generate_pdf
=
not
has_html_certificates_enabled
(
course_key
,
course
)
if
not
course
:
course
=
modulestore
()
.
get_course
(
course_key
,
depth
=
0
)
generate_pdf
=
not
has_html_certificates_enabled
(
course
)
return
xqueue
.
regen_cert
(
return
xqueue
.
regen_cert
(
student
,
student
,
course_key
,
course_key
,
...
@@ -353,44 +362,6 @@ def generate_example_certificates(course_key):
...
@@ -353,44 +362,6 @@ def generate_example_certificates(course_key):
xqueue
.
add_example_cert
(
cert
)
xqueue
.
add_example_cert
(
cert
)
def
has_html_certificates_enabled
(
course_key
,
course
=
None
):
"""
Determine if a course has html certificates enabled.
Arguments:
course_key (CourseKey|str): A course key or a string representation
of one.
course (CourseDescriptor|CourseOverview): A course.
"""
# If the feature is disabled, then immediately return a False
if
not
settings
.
FEATURES
.
get
(
'CERTIFICATES_HTML_VIEW'
,
False
):
return
False
# If we don't have a course object, we'll need to assemble one
if
not
course
:
# Initialize a course key if necessary
if
not
isinstance
(
course_key
,
CourseKey
):
try
:
course_key
=
CourseKey
.
from_string
(
course_key
)
except
InvalidKeyError
:
log
.
warning
(
(
'Unable to parse course_key "
%
s"'
,
course_key
),
exc_info
=
True
)
return
False
# Pull the course data from the cache
try
:
course
=
CourseOverview
.
get_from_id
(
course_key
)
except
:
# pylint: disable=bare-except
log
.
warning
(
(
'Unable to load CourseOverview object for course_key "
%
s"'
,
unicode
(
course_key
)),
exc_info
=
True
)
# Return the flag on the course object
return
course
.
cert_html_view_enabled
if
course
else
False
def
example_certificates_status
(
course_key
):
def
example_certificates_status
(
course_key
):
"""Check the status of example certificates for a course.
"""Check the status of example certificates for a course.
...
@@ -425,50 +396,58 @@ def example_certificates_status(course_key):
...
@@ -425,50 +396,58 @@ def example_certificates_status(course_key):
return
ExampleCertificateSet
.
latest_status
(
course_key
)
return
ExampleCertificateSet
.
latest_status
(
course_key
)
def
_safe_course_key
(
course_key
):
if
not
isinstance
(
course_key
,
CourseKey
):
return
CourseKey
.
from_string
(
course_key
)
return
course_key
def
_course_from_key
(
course_key
):
return
CourseOverview
.
get_from_id
(
_safe_course_key
(
course_key
))
def
_certificate_html_url
(
user_id
,
course_id
,
uuid
):
if
uuid
:
return
reverse
(
'certificates:render_cert_by_uuid'
,
kwargs
=
{
'certificate_uuid'
:
uuid
})
elif
user_id
and
course_id
:
kwargs
=
{
"user_id"
:
str
(
user_id
),
"course_id"
:
unicode
(
course_id
)}
return
reverse
(
'certificates:html_view'
,
kwargs
=
kwargs
)
return
''
def
_certificate_download_url
(
user_id
,
course_id
):
try
:
user_certificate
=
GeneratedCertificate
.
eligible_certificates
.
get
(
user
=
user_id
,
course_id
=
_safe_course_key
(
course_id
)
)
return
user_certificate
.
download_url
except
GeneratedCertificate
.
DoesNotExist
:
log
.
critical
(
'Unable to lookup certificate
\n
'
'user id:
%
d
\n
'
'course:
%
s'
,
user_id
,
unicode
(
course_id
)
)
return
''
def
has_html_certificates_enabled
(
course
):
if
not
settings
.
FEATURES
.
get
(
'CERTIFICATES_HTML_VIEW'
,
False
):
return
False
return
course
.
cert_html_view_enabled
def
get_certificate_url
(
user_id
=
None
,
course_id
=
None
,
uuid
=
None
):
def
get_certificate_url
(
user_id
=
None
,
course_id
=
None
,
uuid
=
None
):
"""
url
=
''
:return certificate url for web or pdf certs. In case of web certs returns either old
or new cert url based on given parameters. For web certs if `uuid` is it would return
course
=
_course_from_key
(
course_id
)
new uuid based cert url url otherwise old url.
if
not
course
:
"""
return
url
url
=
""
if
has_html_certificates_enabled
(
course_id
):
if
uuid
:
url
=
reverse
(
'certificates:render_cert_by_uuid'
,
kwargs
=
dict
(
certificate_uuid
=
uuid
)
)
elif
user_id
and
course_id
:
url
=
reverse
(
'certificates:html_view'
,
kwargs
=
{
"user_id"
:
str
(
user_id
),
"course_id"
:
unicode
(
course_id
),
}
)
else
:
if
isinstance
(
course_id
,
basestring
):
try
:
course_id
=
CourseKey
.
from_string
(
course_id
)
except
InvalidKeyError
:
log
.
warning
(
(
'Unable to parse course_id "
%
s"'
,
course_id
),
exc_info
=
True
)
return
url
try
:
user_certificate
=
GeneratedCertificate
.
eligible_certificates
.
get
(
user
=
user_id
,
course_id
=
course_id
)
url
=
user_certificate
.
download_url
except
GeneratedCertificate
.
DoesNotExist
:
log
.
critical
(
'Unable to lookup certificate
\n
'
'user id:
%
d
\n
'
'course:
%
s'
,
user_id
,
unicode
(
course_id
)
)
if
has_html_certificates_enabled
(
course
):
url
=
_certificate_html_url
(
user_id
,
course_id
,
uuid
)
else
:
url
=
_certificate_download_url
(
user_id
,
course_id
)
return
url
return
url
...
...
lms/djangoapps/certificates/tests/test_webview_views.py
View file @
ab713181
...
@@ -674,7 +674,7 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
...
@@ -674,7 +674,7 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
self
.
_add_course_certificates
(
count
=
1
,
signatory_count
=
0
)
self
.
_add_course_certificates
(
count
=
1
,
signatory_count
=
0
)
test_url
=
get_certificate_url
(
test_url
=
get_certificate_url
(
user_id
=
self
.
user
.
id
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
)
course_id
=
unicode
(
self
.
course
.
id
)
)
)
response
=
self
.
client
.
get
(
test_url
)
response
=
self
.
client
.
get
(
test_url
)
self
.
assertNotIn
(
'Signatory_Name 0'
,
response
.
content
)
self
.
assertNotIn
(
'Signatory_Name 0'
,
response
.
content
)
...
...
lms/djangoapps/certificates/views/webview.py
View file @
ab713181
...
@@ -26,8 +26,7 @@ from certificates.api import (
...
@@ -26,8 +26,7 @@ from certificates.api import (
get_certificate_footer_context
,
get_certificate_footer_context
,
get_certificate_header_context
,
get_certificate_header_context
,
get_certificate_template
,
get_certificate_template
,
get_certificate_url
,
get_certificate_url
has_html_certificates_enabled
)
)
from
certificates.models
import
(
from
certificates.models
import
(
CertificateGenerationCourseSetting
,
CertificateGenerationCourseSetting
,
...
@@ -48,8 +47,7 @@ from student.models import LinkedInAddToProfileConfiguration
...
@@ -48,8 +47,7 @@ from student.models import LinkedInAddToProfileConfiguration
from
util
import
organizations_helpers
as
organization_api
from
util
import
organizations_helpers
as
organization_api
from
util.date_utils
import
strftime_localized
from
util.date_utils
import
strftime_localized
from
util.views
import
handle_500
from
util.views
import
handle_500
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -523,23 +521,18 @@ def render_html_view(request, user_id, course_id):
...
@@ -523,23 +521,18 @@ def render_html_view(request, user_id, course_id):
_update_context_with_basic_info
(
context
,
course_id
,
platform_name
,
configuration
)
_update_context_with_basic_info
(
context
,
course_id
,
platform_name
,
configuration
)
invalid_template_path
=
'certificates/invalid.html'
invalid_template_path
=
'certificates/invalid.html'
# Kick the user back to the "Invalid" screen if the feature is disabled
# Kick the user back to the "Invalid" screen if the feature is disabled globally
if
not
has_html_certificates_enabled
(
course_id
):
if
not
settings
.
FEATURES
.
get
(
'CERTIFICATES_HTML_VIEW'
,
False
):
log
.
info
(
"Invalid cert: HTML certificates disabled for
%
s. User id:
%
d"
,
course_id
,
user_id
,
)
return
render_to_response
(
invalid_template_path
,
context
)
return
render_to_response
(
invalid_template_path
,
context
)
# Load the course and user objects
# Load the course and user objects
try
:
try
:
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
user
=
User
.
objects
.
get
(
id
=
user_id
)
user
=
User
.
objects
.
get
(
id
=
user_id
)
course
=
modulestore
()
.
get_course
(
course_key
)
course
=
get_course_by_id
(
course_key
)
# For any
other expected
exceptions, kick the user back to the "Invalid" screen
# For any
course or user
exceptions, kick the user back to the "Invalid" screen
except
(
InvalidKeyError
,
ItemNotFoundError
,
User
.
DoesNotExist
)
as
exception
:
except
(
InvalidKeyError
,
User
.
DoesNotExist
,
Http404
)
as
exception
:
error_str
=
(
error_str
=
(
"Invalid cert: error finding course
%
s or user with id "
"Invalid cert: error finding course
%
s or user with id "
"
%
d. Specific error:
%
s"
"
%
d. Specific error:
%
s"
...
@@ -547,6 +540,15 @@ def render_html_view(request, user_id, course_id):
...
@@ -547,6 +540,15 @@ def render_html_view(request, user_id, course_id):
log
.
info
(
error_str
,
course_id
,
user_id
,
str
(
exception
))
log
.
info
(
error_str
,
course_id
,
user_id
,
str
(
exception
))
return
render_to_response
(
invalid_template_path
,
context
)
return
render_to_response
(
invalid_template_path
,
context
)
# Kick the user back to the "Invalid" screen if the feature is disabled for the course
if
not
course
.
cert_html_view_enabled
:
log
.
info
(
"Invalid cert: HTML certificates disabled for
%
s. User id:
%
d"
,
course_id
,
user_id
,
)
return
render_to_response
(
invalid_template_path
,
context
)
# Load user's certificate
# Load user's certificate
user_certificate
=
_get_user_certificate
(
request
,
user
,
course_key
,
course
,
preview_mode
)
user_certificate
=
_get_user_certificate
(
request
,
user
,
course_key
,
course
,
preview_mode
)
if
not
user_certificate
:
if
not
user_certificate
:
...
...
lms/djangoapps/courseware/tests/test_views.py
View file @
ab713181
...
@@ -10,8 +10,13 @@ from HTMLParser import HTMLParser
...
@@ -10,8 +10,13 @@ from HTMLParser import HTMLParser
from
urllib
import
quote
,
urlencode
from
urllib
import
quote
,
urlencode
from
uuid
import
uuid4
from
uuid
import
uuid4
import
courseware.views.views
as
views
import
ddt
import
ddt
from
freezegun
import
freeze_time
from
mock
import
MagicMock
,
PropertyMock
,
create_autospec
,
patch
from
nose.plugins.attrib
import
attr
from
pytz
import
UTC
import
courseware.views.views
as
views
import
shoppingcart
import
shoppingcart
from
capa.tests.response_xml_factory
import
MultipleChoiceResponseXMLFactory
from
capa.tests.response_xml_factory
import
MultipleChoiceResponseXMLFactory
from
certificates
import
api
as
certs_api
from
certificates
import
api
as
certs_api
...
@@ -34,13 +39,12 @@ from django.http import Http404, HttpResponseBadRequest
...
@@ -34,13 +39,12 @@ from django.http import Http404, HttpResponseBadRequest
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.test.client
import
Client
,
RequestFactory
from
django.test.client
import
Client
,
RequestFactory
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
freezegun
import
freeze_time
from
lms.djangoapps.commerce.utils
import
EcommerceService
# pylint: disable=import-error
from
lms.djangoapps.commerce.utils
import
EcommerceService
# pylint: disable=import-error
from
lms.djangoapps.grades.config.waffle
import
waffle
as
grades_waffle
from
lms.djangoapps.grades.config.waffle
import
waffle
as
grades_waffle
from
lms.djangoapps.grades.config.waffle
import
ASSUME_ZERO_GRADE_IF_ABSENT
from
lms.djangoapps.grades.config.waffle
import
ASSUME_ZERO_GRADE_IF_ABSENT
from
lms.djangoapps.grades.new.course_grade_factory
import
CourseGradeFactory
from
lms.djangoapps.grades.tests.utils
import
mock_get_score
from
milestones.tests.utils
import
MilestonesTestCaseMixin
from
milestones.tests.utils
import
MilestonesTestCaseMixin
from
mock
import
MagicMock
,
PropertyMock
,
create_autospec
,
patch
from
nose.plugins.attrib
import
attr
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.locations
import
Location
from
opaque_keys.edx.locations
import
Location
from
openedx.core.djangoapps.catalog.tests.factories
import
CourseFactory
as
CatalogCourseFactory
from
openedx.core.djangoapps.catalog.tests.factories
import
CourseFactory
as
CatalogCourseFactory
...
@@ -55,7 +59,6 @@ from openedx.core.djangolib.testing.utils import get_mock_request
...
@@ -55,7 +59,6 @@ from openedx.core.djangolib.testing.utils import get_mock_request
from
openedx.core.lib.gating
import
api
as
gating_api
from
openedx.core.lib.gating
import
api
as
gating_api
from
openedx.features.course_experience
import
COURSE_OUTLINE_PAGE_FLAG
from
openedx.features.course_experience
import
COURSE_OUTLINE_PAGE_FLAG
from
openedx.features.enterprise_support.tests.mixins.enterprise
import
EnterpriseTestConsentRequired
from
openedx.features.enterprise_support.tests.mixins.enterprise
import
EnterpriseTestConsentRequired
from
pytz
import
UTC
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
student.tests.factories
import
AdminFactory
,
CourseEnrollmentFactory
,
UserFactory
from
student.tests.factories
import
AdminFactory
,
CourseEnrollmentFactory
,
UserFactory
from
util.tests.test_date_utils
import
fake_pgettext
,
fake_ugettext
from
util.tests.test_date_utils
import
fake_pgettext
,
fake_ugettext
...
@@ -1222,7 +1225,7 @@ class ProgressPageBaseTests(ModuleStoreTestCase):
...
@@ -1222,7 +1225,7 @@ class ProgressPageBaseTests(ModuleStoreTestCase):
start
=
datetime
(
2013
,
9
,
16
,
7
,
17
,
28
),
start
=
datetime
(
2013
,
9
,
16
,
7
,
17
,
28
),
grade_cutoffs
=
{
u'çü†øƒƒ'
:
0.75
,
'Pass'
:
0.5
},
grade_cutoffs
=
{
u'çü†øƒƒ'
:
0.75
,
'Pass'
:
0.5
},
end
=
datetime
.
now
(),
end
=
datetime
.
now
(),
certificate_available_date
=
datetime
.
now
(),
certificate_available_date
=
datetime
.
now
(
UTC
),
**
options
**
options
)
)
...
@@ -1359,10 +1362,6 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1359,10 +1362,6 @@ class ProgressPageTests(ProgressPageBaseTests):
self
.
assertNotContains
(
resp
,
'Request Certificate'
)
self
.
assertNotContains
(
resp
,
'Request Certificate'
)
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'CERTIFICATES_HTML_VIEW'
:
True
})
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'CERTIFICATES_HTML_VIEW'
:
True
})
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}}),
)
def
test_view_certificate_link
(
self
):
def
test_view_certificate_link
(
self
):
"""
"""
If certificate web view is enabled then certificate web view button should appear for user who certificate is
If certificate web view is enabled then certificate web view button should appear for user who certificate is
...
@@ -1400,28 +1399,30 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1400,28 +1399,30 @@ class ProgressPageTests(ProgressPageBaseTests):
self
.
course
.
save
()
self
.
course
.
save
()
self
.
store
.
update_item
(
self
.
course
,
self
.
user
.
id
)
self
.
store
.
update_item
(
self
.
course
,
self
.
user
.
id
)
resp
=
self
.
_get_progress_page
()
with
patch
(
'lms.djangoapps.grades.new.course_grade_factory.CourseGradeFactory.create'
)
as
mock_create
:
self
.
assertContains
(
resp
,
u"View Certificate"
)
course_grade
=
mock_create
.
return_value
course_grade
.
passed
=
True
course_grade
.
summary
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}}
self
.
assertContains
(
resp
,
u"earned a certificate for this course"
)
resp
=
self
.
_get_progress_page
()
cert_url
=
certs_api
.
get_certificate_url
(
course_id
=
self
.
course
.
id
,
uuid
=
certificate
.
verify_uuid
)
self
.
assertContains
(
resp
,
cert_url
)
# when course certificate is not active
self
.
assertContains
(
resp
,
u"View Certificate"
)
certificates
[
0
][
'is_active'
]
=
False
self
.
store
.
update_item
(
self
.
course
,
self
.
user
.
id
)
resp
=
self
.
_get_progress_page
()
self
.
assertContains
(
resp
,
u"earned a certificate for this course"
)
self
.
assertNotContains
(
resp
,
u"View Your Certificate"
)
cert_url
=
certs_api
.
get_certificate_url
(
course_id
=
self
.
course
.
id
,
uuid
=
certificate
.
verify_uuid
)
self
.
assertNotContains
(
resp
,
u"You can now view your certificate"
)
self
.
assertContains
(
resp
,
cert_url
)
self
.
assertContains
(
resp
,
"working on it..."
)
self
.
assertContains
(
resp
,
"creating your certificate"
)
# when course certificate is not active
certificates
[
0
][
'is_active'
]
=
False
self
.
store
.
update_item
(
self
.
course
,
self
.
user
.
id
)
resp
=
self
.
_get_progress_page
()
self
.
assertNotContains
(
resp
,
u"View Your Certificate"
)
self
.
assertNotContains
(
resp
,
u"You can now view your certificate"
)
self
.
assertContains
(
resp
,
"working on it..."
)
self
.
assertContains
(
resp
,
"creating your certificate"
)
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'CERTIFICATES_HTML_VIEW'
:
False
})
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'CERTIFICATES_HTML_VIEW'
:
False
})
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}})
)
def
test_view_certificate_link_hidden
(
self
):
def
test_view_certificate_link_hidden
(
self
):
"""
"""
If certificate web view is disabled then certificate web view button should not appear for user who certificate
If certificate web view is disabled then certificate web view button should not appear for user who certificate
...
@@ -1441,8 +1442,13 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1441,8 +1442,13 @@ class ProgressPageTests(ProgressPageBaseTests):
# Enable certificate generation for this course
# Enable certificate generation for this course
certs_api
.
set_cert_generation_enabled
(
self
.
course
.
id
,
True
)
certs_api
.
set_cert_generation_enabled
(
self
.
course
.
id
,
True
)
resp
=
self
.
_get_progress_page
()
with
patch
(
'lms.djangoapps.grades.new.course_grade_factory.CourseGradeFactory.create'
)
as
mock_create
:
self
.
assertContains
(
resp
,
u"Download Your Certificate"
)
course_grade
=
mock_create
.
return_value
course_grade
.
passed
=
True
course_grade
.
summary
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}}
resp
=
self
.
_get_progress_page
()
self
.
assertContains
(
resp
,
u"Download Your Certificate"
)
@ddt.data
(
@ddt.data
(
*
itertools
.
product
((
True
,
False
),
(
True
,
False
))
*
itertools
.
product
((
True
,
False
),
(
True
,
False
))
...
@@ -1452,7 +1458,7 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1452,7 +1458,7 @@ class ProgressPageTests(ProgressPageBaseTests):
"""Test that query counts remain the same for self-paced and instructor-paced courses."""
"""Test that query counts remain the same for self-paced and instructor-paced courses."""
SelfPacedConfiguration
(
enabled
=
self_paced_enabled
)
.
save
()
SelfPacedConfiguration
(
enabled
=
self_paced_enabled
)
.
save
()
self
.
setup_course
(
self_paced
=
self_paced
)
self
.
setup_course
(
self_paced
=
self_paced
)
with
self
.
assertNumQueries
(
43
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
),
check_mongo_calls
(
2
):
with
self
.
assertNumQueries
(
43
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
),
check_mongo_calls
(
1
):
self
.
_get_progress_page
()
self
.
_get_progress_page
()
@ddt.data
(
@ddt.data
(
...
@@ -1465,20 +1471,16 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1465,20 +1471,16 @@ class ProgressPageTests(ProgressPageBaseTests):
with
grades_waffle
()
.
override
(
ASSUME_ZERO_GRADE_IF_ABSENT
,
active
=
enable_waffle
):
with
grades_waffle
()
.
override
(
ASSUME_ZERO_GRADE_IF_ABSENT
,
active
=
enable_waffle
):
with
self
.
assertNumQueries
(
with
self
.
assertNumQueries
(
initial
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
initial
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
),
check_mongo_calls
(
2
):
),
check_mongo_calls
(
1
):
self
.
_get_progress_page
()
self
.
_get_progress_page
()
# subsequent accesses to the progress page require fewer queries.
# subsequent accesses to the progress page require fewer queries.
for
_
in
range
(
2
):
for
_
in
range
(
2
):
with
self
.
assertNumQueries
(
with
self
.
assertNumQueries
(
subsequent
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
subsequent
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
),
check_mongo_calls
(
2
):
),
check_mongo_calls
(
1
):
self
.
_get_progress_page
()
self
.
_get_progress_page
()
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}})
)
@ddt.data
(
@ddt.data
(
*
itertools
.
product
(
*
itertools
.
product
(
(
(
...
@@ -1502,23 +1504,24 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1502,23 +1504,24 @@ class ProgressPageTests(ProgressPageBaseTests):
'lms.djangoapps.verify_student.models.SoftwareSecurePhotoVerification.user_is_verified'
'lms.djangoapps.verify_student.models.SoftwareSecurePhotoVerification.user_is_verified'
)
as
user_verify
:
)
as
user_verify
:
user_verify
.
return_value
=
user_verified
user_verify
.
return_value
=
user_verified
resp
=
self
.
client
.
get
(
with
patch
(
'lms.djangoapps.grades.new.course_grade_factory.CourseGradeFactory.create'
)
as
mock_create
:
reverse
(
'progress'
,
args
=
[
unicode
(
self
.
course
.
id
)])
course_grade
=
mock_create
.
return_value
)
course_grade
.
passed
=
True
course_grade
.
summary
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}
}
cert_button_hidden
=
course_mode
is
CourseMode
.
AUDIT
or
\
resp
=
self
.
_get_progress_page
()
course_mode
in
CourseMode
.
VERIFIED_MODES
and
not
user_verified
self
.
assertEqual
(
cert_button_hidden
=
course_mode
is
CourseMode
.
AUDIT
or
\
cert_button_hidden
,
course_mode
in
CourseMode
.
VERIFIED_MODES
and
not
user_verified
'Request Certificate'
not
in
resp
.
content
)
self
.
assertEqual
(
cert_button_hidden
,
'Request Certificate'
not
in
resp
.
content
)
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'CERTIFICATES_HTML_VIEW'
:
True
})
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'CERTIFICATES_HTML_VIEW'
:
True
})
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}})
)
def
test_page_with_invalidated_certificate_with_html_view
(
self
):
def
test_page_with_invalidated_certificate_with_html_view
(
self
):
"""
"""
Verify that for html certs if certificate is marked as invalidated than
Verify that for html certs if certificate is marked as invalidated than
...
@@ -1545,14 +1548,17 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1545,14 +1548,17 @@ class ProgressPageTests(ProgressPageBaseTests):
self
.
course
.
save
()
self
.
course
.
save
()
self
.
store
.
update_item
(
self
.
course
,
self
.
user
.
id
)
self
.
store
.
update_item
(
self
.
course
,
self
.
user
.
id
)
resp
=
self
.
_get_progress_page
()
with
patch
(
'lms.djangoapps.grades.new.course_grade_factory.CourseGradeFactory.create'
)
as
mock_create
:
self
.
assertContains
(
resp
,
u"View Certificate"
)
course_grade
=
mock_create
.
return_value
self
.
assert_invalidate_certificate
(
generated_certificate
)
course_grade
.
passed
=
True
course_grade
.
summary
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}
}
resp
=
self
.
_get_progress_page
()
self
.
assertContains
(
resp
,
u"View Certificate"
)
self
.
assert_invalidate_certificate
(
generated_certificate
)
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}})
)
def
test_page_with_invalidated_certificate_with_pdf
(
self
):
def
test_page_with_invalidated_certificate_with_pdf
(
self
):
"""
"""
Verify that for pdf certs if certificate is marked as invalidated than
Verify that for pdf certs if certificate is marked as invalidated than
...
@@ -1562,14 +1568,15 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1562,14 +1568,15 @@ class ProgressPageTests(ProgressPageBaseTests):
"http://www.example.com/certificate.pdf"
,
"honor"
"http://www.example.com/certificate.pdf"
,
"honor"
)
)
resp
=
self
.
_get_progress_page
()
with
patch
(
'lms.djangoapps.grades.new.course_grade_factory.CourseGradeFactory.create'
)
as
mock_create
:
self
.
assertContains
(
resp
,
u'Download Your Certificate'
)
course_grade
=
mock_create
.
return_value
self
.
assert_invalidate_certificate
(
generated_certificate
)
course_grade
.
passed
=
True
course_grade
.
summary
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}}
resp
=
self
.
_get_progress_page
()
self
.
assertContains
(
resp
,
u'Download Your Certificate'
)
self
.
assert_invalidate_certificate
(
generated_certificate
)
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}})
)
@patch
(
'courseware.views.views.is_course_passed'
,
PropertyMock
(
return_value
=
True
))
@patch
(
'courseware.views.views.is_course_passed'
,
PropertyMock
(
return_value
=
True
))
def
test_message_for_audit_mode
(
self
):
def
test_message_for_audit_mode
(
self
):
""" Verify that message appears on progress page, if learner is enrolled
""" Verify that message appears on progress page, if learner is enrolled
...
@@ -1578,14 +1585,19 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1578,14 +1585,19 @@ class ProgressPageTests(ProgressPageBaseTests):
user
=
UserFactory
.
create
()
user
=
UserFactory
.
create
()
self
.
assertTrue
(
self
.
client
.
login
(
username
=
user
.
username
,
password
=
'test'
))
self
.
assertTrue
(
self
.
client
.
login
(
username
=
user
.
username
,
password
=
'test'
))
CourseEnrollmentFactory
(
user
=
user
,
course_id
=
self
.
course
.
id
,
mode
=
CourseMode
.
AUDIT
)
CourseEnrollmentFactory
(
user
=
user
,
course_id
=
self
.
course
.
id
,
mode
=
CourseMode
.
AUDIT
)
response
=
self
.
_get_progress_page
()
self
.
assertContains
(
with
patch
(
'lms.djangoapps.grades.new.course_grade_factory.CourseGradeFactory.create'
)
as
mock_create
:
response
,
course_grade
=
mock_create
.
return_value
u'You are enrolled in the audit track for this course. The audit track does not include a certificate.'
course_grade
.
passed
=
True
)
course_grade
.
summary
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
,
'section_breakdown'
:
[],
'grade_breakdown'
:
{}}
response
=
self
.
_get_progress_page
()
self
.
assertContains
(
response
,
u'You are enrolled in the audit track for this course. The audit track does not include a certificate.'
)
@patch
(
'courseware.views.views.is_course_passed'
,
PropertyMock
(
return_value
=
True
))
def
test_invalidated_cert_data
(
self
):
def
test_invalidated_cert_data
(
self
):
"""
"""
Verify that invalidated cert data is returned if cert is invalidated.
Verify that invalidated cert data is returned if cert is invalidated.
...
@@ -1600,11 +1612,10 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1600,11 +1612,10 @@ class ProgressPageTests(ProgressPageBaseTests):
)
)
# Invalidate user certificate
# Invalidate user certificate
generated_certificate
.
invalidate
()
generated_certificate
.
invalidate
()
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
self
.
course
.
id
,
True
,
CourseMode
.
HONOR
)
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
CourseMode
.
HONOR
,
MagicMock
(
passed
=
True
)
)
self
.
assertEqual
(
response
.
cert_status
,
'invalidated'
)
self
.
assertEqual
(
response
.
cert_status
,
'invalidated'
)
self
.
assertEqual
(
response
.
title
,
'Your certificate has been invalidated'
)
self
.
assertEqual
(
response
.
title
,
'Your certificate has been invalidated'
)
@patch
(
'courseware.views.views.is_course_passed'
,
PropertyMock
(
return_value
=
True
))
def
test_downloadable_get_cert_data
(
self
):
def
test_downloadable_get_cert_data
(
self
):
"""
"""
Verify that downloadable cert data is returned if cert is downloadable.
Verify that downloadable cert data is returned if cert is downloadable.
...
@@ -1614,12 +1625,11 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1614,12 +1625,11 @@ class ProgressPageTests(ProgressPageBaseTests):
)
)
with
patch
(
'certificates.api.certificate_downloadable_status'
,
with
patch
(
'certificates.api.certificate_downloadable_status'
,
return_value
=
self
.
mock_certificate_downloadable_status
(
is_downloadable
=
True
)):
return_value
=
self
.
mock_certificate_downloadable_status
(
is_downloadable
=
True
)):
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
self
.
course
.
id
,
True
,
CourseMode
.
HONOR
)
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
CourseMode
.
HONOR
,
MagicMock
(
passed
=
True
)
)
self
.
assertEqual
(
response
.
cert_status
,
'downloadable'
)
self
.
assertEqual
(
response
.
cert_status
,
'downloadable'
)
self
.
assertEqual
(
response
.
title
,
'Your certificate is available'
)
self
.
assertEqual
(
response
.
title
,
'Your certificate is available'
)
@patch
(
'courseware.views.views.is_course_passed'
,
PropertyMock
(
return_value
=
True
))
def
test_generating_get_cert_data
(
self
):
def
test_generating_get_cert_data
(
self
):
"""
"""
Verify that generating cert data is returned if cert is generating.
Verify that generating cert data is returned if cert is generating.
...
@@ -1629,12 +1639,11 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1629,12 +1639,11 @@ class ProgressPageTests(ProgressPageBaseTests):
)
)
with
patch
(
'certificates.api.certificate_downloadable_status'
,
with
patch
(
'certificates.api.certificate_downloadable_status'
,
return_value
=
self
.
mock_certificate_downloadable_status
(
is_generating
=
True
)):
return_value
=
self
.
mock_certificate_downloadable_status
(
is_generating
=
True
)):
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
self
.
course
.
id
,
True
,
CourseMode
.
HONOR
)
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
CourseMode
.
HONOR
,
MagicMock
(
passed
=
True
)
)
self
.
assertEqual
(
response
.
cert_status
,
'generating'
)
self
.
assertEqual
(
response
.
cert_status
,
'generating'
)
self
.
assertEqual
(
response
.
title
,
"We're working on it..."
)
self
.
assertEqual
(
response
.
title
,
"We're working on it..."
)
@patch
(
'courseware.views.views.is_course_passed'
,
PropertyMock
(
return_value
=
True
))
def
test_unverified_get_cert_data
(
self
):
def
test_unverified_get_cert_data
(
self
):
"""
"""
Verify that unverified cert data is returned if cert is unverified.
Verify that unverified cert data is returned if cert is unverified.
...
@@ -1644,12 +1653,11 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1644,12 +1653,11 @@ class ProgressPageTests(ProgressPageBaseTests):
)
)
with
patch
(
'certificates.api.certificate_downloadable_status'
,
with
patch
(
'certificates.api.certificate_downloadable_status'
,
return_value
=
self
.
mock_certificate_downloadable_status
(
is_unverified
=
True
)):
return_value
=
self
.
mock_certificate_downloadable_status
(
is_unverified
=
True
)):
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
self
.
course
.
id
,
True
,
CourseMode
.
HONOR
)
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
CourseMode
.
HONOR
,
MagicMock
(
passed
=
True
)
)
self
.
assertEqual
(
response
.
cert_status
,
'unverified'
)
self
.
assertEqual
(
response
.
cert_status
,
'unverified'
)
self
.
assertEqual
(
response
.
title
,
"Certificate unavailable"
)
self
.
assertEqual
(
response
.
title
,
"Certificate unavailable"
)
@patch
(
'courseware.views.views.is_course_passed'
,
PropertyMock
(
return_value
=
True
))
def
test_request_get_cert_data
(
self
):
def
test_request_get_cert_data
(
self
):
"""
"""
Verify that requested cert data is returned if cert is to be requested.
Verify that requested cert data is returned if cert is to be requested.
...
@@ -1659,7 +1667,7 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1659,7 +1667,7 @@ class ProgressPageTests(ProgressPageBaseTests):
)
)
with
patch
(
'certificates.api.certificate_downloadable_status'
,
with
patch
(
'certificates.api.certificate_downloadable_status'
,
return_value
=
self
.
mock_certificate_downloadable_status
()):
return_value
=
self
.
mock_certificate_downloadable_status
()):
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
self
.
course
.
id
,
True
,
CourseMode
.
HONOR
)
response
=
views
.
_get_cert_data
(
self
.
user
,
self
.
course
,
CourseMode
.
HONOR
,
MagicMock
(
passed
=
True
)
)
self
.
assertEqual
(
response
.
cert_status
,
'requesting'
)
self
.
assertEqual
(
response
.
cert_status
,
'requesting'
)
self
.
assertEqual
(
response
.
title
,
"Congratulations, you qualified for a certificate!"
)
self
.
assertEqual
(
response
.
title
,
"Congratulations, you qualified for a certificate!"
)
...
@@ -2013,54 +2021,6 @@ class VerifyCourseKeyDecoratorTests(TestCase):
...
@@ -2013,54 +2021,6 @@ class VerifyCourseKeyDecoratorTests(TestCase):
@attr
(
shard
=
1
)
@attr
(
shard
=
1
)
class
IsCoursePassedTests
(
ModuleStoreTestCase
):
"""
Tests for the is_course_passed helper function
"""
SUCCESS_CUTOFF
=
0.5
def
setUp
(
self
):
super
(
IsCoursePassedTests
,
self
)
.
setUp
()
self
.
student
=
UserFactory
()
self
.
course
=
CourseFactory
.
create
(
org
=
'edx'
,
number
=
'verified'
,
display_name
=
'Verified Course'
,
grade_cutoffs
=
{
'cutoff'
:
0.75
,
'Pass'
:
self
.
SUCCESS_CUTOFF
}
)
self
.
request
=
RequestFactory
()
self
.
request
.
user
=
self
.
student
def
test_user_fails_if_not_clear_exam
(
self
):
# If user has not grade then false will return
self
.
assertFalse
(
views
.
is_course_passed
(
self
.
course
,
None
,
self
.
student
,
self
.
request
))
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'percent'
:
0.9
}))
def
test_user_pass_if_percent_appears_above_passing_point
(
self
):
# Mocking the grades.grade
# If user has above passing marks then True will return
self
.
assertTrue
(
views
.
is_course_passed
(
self
.
course
,
None
,
self
.
student
,
self
.
request
))
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'percent'
:
0.2
}))
def
test_user_fail_if_percent_appears_below_passing_point
(
self
):
# Mocking the grades.grade
# If user has below passing marks then False will return
self
.
assertFalse
(
views
.
is_course_passed
(
self
.
course
,
None
,
self
.
student
,
self
.
request
))
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'percent'
:
SUCCESS_CUTOFF
})
)
def
test_user_with_passing_marks_and_achieved_marks_equal
(
self
):
# Mocking the grades.grade
# If user's achieved passing marks are equal to the required passing
# marks then it will return True
self
.
assertTrue
(
views
.
is_course_passed
(
self
.
course
,
None
,
self
.
student
,
self
.
request
))
@attr
(
shard
=
1
)
class
GenerateUserCertTests
(
ModuleStoreTestCase
):
class
GenerateUserCertTests
(
ModuleStoreTestCase
):
"""
"""
Tests for the view function Generated User Certs
Tests for the view function Generated User Certs
...
@@ -2088,12 +2048,9 @@ class GenerateUserCertTests(ModuleStoreTestCase):
...
@@ -2088,12 +2048,9 @@ class GenerateUserCertTests(ModuleStoreTestCase):
self
.
assertEqual
(
resp
.
status_code
,
HttpResponseBadRequest
.
status_code
)
self
.
assertEqual
(
resp
.
status_code
,
HttpResponseBadRequest
.
status_code
)
self
.
assertIn
(
"Your certificate will be available when you pass the course."
,
resp
.
content
)
self
.
assertIn
(
"Your certificate will be available when you pass the course."
,
resp
.
content
)
@patch
(
@patch
(
'courseware.views.views.is_course_passed'
,
return_value
=
True
)
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
})
)
@override_settings
(
CERT_QUEUE
=
'certificates'
,
LMS_SEGMENT_KEY
=
"foobar"
)
@override_settings
(
CERT_QUEUE
=
'certificates'
,
LMS_SEGMENT_KEY
=
"foobar"
)
def
test_user_with_passing_grade
(
self
):
def
test_user_with_passing_grade
(
self
,
mock_is_course_passed
):
# If user has above passing grading then json will return cert generating message and
# If user has above passing grading then json will return cert generating message and
# status valid code
# status valid code
# mocking xqueue and analytics
# mocking xqueue and analytics
...
@@ -2104,6 +2061,7 @@ class GenerateUserCertTests(ModuleStoreTestCase):
...
@@ -2104,6 +2061,7 @@ class GenerateUserCertTests(ModuleStoreTestCase):
with
patch
(
'capa.xqueue_interface.XQueueInterface.send_to_queue'
)
as
mock_send_to_queue
:
with
patch
(
'capa.xqueue_interface.XQueueInterface.send_to_queue'
)
as
mock_send_to_queue
:
mock_send_to_queue
.
return_value
=
(
0
,
"Successfully queued"
)
mock_send_to_queue
.
return_value
=
(
0
,
"Successfully queued"
)
resp
=
self
.
client
.
post
(
self
.
url
)
resp
=
self
.
client
.
post
(
self
.
url
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
...
@@ -2123,10 +2081,6 @@ class GenerateUserCertTests(ModuleStoreTestCase):
...
@@ -2123,10 +2081,6 @@ class GenerateUserCertTests(ModuleStoreTestCase):
)
)
mock_tracker
.
reset_mock
()
mock_tracker
.
reset_mock
()
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
})
)
def
test_user_with_passing_existing_generating_cert
(
self
):
def
test_user_with_passing_existing_generating_cert
(
self
):
# If user has passing grade but also has existing generating cert
# If user has passing grade but also has existing generating cert
# then json will return cert generating message with bad request code
# then json will return cert generating message with bad request code
...
@@ -2136,14 +2090,15 @@ class GenerateUserCertTests(ModuleStoreTestCase):
...
@@ -2136,14 +2090,15 @@ class GenerateUserCertTests(ModuleStoreTestCase):
status
=
CertificateStatuses
.
generating
,
status
=
CertificateStatuses
.
generating
,
mode
=
'verified'
mode
=
'verified'
)
)
resp
=
self
.
client
.
post
(
self
.
url
)
with
patch
(
'lms.djangoapps.grades.new.course_grade_factory.CourseGradeFactory.create'
)
as
mock_create
:
self
.
assertEqual
(
resp
.
status_code
,
HttpResponseBadRequest
.
status_code
)
course_grade
=
mock_create
.
return_value
self
.
assertIn
(
"Certificate is being created."
,
resp
.
content
)
course_grade
.
passed
=
True
course_grade
.
summary
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
}
resp
=
self
.
client
.
post
(
self
.
url
)
self
.
assertEqual
(
resp
.
status_code
,
HttpResponseBadRequest
.
status_code
)
self
.
assertIn
(
"Certificate is being created."
,
resp
.
content
)
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
})
)
@override_settings
(
CERT_QUEUE
=
'certificates'
,
LMS_SEGMENT_KEY
=
"foobar"
)
@override_settings
(
CERT_QUEUE
=
'certificates'
,
LMS_SEGMENT_KEY
=
"foobar"
)
def
test_user_with_passing_existing_downloadable_cert
(
self
):
def
test_user_with_passing_existing_downloadable_cert
(
self
):
# If user has already downloadable certificate
# If user has already downloadable certificate
...
@@ -2156,9 +2111,14 @@ class GenerateUserCertTests(ModuleStoreTestCase):
...
@@ -2156,9 +2111,14 @@ class GenerateUserCertTests(ModuleStoreTestCase):
mode
=
'verified'
mode
=
'verified'
)
)
resp
=
self
.
client
.
post
(
self
.
url
)
with
patch
(
'lms.djangoapps.grades.new.course_grade_factory.CourseGradeFactory.create'
)
as
mock_create
:
self
.
assertEqual
(
resp
.
status_code
,
HttpResponseBadRequest
.
status_code
)
course_grade
=
mock_create
.
return_value
self
.
assertIn
(
"Certificate has already been created."
,
resp
.
content
)
course_grade
.
passed
=
True
course_grade
.
summay
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
}
resp
=
self
.
client
.
post
(
self
.
url
)
self
.
assertEqual
(
resp
.
status_code
,
HttpResponseBadRequest
.
status_code
)
self
.
assertIn
(
"Certificate has already been created."
,
resp
.
content
)
def
test_user_with_non_existing_course
(
self
):
def
test_user_with_non_existing_course
(
self
):
# If try to access a course with valid key pattern then it will return
# If try to access a course with valid key pattern then it will return
...
...
lms/djangoapps/courseware/views/views.py
View file @
ab713181
...
@@ -71,13 +71,14 @@ from markupsafe import escape
...
@@ -71,13 +71,14 @@ from markupsafe import escape
from
opaque_keys
import
InvalidKeyError
from
opaque_keys
import
InvalidKeyError
from
opaque_keys.edx.keys
import
CourseKey
,
UsageKey
from
opaque_keys.edx.keys
import
CourseKey
,
UsageKey
from
openedx.core.djangoapps.catalog.utils
import
get_programs
,
get_programs_with_type
from
openedx.core.djangoapps.catalog.utils
import
get_programs
,
get_programs_with_type
from
openedx.core.djangoapps.certificates
import
api
as
auto_certs_api
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.credit.api
import
(
from
openedx.core.djangoapps.credit.api
import
(
get_credit_requirement_status
,
get_credit_requirement_status
,
is_credit_course
,
is_credit_course
,
is_user_eligible_for_credit
is_user_eligible_for_credit
)
)
from
openedx.core.djangoapps.certificates
.config
import
waffle
as
certificates_waffle
from
openedx.core.djangoapps.certificates
import
api
as
auto_certs_api
from
openedx.core.djangoapps.models.course_details
import
CourseDetails
from
openedx.core.djangoapps.models.course_details
import
CourseDetails
from
openedx.core.djangoapps.monitoring_utils
import
set_custom_metrics_for_course_key
from
openedx.core.djangoapps.monitoring_utils
import
set_custom_metrics_for_course_key
from
openedx.core.djangoapps.plugin_api.views
import
EdxFragmentView
from
openedx.core.djangoapps.plugin_api.views
import
EdxFragmentView
...
@@ -117,6 +118,62 @@ CertData = namedtuple(
...
@@ -117,6 +118,62 @@ CertData = namedtuple(
"CertData"
,
[
"cert_status"
,
"title"
,
"msg"
,
"download_url"
,
"cert_web_view_url"
]
"CertData"
,
[
"cert_status"
,
"title"
,
"msg"
,
"download_url"
,
"cert_web_view_url"
]
)
)
AUDIT_PASSING_CERT_DATA
=
CertData
(
CertificateStatuses
.
audit_passing
,
_
(
'Your enrollment: Audit track'
),
_
(
'You are enrolled in the audit track for this course. The audit track does not include a certificate.'
),
download_url
=
None
,
cert_web_view_url
=
None
)
GENERATING_CERT_DATA
=
CertData
(
CertificateStatuses
.
generating
,
_
(
"We're working on it..."
),
_
(
"We're creating your certificate. You can keep working in your courses and a link "
"to it will appear here and on your Dashboard when it is ready."
),
download_url
=
None
,
cert_web_view_url
=
None
)
INVALID_CERT_DATA
=
CertData
(
CertificateStatuses
.
invalidated
,
_
(
'Your certificate has been invalidated'
),
_
(
'Please contact your course team if you have any questions.'
),
download_url
=
None
,
cert_web_view_url
=
None
)
REQUESTING_CERT_DATA
=
CertData
(
CertificateStatuses
.
requesting
,
_
(
'Congratulations, you qualified for a certificate!'
),
_
(
"You've earned a certificate for this course."
),
download_url
=
None
,
cert_web_view_url
=
None
)
UNVERIFIED_CERT_DATA
=
CertData
(
CertificateStatuses
.
unverified
,
_
(
'Certificate unavailable'
),
_
(
'You have not received a certificate because you do not have a current {platform_name} '
'verified identity.'
)
.
format
(
platform_name
=
configuration_helpers
.
get_value
(
'PLATFORM_NAME'
,
settings
.
PLATFORM_NAME
)),
download_url
=
None
,
cert_web_view_url
=
None
)
def
_downloadable_cert_data
(
download_url
=
None
,
cert_web_view_url
=
None
):
return
CertData
(
CertificateStatuses
.
downloadable
,
_
(
'Your certificate is available'
),
_
(
"You've earned a certificate for this course."
),
download_url
=
download_url
,
cert_web_view_url
=
cert_web_view_url
)
def
user_groups
(
user
):
def
user_groups
(
user
):
"""
"""
...
@@ -872,23 +929,22 @@ def _progress(request, course_key, student_id):
...
@@ -872,23 +929,22 @@ def _progress(request, course_key, student_id):
course_grade
=
CourseGradeFactory
()
.
create
(
student
,
course
)
course_grade
=
CourseGradeFactory
()
.
create
(
student
,
course
)
courseware_summary
=
course_grade
.
chapter_grades
.
values
()
courseware_summary
=
course_grade
.
chapter_grades
.
values
()
grade_summary
=
course_grade
.
summary
studio_url
=
get_studio_url
(
course
,
'settings/grading'
)
studio_url
=
get_studio_url
(
course
,
'settings/grading'
)
# checking certificate generation configuration
# checking certificate generation configuration
enrollment_mode
,
is_active
=
CourseEnrollment
.
enrollment_mode_for_user
(
student
,
course_key
)
enrollment_mode
,
_
=
CourseEnrollment
.
enrollment_mode_for_user
(
student
,
course_key
)
context
=
{
context
=
{
'course'
:
course
,
'course'
:
course
,
'courseware_summary'
:
courseware_summary
,
'courseware_summary'
:
courseware_summary
,
'studio_url'
:
studio_url
,
'studio_url'
:
studio_url
,
'grade_summary'
:
grade_
summary
,
'grade_summary'
:
course_grade
.
summary
,
'staff_access'
:
staff_access
,
'staff_access'
:
staff_access
,
'masquerade'
:
masquerade
,
'masquerade'
:
masquerade
,
'supports_preview_menu'
:
True
,
'supports_preview_menu'
:
True
,
'student'
:
student
,
'student'
:
student
,
'credit_course_requirements'
:
_credit_course_requirements
(
course_key
,
student
),
'credit_course_requirements'
:
_credit_course_requirements
(
course_key
,
student
),
'certificate_data'
:
_get_cert_data
(
student
,
course
,
course_key
,
is_active
,
enrollment_mode
,
grade_summary
),
'certificate_data'
:
_get_cert_data
(
student
,
course
,
enrollment_mode
,
course_grade
),
}
}
context
.
update
(
context
.
update
(
get_experiment_user_metadata_context
(
get_experiment_user_metadata_context
(
...
@@ -903,128 +959,68 @@ def _progress(request, course_key, student_id):
...
@@ -903,128 +959,68 @@ def _progress(request, course_key, student_id):
return
response
return
response
def
_get_cert_data
(
student
,
course
,
course_key
,
is_active
,
enrollment_mode
,
grade_summary
=
None
):
def
_downloadable_certificate_message
(
course
,
cert_downloadable_status
):
if
certs_api
.
has_html_certificates_enabled
(
course
):
if
certs_api
.
get_active_web_certificate
(
course
)
is
not
None
:
return
_downloadable_cert_data
(
download_url
=
None
,
cert_web_view_url
=
certs_api
.
get_certificate_url
(
course_id
=
course
.
id
,
uuid
=
cert_downloadable_status
[
'uuid'
]
)
)
else
:
return
GENERATING_CERT_DATA
return
_downloadable_cert_data
(
download_url
=
cert_downloadable_status
[
'download_url'
])
def
_missing_required_verification
(
student
,
enrollment_mode
):
return
(
enrollment_mode
in
CourseMode
.
VERIFIED_MODES
and
not
SoftwareSecurePhotoVerification
.
user_is_verified
(
student
)
)
def
_certificate_message
(
student
,
course
,
enrollment_mode
):
if
certs_api
.
is_certificate_invalid
(
student
,
course
.
id
):
return
INVALID_CERT_DATA
cert_downloadable_status
=
certs_api
.
certificate_downloadable_status
(
student
,
course
.
id
)
if
cert_downloadable_status
[
'is_generating'
]:
return
GENERATING_CERT_DATA
if
cert_downloadable_status
[
'is_unverified'
]
or
_missing_required_verification
(
student
,
enrollment_mode
):
return
UNVERIFIED_CERT_DATA
if
cert_downloadable_status
[
'is_downloadable'
]:
return
_downloadable_certificate_message
(
course
,
cert_downloadable_status
)
return
REQUESTING_CERT_DATA
def
_get_cert_data
(
student
,
course
,
enrollment_mode
,
course_grade
=
None
):
"""Returns students course certificate related data.
"""Returns students course certificate related data.
Arguments:
Arguments:
student (User): Student for whom certificate to retrieve.
student (User): Student for whom certificate to retrieve.
course (Course): Course object for which certificate data to retrieve.
course (Course): Course object for which certificate data to retrieve.
course_key (CourseKey): Course identifier for course.
is_active (Bool): Boolean value to check if course is active.
enrollment_mode (String): Course mode in which student is enrolled.
enrollment_mode (String): Course mode in which student is enrolled.
grade_summary (dict): Student grade details
.
course_grade (CourseGrade): Student's course grade record
.
Returns:
Returns:
returns dict if course certificate is available else None.
returns dict if course certificate is available else None.
"""
"""
from
lms.djangoapps.courseware.courses
import
get_course_by_id
if
not
CourseMode
.
is_eligible_for_certificate
(
enrollment_mode
):
if
not
CourseMode
.
is_eligible_for_certificate
(
enrollment_mode
):
return
CertData
(
return
AUDIT_PASSING_CERT_DATA
CertificateStatuses
.
audit_passing
,
_
(
'Your enrollment: Audit track'
),
_
(
'You are enrolled in the audit track for this course. The audit track does not include a certificate.'
),
download_url
=
None
,
cert_web_view_url
=
None
)
may_view_certificate
=
False
if
course_key
:
may_view_certificate
=
get_course_by_id
(
course_key
)
.
may_certify
()
switches
=
certificates_waffle
.
waffle
()
switch_enabled
=
switches
.
is_enabled
(
certificates_waffle
.
AUTO_CERTIFICATE_GENERATION
)
student_cert_generation_enabled
=
switch_enabled
or
certs_api
.
cert_generation_enabled
(
course_key
)
# Don't show certificate information if:
# 1) the learner has not passed the course
# 2) the course is not active
# 3) auto-generated certs flags are not enabled, but student cert generation is not enabled either
# 4) the learner may not view the certificate, based on the course's advanced course settings.
if
not
all
([
is_course_passed
(
course
,
grade_summary
),
is_active
,
student_cert_generation_enabled
,
may_view_certificate
]):
return
None
if
certs_api
.
is_certificate_invalid
(
student
,
course_key
):
certificates_enabled_for_course
=
certs_api
.
cert_generation_enabled
(
course
.
id
)
return
CertData
(
if
course_grade
is
None
:
CertificateStatuses
.
invalidated
,
course_grade
=
CourseGradeFactory
()
.
create
(
student
,
course
)
_
(
'Your certificate has been invalidated'
),
_
(
'Please contact your course team if you have any questions.'
),
download_url
=
None
,
cert_web_view_url
=
None
)
cert_downloadable_status
=
certs_api
.
certificate_downloadable_status
(
student
,
course_key
)
if
not
auto_certs_api
.
can_show_certificate_message
(
course
,
student
,
course_grade
,
certificates_enabled_for_course
):
return
generating_certificate_message
=
CertData
(
return
_certificate_message
(
student
,
course
,
enrollment_mode
)
CertificateStatuses
.
generating
,
_
(
"We're working on it..."
),
_
(
"We're creating your certificate. You can keep working in your courses and a link "
"to it will appear here and on your Dashboard when it is ready."
),
download_url
=
None
,
cert_web_view_url
=
None
)
if
cert_downloadable_status
[
'is_downloadable'
]:
if
certs_api
.
has_html_certificates_enabled
(
course_key
,
course
):
if
certs_api
.
get_active_web_certificate
(
course
)
is
not
None
:
return
CertData
(
CertificateStatuses
.
downloadable
,
_
(
'Your certificate is available'
),
_
(
"You've earned a certificate for this course."
),
download_url
=
None
,
cert_web_view_url
=
certs_api
.
get_certificate_url
(
course_id
=
course_key
,
uuid
=
cert_downloadable_status
[
'uuid'
]
)
)
else
:
# If there is an error, the user should see the generating certificate message
# until a new certificate is generated.
return
generating_certificate_message
return
CertData
(
CertificateStatuses
.
downloadable
,
_
(
'Your certificate is available'
),
_
(
"You've earned a certificate for this course."
),
download_url
=
cert_downloadable_status
[
'download_url'
],
cert_web_view_url
=
None
)
if
cert_downloadable_status
[
'is_generating'
]:
return
generating_certificate_message
# If the learner is in verified modes and the student did not have
# their ID verified, we need to show message to ask learner to verify their ID first
missing_required_verification
=
(
enrollment_mode
in
CourseMode
.
VERIFIED_MODES
and
not
SoftwareSecurePhotoVerification
.
user_is_verified
(
student
)
)
if
missing_required_verification
or
cert_downloadable_status
[
'is_unverified'
]:
platform_name
=
configuration_helpers
.
get_value
(
'PLATFORM_NAME'
,
settings
.
PLATFORM_NAME
)
return
CertData
(
CertificateStatuses
.
unverified
,
_
(
'Certificate unavailable'
),
_
(
'You have not received a certificate because you do not have a current {platform_name} '
'verified identity.'
)
.
format
(
platform_name
=
platform_name
),
download_url
=
None
,
cert_web_view_url
=
None
)
return
CertData
(
CertificateStatuses
.
requesting
,
_
(
'Congratulations, you qualified for a certificate!'
),
_
(
"You've earned a certificate for this course."
),
download_url
=
None
,
cert_web_view_url
=
None
)
def
_credit_course_requirements
(
course_key
,
student
):
def
_credit_course_requirements
(
course_key
,
student
):
...
@@ -1281,26 +1277,21 @@ def course_survey(request, course_id):
...
@@ -1281,26 +1277,21 @@ def course_survey(request, course_id):
)
)
def
is_course_passed
(
course
,
grade_summary
=
None
,
student
=
None
,
request
=
None
):
def
is_course_passed
(
student
,
course
,
course_grade
=
None
):
"""
"""
check user's course passing status. return True if passed
check user's course passing status. return True if passed
Arguments:
Arguments:
course : course object
grade_summary (dict) : contains student grade details.
student : user object
student : user object
request (HttpRequest)
course : course object
course_grade (CourseGrade) : contains student grade details.
Returns:
Returns:
returns bool value
returns bool value
"""
"""
nonzero_cutoffs
=
[
cutoff
for
cutoff
in
course
.
grade_cutoffs
.
values
()
if
cutoff
>
0
]
if
course_grade
is
None
:
success_cutoff
=
min
(
nonzero_cutoffs
)
if
nonzero_cutoffs
else
None
course_grade
=
CourseGradeFactory
()
.
create
(
student
,
course
)
return
course_grade
.
passed
if
grade_summary
is
None
:
grade_summary
=
CourseGradeFactory
()
.
create
(
student
,
course
)
.
summary
return
success_cutoff
and
grade_summary
[
'percent'
]
>=
success_cutoff
# Grades can potentially be written - if so, let grading manage the transaction.
# Grades can potentially be written - if so, let grading manage the transaction.
...
@@ -1343,7 +1334,7 @@ def generate_user_cert(request, course_id):
...
@@ -1343,7 +1334,7 @@ def generate_user_cert(request, course_id):
if
not
course
:
if
not
course
:
return
HttpResponseBadRequest
(
_
(
"Course is not valid"
))
return
HttpResponseBadRequest
(
_
(
"Course is not valid"
))
if
not
is_course_passed
(
course
,
None
,
student
,
request
):
if
not
is_course_passed
(
student
,
course
):
return
HttpResponseBadRequest
(
_
(
"Your certificate will be available when you pass the course."
))
return
HttpResponseBadRequest
(
_
(
"Your certificate will be available when you pass the course."
))
certificate_status
=
certs_api
.
certificate_downloadable_status
(
student
,
course
.
id
)
certificate_status
=
certs_api
.
certificate_downloadable_status
(
student
,
course
.
id
)
...
...
lms/djangoapps/instructor/views/instructor_dashboard.py
View file @
ab713181
...
@@ -331,7 +331,7 @@ def _section_certificates(course):
...
@@ -331,7 +331,7 @@ def _section_certificates(course):
"""
"""
example_cert_status
=
None
example_cert_status
=
None
html_cert_enabled
=
certs_api
.
has_html_certificates_enabled
(
course
.
id
,
course
)
html_cert_enabled
=
certs_api
.
has_html_certificates_enabled
(
course
)
if
html_cert_enabled
:
if
html_cert_enabled
:
can_enable_for_course
=
True
can_enable_for_course
=
True
else
:
else
:
...
...
lms/djangoapps/instructor_task/tests/test_tasks_helper.py
View file @
ab713181
...
@@ -1976,7 +1976,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
...
@@ -1976,7 +1976,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
'failed'
:
3
,
'failed'
:
3
,
'skipped'
:
2
'skipped'
:
2
}
}
with
self
.
assertNumQueries
(
17
1
):
with
self
.
assertNumQueries
(
17
6
):
self
.
assertCertificatesGenerated
(
task_input
,
expected_results
)
self
.
assertCertificatesGenerated
(
task_input
,
expected_results
)
expected_results
=
{
expected_results
=
{
...
...
openedx/core/djangoapps/certificates/api.py
View file @
ab713181
...
@@ -2,8 +2,12 @@
...
@@ -2,8 +2,12 @@
The public API for certificates.
The public API for certificates.
"""
"""
from
datetime
import
datetime
from
datetime
import
datetime
from
pytz
import
UTC
from
pytz
import
UTC
from
course_modes.models
import
CourseMode
from
openedx.core.djangoapps.certificates.config
import
waffle
from
openedx.core.djangoapps.certificates.config
import
waffle
from
student.models
import
CourseEnrollment
SWITCHES
=
waffle
.
waffle
()
SWITCHES
=
waffle
.
waffle
()
...
@@ -19,6 +23,48 @@ def _enabled_and_instructor_paced(course):
...
@@ -19,6 +23,48 @@ def _enabled_and_instructor_paced(course):
return
False
return
False
def
certificates_viewable_for_course
(
course
):
"""
Returns True if certificates are viewable for any student enrolled in the course, False otherwise.
"""
if
course
.
self_paced
:
return
True
if
(
course
.
certificates_display_behavior
in
(
'early_with_info'
,
'early_no_info'
)
or
course
.
certificates_show_before_end
):
return
True
if
(
course
.
certificate_available_date
and
course
.
certificate_available_date
<=
datetime
.
now
(
UTC
)
):
return
True
if
(
course
.
certificate_available_date
is
None
and
course
.
has_ended
()
):
return
True
return
False
def
is_certificate_valid
(
certificate
):
"""
Returns True if the student has a valid, verified certificate for this course, False otherwise.
"""
return
CourseEnrollment
.
is_enrolled_as_verified
(
certificate
.
user
,
certificate
.
course_id
)
and
certificate
.
is_valid
()
def
can_show_certificate_message
(
course
,
student
,
course_grade
,
certificates_enabled_for_course
):
if
not
(
(
auto_certificate_generation_enabled
()
or
certificates_enabled_for_course
)
and
CourseEnrollment
.
is_enrolled
(
student
,
course
.
id
)
and
certificates_viewable_for_course
(
course
)
and
course_grade
.
passed
):
return
False
return
True
def
can_show_certificate_available_date_field
(
course
):
def
can_show_certificate_available_date_field
(
course
):
return
_enabled_and_instructor_paced
(
course
)
return
_enabled_and_instructor_paced
(
course
)
...
...
openedx/core/djangoapps/certificates/apps.py
0 → 100644
View file @
ab713181
"""
Openedx Certificates Application Configuration
"""
from
django.apps
import
AppConfig
class
OpenedxCertificatesConfig
(
AppConfig
):
"""
Application Configuration for Openedx Certificates.
"""
name
=
'openedx.core.djangoapps.certificates'
label
=
'openedx_certificates'
openedx/core/djangoapps/certificates/tests/test_api.py
View file @
ab713181
from
contextlib
import
contextmanager
from
contextlib
import
contextmanager
from
datetime
import
datetime
,
timedelta
import
itertools
import
itertools
from
unittest
import
TestCase
from
unittest
import
TestCase
import
ddt
import
ddt
import
pytz
import
waffle
import
waffle
from
course_modes.models
import
CourseMode
from
openedx.core.djangoapps.certificates
import
api
from
openedx.core.djangoapps.certificates
import
api
from
openedx.core.djangoapps.certificates.config
import
waffle
as
certs_waffle
from
openedx.core.djangoapps.certificates.config
import
waffle
as
certs_waffle
from
openedx.core.djangoapps.content.course_overviews.tests.factories
import
CourseOverviewFactory
from
openedx.core.djangoapps.content.course_overviews.tests.factories
import
CourseOverviewFactory
from
student.tests.factories
import
CourseEnrollmentFactory
,
UserFactory
# TODO: Copied from lms.djangoapps.certificates.models,
# to be resolved per https://openedx.atlassian.net/browse/EDUCATOR-1318
class
CertificateStatuses
(
object
):
"""
Enum for certificate statuses
"""
deleted
=
'deleted'
deleting
=
'deleting'
downloadable
=
'downloadable'
error
=
'error'
generating
=
'generating'
notpassing
=
'notpassing'
restricted
=
'restricted'
unavailable
=
'unavailable'
auditing
=
'auditing'
audit_passing
=
'audit_passing'
audit_notpassing
=
'audit_notpassing'
unverified
=
'unverified'
invalidated
=
'invalidated'
requesting
=
'requesting'
ALL_STATUSES
=
(
deleted
,
deleting
,
downloadable
,
error
,
generating
,
notpassing
,
restricted
,
unavailable
,
auditing
,
audit_passing
,
audit_notpassing
,
unverified
,
invalidated
,
requesting
)
class
MockGeneratedCertificate
(
object
):
"""
We can't import GeneratedCertificate from LMS here, so we roll
our own minimal Certificate model for testing.
"""
def
__init__
(
self
,
user
=
None
,
course_id
=
None
,
mode
=
None
,
status
=
None
):
self
.
user
=
user
self
.
course_id
=
course_id
self
.
mode
=
mode
self
.
status
=
status
def
is_valid
(
self
):
"""
Return True if certificate is valid else return False.
"""
return
self
.
status
==
CertificateStatuses
.
downloadable
@contextmanager
@contextmanager
...
@@ -15,18 +64,29 @@ def configure_waffle_namespace(feature_enabled):
...
@@ -15,18 +64,29 @@ def configure_waffle_namespace(feature_enabled):
namespace
=
certs_waffle
.
waffle
()
namespace
=
certs_waffle
.
waffle
()
with
namespace
.
override
(
certs_waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
feature_enabled
):
with
namespace
.
override
(
certs_waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
feature_enabled
):
yield
yield
@ddt.ddt
@ddt.ddt
class
FeatureEnabled
TestCase
(
TestCase
):
class
CertificatesApi
TestCase
(
TestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
FeatureEnabledTestCase
,
self
)
.
setUp
()
super
(
CertificatesApiTestCase
,
self
)
.
setUp
()
self
.
course
=
CourseOverviewFactory
.
create
()
self
.
course
=
CourseOverviewFactory
.
create
(
start
=
datetime
(
2017
,
1
,
1
,
tzinfo
=
pytz
.
UTC
),
def
tearDown
(
self
):
end
=
datetime
(
2017
,
1
,
31
,
tzinfo
=
pytz
.
UTC
),
super
(
FeatureEnabledTestCase
,
self
)
.
tearDown
()
certificate_available_date
=
None
self
.
course
.
self_paced
=
False
)
self
.
user
=
UserFactory
.
create
()
self
.
enrollment
=
CourseEnrollmentFactory
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
is_active
=
True
,
mode
=
'audit'
,
)
self
.
certificate
=
MockGeneratedCertificate
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
)
@ddt.data
(
True
,
False
)
@ddt.data
(
True
,
False
)
def
test_auto_certificate_generation_enabled
(
self
,
feature_enabled
):
def
test_auto_certificate_generation_enabled
(
self
,
feature_enabled
):
...
@@ -46,3 +106,18 @@ class FeatureEnabledTestCase(TestCase):
...
@@ -46,3 +106,18 @@ class FeatureEnabledTestCase(TestCase):
self
.
course
.
self_paced
=
is_self_paced
self
.
course
.
self_paced
=
is_self_paced
with
configure_waffle_namespace
(
feature_enabled
):
with
configure_waffle_namespace
(
feature_enabled
):
self
.
assertEqual
(
expected_value
,
api
.
can_show_certificate_available_date_field
(
self
.
course
))
self
.
assertEqual
(
expected_value
,
api
.
can_show_certificate_available_date_field
(
self
.
course
))
@ddt.data
(
(
CourseMode
.
VERIFIED
,
CertificateStatuses
.
downloadable
,
True
),
(
CourseMode
.
VERIFIED
,
CertificateStatuses
.
notpassing
,
False
),
(
CourseMode
.
AUDIT
,
CertificateStatuses
.
downloadable
,
False
)
)
@ddt.unpack
def
test_is_certificate_valid
(
self
,
enrollment_mode
,
certificate_status
,
expected_value
):
self
.
enrollment
.
mode
=
enrollment_mode
self
.
enrollment
.
save
()
self
.
certificate
.
mode
=
CourseMode
.
VERIFIED
self
.
certificate
.
status
=
certificate_status
self
.
assertEqual
(
expected_value
,
api
.
is_certificate_valid
(
self
.
certificate
))
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