您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
USTC 自动评价 tqm.ustc.edu.cn
当前为
- // ==UserScript==
- // @name Auto grading
- // @namespace http://tampermonkey.net/
- // @version 0.6.4
- // @description USTC 自动评价 tqm.ustc.edu.cn
- // @author PRO_2684
- // @match https://tqm.ustc.edu.cn/index.html*
- // @icon https://tqm.ustc.edu.cn/favicon.ico
- // @grant none
- // @license gpl-3.0
- // @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
- // ==/UserScript==
- (function () {
- 'use strict';
- let menu_root;
- function clean(str) {
- // Remove spaces
- str = str.replace(/\s+/g, "");
- // Remove leading asterisk
- if (str[0] == '*') str = str.slice(1);
- // Remove leading serial number
- str = str.replace(/^\d*\./, "");
- // Remove "(单选题)"/"(多选题)"
- str = str.replace("(单选题)", "");
- str = str.replace("(多选题)", "");
- return str;
- }
- function add_item(display_name, hint, callback) {
- const new_item = menu_root.appendChild(document.createElement("li"));
- new_item.innerText = display_name;
- new_item.onclick = callback;
- new_item.className = "ant-menu-item";
- new_item.title = hint;
- }
- function help() {
- alert("食用方法:\n1. 进入未完成的评价问卷\n2. 侧栏选择你想要的操作或激活快捷键\n3. 等待脚本执行\n\n快捷键说明:\n- Enter: 智能执行以下中的一项: 下一位教师/选择标准答案/提交回答\n- Shift+Enter: 全自动评教\n- Backspace: 忽略并转到下一个");
- }
- function grade() {
- const questions = document.querySelectorAll("[class^='index_subject-']");
- const disabled = questions[0].querySelector(".ant-radio-wrapper-disabled");
- if (disabled) return false;
- let first_unchosen = null;
- questions.forEach((question) => {
- const required = Boolean(question.querySelector('[class^="index_necessary"]'));
- if (!required) return;
- const tmp = question.querySelector("[class^='index_title']");
- const remark = tmp.querySelector("[class^='index_remarks-']");
- const title = remark?.textContent || clean(tmp.querySelector("[class^='index_richTextContent']").textContent);
- const standard_answer = standard_answers[title];
- console.log(`[Auto grading] ${title}: ${standard_answer}`);
- let chosen = false;
- if (standard_answer) {
- const options = question.querySelectorAll('[style="width: 100%;"]');
- for (const option of options) {
- const is_standard_answer = (standard_answer.indexOf(option.innerText) >= 0);
- if (is_standard_answer) {
- option.firstChild.click();
- chosen = true;
- // break; // Compatible for multiple answers
- }
- }
- }
- if (!chosen && first_unchosen == null) first_unchosen = question;
- });
- if (first_unchosen != null) {
- first_unchosen.scrollIntoView({ behavior: "smooth" });
- return false;
- }
- return true;
- }
- function ignore() {
- const ignore_btn = root_node.querySelector("[class^='TaskDetailsMainContent_normalButton']");
- if (ignore_btn && ignore_btn.parentElement.parentElement.parentElement.getAttribute('aria-hidden') == 'false') {
- ignore_btn.click();
- } else {
- console.log("[Auto grading] 未找到忽略按钮!");
- }
- const tabs = root_node.querySelector("[class='ant-tabs-nav-scroll']");
- if (tabs) {
- tabs = tabs.children[0].children[0];
- } else {
- console.log("[Auto grading] 未找到教师/助教列表!");
- return;
- }
- let flag = false;
- let tab;
- for (tab of tabs.children) {
- if (flag) {
- tab.click();
- break;
- } else if (tab.getAttribute('aria-selected') == 'true') {
- flag = true;
- }
- }
- }
- function auto() {
- if (try_click("button[class^='ant-btn ant-btn-primary']")) // Next teacher/course
- return true;
- if (grade()) { // Select standard answer
- try_click("button[class^='ant-btn index_submit']"); // Submit
- return true;
- }
- return false;
- }
- function full_auto() {
- // while (auto()) { }
- const timer = window.setInterval(() => {
- if (!auto()) {
- window.clearInterval(timer);
- alert("执行结束!");
- }
- }, 500);
- }
- function dump() {
- const questions = document.querySelectorAll("[class^='index_subject-']");
- const disabled = questions[0].querySelector(".ant-radio-wrapper-disabled");
- if (disabled) return false;
- let data = {};
- questions.forEach((question) => {
- const required = Boolean(question.querySelector('[class^="index_necessary"]'));
- if (!required) return;
- const tmp = question.querySelector("[class^='index_title']");
- const remark = tmp.querySelector("[class^='index_remarks-']");
- const title = remark?.textContent || clean(tmp.querySelector("[class^='index_richTextContent']").textContent);
- const options = question.querySelectorAll('[style="width: 100%;"]');
- data[title] = [];
- for (const option of options) {
- data[title].push(option.innerText);
- }
- });
- console.log(JSON.stringify(data));
- }
- function try_click(selector) {
- const ele = document.querySelector(selector);
- if (ele && ele.checkVisibility()) {
- ele.click();
- return true;
- } else {
- return false;
- }
- }
- // Side bar
- const root_node = document.getElementById('root');
- const config = { attributes: false, childList: true, subtree: true };
- const callback = function (mutations, observer) {
- menu_root = root_node.querySelector('.ant-menu-root');
- if (menu_root) {
- observer.disconnect();
- add_item("使用说明", "自动评教脚本使用说明", help);
- add_item("自动评价", "自动选择标准答案", grade);
- add_item("忽略并转到下一个", "(若可能)忽略当前助教并转到下一个助教", ignore);
- add_item("全自动评教", "(实验性功能)彻底解放双手", full_auto);
- add_item("输出答案", "(调试用)输出当前问卷的所有答案", dump);
- }
- }
- const observer = new MutationObserver(callback);
- observer.observe(root_node, config);
- // Shortcut
- document.addEventListener("keyup", (e) => {
- if (document.activeElement.nodeName != "INPUT" || document.activeElement.nodeName != "TEXTAREA") { // Don't trigger when typing
- switch (e.key) {
- case "Enter":
- if (!e.shiftKey) {
- auto();
- } else {
- full_auto();
- }
- break;
- case "Backspace":
- ignore();
- break;
- default:
- break;
- }
- }
- });
- })();