assessment_result.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. import StringDiff from "./../../util/string_diff";
  2. import { LocalizedStrings } from "./../../services/localizedStringsService";
  3. export class OutputAssessmentResult {
  4. static get PAGE_TEMPLATE () {
  5. return `<!DOCTYPE html>
  6. <html>
  7. <head>
  8. <meta http-equiv='content-type' content='text/html; charset=UTF-8'>
  9. <link rel='stylesheet' href='css/ivprog-assessment.css' type='text/css'/>
  10. </head>
  11. <body>
  12. :assessment-result:
  13. </body>
  14. </html>`;
  15. }
  16. static get DETAIL_TEMPLATE () {
  17. return `<div class='details-body'>
  18. <div class='details-header'>
  19. <h2>:test-name:</h2>
  20. <p>:time-label:: <span>:time:ms</span></p>
  21. <p>:grade-label:: <span>:grade:%</span></p>
  22. </div>
  23. <div>
  24. <h3>:input-label:</h3>
  25. <ul>
  26. <li>:input-list:</li>
  27. </ul>
  28. </div>
  29. <div>
  30. <h3>:output-label:</h3>
  31. :output-result:
  32. </div>
  33. </div>`;
  34. }
  35. static get OUPUT_TABLE_TEMPLATE () {
  36. return `<div class='detaisl-div-table'>
  37. <table class='assessment-output-table'>
  38. <tr>
  39. <th>:expected-label:</th>
  40. <th>:generated-label:</th>
  41. <th>:result-label:</th>
  42. </tr>
  43. :results:
  44. </table>
  45. </div>`;
  46. }
  47. static get OUTPUT_TEMPLATE () {
  48. return `<tr><td class=':class-expected:'>$0</td>
  49. <td class=':class-generated:'>$1</td>
  50. <td class=':class-result:'>$2</td></tr>`;
  51. }
  52. static get EMPTY_OUTPUT_TEMPLATE () {
  53. return `<div class='assessment-popup'><img class='assessment-empty-output' src='img/empty.svg'>
  54. <span class='assessment-popuptext'>$0</span></div>`;
  55. }
  56. static get FAILED_TEMPLATE () {
  57. return `<p class='assessment-failed-execution'><span class='assessment-failed-case'>✗</span>$0</p>`;
  58. }
  59. static get INPUT_INFO_TEMPLATE () {
  60. return `<span class='$0'>$1</span>`;
  61. }
  62. // Status code - it is not grade related!
  63. // 0 - Succesful execution
  64. // 1 - failed execution
  65. constructor (
  66. name,
  67. status,
  68. inputs,
  69. result,
  70. store,
  71. time,
  72. tags,
  73. error_msg = "",
  74. error_id = ""
  75. ) {
  76. this.name = name;
  77. this.status = status;
  78. this.inputs = inputs;
  79. this.results = result;
  80. this.store = store;
  81. this.time = time;
  82. this.error_msg = error_msg;
  83. this.tags = tags;
  84. this.error_id = error_id;
  85. }
  86. get grade () {
  87. if (this.results == null) {
  88. return 0;
  89. }
  90. return (
  91. this.results.reduce((prev, val) => prev + val.grade, 0) /
  92. this.results.length
  93. );
  94. }
  95. prepareResults () {
  96. let template = OutputAssessmentResult.DETAIL_TEMPLATE;
  97. const grade = (this.grade * 100).toFixed(2);
  98. const time = this.time || "-";
  99. template = template.replace(
  100. ":test-name:",
  101. LocalizedStrings.getUI("assessment-detail-title", [this.name + 1])
  102. );
  103. template = template.replace(
  104. ":time-label:",
  105. LocalizedStrings.getUI("assessment-detail-time-label")
  106. );
  107. template = template.replace(":time:", time);
  108. template = template.replace(
  109. ":grade-label:",
  110. LocalizedStrings.getUI("assessment-detail-grade-label")
  111. );
  112. template = template.replace(":grade:", grade);
  113. const input_spans = this.prepareInputList(this.inputs);
  114. template = template.replace(
  115. ":input-label:",
  116. LocalizedStrings.getUI("assessment-detail-input-label")
  117. );
  118. template = template.replace(":input-list:", input_spans);
  119. template = template.replace(
  120. ":output-label:",
  121. LocalizedStrings.getUI("assessment-detail-output-label")
  122. );
  123. if (this.status == 0) {
  124. const output_rows = this.results.map((result) => {
  125. if (result.type == "string") {
  126. return this.formatString(result);
  127. } else if (result.type == "number") {
  128. return this.formatNumber(result);
  129. } else {
  130. return this.formatBool(result);
  131. }
  132. }, this);
  133. template = template.replace(
  134. ":output-result:",
  135. this.prepareOutputTable(output_rows)
  136. );
  137. } else {
  138. let failed_text = OutputAssessmentResult.FAILED_TEMPLATE;
  139. failed_text = failed_text.replace("$0", this.error_msg);
  140. template = template.replace(":output-result:", failed_text);
  141. }
  142. return template;
  143. }
  144. prepareInputList (input_list) {
  145. const list = input_list.map((input) => {
  146. let template = OutputAssessmentResult.INPUT_INFO_TEMPLATE;
  147. template = template.replace("$1", input.value);
  148. if (input.read) {
  149. template = template.replace("$0", "assessment-input-read");
  150. } else {
  151. template = template.replace("$0", "assessment-input-unread");
  152. }
  153. return template;
  154. }, this);
  155. return list.join(LocalizedStrings.getUI("text_join_assessment_outputs"));
  156. }
  157. prepareOutputTable (output_rows) {
  158. let template = OutputAssessmentResult.OUPUT_TABLE_TEMPLATE;
  159. template = template.replace(
  160. ":expected-label:",
  161. LocalizedStrings.getUI("assessment-detail-expected-label")
  162. );
  163. template = template.replace(
  164. ":generated-label:",
  165. LocalizedStrings.getUI("assessment-detail-generated-label")
  166. );
  167. template = template.replace(
  168. ":result-label:",
  169. LocalizedStrings.getUI("assessment-detail-result-label")
  170. );
  171. template = template.replace(":results:", output_rows.join(""));
  172. return template;
  173. }
  174. generateOutput () {
  175. const assessment_result = this.prepareResults();
  176. let page = OutputAssessmentResult.PAGE_TEMPLATE;
  177. page = page.replace(":assessment-result:", assessment_result);
  178. page = page.replace(/(\r|\n|\t)/gm, "").replace(/> *</g, "><");
  179. return page;
  180. }
  181. formatNumber (result) {
  182. const result_class =
  183. result.grade == 1
  184. ? "assessment-number-result"
  185. : "assessment-number-result-failed";
  186. const template = this.formatOutput(
  187. "assessment-number-expected",
  188. "assessment-number-generated",
  189. result_class,
  190. result
  191. );
  192. return template;
  193. }
  194. formatBool (result) {
  195. const result_class =
  196. result.grade == 1
  197. ? "assessment-bool-result"
  198. : "assessment-bool-result-failed";
  199. const template = this.formatOutput(
  200. "assessment-bool-expected",
  201. "assessment-bool-generated",
  202. result_class,
  203. result
  204. );
  205. return template;
  206. }
  207. formatOutput (expected_class, generated_class, result_class, result) {
  208. let template = OutputAssessmentResult.OUTPUT_TEMPLATE;
  209. template = template.replace(":class-expected:", expected_class);
  210. template = template.replace(":class-generated:", generated_class);
  211. template = template.replace(":class-result:", result_class);
  212. let expected_tmpl = result.expected;
  213. let generated_tmpl = result.generated;
  214. if (expected_tmpl == null) {
  215. expected_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
  216. "$0",
  217. LocalizedStrings.getMessage("assessment-empty-expected-tooltip")
  218. );
  219. } else if (generated_tmpl == null) {
  220. generated_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
  221. "$0",
  222. LocalizedStrings.getMessage("assessment-empty-generated-tooltip")
  223. );
  224. }
  225. template = template.replace("$0", expected_tmpl);
  226. template = template.replace("$1", generated_tmpl);
  227. const final_result = result.grade == 1 ? "✓" : "✗";
  228. template = template.replace("$2", final_result);
  229. return template;
  230. }
  231. formatString (result) {
  232. const expected_class = "assessment-string-expected";
  233. const generated_class = "assessment-string-generated";
  234. //const result_class = 'assessment-string-result';
  235. let template = OutputAssessmentResult.OUTPUT_TEMPLATE;
  236. template = template.replace(":class-expected:", expected_class);
  237. template = template.replace(":class-generated:", generated_class);
  238. //template = template.replace(":class-result:", result_class);
  239. const g_string = result.generated || "";
  240. const e_string = result.expected || "";
  241. // console.log("generated: ", g_string,"expected: ", e_string);
  242. let g_string_tmpl = g_string;
  243. let e_string_tmpl = e_string;
  244. if (result.generated == null) {
  245. g_string_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
  246. "$0",
  247. LocalizedStrings.getMessage("assessment-empty-generated-tooltip")
  248. );
  249. } else if (result.expected == null) {
  250. e_string_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace(
  251. "$0",
  252. LocalizedStrings.getMessage("assessment-empty-expected-tooltip")
  253. );
  254. }
  255. template = template.replace("$0", e_string_tmpl);
  256. template = template.replace("$1", g_string_tmpl);
  257. if (result.grade == 1) {
  258. template = template.replace("$2", "✓");
  259. template = template.replace(":class-result:", "assessment-string-result");
  260. } else {
  261. const diff = StringDiff(g_string, e_string);
  262. // console.log(diff);
  263. const diff_vec = diff.map(
  264. (part) => this.getDiffStringStyle(part[1], part[0]),
  265. this
  266. );
  267. const diff_string = diff_vec.reduce((prev, actual) => prev + actual, "");
  268. template = template.replace(
  269. "$2",
  270. "<span class='assessment-failed-case'>✗</span>" + diff_string
  271. );
  272. template = template.replace(":class-result:", "assessment-string-diff");
  273. }
  274. return template;
  275. }
  276. getDiffStringStyle (text, action) {
  277. const template = "<span class='$0'>$1</span>";
  278. // Fix missing whitespace when its a single element
  279. text = text.replace(/\s/g, "&#160;");
  280. switch (action) {
  281. case StringDiff.INSERT:
  282. return template.replace("$0", "stringdiff-insert").replace("$1", text);
  283. case StringDiff.DELETE:
  284. return template.replace("$0", "stringdiff-delete").replace("$1", text);
  285. case StringDiff.EQUAL:
  286. return template.replace("$0", "stringdiff-equal").replace("$1", text);
  287. }
  288. }
  289. }