以下のコードは、主に Moodle 2.9 を対象としています。
- Gist / kou1okada / Moodle2LoginAutoFocusUsername.js
小テストの先頭にラベル等を配置し、HTML 編集モードにして以下のようなコードを埋め込んでおくと解答の文字数を表示できる。
<script> (function(){ function countChars() { if (location.href.match('/mod/quiz/review.php')) { [].forEach.call(document.querySelectorAll(".answer"),function(e){ let d = document.createElement("div"); d.textContent = "文字数:"+ e.textContent.length; e.parentElement.insertBefore(d, e.nextSibling); }); } } function onDOMContentLoaded(f) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', f); } else { f(); } } onDOMContentLoaded(countChars); })(); </script>
「課題モジュール」の「すべての提出を表示/評定する」の画面を CSV 化する。
javascript:(function(){ function copyToClipboard(s) { let t = document.createElement("textarea"); document.body.appendChild(t); t.value = s; t.focus(); t.select(); setTimeout(()=>t.parentNode.removeChild(t),0); return document.execCommand("copy"); } function copyDialog(s) { let d = document.createElement("div"); Object.assign(d.style, {position: "absolute", top: 0, left:0, width: "100%", height: "100%", border: "1px solid red", background: "white", zIndex: Number.MAX_SAFE_INTEGER}); document.body.insertBefore(d, document.body.firstChild); d.appendChild(document.createTextNode("Copy to Clipboard")); let t = document.createElement("textarea"); Object.assign(t.style, {width: "100%", height: "20em"}); d.appendChild(t); t.value = s; t.focus(); t.select(); let copy = document.createElement("input"); Object.assign(copy, {type: "button", value: "copy"}); copy.addEventListener("click", ()=>{copyToClipboard(s);d.parentNode.removeChild(d);}); d.appendChild(copy); let cancel = document.createElement("input"); Object.assign(cancel, {type: "button", value: "cancel"}); cancel.addEventListener("click", ()=>d.parentNode.removeChild(d)); d.appendChild(cancel); } let rows = document.querySelector(".paging").parentNode.querySelector("table tbody").rows; let s = [].map.call(rows, function({cells}){ let ret = [2,3,7,8].map(i=>cells[i].textContent); if (ret[2].match(/([0-9]+).*?([0-9]+).*?([0-9]+).*?([0-9]+):([0-9]+)/)) ret[2] = `${RegExp.$1}/${RegExp.$2}/${RegExp.$3} ${RegExp.$4}:${RegExp.$5}`; return ret; }).join("\n"); copyDialog(s); })();
「自動出欠」活動の「出欠表」タブで出席者の一覧を得る
(function(){ var s = [].reduce.call(document.querySelectorAll(".generaltable tbody tr"),(r,e)=>{ let c2 = e.querySelector(".c2").textContent; let radioname = e.querySelector("input").name; let time = e.querySelector(".c11").textContent; let remarks = e.querySelector("input[name^='remarks']").value; let attend = document.querySelector("form[name='takeattend']").elements[radioname].value; let reg = /[PLE]/; // PLEXY=出遅早欠未 let key={P:1,L:2,E:"",X:3,Y:""}; if (attend.match(reg)) r.push(`${c2}\t${attend}\t${key[attend]}\t${time}\t${remarks}`); return r; },[]).join("\n"); console.log(`${s}`); console.log(s.split("\n").length); return ""; })();
「ナビゲーション」→「現在のコース」→「...」→「参加者」に連番を振る
(function(){ let i = 1; [].forEach.call(document.querySelectorAll("#participants tbody tr td:first-of-type"),e=>e.insertBefore(document.createTextNode(i++), e.firstChild)); })();
小テスト作文問題の手動採点で文字数を表示する。
(function(){ let e = document.querySelectorAll(".qtype_essay_response"); [].forEach.call(e, e=>e.parentNode.appendChild(document.createTextNode(`文字数:${e.textContent.length}`))); })();
小テスト作文問題の手動採点で文字数について人数、平均、パーセンタイル、標準偏差、標準誤差、信頼区間と、各解答の文字数、順位、パーセンタイルを表示する。
(function(){ function quantile(x, probs) { return probs.map(v=>{ let i = (x.length - 1) * v; return (x[Math.floor(i)] + x[Math.ceil(i)]) / 2; }); } function round(x, p) { let y = 10**(p|0); return Math.round(x*y)/y; } function fmti(x, w, pad) { w = Math.max(w|0, `${x}`.length); pad = pad == null ? " " : `${pad}`; return `${Array(w).fill(pad).join("")}${x}`.slice(-w); } function fmtf(x, w, p, pad) { x = round(x,p); let rpart = `${x}`.replace(/^[-+]?[0-9]+/,"").length; pad = pad == null ? " " : `${pad}`; if (pad != " " && rpart == 0) {x = `${x}.`; rpart++} console.log(p - rpart + 1, p, rpart - 1); if (rpart - 1 < p) x += Array(p - rpart + 1).fill(pad).join("").slice(0, p - rpart + 1); return fmti(x, w, pad); } [].forEach.call(document.querySelectorAll(".nchars-container, .stat"), e=>e.parentNode.removeChild(e)); let e = document.querySelectorAll(".qtype_essay_response"); [].forEach.call(e, e=>{ let nchars = document.createElement("span"); nchars.classList.add("nchars"); nchars.textContent = e.textContent.length; let div = document.createElement("div"); div.classList.add("nchars-container"); div.textContent = "文字数: "; div.appendChild(nchars); e.parentNode.appendChild(div); }); let spans = document.querySelectorAll(".nchars"); let n = spans.length; let s = [].reduce.call(spans, (r,e)=>r+parseInt(e.textContent) ,0) / n; let varp = [].reduce.call(spans, (r,e)=>r+parseInt(e.textContent)**2,0) / n - s**2; let vars = varp * n / (n - 1); let sdp = Math.sqrt(varp); let sds = Math.sqrt(vars); let se = sds / Math.sqrt(n); let conf95 = se * 1.959964; let ncharss = [].map.call(spans, e=>parseInt(e.textContent)).sort((a,b)=>b-a); // 降順sort let stat = document.createElement("pre"); stat.classList.add("stat") stat.textContent = "" + `n = ${fmti(n,3)} [${[0, 0.25, 0.5, 0.75, 1].map(v=>fmtf((n-1)*v+1,6,2)).join(", ") }]\n` + `s = ${fmtf(s,5,1)} [${quantile(ncharss, [0, 0.25, 0.5, 0.75, 1]).map(v=>fmtf(v,6,2)).join(", ")}]\n` + `sdp = ${fmtf(sdp,5,1)}\n` + `sds = ${fmtf(sds,5,1)}\n` + `se = ${fmtf(se,5,1)}\n` + `95% conf = ${fmtf(s,5,1)} ± ${round(conf95,1)} [${fmtf(s-conf95,5,1)}, ${fmtf(s+conf95,5,1)}]\n` + ""; Object.assign(stat.style, {position: "fixed", zIndex: 999}); document.body.insertBefore(stat, document.body.firstChild); [].forEach.call(spans, e=>{ let nchar = parseInt(e.textContent); let ord = ncharss.indexOf(nchars) + 1; let percent = round((n - ord) * 100 / n,1); let txt = document.createTextNode(` : ${ord} : ${percent}%`); e.parentNode.appendChild(txt); }); })();
小テストの提出一覧画面で、checkbox の後ろに index を付与して、提出数の確認を容易にします。
[].forEach.call(document.querySelectorAll("#attempts input[type='checkbox']"),(e, i)=>{ let t = document.createTextNode(i+1); e.parentNode.insertBefore(t, e.nextSibling); });
ワークショップの評価フェーズにて、相互評価の未提出者と未提出数の一覧を得ます。
(function(){ let never={}; [].reduce.call(document.querySelectorAll(".grading-report tbody tr"), (r,e)=>{ let tds = e.querySelectorAll("td"); if (tds.length == 4) { r = tds[0].textContent; } if (tds[tds.length -1].classList.contains("null")) { never[r] = (never[r] ?? 0) + 1; } return r; },null); let a = Object.keys(never).map(k=>`${k}\t:\t${never[k]}`); console.log(a.join("\n")); })();
SHIFT+PageUp/PageDown で前後の解答に移動します。
/** * Moodle 2 hack for report.php&mode=grading: jump prev/next answer * Jump prev/next answer with SHIFT+PageUp/PageDown key. */ (function(){ function getY(e) { return e ? e.offsetTop + getY(e.offsetParent) : 0; } function scrollY() { return Math.round(window.scrollY) } function jump(dir) { let header = document.querySelector("header"); let hh = getComputedStyle(header).position == "fixed" ? header.clientHeight : 0; let scrollpos = scrollY() + hh; let h4s = [].map.call(document.querySelectorAll("form > div > h4"), e=>e); function prev() {return h4s.reverse().find(e=>getY(e)<scrollpos)||h4s[h4s.length-1];} function next() {return h4s .find(e=>scrollpos<getY(e))||h4s[h4s.length-1];} let e = dir < 0 ? prev() : next(); e.nextSibling.querySelector(".editor_atto_content").focus(); window.scroll(0, getY(e) - hh); } document.addEventListener("keydown",event=>{ if (event.key.match(/Page(Up|Down)/) && event.getModifierState("Shift")) { event.preventDefault(); jump(event.key == "PageUp" ? -1: +1); } }); })();
タグ
コメントをかく