assessment_result.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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 (name, status, inputs, result, store, time, error_msg = '') {
  66. this.name = name;
  67. this.status = status;
  68. this.inputs = inputs;
  69. this.results = result;
  70. this.store = store;
  71. this.time = time;
  72. this.error_msg = error_msg;
  73. }
  74. get grade () {
  75. if(this.results == null) {
  76. return 0;
  77. }
  78. return this.results.reduce((prev, val) => prev + val.grade, 0) / this.results.length;
  79. }
  80. prepareResults () {
  81. let template = OutputAssessmentResult.DETAIL_TEMPLATE;
  82. const grade = (this.grade * 100).toFixed(2);
  83. const time = this.time || "-";
  84. template = template.replace(':test-name:', LocalizedStrings.getUI('assessment-detail-title', [this.name + 1]));
  85. template = template.replace(':time-label:', LocalizedStrings.getUI('assessment-detail-time-label'));
  86. template = template.replace(':time:', time);
  87. template = template.replace(':grade-label:', LocalizedStrings.getUI('assessment-detail-grade-label'));
  88. template = template.replace(':grade:', grade);
  89. const input_spans = this.prepareInputList(this.inputs);
  90. template = template.replace(':input-label:', LocalizedStrings.getUI('assessment-detail-input-label'));
  91. template = template.replace(':input-list:', input_spans);
  92. template = template.replace(':output-label:', LocalizedStrings.getUI('assessment-detail-output-label'));
  93. if(this.status == 0) {
  94. const output_rows = this.results.map(result => {
  95. if(result.type == "string") {
  96. return this.formatString(result);
  97. } else if (result.type == "number") {
  98. return this.formatNumber(result);
  99. } else {
  100. return this.formatBool(result);
  101. }
  102. }, this);
  103. template = template.replace(':output-result:', this.prepareOutputTable(output_rows));
  104. } else {
  105. let failed_text = OutputAssessmentResult.FAILED_TEMPLATE;
  106. failed_text = failed_text.replace("$0", this.error_msg);
  107. template = template.replace(":output-result:", failed_text);
  108. }
  109. return template;
  110. }
  111. prepareInputList (input_list) {
  112. const list = input_list.map(input => {
  113. let template = OutputAssessmentResult.INPUT_INFO_TEMPLATE;
  114. template = template.replace("$1", input.value);
  115. if(input.read) {
  116. template = template.replace("$0", "assessment-input-read");
  117. } else {
  118. template = template.replace("$0", "assessment-input-unread");
  119. }
  120. return template;
  121. }, this);
  122. return list.join(LocalizedStrings.getUI('text_join_assessment_outputs'));
  123. }
  124. prepareOutputTable (output_rows) {
  125. let template = OutputAssessmentResult.OUPUT_TABLE_TEMPLATE;
  126. template = template.replace(':expected-label:', LocalizedStrings.getUI('assessment-detail-expected-label'));
  127. template = template.replace(':generated-label:', LocalizedStrings.getUI('assessment-detail-generated-label'));
  128. template = template.replace(':result-label:', LocalizedStrings.getUI('assessment-detail-result-label'));
  129. template = template.replace(':results:', output_rows.join(''));
  130. return template;
  131. }
  132. generateOutput () {
  133. const assessment_result = this.prepareResults();
  134. let page = OutputAssessmentResult.PAGE_TEMPLATE;
  135. page = page.replace(':assessment-result:', assessment_result);
  136. page = page.replace(/(\r|\n|\t)/gm,'').replace(/> *</g, '><');
  137. return page;
  138. }
  139. formatNumber (result) {
  140. const result_class = result.grade == 1 ? 'assessment-number-result' : 'assessment-number-result-failed';
  141. let template = this.formatOutput('assessment-number-expected',
  142. 'assessment-number-generated', result_class, result);
  143. return template
  144. }
  145. formatBool (result) {
  146. const result_class = result.grade == 1 ? 'assessment-bool-result' : 'assessment-bool-result-failed';
  147. let template = this.formatOutput('assessment-bool-expected',
  148. 'assessment-bool-generated', result_class, result);
  149. return template
  150. }
  151. formatOutput (expected_class, generated_class, result_class, result) {
  152. let template = OutputAssessmentResult.OUTPUT_TEMPLATE;
  153. template = template.replace(":class-expected:", expected_class);
  154. template = template.replace(":class-generated:", generated_class);
  155. template = template.replace(":class-result:", result_class);
  156. let expected_tmpl = result.expected;
  157. let generated_tmpl = result.generated;
  158. if(expected_tmpl == null) {
  159. expected_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace('$0',
  160. LocalizedStrings.getMessage('assessment-empty-expected-tooltip'));
  161. } else if(generated_tmpl == null) {
  162. generated_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace('$0',
  163. LocalizedStrings.getMessage('assessment-empty-generated-tooltip'));
  164. }
  165. template = template.replace("$0", expected_tmpl);
  166. template = template.replace("$1", generated_tmpl);
  167. const final_result = result.grade == 1 ? "✓" : "✗"
  168. template = template.replace("$2", final_result);
  169. return template
  170. }
  171. formatString (result) {
  172. const expected_class = 'assessment-string-expected';
  173. const generated_class = 'assessment-string-generated';
  174. //const result_class = 'assessment-string-result';
  175. let template = OutputAssessmentResult.OUTPUT_TEMPLATE;
  176. template = template.replace(":class-expected:", expected_class);
  177. template = template.replace(":class-generated:", generated_class);
  178. //template = template.replace(":class-result:", result_class);
  179. const g_string = result.generated || "";
  180. const e_string = result.expected || "";
  181. template = template.replace("$0", result.expected);
  182. template = template.replace("$1", result.generated);
  183. if(result.grade == 1) {
  184. template = template.replace("$2", "✓");
  185. template = template.replace(":class-result:", 'assessment-string-result');
  186. } else {
  187. const diff = StringDiff(g_string, e_string);
  188. const diff_vec = diff.map(part => this.getDiffStringStyle(part[1], part[0]), this);
  189. const diff_string = diff_vec.reduce((prev, actual) => prev + actual, "");
  190. template = template.replace("$2", "<span class='assessment-failed-case'>✗</span>" + diff_string);
  191. template = template.replace(":class-result:", "assessment-string-diff");
  192. }
  193. return template;
  194. }
  195. getDiffStringStyle (text, action) {
  196. const template = "<span class='$0'>$1</span>"
  197. switch(action) {
  198. case StringDiff.INSERT:
  199. return template.replace("$0", "stringdiff-insert").replace("$1", text);
  200. case StringDiff.DELETE:
  201. return template.replace("$0", "stringdiff-delete").replace("$1", text);
  202. case StringDiff.EQUAL:
  203. return template.replace("$0", "stringdiff-equal").replace("$1", text);
  204. }
  205. }
  206. }