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
7aa1a346
Unverified
Commit
7aa1a346
authored
Nov 21, 2017
by
Calen Pennington
Committed by
GitHub
Nov 21, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #16615 from edx/cale/schedules-admin-improvements
Cale/schedules admin improvements
parents
c9c67e08
10970f30
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
163 additions
and
7 deletions
+163
-7
common/djangoapps/student/models.py
+1
-1
common/lib/xmodule/xmodule/modulestore/tests/factories.py
+1
-1
lms/envs/devstack.py
+16
-0
openedx/core/djangoapps/schedules/admin.py
+80
-4
openedx/core/djangoapps/schedules/management/commands/setup_models_to_send_test_emails.py
+62
-0
openedx/core/djangoapps/schedules/resolvers.py
+3
-1
No files found.
common/djangoapps/student/models.py
View file @
7aa1a346
...
...
@@ -1760,7 +1760,7 @@ class CourseEnrollment(models.Model):
return
None
try
:
if
not
self
.
schedule
:
if
not
self
.
schedule
or
not
self
.
schedule
.
active
:
return
None
log
.
debug
(
...
...
common/lib/xmodule/xmodule/modulestore/tests/factories.py
View file @
7aa1a346
...
...
@@ -12,7 +12,7 @@ from collections import defaultdict
from
contextlib
import
contextmanager
from
uuid
import
uuid4
from
factory
import
Factory
,
Sequence
,
lazy_attribute_sequence
,
lazy_attribute
from
factory
import
Factory
,
Sequence
,
lazy_attribute_sequence
,
lazy_attribute
,
Faker
from
factory.errors
import
CyclicDefinitionError
from
mock
import
patch
from
nose.tools
import
assert_less_equal
,
assert_greater_equal
...
...
lms/envs/devstack.py
View file @
7aa1a346
...
...
@@ -268,6 +268,22 @@ JWT_AUTH.update({
'JWT_AUDIENCE'
:
'lms-key'
,
})
############## Settings for ACE ####################################
ACE_ENABLED_CHANNELS
=
[
'file_email'
]
ACE_ENABLED_POLICIES
=
[
'bulk_email_optout'
]
ACE_CHANNEL_SAILTHRU_DEBUG
=
True
ACE_CHANNEL_SAILTHRU_TEMPLATE_NAME
=
'Automated Communication Engine Email'
ACE_CHANNEL_SAILTHRU_API_KEY
=
None
ACE_CHANNEL_SAILTHRU_API_SECRET
=
None
ACE_ROUTING_KEY
=
LOW_PRIORITY_QUEUE
#####################################################################
# See if the developer has any local overrides.
if
os
.
path
.
isfile
(
join
(
dirname
(
abspath
(
__file__
)),
'private.py'
)):
...
...
openedx/core/djangoapps/schedules/admin.py
View file @
7aa1a346
import
functools
from
django.contrib
import
admin
from
django
import
forms
from
django.db.models
import
F
from
django.core.urlresolvers
import
reverse
from
django.utils.translation
import
ugettext_lazy
as
_
from
.
import
models
...
...
@@ -9,22 +13,94 @@ class ScheduleExperienceAdminInline(admin.StackedInline):
model
=
models
.
ScheduleExperience
def
_set_experience
(
db_name
,
human_name
,
modeladmin
,
request
,
queryset
):
"""
A django action which will set all selected schedules to the supplied experience.
The intended usage is with functools.partial to generate the action for each experience type
dynamically.
Arguments:
db_name: the database name of the experience being selected
human_name: the human name of the experience being selected
modeladmin: The ModelAdmin subclass, passed by django as part of the standard Action interface
request: The current request, passed by django as part of the standard Action interface
queryset: The queryset selecting schedules, passed by django as part of the standard Action interface
"""
rows_updated
=
models
.
ScheduleExperience
.
objects
.
filter
(
schedule__in
=
list
(
queryset
)
)
.
update
(
experience_type
=
db_name
)
modeladmin
.
message_user
(
request
,
"{} schedule(s) were changed to use the {} experience"
.
format
(
rows_updated
,
human_name
))
# Generate a list of all "set_experience_to_X" actions
experience_actions
=
[]
for
(
db_name
,
human_name
)
in
models
.
ScheduleExperience
.
EXPERIENCES
:
partial
=
functools
.
partial
(
_set_experience
,
db_name
,
human_name
)
partial
.
short_description
=
"Convert the selected schedules to the {} experience"
.
format
(
human_name
)
partial
.
__name__
=
"set_experience_to_{}"
.
format
(
db_name
)
experience_actions
.
append
(
partial
)
class
KnownErrorCases
(
admin
.
SimpleListFilter
):
title
=
_
(
'KnownErrorCases'
)
parameter_name
=
'error'
def
lookups
(
self
,
request
,
model_admin
):
return
(
(
'schedule_start'
,
_
(
'Schedule start < course start'
)),
)
def
queryset
(
self
,
request
,
queryset
):
if
self
.
value
()
==
'schedule_start'
:
return
queryset
.
filter
(
start__lt
=
F
(
'enrollment__course__start'
))
@admin.register
(
models
.
Schedule
)
class
ScheduleAdmin
(
admin
.
ModelAdmin
):
list_display
=
(
'username'
,
'course_id'
,
'active'
,
'start'
,
'upgrade_deadline'
)
list_display
=
(
'username'
,
'course_id'
,
'active'
,
'start'
,
'upgrade_deadline'
,
'experience_display'
)
list_display_links
=
(
'start'
,
'upgrade_deadline'
,
'experience_display'
)
list_filter
=
(
'experience__experience_type'
,
'active'
,
KnownErrorCases
)
raw_id_fields
=
(
'enrollment'
,)
readonly_fields
=
(
'modified'
,)
search_fields
=
(
'enrollment__user__username'
,
'enrollment__course_id'
,)
search_fields
=
(
'enrollment__user__username'
,
'enrollment__course_
_
id'
,)
inlines
=
(
ScheduleExperienceAdminInline
,)
actions
=
[
'deactivate_schedules'
,
'activate_schedules'
]
+
experience_actions
def
deactivate_schedules
(
self
,
request
,
queryset
):
rows_updated
=
queryset
.
update
(
active
=
False
)
self
.
message_user
(
request
,
"{} schedule(s) were deactivated"
.
format
(
rows_updated
))
deactivate_schedules
.
short_description
=
"Deactivate selected schedules"
def
activate_schedules
(
self
,
request
,
queryset
):
rows_updated
=
queryset
.
update
(
active
=
True
)
self
.
message_user
(
request
,
"{} schedule(s) were activated"
.
format
(
rows_updated
))
activate_schedules
.
short_description
=
"Activate selected schedules"
def
experience_display
(
self
,
obj
):
return
obj
.
experience
.
get_experience_type_display
()
experience_display
.
short_descriptions
=
_
(
'Experience'
)
def
username
(
self
,
obj
):
return
obj
.
enrollment
.
user
.
username
return
'<a href="{}">{}</a>'
.
format
(
reverse
(
"admin:auth_user_change"
,
args
=
(
obj
.
enrollment
.
user
.
id
,)),
obj
.
enrollment
.
user
.
username
)
username
.
allow_tags
=
True
username
.
short_description
=
_
(
'Username'
)
def
course_id
(
self
,
obj
):
return
obj
.
enrollment
.
course_id
return
'<a href="{}">{}</a>'
.
format
(
reverse
(
"admin:course_overviews_courseoverview_change"
,
args
=
(
obj
.
enrollment
.
course_id
,
)),
obj
.
enrollment
.
course_id
)
course_id
.
allow_tags
=
True
course_id
.
short_description
=
_
(
'Course ID'
)
def
get_queryset
(
self
,
request
):
...
...
openedx/core/djangoapps/schedules/management/commands/setup_models_to_send_test_emails.py
0 → 100644
View file @
7aa1a346
import
datetime
import
pytz
import
factory
from
django.core.management.base
import
BaseCommand
from
student.models
import
CourseEnrollment
from
django.contrib.sites.models
import
Site
from
openedx.core.djangoapps.schedules.models
import
Schedule
,
ScheduleConfig
,
ScheduleExperience
from
openedx.core.djangoapps.schedules.tests.factories
import
ScheduleFactory
,
ScheduleConfigFactory
,
ScheduleExperienceFactory
from
student.tests.factories
import
CourseEnrollmentFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
XMODULE_FACTORY_LOCK
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
xmodule.modulestore.django
import
modulestore
class
ThreeDayNudgeSchedule
(
ScheduleFactory
):
start
=
factory
.
Faker
(
'date_time_between'
,
start_date
=
'-3d'
,
end_date
=
'-3d'
,
tzinfo
=
pytz
.
UTC
)
class
TenDayNudgeSchedule
(
ScheduleFactory
):
start
=
factory
.
Faker
(
'date_time_between'
,
start_date
=
'-10d'
,
end_date
=
'-10d'
,
tzinfo
=
pytz
.
UTC
)
class
UpgradeReminderSchedule
(
ScheduleFactory
):
start
=
factory
.
Faker
(
'past_datetime'
,
tzinfo
=
pytz
.
UTC
)
upgrade_deadline
=
factory
.
Faker
(
'date_time_between'
,
start_date
=
'+2d'
,
end_date
=
'+2d'
,
tzinfo
=
pytz
.
UTC
)
class
ContentHighlightSchedule
(
ScheduleFactory
):
start
=
factory
.
Faker
(
'date_time_between'
,
start_date
=
'-7d'
,
end_date
=
'-7d'
,
tzinfo
=
pytz
.
UTC
)
experience
=
factory
.
RelatedFactory
(
ScheduleExperienceFactory
,
'schedule'
,
experience_type
=
ScheduleExperience
.
EXPERIENCES
.
course_updates
)
class
Command
(
BaseCommand
):
"""
A management command that generates schedule objects for all expected schedule email types, so that it is easy to
generate test emails of all available types.
"""
def
handle
(
self
,
*
args
,
**
options
):
courses
=
modulestore
()
.
get_courses
()
# Find the largest auto-generated course, and pick the next sequence id to generate the next
# course with.
max_org_sequence_id
=
max
(
int
(
course
.
org
[
4
:])
for
course
in
courses
if
course
.
org
.
startswith
(
'org.'
))
XMODULE_FACTORY_LOCK
.
enable
()
CourseFactory
.
reset_sequence
(
max_org_sequence_id
+
1
,
force
=
True
)
course
=
CourseFactory
(
start
=
datetime
.
datetime
.
today
()
-
datetime
.
timedelta
(
days
=
30
),
end
=
datetime
.
datetime
.
today
()
+
datetime
.
timedelta
(
days
=
30
),
number
=
factory
.
Sequence
(
'schedules_test_course_{}'
.
format
),
display_name
=
factory
.
Sequence
(
'Schedules Test Course {}'
.
format
),
)
XMODULE_FACTORY_LOCK
.
disable
()
course_overview
=
CourseOverview
.
load_from_module_store
(
course
.
id
)
ThreeDayNudgeSchedule
.
create
(
enrollment__course
=
course_overview
)
TenDayNudgeSchedule
.
create
(
enrollment__course
=
course_overview
)
UpgradeReminderSchedule
.
create
(
enrollment__course
=
course_overview
)
ContentHighlightSchedule
.
create
(
enrollment__course
=
course_overview
)
ScheduleConfigFactory
.
create
(
site
=
Site
.
objects
.
get
(
name
=
'example.com'
))
openedx/core/djangoapps/schedules/resolvers.py
View file @
7aa1a346
...
...
@@ -122,10 +122,12 @@ class BinnedSchedulesBaseResolver(PrefixedDebugLoggerMixin, RecipientResolver):
'enrollment__course'
,
)
.
filter
(
Q
(
enrollment__course__end__isnull
=
True
)
|
Q
(
enrollment__course__end__gte
=
self
.
current_datetime
),
enrollment__course__end__gte
=
self
.
current_datetime
),
self
.
experience_filter
,
enrollment__user__in
=
users
,
enrollment__is_active
=
True
,
active
=
True
,
**
schedule_day_equals_target_day_filter
)
.
order_by
(
order_by
)
...
...
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