Auto grading

USTC 自动评价 tqm.ustc.edu.cn

目前为 2023-06-17 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Auto grading
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.6.1
  5. // @description USTC 自动评价 tqm.ustc.edu.cn
  6. // @author PRO_2684
  7. // @match https://tqm.ustc.edu.cn/index.html*
  8. // @icon https://tqm.ustc.edu.cn/favicon.ico
  9. // @grant none
  10. // @license gpl-3.0
  11. // @require https://greasyfork.org/scripts/468177-%E6%95%99%E5%AD%A6%E8%B4%A8%E9%87%8F%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0%E6%A0%87%E5%87%86%E7%AD%94%E6%A1%88/code/%E6%95%99%E5%AD%A6%E8%B4%A8%E9%87%8F%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0%E6%A0%87%E5%87%86%E7%AD%94%E6%A1%88.js
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16. let menu_root;
  17. function add_item(display_name, hint, callback) {
  18. let new_item = document.createElement("li");
  19. new_item.innerText = display_name;
  20. new_item.onclick = callback;
  21. new_item.className = "ant-menu-item";
  22. new_item.title = hint;
  23. menu_root.appendChild(new_item);
  24. }
  25. function help() {
  26. alert("食用方法:\n1. 进入未完成的评价问卷\n2. 侧栏选择你想要的操作或激活快捷键\n3. 等待脚本执行\n\n快捷键说明:\n- Enter: 智能执行以下中的一项: 下一位教师/选择标准答案/提交回答\n- Shift+Enter: 全自动评教\n- Backspace: 忽略并转到下一个");
  27. }
  28. function grade() {
  29. let questions = document.querySelectorAll("[class|='index_subject']");
  30. let disabled = questions[0].querySelector(".ant-radio-wrapper-disabled");
  31. if (disabled) return false;
  32. let first_unchosen = null;
  33. questions.forEach((question) => {
  34. let required = Boolean(question.querySelector('[class|="index_necessary"]'));
  35. if (!required) return;
  36. let tmp = question.querySelector("[class|='index_title']").querySelectorAll('p');
  37. let title = tmp[tmp.length - 1].innerText;
  38. let standard_answer = standard_answers[title];
  39. console.log(`[Auto grading] ${title}: ${standard_answer}`);
  40. let chosen = false;
  41. if (standard_answer) {
  42. let options = question.querySelectorAll('[style="width: 100%;"]');
  43. for (let option of options) {
  44. let is_standard_answer = (standard_answer.indexOf(option.innerText) >= 0);
  45. if (is_standard_answer) {
  46. option.firstChild.click();
  47. chosen = true;
  48. break;
  49. }
  50. }
  51. }
  52. if (!chosen && first_unchosen == null) first_unchosen = question;
  53. });
  54. if (first_unchosen != null) {
  55. first_unchosen.scrollIntoView({ behavior: "smooth" });
  56. return false;
  57. }
  58. return true;
  59. }
  60. function ignore() {
  61. let ignore_btn = root_node.querySelector("[class|='TaskDetailsMainContent_normalButton']");
  62. if (ignore_btn && ignore_btn.parentElement.parentElement.parentElement.getAttribute('aria-hidden') == 'false') {
  63. ignore_btn.click();
  64. } else {
  65. console.log("[Auto grading] 未找到忽略按钮!");
  66. }
  67. let tabs = root_node.querySelector("[class='ant-tabs-nav-scroll']");
  68. if (tabs) {
  69. tabs = tabs.children[0].children[0];
  70. } else {
  71. console.log("[Auto grading] 未找到教师/助教列表!");
  72. return;
  73. }
  74. let flag = false;
  75. let tab;
  76. for (tab of tabs.children) {
  77. if (flag) {
  78. tab.click();
  79. break;
  80. } else if (tab.getAttribute('aria-selected') == 'true') {
  81. flag = true;
  82. }
  83. }
  84. }
  85. function auto() {
  86. if (try_click("button[class^='ant-btn ant-btn-primary']")) // Next teacher/course
  87. return true;
  88. if (grade()) { // Select standard answer
  89. try_click("button[class^='ant-btn index_submit']"); // Submit
  90. return true;
  91. }
  92. return false;
  93. }
  94. function full_auto() {
  95. while (auto()) { }
  96. alert("执行结束!");
  97. }
  98. function try_click(selector) {
  99. let btn = document.querySelector(selector);
  100. if (btn && btn.checkVisibility()) {
  101. btn.click();
  102. return true;
  103. } else {
  104. return false;
  105. }
  106. }
  107. // Side bar
  108. const root_node = document.getElementById('root');
  109. const config = { attributes: false, childList: true, subtree: true };
  110. const callback = function(mutations, observer) {
  111. menu_root = root_node.querySelector('.ant-menu-root');
  112. if (menu_root) {
  113. add_item("使用说明", "自动评教脚本使用说明", help);
  114. add_item("自动评价", "自动选择标准答案", grade);
  115. add_item("忽略并转到下一个", "(若可能)忽略当前助教并转到下一个助教", ignore);
  116. add_item("全自动评教", "(实验性功能)彻底解放双手", full_auto);
  117. observer.disconnect();
  118. }
  119. }
  120. const observer = new MutationObserver(callback);
  121. observer.observe(root_node, config);
  122. // Shortcut
  123. document.addEventListener("keyup", (e) => {
  124. if (document.activeElement.nodeName != "INPUT") { // No invoke if user is typing
  125. switch (e.key) {
  126. case "Enter":
  127. if (!e.shiftKey) {
  128. auto();
  129. } else {
  130. full_auto();
  131. }
  132. break;
  133. case "Backspace":
  134. ignore();
  135. break;
  136. default:
  137. break;
  138. }
  139. }
  140. });
  141. })();