Learning ZJU Helper

show score in course

目前为 2023-09-26 提交的版本。查看 最新版本

// ==UserScript==
// @name         Learning ZJU Helper
// @namespace    https://github.com/Camel-zy/Learning-ZJU-Helper
// @version      1.1.0
// @description  show score in course
// @author       Camel-zy
// @match        https://courses.zju.edu.cn/course/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=zju.edu.cn
// @license      MIT
// @grant        none
// ==/UserScript==

// 监听URL变化
// https://stackoverflow.com/questions/3522090/event-when-window-location-href-changes
const observeURLChange = (callback) => {
	let oldHref = document.location.href;
	const body = document.querySelector("body");
	const observer = new MutationObserver((mulations) => {
		if (oldHref != document.location.href) {
			oldHref = document.location.href;
			callback();
			observer.disconnect();
		}
	});
	observer.observe(body, { childList: true, subtree: true });
};

// 休眠
function sleep(ms) {
	return new Promise((resolve) => setTimeout(resolve, ms));
}

// 等待元素加载完成且满足条件后执行回调函数
function waitElement(selector, callback, condition = true) {
	let element = document.querySelector(selector);
	if (element && condition) {
		callback(element);
	} else {
		setTimeout(() => {
			waitElement(selector, callback);
		}, 500);
	}
}

var header = {
	Accept: "application/json;charset=UTF-8",
	"Content-Type": "application/json;charset=UTF-8",
	Cookie: document.cookie
};

function getActivities() {
	let courseID = window.location.href.split("/")[4];
	let activityForUser;
	let activities;
	fetch(`https://courses.zju.edu.cn/api/course/${courseID}/activity-reads-for-user`, {
		method: "GET",
		headers: header
	})
		.then((res) => res.json())
		.then((res) => {
			activityForUser = res["activity_reads"];
			console.log("[Learning ZJU Helper] get activity-reads-for-user success");
		})
		.catch((err) => {
			console.log("[Learning ZJU Helper] get activity-reads-for-user failed: " + err);
			return new Promise((resolve, reject) => {
				reject();
			});
		});

	fetch(`https://courses.zju.edu.cn/api/courses/${courseID}/activities`, {
		method: "GET",
		headers: header
	})
		.then((res) => res.json())
		.then((res) => {
			activities = res["activities"];
			console.log("[Learning ZJU Helper] get activities success");
		})
		.catch((err) => {
			console.log("[Learning ZJU Helper] get activities failed: " + err);
			return new Promise((resolve, reject) => {
				reject();
			});
		});

	return new Promise((resolve, reject) => {
		let interval = setInterval(() => {
			if (activityForUser && activities) {
				clearInterval(interval);
				resolve([activityForUser, activities]);
			}
		}, 500);
	});
}

