/**
* An object that manages the feedback area, where users are told the state of their
* program's execution and given guidance. Also manages the creation of the Trace Table.
*
* @constructor
* @this {BlockPyFeedback}
* @param {Object} main - The main BlockPy instance
* @param {HTMLElement} tag - The HTML object this is attached to.
*/
function BlockPyFeedback(main, tag) {
this.main = main;
this.tag = tag;
this.body = this.tag.find('.blockpy-feedback-body');
this.title = this.tag.find('.blockpy-feedback-title');
this.original = this.tag.find('.blockpy-feedback-original');
this.status = this.tag.find('.blockpy-feedback-status');
this.trace = this.tag.find('.blockpy-feedback-trace');
// Reload the tracetable on click
this.trace.click(this.buildTraceTable.bind(this));
this.original.hide();
};
/**
* Reload the trace table, showing it if it was hidden and
* resetting its position to the last step.
*/
BlockPyFeedback.prototype.buildTraceTable = function() {
var execution = this.main.model.execution;
execution.show_trace(true);
execution.trace_step(execution.last_step());
this.main.components.server.logEvent('editor', 'trace')
}
/**
* Raises a generic warning. This might not be used anymore.
*
* @param {String} html - Some HTML content to render to the user.
*/
BlockPyFeedback.prototype.error = function(html) {
this.tag.html(html);
this.tag.removeClass("alert-success");
this.tag.addClass("alert-warning");
this.main.components.printer.print("Execution stopped - there was an error!");
}
/**
* Clears any output currently in the feedback area. Also resets the printer and
* any highlighted lines in the editor.
*/
BlockPyFeedback.prototype.clear = function() {
this.title.html("Ready");
this.original.hide();
this.body.html("Run your program to get feedback.");
this.main.model.status.error("none");
this.main.components.editor.unhighlightLines();
this.main.components.printer.resetPrinter()
};
/**
* Clears any errors from the editor area.
*/
BlockPyFeedback.prototype.clearEditorErrors = function() {
if (this.main.model.status.error() == "editor") {
this.clear();
}
}
/**
* Show an error message related to a problem with the editor. This will appear in
* the Feedback area, the Printer, and also log to the server. The relevant line of
* code or block will also be highlighted.
*
* @param {String} original - HTML content that represents the original error message generated by the system.
* @param {String} message - HTML content that is a hopefully friendlier message for the user explaining the error.
* @param {number} line - What line the error occurred on.
*/
BlockPyFeedback.prototype.editorError = function(original, message, line) {
original = this.prettyPrintError(original);
this.title.html("Editor Error");
this.original.show().html(original);
this.body.html(message);
this.main.model.status.error("editor");
this.main.components.editor.highlightError(line-1);
//this.main.components.printer.print("Editor error - could not make blocks!");
this.main.components.server.logEvent('feedback', "Editor Error", original+"\n|\n"+message);
}
/**
* Mark this problem as completed for the student. This will appear in the Feedback area,
* and will also unhighlight lines in the editor and log to the server.
*/
BlockPyFeedback.prototype.complete = function() {
this.title.html("Complete!");
this.original.hide();
this.body.html("Great work!");
this.main.model.status.error("complete");
this.main.components.editor.unhighlightLines();
this.main.components.server.logEvent('feedback', "Success");
}
/**
* Mark this problem as finished for the student. This will appear in the Feedback area,
* and will also unhighlight lines in the editor and log to the server.
*/
BlockPyFeedback.prototype.finished = function() {
this.title.html("Ran");
this.original.hide();
this.body.html("Your program ran successfully, without any errors. However, this problem does not have a correct solution. When you are satisfied with your program, you may stop working.");
this.main.model.status.error("no errors");
this.main.components.editor.unhighlightLines();
this.main.components.server.logEvent('feedback', "Finished");
}
/**
* This notifies the student that their code ran without errors, but that there was no
* Success raised by the Checker. This will appear in the Feedback area,
* and will also unhighlight lines in the editor and log to the server.
*/
BlockPyFeedback.prototype.noErrors = function() {
this.title.html("Ran");
this.original.hide();
this.body.html("No errors reported. View your output on the left.");
this.main.model.status.error("no errors");
this.main.components.editor.unhighlightLines();
this.main.components.server.logEvent('feedback', "No Errors", '');
}
/**
* Show an error message related to syntax issue. This will appear in
* the Feedback area, the Printer, and also log to the server. The relevant line of
* code or block will also be highlighted.
*
* @param {String} original - HTML content that represents the original error message generated by the system.
* @param {String} message - HTML content that is a hopefully friendlier message for the user explaining the error.
* @param {number} line - What line the error occurred on.
*/
BlockPyFeedback.prototype.syntaxError = function(original, message, line) {
original = this.prettyPrintError(original);
this.title.html("Syntax Error");
this.original.show().html(original);
this.body.html(message);
this.main.model.status.error("syntax");
this.main.components.editor.highlightError(line-1);
this.main.components.printer.print("Execution stopped - there was an error!");
this.main.components.server.logEvent('feedback', "Syntax Error", original+"\n|\n"+message);
}
/**
* Show an error message related to semantic error with the program (e.g., unused variable).
* This will appear in the Feedback area, the Printer, and also log to the server. The
* relevant line of code or block will also be highlighted.
*
* @param {String} original - HTML content that represents the original error message generated by the system.
* @param {String} message - HTML content that is a hopefully friendlier message for the user explaining the error.
* @param {number} line - What line the error occurred on.
*/
BlockPyFeedback.prototype.semanticError = function(name, message, line) {
this.title.html(name);
this.original.hide();
this.body.html(message);
this.main.model.status.error("semantic");
if (line !== null) {
this.main.components.editor.highlightError(line-1);
}
this.main.components.printer.print("Execution stopped - there was an error!");
this.main.components.server.logEvent('feedback', "Semantic Error", name+"\n|\n"+message);
}
/**
* Show an error message related to a serious internal BlockPy program. Under normal conditions,
* this should never appear to a student. This will appear in
* the Feedback area, the Printer, and also log to the server. The relevant line of
* code or block will also be highlighted.
*
* @param {String} original - HTML content that represents the original error message generated by the system.
* @param {String} message - HTML content that is a hopefully friendlier message for the user explaining the error.
* @param {number} line - What line the error occurred on.
*/
BlockPyFeedback.prototype.internalError = function(original, name, message) {
original = this.prettyPrintError(original);
this.title.html(name);
this.original.show().html(original);
this.body.html(message);
this.main.model.status.error("internal");
this.main.components.printer.print("Internal error! Please show this to an instructor!");
this.main.components.server.logEvent('feedback', "Internal Error", name+"\n|\n"+original+"\n|\n"+message);
}
/**
* Show an incorrect code message related to a problem as specified by the Checker. This will appear in
* the Feedback area, the Printer, and also log to the server. The relevant line of
* code or block will also be highlighted.
*
* @param {String} original - HTML content that represents the original error message generated by the system.
* @param {String} message - HTML content that is a hopefully friendlier message for the user explaining the error.
* @param {number} line - What line the error occurred on.
*/
BlockPyFeedback.prototype.instructorFeedback = function(name, message, line) {
this.title.html(name);
this.original.hide();
this.body.html(message);
this.main.model.status.error("feedback");
if (line !== undefined) {
this.main.components.editor.highlightError(line-1);
}
this.main.components.server.logEvent('feedback', "Instructor Feedback", name+"\n|\n"+"\n|\n"+message);
}
/**
* Show "Empty Program" error, indicating the student hasn't written any code. This will appear in
* the Feedback area, the Printer, and also log to the server. The relevant line of
* code or block will also be highlighted.
*
* @param {String} original - HTML content that represents the original error message generated by the system.
* @param {String} message - HTML content that is a hopefully friendlier message for the user explaining the error.
* @param {number} line - What line the error occurred on.
*/
BlockPyFeedback.prototype.emptyProgram = function() {
this.title.html("Blank Program");
this.original.hide().html("");
this.body.html("You have not written any code yet.");
this.main.model.status.error("runtime");
this.main.components.server.logEvent('feedback', "Empty Program");
}
/**
* Converts any kind of error (usually a Skulpt one) into a prettier version that's ready
* for users to see. If it's already a string, it is passed along unchanged. But Skulpt
* errors have to be processed more closely.
*/
BlockPyFeedback.prototype.prettyPrintError = function(error) {
if (typeof error === "string") {
return error;
} else {
// A weird skulpt thing?
if (error.tp$str !== undefined) {
return error.tp$str().v;
} else {
return ""+error.name + ": " + error.message;
}
}
}
/**
* Print an error to the printers -- the on screen one and the browser one. This
* will attempt to provide extra explanation and context for an error.
* Notice that this is largely for Run-time errors that will be thrown when the code
* is executed, as opposed to ones raised elsewhere in the environment.
*
* @param {String} error - The error message to be analyzed and printed.
*/
BlockPyFeedback.prototype.printError = function(error) {
//console.log(error);
original = this.prettyPrintError(error);
this.title.html(error.tp$name);
this.original.show().html(original);
if (error.tp$name == "ParseError") {
this.body.html("While attempting to convert the Python code into blocks, I found a syntax error. In other words, your Python code has a spelling or grammatical mistake. You should check to make sure that you have written all of your code correctly. To me, it looks like the problem is on line "+ error.args.v[2]+', where it says:<br><code>'+error.args.v[3][2]+'</code>', error.args.v[2]);
} else if (error.constructor == Sk.builtin.NameError
&& error.args.v.length > 0
&& error.args.v[0].v == "name '___' is not defined") {
this.body.html("You have incomplete blocks. Make sure that you do not have any dangling blocks or blocks that are connected incorrectly.<br><br>If you look at the text view of your Python code, you'll see <code>___</code> in the code. The converter will create these <code>___</code> to show that you have a block that's missing a piece.");
} else if (error.tp$name in EXTENDED_ERROR_EXPLANATION) {
this.body.html(EXTENDED_ERROR_EXPLANATION[error.tp$name]);
} else {
this.body.html(error.enhanced);
}
console.error(error);
if (error.stack) {
console.error(error.stack);
}
this.main.model.status.error("runtime");
this.main.components.editor.highlightError(error.traceback[0].lineno-1);
this.main.components.server.logEvent('feedback', "Runtime", original);
}