- // ==UserScript==
- // @name OJCN Report Gen
- // @description 自动生成作业报告 (docx)。虽然大家并不想理我。
- // @namespace https://gf.qytechs.cn/users/197529
- // @version 0.2.16
- // @author kkocdko
- // @license Unlicense
- // @match *://noi.openjudge.cn/*
- // ==/UserScript==
- "use strict";
-
- const cfg = {
- studentName: "无名氏", // 姓名
- homeworkId: 4, // 作业序号
- userId: document.querySelector("#userToolbar>li")?.textContent,
- };
- cfg.problems = {
- // 9: [
- // "ch0107/01",
- // "ch0107/03",
- // ],
- 8: [
- "ch0110/05",
- "ch0110/09",
- "ch0112/10",
- "ch0113/14",
- "ch0107/22",
- "ch0107/26",
- // https://leetcode.cn/problems/string-to-integer-atoi/
- // https://leetcode.cn/problems/valid-palindrome/
- // https://leetcode.cn/problems/ugly-number/
- // https://leetcode.cn/problems/power-of-two/
- ],
- 7: [
- "ch0107/19",
- "ch0107/20",
- "ch0107/21",
- "ch0107/23",
- "ch0107/25",
- // just them
- ],
- 6: [
- "ch0107/01",
- "ch0107/03",
- "ch0107/05",
- "ch0107/08",
- "ch0107/09",
- "ch0107/10",
- "ch0107/12",
- "ch0107/17",
- "ch0107/33",
- "ch0109/11",
- ],
- 5: [
- "ch0105/15",
- "ch0105/22",
- "ch0105/35",
- "ch0106/08",
- "ch0106/09",
- "ch0106/10",
- "ch0106/11",
- "ch0110/03",
- "ch0110/04",
- "ch0107/02",
- ],
- 4: [
- "ch0106/01",
- "ch0106/02",
- "ch0106/04",
- "ch0106/05",
- "ch0106/06",
- "ch0106/07",
- "ch0109/01",
- "ch0109/05",
- "ch0109/07",
- "ch0110/01",
- ],
- }[cfg.homeworkId];
- if (!document.querySelector(".account-link")) throw alert("login required");
- if (!cfg.studentName === "无名氏") throw alert("please modify the config");
- document.lastChild.appendChild(document.createElement("style")).textContent = `
- body::before { content: ""; position: fixed; left: 40px; top: 40px; padding: 20px; border: 8px solid #37b; border-radius: 25%; z-index: 2000; animation: spin 12s linear; }
- @keyframes spin { 100% { transform: rotate(3600deg) } }
- `;
- const results = cfg.problems.map(() => null);
- const tasks = cfg.problems.map(async (path, idx) => {
- const [ch, subId] = path.split("/");
- const queryUrl = `/${ch}/status/?problemNumber=${subId}&userName=${cfg.userId}`;
- const queryPage = await fetch(queryUrl).then((r) => r.text());
- const table = queryPage.split(/<\/?table>/g)[1];
- const entry = table.split(/<\/?tr>/).find((v) => v.includes("Accepted"));
- const record = [];
- for (let s = entry; s !== ""; ) {
- if (s.startsWith("<")) s = s.slice(s.indexOf(">"));
- let idx = s.indexOf("<");
- if (idx === -1) break;
- let v = s.slice(1, idx).trim();
- if (v) record.push(v);
- s = s.slice(idx).trim();
- }
- record[1] = { text: record[1], target: location.origin + queryUrl };
- const solutionUrl = entry.match(/(?<=language"><a href=")[^"]+/)[0];
- const solutionPage = await fetch(solutionUrl).then((r) => r.text());
- const codeExactor = document.createElement("p");
- codeExactor.innerHTML = solutionPage.match(/<pre(.|\n)+?<\/pre>/)[0];
- results[idx] = { path, code: codeExactor.textContent, record };
- });
- tasks.push(
- import(`https://cdn.jsdelivr.net/npm/docx@7.7.0/build/index.min.js`)
- );
- Promise.all(tasks).then(async () => {
- const {
- AlignmentType,
- BorderStyle,
- Document,
- ExternalHyperlink,
- Footer,
- Packer,
- PageNumber,
- PageNumberSeparator,
- Paragraph,
- Table,
- TableCell,
- TableRow,
- TextRun,
- UnderlineType,
- WidthType,
- } = docx;
- const genProblemPart = ({ num, path, code, record }) => [
- new Paragraph({
- spacing: { before: 500, after: 200 },
- children: [
- new TextRun({
- text: `Problem ${num.toString().padStart(2, "0")}`,
- bold: true,
- size: 24,
- font: "Arial",
- }),
- ],
- }),
- new Paragraph({
- spacing: { before: 200, line: 300 },
- children: [
- new TextRun({
- text: "Description: ",
- font: "Times New Roman",
- size: 21,
- bold: true,
- }),
- new TextRun({
- text: "Read the problem at ",
- font: "Times New Roman",
- size: 21,
- }),
- new TextRun({
- text: `http://noi.openjudge.cn/${path}/`,
- font: "Times New Roman",
- size: 21,
- italics: true,
- }),
- new TextRun({
- text: ", try to make your program ",
- font: "Times New Roman",
- size: 21,
- }),
- new TextRun({
- text: "accepted",
- font: "Times New Roman",
- size: 21,
- italics: true,
- }),
- new TextRun({
- text: " by the OJ system.",
- font: "Times New Roman",
- size: 21,
- }),
- ],
- }),
- new Paragraph({
- spacing: { before: 150, after: 150 },
- children: [
- new TextRun({
- text: "My Program:",
- font: "Segoe UI Semibold",
- size: 21,
- underline: { type: UnderlineType.DOUBLE },
- }),
- ],
- }),
- ...code.split("\n").map(
- (text) =>
- new Paragraph({
- spacing: { line: 280 },
- indent: { left: 400 },
- alignment: AlignmentType.LEFT,
- children: [new TextRun({ text, font: "Consolas", size: 21 })],
- })
- ),
- new Paragraph({
- spacing: { before: 150, after: 150 },
- children: [
- new TextRun({
- text: "My Result:",
- font: "Segoe UI Semibold",
- size: 21,
- underline: { type: UnderlineType.DOUBLE },
- }),
- ],
- }),
- new Table({
- rows: [
- new TableRow({
- children: "提交人|题目|结果|分数|内存|时间|代码长度|语言"
- .split("|")
- .map(
- (field, i) =>
- new TableCell({
- width: {
- size: [1500, 3500, 800, 600, 800, 800, 900, 600][i],
- type: WidthType.DXA,
- },
- margins: { top: 0, bottom: 0, left: 100, right: 100 },
- borders: {
- top: { style: BorderStyle.SINGLE, color: "DDDDDD" },
- right: { style: BorderStyle.SINGLE, color: "DDDDDD" },
- bottom: { style: BorderStyle.SINGLE, color: "DDDDDD" },
- left: { style: BorderStyle.SINGLE, color: "DDDDDD" },
- },
- shading: { fill: "E0EAF1" },
- children: [
- new Paragraph({
- children: [
- new TextRun({
- text: field,
- font: "Microsoft YaHei",
- size: 16,
- }),
- ],
- }),
- ],
- })
- ),
- }),
- new TableRow({
- children: record.slice(0, 8).map(
- (entry, i) =>
- new TableCell({
- width: {
- size: [1500, 3500, 800, 600, 800, 800, 900, 600][i], // sync with upper code
- type: WidthType.DXA,
- },
- margins: { top: 0, bottom: 0, left: 100, right: 100 },
- borders: {
- top: { style: BorderStyle.SINGLE, color: "DDDDDD" },
- right: { style: BorderStyle.SINGLE, color: "DDDDDD" },
- bottom: { style: BorderStyle.SINGLE, color: "DDDDDD" },
- left: { style: BorderStyle.SINGLE, color: "DDDDDD" },
- },
- children: [
- new Paragraph({
- children: [
- entry.target
- ? new ExternalHyperlink({
- children: [
- new TextRun({
- text: entry.text,
- font: "Microsoft YaHei",
- size: 16,
- color: "3070B0",
- }),
- ],
- link: entry.target,
- })
- : new TextRun({
- text: entry,
- font: "Microsoft YaHei",
- size: 16,
- }),
- ],
- }),
- ],
- })
- ),
- }),
- ],
- }),
- ];
- const doc = new Document({
- sections: [
- {
- properties: {
- page: {
- margin: { top: "2cm", right: "2cm", bottom: "2cm", left: "2cm" },
- pageNumbers: { start: 1, separator: PageNumberSeparator.COLON },
- },
- },
- footers: {
- default: new Footer({
- children: [
- new Paragraph({
- alignment: AlignmentType.CENTER,
- children: [
- new TextRun({
- children: [
- PageNumber.CURRENT,
- " / ",
- PageNumber.TOTAL_PAGES,
- ],
- font: "Microsoft YaHei",
- size: 18,
- }),
- ],
- }),
- ],
- }),
- },
- children: [
- new Paragraph({
- spacing: { before: 200, after: 200 },
- alignment: AlignmentType.CENTER,
- children: [
- new TextRun({
- text: `Homework ${cfg.homeworkId.toString().padStart(2, "0")}`,
- bold: true,
- size: 32,
- font: "Microsoft YaHei",
- }),
- ],
- }),
- new Paragraph({
- spacing: { before: 400, after: 720 },
- children: [
- new TextRun({
- text: "Student ID: ",
- bold: true,
- size: 28,
- font: "Calibri",
- }),
- new TextRun({
- text: `\t\t ${cfg.userId}\t\t`,
- size: 28,
- font: "Times New Roman",
- underline: { type: UnderlineType.SINGLE },
- }),
- new TextRun({
- text: " Name: ",
- bold: true,
- size: 28,
- font: "Calibri",
- }),
- new TextRun({
- text: `\t\t ${cfg.studentName} \t\t`,
- size: 28,
- font: "宋体",
- underline: { type: UnderlineType.SINGLE },
- }),
- ],
- alignment: AlignmentType.CENTER,
- }),
- ...results.flatMap((v, i) => genProblemPart({ num: i + 1, ...v })),
- ],
- },
- ],
- });
- const saveLink = document.createElement("a");
- saveLink.download = `${cfg.userId}.docx`;
- saveLink.href = URL.createObjectURL(await Packer.toBlob(doc));
- saveLink.click();
- });
-
- // document.lastChild.appendChild(document.createElement("style")).textContent = `
- // table{ width: 100vw; background: #fff; position: fixed; top: 0; left: 0; z-index: 99999; box-shadow:0 0 0 20px #fff; }
- // tr:not(:first-child){ opacity:0; }
- // #footer { display:none; }
- // `.replace(/;/g, "!important;");
- // ~/misc/apps/miniserve -p 9973 --header cache-control:max-age=3 /home/kkocdko/misc/code/user-scripts/scripts/ojcn-report-gen
- // http://127.0.0.1:9973/index.html