Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit 2440b2c

Browse files
author
Brad Miller
committed
Basic SQL language option working
1 parent 8968a6e commit 2440b2c

5 files changed

Lines changed: 348 additions & 4 deletions

File tree

runestone/activecode/activecode.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def setup(app):
5050
app.add_autoversioned_javascript('activecode.js')
5151
app.add_autoversioned_javascript('clike.js')
5252
app.add_autoversioned_javascript('timed_activecode.js')
53+
app.add_autoversioned_javascript('sql-wasm.js') # todo: only load if we need it
5354

5455

5556

@@ -68,7 +69,7 @@ def setup(app):
6869
TEMPLATE_END = """
6970
<textarea data-component="activecode" id=%(divid)s data-lang="%(language)s" %(autorun)s
7071
%(hidecode)s %(include)s %(timelimit)s %(coach)s %(codelens)s %(enabledownload)s %(chatcodes)s
71-
data-audio='%(ctext)s' %(sourcefile)s %(datafile)s %(stdin)s %(tie)s %(nopair)s
72+
data-audio='%(ctext)s' %(sourcefile)s %(datafile)s %(stdin)s %(tie)s %(dburl)s %(nopair)s
7273
%(cargs)s %(largs)s %(rargs)s %(iargs)s %(gradebutton)s %(caption)s %(hidehistory)s>
7374
%(initialcode)s
7475
</textarea>
@@ -154,6 +155,7 @@ class ActiveCode(RunestoneIdDirective):
154155
:available_files: : other additional files (java, python2, python3)
155156
:enabledownload: -- allow textfield contents to be downloaded as *.py file
156157
:nopair: -- disable pair programming features
158+
:dburl: url to load database for sql mode
157159
158160
If this is a homework problem instead of an example in the text
159161
then the assignment text should go here. The assignment text ends with
@@ -201,7 +203,8 @@ class ActiveCode(RunestoneIdDirective):
201203
'interpreterargs': directives.unchanged,
202204
'runargs': directives.unchanged,
203205
'tie': directives.unchanged,
204-
'nopair': directives.flag
206+
'nopair': directives.flag,
207+
'dburl': directives.unchanged
205208
})
206209

207210

@@ -330,6 +333,11 @@ def run(self):
330333
else:
331334
self.options['tie'] = ""
332335

336+
if 'dburl' in self.options:
337+
self.options['dburl'] = "data-dburl='{}'".format(self.options['dburl'])
338+
else:
339+
self.options['dburl'] = ""
340+
333341
for opt,tp in [('compileargs','cargs'),('linkargs','largs'),('runargs','rargs'),('interpreterargs','iargs')]:
334342
if opt in self.options:
335343
self.options[tp] = 'data-{}="{}"'.format(opt, escape(self.options[opt]))

runestone/activecode/js/activecode.js

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ ActiveCode.prototype.init = function(opts) {
3838
this.chatcodes = $(orig).data('chatcodes');
3939
this.hidehistory = $(orig).data('hidehistory');
4040
this.tie = $(orig).data('tie')
41+
this.dburl = $(orig).data('dburl');
4142
this.runButton = null;
4243
this.enabledownload = $(orig).data('enabledownload');
4344
this.downloadButton = null;
@@ -475,7 +476,7 @@ ActiveCode.prototype.createOutput = function () {
475476
// to hold turtle graphics output. We use a div in case the turtle changes from
476477
// using a canvas to using some other element like svg in the future.
477478
var outDiv = document.createElement("div");
478-
$(outDiv).addClass("ac_output col-md-5");
479+
$(outDiv).addClass("ac_output col-md-12");
479480
this.outDiv = outDiv;
480481
this.output = document.createElement('pre');
481482
this.output.id = this.divid+'_stdout';
@@ -2358,6 +2359,113 @@ LiveCode.prototype.pushDataFile = function (file, resolve, reject) {
23582359
return classes;
23592360
}
23602361

2362+
//
2363+
// SQL
2364+
//
2365+
2366+
SQLActiveCode.prototype = new ActiveCode();
2367+
2368+
function SQLActiveCode(opts) {
2369+
if (opts) {
2370+
this.init(opts)
2371+
}
2372+
}
2373+
2374+
SQLActiveCode.prototype.init = function(opts) {
2375+
2376+
ActiveCode.prototype.init.apply(this,arguments);
2377+
2378+
this.config = {
2379+
locateFile: filename => `/_static/${filename}`
2380+
}
2381+
2382+
var self = this;
2383+
2384+
initSqlJs(this.config).then(function (SQL) {
2385+
// set up call to load database asynchronously if given
2386+
if (self.dburl) {
2387+
var xhr = new XMLHttpRequest();
2388+
$(self.runButton).attr('disabled','disabled')
2389+
// For example: https://github.com/lerocha/chinook-database/raw/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite
2390+
xhr.open('GET', self.dburl, true);
2391+
xhr.responseType = 'arraybuffer';
2392+
2393+
xhr.onload = e => {
2394+
var uInt8Array = new Uint8Array(xhr.response);
2395+
self.db = new SQL.Database(uInt8Array);
2396+
$(self.runButton).removeAttr('disabled')
2397+
// contents is now [{columns:['col1','col2',...], values:[[first row], [second row], ...]}]
2398+
};
2399+
xhr.send();
2400+
} else {
2401+
self.db = new SQL.Database();
2402+
}
2403+
});
2404+
};
2405+
2406+
2407+
SQLActiveCode.prototype.runProg = function() {
2408+
2409+
// Run a query without reading the results
2410+
//this.db.run("CREATE TABLE test (col1, col2);");
2411+
// Insert two rows: (1,111) and (2,222)
2412+
//this.db.run("INSERT INTO test VALUES (?,?), (?,?)", [1,111,2,222]);
2413+
2414+
let divid = this.divid+'_sql_out';
2415+
let respDiv = document.getElementById(divid);
2416+
if (respDiv) {
2417+
respDiv.parentElement.removeChild(respDiv)
2418+
}
2419+
let query = this.buildProg();
2420+
var res = this.db.exec(query);
2421+
console.log(res); //res is an array??
2422+
let table = createTable(res[0]);
2423+
respDiv = document.createElement('div')
2424+
respDiv.id = divid;
2425+
$(respDiv).addClass('table-responsive-md')
2426+
$(respDiv).css('max-height', '500px')
2427+
$(respDiv).css('overflow', 'scroll')
2428+
this.outDiv.appendChild(respDiv)
2429+
respDiv.appendChild(table)
2430+
$(this.outDiv).show()
2431+
2432+
}
2433+
2434+
function createTable(tableData) {
2435+
var table = document.createElement('table');
2436+
var head = document.createElement('thead');
2437+
var tableBody = document.createElement('tbody');
2438+
var theads = document.createElement('tr')
2439+
2440+
tableData.columns.forEach(function(colData) {
2441+
let th = document.createElement('th');
2442+
th.appendChild(document.createTextNode(colData));
2443+
theads.appendChild(th);
2444+
});
2445+
table.appendChild(head);
2446+
head.appendChild(theads);
2447+
tableData.values.forEach(function(rowData) {
2448+
var row = document.createElement('tr');
2449+
2450+
rowData.forEach(function(cellData) {
2451+
var cell = document.createElement('td');
2452+
cell.appendChild(document.createTextNode(cellData));
2453+
row.appendChild(cell);
2454+
});
2455+
2456+
tableBody.appendChild(row);
2457+
});
2458+
2459+
table.appendChild(tableBody);
2460+
$(table).css('background', 'white');
2461+
$(table).addClass('table-striped table-light thead-dark')
2462+
return table;
2463+
}
2464+
2465+
2466+
//
2467+
// ActiveCode Factory Class
2468+
//
23612469

23622470
ACFactory = {};
23632471

@@ -2372,6 +2480,8 @@ ACFactory.createActiveCode = function (orig, lang, addopts) {
23722480
return new JSActiveCode(opts);
23732481
} else if (lang === 'htmlmixed') {
23742482
return new HTMLActiveCode(opts);
2483+
} else if (lang === 'sql') {
2484+
return new SQLActiveCode(opts);
23752485
} else if (['java', 'cpp', 'c', 'python3', 'python2'].indexOf(lang) > -1) {
23762486
return new LiveCode(opts);
23772487
} else { // default is python
@@ -2380,6 +2490,7 @@ ACFactory.createActiveCode = function (orig, lang, addopts) {
23802490

23812491
};
23822492

2493+
23832494
// used by web2py controller(s)
23842495
ACFactory.addActiveCodeToDiv = function(outerdivid, acdivid, sid, initialcode, language) {
23852496
var thepre, newac;
@@ -2462,6 +2573,11 @@ ACFactory.toggleScratchActivecode = function () {
24622573

24632574
};
24642575

2576+
2577+
//
2578+
// Page Initialization
2579+
//
2580+
24652581
$(document).ready(function() {
24662582
ACFactory.createScratchActivecode();
24672583
$('[data-component=activecode]').each( function(index ) {

0 commit comments

Comments
 (0)