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
741917e9
Commit
741917e9
authored
Oct 12, 2017
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Course Update emails
parent
44897454
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
236 additions
and
15 deletions
+236
-15
common/djangoapps/student/models.py
+0
-2
openedx/core/djangoapps/schedules/admin.py
+6
-3
openedx/core/djangoapps/schedules/config.py
+6
-0
openedx/core/djangoapps/schedules/exceptions.py
+2
-0
openedx/core/djangoapps/schedules/management/commands/send_course_update.py
+14
-0
openedx/core/djangoapps/schedules/migrations/0005_auto_20171010_1722.py
+24
-0
openedx/core/djangoapps/schedules/models.py
+2
-0
openedx/core/djangoapps/schedules/resolvers.py
+17
-1
openedx/core/djangoapps/schedules/tasks.py
+97
-2
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/body.html
+51
-0
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/body.txt
+9
-0
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/from_name.txt
+1
-0
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/head.html
+1
-0
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/subject.txt
+3
-0
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/upgradereminder/email/body.txt
+3
-7
No files found.
common/djangoapps/student/models.py
View file @
741917e9
...
...
@@ -55,9 +55,7 @@ from courseware.models import DynamicUpgradeDeadlineConfiguration, CourseDynamic
from
enrollment.api
import
_default_course_mode
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.schedules.models
import
ScheduleConfig
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.djangoapps.theming.helpers
import
get_current_site
from
openedx.core.djangoapps.xmodule_django.models
import
CourseKeyField
,
NoneToEmptyManager
from
track
import
contexts
from
util.milestones_helpers
import
is_entrance_exams_enabled
...
...
openedx/core/djangoapps/schedules/admin.py
View file @
741917e9
...
...
@@ -30,6 +30,9 @@ class ScheduleAdmin(admin.ModelAdmin):
@admin.register
(
models
.
ScheduleConfig
)
class
ScheduleConfigAdmin
(
admin
.
ModelAdmin
):
search_fields
=
(
'site'
,)
list_display
=
(
'site'
,
'create_schedules'
,
'enqueue_recurring_nudge'
,
'deliver_recurring_nudge'
,
'enqueue_upgrade_reminder'
,
'deliver_upgrade_reminder'
)
list_display
=
(
'site'
,
'create_schedules'
,
'enqueue_recurring_nudge'
,
'deliver_recurring_nudge'
,
'enqueue_upgrade_reminder'
,
'deliver_upgrade_reminder'
,
'enqueue_course_update'
,
'deliver_course_update'
,
)
openedx/core/djangoapps/schedules/config.py
View file @
741917e9
...
...
@@ -9,4 +9,10 @@ CREATE_SCHEDULE_WAFFLE_FLAG = CourseWaffleFlag(
flag_undefined_default
=
False
)
COURSE_UPDATE_WAFFLE_FLAG
=
CourseWaffleFlag
(
waffle_namespace
=
WAFFLE_FLAG_NAMESPACE
,
flag_name
=
u'send_updates_for_course'
,
flag_undefined_default
=
False
)
DEBUG_MESSAGE_WAFFLE_FLAG
=
WaffleFlag
(
WAFFLE_FLAG_NAMESPACE
,
u'enable_debugging'
)
openedx/core/djangoapps/schedules/exceptions.py
0 → 100644
View file @
741917e9
class
CourseUpdateDoesNotExist
(
Exception
):
pass
openedx/core/djangoapps/schedules/management/commands/send_course_update.py
0 → 100644
View file @
741917e9
from
openedx.core.djangoapps.schedules.management.commands
import
SendEmailBaseCommand
from
openedx.core.djangoapps.schedules.resolvers
import
CourseUpdateResolver
class
Command
(
SendEmailBaseCommand
):
resolver_class
=
CourseUpdateResolver
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
Command
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
log_prefix
=
'Upgrade Reminder'
def
send_emails
(
self
,
resolver
,
*
args
,
**
options
):
for
day_offset
in
xrange
(
-
7
,
-
77
,
-
7
):
resolver
.
send
(
day_offset
,
options
.
get
(
'override_recipient_email'
))
openedx/core/djangoapps/schedules/migrations/0005_auto_20171010_1722.py
0 → 100644
View file @
741917e9
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'schedules'
,
'0004_auto_20170922_1428'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'scheduleconfig'
,
name
=
'deliver_course_update'
,
field
=
models
.
BooleanField
(
default
=
False
),
),
migrations
.
AddField
(
model_name
=
'scheduleconfig'
,
name
=
'enqueue_course_update'
,
field
=
models
.
BooleanField
(
default
=
False
),
),
]
openedx/core/djangoapps/schedules/models.py
View file @
741917e9
...
...
@@ -37,3 +37,5 @@ class ScheduleConfig(ConfigurationModel):
deliver_recurring_nudge
=
models
.
BooleanField
(
default
=
False
)
enqueue_upgrade_reminder
=
models
.
BooleanField
(
default
=
False
)
deliver_upgrade_reminder
=
models
.
BooleanField
(
default
=
False
)
enqueue_course_update
=
models
.
BooleanField
(
default
=
False
)
deliver_course_update
=
models
.
BooleanField
(
default
=
False
)
openedx/core/djangoapps/schedules/resolvers.py
View file @
741917e9
...
...
@@ -8,8 +8,10 @@ from openedx.core.djangoapps.schedules.tasks import (
DEFAULT_NUM_BINS
,
RECURRING_NUDGE_NUM_BINS
,
UPGRADE_REMINDER_NUM_BINS
,
COURSE_UPDATE_NUM_BINS
,
recurring_nudge_schedule_bin
,
upgrade_reminder_schedule_bin
upgrade_reminder_schedule_bin
,
course_update_schedule_bin
,
)
from
openedx.core.djangoapps.schedules.utils
import
PrefixedDebugLoggerMixin
from
openedx.core.djangoapps.site_configuration.models
import
SiteConfiguration
...
...
@@ -118,3 +120,17 @@ class UpgradeReminderResolver(BinnedSchedulesBaseResolver):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
UpgradeReminderResolver
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
log_prefix
=
'Upgrade Reminder'
class
CourseUpdateResolver
(
BinnedSchedulesBaseResolver
):
"""
Send a message to all users whose schedule started at ``self.current_date`` + ``day_offset`` and the
course has updates.
"""
async_send_task
=
course_update_schedule_bin
num_bins
=
COURSE_UPDATE_NUM_BINS
enqueue_config_var
=
'enqueue_course_update'
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
CourseUpdateResolver
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
log_prefix
=
'Course Update'
openedx/core/djangoapps/schedules/tasks.py
View file @
741917e9
...
...
@@ -20,6 +20,8 @@ from edx_ace.utils.date import deserialize
from
opaque_keys.edx.keys
import
CourseKey
from
edxmako.shortcuts
import
marketing_link
from
openedx.core.djangoapps.schedules.config
import
COURSE_UPDATE_WAFFLE_FLAG
from
openedx.core.djangoapps.schedules.exceptions
import
CourseUpdateDoesNotExist
from
openedx.core.djangoapps.schedules.message_type
import
ScheduleMessageType
from
openedx.core.djangoapps.schedules.models
import
Schedule
,
ScheduleConfig
from
openedx.core.djangoapps.schedules.template_context
import
(
...
...
@@ -28,6 +30,8 @@ from openedx.core.djangoapps.schedules.template_context import (
encode_urls_in_dict
,
get_base_template_context
)
from
request_cache.middleware
import
request_cached
from
xmodule.modulestore.django
import
modulestore
LOG
=
logging
.
getLogger
(
__name__
)
...
...
@@ -41,6 +45,7 @@ KNOWN_RETRY_ERRORS = ( # Errors we expect occasionally that could resolve on re
DEFAULT_NUM_BINS
=
24
RECURRING_NUDGE_NUM_BINS
=
DEFAULT_NUM_BINS
UPGRADE_REMINDER_NUM_BINS
=
DEFAULT_NUM_BINS
COURSE_UPDATE_NUM_BINS
=
DEFAULT_NUM_BINS
@task
(
bind
=
True
,
default_retry_delay
=
30
,
routing_key
=
ROUTING_KEY
)
...
...
@@ -309,8 +314,98 @@ def _upgrade_reminder_schedules_for_bin(site, target_day, bin_num, org_list, exc
yield
(
user
,
first_schedule
.
enrollment
.
course
.
language
,
template_context
)
class
CourseUpdate
(
ScheduleMessageType
):
pass
@task
(
ignore_result
=
True
,
routing_key
=
ROUTING_KEY
)
def
course_update_schedule_bin
(
site_id
,
target_day_str
,
day_offset
,
bin_num
,
org_list
,
exclude_orgs
=
False
,
override_recipient_email
=
None
,
):
target_day
=
deserialize
(
target_day_str
)
msg_type
=
CourseUpdate
()
for
(
user
,
language
,
context
)
in
_course_update_schedules_for_bin
(
Site
.
objects
.
get
(
id
=
site_id
),
target_day
,
day_offset
,
bin_num
,
org_list
,
exclude_orgs
):
msg
=
msg_type
.
personalize
(
Recipient
(
user
.
username
,
override_recipient_email
or
user
.
email
,
),
language
,
context
,
)
_course_update_schedule_send
.
apply_async
((
site_id
,
str
(
msg
)),
retry
=
False
)
@task
(
ignore_result
=
True
,
routing_key
=
ROUTING_KEY
)
def
_course_update_schedule_send
(
site_id
,
msg_str
):
site
=
Site
.
objects
.
get
(
pk
=
site_id
)
if
not
ScheduleConfig
.
current
(
site
)
.
deliver_course_update
:
return
msg
=
Message
.
from_string
(
msg_str
)
ace
.
send
(
msg
)
def
_course_update_schedules_for_bin
(
site
,
target_day
,
day_offset
,
bin_num
,
org_list
,
exclude_orgs
=
False
):
week_num
=
abs
(
day_offset
)
/
7
beginning_of_day
=
target_day
.
replace
(
hour
=
0
,
minute
=
0
,
second
=
0
)
schedules
=
get_schedules_with_target_date_by_bin_and_orgs
(
schedule_date_field
=
'start'
,
target_date
=
beginning_of_day
,
bin_num
=
bin_num
,
num_bins
=
COURSE_UPDATE_NUM_BINS
,
org_list
=
org_list
,
exclude_orgs
=
exclude_orgs
,
order_by
=
'enrollment__course'
,
)
LOG
.
debug
(
'Course Update: Query =
%
r'
,
schedules
.
query
.
sql_with_params
())
for
schedule
in
schedules
:
enrollment
=
schedule
.
enrollment
try
:
week_summary
=
get_course_week_summary
(
enrollment
.
course_id
,
week_num
)
except
CourseUpdateDoesNotExist
:
continue
user
=
enrollment
.
user
course_id_str
=
str
(
enrollment
.
course_id
)
template_context
=
get_base_template_context
(
site
)
template_context
.
update
({
'student_name'
:
user
.
profile
.
name
,
'user_personal_address'
:
user
.
profile
.
name
if
user
.
profile
.
name
else
user
.
username
,
'course_name'
:
schedule
.
enrollment
.
course
.
display_name
,
'course_url'
:
absolute_url
(
site
,
reverse
(
'course_root'
,
args
=
[
str
(
schedule
.
enrollment
.
course_id
)])),
'week_num'
:
week_num
,
'week_summary'
:
week_summary
,
# This is used by the bulk email optout policy
'course_ids'
:
[
course_id_str
],
})
yield
(
user
,
schedule
.
enrollment
.
course
.
language
,
template_context
)
@request_cached
def
get_course_week_summary
(
course_id
,
week_num
):
if
COURSE_UPDATE_WAFFLE_FLAG
.
is_enabled
(
course_id
):
course
=
modulestore
()
.
get_course
(
course_id
)
return
course
.
week_summary
(
week_num
)
else
:
raise
CourseUpdateDoesNotExist
()
def
get_schedules_with_target_date_by_bin_and_orgs
(
schedule_date_field
,
target_date
,
bin_num
,
num_bins
=
DEFAULT_NUM_BINS
,
org_list
=
None
,
exclude_orgs
=
False
):
org_list
=
None
,
exclude_orgs
=
False
,
order_by
=
'enrollment__user__id'
):
"""
Returns Schedules with the target_date, related to Users whose id matches the bin_num, and filtered by org_list.
...
...
@@ -348,7 +443,7 @@ def get_schedules_with_target_date_by_bin_and_orgs(schedule_date_field, target_d
enrollment__user__in
=
users
,
enrollment__is_active
=
True
,
**
schedule_date_equals_target_date_filter
)
.
order_by
(
'enrollment__user__id'
)
)
.
order_by
(
order_by
)
if
org_list
is
not
None
:
if
exclude_orgs
:
...
...
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/body.html
0 → 100644
View file @
741917e9
{% extends 'schedules/edx_ace/common/base_body.html' %}
{% load i18n %}
{% load static %}
{% block preview_text %}
{% blocktrans trimmed %}
Welcome to week {{ week_num }} of our {{ course_name }} course!
{% endblocktrans %}
{% endblock %}
{% block content %}
<table
width=
"100%"
align=
"left"
border=
"0"
cellpadding=
"0"
cellspacing=
"0"
role=
"presentation"
>
<tr>
<td>
<p>
{% blocktrans trimmed %}
Welcome to week {{ week_num }} of
<strong>
{{ course_name }}
</strong>
!
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
Here is what you can look forward to learning this week:
<p>
{{ week_summary }}
</p>
{% endblocktrans %}
</p>
<p>
<!-- email client support for style sheets is pretty spotty, so we have to inline all of these styles -->
<a
href=
"{{ course_url }}"
style=
"
color: #ffffff;
text-decoration: none;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
background-color: #005686;
border-top: 10px solid #005686;
border-bottom: 10px solid #005686;
border-right: 16px solid #005686;
border-left: 16px solid #005686;
display: inline-block;
"
>
<!-- old email clients require the use of the font tag :( -->
<font
color=
"#ffffff"
><b>
{% trans "Resume your course now" %}
</b></font>
</a>
</p>
</td>
</tr>
</table>
{% endblock %}
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/body.txt
0 → 100644
View file @
741917e9
{% load i18n %}
{% blocktrans trimmed %}
Welcome to week {{ week_num }} of our {{ course_name }} course!
Here is what you can look forward to learning this week:
{{ week_summary }}
{% endblocktrans %}
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/from_name.txt
0 → 100644
View file @
741917e9
{{ course_name }}
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/head.html
0 → 100644
View file @
741917e9
{% extends 'schedules/edx_ace/common/base_head.html' %}
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/subject.txt
0 → 100644
View file @
741917e9
{% load i18n %}
{% blocktrans %}{{ course_name }} - Welcome to Week {{ week_num }} {% endblocktrans %}
openedx/core/djangoapps/schedules/templates/schedules/edx_ace/upgradereminder/email/body.txt
View file @
741917e9
{% load i18n %}
{% blocktrans trimmed %}
Dear {{ user_personal_address }},
{% endblocktrans %}
{% blocktrans trimmed %}
We hope you are enjoying learning with us so far in {{ course_name }}! A verified certificate
will allow you to highlight your new knowledge and skills. It's official, and easily shareable.
We hope you are enjoying learning with us so far in {{ course_name }}! A verified certificate
will allow you to highlight your new knowledge and skills. It's official, and easily shareable.
Upgrade by {{ user_schedule_upgrade_deadline_time }}.
Upgrade by {{ user_schedule_upgrade_deadline_time }}.
{% endblocktrans %}
{% trans "Upgrade now at" %} <{{ course_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