class TextInputBlock(InputBlock): """An XBlock that accepts text input.""" input_type = String(help="Type of conversion to attempt on input string") student_input = Any(help="Last input submitted by the student", default="", scope=Scope.user_state) def student_view(self, context=None): # pylint: disable=W0613 """Returns default student view.""" return Fragment(u"<p>I can only appear inside problems.</p>") def problem_view(self, context=None): # pylint: disable=W0613 """Returns a view of the problem - a javascript text input field.""" html = u"<input type='text' name='input' value='{0}'><span class='message'></span>".format( self.student_input) result = Fragment(html) result.add_javascript(u""" function TextInputBlock(runtime, element) { return { submit: function() { return $(element).find(':input').serializeArray(); }, handleSubmit: function(result) { $(element).find('.message').text((result || {}).error || ''); } } } """) result.initialize_js('TextInputBlock') return result def submit(self, submission): self.student_input = submission[0]['value'] if self.input_type == 'int': try: self.student_input = int(submission[0]['value']) except ValueError: return { 'error': u'"%s" is not an integer' % self.student_input } return None
class EqualityCheckerBlock(CheckerBlock): """An XBlock that checks the equality of two student data fields.""" # Content: the problem will hook us up with our data. content = String(help="Message describing the equality test", scope=Scope.content, default="Equality test") # Student data left = Any(scope=Scope.user_state) right = Any(scope=Scope.user_state) attempted = Boolean(scope=Scope.user_state) def problem_view(self, context=None): """Renders the problem view. The view is specific to whether or not this problem was attempted, and, if so, if it was answered correctly. """ correct = self.left == self.right # TODO: I originally named this class="data", but that conflicted with # the CSS on the page! :( We might have to do something to namespace # things. # TODO: Should we have a way to spit out JSON islands full of data? # Note the horror of mixed Python-Javascript data below... content = string.Template(self.content).substitute(**context) result = Fragment(u""" <span class="mydata" data-attempted='{self.attempted}' data-correct='{correct}'> {content} <span class='indicator'></span> </span> """.format(self=self, content=content, correct=correct)) # TODO: This is a runtime-specific URL. But if each XBlock ships their # own copy of underscore.js, we won't be able to uniquify them. # Perhaps runtimes can offer a palette of popular libraries so that # XBlocks can refer to them in XBlock-standard ways? result.add_javascript_url( self.runtime.resource_url("js/vendor/underscore-min.js")) # TODO: The image tag here needs a magic URL, not a hard-coded one. format_data = { 'correct': self.runtime.local_resource_url(self, 'public/images/correct-icon.png'), 'incorrect': self.runtime.local_resource_url( self, 'public/images/incorrect-icon.png'), } result.add_resource( u""" <script type="text/template" id="xblock-equality-template"> <% if (attempted !== "True") {{ %> (Not attempted) <% }} else if (correct === "True") {{ %> <img src="{correct}"> <% }} else {{ %> <img src="{incorrect}"> <% }} %> </script> """.format(**format_data), "text/html") result.add_javascript(""" function EqualityCheckerBlock(runtime, element) { var template = _.template($("#xblock-equality-template").html()); function render() { var data = $("span.mydata", element).data(); $("span.indicator", element).html(template(data)); } render(); return { handleCheck: function(result) { $("span.mydata", element) .data("correct", result ? "True" : "False") .data("attempted", "True"); render(); } } } """) result.initialize_js('EqualityCheckerBlock') return result def check(self, left, right): # pylint: disable=W0221 self.attempted = True self.left = left self.right = right event_data = {'value': 1 if left == right else 0, 'max_value': 1} self.runtime.publish(self, 'grade', event_data) return left == right