@@ -45,10 +45,14 @@ export default class SQLFeedback extends HParsonsFeedback {
4545 // fnprefix sets the path to load the sql-wasm.wasm file
4646 var bookprefix ;
4747 var fnprefix ;
48- if ( eBookConfig . useRunestoneServices ) {
48+ if (
49+ eBookConfig . useRunestoneServices ||
50+ window . location . search . includes ( "mode=browsing" )
51+ ) {
4952 bookprefix = `${ eBookConfig . app } /books/published/${ eBookConfig . basecourse } ` ;
5053 fnprefix = bookprefix + "/_static" ;
5154 } else {
55+ // The else clause handles the case where you are building for a static web browser
5256 bookprefix = "" ;
5357 fnprefix = "/_static" ;
5458 }
@@ -130,17 +134,115 @@ export default class SQLFeedback extends HParsonsFeedback {
130134 respDiv . parentElement . removeChild ( respDiv ) ;
131135 }
132136 $ ( this . output ) . text ( "" ) ;
137+ // creating new results div
138+ respDiv = document . createElement ( "div" ) ;
139+ respDiv . id = divid ;
140+ this . outDiv . appendChild ( respDiv ) ;
141+ // show the output div
142+ $ ( this . outDiv ) . show ( ) ;
143+
133144 // Run this query
134145 let query = await this . buildProg ( ) ;
135146 if ( ! this . hparsons . db ) {
136147 $ ( this . output ) . text (
137- `Error: Database not initialized! DBURL: ${ this . dburl } `
148+ `Error: Database not initialized! DBURL: ${ this . hparsons . dburl } `
138149 ) ;
139150 return ;
140151 }
141152
142- let it = this . hparsons . db . iterateStatements ( query ) ;
143- this . results = [ ] ;
153+ // When having prefix/suffix, the visualization is consistent with "showlastsql" option of sql activecode:
154+ // only visualize last entry
155+
156+ let executionSuccessFlag = true ;
157+ // executing hidden prefix if exist
158+ if ( query . prefix ) {
159+ this . prefixresults = this . executeIteratedStatements ( this . hparsons . db . iterateStatements ( query . prefix ) ) ;
160+ if ( this . prefixresults . at ( - 1 ) . status == 'failure' ) {
161+ // if error occured in hidden prefix, log and stop executing the rest
162+ this . visualizeResults ( respDiv , this . prefixresults , "Error executing hidden code in prefix" ) ;
163+ executionSuccessFlag = false ;
164+ }
165+ }
166+
167+ // executing student input in micro Parsons
168+ if ( executionSuccessFlag ) {
169+ this . results = this . executeIteratedStatements ( this . hparsons . db . iterateStatements ( query . input ) ) ;
170+ if ( this . results . at ( - 1 ) . status == 'failure' ) {
171+ // if error occured in student input, stop executing suffix/unitttest
172+ // and visualize the error
173+ this . visualizeResults ( respDiv , this . results ) ;
174+ executionSuccessFlag = false ;
175+ } else if ( ! query . suffix ) {
176+ this . visualizeResults ( respDiv , this . results ) ;
177+ }
178+ }
179+
180+ // executing hidden suffix if exist
181+ // In most cases the suffix is just "select * from x" to
182+ // get all data from the table to see if the operations the table is correct
183+ if ( executionSuccessFlag && query . suffix ) {
184+ this . suffixresults = this . executeIteratedStatements ( this . hparsons . db . iterateStatements ( query . suffix ) ) ;
185+ if ( this . suffixresults . at ( - 1 ) . status == 'failure' ) {
186+ // if error occured in hidden suffix, visualize the results
187+ this . visualizeResults ( respDiv , this . suffixresults , "Error executing hidden code in suffix" ) ;
188+ executionSuccessFlag = false ;
189+ } else {
190+ this . visualizeResults ( respDiv , this . suffixresults ) ;
191+ }
192+ }
193+
194+ // Now handle autograding
195+ // autograding takes the results of the hidden suffix if exist
196+ // otherwise take the result of student input
197+ if ( this . hparsons . unittest ) {
198+ if ( executionSuccessFlag ) {
199+ if ( this . suffixresults ) {
200+ this . testResult = this . autograde (
201+ this . suffixresults [ this . suffixresults . length - 1 ]
202+ ) ;
203+ } else {
204+ this . testResult = this . autograde (
205+ this . results [ this . results . length - 1 ]
206+ ) ;
207+ }
208+ } else {
209+ // unit test results when execution failed
210+ this . passed = 0 ;
211+ this . failed = 0 ;
212+ this . percent = NaN ;
213+ this . unit_results = `percent:${ this . percent } :passed:${ this . passed } :failed:${ this . failed } ` ;
214+ // Do not show unittest results if execution failed
215+ $ ( this . output ) . css ( "visibility" , "hidden" ) ;
216+ }
217+ } else {
218+ $ ( this . output ) . css ( "visibility" , "hidden" ) ;
219+ }
220+
221+ return Promise . resolve ( "done" ) ;
222+ }
223+
224+ // Refactored from activecode-sql.
225+ // Takes iterated statements from db.iterateStatemnts(queryString)
226+ // Returns Array<result>:
227+ /* each result: {
228+ status: "success" or "faliure",
229+ // for SELECT statements (?):
230+ columns: number of columns,
231+ values: data,
232+ rowcount: number of rows in data,
233+ // for INSERT, UPDATE, DELETE:
234+ operation: "INSERT", "UPDATE", or "DELETE",
235+ rowcount: number of rows modified,
236+ // when error occurred (aside from status):
237+ message: error message,
238+ sql: remaining SQL (?)
239+ // when no queries were executed:
240+ message: "no queries submitted"
241+ }*/
242+ // If an error occurs it will stop executing the rest of queries in it.
243+ // Thus the error result will always be the last item.
244+ executeIteratedStatements ( it ) {
245+ let results = [ ] ;
144246 try {
145247 for ( let statement of it ) {
146248 let columns = statement . getColumnNames ( ) ;
@@ -150,7 +252,7 @@ export default class SQLFeedback extends HParsonsFeedback {
150252 while ( statement . step ( ) ) {
151253 data . push ( statement . get ( ) ) ;
152254 }
153- this . results . push ( {
255+ results . push ( {
154256 status : "success" ,
155257 columns : columns ,
156258 values : data ,
@@ -169,44 +271,53 @@ export default class SQLFeedback extends HParsonsFeedback {
169271 prefix === "update" ||
170272 prefix === "delete"
171273 ) {
172- this . results . push ( {
274+ results . push ( {
173275 status : "success" ,
174276 operation : prefix ,
175- rowcount : this . db . getRowsModified ( ) ,
277+ rowcount : this . hparsons . db . getRowsModified ( ) ,
176278 } ) ;
177279 } else {
178- this . results . push ( { status : "success" } ) ;
280+ results . push ( { status : "success" } ) ;
179281 }
180282 }
181283 }
182284 } catch ( e ) {
183- this . results . push ( {
285+ results . push ( {
184286 status : "failure" ,
185287 message : e . toString ( ) ,
186288 sql : it . getRemainingSQL ( ) ,
187289 } ) ;
188290 }
189-
190- if ( this . results . length === 0 ) {
191- this . results . push ( {
291+ if ( results . length === 0 ) {
292+ results . push ( {
192293 status : "failure" ,
193294 message : "No queries submitted." ,
194295 } ) ;
195296 }
297+ return results ;
298+ }
196299
197- respDiv = document . createElement ( "div" ) ;
198- respDiv . id = divid ;
199- this . outDiv . appendChild ( respDiv ) ;
200- $ ( this . outDiv ) . show ( ) ;
201- // Sometimes we don't want to show a bunch of intermediate results
202- // like when we are including a bunch of previous statements from
203- // other activecodes In that case the showlastsql flag can be set
204- // so we only show the last result
205- let resultArray = this . results ;
300+ // output the results in the resultArray(Array<results>).
301+ // container: the container that contains the results
302+ // resultArray (Array<result>): see executeIteratedStatements
303+ // Each result will be in a separate row.
304+ // devNote will be displayed in the top row if exist;
305+ // it usually won't happen unless something is wrong with prefix and suffix.
306+ // ("error execution prefix/suffix")
307+ visualizeResults ( container , resultArray , devNote ) {
308+ if ( devNote ) {
309+ let section = document . createElement ( "div" ) ;
310+ section . setAttribute ( "class" , "hp_sql_result" ) ;
311+ container . appendChild ( section ) ;
312+ let messageBox = document . createElement ( "pre" ) ;
313+ messageBox . textContent = devNote ;
314+ messageBox . setAttribute ( "class" , "hp_sql_result_failure" ) ;
315+ section . appendChild ( messageBox ) ;
316+ }
206317 for ( let r of resultArray ) {
207318 let section = document . createElement ( "div" ) ;
208319 section . setAttribute ( "class" , "hp_sql_result" ) ;
209- respDiv . appendChild ( section ) ;
320+ container . appendChild ( section ) ;
210321 if ( r . status === "success" ) {
211322 if ( r . columns ) {
212323 let tableDiv = document . createElement ( "div" ) ;
@@ -245,26 +356,19 @@ export default class SQLFeedback extends HParsonsFeedback {
245356 section . appendChild ( messageBox ) ;
246357 }
247358 }
248-
249- // Now handle autograding
250- if ( this . hparsons . suffix ) {
251- this . testResult = this . autograde (
252- this . results [ this . results . length - 1 ]
253- ) ;
254- } else {
255- $ ( this . output ) . css ( "visibility" , "hidden" ) ;
256- }
257-
258- return Promise . resolve ( "done" ) ;
259359 }
260-
360+
261361 // adapted from activecode
262362 async buildProg ( ) {
263363 // assemble code from prefix, suffix, and editor for running.
264- // TODO: automatically joins the text array with space.
265- // Should be joining without space when implementing regex.
266- var prog ;
267- prog = this . hparsons . hparsonsInput . getParsonsTextArray ( ) . join ( ' ' ) + "\n" ;
364+ let prog = { } ;
365+ if ( this . hparsons . hiddenPrefix ) {
366+ prog . prefix = this . hparsons . hiddenPrefix ;
367+ }
368+ prog . input = this . hparsons . hparsonsInput . getParsonsTextArray ( ) . join ( ' ' ) + "\n" ;
369+ if ( this . hparsons . hiddenSuffix ) {
370+ prog . suffix = this . hparsons . hiddenSuffix ;
371+ }
268372 return Promise . resolve ( prog ) ;
269373 }
270374
@@ -273,7 +377,7 @@ export default class SQLFeedback extends HParsonsFeedback {
273377 if ( this . unit_results ) {
274378 let act = {
275379 scheme : "execution" ,
276- correct : ( this . failed === 0 && this . percent != null ) ? "T" : "F" ,
380+ correct : ( this . failed == 0 && this . passed != 0 ) ? "T" : "F" ,
277381 answer : this . hparsons . hparsonsInput . getParsonsTextArray ( ) ,
278382 percent : this . percent // percent is null if there is execution error
279383 }
@@ -291,7 +395,7 @@ export default class SQLFeedback extends HParsonsFeedback {
291395
292396 // might move to base class if used by multiple execution based feedback
293397 autograde ( result_table ) {
294- var tests = this . hparsons . suffix . split ( / \n / ) ;
398+ var tests = this . hparsons . unittest . split ( / \n / ) ;
295399 this . passed = 0 ;
296400 this . failed = 0 ;
297401 // Tests should be of the form
0 commit comments