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
7fd643fa
Unverified
Commit
7fd643fa
authored
Oct 30, 2017
by
Gabe Mulley
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add tests for experience types, ensure courses have a verified mode
parent
57bf89c8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
162 additions
and
125 deletions
+162
-125
common/djangoapps/student/models.py
+15
-9
common/djangoapps/student/tests/factories.py
+29
-3
common/djangoapps/student/tests/test_certificates.py
+2
-0
lms/djangoapps/certificates/tests/test_webview_views.py
+5
-1
lms/djangoapps/courseware/date_summary.py
+9
-6
lms/djangoapps/courseware/tests/test_view_authentication.py
+1
-0
lms/djangoapps/courseware/tests/test_views.py
+6
-1
lms/djangoapps/discussion/tests/test_views.py
+4
-4
openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py
+0
-0
openedx/core/djangoapps/schedules/management/commands/tests/test_experiences.py
+0
-59
openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py
+17
-2
openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py
+16
-1
openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py
+26
-11
openedx/core/djangoapps/schedules/management/commands/tests/upsell_base.py
+2
-5
openedx/core/djangoapps/schedules/models.py
+6
-7
openedx/core/djangoapps/schedules/resolvers.py
+7
-6
openedx/core/djangoapps/schedules/signals.py
+3
-3
openedx/core/djangoapps/schedules/tests/factories.py
+1
-1
openedx/core/djangoapps/schedules/tests/test_signals.py
+13
-6
No files found.
common/djangoapps/student/models.py
View file @
7fd643fa
...
...
@@ -1686,9 +1686,13 @@ class CourseEnrollment(models.Model):
"""
if
not
self
.
_course_overview
:
try
:
self
.
_course_overview
=
CourseOverview
.
get_from_id
(
self
.
course_id
)
except
(
CourseOverview
.
DoesNotExist
,
IOError
):
self
.
_course_overview
=
None
self
.
_course_overview
=
self
.
course
except
CourseOverview
.
DoesNotExist
:
log
.
info
(
'Course Overviews: unable to find course overview for enrollment, loading from modulestore.'
)
try
:
self
.
_course_overview
=
CourseOverview
.
get_from_id
(
self
.
course_id
)
except
(
CourseOverview
.
DoesNotExist
,
IOError
):
self
.
_course_overview
=
None
return
self
.
_course_overview
@cached_property
...
...
@@ -1717,7 +1721,14 @@ class CourseEnrollment(models.Model):
# When course modes expire they aren't found any more and None would be returned.
# Replicate that behavior here by returning None if the personalized deadline is in the past.
if
datetime
.
now
(
UTC
)
>=
self
.
dynamic_upgrade_deadline
:
log
.
debug
(
'Schedules: Returning None since dynamic upgrade deadline has already passed.'
)
return
None
if
self
.
verified_mode
is
None
:
log
.
debug
(
'Schedules: Returning None for dynamic upgrade deadline since the course does not have a '
'verified mode.'
)
return
None
return
self
.
dynamic_upgrade_deadline
return
self
.
course_upgrade_deadline
...
...
@@ -1733,12 +1744,7 @@ class CourseEnrollment(models.Model):
Returns:
datetime|None
"""
try
:
course_overview
=
self
.
course
except
CourseOverview
.
DoesNotExist
:
course_overview
=
self
.
course_overview
if
not
course_overview
.
self_paced
:
if
not
self
.
course_overview
.
self_paced
:
return
None
if
not
DynamicUpgradeDeadlineConfiguration
.
is_enabled
():
...
...
common/djangoapps/student/tests/factories.py
View file @
7fd643fa
...
...
@@ -12,6 +12,8 @@ from opaque_keys.edx.keys import CourseKey
from
pytz
import
UTC
from
course_modes.models
import
CourseMode
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.content.course_overviews.tests.factories
import
CourseOverviewFactory
from
student.models
import
(
CourseAccessRole
,
CourseEnrollment
,
...
...
@@ -126,9 +128,33 @@ class CourseEnrollmentFactory(DjangoModelFactory):
model
=
CourseEnrollment
user
=
factory
.
SubFactory
(
UserFactory
)
course
=
factory
.
SubFactory
(
'openedx.core.djangoapps.content.course_overviews.tests.factories.CourseOverviewFactory'
,
)
@classmethod
def
_create
(
cls
,
model_class
,
*
args
,
**
kwargs
):
manager
=
cls
.
_get_manager
(
model_class
)
course_kwargs
=
{}
for
key
in
kwargs
.
keys
():
if
key
.
startswith
(
'course__'
):
course_kwargs
[
key
.
split
(
'__'
)[
1
]]
=
kwargs
.
pop
(
key
)
if
'course'
not
in
kwargs
:
course_id
=
kwargs
.
get
(
'course_id'
)
course_overview
=
None
if
course_id
is
not
None
:
if
isinstance
(
course_id
,
basestring
):
course_id
=
CourseKey
.
from_string
(
course_id
)
course_kwargs
.
setdefault
(
'id'
,
course_id
)
try
:
course_overview
=
CourseOverview
.
get_from_id
(
course_id
)
except
CourseOverview
.
DoesNotExist
:
pass
if
course_overview
is
None
:
course_overview
=
CourseOverviewFactory
(
**
course_kwargs
)
kwargs
[
'course'
]
=
course_overview
return
manager
.
create
(
*
args
,
**
kwargs
)
class
CourseAccessRoleFactory
(
DjangoModelFactory
):
...
...
common/djangoapps/student/tests/test_certificates.py
View file @
7fd643fa
...
...
@@ -111,6 +111,8 @@ class CertificateDashboardMessageDisplayTest(CertificateDisplayTestBase):
Tests the certificates messages for a course in the dashboard.
"""
ENABLED_SIGNALS
=
[
'course_published'
]
@classmethod
def
setUpClass
(
cls
):
super
(
CertificateDashboardMessageDisplayTest
,
cls
)
.
setUpClass
()
...
...
lms/djangoapps/certificates/tests/test_webview_views.py
View file @
7fd643fa
...
...
@@ -12,8 +12,9 @@ from django.conf import settings
from
django.core.urlresolvers
import
reverse
from
django.test.client
import
Client
,
RequestFactory
from
django.test.utils
import
override_settings
from
util.date_utils
import
strftime_localized
from
mock
import
Mock
,
patch
from
mock
import
patch
from
nose.plugins.attrib
import
attr
from
certificates.api
import
get_certificate_url
...
...
@@ -73,6 +74,9 @@ class CommonCertificatesTestCase(ModuleStoreTestCase):
"""
Common setUp and utility methods for Certificate tests
"""
ENABLED_SIGNALS
=
[
'course_published'
]
def
setUp
(
self
):
super
(
CommonCertificatesTestCase
,
self
)
.
setUp
()
self
.
client
=
Client
()
...
...
lms/djangoapps/courseware/date_summary.py
View file @
7fd643fa
...
...
@@ -405,16 +405,19 @@ def verified_upgrade_deadline_link(user, course=None, course_id=None):
ecommerce_service
=
EcommerceService
()
if
ecommerce_service
.
is_enabled
(
user
):
if
course
is
not
None
and
isinstance
(
course
,
CourseOverview
):
course_mode
=
course
.
modes
.
get
(
mode_slug
=
CourseMode
.
VERIFIED
)
course_mode
=
CourseMode
.
verified_mode_for_course
(
course_id
)
if
course_mode
is
not
None
:
return
ecommerce_service
.
get_checkout_page_url
(
course_mode
.
sku
)
else
:
course_mode
=
CourseMode
.
objects
.
get
(
course_id
=
course_id
,
mode_slug
=
CourseMode
.
VERIFIED
)
return
ecommerce_service
.
get_checkout_page_url
(
course_mode
.
sku
)
raise
CourseModeNotFoundException
(
'Cannot generate a verified upgrade link without a valid verified mode'
' for course {}'
.
format
(
unicode
(
course_id
)))
return
reverse
(
'verify_student_upgrade_and_verify'
,
args
=
(
course_id
,))
class
CourseModeNotFoundException
(
Exception
):
pass
def
verified_upgrade_link_is_valid
(
enrollment
=
None
):
"""
Return whether this enrollment can be upgraded.
...
...
lms/djangoapps/courseware/tests/test_view_authentication.py
View file @
7fd643fa
...
...
@@ -29,6 +29,7 @@ class TestViewAuth(EnterpriseTestConsentRequired, ModuleStoreTestCase, LoginEnro
"""
ACCOUNT_INFO
=
[(
'view@test.com'
,
'foo'
),
(
'view2@test.com'
,
'foo'
)]
ENABLED_SIGNALS
=
[
'course_published'
]
@staticmethod
def
_reverse_urls
(
names
,
course
):
...
...
lms/djangoapps/courseware/tests/test_views.py
View file @
7fd643fa
...
...
@@ -807,7 +807,12 @@ class ViewsTestCase(ModuleStoreTestCase):
CourseModeFactory
.
create
(
mode_slug
=
CourseMode
.
VERIFIED
,
course_id
=
course
)
# Enroll user in the course
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
,
user
=
self
.
user
,
mode
=
CourseMode
.
AUDIT
)
# Don't use the CourseEnrollmentFactory since it ensures a CourseOverview is available
enrollment
=
CourseEnrollment
.
objects
.
create
(
course_id
=
course
,
user
=
self
.
user
,
mode
=
CourseMode
.
AUDIT
,
)
self
.
assertEqual
(
enrollment
.
course_overview
,
None
)
...
...
lms/djangoapps/discussion/tests/test_views.py
View file @
7fd643fa
...
...
@@ -403,15 +403,15 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
# course is outside the context manager that is verifying the number of queries,
# and with split mongo, that method ends up querying disabled_xblocks (which is then
# cached and hence not queried as part of call_single_thread).
(
ModuleStoreEnum
.
Type
.
mongo
,
False
,
1
,
6
,
4
,
1
7
,
4
),
(
ModuleStoreEnum
.
Type
.
mongo
,
False
,
50
,
6
,
4
,
1
7
,
4
),
(
ModuleStoreEnum
.
Type
.
mongo
,
False
,
1
,
6
,
4
,
1
6
,
4
),
(
ModuleStoreEnum
.
Type
.
mongo
,
False
,
50
,
6
,
4
,
1
6
,
4
),
# split mongo: 3 queries, regardless of thread response size.
(
ModuleStoreEnum
.
Type
.
split
,
False
,
1
,
3
,
3
,
16
,
4
),
(
ModuleStoreEnum
.
Type
.
split
,
False
,
50
,
3
,
3
,
16
,
4
),
# Enabling Enterprise integration should have no effect on the number of mongo queries made.
(
ModuleStoreEnum
.
Type
.
mongo
,
True
,
1
,
6
,
4
,
1
7
,
4
),
(
ModuleStoreEnum
.
Type
.
mongo
,
True
,
50
,
6
,
4
,
1
7
,
4
),
(
ModuleStoreEnum
.
Type
.
mongo
,
True
,
1
,
6
,
4
,
1
6
,
4
),
(
ModuleStoreEnum
.
Type
.
mongo
,
True
,
50
,
6
,
4
,
1
6
,
4
),
# split mongo: 3 queries, regardless of thread response size.
(
ModuleStoreEnum
.
Type
.
split
,
True
,
1
,
3
,
3
,
16
,
4
),
(
ModuleStoreEnum
.
Type
.
split
,
True
,
50
,
3
,
3
,
16
,
4
),
...
...
openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py
View file @
7fd643fa
This diff is collapsed.
Click to expand it.
openedx/core/djangoapps/schedules/management/commands/tests/test_experiences.py
deleted
100644 → 0
View file @
57bf89c8
from
collections
import
namedtuple
import
datetime
import
ddt
import
pytz
from
edx_ace.utils.date
import
serialize
from
freezegun
import
freeze_time
from
mock
import
patch
from
courseware.models
import
DynamicUpgradeDeadlineConfiguration
from
openedx.core.djangoapps.schedules
import
tasks
from
openedx.core.djangoapps.schedules.models
import
ScheduleExperience
from
openedx.core.djangoapps.schedules.resolvers
import
_get_datetime_beginning_of_day
from
openedx.core.djangoapps.schedules.tests.factories
import
ScheduleFactory
,
ScheduleConfigFactory
from
openedx.core.djangoapps.site_configuration.tests.factories
import
SiteFactory
,
SiteConfigurationFactory
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
,
FilteredQueryCountMixin
,
CacheIsolationTestCase
@ddt.ddt
@skip_unless_lms
@freeze_time
(
'2017-08-01 00:00:00'
,
tz_offset
=
0
,
tick
=
True
)
class
TestExperiences
(
FilteredQueryCountMixin
,
CacheIsolationTestCase
):
ENABLED_CACHES
=
[
'default'
]
ExperienceTest
=
namedtuple
(
'ExperienceTest'
,
'experience offset email_sent'
)
def
setUp
(
self
):
super
(
TestExperiences
,
self
)
.
setUp
()
site
=
SiteFactory
.
create
()
self
.
site_config
=
SiteConfigurationFactory
.
create
(
site
=
site
)
ScheduleConfigFactory
.
create
(
site
=
self
.
site_config
.
site
)
DynamicUpgradeDeadlineConfiguration
.
objects
.
create
(
enabled
=
True
)
@ddt.data
(
ExperienceTest
(
experience
=
ScheduleExperience
.
DEFAULT
,
offset
=-
3
,
email_sent
=
True
),
ExperienceTest
(
experience
=
ScheduleExperience
.
DEFAULT
,
offset
=-
10
,
email_sent
=
True
),
ExperienceTest
(
experience
=
ScheduleExperience
.
COURSE_UPDATES
,
offset
=-
3
,
email_sent
=
True
),
ExperienceTest
(
experience
=
ScheduleExperience
.
COURSE_UPDATES
,
offset
=-
10
,
email_sent
=
False
),
)
@patch.object
(
tasks
,
'ace'
)
def
test_experience_type_exclusion
(
self
,
test_config
,
mock_ace
):
current_day
=
_get_datetime_beginning_of_day
(
datetime
.
datetime
.
now
(
pytz
.
UTC
))
target_day
=
current_day
+
datetime
.
timedelta
(
days
=
test_config
.
offset
)
schedule
=
ScheduleFactory
.
create
(
start
=
target_day
,
enrollment__course__self_paced
=
True
,
experience__experience_type
=
test_config
.
experience
,
)
tasks
.
ScheduleRecurringNudge
.
apply
(
kwargs
=
dict
(
site_id
=
self
.
site_config
.
site
.
id
,
target_day_str
=
serialize
(
target_day
),
day_offset
=
test_config
.
offset
,
bin_num
=
(
schedule
.
enrollment
.
user
.
id
%
tasks
.
ScheduleRecurringNudge
.
num_bins
),
))
self
.
assertEqual
(
mock_ace
.
send
.
called
,
test_config
.
email_sent
)
openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py
View file @
7fd643fa
import
ddt
from
mock
import
patch
from
unittest
import
skipUnless
...
...
@@ -5,11 +6,16 @@ from django.conf import settings
from
openedx.core.djangoapps.schedules
import
resolvers
,
tasks
from
openedx.core.djangoapps.schedules.management.commands
import
send_course_update
as
nudge
from
openedx.core.djangoapps.schedules.management.commands.tests.send_email_base
import
ScheduleSendEmailTestBase
from
openedx.core.djangoapps.schedules.management.commands.tests.send_email_base
import
(
ScheduleSendEmailTestBase
,
ExperienceTest
)
from
openedx.core.djangoapps.schedules.management.commands.tests.upsell_base
import
ScheduleUpsellTestMixin
from
openedx.core.djangoapps.schedules.models
import
ScheduleExperience
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
@ddt.ddt
@skip_unless_lms
@skipUnless
(
'openedx.core.djangoapps.schedules.apps.SchedulesConfig'
in
settings
.
INSTALLED_APPS
,
...
...
@@ -25,7 +31,8 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase):
command
=
nudge
.
Command
deliver_config
=
'deliver_course_update'
enqueue_config
=
'enqueue_course_update'
expected_offsets
=
xrange
(
-
7
,
-
77
,
-
7
)
expected_offsets
=
range
(
-
7
,
-
77
,
-
7
)
experience_type
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
queries_deadline_for_each_course
=
True
...
...
@@ -35,3 +42,11 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase):
mock_highlights
=
patcher
.
start
()
mock_highlights
.
return_value
=
[
'Highlight {}'
.
format
(
num
+
1
)
for
num
in
range
(
3
)]
self
.
addCleanup
(
patcher
.
stop
)
@ddt.data
(
ExperienceTest
(
experience
=
ScheduleExperience
.
EXPERIENCES
.
default
,
offset
=
expected_offsets
[
0
],
email_sent
=
False
),
ExperienceTest
(
experience
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
,
offset
=
expected_offsets
[
0
],
email_sent
=
True
),
ExperienceTest
(
experience
=
None
,
offset
=
expected_offsets
[
0
],
email_sent
=
False
),
)
def
test_schedule_in_different_experience
(
self
,
test_config
):
self
.
_check_if_email_sent_for_experience
(
test_config
)
openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py
View file @
7fd643fa
from
unittest
import
skipUnless
import
ddt
from
django.conf
import
settings
from
openedx.core.djangoapps.schedules
import
resolvers
,
tasks
from
openedx.core.djangoapps.schedules.management.commands
import
send_recurring_nudge
as
nudge
from
openedx.core.djangoapps.schedules.management.commands.tests.send_email_base
import
ScheduleSendEmailTestBase
from
openedx.core.djangoapps.schedules.management.commands.tests.send_email_base
import
ScheduleSendEmailTestBase
,
\
ExperienceTest
from
openedx.core.djangoapps.schedules.management.commands.tests.upsell_base
import
ScheduleUpsellTestMixin
from
openedx.core.djangoapps.schedules.models
import
ScheduleExperience
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
@ddt.ddt
@skip_unless_lms
@skipUnless
(
'openedx.core.djangoapps.schedules.apps.SchedulesConfig'
in
settings
.
INSTALLED_APPS
,
...
...
@@ -27,3 +31,14 @@ class TestSendRecurringNudge(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase)
expected_offsets
=
(
-
3
,
-
10
)
consolidates_emails_for_learner
=
True
@ddt.data
(
ExperienceTest
(
experience
=
ScheduleExperience
.
EXPERIENCES
.
default
,
offset
=-
3
,
email_sent
=
True
),
ExperienceTest
(
experience
=
ScheduleExperience
.
EXPERIENCES
.
default
,
offset
=-
10
,
email_sent
=
True
),
ExperienceTest
(
experience
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
,
offset
=-
3
,
email_sent
=
True
),
ExperienceTest
(
experience
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
,
offset
=-
10
,
email_sent
=
False
),
ExperienceTest
(
experience
=
None
,
offset
=-
3
,
email_sent
=
True
),
ExperienceTest
(
experience
=
None
,
offset
=-
10
,
email_sent
=
True
),
)
def
test_nudge_experience
(
self
,
test_config
):
self
.
_check_if_email_sent_for_experience
(
test_config
)
openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py
View file @
7fd643fa
...
...
@@ -11,8 +11,9 @@ from opaque_keys.edx.locator import CourseLocator
from
course_modes.models
import
CourseMode
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.tests.send_email_base
import
ScheduleSendEmailTestBase
from
openedx.core.djangoapps.schedules.tests.factories
import
ScheduleFactory
from
openedx.core.djangoapps.schedules.management.commands.tests.send_email_base
import
ScheduleSendEmailTestBase
,
\
ExperienceTest
from
openedx.core.djangoapps.schedules.models
import
ScheduleExperience
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
from
student.tests.factories
import
UserFactory
...
...
@@ -41,18 +42,14 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
@ddt.data
(
True
,
False
)
@patch.object
(
tasks
,
'ace'
)
def
test_verified_learner
(
self
,
is_verified
,
mock_ace
):
user
=
UserFactory
.
create
(
id
=
self
.
task
.
num_bins
)
current_day
,
offset
,
target_day
,
upgrade_deadline
=
self
.
_get_dates
()
ScheduleFactory
.
create
(
upgrade_deadline
=
upgrade_deadline
,
enrollment__course__self_paced
=
True
,
enrollment__user
=
user
,
schedule
=
self
.
_schedule_factory
(
enrollment__mode
=
CourseMode
.
VERIFIED
if
is_verified
else
CourseMode
.
AUDIT
,
)
self
.
task
.
apply
(
kwargs
=
dict
(
site_id
=
self
.
site_config
.
site
.
id
,
target_day_str
=
serialize
(
target_day
),
day_offset
=
offset
,
bin_num
=
self
.
_calculate_bin_for_user
(
user
),
bin_num
=
self
.
_calculate_bin_for_user
(
schedule
.
enrollment
.
user
),
))
self
.
assertEqual
(
mock_ace
.
send
.
called
,
not
is_verified
)
...
...
@@ -62,10 +59,8 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
user
=
UserFactory
.
create
()
schedules
=
[
ScheduleFactory
.
create
(
upgrade_deadline
=
upgrade_deadline
,
self
.
_schedule_factory
(
enrollment__user
=
user
,
enrollment__course__self_paced
=
True
,
enrollment__course__id
=
CourseLocator
(
'edX'
,
'toy'
,
'Course{}'
.
format
(
i
)),
enrollment__mode
=
CourseMode
.
VERIFIED
if
i
in
(
0
,
3
)
else
CourseMode
.
AUDIT
,
)
...
...
@@ -88,3 +83,23 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
message
.
context
[
'course_ids'
],
[
str
(
schedules
[
i
]
.
enrollment
.
course
.
id
)
for
i
in
(
1
,
2
,
4
)]
)
@patch.object
(
tasks
,
'ace'
)
def
test_course_without_verified_mode
(
self
,
mock_ace
):
current_day
,
offset
,
target_day
,
upgrade_deadline
=
self
.
_get_dates
()
schedule
=
self
.
_schedule_factory
()
schedule
.
enrollment
.
course
.
modes
.
filter
(
mode_slug
=
CourseMode
.
VERIFIED
)
.
delete
()
self
.
task
.
apply
(
kwargs
=
dict
(
site_id
=
self
.
site_config
.
site
.
id
,
target_day_str
=
serialize
(
target_day
),
day_offset
=
offset
,
bin_num
=
self
.
_calculate_bin_for_user
(
schedule
.
enrollment
.
user
),
))
self
.
assertEqual
(
mock_ace
.
send
.
called
,
False
)
@ddt.data
(
ExperienceTest
(
experience
=
ScheduleExperience
.
EXPERIENCES
.
default
,
offset
=
expected_offsets
[
0
],
email_sent
=
True
),
ExperienceTest
(
experience
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
,
offset
=
expected_offsets
[
0
],
email_sent
=
False
),
ExperienceTest
(
experience
=
None
,
offset
=
expected_offsets
[
0
],
email_sent
=
True
),
)
def
test_upgrade_reminder_experience
(
self
,
test_config
):
self
.
_check_if_email_sent_for_experience
(
test_config
)
openedx/core/djangoapps/schedules/management/commands/tests/upsell_base.py
View file @
7fd643fa
...
...
@@ -8,7 +8,6 @@ from edx_ace.utils.date import serialize
from
edx_ace.message
import
Message
from
courseware.models
import
DynamicUpgradeDeadlineConfiguration
from
openedx.core.djangoapps.schedules.tests.factories
import
ScheduleFactory
@ddt.ddt
...
...
@@ -34,10 +33,8 @@ class ScheduleUpsellTestMixin(object):
if
testcase
.
set_deadline
:
upgrade_deadline
=
current_day
+
datetime
.
timedelta
(
days
=
testcase
.
deadline_offset
)
schedule
=
ScheduleFactory
.
create
(
start
=
target_day
,
upgrade_deadline
=
upgrade_deadline
,
enrollment__course__self_paced
=
True
,
schedule
=
self
.
_schedule_factory
(
upgrade_deadline
=
upgrade_deadline
)
sent_messages
=
[]
...
...
openedx/core/djangoapps/schedules/models.py
View file @
7fd643fa
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.contrib.sites.models
import
Site
from
model_utils
import
Choices
from
model_utils.models
import
TimeStampedModel
from
config_models.models
import
ConfigurationModel
...
...
@@ -27,7 +28,7 @@ class Schedule(TimeStampedModel):
try
:
return
self
.
experience
.
experience_type
except
ScheduleExperience
.
DoesNotExist
:
return
ScheduleExperience
.
DEFAULT
return
ScheduleExperience
.
EXPERIENCES
.
default
class
Meta
(
object
):
verbose_name
=
_
(
'Schedule'
)
...
...
@@ -48,12 +49,10 @@ class ScheduleConfig(ConfigurationModel):
class
ScheduleExperience
(
models
.
Model
):
DEFAULT
=
0
COURSE_UPDATES
=
1
EXPERIENCES
=
(
(
DEFAULT
,
'Recurring Nudge and Upgrade Reminder'
),
(
COURSE_UPDATES
,
'Course Updates'
)
EXPERIENCES
=
Choices
(
(
0
,
'default'
,
'Recurring Nudge and Upgrade Reminder'
),
(
1
,
'course_updates'
,
'Course Updates'
)
)
schedule
=
models
.
OneToOneField
(
Schedule
,
related_name
=
'experience'
)
experience_type
=
models
.
PositiveSmallIntegerField
(
choices
=
EXPERIENCES
,
default
=
DEFAULT
)
experience_type
=
models
.
PositiveSmallIntegerField
(
choices
=
EXPERIENCES
,
default
=
EXPERIENCES
.
default
)
openedx/core/djangoapps/schedules/resolvers.py
View file @
7fd643fa
...
...
@@ -77,7 +77,8 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
schedule_date_field
=
None
num_bins
=
DEFAULT_NUM_BINS
experience_filter
=
Q
(
experience__experience_type
=
ScheduleExperience
.
DEFAULT
)
|
Q
(
experience__isnull
=
True
)
experience_filter
=
(
Q
(
experience__experience_type
=
ScheduleExperience
.
EXPERIENCES
.
default
)
|
Q
(
experience__isnull
=
True
))
def
__attrs_post_init__
(
self
):
# TODO: in the next refactor of this task, pass in current_datetime instead of reproducing it here
...
...
@@ -126,8 +127,6 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
schedules
=
Schedule
.
objects
.
select_related
(
'enrollment__user__profile'
,
'enrollment__course'
,
)
.
prefetch_related
(
'enrollment__course__modes'
,
)
.
filter
(
Q
(
enrollment__course__end__isnull
=
True
)
|
Q
(
enrollment__course__end__gte
=
self
.
current_datetime
),
...
...
@@ -148,6 +147,8 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
# This will run the query and cache all of the results in memory.
num_schedules
=
len
(
schedules
)
LOG
.
debug
(
'Number of schedules =
%
d'
,
num_schedules
)
# This should give us a sense of the volume of data being processed by each task.
set_custom_metric
(
'num_schedules'
,
num_schedules
)
...
...
@@ -240,10 +241,10 @@ class RecurringNudgeResolver(BinnedSchedulesBaseResolver):
@property
def
experience_filter
(
self
):
if
self
.
day_offset
==
-
3
:
experiences
=
[
ScheduleExperience
.
DEFAULT
,
ScheduleExperience
.
COURSE_UPDATES
]
experiences
=
[
ScheduleExperience
.
EXPERIENCES
.
default
,
ScheduleExperience
.
EXPERIENCES
.
course_updates
]
return
Q
(
experience__experience_type__in
=
experiences
)
|
Q
(
experience__isnull
=
True
)
else
:
return
Q
(
experience__experience_type
=
ScheduleExperience
.
DEFAULT
)
|
Q
(
experience__isnull
=
True
)
return
Q
(
experience__experience_type
=
ScheduleExperience
.
EXPERIENCES
.
default
)
|
Q
(
experience__isnull
=
True
)
def
get_template_context
(
self
,
user
,
user_schedules
):
first_schedule
=
user_schedules
[
0
]
...
...
@@ -346,7 +347,7 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver):
log_prefix
=
'Course Update'
schedule_date_field
=
'start'
num_bins
=
COURSE_UPDATE_NUM_BINS
experience_filter
=
Q
(
experience__experience_type
=
ScheduleExperience
.
COURSE_UPDATES
)
experience_filter
=
Q
(
experience__experience_type
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
)
def
schedules_for_bin
(
self
):
week_num
=
abs
(
self
.
day_offset
)
/
7
...
...
openedx/core/djangoapps/schedules/signals.py
View file @
7fd643fa
...
...
@@ -64,14 +64,14 @@ def create_schedule(sender, **kwargs):
try
:
get_week_highlights
(
enrollment
.
course_id
,
1
)
experience_type
=
ScheduleExperience
.
COURSE_UPDATES
experience_type
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
except
CourseUpdateDoesNotExist
:
experience_type
=
ScheduleExperience
.
DEFAULT
experience_type
=
ScheduleExperience
.
EXPERIENCES
.
default
ScheduleExperience
(
schedule
=
schedule
,
experience_type
=
experience_type
)
.
save
()
log
.
debug
(
'Schedules: created a new schedule starting at
%
s with an upgrade deadline of
%
s and experience type:
%
s'
,
content_availability_date
,
upgrade_deadline
,
ScheduleExperience
.
EXPERIENCES
[
experience_type
]
[
1
]
)
content_availability_date
,
upgrade_deadline
,
ScheduleExperience
.
EXPERIENCES
[
experience_type
])
@receiver
(
COURSE_START_DATE_CHANGED
,
dispatch_uid
=
"update_schedules_on_course_start_changed"
)
...
...
openedx/core/djangoapps/schedules/tests/factories.py
View file @
7fd643fa
...
...
@@ -10,7 +10,7 @@ class ScheduleExperienceFactory(factory.DjangoModelFactory):
class
Meta
(
object
):
model
=
models
.
ScheduleExperience
experience_type
=
models
.
ScheduleExperience
.
DEFAULT
experience_type
=
models
.
ScheduleExperience
.
EXPERIENCES
.
default
class
ScheduleFactory
(
factory
.
DjangoModelFactory
):
...
...
openedx/core/djangoapps/schedules/tests/test_signals.py
View file @
7fd643fa
...
...
@@ -6,6 +6,7 @@ from pytz import utc
from
course_modes.models
import
CourseMode
from
course_modes.tests.factories
import
CourseModeFactory
from
courseware.models
import
DynamicUpgradeDeadlineConfiguration
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.schedules.models
import
ScheduleExperience
from
openedx.core.djangoapps.schedules.signals
import
CREATE_SCHEDULE_WAFFLE_FLAG
from
openedx.core.djangoapps.site_configuration.tests.factories
import
SiteFactory
...
...
@@ -24,16 +25,22 @@ from ..tests.factories import ScheduleConfigFactory
@skip_unless_lms
class
CreateScheduleTests
(
SharedModuleStoreTestCase
):
def
assert_schedule_created
(
self
,
experience_type
=
ScheduleExperience
.
DEFAULT
):
def
assert_schedule_created
(
self
,
experience_type
=
ScheduleExperience
.
EXPERIENCES
.
default
):
course
=
_create_course_run
(
self_paced
=
True
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
,
)
self
.
assertIsNotNone
(
enrollment
.
schedule
)
self
.
assertIsNone
(
enrollment
.
schedule
.
upgrade_deadline
)
self
.
assertEquals
(
enrollment
.
schedule
.
experience
.
experience_type
,
experience_type
)
def
assert_schedule_not_created
(
self
):
course
=
_create_course_run
(
self_paced
=
True
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
,
)
with
self
.
assertRaises
(
Schedule
.
DoesNotExist
):
enrollment
.
schedule
...
...
@@ -86,7 +93,7 @@ class CreateScheduleTests(SharedModuleStoreTestCase):
site
=
SiteFactory
.
create
()
mock_get_week_highlights
.
return_value
=
True
mock_get_current_site
.
return_value
=
site
self
.
assert_schedule_created
(
experience_type
=
ScheduleExperience
.
COURSE_UPDATES
)
self
.
assert_schedule_created
(
experience_type
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
)
@ddt.ddt
...
...
@@ -114,7 +121,7 @@ class UpdateScheduleTests(SharedModuleStoreTestCase):
course
=
_create_course_run
(
self_paced
=
True
,
start_day_offset
=
5
)
# course starts in future
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
)
self
.
assert_schedule_dates
(
enrollment
.
schedule
,
enrollment
.
course
_overview
.
start
)
self
.
assert_schedule_dates
(
enrollment
.
schedule
,
enrollment
.
course
.
start
)
course
.
start
=
course
.
start
+
datetime
.
timedelta
(
days
=
3
)
# new course start changes to another future date
self
.
store
.
update_item
(
course
,
ModuleStoreEnum
.
UserID
.
test
)
...
...
@@ -138,7 +145,7 @@ class UpdateScheduleTests(SharedModuleStoreTestCase):
course
=
_create_course_run
(
self_paced
=
True
,
start_day_offset
=
5
)
# course starts in future
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
mode
=
CourseMode
.
AUDIT
)
previous_start
=
enrollment
.
course
_overview
.
start
previous_start
=
enrollment
.
course
.
start
self
.
assert_schedule_dates
(
enrollment
.
schedule
,
previous_start
)
course
.
start
=
course
.
start
+
datetime
.
timedelta
(
days
=-
10
)
# new course start changes to a past date
...
...
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