|
@@ -1,279 +1,332 @@
|
|
|
-import { Decimal } from "decimal.js";
|
|
|
-import { InputAssessment } from "../../util/input_assessment";
|
|
|
-import { OutputTest } from "../../util/outputTest";
|
|
|
-import { Config } from "../../util/config";
|
|
|
-import { levenshteinDistance } from "../../util/utils";
|
|
|
-import { OutputAssessmentResult } from "./assessment_result";
|
|
|
-import * as TypeParser from "./../../typeSystem/parsers";
|
|
|
-import * as LocalizedStringsService from "../../services/localizedStringsService";
|
|
|
-import * as OutputResult from "./output_result";
|
|
|
+import StringDiff from "./../../util/string_diff";
|
|
|
+import { LocalizedStrings } from "./../../services/localizedStringsService";
|
|
|
|
|
|
-const LocalizedStrings = LocalizedStringsService.getInstance();
|
|
|
+export class OutputAssessmentResult {
|
|
|
+ static get PAGE_TEMPLATE () {
|
|
|
+ return `<!DOCTYPE html>
|
|
|
+ <html>
|
|
|
+ <head>
|
|
|
+ <meta http-equiv='content-type' content='text/html; charset=UTF-8'>
|
|
|
+ <link rel='stylesheet' href='css/ivprog-assessment.css' type='text/css'/>
|
|
|
+ </head>
|
|
|
+ <body>
|
|
|
+ :assessment-result:
|
|
|
+ </body>
|
|
|
+ </html>`;
|
|
|
+ }
|
|
|
|
|
|
-export class OutputMatching {
|
|
|
- static get NUM_REGEX () {
|
|
|
- return /^[+-]?([0-9]+([.][0-9]*)?(e[+-]?[0-9]+)?)$/;
|
|
|
+ static get DETAIL_TEMPLATE () {
|
|
|
+ return `<div class='details-body'>
|
|
|
+ <div class='details-header'>
|
|
|
+ <h2>:test-name:</h2>
|
|
|
+ <p>:time-label:: <span>:time:ms</span></p>
|
|
|
+ <p>:grade-label:: <span>:grade:%</span></p>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <h3>:input-label:</h3>
|
|
|
+ <ul>
|
|
|
+ <li>:input-list:</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <h3>:output-label:</h3>
|
|
|
+ :output-result:
|
|
|
+ </div>
|
|
|
+ </div>`;
|
|
|
}
|
|
|
|
|
|
- static get NUM_IN_STRING_REGEX () {
|
|
|
- return /[+-]?([0-9]+([.][0-9]*)?(e[+-]?[0-9]+)?)/g;
|
|
|
+ static get ASSISTANT_TEMPLATE () {
|
|
|
+ return `<div class='details-body'>
|
|
|
+ <div class='assistant-div-left'>
|
|
|
+ <h1>Desempenho</h1>
|
|
|
+ <p style='padding-left:0.5rem;'>:performance-text:</p>
|
|
|
+ </div>
|
|
|
+ <div class='assistant-div-right'>
|
|
|
+ <h1>Sugestões</h1>
|
|
|
+ <p style='padding-left:0.5rem;'>:suggestion-text:</p>
|
|
|
+ </div>
|
|
|
+ </div>`;
|
|
|
}
|
|
|
|
|
|
- static get BOOLEAN_REGEX () {
|
|
|
- const str = `^(${LocalizedStrings.getUI(
|
|
|
- "logic_value_true"
|
|
|
- )}|${LocalizedStrings.getUI("logic_value_false")})$`;
|
|
|
- return new RegExp(str);
|
|
|
+ static get OUPUT_TABLE_TEMPLATE () {
|
|
|
+ return `<div class='detaisl-div-table'>
|
|
|
+ <table class='assessment-output-table'>
|
|
|
+ <tr>
|
|
|
+ <th>:expected-label:</th>
|
|
|
+ <th>:generated-label:</th>
|
|
|
+ <th>:result-label:</th>
|
|
|
+ </tr>
|
|
|
+ :results:
|
|
|
+ </table>
|
|
|
+ </div>`;
|
|
|
}
|
|
|
|
|
|
- static get BOOLEAN_IN_STRING_REGEX () {
|
|
|
- const str = `(${LocalizedStrings.getUI(
|
|
|
- "logic_value_true"
|
|
|
- )}|${LocalizedStrings.getUI("logic_value_false")})`;
|
|
|
- return new RegExp(str, "g");
|
|
|
+ static get OUTPUT_TEMPLATE () {
|
|
|
+ return `<tr><td class=':class-expected:'>$0</td>
|
|
|
+ <td class=':class-generated:'>$1</td>
|
|
|
+ <td class=':class-result:'>$2</td></tr>`;
|
|
|
}
|
|
|
|
|
|
- constructor (program, input_list, expected_output, test_name, tag = null) {
|
|
|
- this.program = program;
|
|
|
- this.name = test_name;
|
|
|
- this.input_list = input_list;
|
|
|
- this.expected_output = expected_output;
|
|
|
- this.tag = tag;
|
|
|
+ static get EMPTY_OUTPUT_TEMPLATE () {
|
|
|
+ return `<div class='assessment-popup'><img class='assessment-empty-output' src='img/empty.svg'>
|
|
|
+ <span class='assessment-popuptext'>$0</span></div>`;
|
|
|
}
|
|
|
|
|
|
- eval () {
|
|
|
- const input = new InputAssessment(this.input_list);
|
|
|
- const gen_output = new OutputTest();
|
|
|
- this.program.registerInput(input);
|
|
|
- this.program.registerOutput(gen_output);
|
|
|
- const start_time = Date.now();
|
|
|
- return this.program
|
|
|
- .interpretAST()
|
|
|
- .then((sto) => {
|
|
|
- const final_time = Date.now() - start_time;
|
|
|
- if (input.isInputAvailable()) {
|
|
|
- const error = this.getErrorMessage(
|
|
|
- "test_case_few_reads",
|
|
|
- this.name + 1
|
|
|
- );
|
|
|
- return new OutputAssessmentResult(
|
|
|
- this.name,
|
|
|
- 1,
|
|
|
- input.input_list,
|
|
|
- null,
|
|
|
- sto,
|
|
|
- final_time,
|
|
|
- this.tag,
|
|
|
- error,
|
|
|
- error.id
|
|
|
- );
|
|
|
- }
|
|
|
- const result = gen_output.list.map((g_out, i) => {
|
|
|
- if (i >= this.expected_output.length) {
|
|
|
- return new OutputResult.OutputMatchResult(
|
|
|
- null,
|
|
|
- g_out,
|
|
|
- 0,
|
|
|
- this.getPotentialOutputType(g_out)
|
|
|
- );
|
|
|
- }
|
|
|
- return this.outputMatch(g_out, this.expected_output[i]);
|
|
|
- }, this);
|
|
|
- if (this.expected_output.length > gen_output.list.length) {
|
|
|
- console.log(
|
|
|
- "Saída insuficientes!",
|
|
|
- this.expected_output.length,
|
|
|
- gen_output.list.length
|
|
|
- );
|
|
|
- for (
|
|
|
- let i = gen_output.list.length;
|
|
|
- i < this.expected_output.length;
|
|
|
- ++i
|
|
|
- ) {
|
|
|
- const e_out = this.expected_output[i];
|
|
|
- result.push(
|
|
|
- new OutputResult.OutputMatchResult(
|
|
|
- e_out,
|
|
|
- null,
|
|
|
- 0,
|
|
|
- this.getPotentialOutputType(e_out)
|
|
|
- )
|
|
|
- );
|
|
|
- }
|
|
|
- } else if (
|
|
|
- this.expected_output.length == 0 &&
|
|
|
- this.expected_output.length == gen_output.list.length
|
|
|
- ) {
|
|
|
- // no output expected....
|
|
|
- result.push(new OutputResult.OutputMatchResult("", "", 1, "string"));
|
|
|
- }
|
|
|
- return new OutputAssessmentResult(
|
|
|
- this.name,
|
|
|
- 0,
|
|
|
- input.input_list,
|
|
|
- result,
|
|
|
- sto,
|
|
|
- final_time,
|
|
|
- this.tag
|
|
|
- );
|
|
|
- })
|
|
|
- .catch((error) => {
|
|
|
- return new OutputAssessmentResult(
|
|
|
- this.name,
|
|
|
- 1,
|
|
|
- input.input_list,
|
|
|
- null,
|
|
|
- null,
|
|
|
- null,
|
|
|
- this.tags,
|
|
|
- this.getErrorMessage(
|
|
|
- "test_case_exception",
|
|
|
- this.name + 1,
|
|
|
- error.message
|
|
|
- ),
|
|
|
- error.id
|
|
|
- );
|
|
|
- });
|
|
|
+ static get FAILED_TEMPLATE () {
|
|
|
+ return `<p class='assessment-failed-execution'><span class='assessment-failed-case'>✗</span>$0</p>`;
|
|
|
}
|
|
|
|
|
|
- getPotentialOutputType (output) {
|
|
|
- if (OutputMatching.NUM_REGEX.test(output)) {
|
|
|
- return "number";
|
|
|
- } else if (OutputMatching.BOOLEAN_REGEX.test(output)) {
|
|
|
- return "bool";
|
|
|
- } else {
|
|
|
- return "string";
|
|
|
+ static get INPUT_INFO_TEMPLATE () {
|
|
|
+ return `<span class='$0'>$1</span>`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Status code - it is not grade related!
|
|
|
+ // 0 - Succesful execution
|
|
|
+ // 1 - failed execution
|
|
|
+ constructor (
|
|
|
+ name,
|
|
|
+ status,
|
|
|
+ inputs,
|
|
|
+ result,
|
|
|
+ store,
|
|
|
+ time,
|
|
|
+ tags,
|
|
|
+ error_msg = "",
|
|
|
+ error_id = ""
|
|
|
+ ) {
|
|
|
+ this.name = name;
|
|
|
+ this.status = status;
|
|
|
+ this.inputs = inputs;
|
|
|
+ this.results = result;
|
|
|
+ this.store = store;
|
|
|
+ this.time = time;
|
|
|
+ this.error_msg = error_msg;
|
|
|
+ this.tags = tags;
|
|
|
+ this.error_id = error_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ get grade () {
|
|
|
+ if (this.results == null) {
|
|
|
+ return 0;
|
|
|
}
|
|
|
+ return (
|
|
|
+ this.results.reduce((prev, val) => prev + val.grade, 0) /
|
|
|
+ this.results.length
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- outputMatch (g_output, e_output) {
|
|
|
- if (OutputMatching.NUM_REGEX.test(e_output)) {
|
|
|
- if (!OutputMatching.NUM_REGEX.test(g_output)) {
|
|
|
- return this.checkStrings(g_output, e_output);
|
|
|
- }
|
|
|
- const g_num = new Decimal(g_output);
|
|
|
- const e_num = new Decimal(e_output);
|
|
|
- return this.checkNumbers(g_num, e_num);
|
|
|
- } else if (OutputMatching.BOOLEAN_REGEX.test(e_output)) {
|
|
|
- if (!OutputMatching.BOOLEAN_REGEX.test(g_output)) {
|
|
|
- return this.checkStrings(g_output, e_output);
|
|
|
- }
|
|
|
- const g_bool = TypeParser.toBool(g_output);
|
|
|
- const e_bool = TypeParser.toBool(e_output);
|
|
|
- return this.checkBoolean(g_bool, e_bool);
|
|
|
+ prepareResults () {
|
|
|
+ let template = OutputAssessmentResult.DETAIL_TEMPLATE;
|
|
|
+ const grade = (this.grade * 100).toFixed(2);
|
|
|
+ const time = this.time || "-";
|
|
|
+ template = template.replace(
|
|
|
+ ":test-name:",
|
|
|
+ LocalizedStrings.getUI("assessment-detail-title", [this.name + 1])
|
|
|
+ );
|
|
|
+ template = template.replace(
|
|
|
+ ":time-label:",
|
|
|
+ LocalizedStrings.getUI("assessment-detail-time-label")
|
|
|
+ );
|
|
|
+ template = template.replace(":time:", time);
|
|
|
+ template = template.replace(
|
|
|
+ ":grade-label:",
|
|
|
+ LocalizedStrings.getUI("assessment-detail-grade-label")
|
|
|
+ );
|
|
|
+ template = template.replace(":grade:", grade);
|
|
|
+ const input_spans = this.prepareInputList(this.inputs);
|
|
|
+ template = template.replace(
|
|
|
+ ":input-label:",
|
|
|
+ LocalizedStrings.getUI("assessment-detail-input-label")
|
|
|
+ );
|
|
|
+ template = template.replace(":input-list:", input_spans);
|
|
|
+ template = template.replace(
|
|
|
+ ":output-label:",
|
|
|
+ LocalizedStrings.getUI("assessment-detail-output-label")
|
|
|
+ );
|
|
|
+ if (this.status == 0) {
|
|
|
+ const output_rows = this.results.map((result) => {
|
|
|
+ if (result.type == "string") {
|
|
|
+ return this.formatString(result);
|
|
|
+ } else if (result.type == "number") {
|
|
|
+ return this.formatNumber(result);
|
|
|
+ } else {
|
|
|
+ return this.formatBool(result);
|
|
|
+ }
|
|
|
+ }, this);
|
|
|
+ template = template.replace(
|
|
|
+ ":output-result:",
|
|
|
+ this.prepareOutputTable(output_rows)
|
|
|
+ );
|
|
|
} else {
|
|
|
- return this.checkStrings(g_output, e_output);
|
|
|
+ let failed_text = OutputAssessmentResult.FAILED_TEMPLATE;
|
|
|
+ failed_text = failed_text.replace("$0", this.error_msg);
|
|
|
+ template = template.replace(":output-result:", failed_text);
|
|
|
}
|
|
|
+ return template;
|
|
|
}
|
|
|
|
|
|
- checkNumbers (g_num, e_num) {
|
|
|
- const decimalPlaces = Math.min(e_num.dp(), Config.decimalPlaces);
|
|
|
- g_num = new Decimal(g_num.toFixed(decimalPlaces, Decimal.ROUND_FLOOR));
|
|
|
- e_num = new Decimal(e_num.toFixed(decimalPlaces, Decimal.ROUND_FLOOR));
|
|
|
- const result = g_num.eq(e_num);
|
|
|
- const grade = result ? 1 : 0;
|
|
|
- return OutputResult.createNumberResult(
|
|
|
- e_num.toNumber(),
|
|
|
- g_num.toNumber(),
|
|
|
- grade
|
|
|
+ prepareInputList (input_list) {
|
|
|
+ const list = input_list.map((input) => {
|
|
|
+ let template = OutputAssessmentResult.INPUT_INFO_TEMPLATE;
|
|
|
+ template = template.replace("$1", input.value);
|
|
|
+ if (input.read) {
|
|
|
+ template = template.replace("$0", "assessment-input-read");
|
|
|
+ } else {
|
|
|
+ template = template.replace("$0", "assessment-input-unread");
|
|
|
+ }
|
|
|
+ return template;
|
|
|
+ }, this);
|
|
|
+ return list.join(LocalizedStrings.getUI("text_join_assessment_outputs"));
|
|
|
+ }
|
|
|
+
|
|
|
+ prepareOutputTable (output_rows) {
|
|
|
+ let template = OutputAssessmentResult.OUPUT_TABLE_TEMPLATE;
|
|
|
+ template = template.replace(
|
|
|
+ ":expected-label:",
|
|
|
+ LocalizedStrings.getUI("assessment-detail-expected-label")
|
|
|
);
|
|
|
+ template = template.replace(
|
|
|
+ ":generated-label:",
|
|
|
+ LocalizedStrings.getUI("assessment-detail-generated-label")
|
|
|
+ );
|
|
|
+ template = template.replace(
|
|
|
+ ":result-label:",
|
|
|
+ LocalizedStrings.getUI("assessment-detail-result-label")
|
|
|
+ );
|
|
|
+ template = template.replace(":results:", output_rows.join(""));
|
|
|
+ return template;
|
|
|
}
|
|
|
|
|
|
- checkBoolean (g_bool, e_bool) {
|
|
|
- const grade = g_bool == e_bool ? 1 : 0;
|
|
|
- const g_bool_text = TypeParser.convertBoolToString(g_bool);
|
|
|
- const e_bool_text = TypeParser.convertBoolToString(e_bool);
|
|
|
- return OutputResult.createBoolResult(e_bool_text, g_bool_text, grade);
|
|
|
+ generateOutput () {
|
|
|
+ const assessment_result = this.prepareResults();
|
|
|
+ let page = OutputAssessmentResult.PAGE_TEMPLATE;
|
|
|
+ page = page.replace(":assessment-result:", assessment_result);
|
|
|
+ page = page.replace(/(\r|\n|\t)/gm, "").replace(/> *</g, "><");
|
|
|
+ return page;
|
|
|
}
|
|
|
|
|
|
- checkStrings (g_output, e_ouput) {
|
|
|
- const assessmentList = [];
|
|
|
- let e_output_clean = e_ouput.trim();
|
|
|
- let g_output_clean = g_output.trim();
|
|
|
- if (OutputMatching.NUM_IN_STRING_REGEX.test(e_ouput)) {
|
|
|
- const expected_numbers = e_ouput.match(
|
|
|
- OutputMatching.NUM_IN_STRING_REGEX
|
|
|
- );
|
|
|
- const generated_numbers =
|
|
|
- g_output.match(OutputMatching.NUM_IN_STRING_REGEX) || [];
|
|
|
- const result = generated_numbers.map((val, i) => {
|
|
|
- if (i >= expected_numbers.length) {
|
|
|
- return OutputResult.createNumberResult(null, val, 0);
|
|
|
- }
|
|
|
- const g_val = new Decimal(val);
|
|
|
- const e_val = new Decimal(expected_numbers[i]);
|
|
|
- return this.checkNumbers(g_val, e_val);
|
|
|
- }, this);
|
|
|
- if (expected_numbers.length > generated_numbers.length) {
|
|
|
- for (
|
|
|
- let i = generated_numbers.length;
|
|
|
- i < expected_numbers.length;
|
|
|
- ++i
|
|
|
- ) {
|
|
|
- result.push(
|
|
|
- OutputResult.createNumberResult(expected_numbers[i], null, 0)
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
- e_output_clean = e_output_clean.replace(
|
|
|
- OutputMatching.NUM_IN_STRING_REGEX,
|
|
|
- ""
|
|
|
+ generateAssistantOutput (performanceText,suggestionText) {
|
|
|
+ let template = OutputAssessmentResult.ASSISTANT_TEMPLATE;
|
|
|
+ template = template.replace(":performance-text:",performanceText);
|
|
|
+ template = template.replace(":suggestion-text:",suggestionText);
|
|
|
+ let page = OutputAssessmentResult.PAGE_TEMPLATE;
|
|
|
+ page = page.replace(":assessment-result:", template);
|
|
|
+ page = page.replace(/(\r|\n|\t)/gm, "").replace(/> *</g, "><");
|
|
|
+ return page;
|
|
|
+ }
|
|
|
+
|
|
|
+ formatNumber (result) {
|
|
|
+ const result_class =
|
|
|
+ result.grade == 1
|
|
|
+ ? "assessment-number-result"
|
|
|
+ : "assessment-number-result-failed";
|
|
|
+ const template = this.formatOutput(
|
|
|
+ "assessment-number-expected",
|
|
|
+ "assessment-number-generated",
|
|
|
+ result_class,
|
|
|
+ result
|
|
|
+ );
|
|
|
+ return template;
|
|
|
+ }
|
|
|
+
|
|
|
+ formatBool (result) {
|
|
|
+ const result_class =
|
|
|
+ result.grade == 1
|
|
|
+ ? "assessment-bool-result"
|
|
|
+ : "assessment-bool-result-failed";
|
|
|
+ const template = this.formatOutput(
|
|
|
+ "assessment-bool-expected",
|
|
|
+ "assessment-bool-generated",
|
|
|
+ result_class,
|
|
|
+ result
|
|
|
+ );
|
|
|
+ return template;
|
|
|
+ }
|
|
|
+
|
|
|
+ formatOutput (expected_class, generated_class, result_class, result) {
|
|
|
+ let template = OutputAssessmentResult.OUTPUT_TEMPLATE;
|
|
|
+ template = template.replace(":class-expected:", expected_class);
|
|
|
+ template = template.replace(":class-generated:", generated_class);
|
|
|
+ template = template.replace(":class-result:", result_class);
|
|
|
+ let expected_tmpl = result.expected;
|
|
|
+ let generated_tmpl = result.generated;
|
|
|
+ if (expected_tmpl == null) {
|
|
|
+ expected_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
|
|
|
+ "$0",
|
|
|
+ LocalizedStrings.getMessage("assessment-empty-expected-tooltip")
|
|
|
);
|
|
|
- g_output_clean = g_output_clean.replace(
|
|
|
- OutputMatching.NUM_IN_STRING_REGEX,
|
|
|
- ""
|
|
|
+ } else if (generated_tmpl == null) {
|
|
|
+ generated_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
|
|
|
+ "$0",
|
|
|
+ LocalizedStrings.getMessage("assessment-empty-generated-tooltip")
|
|
|
);
|
|
|
- const numberGrade =
|
|
|
- result.reduce((prev, r) => prev + r.grade, 0) / result.length;
|
|
|
- assessmentList.push(numberGrade);
|
|
|
}
|
|
|
- if (OutputMatching.BOOLEAN_IN_STRING_REGEX.test(e_ouput)) {
|
|
|
- const expected_bools = e_ouput.match(
|
|
|
- OutputMatching.BOOLEAN_IN_STRING_REGEX
|
|
|
+ template = template.replace("$0", expected_tmpl);
|
|
|
+ template = template.replace("$1", generated_tmpl);
|
|
|
+ const final_result = result.grade == 1 ? "✓" : "✗";
|
|
|
+ template = template.replace("$2", final_result);
|
|
|
+ return template;
|
|
|
+ }
|
|
|
+
|
|
|
+ formatString (result) {
|
|
|
+ const expected_class = "assessment-string-expected";
|
|
|
+ const generated_class = "assessment-string-generated";
|
|
|
+ //const result_class = 'assessment-string-result';
|
|
|
+
|
|
|
+ let template = OutputAssessmentResult.OUTPUT_TEMPLATE;
|
|
|
+ template = template.replace(":class-expected:", expected_class);
|
|
|
+ template = template.replace(":class-generated:", generated_class);
|
|
|
+ //template = template.replace(":class-result:", result_class);
|
|
|
+
|
|
|
+ const g_string = result.generated || "";
|
|
|
+ const e_string = result.expected || "";
|
|
|
+ // console.log("generated: ", g_string,"expected: ", e_string);
|
|
|
+ let g_string_tmpl = g_string;
|
|
|
+ let e_string_tmpl = e_string;
|
|
|
+ if (result.generated == null) {
|
|
|
+ g_string_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
|
|
|
+ "$0",
|
|
|
+ LocalizedStrings.getMessage("assessment-empty-generated-tooltip")
|
|
|
);
|
|
|
- const generated_bools =
|
|
|
- g_output.match(OutputMatching.BOOLEAN_IN_STRING_REGEX) || [];
|
|
|
- const result = generated_bools.map((val, i) => {
|
|
|
- if (i >= expected_bools.length) {
|
|
|
- return OutputResult.createBoolResult(null, val, 0);
|
|
|
- }
|
|
|
- const g_bool = TypeParser.toBool(val);
|
|
|
- const e_bool = TypeParser.toBool(expected_bools[i]);
|
|
|
- return this.checkBoolean(g_bool, e_bool);
|
|
|
- }, this);
|
|
|
- if (expected_bools.length > generated_bools.length) {
|
|
|
- for (let i = generated_bools.length; i < expected_bools.length; ++i) {
|
|
|
- result.push(
|
|
|
- OutputResult.createBoolResult(expected_bools[i], null, 0)
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
- e_output_clean = e_output_clean.replace(
|
|
|
- OutputMatching.BOOLEAN_IN_STRING_REGEX,
|
|
|
- ""
|
|
|
+ } else if (result.expected == null) {
|
|
|
+ e_string_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
|
|
|
+ "$0",
|
|
|
+ LocalizedStrings.getMessage("assessment-empty-expected-tooltip")
|
|
|
+ );
|
|
|
+ }
|
|
|
+ template = template.replace("$0", e_string_tmpl);
|
|
|
+ template = template.replace("$1", g_string_tmpl);
|
|
|
+ if (result.grade == 1) {
|
|
|
+ template = template.replace("$2", "✓");
|
|
|
+ template = template.replace(":class-result:", "assessment-string-result");
|
|
|
+ } else {
|
|
|
+ const diff = StringDiff(g_string, e_string);
|
|
|
+ // console.log(diff);
|
|
|
+ const diff_vec = diff.map(
|
|
|
+ (part) => this.getDiffStringStyle(part[1], part[0]),
|
|
|
+ this
|
|
|
);
|
|
|
- g_output_clean = g_output_clean.replace(
|
|
|
- OutputMatching.BOOLEAN_IN_STRING_REGEX,
|
|
|
- ""
|
|
|
+ const diff_string = diff_vec.reduce((prev, actual) => prev + actual, "");
|
|
|
+ template = template.replace(
|
|
|
+ "$2",
|
|
|
+ "<span class='assessment-failed-case'>✗</span>" + diff_string
|
|
|
);
|
|
|
- const boolGrade =
|
|
|
- result.reduce((prev, r) => prev + r.grade, 0) / result.length;
|
|
|
- assessmentList.push(boolGrade);
|
|
|
+ template = template.replace(":class-result:", "assessment-string-diff");
|
|
|
}
|
|
|
- const dist = levenshteinDistance(g_output_clean, e_output_clean);
|
|
|
- let gradeDiff =
|
|
|
- Math.max(0, e_output_clean.length - dist) / e_output_clean.length;
|
|
|
- gradeDiff = Number.isNaN(gradeDiff) ? 0 : gradeDiff;
|
|
|
- const assessment_size = assessmentList.length + 1;
|
|
|
- const gradeAcc = assessmentList.reduce(
|
|
|
- (prev, val) => prev + val / assessment_size,
|
|
|
- 0
|
|
|
- );
|
|
|
- const finalGrade = 1 * (gradeDiff / assessment_size + gradeAcc);
|
|
|
- return OutputResult.createStringResult(e_ouput, g_output, finalGrade);
|
|
|
+ return template;
|
|
|
}
|
|
|
|
|
|
- getErrorMessage (errorID, ...args) {
|
|
|
- return LocalizedStrings.getError(errorID, args);
|
|
|
+ getDiffStringStyle (text, action) {
|
|
|
+ const template = "<span class='$0'>$1</span>";
|
|
|
+ // Fix missing whitespace when its a single element
|
|
|
+ text = text.replace(/\s/g, " ");
|
|
|
+ switch (action) {
|
|
|
+ case StringDiff.INSERT:
|
|
|
+ return template.replace("$0", "stringdiff-insert").replace("$1", text);
|
|
|
+ case StringDiff.DELETE:
|
|
|
+ return template.replace("$0", "stringdiff-delete").replace("$1", text);
|
|
|
+ case StringDiff.EQUAL:
|
|
|
+ return template.replace("$0", "stringdiff-equal").replace("$1", text);
|
|
|
+ }
|
|
|
}
|
|
|
}
|