Commit 3246b816 by Joe Blaylock

First pass at XBlock for composition

* Implement a basic XBlock for composing. Allows the user to enter text
  and see what they entered.
* Dirty shell script to install the XBlock demos into this working tree
  so that we can use the workbench for early prototyping. This should
  not be used any more once we integrate into LMS, obviously.
parent f88acf51
......@@ -19,6 +19,10 @@ lib
lib64
__pycache__
# Various common editor backups
*.orig
.*.swp
# Installer logs
pip-log.txt
......@@ -35,4 +39,9 @@ nosetests.xml
.project
.pydevproject
# some mac thing
.DS_Store
# If the XBlock repo is checked out here, ignore it
XBlock
XBlock/*
Tasks unworthy of JIRA tickets, but still worth doing
jrbl:
* Convert Compose xblock to use django templates
from .openassessment_compose import openassessmentComposeXBlock
"""An XBlock where students can read a question and compose their response"""
import pkg_resources
from mako.template import Template
from xblock.core import XBlock
from xblock.fields import Scope, String
from xblock.fragment import Fragment
mako_default_filters = ['unicode', 'h', 'trim']
class openassessmentComposeXBlock(XBlock):
"""
Displays a question and gives an area where students can compose a response.
"""
question = String(default=u"Undefined question", scope=Scope.content, help="A question to display to a student")
def _get_xblock_trace(self):
"""Uniquely identify this xblock by context.
Every XBlock has a scope_ids, which is a NamedTuple describing
important contextual information. Per @nedbat, the usage_id attribute
uniquely identifies this block in this course, and the user_id uniquely
identifies this student. With the two of them, we can trace all the
interactions emenating from this interaction.
Useful for logging, debugging, and uniqueification."""
return (self.scope_ids.usage_id, self.scope_ids.user_id)
def student_view(self, context=None):
"""
The primary view of the OpenassessmentComposeXBlock, shown to students
when viewing courses.
"""
def load(path):
"""Handy helper for getting resources from our kit."""
data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8")
trace = self._get_xblock_trace()
html = Template(load("static/html/openassessment_compose.html"),
default_filters=mako_default_filters,
input_encoding='utf-8',
)
frag = Fragment(html.render_unicode(xblock_trace=trace, question=self.question))
frag.add_css(load("static/css/openassessment_compose.css"))
# XXX: I'm sure there's a more socially acceptable way to get our values
# into the js. But once we've invoked mako it's so tempting....
#frag.add_javascript(Template(load("static/js/src/openassessment_compose.js"),
# default_filters=mako_default_filters,
# output_encoding='utf-8'
# ).render(xblock_trace=trace))
frag.add_javascript(load("static/js/src/openassessment_compose.js"))
frag.initialize_js('OpenassessmentComposeXBlock')
return frag
@XBlock.json_handler
def submit(self, data, suffix=''):
"""
Place the submission text into Openassessment system
"""
# FIXME: Do something
student_sub = data['submission']
self.student_sub = student_sub
return '{"sub": "%s"}' % student_sub
# TO-DO: change this to create the scenarios you'd like to see in the
# workbench while developing your XBlock.
@staticmethod
def workbench_scenarios():
"""A canned scenario for display in the workbench."""
return [
("OpenassessmentComposeXBlock",
"""<vertical_demo>
<openassessment_compose/>
</vertical_demo>
"""),
]
/* START CSS for OpenassessmentComposeXBlock */
.openassessment_compose_block .openassessment_question {
font-weight: bold;
}
.openassessment_compose_block textarea {
width: 80%;
height: 30%;
}
/* END CSS for OpenassessmentComposeXBlock */
<!-- START openassessment_compose-xblock HTML -->
<div class="openassessment_compose_block" id="openassessment_compose_block_${xblock_trace[0]}">
<p class="openassessment_question" id="openassessment_question_${xblock_trace[0]}">${question}</p>
<textarea class="openassessment_submission" id="openassessment_submission_${xblock_trace[0]}">Compose your response here</textarea>
<input type="button" class="openassessment_submit" id="openassessment_submit_${xblock_trace[0]}" value="Submit" />
</div>
<!-- END openassessment_compose-xblock HTML -->
/* START Javascript for OpenassessmentComposeXBlock. */
function OpenassessmentComposeXBlock(runtime, element) {
function itWorked(result) {
alert(result);
}
var handlerUrl = runtime.handlerUrl(element, 'submit');
$('.openassessment_submit', element).click(function(eventObject) {
$.ajax({
type: "POST",
url: handlerUrl,
data: JSON.stringify({"submission": $('.openassessment_submission', element).val()}),
success: itWorked
});
});
$(function ($) {
/* Here's where you'd do things on page load. */
$(element).css('background-color', 'DarkOrchid')
});
}
/* END Javascript for OpenassessmentComposeXBlock. */
"""Setup for openassessment_compose XBlock."""
import os
from setuptools import setup
def package_data(pkg, root):
"""Generic function to find package_data for `pkg` under `root`."""
data = []
for dirname, _, files in os.walk(os.path.join(pkg, root)):
for fname in files:
data.append(os.path.relpath(os.path.join(dirname, fname), pkg))
return {pkg: data}
setup(
name='openassessment_compose-xblock',
version='0.1',
description='openassessment Composition XBlock',
packages=[
'openassessment_compose',
],
install_requires=[
'XBlock',
'Mako', # XXX: convert to django template, eliminate dependency
],
entry_points={
'xblock.v1': [
'openassessment_compose = openassessment_compose:openassessmentComposeXBlock',
]
},
package_data=package_data("openassessment_compose", "static"),
)
......@@ -110,9 +110,10 @@ MIDDLEWARE_CLASSES = (
)
ROOT_URLCONF = 'common_grading.urls'
#ROOT_URLCONF = 'urls'
# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'peer_grading.wsgi.application'
# WSGI_APPLICATION = 'peer_grading.wsgi.application'
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
......@@ -131,6 +132,7 @@ INSTALLED_APPS = (
'django.contrib.admindocs',
'submissions',
'openassessment.peer',
'openasssessment_compose',
)
# A sample logging configuration. The only tangible logging
......
......@@ -2,7 +2,7 @@
from setuptools import setup
PACKAGES = ['submissions', 'openassessment.peer']
PACKAGES = ['submissions', 'openassessment.peer', 'openassessment_compose']
def is_requirement(line):
......
#!/bin/bash
if [ ! -d 'apps' ] || [ ! -f 'manage.py' ]; then
echo "Sorry; this must be run from the top-level of the tim working tree"
exit 1;
fi
echo "Ok, installing XBlocks..."
git clone https://github.com/edx/XBlock.git
MODULES=`find XBlock/ -iname 'setup.py' | grep -v prototype`
# Installing XBlock base package is a prerequisite for the rest working
cd XBlock
pip install -e .
cd ..
# This will install XBlock base twice, but that's ok
for dir in $MODULES; do
mod_work_dir=`dirname $dir`
cd $mod_work_dir
pip install -e .
if [ ! $? ]; then # If anything goes wrong
exit 187 # then die violently
fi
cd -
done
cd apps/openassessment_compose
pip install -e .
cd -
echo "You can run the workbench by saying:"
echo "cd XBlock; python manage.py runserver"
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