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
386b1fc2
Commit
386b1fc2
authored
Sep 14, 2017
by
sanfordstudent
Committed by
GitHub
Sep 14, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #16013 from edx/sstudent/universal_waffle_switch
Use unified waffle switch
parents
8f7fbbf0
e4735a6a
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
37 additions
and
80 deletions
+37
-80
lms/djangoapps/certificates/signals.py
+3
-6
lms/djangoapps/certificates/tests/test_signals.py
+10
-10
lms/djangoapps/certificates/tests/test_webview_views.py
+2
-3
lms/djangoapps/courseware/tests/test_date_summary.py
+3
-4
lms/djangoapps/courseware/views/views.py
+2
-3
openedx/core/djangoapps/certificates/api.py
+5
-22
openedx/core/djangoapps/certificates/config/waffle.py
+0
-2
openedx/core/djangoapps/certificates/tests/test_api.py
+12
-30
No files found.
lms/djangoapps/certificates/signals.py
View file @
386b1fc2
...
...
@@ -14,10 +14,7 @@ from certificates.models import (
)
from
certificates.tasks
import
generate_certificate
from
lms.djangoapps.grades.new.course_grade_factory
import
CourseGradeFactory
from
openedx.core.djangoapps.certificates.api
import
(
auto_certificate_generation_enabled
,
auto_certificate_generation_enabled_for_course
,
)
from
openedx.core.djangoapps.certificates.api
import
auto_certificate_generation_enabled
from
openedx.core.djangoapps.certificates.config
import
waffle
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.signals.signals
import
COURSE_GRADE_NOW_PASSED
,
LEARNER_NOW_VERIFIED
...
...
@@ -30,7 +27,7 @@ log = logging.getLogger(__name__)
@receiver
(
post_save
,
sender
=
CertificateWhitelist
,
dispatch_uid
=
"append_certificate_whitelist"
)
def
_listen_for_certificate_whitelist_append
(
sender
,
instance
,
**
kwargs
):
# pylint: disable=unused-argument
course
=
CourseOverview
.
get_from_id
(
instance
.
course_id
)
if
not
auto_certificate_generation_enabled
_for_course
(
course
):
if
not
auto_certificate_generation_enabled
(
):
return
fire_ungenerated_certificate_task
(
instance
.
user
,
instance
.
course_id
)
...
...
@@ -47,7 +44,7 @@ def _listen_for_passing_grade(sender, user, course_id, **kwargs): # pylint: dis
downstream signal from COURSE_GRADE_CHANGED
"""
course
=
CourseOverview
.
get_from_id
(
course_id
)
if
not
auto_certificate_generation_enabled
_for_course
(
course
):
if
not
auto_certificate_generation_enabled
(
):
return
if
fire_ungenerated_certificate_task
(
user
,
course_id
):
...
...
lms/djangoapps/certificates/tests/test_signals.py
View file @
386b1fc2
...
...
@@ -74,19 +74,19 @@ class WhitelistGeneratedCertificatesTest(ModuleStoreTestCase):
def
test_cert_generation_on_whitelist_append_self_paced
(
self
):
"""
Verify that signal is sent, received, and fires task
based on '
SELF_PACED_ONLY
' flag
based on '
AUTO_CERTIFICATE_GENERATION
' flag
"""
with
mock
.
patch
(
'lms.djangoapps.certificates.signals.generate_certificate.apply_async'
,
return_value
=
None
)
as
mock_generate_certificate_apply_async
:
with
waffle
.
waffle
()
.
override
(
waffle
.
SELF_PACED_ONLY
,
active
=
False
):
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
False
):
CertificateWhitelist
.
objects
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
)
mock_generate_certificate_apply_async
.
assert_not_called
()
with
waffle
.
waffle
()
.
override
(
waffle
.
SELF_PACED_ONLY
,
active
=
True
):
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
True
):
CertificateWhitelist
.
objects
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
...
...
@@ -99,19 +99,19 @@ class WhitelistGeneratedCertificatesTest(ModuleStoreTestCase):
def
test_cert_generation_on_whitelist_append_instructor_paced
(
self
):
"""
Verify that signal is sent, received, and fires task
based on '
INSTRUCTOR_PACED_ONLY
' flag
based on '
AUTO_CERTIFICATE_GENERATION
' flag
"""
with
mock
.
patch
(
'lms.djangoapps.certificates.signals.generate_certificate.apply_async'
,
return_value
=
None
)
as
mock_generate_certificate_apply_async
:
with
waffle
.
waffle
()
.
override
(
waffle
.
INSTRUCTOR_PACED_ONLY
,
active
=
False
):
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
False
):
CertificateWhitelist
.
objects
.
create
(
user
=
self
.
user
,
course_id
=
self
.
ip_course
.
id
)
mock_generate_certificate_apply_async
.
assert_not_called
()
with
waffle
.
waffle
()
.
override
(
waffle
.
INSTRUCTOR_PACED_ONLY
,
active
=
True
):
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
True
):
CertificateWhitelist
.
objects
.
create
(
user
=
self
.
user
,
course_id
=
self
.
ip_course
.
id
...
...
@@ -156,7 +156,7 @@ class PassingGradeCertsTest(ModuleStoreTestCase):
'lms.djangoapps.certificates.signals.generate_certificate.apply_async'
,
return_value
=
None
)
as
mock_generate_certificate_apply_async
:
with
waffle
.
waffle
()
.
override
(
waffle
.
SELF_PACED_ONLY
,
active
=
True
):
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
True
):
grade_factory
=
CourseGradeFactory
()
# Not passing
grade_factory
.
update
(
self
.
user
,
self
.
course
)
...
...
@@ -174,7 +174,7 @@ class PassingGradeCertsTest(ModuleStoreTestCase):
'lms.djangoapps.certificates.signals.generate_certificate.apply_async'
,
return_value
=
None
)
as
mock_generate_certificate_apply_async
:
with
waffle
.
waffle
()
.
override
(
waffle
.
INSTRUCTOR_PACED_ONLY
,
active
=
True
):
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
True
):
grade_factory
=
CourseGradeFactory
()
# Not passing
grade_factory
.
update
(
self
.
user
,
self
.
ip_course
)
...
...
@@ -237,7 +237,7 @@ class LearnerTrackChangeCertsTest(ModuleStoreTestCase):
'lms.djangoapps.certificates.signals.generate_certificate.apply_async'
,
return_value
=
None
)
as
mock_generate_certificate_apply_async
:
with
waffle
.
waffle
()
.
override
(
waffle
.
SELF_PACED_ONLY
,
active
=
True
):
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
True
):
mock_generate_certificate_apply_async
.
assert_not_called
()
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user_one
,
...
...
@@ -254,7 +254,7 @@ class LearnerTrackChangeCertsTest(ModuleStoreTestCase):
'lms.djangoapps.certificates.signals.generate_certificate.apply_async'
,
return_value
=
None
)
as
mock_generate_certificate_apply_async
:
with
waffle
.
waffle
()
.
override
(
waffle
.
INSTRUCTOR_PACED_ONLY
,
active
=
True
):
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
True
):
mock_generate_certificate_apply_async
.
assert_not_called
()
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user_two
,
...
...
lms/djangoapps/certificates/tests/test_webview_views.py
View file @
386b1fc2
...
...
@@ -820,9 +820,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
expected_date
=
today
else
:
expected_date
=
self
.
course
.
certificate_available_date
with
waffle
.
waffle
()
.
override
(
waffle
.
SELF_PACED_ONLY
,
active
=
True
):
with
waffle
.
waffle
()
.
override
(
waffle
.
INSTRUCTOR_PACED_ONLY
,
active
=
True
):
response
=
self
.
client
.
get
(
test_url
)
with
waffle
.
waffle
()
.
override
(
waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
True
):
response
=
self
.
client
.
get
(
test_url
)
date
=
'{month} {day}, {year}'
.
format
(
month
=
strftime_localized
(
expected_date
,
"
%
B"
),
day
=
expected_date
.
day
,
...
...
lms/djangoapps/courseware/tests/test_date_summary.py
View file @
386b1fc2
...
...
@@ -329,7 +329,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
self
.
assertEqual
(
block
.
link
,
'{}?sku={}'
.
format
(
configuration
.
MULTIPLE_ITEMS_BASKET_PAGE_URL
,
sku
))
## CertificateAvailableDate
@waffle.testutils.override_switch
(
'certificates.
instructor_paced_only
'
,
True
)
@waffle.testutils.override_switch
(
'certificates.
auto_certificate_generation
'
,
True
)
def
test_no_certificate_available_date
(
self
):
course
=
create_course_run
(
days_till_start
=-
1
)
user
=
self
.
create_user
()
...
...
@@ -339,7 +339,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
self
.
assertFalse
(
block
.
is_enabled
)
## CertificateAvailableDate
@waffle.testutils.override_switch
(
'certificates.
instructor_paced_only
'
,
True
)
@waffle.testutils.override_switch
(
'certificates.
auto_certificate_generation
'
,
True
)
def
test_no_certificate_available_date_for_self_paced
(
self
):
course
=
create_self_paced_course_run
()
verified_user
=
self
.
create_user
()
...
...
@@ -350,7 +350,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
self
.
assertNotEqual
(
block
.
date
,
None
)
self
.
assertFalse
(
block
.
is_enabled
)
# @waffle.testutils.override_switch('certificates.instructor_paced_only', True)
def
test_no_certificate_available_date_for_audit_course
(
self
):
"""
Tests that Certificate Available Date is not visible in the course "Important Course Dates" section
...
...
@@ -374,7 +373,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
self
.
assertFalse
(
block
.
is_enabled
)
self
.
assertNotEqual
(
block
.
date
,
None
)
@waffle.testutils.override_switch
(
'certificates.
instructor_paced_only
'
,
True
)
@waffle.testutils.override_switch
(
'certificates.
auto_certificate_generation
'
,
True
)
def
test_certificate_available_date_defined
(
self
):
course
=
create_course_run
()
audit_user
=
self
.
create_user
()
...
...
lms/djangoapps/courseware/views/views.py
View file @
386b1fc2
...
...
@@ -933,9 +933,8 @@ def _get_cert_data(student, course, course_key, is_active, enrollment_mode, grad
may_view_certificate
=
get_course_by_id
(
course_key
)
.
may_certify
()
switches
=
certificates_waffle
.
waffle
()
switches_enabled
=
(
switches
.
is_enabled
(
certificates_waffle
.
SELF_PACED_ONLY
)
and
switches
.
is_enabled
(
certificates_waffle
.
INSTRUCTOR_PACED_ONLY
))
student_cert_generation_enabled
=
certs_api
.
cert_generation_enabled
(
course_key
)
if
not
switches_enabled
else
True
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
...
...
openedx/core/djangoapps/certificates/api.py
View file @
386b1fc2
...
...
@@ -10,39 +10,22 @@ SWITCHES = waffle.waffle()
def
auto_certificate_generation_enabled
():
return
(
SWITCHES
.
is_enabled
(
waffle
.
SELF_PACED_ONLY
)
or
SWITCHES
.
is_enabled
(
waffle
.
INSTRUCTOR_PACED_ONLY
)
)
return
SWITCHES
.
is_enabled
(
waffle
.
AUTO_CERTIFICATE_GENERATION
)
def
auto_certificate_generation_enabled_for_course
(
course
):
if
not
auto_certificate_generation_enabled
():
return
False
if
course
.
self_paced
:
if
not
SWITCHES
.
is_enabled
(
waffle
.
SELF_PACED_ONLY
):
return
False
else
:
if
not
SWITCHES
.
is_enabled
(
waffle
.
INSTRUCTOR_PACED_ONLY
):
return
False
return
True
def
_enabled_and_self_paced
(
course
):
if
auto_certificate_generation_enabled_for_course
(
course
):
def
_enabled_and_instructor_paced
(
course
):
if
auto_certificate_generation_enabled
():
return
not
course
.
self_paced
return
False
def
can_show_certificate_available_date_field
(
course
):
return
_enabled_and_
self
_paced
(
course
)
return
_enabled_and_
instructor
_paced
(
course
)
def
display_date_for_certificate
(
course
,
certificate
):
if
(
auto_certificate_generation_enabled
_for_course
(
course
)
and
auto_certificate_generation_enabled
(
)
and
not
course
.
self_paced
and
course
.
certificate_available_date
and
course
.
certificate_available_date
<
datetime
.
now
(
UTC
)
...
...
openedx/core/djangoapps/certificates/config/waffle.py
View file @
386b1fc2
...
...
@@ -9,8 +9,6 @@ WAFFLE_NAMESPACE = u'certificates'
# Switches
AUTO_CERTIFICATE_GENERATION
=
u'auto_certificate_generation'
SELF_PACED_ONLY
=
u'self_paced_only'
INSTRUCTOR_PACED_ONLY
=
u'instructor_paced_only'
def
waffle
():
...
...
openedx/core/djangoapps/certificates/tests/test_api.py
View file @
386b1fc2
...
...
@@ -11,11 +11,10 @@ from openedx.core.djangoapps.content.course_overviews.tests.factories import Cou
@contextmanager
def
configure_waffle_namespace
(
self_paced_enabled
,
instructor_paced
_enabled
):
def
configure_waffle_namespace
(
feature
_enabled
):
namespace
=
certs_waffle
.
waffle
()
with
namespace
.
override
(
certs_waffle
.
SELF_PACED_ONLY
,
active
=
self_paced_enabled
):
with
namespace
.
override
(
certs_waffle
.
INSTRUCTOR_PACED_ONLY
,
active
=
instructor_paced_enabled
):
with
namespace
.
override
(
certs_waffle
.
AUTO_CERTIFICATE_GENERATION
,
active
=
feature_enabled
):
yield
...
...
@@ -29,38 +28,21 @@ class FeatureEnabledTestCase(TestCase):
super
(
FeatureEnabledTestCase
,
self
)
.
tearDown
()
self
.
course
.
self_paced
=
False
@ddt.data
(
*
itertools
.
product
((
True
,
False
),
(
True
,
False
)))
@ddt.unpack
def
test_auto_certificate_generation_enabled
(
self
,
self_paced_enabled
,
instructor_paced_enabled
):
expected_value
=
self_paced_enabled
or
instructor_paced_enabled
with
configure_waffle_namespace
(
self_paced_enabled
,
instructor_paced_enabled
):
self
.
assertEqual
(
expected_value
,
api
.
auto_certificate_generation_enabled
())
@ddt.data
(
(
False
,
False
,
True
,
False
),
# feature not enabled should return False
(
False
,
True
,
True
,
False
),
# self-paced feature enabled and self-paced course should return False
(
True
,
False
,
True
,
True
),
# self-paced feature enabled and self-paced course should return True
(
True
,
False
,
False
,
False
),
# instructor-paced feature enabled and self-paced course should return False
(
False
,
True
,
False
,
True
)
# instructor-paced feature enabled and instructor-paced course should return True
)
@ddt.unpack
def
test_auto_certificate_generation_enabled_for_course
(
self
,
self_paced_enabled
,
instructor_paced_enabled
,
is_self_paced
,
expected_value
):
self
.
course
.
self_paced
=
is_self_paced
with
configure_waffle_namespace
(
self_paced_enabled
,
instructor_paced_enabled
):
self
.
assertEqual
(
expected_value
,
api
.
auto_certificate_generation_enabled_for_course
(
self
.
course
))
@ddt.data
(
True
,
False
)
def
test_auto_certificate_generation_enabled
(
self
,
feature_enabled
):
with
configure_waffle_namespace
(
feature_enabled
):
self
.
assertEqual
(
feature_enabled
,
api
.
auto_certificate_generation_enabled
())
@ddt.data
(
(
True
,
False
,
True
,
False
),
# feature enabled and self-paced should return False
(
False
,
True
,
False
,
True
),
# feature enabled and instructor-paced should return True
(
False
,
False
,
True
,
False
),
# feature not enabled and self-paced should return False
(
False
,
False
,
False
,
False
),
# feature not enabled and instructor-paced should return False
(
True
,
True
,
False
),
# feature enabled and self-paced should return False
(
True
,
False
,
True
),
# feature enabled and instructor-paced should return True
(
False
,
True
,
False
),
# feature not enabled and self-paced should return False
(
False
,
False
,
False
),
# feature not enabled and instructor-paced should return False
)
@ddt.unpack
def
test_can_show_certificate_available_date_field
(
self
,
self_paced_enabled
,
instructor_paced
_enabled
,
is_self_paced
,
expected_value
self
,
feature
_enabled
,
is_self_paced
,
expected_value
):
self
.
course
.
self_paced
=
is_self_paced
with
configure_waffle_namespace
(
self_paced_enabled
,
instructor_paced
_enabled
):
with
configure_waffle_namespace
(
feature
_enabled
):
self
.
assertEqual
(
expected_value
,
api
.
can_show_certificate_available_date_field
(
self
.
course
))
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