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
7b1fce9e
Commit
7b1fce9e
authored
Aug 01, 2014
by
Julia Hansbrough
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4641 from edx/reruns/server-side-handlers
Basic notifications handling.
parents
5f8ed7cb
69f900dd
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
160 additions
and
6 deletions
+160
-6
cms/djangoapps/contentstore/utils.py
+1
-2
cms/djangoapps/contentstore/views/course.py
+94
-1
cms/djangoapps/contentstore/views/tests/test_course_index.py
+63
-2
cms/urls.py
+1
-0
common/djangoapps/course_action_state/managers.py
+1
-1
No files found.
cms/djangoapps/contentstore/utils.py
View file @
7b1fce9e
...
...
@@ -57,8 +57,7 @@ def initialize_permissions(course_key, user_who_created_course):
def
remove_all_instructors
(
course_key
):
"""
Removes given user as instructor and staff to the given course,
after verifying that the requesting_user has permission to do so.
Removes all instructor and staff users from the given course.
"""
staff_role
=
CourseStaffRole
(
course_key
)
staff_role
.
remove_users
(
*
staff_role
.
users_with_role
())
...
...
cms/djangoapps/contentstore/views/course.py
View file @
7b1fce9e
...
...
@@ -38,6 +38,7 @@ from contentstore.utils import (
reverse_course_url
,
reverse_usage_url
,
reverse_url
,
remove_all_instructors
,
)
from
models.settings.course_details
import
CourseDetails
,
CourseSettingsEncoder
from
models.settings.course_grading
import
CourseGradingModel
...
...
@@ -62,6 +63,7 @@ from student.roles import (
)
from
student
import
auth
from
course_action_state.models
import
CourseRerunState
,
CourseRerunUIStateManager
from
course_action_state.managers
import
CourseActionStateItemNotFoundError
from
microsite_configuration
import
microsite
...
...
@@ -70,6 +72,7 @@ __all__ = ['course_info_handler', 'course_handler', 'course_info_update_handler'
'settings_handler'
,
'grading_handler'
,
'advanced_settings_handler'
,
'course_notifications_handler'
,
'textbooks_list_handler'
,
'textbooks_detail_handler'
,
'group_configurations_list_handler'
,
'group_configurations_detail_handler'
]
...
...
@@ -95,6 +98,90 @@ def _get_course_module(course_key, user, depth=0):
return
course_module
@login_required
def
course_notifications_handler
(
request
,
course_key_string
=
None
,
action_state_id
=
None
):
"""
Handle incoming requests for notifications in a RESTful way.
course_key_string and action_state_id must both be set; else a HttpBadResponseRequest is returned.
For each of these operations, the requesting user must have access to the course;
else a PermissionDenied error is returned.
GET
json: return json representing information about the notification (action, state, etc)
DELETE
json: return json repressing success or failure of dismissal/deletion of the notification
PUT
Raises a NotImplementedError.
POST
Raises a NotImplementedError.
"""
# ensure that we have a course and an action state
if
not
course_key_string
or
not
action_state_id
:
return
HttpResponseBadRequest
()
response_format
=
request
.
REQUEST
.
get
(
'format'
,
'html'
)
course_key
=
CourseKey
.
from_string
(
course_key_string
)
if
response_format
==
'json'
or
'application/json'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'application/json'
):
if
not
has_course_access
(
request
.
user
,
course_key
):
raise
PermissionDenied
()
if
request
.
method
==
'GET'
:
return
_course_notifications_json_get
(
action_state_id
)
elif
request
.
method
==
'DELETE'
:
# we assume any delete requests dismiss actions from the UI
return
_dismiss_notification
(
request
,
action_state_id
)
elif
request
.
method
==
'PUT'
:
raise
NotImplementedError
()
elif
request
.
method
==
'POST'
:
raise
NotImplementedError
()
else
:
return
HttpResponseBadRequest
()
else
:
return
HttpResponseNotFound
()
def
_course_notifications_json_get
(
course_action_state_id
):
"""
Return the action and the action state for the given id
"""
try
:
action_state
=
CourseRerunState
.
objects
.
find_first
(
id
=
course_action_state_id
)
except
CourseActionStateItemNotFoundError
:
return
HttpResponseBadRequest
()
action_state_info
=
{
'action'
:
action_state
.
action
,
'state'
:
action_state
.
state
,
'should_display'
:
action_state
.
should_display
}
return
JsonResponse
(
action_state_info
)
def
_dismiss_notification
(
request
,
course_action_state_id
):
# pylint: disable=unused-argument
"""
Update the display of the course notification
"""
try
:
action_state
=
CourseRerunState
.
objects
.
find_first
(
id
=
course_action_state_id
)
except
CourseActionStateItemNotFoundError
:
# Can't dismiss a notification that doesn't exist in the first place
return
HttpResponseBadRequest
()
if
action_state
.
state
==
CourseRerunUIStateManager
.
State
.
FAILED
:
# We remove all permissions for this course key at this time, since
# no further access is required to a course that failed to be created.
remove_all_instructors
(
action_state
.
course_key
)
# The CourseRerunState is no longer needed by the UI; delete
action_state
.
delete
()
return
JsonResponse
({
'success'
:
True
})
# pylint: disable=unused-argument
@login_required
def
course_handler
(
request
,
course_key_string
=
None
):
...
...
@@ -297,6 +384,11 @@ def course_index(request, course_key):
lms_link
=
get_lms_link_for_item
(
course_module
.
location
)
sections
=
course_module
.
get_children
()
try
:
current_action
=
CourseRerunState
.
objects
.
find_first
(
course_key
=
course_key
,
should_display
=
True
)
except
(
ItemNotFoundError
,
CourseActionStateItemNotFoundError
):
current_action
=
None
return
render_to_response
(
'overview.html'
,
{
'context_course'
:
course_module
,
'lms_link'
:
lms_link
,
...
...
@@ -307,7 +399,8 @@ def course_index(request, course_key):
'new_section_category'
:
'chapter'
,
'new_subsection_category'
:
'sequential'
,
'new_unit_category'
:
'vertical'
,
'category'
:
'vertical'
'category'
:
'vertical'
,
'rerun_notification_id'
:
current_action
.
id
if
current_action
else
None
,
})
...
...
cms/djangoapps/contentstore/views/tests/test_course_index.py
View file @
7b1fce9e
...
...
@@ -5,9 +5,13 @@ import json
import
lxml
from
contentstore.tests.utils
import
CourseTestCase
from
contentstore.utils
import
reverse_course_url
from
contentstore.utils
import
reverse_course_url
,
add_instructor
from
contentstore.views.access
import
has_course_access
from
course_action_state.models
import
CourseRerunState
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
opaque_keys.edx.locator
import
Locator
from
opaque_keys.edx.locator
import
CourseLocator
from
student.tests.factories
import
UserFactory
from
course_action_state.managers
import
CourseRerunUIStateManager
from
django.conf
import
settings
...
...
@@ -115,6 +119,63 @@ class TestCourseIndex(CourseTestCase):
# Finally, validate the entire response for consistency
self
.
assert_correct_json_response
(
json_response
)
def
test_notifications_handler_get
(
self
):
state
=
CourseRerunUIStateManager
.
State
.
FAILED
action
=
CourseRerunUIStateManager
.
ACTION
should_display
=
True
# try when no notification exists
notification_url
=
reverse_course_url
(
'course_notifications_handler'
,
self
.
course
.
id
,
kwargs
=
{
'action_state_id'
:
1
,
})
resp
=
self
.
client
.
get
(
notification_url
,
HTTP_ACCEPT
=
'application/json'
)
# verify that we get an empty dict out
self
.
assertEquals
(
resp
.
status_code
,
400
)
# create a test notification
rerun_state
=
CourseRerunState
.
objects
.
update_state
(
course_key
=
self
.
course
.
id
,
new_state
=
state
,
allow_not_found
=
True
)
CourseRerunState
.
objects
.
update_should_display
(
entry_id
=
rerun_state
.
id
,
user
=
UserFactory
(),
should_display
=
should_display
)
# try to get information on this notification
notification_url
=
reverse_course_url
(
'course_notifications_handler'
,
self
.
course
.
id
,
kwargs
=
{
'action_state_id'
:
rerun_state
.
id
,
})
resp
=
self
.
client
.
get
(
notification_url
,
HTTP_ACCEPT
=
'application/json'
)
json_response
=
json
.
loads
(
resp
.
content
)
self
.
assertEquals
(
json_response
[
'state'
],
state
)
self
.
assertEquals
(
json_response
[
'action'
],
action
)
self
.
assertEquals
(
json_response
[
'should_display'
],
should_display
)
def
test_notifications_handler_dismiss
(
self
):
state
=
CourseRerunUIStateManager
.
State
.
FAILED
should_display
=
True
rerun_course_key
=
CourseLocator
(
org
=
'testx'
,
course
=
'test_course'
,
run
=
'test_run'
)
# add an instructor to this course
user2
=
UserFactory
()
add_instructor
(
rerun_course_key
,
self
.
user
,
user2
)
# create a test notification
rerun_state
=
CourseRerunState
.
objects
.
update_state
(
course_key
=
rerun_course_key
,
new_state
=
state
,
allow_not_found
=
True
)
CourseRerunState
.
objects
.
update_should_display
(
entry_id
=
rerun_state
.
id
,
user
=
user2
,
should_display
=
should_display
)
# try to get information on this notification
notification_dismiss_url
=
reverse_course_url
(
'course_notifications_handler'
,
self
.
course
.
id
,
kwargs
=
{
'action_state_id'
:
rerun_state
.
id
,
})
resp
=
self
.
client
.
delete
(
notification_dismiss_url
)
self
.
assertEquals
(
resp
.
status_code
,
200
)
with
self
.
assertRaises
(
CourseRerunState
.
DoesNotExist
):
# delete nofications that are dismissed
CourseRerunState
.
objects
.
get
(
id
=
rerun_state
.
id
)
self
.
assertFalse
(
has_course_access
(
user2
,
rerun_course_key
))
def
assert_correct_json_response
(
self
,
json_response
):
"""
Asserts that the JSON response is syntactically consistent
...
...
cms/urls.py
View file @
7b1fce9e
...
...
@@ -73,6 +73,7 @@ urlpatterns += patterns(
'course_info_update_handler'
),
url
(
r'^course/{}?$'
.
format
(
settings
.
COURSE_KEY_PATTERN
),
'course_handler'
,
name
=
'course_handler'
),
url
(
r'^course_notifications/{}/(?P<action_state_id>\d+)?$'
.
format
(
settings
.
COURSE_KEY_PATTERN
),
'course_notifications_handler'
),
url
(
r'^subsection/{}$'
.
format
(
settings
.
USAGE_KEY_PATTERN
),
'subsection_handler'
),
url
(
r'^unit/{}$'
.
format
(
settings
.
USAGE_KEY_PATTERN
),
'unit_handler'
),
url
(
r'^container/{}$'
.
format
(
settings
.
USAGE_KEY_PATTERN
),
'container_handler'
),
...
...
common/djangoapps/course_action_state/managers.py
View file @
7b1fce9e
...
...
@@ -96,7 +96,7 @@ class CourseActionUIStateManager(CourseActionStateManager):
"""
Updates the should_display field with the given value for the entry for the given id.
"""
self
.
update
(
id
=
entry_id
,
updated_user
=
user
,
should_display
=
should_display
)
return
self
.
update
(
id
=
entry_id
,
updated_user
=
user
,
should_display
=
should_display
)
class
CourseRerunUIStateManager
(
CourseActionUIStateManager
):
...
...
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