aboutsummaryrefslogtreecommitdiff
path: root/src/student-prod.ts
blob: 67379d931155b96ae0575ea8b1cea207fabd4a2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import type { Graduation } from "./graduation-common.ts";
import { Student } from "./student-common.ts";
import { getUserIconUrl, getCourseParticipantsUrl } from "./utils-prod.js";
import { fetchGraduation } from "./graduation-prod.ts";

declare module "./student-common.ts" {
	interface Student {
		getGraduation(): Promise<Graduation>;
		getIconUrl(): Promise<string>;
	}
}
Student.prototype.getGraduation = function () {
	return fetchGraduation(this);
};

Student.prototype.getIconUrl = function () {
	return getUserIconUrl(this.id);
};

// Cache the fetched students.
let students: Array<Student> | null = null;

const idRegex = /id=(\d+)/;
async function _fetchStudents() {
	const res = await fetch(getCourseParticipantsUrl());
	const doc = new DOMParser().parseFromString(await res.text(), "text/html");
	const table = doc.querySelector("tbody")!;
	const fetchedStudents: Array<Student> = [];
	table.querySelectorAll("tr").forEach((row) => {
		if (row.classList.contains("emptyrow")) return;

		const roleCell = row.querySelector(".c4")!;
		// TODO(muhq): Is this locale dependent?
		if (roleCell.innerHTML.includes("Lehrer")) {
			return;
		}

		const nameCell = row.querySelector<HTMLElement>(".c1")!;
		let name;
		const c = nameCell.firstChild!.firstChild! as HTMLElement;
		if (c.tagName == "SPAN") name = c.title;
		else name = nameCell.innerText;

		const profileUrl = nameCell.querySelector<HTMLAnchorElement>("a.aabtn")!.href;
		const match = idRegex.exec(profileUrl);
		if (match === null) {
			console.error("Profile Regex did not match");
			return;
		}
		const id = match[1];

		const groupCell = row.querySelector(".c5")!;
		const group = groupCell.querySelector<HTMLElement>("a.quickeditlink")!.textContent;

		fetchedStudents.push(new Student(Number(id), name, group));
	});
	return fetchedStudents;
}

async function fetchStudents(idFilter?: number, nameFilter?: string, groupFilter?: string) {
	if (students === null) {
		const studentsPromise = _fetchStudents();
		const storedStudents = localStorage.getItem("students");
		if (storedStudents) {
			studentsPromise.then((fetched) => localStorage.setItem("students", JSON.stringify(fetched)));
			students = [];
			for (const o of JSON.parse(storedStudents)) {
				students.push(new Student(o.id, o.name, o.group));
			}
		} else {
			students = await studentsPromise;
			localStorage.setItem("students", JSON.stringify(students));
		}
	}

	return students.filter(
		(s) =>
			(typeof idFilter === "undefined" || s.id === idFilter) &&
			(typeof nameFilter === "undefined" || s.name.includes(nameFilter)) &&
			(typeof groupFilter === "undefined" || s.group.includes(groupFilter)),
	);
}

async function fetchStudent(idFilter: number, nameFilter: string, groupFilter: string) {
	const students = await fetchStudents(idFilter, nameFilter, groupFilter);
	return students[0];
}

export { Student, fetchStudents, fetchStudent };