确定多考点习题的主考点
// ==UserScript== // @name 获取习题主考点 // @namespace http://tampermonkey.net/ // @version 1.3.2 // @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(); // 删除开头的分数,如“(5分)” let textContent = fieldset.textContent; textContent = textContent.replace(/^(\d+分)/,""); exList.forEach(exInfo=>exInfo[5]=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 questypeEl of document.querySelectorAll("#divQues div.questype") ){ const titleEl = questypeEl.querySelector("div.questypetitle"); let pointCode = titleEl.getAttribute("cate").toLowerCase(); let pointName = GM_getValue(pointCode, "知识点-找不到/"+pointCode); // 需要先在 组卷中心 知识点挑题 页面,点击“保存2”按钮 // 逐题 for( let fieldset of questypeEl.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; } })();