Skip to content

Commit

Permalink
Add support for the LTI Consumer XBlock
Browse files Browse the repository at this point in the history
Add support for the LTI Consumer XBlock
Added org and course title
Docs
Added settings service so XBlocks can use it
  • Loading branch information
OmarIthawi committed Feb 24, 2019
1 parent 1bb25c0 commit 112f998
Show file tree
Hide file tree
Showing 21 changed files with 140 additions and 49 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
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ clean: ## remove generated byte code, coverage reports, and build artifacts
rm -rf .tox
rm -f .coverage

# 9.0.3 does not work on Sierra Mac OSX. 10.0.1 should be used instead.
pin_pip: ## pin to 9.0.3 until tox-battery upgrades
pip install --upgrade pip==9.0.3

coverage: clean ## generate and view HTML coverage report
pip install -qr requirements/local.txt --exists-action w ## Install sample xblocks
pytest --cov-report html
Expand All @@ -63,7 +67,7 @@ upgrade: ## update the requirements/*.txt files with the latest packages satisfy
quality: ## check coding style with pycodestyle and pylint
tox -e quality

requirements: ## install development environment requirements
requirements: pin_pip ## install development environment requirements
pip install -qr requirements/dev.txt --exists-action w
pip install -qr requirements/local.txt --exists-action w

Expand Down
1 change: 1 addition & 0 deletions sample_xblocks/basic/content.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
"""Content-oriented XBlocks."""
from __future__ import unicode_literals

from string import Template

Expand Down
1 change: 1 addition & 0 deletions sample_xblocks/basic/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
| | | | | |
"""
from __future__ import unicode_literals

import inspect
import random
Expand Down
1 change: 1 addition & 0 deletions sample_xblocks/basic/slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
WARNING: This is an experimental module, subject to future change or removal.
"""
from __future__ import unicode_literals

import json

Expand Down
1 change: 1 addition & 0 deletions sample_xblocks/basic/structure.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Structure-oriented XBlocks."""
from __future__ import unicode_literals

from xblock.core import XBlock
from xblock.fragment import Fragment
Expand Down
4 changes: 2 additions & 2 deletions sample_xblocks/basic/test/test_problem.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""
Tests of the Problem XBlock, and its components.
"""
from __future__ import unicode_literals

import json

import pytest
import webob
from xblock.test.tools import assert_equals

from workbench.runtime import WorkbenchRuntime

Expand Down Expand Up @@ -47,4 +47,4 @@ def test_problem_submission():
json_data = json.dumps({"vote_count": [{"name": "input", "value": "4"}]})
resp = runtime.handle(problem, 'check', make_request(json_data))
resp_data = json.loads(text_of_response(resp))
assert_equals(resp_data['checkResults']['votes_named'], True)
assert resp_data['checkResults']['votes_named']
8 changes: 4 additions & 4 deletions sample_xblocks/basic/test/test_view_counter.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
""" Simple test for the view counter that verifies that it is updating properly """
from __future__ import unicode_literals

from mock import Mock
from xblock.runtime import DictKeyValueStore, KvsFieldData
from xblock.test.tools import TestRuntime as Runtime # Workaround for pytest trying to collect "TestRuntime" as a test
from xblock.test.tools import assert_equals, assert_in

from sample_xblocks.basic.view_counter import ViewCounter

Expand All @@ -14,11 +14,11 @@ def test_view_counter_state():
runtime = Runtime(services={'field-data': field_data})
tester = ViewCounter(runtime, scope_ids=Mock())

assert_equals(tester.views, 0)
assert tester.views == 0

# View the XBlock five times
for i in xrange(5):
generated_html = tester.student_view({})
# Make sure the html fragment we're expecting appears in the body_html
assert_in('<span class="views">{0}</span>'.format(i + 1), generated_html.body_html())
assert_equals(tester.views, i + 1)
assert '<span class="views">{0}</span>'.format(i + 1) in generated_html.body_html()
assert tester.views == i + 1
5 changes: 0 additions & 5 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
[nosetests]
logging-clear-handlers=1
with-xunit=1
rednose=1

# Uncomment the following line to open pdb when a test fails
#pdb=1

Expand Down
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ norecursedirs = .* doc bin prototype screenshots requirements
[testenv]
whitelist_externals =
make
pip
deps =
django111: Django>=1.11,<2.0
django20: Django>=2.0,<2.1
Expand All @@ -24,6 +25,7 @@ passenv =
BOTO_CONFIG
commands =
make var/workbench.db
pip freeze
pytest {posargs}

[testenv:quality]
Expand Down
2 changes: 2 additions & 0 deletions workbench/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
Admin gives us a lot of basic search/filtering for free.
"""
from __future__ import unicode_literals

from xblock.fields import BlockScope, Scope

from django.db import models
Expand Down
45 changes: 45 additions & 0 deletions workbench/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
Code in this file is a mix of Runtime layer and Workbench layer.
"""
from __future__ import unicode_literals

import importlib
import itertools
import logging
from collections import defaultdict
from datetime import datetime, timedelta

from mock import Mock
from six import iteritems
from xblock.core import XBlockAside
from xblock.exceptions import NoSuchDefinition, NoSuchUsage
Expand All @@ -17,6 +21,7 @@

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 @@ -238,6 +243,8 @@ class WorkbenchRuntime(Runtime):
`self.runtime`.
"""
anonymous_student_id = '7118f8d6-11c3-11e9-a324-7f11806df4d3'
hostname = '127.0.0.1:8000'

def __init__(self, user_id=None):
# TODO: Add params for user, runtime, etc. to service initialization
Expand All @@ -261,8 +268,46 @@ 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."""
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
2 changes: 2 additions & 0 deletions workbench/scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
This code is in the Workbench layer.
"""
from __future__ import unicode_literals

import logging
from collections import namedtuple

Expand Down
27 changes: 27 additions & 0 deletions workbench/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
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 {0} of type {1}".format(block, 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 @@ -203,6 +203,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
2 changes: 1 addition & 1 deletion workbench/test/test_problems.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Test that problems and problem submission works well."""
from __future__ import print_function
from __future__ import print_function, unicode_literals

import time
import unittest
Expand Down
1 change: 1 addition & 0 deletions workbench/test/test_runtime.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Test Workbench Runtime"""
from __future__ import unicode_literals

from unittest import TestCase

Expand Down
4 changes: 2 additions & 2 deletions workbench/test/test_scenarios.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""
Test that all scenarios render successfully.
"""
from __future__ import unicode_literals

import unittest

import lxml.html
import pytest
from xblock.test.tools import assert_equals

from django.core.urlresolvers import reverse
from django.test.client import Client
Expand Down Expand Up @@ -39,7 +39,7 @@ def test_all_scenarios(self):
loaded_scenarios = scenarios.get_scenarios().values()

# We should have an <a> tag for each scenario.
assert_equals(len(a_tags), len(loaded_scenarios))
assert len(a_tags) == len(loaded_scenarios)

# We should have at least one scenario with a vertical tag, since we use
# empty verticals as our canary in the coal mine that something has gone
Expand Down
Loading

0 comments on commit 112f998

Please sign in to comment.