// ==UserScript==
// @name SIT上应口语伙伴工具箱
// @namespace snomiao@gmail.com
// @version 20190427.121142
// @description 可查询自己读了多少秒,读了什么课文
// @author snomiao
// @include *://210.35.98.12:8844/*
// @grant none
// ==/UserScript==
var httpGetAsync = (theUrl) => {
return new Promise((resolve, reject) => {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
//if you fetch a file you can JSON.parse(httpRequest.responseText)
var data = httpRequest.responseText;
resolve(data);
}
};
httpRequest.open('GET', theUrl, true);
httpRequest.send(null);
});
};
var getLearningReports = async (e) => {
e.target.disabled = true; //不可用
var timeZone = 28800000; // === 8 * 60 * 60 * 1000
// isoDateTimeExample = "1970-01-01T00:00:00.000Z"
var isoDateTime = (timestamp) =>
new Date(timestamp * 1000 + timeZone).toISOString();
var isoDate = (timestamp) => isoDateTime(timestamp).substring(0, 10); // "1970-01-01" .split(/T|\./)[0]
var isoTime = (timestamp) => isoDateTime(timestamp).substring(11, 19); // "00:00:00" .split(/T|\./)[1]
var localeDate = (anIsoDate) => {
// "1970-01-01" => "1970年1月1日 星期四"
anIsoDate = new Date(anIsoDate);
return (
anIsoDate.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
}) +
' ' +
anIsoDate.toLocaleDateString('zh-CN', { weekday: 'long' })
);
};
var getUrlPrefix = '/report_new.php?do=ajax&op=';
var getList = async (starttime) => {
console.log('getList(starttime)', { starttime });
return JSON.parse(
await httpGetAsync(
getUrlPrefix +
'list' +
((starttime && '&starttime=' + starttime) || '')
)
);
};
var getOne = async (cid) => {
console.log('getOne(cid)', { cid });
return JSON.parse(await httpGetAsync(getUrlPrefix + 'one&cid=' + cid));
};
var getDetail = async (cid, date) => {
console.log('getDetail(cid, date)', { cid, date });
return JSON.parse(
await httpGetAsync(
getUrlPrefix + 'detail&cid=' + cid + '&date=' + date
)
);
};
var starttime = +new Date('2019-02-24') / 1000;
var reList = await getList(starttime);
//var reList = await getList();
if (reList.success) {
var cids = Object.keys(reList.data);
var sum_time = 0;
var sum_scores = [];
var student_id = document.cookie.match(/uchome_loginuser=(\d+)/)[1];
var report = (
await Promise.all(
cids.map(async (cid) => {
// WARP -3
var reOne = await getOne(cid);
if (reOne.success) {
var days = Math.round(
Object.values(reList.data[cid].days)[0].day_count
); // 这些四舍五入必要吗?
var lessons = Math.round(
Object.values(reList.data[cid].lessons)[0]
.lesson_count
);
var avgscore =
Math.round(
Object.values(reList.data[cid].avgscore)[0]
.avgscore * 100
) / 100;
var maxscore =
Math.round(
Object.values(reList.data[cid].maxscore)[0]
.maxscore * 100
) / 100;
var spendtime = Math.round(
Object.values(reList.data[cid].spendtime)[0]
.spendtime
);
var timeCalc = (s, n = true) => {
// 秒 => [小时, 分钟, 秒]
let m = Math.trunc(s / 60);
s -= m * 60;
return n ? [...timeCalc(m, false), s] : [m, s];
};
var spendtimehms = timeCalc(spendtime);
spendtimehms = `${spendtimehms[0]} 小时 ${spendtimehms[1]} 分钟 ${spendtimehms[2]} 秒`;
var dates_summary = `<h3 class="StudyReport-lesson">课程 ${cid}</h3>
<p><strong>学习了 ${days} 天,共 ${lessons} 篇。</strong></p>
<table class="StudyReport-summary">
<tr><th>平均分</th><th>最高分</th><th>累计秒数</th><th>累计时间</th></tr>
<tr><td>${avgscore} 分</td><td>${maxscore} 分</td><td>${spendtime} 秒</td><td>${spendtimehms}</td></tr>
</table>
<p>详情如下:</p>`;
var dates = Object.keys(reOne.data).filter(
(x) => parseInt(x) > starttime
);
var dates_report = (
await Promise.all(
dates.map(async (date) => {
console.log(date);
var str_date = localeDate(isoDate(date));
var lesson_count =
reOne.data[date].lesson_count;
var studied_count =
reOne.data[date].studied_count;
var details_summary = `<h4 class="StudyReport-date"><time datetime="${str_date}">${str_date}</time></h4>
<p>读了 ${lesson_count} 篇课文, 共读 ${studied_count} 次,分别为:</p>`;
var details_thead =
'<tr><th>时间</th><th>课文</th><th>分数</th></tr>\n';
var reDetail = await getDetail(cid, date);
var details_tbody = Object.values(
reDetail.data
)
.map(function (obj) {
var lessontitle = obj.lessontitle;
// var lessonid = obj.lessonid;
var details_tr = obj.data
.map(function (data_score) {
var time = isoTime(
data_score.dateline
);
var score = Number(
data_score.score
);
sum_scores.push(score);
return `<tr><td><time datetime="${time}">${time}</time></td><td>${lessontitle}</td><td>${score} 分</td></tr>`;
})
.join('\n');
return details_tr;
})
.join('\n');
details_table = `<table class="StudyReport-details">${
details_thead + details_tbody
}</table>`;
return details_summary + details_table;
})
)
).join('\n');
sum_time += spendtime;
return `<li>${dates_summary + dates_report}</li>`;
} // if end
// WARP +3
})
)
).join('\n');
var report_title = `<h2>${student_id} 口语伙伴学习情况汇总</h2>`;
var avg = (array) => array.reduce((a, b, i) => (a * i + b) / (i + 1));
var avg_score = Math.round(avg(sum_scores) * 100) / 100;
var sum_time_str =
Math.round((sum_time / 60 / 60) * 100) / 100 + ' 小时';
var report_summary = `总地来看,累计时间为 ${sum_time_str}, 平均分为 ${avg_score} 分。`;
} // if end
// alert(report_title + report + report_summary)
var report_style = `
#StudyReport {
box-sizing: border-box;
width: 1000px;
margin: 0 auto 1rem;
border-bottom: 1px dashed #08c;
font-size: .8rem;
}
#StudyReport details {
background-color: #f5f5f5;
border: 1px solid #cbcbcb;
}
#StudyReport details > summary,
#StudyReport details > ul,
#StudyReport details ~ p { margin: 1rem; }
#StudyReport summary { cursor: pointer; }
#StudyReport summary h2 {
font-size: 1rem;
display: inline-block;
}
#StudyReport details > ul {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 1rem;
}
#StudyReport details > ul > li {
padding: 1rem;
-webkit-column-break-inside: avoid;
break-inside: avoid;
background-color: rgba(255, 255, 255, .5);
}
#StudyReport strong { font-size: larger; }
#StudyReport button {
padding: .125em 1ch;
border: 1px solid #cbcbcb;
}
h3.StudyReport-lesson,
h4.StudyReport-date {
margin-bottom: 1ex;
margin-block-end: 1ex;
}
h4.StudyReport-date { font-size: 1rem; }
h3.StudyReport-lesson::before {
content: "";
display: inline-block;
background-image: url(../images/icon.png);
background-repeat: no-repeat;
width: 1.6em;
height: 1.6em;
background-position: center -27px;
vertical-align: middle;
}
h4.StudyReport-date {
font-size: .9rem;
text-align: center;
}
/* HTML table style below uses a color scheme partly from:
https://purecss.io/tables/
By Yahoo! Inc.
License: https://github.com/pure-css/pure-site/blob/master/LICENSE.md */
#StudyReport table {
width: 100%;
margin-top: 1rem;
margin-bottom: 1rem;
margin-block: 1rem;
}
#StudyReport table:last-child {
margin-bottom: 0;
margin-block-end: 0;
}
#StudyReport th,
#StudyReport td {
padding: .125em .5ch; /* = 1/8em 1/2ch */
border: 1px solid #cbcbcb;
font-size: inherit;
}
#StudyReport th { background-color: #e0e0e0; }
#StudyReport td,
#StudyReport tr:nth-of-type(even) td { background-color: #fff; }
#StudyReport tr:nth-of-type(odd) td { background-color: #f0f0f0; }
/* 将除 课文 以外的列全部居中 */
/*
table.StudyReport-summary th,
table.StudyReport-summary td, */
table.StudyReport-summary,
table.StudyReport-details th:nth-of-type(odd),
table.StudyReport-details td:nth-of-type(odd) { text-align: center; }
/* 为可能有大量数据的表格添加数据行悬停样式 */
table.StudyReport-details tr:hover td { background-image: linear-gradient(to left, #3bf2, #3bf2) }
/* 给口语伙伴修一行 CSS… */
.my_message { background-position: 15px -135px; }
`;
var ReportSection = `<section id="StudyReport">
<style>${report_style}</style>
<details open>
<summary>${report_title}</summary>
<ul>${report}</ul>
</details>
<p>${report_summary}</p>
<p><button>复制</button></p>
</section>`;
document.body.insertAdjacentHTML('afterbegin', ReportSection);
ReportSection = document.getElementById('StudyReport'); // #text => Node/Element
// 不知为什么,即使复制操作是点击获取成绩按钮触发的,还是得再给个 button 让人手动复制,才不会被浏览器禁止
var CopyReport = () => {
var selection = window.getSelection();
selection.selectAllChildren(ReportSection);
var msgbox = (msg) => {
var themsgbox = document.createElement('div');
themsgbox.style =
'position: fixed; font-size: 2rem; top: 1rem; left: 1rem; background-color: #fff8; z-index: 999;';
themsgbox.textContent = msg;
document.body.append(themsgbox);
setTimeout(() => themsgbox.parentNode.removeChild(themsgbox), 3000);
};
document.execCommand('copy')
? msgbox('内容已复制')
: msgbox('内容复制失败');
selection.removeAllRanges();
};
ReportSection.getElementsByTagName('button')[0].addEventListener(
'click',
CopyReport
);
e.target.disabled = false; //可用
};
var LearningReport = async () => {
var btn = document.createElement('button');
btn.innerHTML = `了解我的学习情况<br>(查成绩查时间)`;
btn.addEventListener('click', getLearningReports);
document.querySelector('.u_name').append(btn);
};
var ForceSubmitScore = async (lid, uid) => {
let httpGetAsync = (theUrl) => {
return new Promise((resolve, reject) => {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
//if you fetch a file you can JSON.parse(httpRequest.responseText)
var data = httpRequest.responseText;
resolve(data);
}
};
httpRequest.open('GET', theUrl, true);
httpRequest.send(null);
});
};
if (!uid) {
let profileHTML = await httpGetAsync(
'http://210.35.98.12:8844/cp.php?ac=profile'
);
let match = profileHTML.match(/uid=(\d+)/);
if (!match) return false;
let uid = match[1];
return ForceSubmitScore(lid, uid);
}
let pron = round(Math.random() * 10 + 84, 2);
let tone = round(Math.random() * 10 + 84, 2);
let rhythm = round(Math.random() * 10 + 84, 2);
let scope = round(Math.random() * 10 + 84, 2);
let total = round(Math.random() * 10 + 84, 2);
let spendtime = round(Math.random() * 150 + 400, 0);
let token = 11200000 + parseInt(Math.random() * 10000);
let url = `http://210.35.98.12:8844//playserver.php?target=&lid=${lid}&testtype=0&targetid=&uid=${uid}&do=submitscore&total=${total}&pron=${pron}&tone=${tone}&rhythm=${rhythm}&scope=${scope}&spendtime=${spendtime}&token=${token}`;
return true;
};
var ForceSubmitScoreButtons = async (uid) => {
if (!uid) {
let profileHTML = await httpGetAsync(
'http://210.35.98.12:8844/cp.php?ac=profile'
);
let match = profileHTML.match(/uid=(\d+)/);
if (!match) return false;
let uid = match[1];
return ForceSubmitScoreButtons(uid);
}
// 历史成绩页面 */s.php?do=lesson&lid=*
[...document.querySelectorAll('a')]
.filter((e) => e.href.match(/\.\/s\.php\?do=lesson&lid=(\d+)/))
.map((e) => {
var round = (number, precision) =>
Math.round(+number + 'e' + precision) / Math.pow(10, precision);
let lid = e.href.match(/.\/s\.php\?do=lesson&lid=(\d+)/)[1];
let pron = round(Math.random() * 10 + 84, 2);
let tone = round(Math.random() * 10 + 84, 2);
let rhythm = round(Math.random() * 10 + 84, 2);
let scope = round(Math.random() * 10 + 84, 2);
let total = round(Math.random() * 10 + 84, 2);
let spendtime = round(Math.random() * 150 + 400, 0);
let token = 11200000 + parseInt(Math.random() * 10000);
let url = `http://210.35.98.12:8844//playserver.php?target=&lid=${lid}&testtype=0&targetid=&uid=${uid}&do=submitscore&total=${total}&pron=${pron}&tone=${tone}&rhythm=${rhythm}&scope=${scope}&spendtime=${spendtime}&token=${token}`;
let btn = document.createElement('button');
btn.innerHTML = `我要 ${total} 分!`;
let a = document.createElement('a');
a.append(btn);
a.href = url;
a.target = '_BLANK';
let div = document.createElement('div');
div.append(a);
e.parentNode.append(div);
});
// 课文列表页面 */s.php?do=lesson&iden=*
[...document.querySelectorAll('a')]
.filter((e) => e.href.match(/.*\/s\.php\?do=lesson&iden=.*/))
.map(async (e) => {
e.addEventListener('mouseover', async () => {
if (e.disabled) return;
e.disabled = true;
let lessonHTML = await httpGetAsync(e.href);
let match1 = lessonHTML.match(/uid=(\d+)/);
if (!match1) return false;
let uid = match1[1];
let match2 = lessonHTML.match(/lid=(\d+)/);
if (!match2) return false;
let lid = match2[1];
var round = (number, precision) =>
Math.round(+number + 'e' + precision) /
Math.pow(10, precision);
let pron = round(Math.random() * 10 + 84, 2);
let tone = round(Math.random() * 10 + 84, 2);
let rhythm = round(Math.random() * 10 + 84, 2);
let scope = round(Math.random() * 10 + 84, 2);
let total = round(Math.random() * 10 + 84, 2);
let spendtime = round(Math.random() * 150 + 400, 0);
let token = 11200000 + parseInt(Math.random() * 10000);
let url = `http://210.35.98.12:8844//playserver.php?target=&lid=${lid}&testtype=0&targetid=&uid=${uid}&do=submitscore&total=${total}&pron=${pron}&tone=${tone}&rhythm=${rhythm}&scope=${scope}&spendtime=${spendtime}&token=${token}`;
let btn = document.createElement('button');
btn.innerHTML = `我要 ${total} 分!`;
btn.addEventListener('click', async () => {
await httpGetAsync(url);
window.location = window.location;
});
let a = document.createElement('a');
a.append(btn); // a.href=url; a.target = "_BLANK";
let div = document.createElement('div');
div.append(a);
e.parentNode.append(a);
});
});
return true;
};
LearningReport();
ForceSubmitScoreButtons();