Skip to content

Commit

Permalink
Add support for the LTI Consumer XBlock (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
OmarIthawi authored and jmbowman committed Mar 1, 2019
1 parent 5b64b51 commit c4868e0
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ geckodriver.log
workbench/static/djpyfs/
workbench.test.*

### Developer Settings
private.py

# Documentation
doc/_build
.pip_cache
Expand Down
48 changes: 47 additions & 1 deletion workbench/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
import itertools
import logging
from collections import defaultdict
from datetime import datetime, timedelta

from mock import Mock
from six import iteritems

import django.utils.translation
from django.conf import settings
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.template import loader as django_template_loader
from django.templatetags.static import static
Expand Down Expand Up @@ -239,8 +242,9 @@ class WorkbenchRuntime(Runtime):
A pre-configured instance of this class will be available to XBlocks as
`self.runtime`.
"""
anonymous_student_id = 'dummydummy000-fake-fake-dummydummy00' # Needed for the LTI XBlock
hostname = '127.0.0.1:8000' # Arbitrary value, needed for the LTI XBlock

def __init__(self, user_id=None):
# TODO: Add params for user, runtime, etc. to service initialization
Expand All @@ -264,8 +268,50 @@ def __init__(self, user_id=None):
self.id_generator = ID_MANAGER
self.user_id = user_id

def get_user_role(self):
"""Provide a dummy user role."""
return 'Student'

@property
def descriptor_runtime(self):
"""Provide a dummy course."""
course = Mock(
lti_passports=['test:test:secret'],
display_name_with_default='Test Course',
display_org_with_default='edX',
)

return Mock(modulestore=Mock(
get_course=Mock(return_value=course)
))

def get_real_user(self, _):
"""
Return a dummy user with a Mock profile.
Expected by the LTI Consumer XBlock.
"""
u = User()
u.profile = Mock()
u.profile.name = 'John Doe'
return u

def _patch_xblock(self, block):
"""Add required attributes by some legacy XBlocks such as the LTI Consumer XBlock."""
block.location = Mock(html_id=Mock(return_value='course-v1:edX+Demo+2020'))
block.course_id = block.location.html_id()
block.due = datetime.utcnow()
block.graceperiod = timedelta(seconds=0)
block.category = 'chapter'

def handle(self, block, handler_name, request, suffix=''):
"""Patch the XBlock with required fields."""
self._patch_xblock(block)
return super(WorkbenchRuntime, self).handle(block, handler_name, request, suffix)

def render(self, block, view_name, context=None):
"""Renders using parent class render() method"""
self._patch_xblock(block)
try:
return super(WorkbenchRuntime, self).render(block, view_name, context)
except NoSuchViewError:
Expand Down
30 changes: 30 additions & 0 deletions workbench/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
Module contains various XBlock services for the workbench.
"""
from __future__ import unicode_literals

from django.conf import settings


class SettingsService(object):
"""
Allows server-wide configuration of XBlocks on a per-type basis.
The service is copied as-is from the Open edX platform:
- `edx-platform/common/lib/xmodule/xmodule/services.py`
"""
xblock_settings_bucket_selector = 'block_settings_key'

def get_settings_bucket(self, block, default=None):
""" Gets xblock settings dictionary from settings. """
if not block:
raise ValueError("Expected XBlock instance, got {block} of type {type}".format(
block=block,
type=type(block)
))

actual_default = default if default is not None else {}
xblock_settings_bucket = getattr(block, self.xblock_settings_bucket_selector, block.unmixed_class.__name__)
xblock_settings = settings.XBLOCK_SETTINGS if hasattr(settings, "XBLOCK_SETTINGS") else {}
return xblock_settings.get(xblock_settings_bucket, actual_default)
8 changes: 7 additions & 1 deletion workbench/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@
),

'services': {
'fs': 'xblock.reference.plugins.FSService'
'fs': 'xblock.reference.plugins.FSService',
'settings': 'workbench.services.SettingsService',
}
}

try:
from .private import * # pylint: disable=wildcard-import,import-error,useless-suppression
except ImportError:
pass
16 changes: 16 additions & 0 deletions workbench/test/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ def test_asides(self):
self.assertEqual(self.id_mgr.get_usage_id_from_aside(aside_usage), usage_id)


class WorkbenchRuntimeTests(TestCase):
"""
Tests for the WorkbenchRuntime.
"""

def test_lti_consumer_xblock_requirements(self):
"""
The LTI Consumer XBlock expects a lot of values from the LMS Runtime,
this test ensures that those requirements fulfilled.
"""
runtime = WorkbenchRuntime('test_user')
assert runtime.get_real_user(object()), 'The LTI Consumer XBlock needs this method.'
assert runtime.hostname, 'The LTI Consumer XBlock needs this property.'
assert runtime.anonymous_student_id, 'The LTI Consumer XBlock needs this property.'


class TestKVStore(TestCase):
"""
Test the Workbench KVP Store
Expand Down

0 comments on commit c4868e0

Please sign in to comment.