Commit d9a599ce by Chris Dodge

add a namespace resolver

parent be7723d4
......@@ -16,6 +16,7 @@ from openedx.core.djangoapps.course_groups.scope_resolver import CourseGroupScop
from student.scope_resolver import CourseEnrollmentsScopeResolver, StudentEmailScopeResolver
from projects.scope_resolver import GroupProjectParticipantsScopeResolver
from edx_notifications.scopes import register_user_scope_resolver
from util.namespace_resolver import CourseNamespaceResolver
log = logging.getLogger(__name__)
......@@ -88,13 +89,16 @@ def startup_notification_subsystem():
try:
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
register_user_scope_resolver('course_enrollments', CourseEnrollmentsScopeResolver())
register_user_scope_resolver('course_group', CourseGroupScopeResolver())
register_user_scope_resolver('group_project_participants', GroupProjectParticipantsScopeResolver())
register_user_scope_resolver('group_project_workgroup', GroupProjectParticipantsScopeResolver())
register_user_scope_resolver('student_email_resolver', StudentEmailScopeResolver())
# register namespace resolver
register_namespace_resolver(CourseNamespaceResolver())
except Exception, ex:
# 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.
......
......@@ -33,7 +33,7 @@ class CourseEnrollmentsScopeResolver(NotificationUserScopeResolver):
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
return None
......@@ -51,12 +51,44 @@ class CourseEnrollmentsScopeResolver(NotificationUserScopeResolver):
else:
course_key = course_id
if scope_name == 'course_enrollments':
return CourseEnrollment.objects.values_list('user_id', flat=True).filter(
is_active=1,
course_id=course_key
)
elif scope_name == 'namespace_scope':
class NamespaceEnrollmentsScopeResolver(NotificationUserScopeResolver):
"""
Implementation of the NotificationUserScopeResolver abstract
interface defined in edx-notifications.
We will be passed in a namespace (aka course_id) in the context
and we must return a Django ORM resultset or None if
we cannot match.
"""
def resolve(self, scope_name, scope_context, instance_context):
"""
The entry point to resolve a scope_name with a given scope_context
"""
if scope_name != 'namespace_scope':
# we can't resolve any other scopes
return None
if 'namespace' not in scope_context:
# did not receive expected parameters
return None
course_id = scope_context['namespace']
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')
......
......@@ -7,7 +7,11 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from student.models import CourseEnrollment
from student.scope_resolver import CourseEnrollmentsScopeResolver, StudentEmailScopeResolver
from student.scope_resolver import (
CourseEnrollmentsScopeResolver,
StudentEmailScopeResolver,
NamespaceEnrollmentsScopeResolver
)
class StudentTasksTestCase(ModuleStoreTestCase):
......@@ -89,12 +93,12 @@ class StudentTasksTestCase(ModuleStoreTestCase):
enrollment.is_active = False
enrollment.save()
resolver = CourseEnrollmentsScopeResolver()
resolver = NamespaceEnrollmentsScopeResolver()
users = resolver.resolve(
'namespace_scope',
{
'course_id': self.course.id,
'namespace': self.course.id,
'fields': {
'id': True,
'email': True,
......
"""
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(),
}
"""
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'])
......@@ -20,6 +20,7 @@ from openedx.core.djangoapps.course_groups.scope_resolver import CourseGroupScop
from student.scope_resolver import CourseEnrollmentsScopeResolver, StudentEmailScopeResolver
from projects.scope_resolver import GroupProjectParticipantsScopeResolver
from edx_notifications.scopes import register_user_scope_resolver
from util.namespace_resolver import CourseNamespaceResolver
log = logging.getLogger(__name__)
......@@ -215,13 +216,16 @@ def startup_notification_subsystem():
try:
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
register_user_scope_resolver('course_enrollments', CourseEnrollmentsScopeResolver())
register_user_scope_resolver('course_group', CourseGroupScopeResolver())
register_user_scope_resolver('group_project_participants', GroupProjectParticipantsScopeResolver())
register_user_scope_resolver('group_project_workgroup', GroupProjectParticipantsScopeResolver())
register_user_scope_resolver('student_email_resolver', StudentEmailScopeResolver())
# register namespace resolver
register_namespace_resolver(CourseNamespaceResolver())
except Exception, ex:
# 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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment