Skip to content

Commit e0ce2ea

Browse files
pgbovinemmmicedcoffee
authored andcommitted
added matrix demos
1 parent fa0418b commit e0ce2ea

File tree

8 files changed

+516
-1
lines changed

8 files changed

+516
-1
lines changed

v3/bottle_server.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Lightweight OPT server that works on both Python 2 and 3
2+
3+
# to invoke, run 'python bottle_server.py'
4+
# and visit http://localhost:8080/index.html
5+
#
6+
# external dependencies: bottle
7+
#
8+
# easy_install pip
9+
# pip install bottle
10+
11+
from bottle import route, get, request, run, template, static_file
12+
import cStringIO
13+
import json
14+
import pg_logger
15+
16+
@route('/<filepath:path>')
17+
def index(filepath):
18+
return static_file(filepath, root='.')
19+
20+
@get('/exec')
21+
def get_exec():
22+
out_s = cStringIO.StringIO()
23+
24+
def json_finalizer(input_code, output_trace):
25+
ret = dict(code=input_code, trace=output_trace)
26+
json_output = json.dumps(ret, indent=None)
27+
out_s.write(json_output)
28+
29+
options = json.loads(request.query.options_json)
30+
31+
pg_logger.exec_script_str_local(request.query.user_script,
32+
request.query.raw_input_json,
33+
options['cumulative_mode'],
34+
options['heap_primitives'],
35+
json_finalizer)
36+
37+
return out_s.getvalue()
38+
39+
40+
@get('/load_matrix_problem')
41+
def load_matrix_problem():
42+
prob_name = request.query.problem_name
43+
assert type(prob_name) in (str, unicode)
44+
45+
# whitelist
46+
assert prob_name in ('python_comprehension-1',)
47+
48+
fn = 'matrix-demo/' + prob_name + '.py'
49+
cod = open(fn).read()
50+
51+
import doctest
52+
import sys
53+
p = doctest.DocTestParser()
54+
examples = p.get_examples(cod)
55+
if len(examples):
56+
first_ex = examples[0]
57+
#print >> sys.stderr, 'Source:', `first_ex.source`
58+
testCod = 'result = ' + first_ex.source
59+
60+
return json.dumps(dict(code=cod, test=testCod))
61+
62+
63+
if __name__ == "__main__":
64+
run(host='localhost', port=8080, reloader=True)

v3/css/matrixtutor.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* custom CSS for ../matrixtutor.html
2+
3+
always include this file AFTER pytutor.css
4+
*/
5+
6+
#testInputPane {
7+
margin-top: 5px;
8+
font-size: 12pt;
9+
border: 1px solid #ddd;
10+
}

v3/js/matrixtutor.js

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
/*
2+
3+
Online Python Tutor
4+
https://github.com/pgbovine/OnlinePythonTutor/
5+
6+
Copyright (C) 2010-2013 Philip J. Guo (philip@pgbovine.net)
7+
8+
Permission is hereby granted, free of charge, to any person obtaining a
9+
copy of this software and associated documentation files (the
10+
"Software"), to deal in the Software without restriction, including
11+
without limitation the rights to use, copy, modify, merge, publish,
12+
distribute, sublicense, and/or sell copies of the Software, and to
13+
permit persons to whom the Software is furnished to do so, subject to
14+
the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included
17+
in all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
27+
*/
28+
29+
30+
// custom version of opt-frontend.js for ../matrixtutor.html
31+
32+
33+
// Pre-reqs:
34+
// - pytutor.js
35+
// - jquery.ba-bbq.min.js
36+
// - opt-frontend-common.js
37+
// should all be imported BEFORE this file
38+
39+
40+
var appMode = 'edit'; // 'edit', 'display', or 'display_no_frills'
41+
42+
var preseededCurInstr = null; // if you passed in a 'curInstr=<number>' in the URL, then set this var
43+
44+
var myVisualizer = null; // singleton ExecutionVisualizer instance
45+
46+
47+
function enterEditMode() {
48+
$.bbq.pushState({ mode: 'edit' }, 2 /* completely override other hash strings to keep URL clean */);
49+
}
50+
51+
function enterDisplayNoFrillsMode() {
52+
$.bbq.pushState({ mode: 'display_no_frills' }, 2 /* completely override other hash strings to keep URL clean */);
53+
}
54+
55+
var pyInputCodeMirror; // CodeMirror object that contains the solution code
56+
var pyTestInputCodeMirror; // CodeMirror object that contains the test code
57+
58+
59+
$(document).ready(function() {
60+
61+
$("#embedLinkDiv").hide();
62+
63+
pyInputCodeMirror = CodeMirror(document.getElementById('codeInputPane'), {
64+
mode: 'python',
65+
lineNumbers: true,
66+
tabSize: 4,
67+
indentUnit: 4,
68+
// convert tab into four spaces:
69+
extraKeys: {Tab: function(cm) {cm.replaceSelection(" ", "end");}}
70+
});
71+
pyInputCodeMirror.setSize(null, '300px');
72+
73+
pyTestInputCodeMirror = CodeMirror(document.getElementById('testInputPane'), {
74+
mode: 'python',
75+
lineNumbers: true,
76+
tabSize: 4,
77+
indentUnit: 4,
78+
// convert tab into four spaces:
79+
extraKeys: {Tab: function(cm) {cm.replaceSelection(" ", "end");}}
80+
});
81+
pyTestInputCodeMirror.setSize(null, '100px');
82+
83+
84+
// be friendly to the browser's forward and back buttons
85+
// thanks to http://benalman.com/projects/jquery-bbq-plugin/
86+
$(window).bind("hashchange", function(e) {
87+
appMode = $.bbq.getState('mode'); // assign this to the GLOBAL appMode
88+
89+
if (appMode === undefined || appMode == 'edit') {
90+
$("#pyInputPane").show();
91+
$("#pyOutputPane").hide();
92+
$("#embedLinkDiv").hide();
93+
94+
// destroy all annotation bubbles (NB: kludgy)
95+
if (myVisualizer) {
96+
myVisualizer.destroyAllAnnotationBubbles();
97+
}
98+
}
99+
else if (appMode == 'display') {
100+
$("#pyInputPane").hide();
101+
$("#pyOutputPane").show();
102+
103+
$("#embedLinkDiv").show();
104+
105+
$('#executeBtn').html("Visualize Execution");
106+
$('#executeBtn').attr('disabled', false);
107+
108+
109+
// do this AFTER making #pyOutputPane visible, or else
110+
// jsPlumb connectors won't render properly
111+
myVisualizer.updateOutput();
112+
113+
// customize edit button click functionality AFTER rendering (NB: awkward!)
114+
$('#pyOutputPane #editCodeLinkDiv').show();
115+
$('#pyOutputPane #editBtn').click(function() {
116+
enterEditMode();
117+
});
118+
}
119+
else if (appMode == 'display_no_frills') {
120+
$("#pyInputPane").hide();
121+
$("#pyOutputPane").show();
122+
$("#embedLinkDiv").show();
123+
}
124+
else {
125+
assert(false);
126+
}
127+
128+
$('#urlOutput,#embedCodeOutput').val(''); // clear to avoid stale values
129+
});
130+
131+
132+
function executeCode(inputCod, testCod) {
133+
backend_script = python3_backend_script;
134+
135+
var allCod = inputCod + '\n\n# test code (ungraded)\n' + testCod;
136+
137+
var nCodeLines = inputCod.split('\n').length + 2;
138+
139+
$('#executeBtn').html("Please wait ... processing your code");
140+
$('#executeBtn').attr('disabled', true);
141+
$("#pyOutputPane").hide();
142+
$("#embedLinkDiv").hide();
143+
144+
var backendOptionsObj = {cumulative_mode: ($('#cumulativeModeSelector').val() == 'true'),
145+
heap_primitives: false,
146+
show_only_outputs: false,
147+
py_crazy_mode: false,
148+
origin: 'matrixtutor.js'};
149+
150+
var frontendOptionsObj = {updateOutputCallback: function() {$('#urlOutput,#embedCodeOutput').val('');},
151+
compactFuncLabels: true,
152+
jumpToEnd: true,
153+
}
154+
155+
function handleSuccessFunc() {
156+
// also scroll to top to make the UI more usable on smaller monitors
157+
$(document).scrollTop(0);
158+
159+
$.bbq.pushState({ mode: 'display' }, 2 /* completely override other hash strings to keep URL clean */);
160+
}
161+
162+
function handleUncaughtExceptionFunc(trace) {
163+
if (trace.length == 1) {
164+
var errorLineNo = trace[0].line - 1; /* CodeMirror lines are zero-indexed */
165+
if (errorLineNo !== undefined) {
166+
167+
if (errorLineNo < nCodeLines) {
168+
// highlight the faulting line in pyInputCodeMirror
169+
pyInputCodeMirror.focus();
170+
pyInputCodeMirror.setCursor(errorLineNo, 0);
171+
pyInputCodeMirror.setLineClass(errorLineNo, null, 'errorLine');
172+
173+
pyInputCodeMirror.setOption('onChange', function() {
174+
pyInputCodeMirror.setLineClass(errorLineNo, null, null); // reset line back to normal
175+
pyInputCodeMirror.setOption('onChange', null); // cancel
176+
});
177+
}
178+
else {
179+
console.log('wtf?', errorLineNo, nCodeLines);
180+
// instead highlight the faulting line in pyTestInputCodeMirror
181+
errorLineNo -= nCodeLines;
182+
183+
pyTestInputCodeMirror.focus();
184+
pyTestInputCodeMirror.setCursor(errorLineNo, 0);
185+
pyTestInputCodeMirror.setLineClass(errorLineNo, null, 'errorLine');
186+
187+
pyTestInputCodeMirror.setOption('onChange', function() {
188+
pyTestInputCodeMirror.setLineClass(errorLineNo, null, null);
189+
pyTestInputCodeMirror.setOption('onChange', null); // cancel
190+
});
191+
}
192+
}
193+
194+
$('#executeBtn').html("Visualize Execution");
195+
$('#executeBtn').attr('disabled', false);
196+
}
197+
}
198+
199+
executePythonCode(allCod,
200+
backend_script, backendOptionsObj,
201+
frontendOptionsObj,
202+
'pyOutputPane',
203+
handleSuccessFunc, handleUncaughtExceptionFunc);
204+
}
205+
206+
function executeCodeFromScratch() {
207+
var inputCod = pyInputCodeMirror.getValue();
208+
var testCod = pyTestInputCodeMirror.getValue();
209+
210+
// don't execute empty string:
211+
if (($.trim(inputCod) == '') && ($.trim(testCod) == '')) {
212+
alert('Type in some code to visualize.');
213+
return;
214+
}
215+
216+
executeCode(inputCod, testCod);
217+
}
218+
219+
$("#executeBtn").attr('disabled', false);
220+
$("#executeBtn").click(executeCodeFromScratch);
221+
222+
223+
var queryStrOptions = getQueryStringOptions();
224+
225+
// ugh, ugly tristate due to the possibility of each being undefined
226+
if (queryStrOptions.cumulativeState !== undefined) {
227+
$('#cumulativeModeSelector').val(queryStrOptions.cumulativeState);
228+
}
229+
230+
appMode = $.bbq.getState('mode'); // assign this to the GLOBAL appMode
231+
if ((appMode == "display") && queryStrOptions.preseededCode /* jump to display only with pre-seeded code */) {
232+
preseededCurInstr = queryStrOptions.preseededCurInstr; // ugly global
233+
$("#executeBtn").trigger('click');
234+
}
235+
else {
236+
if (appMode === undefined) {
237+
// default mode is 'edit', don't trigger a "hashchange" event
238+
appMode = 'edit';
239+
}
240+
else {
241+
// fail-soft by killing all passed-in hashes and triggering a "hashchange"
242+
// event, which will then go to 'edit' mode
243+
$.bbq.removeState();
244+
}
245+
}
246+
247+
248+
// log a generic AJAX error handler
249+
$(document).ajaxError(function() {
250+
alert("Server error (possibly due to memory/resource overload). " +
251+
"Report a bug to philip@pgbovine.net\n\n" +
252+
"(Click the 'Generate URL' button to include a unique URL in your email bug report.)");
253+
254+
$('#executeBtn').html("Visualize Execution");
255+
$('#executeBtn').attr('disabled', false);
256+
});
257+
258+
259+
// redraw connector arrows on window resize
260+
$(window).resize(function() {
261+
if (appMode == 'display') {
262+
myVisualizer.redrawConnectors();
263+
}
264+
});
265+
266+
$('#genUrlBtn').bind('click', function() {
267+
var myArgs = {code: pyInputCodeMirror.getValue(),
268+
mode: appMode,
269+
cumulative: $('#cumulativeModeSelector').val(),
270+
py: $('#pythonVersionSelector').val()};
271+
272+
if (appMode == 'display') {
273+
myArgs.curInstr = myVisualizer.curInstr;
274+
}
275+
276+
var urlStr = $.param.fragment(window.location.href, myArgs, 2 /* clobber all */);
277+
$('#urlOutput').val(urlStr);
278+
});
279+
280+
281+
$.get('load_matrix_problem',
282+
{problem_name: 'python_comprehension-1'},
283+
function(dataFromBackend) {
284+
pyInputCodeMirror.setValue(dataFromBackend.code.rtrim());
285+
pyTestInputCodeMirror.setValue(dataFromBackend.test.rtrim());
286+
},
287+
"json");
288+
});
289+

v3/js/opt-frontend-common.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4343

4444
// uncomment below if you're running on Google App Engine using the built-in app.yaml
4545
var python2_backend_script = 'exec';
46-
var python3_backend_script = null;
46+
var python3_backend_script = 'exec';
4747

4848
// KRAZY experimental KODE!!! Use a custom hacked CPython interpreter
4949
var python2crazy_backend_script = 'web_exec_py2-crazy.py';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def increments(L):
2+
'''
3+
Input:
4+
-L: a list of numbers
5+
Output:
6+
- a list where the elements in L were incremented by 1
7+
Example:
8+
>>> increments([1,4,6])
9+
[2,5,7]
10+
'''
11+
pass
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def increments(L): return [1+i for i in L]

0 commit comments

Comments
 (0)