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
d9a599ce
Commit
d9a599ce
authored
Apr 14, 2015
by
Chris Dodge
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add a namespace resolver
parent
be7723d4
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
223 additions
and
37 deletions
+223
-37
cms/startup.py
+5
-1
common/djangoapps/student/scope_resolver.py
+64
-32
common/djangoapps/student/tests/test_scope_resolver.py
+7
-3
common/djangoapps/util/namespace_resolver.py
+66
-0
common/djangoapps/util/tests/test_namespace_resolver.py
+76
-0
lms/startup.py
+5
-1
No files found.
cms/startup.py
View file @
d9a599ce
...
@@ -16,6 +16,7 @@ from openedx.core.djangoapps.course_groups.scope_resolver import CourseGroupScop
...
@@ -16,6 +16,7 @@ from openedx.core.djangoapps.course_groups.scope_resolver import CourseGroupScop
from
student.scope_resolver
import
CourseEnrollmentsScopeResolver
,
StudentEmailScopeResolver
from
student.scope_resolver
import
CourseEnrollmentsScopeResolver
,
StudentEmailScopeResolver
from
projects.scope_resolver
import
GroupProjectParticipantsScopeResolver
from
projects.scope_resolver
import
GroupProjectParticipantsScopeResolver
from
edx_notifications.scopes
import
register_user_scope_resolver
from
edx_notifications.scopes
import
register_user_scope_resolver
from
util.namespace_resolver
import
CourseNamespaceResolver
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -88,13 +89,16 @@ def startup_notification_subsystem():
...
@@ -88,13 +89,16 @@ def startup_notification_subsystem():
try
:
try
:
startup
.
initialize
()
startup
.
initialize
()
# register the
two scope resolvers that the LMS
will be providing
# register the
scope resolvers that the runtime
will be providing
# to edx-notifications
# to edx-notifications
register_user_scope_resolver
(
'course_enrollments'
,
CourseEnrollmentsScopeResolver
())
register_user_scope_resolver
(
'course_enrollments'
,
CourseEnrollmentsScopeResolver
())
register_user_scope_resolver
(
'course_group'
,
CourseGroupScopeResolver
())
register_user_scope_resolver
(
'course_group'
,
CourseGroupScopeResolver
())
register_user_scope_resolver
(
'group_project_participants'
,
GroupProjectParticipantsScopeResolver
())
register_user_scope_resolver
(
'group_project_participants'
,
GroupProjectParticipantsScopeResolver
())
register_user_scope_resolver
(
'group_project_workgroup'
,
GroupProjectParticipantsScopeResolver
())
register_user_scope_resolver
(
'group_project_workgroup'
,
GroupProjectParticipantsScopeResolver
())
register_user_scope_resolver
(
'student_email_resolver'
,
StudentEmailScopeResolver
())
register_user_scope_resolver
(
'student_email_resolver'
,
StudentEmailScopeResolver
())
# register namespace resolver
register_namespace_resolver
(
CourseNamespaceResolver
())
except
Exception
,
ex
:
except
Exception
,
ex
:
# Note this will fail when we try to run migrations as manage.py will call startup.py
# Note this will fail when we try to run migrations as manage.py will call startup.py
# and startup.initialze() will try to manipulate some database tables.
# and startup.initialze() will try to manipulate some database tables.
...
...
common/djangoapps/student/scope_resolver.py
View file @
d9a599ce
...
@@ -33,7 +33,7 @@ class CourseEnrollmentsScopeResolver(NotificationUserScopeResolver):
...
@@ -33,7 +33,7 @@ class CourseEnrollmentsScopeResolver(NotificationUserScopeResolver):
The entry point to resolve a scope_name with a given scope_context
The entry point to resolve a scope_name with a given scope_context
"""
"""
if
scope_name
!=
'course_enrollments'
and
scope_name
!=
'namespace_scope'
:
if
scope_name
!=
'course_enrollments'
:
# we can't resolve any other scopes
# we can't resolve any other scopes
return
None
return
None
...
@@ -51,37 +51,69 @@ class CourseEnrollmentsScopeResolver(NotificationUserScopeResolver):
...
@@ -51,37 +51,69 @@ class CourseEnrollmentsScopeResolver(NotificationUserScopeResolver):
else
:
else
:
course_key
=
course_id
course_key
=
course_id
if
scope_name
==
'course_enrollments'
:
return
CourseEnrollment
.
objects
.
values_list
(
'user_id'
,
flat
=
True
)
.
filter
(
return
CourseEnrollment
.
objects
.
values_list
(
'user_id'
,
flat
=
True
)
.
filter
(
is_active
=
1
,
is_active
=
1
,
course_id
=
course_key
course_id
=
course_key
)
)
elif
scope_name
==
'namespace_scope'
:
class
NamespaceEnrollmentsScopeResolver
(
NotificationUserScopeResolver
):
query
=
User
.
objects
.
select_related
(
'courseenrollment'
)
"""
Implementation of the NotificationUserScopeResolver abstract
if
'fields'
in
scope_context
:
interface defined in edx-notifications.
fields
=
[]
if
scope_context
[
'fields'
]
.
get
(
'id'
):
We will be passed in a namespace (aka course_id) in the context
fields
.
append
(
'id'
)
and we must return a Django ORM resultset or None if
we cannot match.
if
scope_context
[
'fields'
]
.
get
(
'email'
):
"""
fields
.
append
(
'email'
)
def
resolve
(
self
,
scope_name
,
scope_context
,
instance_context
):
if
scope_context
[
'fields'
]
.
get
(
'first_name'
):
"""
fields
.
append
(
'first_name'
)
The entry point to resolve a scope_name with a given scope_context
"""
if
scope_context
[
'fields'
]
.
get
(
'last_name'
):
fields
.
append
(
'last_name'
)
if
scope_name
!=
'namespace_scope'
:
else
:
# we can't resolve any other scopes
fields
=
[
'id'
,
'email'
,
'first_name'
,
'last_name'
]
return
None
query
=
query
.
values
(
*
fields
)
if
'namespace'
not
in
scope_context
:
query
=
query
.
filter
(
# did not receive expected parameters
courseenrollment__is_active
=
1
,
return
None
courseenrollment__course_id
=
course_key
)
course_id
=
scope_context
[
'namespace'
]
return
query
if
not
isinstance
(
course_id
,
CourseKey
):
try
:
course_key
=
CourseKey
.
from_string
(
course_id
)
except
InvalidKeyError
:
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
else
:
course_key
=
course_id
query
=
User
.
objects
.
select_related
(
'courseenrollment'
)
if
'fields'
in
scope_context
:
fields
=
[]
if
scope_context
[
'fields'
]
.
get
(
'id'
):
fields
.
append
(
'id'
)
if
scope_context
[
'fields'
]
.
get
(
'email'
):
fields
.
append
(
'email'
)
if
scope_context
[
'fields'
]
.
get
(
'first_name'
):
fields
.
append
(
'first_name'
)
if
scope_context
[
'fields'
]
.
get
(
'last_name'
):
fields
.
append
(
'last_name'
)
else
:
fields
=
[
'id'
,
'email'
,
'first_name'
,
'last_name'
]
query
=
query
.
values
(
*
fields
)
query
=
query
.
filter
(
courseenrollment__is_active
=
1
,
courseenrollment__course_id
=
course_key
)
return
query
class
StudentEmailScopeResolver
(
NotificationUserScopeResolver
):
class
StudentEmailScopeResolver
(
NotificationUserScopeResolver
):
...
...
common/djangoapps/student/tests/test_scope_resolver.py
View file @
d9a599ce
...
@@ -7,7 +7,11 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
...
@@ -7,7 +7,11 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
student.scope_resolver
import
CourseEnrollmentsScopeResolver
,
StudentEmailScopeResolver
from
student.scope_resolver
import
(
CourseEnrollmentsScopeResolver
,
StudentEmailScopeResolver
,
NamespaceEnrollmentsScopeResolver
)
class
StudentTasksTestCase
(
ModuleStoreTestCase
):
class
StudentTasksTestCase
(
ModuleStoreTestCase
):
...
@@ -89,12 +93,12 @@ class StudentTasksTestCase(ModuleStoreTestCase):
...
@@ -89,12 +93,12 @@ class StudentTasksTestCase(ModuleStoreTestCase):
enrollment
.
is_active
=
False
enrollment
.
is_active
=
False
enrollment
.
save
()
enrollment
.
save
()
resolver
=
Cours
eEnrollmentsScopeResolver
()
resolver
=
Namespac
eEnrollmentsScopeResolver
()
users
=
resolver
.
resolve
(
users
=
resolver
.
resolve
(
'namespace_scope'
,
'namespace_scope'
,
{
{
'
course_id
'
:
self
.
course
.
id
,
'
namespace
'
:
self
.
course
.
id
,
'fields'
:
{
'fields'
:
{
'id'
:
True
,
'id'
:
True
,
'email'
:
True
,
'email'
:
True
,
...
...
common/djangoapps/util/namespace_resolver.py
0 → 100644
View file @
d9a599ce
"""
A namespace resolver for edx-notifications. This basically translates a namespace
into information about the namespace
"""
from
xmodule.modulestore.django
import
modulestore
from
student.scope_resolver
import
NamespaceEnrollmentsScopeResolver
from
edx_notifications.namespaces
import
NotificationNamespaceResolver
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys
import
InvalidKeyError
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
class
CourseNamespaceResolver
(
NotificationNamespaceResolver
):
"""
An implementation of NotificationNamespaceResolver which treats
namespaces as courses
"""
def
resolve
(
self
,
namespace
,
instance_context
):
"""
Namespace resolvers will return this information as a dict:
{
'namespace': <String> ,
'display_name': <String representing a human readible name for the namespace>,
'features': {
'digests': <boolean, saying if namespace supports a digest>
},
'default_user_resolver': <pointer to a UserScopeResolver instance>
}
or None if the handler cannot resolve it
"""
# namespace = course_id
course_id
=
namespace
if
not
isinstance
(
course_id
,
CourseKey
):
try
:
course_key
=
CourseKey
.
from_string
(
course_id
)
except
InvalidKeyError
:
try
:
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
except
InvalidKeyError
:
return
None
else
:
course_key
=
course_id
course
=
modulestore
()
.
get_course
(
course_key
)
if
not
course
:
# not found, we can't resolve it
return
None
# return expected results to caller per the interface contract
return
{
'namespace'
:
course_id
,
'display_name'
:
course
.
display_name
,
'features'
:
{
'digests'
:
course
.
has_started
()
and
not
course
.
has_ended
(),
},
'default_user_resolver'
:
NamespaceEnrollmentsScopeResolver
(),
}
common/djangoapps/util/tests/test_namespace_resolver.py
0 → 100644
View file @
d9a599ce
"""
Unit tests for namespace_resolver.py
"""
from
django.test
import
TestCase
from
datetime
import
datetime
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
util.namespace_resolver
import
CourseNamespaceResolver
from
student.scope_resolver
import
NamespaceEnrollmentsScopeResolver
class
NamespaceResolverTests
(
TestCase
):
"""
Tests for the CourseNamespaceResolver
"""
def
setUp
(
self
):
"""
Test initialization
"""
self
.
course
=
CourseFactory
(
org
=
'foo'
,
start
=
datetime
(
1980
,
1
,
1
),
end
=
datetime
(
2200
,
1
,
1
)
)
self
.
closed_course
=
CourseFactory
(
org
=
'bar'
,
start
=
datetime
(
1975
,
1
,
1
),
end
=
datetime
(
1980
,
1
,
1
)
)
self
.
not_open_course
=
CourseFactory
(
org
=
'baz'
,
start
=
datetime
(
2200
,
1
,
1
),
end
=
datetime
(
2222
,
1
,
1
)
)
def
test_resolve_namespace
(
self
):
"""
Make sure the interface is properly implemented
"""
resolver
=
CourseNamespaceResolver
()
# can't resolve a non existing course
self
.
assertIsNone
(
resolver
.
resolve
(
'foo'
,
None
))
# happy path
result
=
resolver
.
resolve
(
self
.
course
.
id
,
None
)
self
.
assertIsNotNone
(
result
)
self
.
assertEqual
(
result
[
'namespace'
],
self
.
course
.
id
)
self
.
assertEqual
(
result
[
'display_name'
],
self
.
course
.
display_name
)
self
.
assertTrue
(
isinstance
(
result
[
'default_user_resolver'
],
NamespaceEnrollmentsScopeResolver
))
self
.
assertTrue
(
result
[
'features'
][
'digests'
])
# course that is closed
result
=
resolver
.
resolve
(
self
.
closed_course
.
id
,
None
)
self
.
assertIsNotNone
(
result
)
self
.
assertEqual
(
result
[
'namespace'
],
self
.
closed_course
.
id
)
self
.
assertEqual
(
result
[
'display_name'
],
self
.
closed_course
.
display_name
)
self
.
assertTrue
(
isinstance
(
result
[
'default_user_resolver'
],
NamespaceEnrollmentsScopeResolver
))
self
.
assertFalse
(
result
[
'features'
][
'digests'
])
# course that has not opened
result
=
resolver
.
resolve
(
self
.
not_open_course
.
id
,
None
)
self
.
assertIsNotNone
(
result
)
self
.
assertEqual
(
result
[
'namespace'
],
self
.
not_open_course
.
id
)
self
.
assertEqual
(
result
[
'display_name'
],
self
.
not_open_course
.
display_name
)
self
.
assertTrue
(
isinstance
(
result
[
'default_user_resolver'
],
NamespaceEnrollmentsScopeResolver
))
self
.
assertFalse
(
result
[
'features'
][
'digests'
])
lms/startup.py
View file @
d9a599ce
...
@@ -20,6 +20,7 @@ from openedx.core.djangoapps.course_groups.scope_resolver import CourseGroupScop
...
@@ -20,6 +20,7 @@ from openedx.core.djangoapps.course_groups.scope_resolver import CourseGroupScop
from
student.scope_resolver
import
CourseEnrollmentsScopeResolver
,
StudentEmailScopeResolver
from
student.scope_resolver
import
CourseEnrollmentsScopeResolver
,
StudentEmailScopeResolver
from
projects.scope_resolver
import
GroupProjectParticipantsScopeResolver
from
projects.scope_resolver
import
GroupProjectParticipantsScopeResolver
from
edx_notifications.scopes
import
register_user_scope_resolver
from
edx_notifications.scopes
import
register_user_scope_resolver
from
util.namespace_resolver
import
CourseNamespaceResolver
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -215,13 +216,16 @@ def startup_notification_subsystem():
...
@@ -215,13 +216,16 @@ def startup_notification_subsystem():
try
:
try
:
startup
.
initialize
()
startup
.
initialize
()
# register the
two scope resolvers that the LMS
will be providing
# register the
scope resolvers that the runtime
will be providing
# to edx-notifications
# to edx-notifications
register_user_scope_resolver
(
'course_enrollments'
,
CourseEnrollmentsScopeResolver
())
register_user_scope_resolver
(
'course_enrollments'
,
CourseEnrollmentsScopeResolver
())
register_user_scope_resolver
(
'course_group'
,
CourseGroupScopeResolver
())
register_user_scope_resolver
(
'course_group'
,
CourseGroupScopeResolver
())
register_user_scope_resolver
(
'group_project_participants'
,
GroupProjectParticipantsScopeResolver
())
register_user_scope_resolver
(
'group_project_participants'
,
GroupProjectParticipantsScopeResolver
())
register_user_scope_resolver
(
'group_project_workgroup'
,
GroupProjectParticipantsScopeResolver
())
register_user_scope_resolver
(
'group_project_workgroup'
,
GroupProjectParticipantsScopeResolver
())
register_user_scope_resolver
(
'student_email_resolver'
,
StudentEmailScopeResolver
())
register_user_scope_resolver
(
'student_email_resolver'
,
StudentEmailScopeResolver
())
# register namespace resolver
register_namespace_resolver
(
CourseNamespaceResolver
())
except
Exception
,
ex
:
except
Exception
,
ex
:
# Note this will fail when we try to run migrations as manage.py will call startup.py
# Note this will fail when we try to run migrations as manage.py will call startup.py
# and startup.initialze() will try to manipulate some database tables.
# and startup.initialze() will try to manipulate some database tables.
...
...
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