// ==UserScript==
// @name 获取习题主考点
// @namespace http://tampermonkey.net/
// @version 1.3.0
// @description 确定多考点习题的主考点
// @author Jin
// @match http://*.com/*/report/detail/*
// @match http://*.com/*/paper/detail/*
// @match http://*.com/*/paper/make
// @match http://*.com/*/ques/search?f=1*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_download
// @grant window.confirm
// ==/UserScript==
(async function() {
'use strict';
// Your code here...
// 添加到试题篮 按钮
let newEl = document.createElement("div");
newEl.innerHTML = `<a href="javascript:void(0)" class="btn btn-orange btn-lg btn-scale" title="油猴脚本,先导入“提取的主知识点.txt”,再将“知识点-待定”的习题添加到试题篮">添加多考点习题到<i class="icon i-basket"></i></a>`
newEl = newEl.firstElementChild;
newEl.onclick = async (event)=>{ // load or add
await loadExerciseInfoListFromTxtFile();
addUnclassifiedExerciseToCart(event);
};
const referenceEl = document.querySelector("div.fixed-bottom > div");
if(referenceEl){ referenceEl.appendChild(newEl); }
// step1 载入“提取的主知识点.txt”
async function loadExerciseInfoListFromTxtFile(event){
debugger
// GM_deleteValue("autoGetMainPoint");
// exInfo=[id, 知识点, 试卷名, 题号, 题型, 全文]
let autoGetMainPoint = GM_getValue("autoGetMainPoint", {
exInfoList:[["",,,,,],],
step:0, // 是否自动处理
expirationDate:0 // 是否自动处理
});
//习题id的匹配数量
let exIds = autoGetMainPoint.exInfoList.map(a=>a[0].toLowerCase());
let includesCount = [...document.querySelectorAll("fieldset")].map( el=>el.id ).filter( a=>exIds.includes(a) ).length;
// 载入“提取的主知识点.txt”
if( includesCount === 0 ){
// 检查试题蓝是否为空,可能不准
if( !document.querySelector("#divCartBox > div > a > span:nth-child(1)").textContent.startsWith("0/") ){
showMessage("请先清空试题篮");
return;
}
// 载入文件
const textContent = await OpenAndReadLocalTxtFile("提取的主知识点.txt")
autoGetMainPoint.exInfoList = textContent.split("\n")
.map(row=>row.trimStart().trimEnd())
.filter(row=>row)
.map(row=>row.split("\t"));
// 再次 校验习题id是否全部匹配
exIds = autoGetMainPoint.exInfoList.map(a=>a[0].toLowerCase());
includesCount = [...document.querySelectorAll("fieldset")].map( el=>el.id ).filter( a=>exIds.includes(a) ).length;
if( includesCount === 0 ){
showMessage("习题id全不匹配,", false, false);
}
}
// 保存信息
await GM_setValue("autoGetMainPoint", autoGetMainPoint);
}
// step2 开始添加到试题篮
async function addUnclassifiedExerciseToCart(event){
let autoGetMainPoint = GM_getValue("autoGetMainPoint");
// 添加“知识点-待定”习题到试题篮
let count=0;
for( let fieldset of document.querySelectorAll("fieldset") ){
// 查找未分类习题
const exList = autoGetMainPoint.exInfoList.filter(exInfo=>exInfo[1]==="知识点-待定").filter( a=>a[0].toLowerCase()===fieldset.id ); // 可能有重题
if( exList.length === 0 ){ continue }
location.href = "#" + fieldset.id; // 滑动页面
await new Promise(r => setTimeout(r, 1000)); //
let el = fieldset.nextElementSibling.querySelector("div.fieldtip-right > a.add");
if( el.querySelector("i.i-subtract") ){ continue }
// 检查试题蓝是否已满
let chartCounts = document.querySelector("#divCartBox > div > a > span:nth-child(1)").textContent.split("/");
if( chartCounts[0] === chartCounts[1] ){
showMessage("试题篮已满,请组卷后再次添加");
break;
}
el.click(); // 点击添加按钮
count += 1;
// 同时保持习题全文,作为识别特征
fieldset.querySelector("div.pt1 span.qseq")?.remove();
fieldset.querySelector("div.pt1 a.ques-source")?.remove();
fieldset.querySelector("div.pt6.ques-report")?.remove();
exList.forEach(exInfo=>exInfo[5]=fieldset.textContent);
// 逐题保存信息
await GM_setValue("autoGetMainPoint", autoGetMainPoint);
await new Promise( r => setTimeout(r, 1000*2*(0.5+Math.random())) );
}
// 下载 或 组卷,需手动点击按钮
if( count===0 ){
event.target.innerHTML = "下载所有试卷<br\>的习题主知识点";
event.target.onclick = downloadAllAndClear;
}else{
event.target.innerHTML = "一键开始组卷并<br\>提取习题主知识点";
event.target.onclick = async ()=>{
// 一键 按知识点组卷
let autoGetMainPoint = GM_getValue("autoGetMainPoint", {step:0, expirationDate:0});
autoGetMainPoint.step = 3; //自动开始组卷
autoGetMainPoint.expirationDate = Date.now()+10*1000;
await GM_setValue("autoGetMainPoint", autoGetMainPoint);
// 点击 进入组卷
document.querySelector("#divCartBox > div > a").click()
}
}
}
// step3 组卷页面,不完成组卷,通过文字比对确定习题,而不是id
if( location.pathname.endsWith("/paper/make") ){
// 是否自动处理
let autoGetMainPoint = GM_getValue("autoGetMainPoint", {step:0, expirationDate:0});
if( autoGetMainPoint.step !== 3 ){ return; }
if( autoGetMainPoint.expirationDate < Date.now() ){ return; }
await new Promise(r => {console.log("等待2000ms"); setTimeout(r, 2000);}); //
// 按知识点排序
let el = document.querySelector("#group-order > span:nth-child(2)");
if( !el.classList.contains("active") ){
// 保存信息
autoGetMainPoint.step = 3;
autoGetMainPoint.expirationDate = Date.now()+10*1000;
await GM_setValue("autoGetMainPoint", autoGetMainPoint);
// 点击 按知识点 按钮
el.click()
//点击 确认 按钮,页面将会自动刷新,
await new Promise( r => setTimeout(r, 500) ); // 等待弹框
document.querySelector("div.box-wrapper input.btn.btn-blue.btn-lg").click();
return
}
// 逐 知识点 子标题
for( let exampartEl of document.querySelectorAll("#divQues div.exampart") ){
const titleEl = exampartEl.querySelector("div.questypetitle");
let pointCode = titleEl.getAttribute("cate").toLowerCase();
let pointName = GM_getValue(pointCode, "知识点-找不到/"+pointCode); // 需要先在 组卷中心 知识点挑题 页面,点击“保存2”按钮
// 逐题
for( let fieldset of exampartEl.querySelectorAll("fieldset") ){
// 清理题号、题源
fieldset.querySelector("div.pt1 span.qseq")?.remove();
fieldset.querySelector("div.pt1 a.ques-source")?.remove();
fieldset.querySelector("div.pt6.ques-report")?.remove();
// 全文匹配
let exList = autoGetMainPoint.exInfoList // 可能有重题
.filter(exInfo=>exInfo[1]==="知识点-待定")
.filter(exInfo=>exInfo[5]===fieldset.textContent );
// fieldset.textContent可能含题源,尝试取得题源
if( exList.length === 0 ){
let textContent = fieldset.textContent.split(/)(.+)/)[1]; // 去掉题源,标记为首个")"
exList = autoGetMainPoint.exInfoList // 可能有重题
.filter(exInfo=>exInfo[1]==="知识点-待定")
.filter(exInfo=>exInfo[5]===textContent );
}
// 异常
if( exList.length === 0 ){
console.log("未匹配到习题:"+fieldset.textContent);
showMessage("未匹配到习题", false, false);
}
// 添加知识点信息
exList.forEach(exInfo=>exInfo[1]=pointName+";");//“;”作为标记
}
}
// 保存信息
autoGetMainPoint.step = 0; //停止自动处理
autoGetMainPoint.expirationDate = 0;
await GM_setValue("autoGetMainPoint", autoGetMainPoint);
debugger
// 全部完成则自动下载
if( !autoGetMainPoint.exInfoList.find(exInfo=>exInfo[1]==="知识点-待定") ){
await downloadAllAndClear();
// 点击 清空试题,
await new Promise( r => setTimeout(r, 5000) ); // 等待弹框
debugger
// window.confirm=()=>true;//避免出现confirm对话框,需要手动点击
//document.querySelector("#sortBox > div.pb10 > a:nth-child(2)").click();
// QuesCart.clear(subject,null,function(){setTimeout(close, 5000)})
// QuesCart.clear 跳过 window.confirm
const [a,b,c] = [subject, null, function(){setTimeout(close, 5000)}];
$.post("/" + subject + "/paper/clearcart", {
r: Math.random()
}, function(b) {
b && 0 == b.S && (QuesCart.init(a, !0),
showMessage(b.M));
c && c();
$(".added").removeClass("added").closest(".fieldtip").prev("fieldset").css({
color: "#000"
})
})
// 跳转页面到组卷中心
}
}
// step4 下载"*.习题备注.txt"
async function downloadAllAndClear(){
let autoGetMainPoint = GM_getValue("autoGetMainPoint");
let exInfoList = autoGetMainPoint.exInfoList.slice(1);
// 按试卷下载
for( let paperName of new Set(exInfoList.map(a=>a[2])) ){
let text = exInfoList.filter( a=>a[2]===paperName )
.filter( a=>a[1].endsWith(";") ) // 新增知识点标记
.sort((a,b) => a[3]-b[3])// 按题号排序
.map( a=> a[3]+".【备注】"+a[1] ) // 如“1.【备注】*”
.join("\n");
if( !text ){ continue }
paperName = paperName.split(".").shift();
text = paperName+"答案\n"+text;
let filename = paperName+".习题备注.txt"
download(filename, text);
await new Promise(r => {console.log("等待下载:"+filename); setTimeout(r, 1000);}); //
}
// 清理
GM_deleteValue("autoGetMainPoint");
}
async function OpenAndReadLocalTxtFile(checkFileName){
const element = document.createElement('input');
element.setAttribute('type', 'file' );
element.setAttribute('accept', '.txt');
element.style.display = 'none';
document.body.appendChild(element);
let result = "";
element.onchange = async (event)=>{
if( checkFileName && event.target.files[0].name!==checkFileName ){
showMessage("文件名必须是:"+checkFileName);
return;
}
result = await event.target.files[0].text();
console.log("读取了文件:"+event.target.files[0].name);
}
element.click()
while( !result ) {
await new Promise(r => {console.log("等待读取文件"); setTimeout(r, 500);});
}
element.remove();
return result;
}
function download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
})();
// 保存 考点列表
(async function(){
'use strict';
// 组卷中心,知识点挑题
if( !/\/ques\/search/.test(location.pathname) ){ return; }
// 等待考点列表载入
await new Promise(r => {console.log("等待考点列表载入"); setTimeout(r, 500);});
while(!document.querySelector(".qseq") && !document.querySelector("#divTree") ) {
await new Promise(r => {console.log("等待考点列表载入"); setTimeout(r, 500);});
}
// 插入考点列表 保存按钮
let pointEl = document.querySelector("#divTree .tree-head");
if( pointEl && pointEl.firstElementChild.textContent==="考点列表" ){
pointEl.lastElementChild.appendChild(createSavePointButton());
console.log("插入了考点列表保存钮数。");
}
// 保存 考点列表 到 GM_setValue
function createSavePointButton(){
let newElement = document.createElement('div');
newElement.innerHTML = '<li title="油猴脚本,保存考点列表到浏览器,用于脚本:获取习题主考点">保存2</li>';
newElement = newElement.firstElementChild;
// 功能脚本
newElement.onclick = async function(event){
// 三级知识点
let points = document.querySelectorAll("#divTree2 > div > ul > li > ul > li > ul > li > a");
let text = "";
for( let a3 of points ){
let a2=a3.parentElement.parentElement.previousElementSibling; // 二级知识点
let a1=a2.parentElement.parentElement.previousElementSibling; // 一级知识点
let tagName ="知识点-" + a1.innerText+"/"+a2.innerText+"/"+a3.innerText;
tagName=tagName.replace(/\s+/g,"");
let tagCode = a3.getAttribute("pk").toLowerCase();
await GM_setValue(tagCode, tagName);
text += tagCode + "\t" + tagName+"\n";
}
// 输出到console
console.log(text);
showMessage("保存了" + points.length + "个考点到浏览器");
};
return newElement;
}
})();