|
@@ -1,332 +1,279 @@
|
|
-import StringDiff from "./../../util/string_diff";
|
|
|
|
-import { LocalizedStrings } from "./../../services/localizedStringsService";
|
|
|
|
|
|
+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";
|
|
|
|
|
|
-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>`;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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 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 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>`;
|
|
|
|
- }
|
|
|
|
|
|
+const LocalizedStrings = LocalizedStringsService.getInstance();
|
|
|
|
|
|
- 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>`;
|
|
|
|
|
|
+export class OutputMatching {
|
|
|
|
+ static get NUM_REGEX () {
|
|
|
|
+ return /^[+-]?([0-9]+([.][0-9]*)?(e[+-]?[0-9]+)?)$/;
|
|
}
|
|
}
|
|
|
|
|
|
- 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>`;
|
|
|
|
|
|
+ static get NUM_IN_STRING_REGEX () {
|
|
|
|
+ return /[+-]?([0-9]+([.][0-9]*)?(e[+-]?[0-9]+)?)/g;
|
|
}
|
|
}
|
|
|
|
|
|
- static get FAILED_TEMPLATE () {
|
|
|
|
- return `<p class='assessment-failed-execution'><span class='assessment-failed-case'>✗</span>$0</p>`;
|
|
|
|
|
|
+ static get BOOLEAN_REGEX () {
|
|
|
|
+ const str = `^(${LocalizedStrings.getUI(
|
|
|
|
+ "logic_value_true"
|
|
|
|
+ )}|${LocalizedStrings.getUI("logic_value_false")})$`;
|
|
|
|
+ return new RegExp(str);
|
|
}
|
|
}
|
|
|
|
|
|
- static get INPUT_INFO_TEMPLATE () {
|
|
|
|
- return `<span class='$0'>$1</span>`;
|
|
|
|
|
|
+ static get BOOLEAN_IN_STRING_REGEX () {
|
|
|
|
+ const str = `(${LocalizedStrings.getUI(
|
|
|
|
+ "logic_value_true"
|
|
|
|
+ )}|${LocalizedStrings.getUI("logic_value_false")})`;
|
|
|
|
+ return new RegExp(str, "g");
|
|
}
|
|
}
|
|
|
|
|
|
- // 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;
|
|
|
|
|
|
+ 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;
|
|
}
|
|
}
|
|
|
|
|
|
- get grade () {
|
|
|
|
- if (this.results == null) {
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- return (
|
|
|
|
- this.results.reduce((prev, val) => prev + val.grade, 0) /
|
|
|
|
- this.results.length
|
|
|
|
- );
|
|
|
|
|
|
+ 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
|
|
|
|
+ );
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
- 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)
|
|
|
|
- );
|
|
|
|
|
|
+ getPotentialOutputType (output) {
|
|
|
|
+ if (OutputMatching.NUM_REGEX.test(output)) {
|
|
|
|
+ return "number";
|
|
|
|
+ } else if (OutputMatching.BOOLEAN_REGEX.test(output)) {
|
|
|
|
+ return "bool";
|
|
} else {
|
|
} else {
|
|
- let failed_text = OutputAssessmentResult.FAILED_TEMPLATE;
|
|
|
|
- failed_text = failed_text.replace("$0", this.error_msg);
|
|
|
|
- template = template.replace(":output-result:", failed_text);
|
|
|
|
|
|
+ return "string";
|
|
}
|
|
}
|
|
- return template;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- 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");
|
|
|
|
|
|
+ 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);
|
|
}
|
|
}
|
|
- 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;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
|
|
+ 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);
|
|
|
|
+ } else {
|
|
|
|
+ return this.checkStrings(g_output, e_output);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- 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
|
|
|
|
|
|
+ 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
|
|
);
|
|
);
|
|
- 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;
|
|
|
|
|
|
+ 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);
|
|
}
|
|
}
|
|
|
|
|
|
- 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")
|
|
|
|
|
|
+ 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
|
|
);
|
|
);
|
|
- } else if (generated_tmpl == null) {
|
|
|
|
- generated_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
|
|
|
|
- "$0",
|
|
|
|
- LocalizedStrings.getMessage("assessment-empty-generated-tooltip")
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- 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_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,
|
|
|
|
+ ""
|
|
);
|
|
);
|
|
- } else if (result.expected == null) {
|
|
|
|
- e_string_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,
|
|
|
|
+ ""
|
|
);
|
|
);
|
|
|
|
+ const numberGrade =
|
|
|
|
+ result.reduce((prev, r) => prev + r.grade, 0) / result.length;
|
|
|
|
+ assessmentList.push(numberGrade);
|
|
}
|
|
}
|
|
- 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
|
|
|
|
|
|
+ if (OutputMatching.BOOLEAN_IN_STRING_REGEX.test(e_ouput)) {
|
|
|
|
+ const expected_bools = e_ouput.match(
|
|
|
|
+ OutputMatching.BOOLEAN_IN_STRING_REGEX
|
|
|
|
+ );
|
|
|
|
+ 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,
|
|
|
|
+ ""
|
|
);
|
|
);
|
|
- const diff_string = diff_vec.reduce((prev, actual) => prev + actual, "");
|
|
|
|
- template = template.replace(
|
|
|
|
- "$2",
|
|
|
|
- "<span class='assessment-failed-case'>✗</span>" + diff_string
|
|
|
|
|
|
+ g_output_clean = g_output_clean.replace(
|
|
|
|
+ OutputMatching.BOOLEAN_IN_STRING_REGEX,
|
|
|
|
+ ""
|
|
);
|
|
);
|
|
- template = template.replace(":class-result:", "assessment-string-diff");
|
|
|
|
|
|
+ const boolGrade =
|
|
|
|
+ result.reduce((prev, r) => prev + r.grade, 0) / result.length;
|
|
|
|
+ assessmentList.push(boolGrade);
|
|
}
|
|
}
|
|
- return template;
|
|
|
|
|
|
+ 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);
|
|
}
|
|
}
|
|
|
|
|
|
- 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);
|
|
|
|
- }
|
|
|
|
|
|
+ getErrorMessage (errorID, ...args) {
|
|
|
|
+ return LocalizedStrings.getError(errorID, args);
|
|
}
|
|
}
|
|
}
|
|
}
|