function showScoreInHomework() {
	getActivities().then((res) => {
		let activityForUser = res[0];
		let activities = res[1];

		// modify table header
		if (document.getElementsByClassName("large-12 column").length != 0) {
			let tableHeader = document.getElementsByClassName("column-header row collapse")[0];
			let statusElement = tableHeader.getElementsByClassName("large-4 column")[0];
			statusElement.className = "large-2 column";
			let scoreElement = tableHeader.getElementsByClassName("large-12 column")[0];
			scoreElement.className = "large-4 column";
			let formElement = tableHeader.getElementsByClassName("large-6 column")[0];
			formElement.className = "large-4 column";
			let highestScoreElement = document.createElement("div");
			highestScoreElement.className = "large-2 column";
			highestScoreElement.innerHTML = "<span>最高分</span>";
			tableHeader.appendChild(highestScoreElement);
			let averageScoreElement = document.createElement("div");
			averageScoreElement.className = "large-2 column";
			averageScoreElement.innerHTML = "<span>平均分</span>";
			tableHeader.appendChild(averageScoreElement);
			let lowestScoreElement = document.createElement("div");
			lowestScoreElement.className = "large-2 column";
			lowestScoreElement.innerHTML = "<span>最低分</span>";
			tableHeader.appendChild(lowestScoreElement);
			let emptyElement = document.createElement("div");
			emptyElement.className = "large-4 column";
			tableHeader.appendChild(emptyElement);
		}

		let homeworkList = document.getElementsByClassName("list-item row collapse ng-scope");
		for (let i = 0; i < homeworkList.length; i++) {
			let scoreElement = homeworkList[i].getElementsByClassName("large-4 column")[1].getElementsByTagName("span")[0];
			let name = homeworkList[i].getElementsByClassName("large-10 column")[0].getElementsByTagName("span")[0].innerText;

			// find activityID
			let activityID;
			let averageScore;
			let highestScore;
			let lowestScore;

			for (let j = 0; j < activities.length; j++) {
				if (activities[j]["title"] == name) {
					activityID = activities[j]["id"];
					averageScore = activities[j]["average_score"];
					highestScore = activities[j]["highest_score"];
					lowestScore = activities[j]["lowest_score"];
					break;
				}
			}

			if (scoreElement.getAttribute("ng-if") == "homework.notAnnounced" || scoreElement.getAttribute("ng-if") == "homework.notPublish") {
				// if (scoreElement.innerText.includes("未公布")) {
				// 未显示过分数
				// find score
				let score;
				for (let j = 0; j < activityForUser.length; j++) {
					if (activityForUser[j]["activity_id"] == activityID) {
						score = activityForUser[j]["data"]["score"];
						break;
					}
				}

				// show score
				if (score != null) {
					scoreElement.innerText = score;
				} else {
					scoreElement.innerText = "未公布";
				}
				// }
			}

			let endElement;
			if (homeworkList[i].getElementsByClassName("large-8 column end").length != 0) {
				endElement = homeworkList[i].getElementsByClassName("large-8 column end")[0];
				endElement.className = "large-6 column end";
				let statusElement = homeworkList[i].getElementsByClassName("large-4 column")[0];
				statusElement.className = "large-2 column";
				let formElement = homeworkList[i].getElementsByClassName("large-6 group-set column")[0];
				formElement.className = "large-4 group-set column";
			} else {
				endElement = homeworkList[i].getElementsByClassName("large-6 column end")[0];
			}

			// show highest score
			if (highestScore) {
				highestScore = highestScore.toFixed(2);
			} else {
				highestScore = "未公布";
			}
			if (homeworkList[i].getElementsByClassName("large-2 column").length != 4) {
				let highestScoreElement = document.createElement("div");
				highestScoreElement.className = "large-2 column";
				highestScoreElement.innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-red">${highestScore}</span></a>`;
				endElement.parentNode.insertBefore(highestScoreElement, endElement);
			} else {
				homeworkList[i].getElementsByClassName("large-2 column")[1].innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-red">${highestScore}</span></a>`;
			}

			// show average score
			if (averageScore != null) {
				averageScore = averageScore.toFixed(2);
			} else {
				averageScore = "未公布";
			}
			if (homeworkList[i].getElementsByClassName("large-2 column").length != 4) {
				let averageScoreElement = document.createElement("div");
				averageScoreElement.className = "large-2 column";
				averageScoreElement.innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-black">${averageScore}</span></a>`;
				endElement.parentNode.insertBefore(averageScoreElement, endElement);
			} else {
				homeworkList[i].getElementsByClassName("large-2 column")[2].innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-black">${averageScore}</span></a>`;
			}

			// show lowest score
			if (lowestScore != null) {
				lowestScore = lowestScore.toFixed(2);
			} else {
				lowestScore = "未公布";
			}
			if (homeworkList[i].getElementsByClassName("large-2 column").length != 4) {
				let lowestScoreElement = document.createElement("div");
				lowestScoreElement.className = "large-2 column";
				lowestScoreElement.innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-green">${lowestScore}</span></a>`;
				endElement.parentNode.insertBefore(lowestScoreElement, endElement);
			} else {
				homeworkList[i].getElementsByClassName("large-2 column")[3].innerHTML = `<a class="detail" ng-click="openActivity(homework, false)">
                <span class="color-green">${lowestScore}</span></a>`;
			}
			console.log("[Learning ZJU Helper] show score in homework " + name + " success: score=" + scoreElement.innerText + " highestScore=" + highestScore + " averageScore=" + averageScore + " lowestScore=" + lowestScore);
		}
	});
}

