# Copyright (C) 2011 Bradley N. Miller # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __author__ = 'bmiller' from docutils import nodes from docutils.parsers.rst import directives from docutils.parsers.rst import Directive from pg_logger import exec_script_str_local import json def setup(app): app.add_directive('codelens',Codelens) app.add_stylesheet('css/pytutor.css') app.add_stylesheet('css/basic.css') app.add_javascript('js/d3.v2.min.js') app.add_javascript('jquery-migrate-1.2.1.min.js') # needed so that ba-bbq can use the latest jQuery that we've included app.add_javascript('js/jquery.ba-bbq.min.js') app.add_javascript('js/jquery.jsPlumb-1.3.10-all-min.js') app.add_javascript('js/pytutor.js') VIS = '''

%(caption)s (%(divid)s)

''' QUESTION = ''' ''' DATA = ''' ''' # Some documentation to help the author. # Here's and example of a single stack frame. # you might ask a qestion about the value of a global variable # in which case the correct answer is expressed as: # # globals.a # # You could ask about a value on the heap # # heap.variable # # You could ask about a local variable -- not shown here. # # locals.variable # # You could even ask about what line is going to be executed next # # line # { # "ordered_globals": [ # "a", # "b" # ], # "stdout": "1\n", # "func_name": "", # "stack_to_render": [], # "globals": { # "a": 1, # "b": 1 # }, # "heap": {}, # "line": 5, # "event": "return" # } class Codelens(Directive): required_arguments = 1 optional_arguments = 1 option_spec = { 'tracedata':directives.unchanged, 'caption':directives.unchanged, 'showoutput':directives.flag, 'question':directives.unchanged, 'correct':directives.unchanged, 'feedback':directives.unchanged, 'breakline':directives.nonnegative_int } has_content = True def run(self): self.JS_VARNAME = "" self.JS_VARVAL = "" def raw_dict(input_code, output_trace): ret = dict(code=input_code, trace=output_trace) return ret def js_var_finalizer(input_code, output_trace): global JS_VARNAME ret = dict(code=input_code, trace=output_trace) json_output = json.dumps(ret, indent=None) return "var %s = %s;" % (self.JS_VARNAME, json_output) self.options['divid'] = self.arguments[0] if self.content: source = "\n".join(self.content) else: source = '\n' CUMULATIVE_MODE=False self.JS_VARNAME = self.options['divid']+'_trace' if 'showoutput' not in self.options: self.options['embedded'] = 'true' # to set embeddedmode to true else: self.options['embedded'] = 'false' if 'question' in self.options: curTrace = exec_script_str_local(source, CUMULATIVE_MODE, raw_dict) self.inject_questions(curTrace) json_output = json.dumps(curTrace, indent=None) self.options['tracedata'] = "var %s = %s;" % (self.JS_VARNAME, json_output) else: self.options['tracedata'] = exec_script_str_local(source, CUMULATIVE_MODE, js_var_finalizer) res = VIS if 'caption' not in self.options: self.options['caption'] = '' if 'question' in self.options: res += QUESTION if 'tracedata' in self.options: res += DATA return [nodes.raw('',res % self.options,format='html')] def inject_questions(self,curTrace): if 'breakline' not in self.options: raise RuntimeError('Must have breakline option') breakline = self.options['breakline'] for frame in curTrace['trace']: if frame['line'] == breakline: frame['question'] = dict(text=self.options['question'], correct = self.options['correct'], div = self.options['divid']+'_modal', feedback = self.options['feedback'] )