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
40d3f4f2
Commit
40d3f4f2
authored
Oct 10, 2017
by
sandroroux
Committed by
Calen Pennington
Oct 13, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Unit tests for "_add_upsell_button_to_email_template".
parent
d571adfb
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
192 additions
and
110 deletions
+192
-110
common/djangoapps/student/models.py
+40
-22
common/djangoapps/student/tests/test_models.py
+8
-2
lms/djangoapps/ccx/tests/test_field_override_performance.py
+27
-27
lms/djangoapps/courseware/date_summary.py
+1
-1
lms/djangoapps/courseware/migrations/0004_auto_20171010_1639.py
+19
-0
lms/djangoapps/courseware/models.py
+1
-1
lms/djangoapps/courseware/tests/test_date_summary.py
+0
-12
lms/djangoapps/courseware/tests/test_views.py
+5
-5
openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py
+0
-0
openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py
+54
-13
openedx/core/djangoapps/schedules/signals.py
+1
-1
openedx/core/djangoapps/schedules/tasks.py
+35
-25
openedx/features/course_experience/tests/views/test_course_updates.py
+1
-1
No files found.
common/djangoapps/student/models.py
View file @
40d3f4f2
...
@@ -1700,12 +1700,9 @@ class CourseEnrollment(models.Model):
...
@@ -1700,12 +1700,9 @@ class CourseEnrollment(models.Model):
def
upgrade_deadline
(
self
):
def
upgrade_deadline
(
self
):
"""
"""
Returns the upgrade deadline for this enrollment, if it is upgradeable.
Returns the upgrade deadline for this enrollment, if it is upgradeable.
If the seat cannot be upgraded, None is returned.
If the seat cannot be upgraded, None is returned.
Note:
Note:
When loading this model, use `select_related` to retrieve the associated schedule object.
When loading this model, use `select_related` to retrieve the associated schedule object.
Returns:
Returns:
datetime|None
datetime|None
"""
"""
...
@@ -1717,40 +1714,61 @@ class CourseEnrollment(models.Model):
...
@@ -1717,40 +1714,61 @@ class CourseEnrollment(models.Model):
)
)
return
None
return
None
if
self
.
dynamic_upgrade_deadline
is
not
None
:
return
self
.
dynamic_upgrade_deadline
return
self
.
course_upgrade_deadline
@cached_property
def
dynamic_upgrade_deadline
(
self
):
try
:
try
:
schedule_driven_deadlines_enabled
=
(
course_overview
=
self
.
course
DynamicUpgradeDeadlineConfiguration
.
is_enabled
()
except
CourseOverview
.
DoesNotExist
:
or
CourseDynamicUpgradeDeadlineConfiguration
.
is_enabled
(
self
.
course_id
)
course_overview
=
self
.
course_overview
if
not
course_overview
.
self_paced
:
return
None
if
not
DynamicUpgradeDeadlineConfiguration
.
is_enabled
():
return
None
course_config
=
CourseDynamicUpgradeDeadlineConfiguration
.
current
(
self
.
course_id
)
if
course_config
.
enabled
and
course_config
.
opt_out
:
return
None
try
:
if
not
self
.
schedule
:
return
None
log
.
debug
(
'Schedules: Pulling upgrade deadline for CourseEnrollment
%
d from Schedule
%
d.'
,
self
.
id
,
self
.
schedule
.
id
)
)
if
(
upgrade_deadline
=
self
.
schedule
.
upgrade_deadline
schedule_driven_deadlines_enabled
and
self
.
course_overview
.
self_paced
and
self
.
schedule
and
self
.
schedule
.
upgrade_deadline
is
not
None
):
log
.
debug
(
'Schedules: Pulling upgrade deadline for CourseEnrollment
%
d from Schedule
%
d.'
,
self
.
id
,
self
.
schedule
.
id
)
return
self
.
schedule
.
upgrade_deadline
except
ObjectDoesNotExist
:
except
ObjectDoesNotExist
:
# NOTE: Schedule has a one-to-one mapping with CourseEnrollment. If no schedule is associated
# NOTE: Schedule has a one-to-one mapping with CourseEnrollment. If no schedule is associated
# with this enrollment, Django will raise an exception rather than return None.
# with this enrollment, Django will raise an exception rather than return None.
log
.
debug
(
'Schedules: No schedule exists for CourseEnrollment
%
d.'
,
self
.
id
)
log
.
debug
(
'Schedules: No schedule exists for CourseEnrollment
%
d.'
,
self
.
id
)
pass
return
None
if
upgrade_deadline
is
None
or
datetime
.
now
(
UTC
)
>=
upgrade_deadline
:
return
None
return
upgrade_deadline
@cached_property
def
course_upgrade_deadline
(
self
):
try
:
try
:
if
self
.
verified_mode
:
if
self
.
verified_mode
:
log
.
debug
(
'Schedules: Defaulting to verified mode expiration date-time for
%
s.'
,
self
.
course_id
)
log
.
debug
(
'Schedules: Defaulting to verified mode expiration date-time for
%
s.'
,
self
.
course_id
)
return
self
.
verified_mode
.
expiration_datetime
return
self
.
verified_mode
.
expiration_datetime
else
:
else
:
log
.
debug
(
'Schedules: No verified mode located for
%
s.'
,
self
.
course_id
)
log
.
debug
(
'Schedules: No verified mode located for
%
s.'
,
self
.
course_id
)
return
None
except
CourseMode
.
DoesNotExist
:
except
CourseMode
.
DoesNotExist
:
log
.
debug
(
'Schedules:
%
s has no verified mode.'
,
self
.
course_id
)
log
.
debug
(
'Schedules:
%
s has no verified mode.'
,
self
.
course_id
)
pass
return
None
log
.
debug
(
'Schedules: Returning default of `None`'
)
return
None
def
is_verified_enrollment
(
self
):
def
is_verified_enrollment
(
self
):
"""
"""
...
...
common/djangoapps/student/tests/test_models.py
View file @
40d3f4f2
...
@@ -14,6 +14,7 @@ from django.db.models.functions import Lower
...
@@ -14,6 +14,7 @@ from django.db.models.functions import Lower
from
course_modes.models
import
CourseMode
from
course_modes.models
import
CourseMode
from
course_modes.tests.factories
import
CourseModeFactory
from
course_modes.tests.factories
import
CourseModeFactory
from
courseware.models
import
DynamicUpgradeDeadlineConfiguration
from
courseware.models
import
DynamicUpgradeDeadlineConfiguration
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.schedules.models
import
Schedule
from
openedx.core.djangoapps.schedules.models
import
Schedule
from
openedx.core.djangoapps.schedules.tests.factories
import
ScheduleFactory
from
openedx.core.djangoapps.schedules.tests.factories
import
ScheduleFactory
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
...
@@ -142,9 +143,14 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
...
@@ -142,9 +143,14 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
course_id
=
course
.
id
,
course_id
=
course
.
id
,
mode_slug
=
CourseMode
.
VERIFIED
,
mode_slug
=
CourseMode
.
VERIFIED
,
# This must be in the future to ensure it is returned by downstream code.
# This must be in the future to ensure it is returned by downstream code.
expiration_datetime
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
+
datetime
.
timedelta
(
days
=
1
),
expiration_datetime
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
+
datetime
.
timedelta
(
days
=
30
),
)
course_overview
=
CourseOverview
.
load_from_module_store
(
course
.
id
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
,
course
=
course_overview
,
)
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
)
# The schedule's upgrade deadline should be used if a schedule exists
# The schedule's upgrade deadline should be used if a schedule exists
DynamicUpgradeDeadlineConfiguration
.
objects
.
create
(
enabled
=
True
)
DynamicUpgradeDeadlineConfiguration
.
objects
.
create
(
enabled
=
True
)
...
...
lms/djangoapps/ccx/tests/test_field_override_performance.py
View file @
40d3f4f2
...
@@ -237,18 +237,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
...
@@ -237,18 +237,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
# # of sql queries to default,
# # of sql queries to default,
# # of mongo queries,
# # of mongo queries,
# )
# )
(
'no_overrides'
,
1
,
True
,
False
):
(
1
8
,
1
),
(
'no_overrides'
,
1
,
True
,
False
):
(
1
6
,
1
),
(
'no_overrides'
,
2
,
True
,
False
):
(
1
8
,
1
),
(
'no_overrides'
,
2
,
True
,
False
):
(
1
6
,
1
),
(
'no_overrides'
,
3
,
True
,
False
):
(
1
8
,
1
),
(
'no_overrides'
,
3
,
True
,
False
):
(
1
6
,
1
),
(
'ccx'
,
1
,
True
,
False
):
(
1
8
,
1
),
(
'ccx'
,
1
,
True
,
False
):
(
1
6
,
1
),
(
'ccx'
,
2
,
True
,
False
):
(
1
8
,
1
),
(
'ccx'
,
2
,
True
,
False
):
(
1
6
,
1
),
(
'ccx'
,
3
,
True
,
False
):
(
1
8
,
1
),
(
'ccx'
,
3
,
True
,
False
):
(
1
6
,
1
),
(
'no_overrides'
,
1
,
False
,
False
):
(
1
8
,
1
),
(
'no_overrides'
,
1
,
False
,
False
):
(
1
6
,
1
),
(
'no_overrides'
,
2
,
False
,
False
):
(
1
8
,
1
),
(
'no_overrides'
,
2
,
False
,
False
):
(
1
6
,
1
),
(
'no_overrides'
,
3
,
False
,
False
):
(
1
8
,
1
),
(
'no_overrides'
,
3
,
False
,
False
):
(
1
6
,
1
),
(
'ccx'
,
1
,
False
,
False
):
(
1
8
,
1
),
(
'ccx'
,
1
,
False
,
False
):
(
1
6
,
1
),
(
'ccx'
,
2
,
False
,
False
):
(
1
8
,
1
),
(
'ccx'
,
2
,
False
,
False
):
(
1
6
,
1
),
(
'ccx'
,
3
,
False
,
False
):
(
1
8
,
1
),
(
'ccx'
,
3
,
False
,
False
):
(
1
6
,
1
),
}
}
...
@@ -260,19 +260,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
...
@@ -260,19 +260,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
__test__
=
True
__test__
=
True
TEST_DATA
=
{
TEST_DATA
=
{
(
'no_overrides'
,
1
,
True
,
False
):
(
1
8
,
3
),
(
'no_overrides'
,
1
,
True
,
False
):
(
1
6
,
3
),
(
'no_overrides'
,
2
,
True
,
False
):
(
1
8
,
3
),
(
'no_overrides'
,
2
,
True
,
False
):
(
1
6
,
3
),
(
'no_overrides'
,
3
,
True
,
False
):
(
1
8
,
3
),
(
'no_overrides'
,
3
,
True
,
False
):
(
1
6
,
3
),
(
'ccx'
,
1
,
True
,
False
):
(
1
8
,
3
),
(
'ccx'
,
1
,
True
,
False
):
(
1
6
,
3
),
(
'ccx'
,
2
,
True
,
False
):
(
1
8
,
3
),
(
'ccx'
,
2
,
True
,
False
):
(
1
6
,
3
),
(
'ccx'
,
3
,
True
,
False
):
(
1
8
,
3
),
(
'ccx'
,
3
,
True
,
False
):
(
1
6
,
3
),
(
'ccx'
,
1
,
True
,
True
):
(
1
9
,
3
),
(
'ccx'
,
1
,
True
,
True
):
(
1
7
,
3
),
(
'ccx'
,
2
,
True
,
True
):
(
1
9
,
3
),
(
'ccx'
,
2
,
True
,
True
):
(
1
7
,
3
),
(
'ccx'
,
3
,
True
,
True
):
(
1
9
,
3
),
(
'ccx'
,
3
,
True
,
True
):
(
1
7
,
3
),
(
'no_overrides'
,
1
,
False
,
False
):
(
1
8
,
3
),
(
'no_overrides'
,
1
,
False
,
False
):
(
1
6
,
3
),
(
'no_overrides'
,
2
,
False
,
False
):
(
1
8
,
3
),
(
'no_overrides'
,
2
,
False
,
False
):
(
1
6
,
3
),
(
'no_overrides'
,
3
,
False
,
False
):
(
1
8
,
3
),
(
'no_overrides'
,
3
,
False
,
False
):
(
1
6
,
3
),
(
'ccx'
,
1
,
False
,
False
):
(
1
8
,
3
),
(
'ccx'
,
1
,
False
,
False
):
(
1
6
,
3
),
(
'ccx'
,
2
,
False
,
False
):
(
1
8
,
3
),
(
'ccx'
,
2
,
False
,
False
):
(
1
6
,
3
),
(
'ccx'
,
3
,
False
,
False
):
(
1
8
,
3
),
(
'ccx'
,
3
,
False
,
False
):
(
1
6
,
3
),
}
}
lms/djangoapps/courseware/date_summary.py
View file @
40d3f4f2
...
@@ -396,7 +396,7 @@ def verified_upgrade_deadline_link(user, course=None, course_id=None):
...
@@ -396,7 +396,7 @@ def verified_upgrade_deadline_link(user, course=None, course_id=None):
course_id (:class:`.CourseKey`): The course_id of the course to render for.
course_id (:class:`.CourseKey`): The course_id of the course to render for.
Returns:
Returns:
The formatted link t
o t
hat will allow the user to upgrade to verified
The formatted link that will allow the user to upgrade to verified
in this course.
in this course.
"""
"""
if
course
is
not
None
:
if
course
is
not
None
:
...
...
lms/djangoapps/courseware/migrations/0004_auto_20171010_1639.py
0 → 100644
View file @
40d3f4f2
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'courseware'
,
'0003_auto_20170825_0935'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'coursedynamicupgradedeadlineconfiguration'
,
name
=
'opt_out'
,
field
=
models
.
BooleanField
(
default
=
False
,
help_text
=
'Disable the dynamic upgrade deadline for this course run.'
),
),
]
lms/djangoapps/courseware/models.py
View file @
40d3f4f2
...
@@ -398,5 +398,5 @@ class CourseDynamicUpgradeDeadlineConfiguration(ConfigurationModel):
...
@@ -398,5 +398,5 @@ class CourseDynamicUpgradeDeadlineConfiguration(ConfigurationModel):
)
)
opt_out
=
models
.
BooleanField
(
opt_out
=
models
.
BooleanField
(
default
=
False
,
default
=
False
,
help_text
=
_
(
'
This does not do anything and is no longer used. Setting enabled=False has the same effect
.'
)
help_text
=
_
(
'
Disable the dynamic upgrade deadline for this course run
.'
)
)
)
lms/djangoapps/courseware/tests/test_date_summary.py
View file @
40d3f4f2
...
@@ -618,18 +618,6 @@ class TestScheduleOverrides(SharedModuleStoreTestCase):
...
@@ -618,18 +618,6 @@ class TestScheduleOverrides(SharedModuleStoreTestCase):
self
.
assertEqual
(
block
.
date
,
expected
)
self
.
assertEqual
(
block
.
date
,
expected
)
@override_waffle_flag
(
CREATE_SCHEDULE_WAFFLE_FLAG
,
True
)
@override_waffle_flag
(
CREATE_SCHEDULE_WAFFLE_FLAG
,
True
)
def
test_date_with_self_paced_with_single_course
(
self
):
""" If the global switch is off, a single course can still be enabled. """
course
=
create_self_paced_course_run
(
days_till_start
=-
1
)
DynamicUpgradeDeadlineConfiguration
.
objects
.
create
(
enabled
=
False
)
course_config
=
CourseDynamicUpgradeDeadlineConfiguration
.
objects
.
create
(
enabled
=
True
,
course_id
=
course
.
id
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
)
block
=
VerifiedUpgradeDeadlineDate
(
course
,
enrollment
.
user
)
expected
=
enrollment
.
created
+
timedelta
(
days
=
course_config
.
deadline_days
)
self
.
assertEqual
(
block
.
date
,
expected
)
@override_waffle_flag
(
CREATE_SCHEDULE_WAFFLE_FLAG
,
True
)
def
test_date_with_existing_schedule
(
self
):
def
test_date_with_existing_schedule
(
self
):
""" If a schedule is created while deadlines are disabled, they shouldn't magically appear once the feature is
""" If a schedule is created while deadlines are disabled, they shouldn't magically appear once the feature is
turned on. """
turned on. """
...
...
lms/djangoapps/courseware/tests/test_views.py
View file @
40d3f4f2
...
@@ -213,8 +213,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
...
@@ -213,8 +213,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
NUM_PROBLEMS
=
20
NUM_PROBLEMS
=
20
@ddt.data
(
@ddt.data
(
(
ModuleStoreEnum
.
Type
.
mongo
,
10
,
14
6
),
(
ModuleStoreEnum
.
Type
.
mongo
,
10
,
14
5
),
(
ModuleStoreEnum
.
Type
.
split
,
4
,
14
6
),
(
ModuleStoreEnum
.
Type
.
split
,
4
,
14
5
),
)
)
@ddt.unpack
@ddt.unpack
def
test_index_query_counts
(
self
,
store_type
,
expected_mongo_query_count
,
expected_mysql_query_count
):
def
test_index_query_counts
(
self
,
store_type
,
expected_mongo_query_count
,
expected_mysql_query_count
):
...
@@ -1457,13 +1457,13 @@ class ProgressPageTests(ProgressPageBaseTests):
...
@@ -1457,13 +1457,13 @@ 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
(
3
5
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
),
check_mongo_calls
(
1
):
with
self
.
assertNumQueries
(
3
4
if
self_paced
else
33
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
),
check_mongo_calls
(
1
):
self
.
_get_progress_page
()
self
.
_get_progress_page
()
@patch.dict
(
settings
.
FEATURES
,
{
'ASSUME_ZERO_GRADE_IF_ABSENT_FOR_ALL_TESTS'
:
False
})
@patch.dict
(
settings
.
FEATURES
,
{
'ASSUME_ZERO_GRADE_IF_ABSENT_FOR_ALL_TESTS'
:
False
})
@ddt.data
(
@ddt.data
(
(
False
,
4
2
,
26
),
(
False
,
4
0
,
26
),
(
True
,
3
5
,
22
)
(
True
,
3
3
,
22
)
)
)
@ddt.unpack
@ddt.unpack
def
test_progress_queries
(
self
,
enable_waffle
,
initial
,
subsequent
):
def
test_progress_queries
(
self
,
enable_waffle
,
initial
,
subsequent
):
...
...
openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py
View file @
40d3f4f2
This diff is collapsed.
Click to expand it.
openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py
View file @
40d3f4f2
...
@@ -14,21 +14,41 @@ from mock import Mock, patch
...
@@ -14,21 +14,41 @@ from mock import Mock, patch
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.locator
import
CourseLocator
from
opaque_keys.edx.locator
import
CourseLocator
from
course_modes.models
import
CourseMode
from
course_modes.tests.factories
import
CourseModeFactory
from
courseware.models
import
DynamicUpgradeDeadlineConfiguration
from
openedx.core.djangoapps.schedules
import
resolvers
,
tasks
from
openedx.core.djangoapps.schedules
import
resolvers
,
tasks
from
openedx.core.djangoapps.schedules.management.commands
import
send_upgrade_reminder
as
reminder
from
openedx.core.djangoapps.schedules.management.commands
import
send_upgrade_reminder
as
reminder
from
openedx.core.djangoapps.schedules.tests.factories
import
ScheduleConfigFactory
,
ScheduleFactory
from
openedx.core.djangoapps.schedules.tests.factories
import
ScheduleConfigFactory
,
ScheduleFactory
from
openedx.core.djangoapps.site_configuration.tests.factories
import
SiteConfigurationFactory
,
SiteFactory
from
openedx.core.djangoapps.site_configuration.tests.factories
import
SiteConfigurationFactory
,
SiteFactory
from
openedx.core.djangolib.testing.utils
import
CacheIsolationTestCase
,
skip_unless_lms
from
openedx.core.djangoapps.waffle_utils.testutils
import
WAFFLE_TABLES
from
openedx.core.djangolib.testing.utils
import
CacheIsolationTestCase
,
skip_unless_lms
,
FilteredQueryCountMixin
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
UserFactory
# 1) Load the current django site
# 2) Query the schedules to find all of the template context information
NUM_QUERIES_NO_MATCHING_SCHEDULES
=
2
# 3) Query all course modes for all courses in returned schedules
NUM_QUERIES_WITH_MATCHES
=
NUM_QUERIES_NO_MATCHING_SCHEDULES
+
1
# 1) Global dynamic deadline switch
# 2) E-commerce configuration
NUM_QUERIES_WITH_DEADLINE
=
2
NUM_COURSE_MODES_QUERIES
=
1
@ddt.ddt
@ddt.ddt
@skip_unless_lms
@skip_unless_lms
@skipUnless
(
'openedx.core.djangoapps.schedules.apps.SchedulesConfig'
in
settings
.
INSTALLED_APPS
,
@skipUnless
(
'openedx.core.djangoapps.schedules.apps.SchedulesConfig'
in
settings
.
INSTALLED_APPS
,
"Can't test schedules if the app isn't installed"
)
"Can't test schedules if the app isn't installed"
)
class
TestUpgradeReminder
(
CacheIsolationTestCase
):
class
TestUpgradeReminder
(
FilteredQueryCountMixin
,
CacheIsolationTestCase
):
# pylint: disable=protected-access
# pylint: disable=protected-access
ENABLED_CACHES
=
[
'default'
]
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestUpgradeReminder
,
self
)
.
setUp
()
super
(
TestUpgradeReminder
,
self
)
.
setUp
()
...
@@ -74,20 +94,26 @@ class TestUpgradeReminder(CacheIsolationTestCase):
...
@@ -74,20 +94,26 @@ class TestUpgradeReminder(CacheIsolationTestCase):
schedules
=
[
schedules
=
[
ScheduleFactory
.
create
(
ScheduleFactory
.
create
(
upgrade_deadline
=
datetime
.
datetime
(
2017
,
8
,
3
,
18
,
44
,
30
,
tzinfo
=
pytz
.
UTC
),
upgrade_deadline
=
datetime
.
datetime
(
2017
,
8
,
3
,
18
,
44
,
30
,
tzinfo
=
pytz
.
UTC
),
enrollment__user
=
UserFactory
.
create
(),
enrollment__course__id
=
CourseLocator
(
'edX'
,
'toy'
,
'Bin'
)
enrollment__course__id
=
CourseLocator
(
'edX'
,
'toy'
,
'Bin'
)
)
for
_
in
range
(
schedule_count
)
)
for
i
in
range
(
schedule_count
)
]
]
bins_in_use
=
frozenset
((
s
.
enrollment
.
user
.
id
%
tasks
.
UPGRADE_REMINDER_NUM_BINS
)
for
s
in
schedules
)
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
18
,
tzinfo
=
pytz
.
UTC
)
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
18
,
tzinfo
=
pytz
.
UTC
)
test_time_str
=
serialize
(
test_time
)
test_time_str
=
serialize
(
test_time
)
for
b
in
range
(
tasks
.
UPGRADE_REMINDER_NUM_BINS
):
for
b
in
range
(
tasks
.
UPGRADE_REMINDER_NUM_BINS
):
# waffle flag takes an extra query before it is cached
expected_queries
=
NUM_QUERIES_NO_MATCHING_SCHEDULES
with
self
.
assertNumQueries
(
3
if
b
==
0
else
2
):
if
b
in
bins_in_use
:
# to fetch course modes for valid schedules
expected_queries
+=
NUM_COURSE_MODES_QUERIES
with
self
.
assertNumQueries
(
expected_queries
,
table_blacklist
=
WAFFLE_TABLES
):
tasks
.
upgrade_reminder_schedule_bin
(
tasks
.
upgrade_reminder_schedule_bin
(
self
.
site_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
2
,
bin_num
=
b
,
self
.
site_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
2
,
bin_num
=
b
,
org_list
=
[
schedules
[
0
]
.
enrollment
.
course
.
org
],
org_list
=
[
schedules
[
0
]
.
enrollment
.
course
.
org
],
)
)
self
.
assertEqual
(
mock_schedule_send
.
apply_async
.
call_count
,
schedule_count
)
self
.
assertEqual
(
mock_schedule_send
.
apply_async
.
call_count
,
schedule_count
)
self
.
assertFalse
(
mock_ace
.
send
.
called
)
self
.
assertFalse
(
mock_ace
.
send
.
called
)
...
@@ -103,8 +129,7 @@ class TestUpgradeReminder(CacheIsolationTestCase):
...
@@ -103,8 +129,7 @@ class TestUpgradeReminder(CacheIsolationTestCase):
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
20
,
tzinfo
=
pytz
.
UTC
)
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
20
,
tzinfo
=
pytz
.
UTC
)
test_time_str
=
serialize
(
test_time
)
test_time_str
=
serialize
(
test_time
)
for
b
in
range
(
tasks
.
UPGRADE_REMINDER_NUM_BINS
):
for
b
in
range
(
tasks
.
UPGRADE_REMINDER_NUM_BINS
):
# waffle flag takes an extra query before it is cached
with
self
.
assertNumQueries
(
NUM_QUERIES_NO_MATCHING_SCHEDULES
,
table_blacklist
=
WAFFLE_TABLES
):
with
self
.
assertNumQueries
(
3
if
b
==
0
else
2
):
tasks
.
upgrade_reminder_schedule_bin
(
tasks
.
upgrade_reminder_schedule_bin
(
self
.
site_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
2
,
bin_num
=
b
,
self
.
site_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
2
,
bin_num
=
b
,
org_list
=
[
schedule
.
enrollment
.
course
.
org
],
org_list
=
[
schedule
.
enrollment
.
course
.
org
],
...
@@ -176,7 +201,7 @@ class TestUpgradeReminder(CacheIsolationTestCase):
...
@@ -176,7 +201,7 @@ class TestUpgradeReminder(CacheIsolationTestCase):
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
17
,
tzinfo
=
pytz
.
UTC
)
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
17
,
tzinfo
=
pytz
.
UTC
)
test_time_str
=
serialize
(
test_time
)
test_time_str
=
serialize
(
test_time
)
with
self
.
assertNumQueries
(
3
):
with
self
.
assertNumQueries
(
NUM_QUERIES_WITH_MATCHES
,
table_blacklist
=
WAFFLE_TABLES
):
tasks
.
upgrade_reminder_schedule_bin
(
tasks
.
upgrade_reminder_schedule_bin
(
limited_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
2
,
bin_num
=
0
,
limited_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
2
,
bin_num
=
0
,
org_list
=
org_list
,
exclude_orgs
=
exclude_orgs
,
org_list
=
org_list
,
exclude_orgs
=
exclude_orgs
,
...
@@ -200,7 +225,7 @@ class TestUpgradeReminder(CacheIsolationTestCase):
...
@@ -200,7 +225,7 @@ class TestUpgradeReminder(CacheIsolationTestCase):
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
19
,
44
,
30
,
tzinfo
=
pytz
.
UTC
)
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
19
,
44
,
30
,
tzinfo
=
pytz
.
UTC
)
test_time_str
=
serialize
(
test_time
)
test_time_str
=
serialize
(
test_time
)
with
self
.
assertNumQueries
(
3
):
with
self
.
assertNumQueries
(
NUM_QUERIES_WITH_MATCHES
,
table_blacklist
=
WAFFLE_TABLES
):
tasks
.
upgrade_reminder_schedule_bin
(
tasks
.
upgrade_reminder_schedule_bin
(
self
.
site_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
2
,
self
.
site_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
2
,
bin_num
=
user
.
id
%
tasks
.
UPGRADE_REMINDER_NUM_BINS
,
bin_num
=
user
.
id
%
tasks
.
UPGRADE_REMINDER_NUM_BINS
,
...
@@ -212,18 +237,31 @@ class TestUpgradeReminder(CacheIsolationTestCase):
...
@@ -212,18 +237,31 @@ class TestUpgradeReminder(CacheIsolationTestCase):
@ddt.data
(
*
itertools
.
product
((
1
,
10
,
100
),
(
2
,
10
)))
@ddt.data
(
*
itertools
.
product
((
1
,
10
,
100
),
(
2
,
10
)))
@ddt.unpack
@ddt.unpack
def
test_templates
(
self
,
message_count
,
day
):
def
test_templates
(
self
,
message_count
,
day
):
DynamicUpgradeDeadlineConfiguration
.
objects
.
create
(
enabled
=
True
)
now
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
future_date
=
now
+
datetime
.
timedelta
(
days
=
21
)
user
=
UserFactory
.
create
()
user
=
UserFactory
.
create
()
schedules
=
[
schedules
=
[
ScheduleFactory
.
create
(
ScheduleFactory
.
create
(
upgrade_deadline
=
datetime
.
datetime
(
2017
,
8
,
3
,
19
,
44
,
30
,
tzinfo
=
pytz
.
UTC
)
,
upgrade_deadline
=
future_date
,
enrollment__user
=
user
,
enrollment__user
=
user
,
enrollment__course__id
=
CourseLocator
(
'edX'
,
'toy'
,
'Course{}'
.
format
(
course_num
))
enrollment__course__id
=
CourseLocator
(
'edX'
,
'toy'
,
'Course{}'
.
format
(
course_num
))
)
)
for
course_num
in
range
(
message_count
)
for
course_num
in
range
(
message_count
)
]
]
test_time
=
datetime
.
datetime
(
2017
,
8
,
3
,
19
,
tzinfo
=
pytz
.
UTC
)
for
schedule
in
schedules
:
schedule
.
enrollment
.
course
.
self_paced
=
True
schedule
.
enrollment
.
course
.
save
()
CourseModeFactory
(
course_id
=
schedule
.
enrollment
.
course
.
id
,
mode_slug
=
CourseMode
.
VERIFIED
,
expiration_datetime
=
future_date
)
test_time
=
future_date
test_time_str
=
serialize
(
test_time
)
test_time_str
=
serialize
(
test_time
)
patch_policies
(
self
,
[
StubPolicy
([
ChannelType
.
PUSH
])])
patch_policies
(
self
,
[
StubPolicy
([
ChannelType
.
PUSH
])])
...
@@ -241,7 +279,10 @@ class TestUpgradeReminder(CacheIsolationTestCase):
...
@@ -241,7 +279,10 @@ class TestUpgradeReminder(CacheIsolationTestCase):
with
patch
.
object
(
tasks
,
'_upgrade_reminder_schedule_send'
)
as
mock_schedule_send
:
with
patch
.
object
(
tasks
,
'_upgrade_reminder_schedule_send'
)
as
mock_schedule_send
:
mock_schedule_send
.
apply_async
=
lambda
args
,
*
_a
,
**
_kw
:
sent_messages
.
append
(
args
)
mock_schedule_send
.
apply_async
=
lambda
args
,
*
_a
,
**
_kw
:
sent_messages
.
append
(
args
)
with
self
.
assertNumQueries
(
3
):
# we execute one query per course to see if it's opted out of dynamic upgrade deadlines, however,
# since we create a new course for each schedule in this test, we expect there to be one per message
num_expected_queries
=
NUM_QUERIES_WITH_MATCHES
+
NUM_QUERIES_WITH_DEADLINE
+
message_count
with
self
.
assertNumQueries
(
num_expected_queries
,
table_blacklist
=
WAFFLE_TABLES
):
tasks
.
upgrade_reminder_schedule_bin
(
tasks
.
upgrade_reminder_schedule_bin
(
self
.
site_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
day
,
self
.
site_config
.
site
.
id
,
target_day_str
=
test_time_str
,
day_offset
=
day
,
bin_num
=
user
.
id
%
tasks
.
UPGRADE_REMINDER_NUM_BINS
,
bin_num
=
user
.
id
%
tasks
.
UPGRADE_REMINDER_NUM_BINS
,
...
...
openedx/core/djangoapps/schedules/signals.py
View file @
40d3f4f2
...
@@ -112,7 +112,7 @@ def _get_upgrade_deadline_delta_setting(course_id):
...
@@ -112,7 +112,7 @@ def _get_upgrade_deadline_delta_setting(course_id):
# Check if the course has a deadline
# Check if the course has a deadline
course_config
=
CourseDynamicUpgradeDeadlineConfiguration
.
current
(
course_id
)
course_config
=
CourseDynamicUpgradeDeadlineConfiguration
.
current
(
course_id
)
if
course_config
.
enabled
:
if
course_config
.
enabled
and
not
course_config
.
opt_out
:
delta
=
course_config
.
deadline_days
delta
=
course_config
.
deadline_days
return
delta
return
delta
openedx/core/djangoapps/schedules/tasks.py
View file @
40d3f4f2
...
@@ -12,13 +12,15 @@ from django.core.urlresolvers import reverse
...
@@ -12,13 +12,15 @@ from django.core.urlresolvers import reverse
from
django.db.models
import
F
,
Min
from
django.db.models
import
F
,
Min
from
django.db.utils
import
DatabaseError
from
django.db.utils
import
DatabaseError
from
django.utils.formats
import
dateformat
,
get_format
from
django.utils.formats
import
dateformat
,
get_format
import
pytz
from
edx_ace
import
ace
from
edx_ace
import
ace
from
edx_ace.message
import
Message
from
edx_ace.message
import
Message
from
edx_ace.recipient
import
Recipient
from
edx_ace.recipient
import
Recipient
from
edx_ace.utils.date
import
deserialize
from
edx_ace.utils.date
import
deserialize
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
lms.djangoapps.experiments.utils
import
check_and_get_upgrade_link_and_date
from
courseware.date_summary
import
verified_upgrade_deadline_link
,
verified_upgrade_link_is_valid
from
edxmako.shortcuts
import
marketing_link
from
edxmako.shortcuts
import
marketing_link
from
openedx.core.djangoapps.schedules.message_type
import
ScheduleMessageType
from
openedx.core.djangoapps.schedules.message_type
import
ScheduleMessageType
...
@@ -134,7 +136,7 @@ def _recurring_nudge_schedules_for_hour(site, target_hour, org_list, exclude_org
...
@@ -134,7 +136,7 @@ def _recurring_nudge_schedules_for_hour(site, target_hour, org_list, exclude_org
}
}
# Information for including upsell messaging in template.
# Information for including upsell messaging in template.
_add_upsell_button_
to_email_template
(
user
,
first_schedule
,
template_context
)
_add_upsell_button_
information_to_template_context
(
user
,
first_schedule
,
template_context
)
yield
(
user
,
first_schedule
.
enrollment
.
course
.
language
,
template_context
)
yield
(
user
,
first_schedule
.
enrollment
.
course
.
language
,
template_context
)
...
@@ -178,27 +180,6 @@ def _gather_users_and_schedules_for_target_hour(target_hour, org_list, exclude_o
...
@@ -178,27 +180,6 @@ def _gather_users_and_schedules_for_target_hour(target_hour, org_list, exclude_o
return
users
,
schedules
return
users
,
schedules
def
_add_upsell_button_to_email_template
(
a_user
,
a_schedule
,
template_context
):
# Check and upgrade link performs a query on CourseMode, which is triggering failures in
# test_send_recurring_nudge.py
upgrade_link
,
upgrade_date
=
check_and_get_upgrade_link_and_date
(
a_user
,
a_schedule
.
enrollment
)
has_dynamic_deadline
=
a_schedule
.
upgrade_deadline
is
not
None
has_upgrade_link
=
upgrade_link
is
not
None
show_upsell
=
has_dynamic_deadline
and
has_upgrade_link
template_context
[
'show_upsell'
]
=
show_upsell
if
show_upsell
:
template_context
[
'upsell_link'
]
=
upgrade_link
template_context
[
'user_schedule_upgrade_deadline_time'
]
=
dateformat
.
format
(
upgrade_date
,
get_format
(
'DATE_FORMAT'
,
lang
=
a_schedule
.
enrollment
.
course
.
language
,
use_l10n
=
True
)
)
@task
(
ignore_result
=
True
,
routing_key
=
ROUTING_KEY
)
@task
(
ignore_result
=
True
,
routing_key
=
ROUTING_KEY
)
def
recurring_nudge_schedule_bin
(
def
recurring_nudge_schedule_bin
(
site_id
,
target_day_str
,
day_offset
,
bin_num
,
org_list
,
exclude_orgs
=
False
,
override_recipient_email
=
None
,
site_id
,
target_day_str
,
day_offset
,
bin_num
,
org_list
,
exclude_orgs
=
False
,
override_recipient_email
=
None
,
...
@@ -254,7 +235,7 @@ def _recurring_nudge_schedules_for_bin(site, target_day, bin_num, org_list, excl
...
@@ -254,7 +235,7 @@ def _recurring_nudge_schedules_for_bin(site, target_day, bin_num, org_list, excl
})
})
# Information for including upsell messaging in template.
# Information for including upsell messaging in template.
_add_upsell_button_
to_email_template
(
user
,
first_schedule
,
template_context
)
_add_upsell_button_
information_to_template_context
(
user
,
first_schedule
,
template_context
)
yield
(
user
,
first_schedule
.
enrollment
.
course
.
language
,
template_context
)
yield
(
user
,
first_schedule
.
enrollment
.
course
.
language
,
template_context
)
...
@@ -335,7 +316,7 @@ def _upgrade_reminder_schedules_for_bin(site, target_day, bin_num, org_list, exc
...
@@ -335,7 +316,7 @@ def _upgrade_reminder_schedules_for_bin(site, target_day, bin_num, org_list, exc
'cert_image'
:
absolute_url
(
site
,
static
(
'course_experience/images/verified-cert.png'
)),
'cert_image'
:
absolute_url
(
site
,
static
(
'course_experience/images/verified-cert.png'
)),
})
})
_add_upsell_button_
to_email_template
(
user
,
first_schedule
,
template_context
)
_add_upsell_button_
information_to_template_context
(
user
,
first_schedule
,
template_context
)
yield
(
user
,
first_schedule
.
enrollment
.
course
.
language
,
template_context
)
yield
(
user
,
first_schedule
.
enrollment
.
course
.
language
,
template_context
)
...
@@ -393,3 +374,32 @@ def get_schedules_with_target_date_by_bin_and_orgs(schedule_date_field, target_d
...
@@ -393,3 +374,32 @@ def get_schedules_with_target_date_by_bin_and_orgs(schedule_date_field, target_d
schedules
=
schedules
.
using
(
"read_replica"
)
schedules
=
schedules
.
using
(
"read_replica"
)
return
schedules
return
schedules
def
_add_upsell_button_information_to_template_context
(
user
,
schedule
,
template_context
):
enrollment
=
schedule
.
enrollment
course
=
enrollment
.
course
verified_upgrade_link
=
_get_link_to_purchase_verified_certificate
(
user
,
schedule
)
has_verified_upgrade_link
=
verified_upgrade_link
is
not
None
if
has_verified_upgrade_link
:
template_context
[
'upsell_link'
]
=
verified_upgrade_link
template_context
[
'user_schedule_upgrade_deadline_time'
]
=
dateformat
.
format
(
enrollment
.
dynamic_upgrade_deadline
,
get_format
(
'DATE_FORMAT'
,
lang
=
course
.
language
,
use_l10n
=
True
)
)
template_context
[
'show_upsell'
]
=
has_verified_upgrade_link
def
_get_link_to_purchase_verified_certificate
(
a_user
,
a_schedule
):
enrollment
=
a_schedule
.
enrollment
if
enrollment
.
dynamic_upgrade_deadline
is
None
or
not
verified_upgrade_link_is_valid
(
enrollment
):
return
None
return
verified_upgrade_deadline_link
(
a_user
,
enrollment
.
course
)
openedx/features/course_experience/tests/views/test_course_updates.py
View file @
40d3f4f2
...
@@ -126,7 +126,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
...
@@ -126,7 +126,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
course_updates_url
(
self
.
course
)
course_updates_url
(
self
.
course
)
# Fetch the view and verify that the query counts haven't changed
# Fetch the view and verify that the query counts haven't changed
with
self
.
assertNumQueries
(
3
2
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
):
with
self
.
assertNumQueries
(
3
0
,
table_blacklist
=
QUERY_COUNT_TABLE_BLACKLIST
):
with
check_mongo_calls
(
4
):
with
check_mongo_calls
(
4
):
url
=
course_updates_url
(
self
.
course
)
url
=
course_updates_url
(
self
.
course
)
self
.
client
.
get
(
url
)
self
.
client
.
get
(
url
)
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