function addSortEventListeners() {
	let sortButton = document.getElementsByClassName("dropdown-list dropdown-list-sorter")[0];
	let sortBy = sortButton.getElementsByTagName("li");
	for (let i = 0; i < sortBy.length; i++) {
		sortBy[i].addEventListener("click", function () {
			sleep(250).then(() => {
				waitElement(".list-item.row.collapse.ng-scope", showScoreInHomework);
			});
		});
	}

	let sortMethodButton = document.getElementsByClassName("dropdown-list sort-by-method")[0];
	let sortMethod = sortMethodButton.getElementsByTagName("li");
	for (let i = 0; i < sortMethod.length; i++) {
		sortMethod[i].addEventListener("click", function () {
			sleep(250).then(() => {
				waitElement(".list-item.row.collapse.ng-scope", showScoreInHomework);
			});
		});
	}
}

function showScoreInScore() {
	getActivities().then((res) => {
		let activityForUser = res[0];
		let activities = res[1];
		let activityList = document.getElementsByClassName("activity row ng-scope");
		for (let i = 0; i < activityList.length; i++) {
			let originScoreElement = activityList[i].getElementsByClassName("operand large-10 columns zh-CN")[0].getElementsByTagName("span")[0];
			let name = activityList[i].getElementsByClassName("title ng-scope")[0].getElementsByTagName("a")[0].innerText;

			// find activityID
			let activityID;
			let averageScore;
			let highestScore;
			let lowestScore;

			for (let j = 0; j < activities.length; j++) {
				if (activities[j]["title"] == name) {
					activityID = activities[j]["id"];
					averageScore = activities[j]["average_score"];
					highestScore = activities[j]["highest_score"];
					lowestScore = activities[j]["lowest_score"];
					break;
				}
			}

			if (originScoreElement.className.includes("no-published")) {
				// find score
				let score;
				for (let j = 0; j < activityForUser.length; j++) {
					if (activityForUser[j]["activity_id"] == activityID) {
						score = activityForUser[j]["data"]["score"];
						break;
					}
				}

				// show score
				if (score != null) {
					originScoreElement.innerText = score;
				} else {
					originScoreElement.innerText = "未公布";
				}

				let actualScoreElement = originScoreElement.parentElement.parentElement.getElementsByClassName("operand large-10 columns zh-CN")[2].getElementsByTagName("span")[0];
				if (score != null) {
					actualScoreElement.innerText = 
                        ((score * parseFloat(originScoreElement.parentElement.parentElement.getElementsByClassName("operand large-10 columns zh-CN")[1].getElementsByTagName("span")[0].innerText)) / 100).toFixed(2);
				} else {
					actualScoreElement.innerText = "未公布";
				}
			}
			console.log("[Learning ZJU Helper] show score in score " + name + " success: score=" + originScoreElement.innerText);
		}
	});
}

(function () {
	"use strict";
	console.log("[Learning ZJU Helper] start");
	let URLChangeCallback = function () {
		let url = new URL(window.location.href);
		let path = url.pathname;

		if (path.includes("homework")) {
			// 作业页面
			sleep(250).then(() => {
				waitElement(".list-item.row.collapse.ng-scope", addSortEventListeners);
				waitElement(".list-item.row.collapse.ng-scope", showScoreInHomework);
			});
		} else if (path.includes("score")) {
			// 成绩页面
			sleep(250).then(() => {
				waitElement(".activity.row.ng-scope", showScoreInScore);
			});
		}
	};
	URLChangeCallback();
	window.onload = observeURLChange(URLChangeCallback);
	window.addEventListener("hashchange", function (event) {
		URLChangeCallback();
	});
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址