@wilm-ai/wilma-client 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -12,8 +12,13 @@ import type { LessonNote, LessonNoteSummary } from "../types.js";
|
|
|
12
12
|
* three grid columns but consumes one index, so by the third or fourth cell
|
|
13
13
|
* the index has drifted from the true grid column.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Two kinds of event cells: absence-type marks (`<td class="at-tpN">`, whose
|
|
16
|
+
* text is the teacher's code) and free-text remarks such as praise or
|
|
17
|
+
* behaviour notes (`<td class="at-tp-other">`, whose text is a `<small>`
|
|
18
|
+
* label plus an optional `<sup>` footnote marker pointing at the
|
|
19
|
+
* "Huomioita" column). The title attribute carries the details:
|
|
20
|
+
* "TypeLabel /TeacherName", "SubjectCode; TypeLabel /TeacherName" or
|
|
21
|
+
* "SubjectCode; TypeLabel; teacher's note /TeacherName".
|
|
17
22
|
*/
|
|
18
23
|
export declare function parseAttendanceHtml(html: string, date?: string): LessonNote[];
|
|
19
24
|
/** Count lesson notes by kind (absences, lateness, feedback…), most common first. */
|
|
@@ -12,8 +12,13 @@ import * as cheerio from "cheerio";
|
|
|
12
12
|
* three grid columns but consumes one index, so by the third or fourth cell
|
|
13
13
|
* the index has drifted from the true grid column.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Two kinds of event cells: absence-type marks (`<td class="at-tpN">`, whose
|
|
16
|
+
* text is the teacher's code) and free-text remarks such as praise or
|
|
17
|
+
* behaviour notes (`<td class="at-tp-other">`, whose text is a `<small>`
|
|
18
|
+
* label plus an optional `<sup>` footnote marker pointing at the
|
|
19
|
+
* "Huomioita" column). The title attribute carries the details:
|
|
20
|
+
* "TypeLabel /TeacherName", "SubjectCode; TypeLabel /TeacherName" or
|
|
21
|
+
* "SubjectCode; TypeLabel; teacher's note /TeacherName".
|
|
17
22
|
*/
|
|
18
23
|
export function parseAttendanceHtml(html, date) {
|
|
19
24
|
const $ = cheerio.load(html);
|
|
@@ -62,10 +67,14 @@ export function parseAttendanceHtml(html, date) {
|
|
|
62
67
|
for (let i = 2; i < cells.length; i++) {
|
|
63
68
|
const $cell = $(cells[i]);
|
|
64
69
|
const colspan = parseInt($cell.attr("colspan") ?? "1", 10) || 1;
|
|
65
|
-
const tpClass = (($cell.attr("class") ?? "").match(/\bat-tp\
|
|
70
|
+
const tpClass = (($cell.attr("class") ?? "").match(/\bat-tp(?:\d+|-other)\b/) ?? [])[0];
|
|
66
71
|
if (tpClass) {
|
|
72
|
+
const isRemark = tpClass === "at-tp-other";
|
|
67
73
|
const title = ($cell.attr("title") ?? "").trim();
|
|
68
|
-
|
|
74
|
+
// The visible text: a remark's <small> label, or an absence mark's
|
|
75
|
+
// teacher code; a <sup> footnote marker is never part of either.
|
|
76
|
+
const $small = $cell.find("small");
|
|
77
|
+
const cellText = ($small.length ? $small.text() : $cell.clone().find("sup").remove().end().text()).trim();
|
|
69
78
|
// Title formats observed:
|
|
70
79
|
// "TypeLabel /TeacherFullName"
|
|
71
80
|
// "SubjectCode; TypeLabel /TeacherFullName"
|
|
@@ -76,7 +85,8 @@ export function parseAttendanceHtml(html, date) {
|
|
|
76
85
|
let subject = "";
|
|
77
86
|
let typeLabel = "";
|
|
78
87
|
let note = null;
|
|
79
|
-
|
|
88
|
+
// A remark's cell text is its label, never a teacher.
|
|
89
|
+
let teacher = isRemark ? "" : cellText;
|
|
80
90
|
if (title) {
|
|
81
91
|
let rest = title;
|
|
82
92
|
const slashIdx = rest.lastIndexOf(" /");
|
|
@@ -113,7 +123,7 @@ export function parseAttendanceHtml(html, date) {
|
|
|
113
123
|
start,
|
|
114
124
|
end,
|
|
115
125
|
subject,
|
|
116
|
-
typeLabel: typeLabel || tpClass.replace("at-tp", "Type "),
|
|
126
|
+
typeLabel: typeLabel || (isRemark ? cellText : tpClass.replace("at-tp", "Type ")),
|
|
117
127
|
typeClass: tpClass,
|
|
118
128
|
teacher,
|
|
119
129
|
note,
|
package/dist/parsers/schedule.js
CHANGED
|
@@ -98,7 +98,12 @@ export function parseTimetableJson(payload) {
|
|
|
98
98
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(date))
|
|
99
99
|
continue;
|
|
100
100
|
for (const module of lesson.modules ?? []) {
|
|
101
|
-
|
|
101
|
+
// The caption is the course code, sometimes followed by the name
|
|
102
|
+
// ("BI_ Biologia", or just "yRUB1.1" in upper secondary schools); the
|
|
103
|
+
// name alone is in courseName. Lessons without a course (lunch, a
|
|
104
|
+
// class teacher's hour) have only a caption.
|
|
105
|
+
const caption = (module.caption ?? "").trim();
|
|
106
|
+
const subject = (module.courseName ?? "").trim() || caption;
|
|
102
107
|
const teachers = module.teachers ?? [];
|
|
103
108
|
const rooms = (module.rooms ?? []).map((room) => (room.abbreviation ?? room.caption ?? "").trim()).filter(Boolean);
|
|
104
109
|
lessons.push({
|
|
@@ -107,7 +112,7 @@ export function parseTimetableJson(payload) {
|
|
|
107
112
|
start,
|
|
108
113
|
end,
|
|
109
114
|
subject,
|
|
110
|
-
subjectCode: firstSubjectToken(
|
|
115
|
+
subjectCode: firstSubjectToken(caption),
|
|
111
116
|
teacher: teachers.map((t) => [t.lastname, t.firstname].filter(Boolean).join(" ")).join(", "),
|
|
112
117
|
teacherCode: teachers.map((t) => t.abbreviation ?? "").filter(Boolean).join(", "),
|
|
113
118
|
groupId: Number(module.id) || 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wilm-ai/wilma-client",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Unofficial client for Wilma, the Finnish school information system: login (with two-step verification), schedule, homework, exams, messages and news.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsc -p tsconfig.json",
|
|
33
33
|
"lint": "tsc -p tsconfig.json --noEmit",
|
|
34
|
-
"test": "pnpm run build && node test/login.mjs && node test/parser-messages.mjs && node test/parser-news.mjs && node test/parser-schedule.mjs && node test/parser-students.mjs && node test/client-students.mjs && node test/client-schedule.mjs && node test/client-news-resources.mjs && node test/network-error.mjs && node test/audit.mjs && node test/real-pages.mjs && node test/client-features.mjs",
|
|
34
|
+
"test": "pnpm run build && node test/login.mjs && node test/parser-attendance.mjs && node test/parser-messages.mjs && node test/parser-news.mjs && node test/parser-schedule.mjs && node test/parser-students.mjs && node test/client-students.mjs && node test/client-schedule.mjs && node test/client-attendance.mjs && node test/client-news-resources.mjs && node test/network-error.mjs && node test/audit.mjs && node test/real-pages.mjs && node test/client-features.mjs",
|
|
35
35
|
"test:live": "tsc -p tsconfig.json && node test/live.spec.mjs",
|
|
36
36
|
"fixtures:anonymize": "node scripts/anonymize-fixture.mjs"
|
|
37
37
|
},
|