Big Update
This commit is contained in:
parent
ec6b611646
commit
18605dfc64
13
README.md
13
README.md
@ -10,6 +10,10 @@ You can install this library from [npm](https://npmjs.com/package/virtueller-stu
|
|||||||
npm install virtueller-stundenplan
|
npm install virtueller-stundenplan
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Note
|
||||||
|
|
||||||
|
This project in **very early developement**, expect things to break and change.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
This is a example on how to use the package:
|
This is a example on how to use the package:
|
||||||
@ -17,7 +21,10 @@ This is a example on how to use the package:
|
|||||||
```javascript
|
```javascript
|
||||||
const { VirtuellerStundenplan } = require("virtueller-stundenplan");
|
const { VirtuellerStundenplan } = require("virtueller-stundenplan");
|
||||||
|
|
||||||
const vs = new VirtuellerStundenplan("user@example.com", "Password");
|
const vs = new VirtuellerStundenplan({
|
||||||
|
username: "user@example.com",
|
||||||
|
password: "Password"
|
||||||
|
});
|
||||||
|
|
||||||
(async() => {
|
(async() => {
|
||||||
const timetable = await vs.getMyTimetable();
|
const timetable = await vs.getMyTimetable();
|
||||||
@ -25,7 +32,3 @@ const vs = new VirtuellerStundenplan("user@example.com", "Password");
|
|||||||
console.log(timetable);
|
console.log(timetable);
|
||||||
})()
|
})()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Note
|
|
||||||
|
|
||||||
This project in **very early developement**, expect things to break and change.
|
|
@ -1,110 +1,269 @@
|
|||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
const EncryptionHelper = require("./EncryptionHelper");
|
const EncryptionHelper = require("./EncryptionHelper");
|
||||||
|
|
||||||
|
const { Head, Functions, Message, Homework, SchoolYear, TimetableDay, AbsentSummary } = require("./parsers");
|
||||||
|
|
||||||
class VirtuellerStundenplan {
|
class VirtuellerStundenplan {
|
||||||
baseUrl = "https://virtueller-stundenplan.de/Reservierung/";
|
baseUrl = "https://virtueller-stundenplan.de/Reservierung/";
|
||||||
|
|
||||||
constructor(username, password, key) {
|
#password;
|
||||||
this.username = username;
|
|
||||||
this.password = password;
|
constructor(config) {
|
||||||
if(key) this.key = key;
|
this.username = config.username;
|
||||||
|
this.#password = config.password;
|
||||||
|
if(config.key) this.key = config.key;
|
||||||
|
|
||||||
|
//this.teacherList = config.teachers;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchKey(returnKey=false) {
|
async fetchKey(returnKey=false) {
|
||||||
const url = new URL("RestGetKeyphrase.php", this.baseUrl);
|
const url = new URL("RestGetKeyphrase.php", this.baseUrl);
|
||||||
|
|
||||||
try {
|
const req = await axios(url.href, {
|
||||||
const req = await axios(url.href, {
|
headers: {
|
||||||
headers: {
|
Authorization: "Basic " + Buffer.from(this.username + ":" + this.#password).toString('base64'),
|
||||||
Authorization: "Basic " + Buffer.from(this.username + ":" + this.password).toString('base64'),
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
this.key = req.data.KeyPhrase;
|
this.key = req.data.KeyPhrase;
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnKey ? this.key : true;
|
return returnKey ? this.key : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEnabledFunctions() {
|
/**
|
||||||
return await this.request("RESTEnabledFunctions.php");
|
* Returns all enabled and disabled functions.
|
||||||
|
*
|
||||||
|
* @returns {Functions}
|
||||||
|
*/
|
||||||
|
async getFunctions() {
|
||||||
|
return new Functions(await this.request("RESTEnabledFunctions.php"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns something with Courses.
|
||||||
|
* This Response is currently unparsed.
|
||||||
|
*
|
||||||
|
* @returns {Any[]}
|
||||||
|
*/
|
||||||
async getCourseList() {
|
async getCourseList() {
|
||||||
return await this.request("RESTKursliste.php");
|
const data = await this.request("RESTKursliste.php");
|
||||||
|
return {
|
||||||
|
head: new Head(data.Msg),
|
||||||
|
content: data.Kurse,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all Messages.
|
||||||
|
*
|
||||||
|
* @returns {Message[]}
|
||||||
|
*/
|
||||||
async getMessages() {
|
async getMessages() {
|
||||||
return await this.request("RESTMessages.php");
|
const data = await this.request("RESTMessages.php");
|
||||||
|
let res = [];
|
||||||
|
data.Messages.forEach(message => {
|
||||||
|
res.push(new Message(message));
|
||||||
|
});
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getHomework response
|
||||||
|
*
|
||||||
|
* @typedef {Object} getHomeworkResponse
|
||||||
|
* @property {Head} head - Info to the response
|
||||||
|
* @property {Homework[]} content
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all Homework (Classbook) entries.
|
* Returns all Homework (Classbook) entries.
|
||||||
* Date arguments are inclusive.
|
* Date arguments are inclusive.
|
||||||
*
|
*
|
||||||
* @param {String|Date} from
|
* @param {[String|Date|number]} [from=Date.now()] - First Day
|
||||||
* @param {String|Date} to
|
* @param {[String|Date|number]} [to=Date.now()] - Last Day
|
||||||
* @returns {Object}
|
* @returns {getHomeworkResponse} getHomework response
|
||||||
*/
|
*/
|
||||||
async getHomework(from=new Date(), to=new Date()) {
|
async getHomework(from=new Date(), to=new Date()) {
|
||||||
const query = new URLSearchParams();
|
const query = new URLSearchParams();
|
||||||
query.set("KlaBuDateVon", this.getDateString(from));
|
query.set("KlaBuDateVon", this.getDateString(from));
|
||||||
query.set("KlaBuDateBis", this.getDateString(to));
|
query.set("KlaBuDateBis", this.getDateString(to));
|
||||||
return await this.request("RESTHausaufgaben.php", query);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getSchoolYears() {
|
const data = await this.request("RESTHausaufgaben.php", query);
|
||||||
return await this.request("RESTSchuljahre.php");
|
|
||||||
|
let res = {
|
||||||
|
head: new Head(data.Msg),
|
||||||
|
content: [],
|
||||||
|
};
|
||||||
|
data.Inhalte.forEach(homework => {
|
||||||
|
res.content.push(new Homework(homework));
|
||||||
|
});
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* getSchoolYears response
|
||||||
*
|
*
|
||||||
* @param {string|number} schoolYear ID of the school year
|
* @typedef {Object} getSchoolYearsResponse
|
||||||
* @returns
|
* @property {Head} head - Info to the response
|
||||||
|
* @property {SchoolYear[]} content
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Returns all school years.
|
||||||
|
*
|
||||||
|
* @returns {getSchoolYearsResponse} getSchoolYears response
|
||||||
|
*/
|
||||||
|
async getSchoolYears() {
|
||||||
|
const data = await this.request("RESTSchuljahre.php");
|
||||||
|
let res = {
|
||||||
|
head: new Head(data.Msg),
|
||||||
|
content: [],
|
||||||
|
};
|
||||||
|
data.Schuljahre.forEach(year => {
|
||||||
|
res.content.push(new SchoolYear(year));
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns own grades (performances).
|
||||||
|
* The response is currently mostly unparsed
|
||||||
|
*
|
||||||
|
* @param {string|number} schoolYear ID of the school year.
|
||||||
|
* @returns {Object} response
|
||||||
|
* @returns {Head} response.head - Info to the response
|
||||||
|
* @returns {Object} response.content
|
||||||
*/
|
*/
|
||||||
async getMyGrades(schoolYear) {
|
async getMyGrades(schoolYear) {
|
||||||
const query = new URLSearchParams();
|
const query = new URLSearchParams();
|
||||||
query.set("SJ", schoolYear);
|
query.set("SJ", schoolYear);
|
||||||
return await this.request("RESTMeineNoten.php", query);
|
const data = await this.request("RESTMeineNoten.php", query);
|
||||||
|
|
||||||
|
return {
|
||||||
|
head: new Head(data.Msg),
|
||||||
|
content: {
|
||||||
|
performances: data.Leistungen,
|
||||||
|
totalPerformances: data.LeistungenGesamt,
|
||||||
|
comment: data.Kommentar,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getTimetable response
|
||||||
|
*
|
||||||
|
* @typedef {Object} getTimetableResponse
|
||||||
|
* @property {Head} head - Info to the response
|
||||||
|
* @property {TimetableDay[]} content
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Returns own timetable.
|
||||||
|
*
|
||||||
|
* Small note: If you are in only one class, and want to
|
||||||
|
* fetch it with the class name, the response head will
|
||||||
|
* be slightly different -> has a bit less metadata
|
||||||
|
*
|
||||||
|
* @param {[String|Date|number]} [from=Date.now()] - Date or timestamp
|
||||||
|
* @param {[String|Date|number]} [Class] - The class of which timetable should be fetched. unset/null to own timetable.
|
||||||
|
* @returns {getTimetableResponse} getTimetable response
|
||||||
|
*/
|
||||||
|
async getTimetable(from=new Date(), Class=null) {
|
||||||
|
const query = new URLSearchParams();
|
||||||
|
query.set("KlaBuDateVon", this.getDateString(from));
|
||||||
|
if(Class) query.set("KLASSE", Class);
|
||||||
|
|
||||||
|
const data = await this.request(Class ? "RESTKlassenStundenplan.php" : "RESTMeinStundenplan.php", query);
|
||||||
|
|
||||||
|
let res = {
|
||||||
|
head: new Head(data.Msg),
|
||||||
|
content: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < data.Stunden.length; i++) {
|
||||||
|
const day = [
|
||||||
|
data.Stunden[i],
|
||||||
|
data.StundenF[i],
|
||||||
|
data.StundenR[i],
|
||||||
|
data.StundenK[i],
|
||||||
|
data.CellStyles[i],
|
||||||
|
];
|
||||||
|
|
||||||
|
const hasContent = /[\da-zA-Z]/.test(JSON.stringify(day));
|
||||||
|
|
||||||
|
res.content[i] = hasContent ? new TimetableDay(day) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns own timetable.
|
||||||
|
* This function is a alias of getTimetable(), but without the Class option
|
||||||
|
*
|
||||||
|
* @returns {getTimetableResponse} getTimetable response
|
||||||
|
*/
|
||||||
async getMyTimetable(from=new Date()) {
|
async getMyTimetable(from=new Date()) {
|
||||||
const query = new URLSearchParams();
|
return await this.getTimetable(from);
|
||||||
query.set("KlaBuDateVon", this.getDateString(from));
|
|
||||||
return await this.request("RESTMeinStundenplan.php", query);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getTimetable(Class, from=new Date()) {
|
|
||||||
const query = new URLSearchParams();
|
|
||||||
query.set("KlaBuDateVon", this.getDateString(from));
|
|
||||||
query.set("KLASSE", Class);
|
|
||||||
return await this.request("RESTKlassenStundenplan.php", query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns own Infos.
|
||||||
|
* This Response is currently unparsed.
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
async getMyInfos() {
|
async getMyInfos() {
|
||||||
return await this.request("RESTSuSInfo.php");
|
return await this.request("RESTSuSInfo.php");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns own profile picture.
|
||||||
|
* The response is currently mostly unparsed
|
||||||
|
*
|
||||||
|
* @returns {Object} response
|
||||||
|
* @returns {Head} response.head - Info to the response
|
||||||
|
* @returns {Object} response.content
|
||||||
|
*/
|
||||||
async getMyPicture() {
|
async getMyPicture() {
|
||||||
return await this.request("RESTSuSMeinBild.php");
|
const data = await this.request("RESTSuSMeinBild.php");
|
||||||
|
|
||||||
|
return {
|
||||||
|
head: new Head(data.Msg),
|
||||||
|
content: {
|
||||||
|
schoolName: data.Schulname,
|
||||||
|
picture: data.Bild,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BBS ID QR
|
* Returns own BBS ID QR Code.
|
||||||
|
* This Response is currently unparsed.
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
async getMyBBSIDQR() {
|
async getMyBBSIDQR() {
|
||||||
return await this.request("RESTBBSIDQR.php");
|
return await this.request("RESTBBSIDQR.php");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BBS GUID QR
|
* Returns own BBS GUID QR Code.
|
||||||
|
* This Response is currently unparsed.
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
async getMyBBSGUID() {
|
async getMyBBSGUID() {
|
||||||
return await this.request("RESTBBSIDGUID.php");
|
return await this.request("RESTBBSIDGUID.php");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports self as sick.
|
||||||
|
*
|
||||||
|
* This Request is untested.
|
||||||
|
*
|
||||||
|
* This Response is currently unparsed.
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
async reportAsSick(from=new Date(), to=new Date()) {
|
async reportAsSick(from=new Date(), to=new Date()) {
|
||||||
const query = new URLSearchParams();
|
const query = new URLSearchParams();
|
||||||
query.set("KlaBuDateVon", this.getDateString(from));
|
query.set("KlaBuDateVon", this.getDateString(from));
|
||||||
@ -112,20 +271,60 @@ class VirtuellerStundenplan {
|
|||||||
return await this.request("RESTSuSKrankmelden.php", query);
|
return await this.request("RESTSuSKrankmelden.php", query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getAbsentDays response
|
||||||
|
*
|
||||||
|
* @typedef {Object} getAbsentDaysResponse
|
||||||
|
* @property {Head} head - Info to the response
|
||||||
|
* @property {AbsentSummary[]} content
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Returns absent days.
|
||||||
|
*
|
||||||
|
* @param {[String|Date|number]} [from=Date.now()] - Date or timestamp
|
||||||
|
* @param {[String|Date|number]} [to=Date.now()] - Date or timestamp
|
||||||
|
* @returns {getAbsentDaysResponse} getAbsentDays response
|
||||||
|
*/
|
||||||
async getAbsentDays(from=new Date(), to=new Date()) {
|
async getAbsentDays(from=new Date(), to=new Date()) {
|
||||||
const query = new URLSearchParams();
|
const query = new URLSearchParams();
|
||||||
query.set("KlaBuDateVon", this.getDateString(from));
|
query.set("KlaBuDateVon", this.getDateString(from));
|
||||||
query.set("KlaBuDateBis", this.getDateString(to));
|
query.set("KlaBuDateBis", this.getDateString(to));
|
||||||
return await this.request("RESTFehltageliste.php", query);
|
|
||||||
|
const data = await this.request("RESTFehltageliste.php", query);
|
||||||
|
|
||||||
|
return {
|
||||||
|
head: new Head(data.Msg),
|
||||||
|
content: new AbsentSummary(data),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of exams.
|
||||||
|
* The response is currently mostly unparsed
|
||||||
|
*
|
||||||
|
* @returns {Object} response
|
||||||
|
* @returns {Head} response.head - Info to the response
|
||||||
|
* @returns {Object[]} response.content
|
||||||
|
*/
|
||||||
async getExamList(from=new Date(), to=new Date()) {
|
async getExamList(from=new Date(), to=new Date()) {
|
||||||
const query = new URLSearchParams();
|
const query = new URLSearchParams();
|
||||||
query.set("KlaBuDateVon", this.getDateString(from));
|
query.set("KlaBuDateVon", this.getDateString(from));
|
||||||
query.set("KlaBuDateBis", this.getDateString(to));
|
query.set("KlaBuDateBis", this.getDateString(to));
|
||||||
return await this.request("RESTKlausurliste.php", query);
|
|
||||||
|
const data = await this.request("RESTKlausurliste.php", query);
|
||||||
|
|
||||||
|
return {
|
||||||
|
head: new Head(data.Msg),
|
||||||
|
content: data.Klausuren,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes to notifications.
|
||||||
|
* This Response is currently unparsed.
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
async subscribeToNotifications(Token, Register, IOS) {
|
async subscribeToNotifications(Token, Register, IOS) {
|
||||||
const query = new URLSearchParams();
|
const query = new URLSearchParams();
|
||||||
query.set("Token", Token);
|
query.set("Token", Token);
|
||||||
@ -141,6 +340,9 @@ class VirtuellerStundenplan {
|
|||||||
if(date instanceof Date) {
|
if(date instanceof Date) {
|
||||||
return date.toISOString().slice(0, 10);
|
return date.toISOString().slice(0, 10);
|
||||||
}
|
}
|
||||||
|
if(typeof date === "number") {
|
||||||
|
return this.getDateString(new Date(date));
|
||||||
|
}
|
||||||
throw new Error(`"${date}" is not a valid type for date.`);
|
throw new Error(`"${date}" is not a valid type for date.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,16 +359,11 @@ class VirtuellerStundenplan {
|
|||||||
if(query) url.search = query.toString();
|
if(query) url.search = query.toString();
|
||||||
url.searchParams.set("key", EncryptionHelper.makeEncryptedParameter(this.username, this.key));
|
url.searchParams.set("key", EncryptionHelper.makeEncryptedParameter(this.username, this.key));
|
||||||
|
|
||||||
let req;
|
const req = await axios(url.href, {
|
||||||
try {
|
headers: {
|
||||||
req = await axios(url.href, {
|
Authorization: "Basic " + Buffer.from(this.username + ":" + this.#password).toString('base64'),
|
||||||
headers: {
|
},
|
||||||
Authorization: "Basic " + Buffer.from(this.username + ":" + this.password).toString('base64'),
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return req.data;
|
return req.data;
|
||||||
}
|
}
|
||||||
|
36
classes/parsers/AbsentDay.js
Normal file
36
classes/parsers/AbsentDay.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const ParseTools = require("./ParseTools");
|
||||||
|
|
||||||
|
class AbsentDay {
|
||||||
|
constructor(data) {
|
||||||
|
this.date = ParseTools.parseDateTime(data.Datum);
|
||||||
|
this.hour = data.Stunde;
|
||||||
|
this.status = data.Status;
|
||||||
|
this.comment = ParseTools.isFilled(data.Bemerkung);
|
||||||
|
}
|
||||||
|
|
||||||
|
get present() {
|
||||||
|
switch (this.status) {
|
||||||
|
case "o":
|
||||||
|
case "G5":
|
||||||
|
case "L5":
|
||||||
|
case "V5":
|
||||||
|
return true;
|
||||||
|
case "--":
|
||||||
|
case "K":
|
||||||
|
case "E":
|
||||||
|
case "A":
|
||||||
|
case "( )":
|
||||||
|
case "U":
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get absent() {
|
||||||
|
if(this.present === null) return null;
|
||||||
|
return !this.present;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AbsentDay
|
20
classes/parsers/AbsentSummary.js
Normal file
20
classes/parsers/AbsentSummary.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const ParseTools = require("./ParseTools");
|
||||||
|
const AbsentDay = require("./AbsentDay");
|
||||||
|
|
||||||
|
class AbsentSummary {
|
||||||
|
days = [];
|
||||||
|
|
||||||
|
constructor(data) {
|
||||||
|
this.presentDays = ParseTools.daysToArray(data.Summary['Anwesend am']);
|
||||||
|
this.absentDays = ParseTools.daysToArray(data.Summary['Fehlte am']);
|
||||||
|
this.partlyAbsentDays = ParseTools.daysToArray(data.Summary['Fehlte teilweise am']);
|
||||||
|
this.unexcusedDays = ParseTools.daysToArray(data.Summary['unentschuldigte Tage']);
|
||||||
|
this.excusedDays = ParseTools.daysToArray(data.Summary['unentschuldigte Tage']);
|
||||||
|
|
||||||
|
data.Details.forEach((day, i) => {
|
||||||
|
this.days[i] = new AbsentDay(day);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AbsentSummary
|
16
classes/parsers/Functions.js
Normal file
16
classes/parsers/Functions.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
class Functions {
|
||||||
|
#data;
|
||||||
|
|
||||||
|
constructor(data) {
|
||||||
|
this.#data = data;
|
||||||
|
|
||||||
|
this.enabled = data.Enabled;
|
||||||
|
this.disabled = data.Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAll() {
|
||||||
|
return [...this.enabled, ...this.disabled];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Functions
|
37
classes/parsers/Head.js
Normal file
37
classes/parsers/Head.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
const ParseTools = require("./ParseTools");
|
||||||
|
|
||||||
|
class Head {
|
||||||
|
#data;
|
||||||
|
|
||||||
|
constructor(data) {
|
||||||
|
this.#data = data;
|
||||||
|
|
||||||
|
this.httpStatus = data.Status ? Number(data.Status) : null;
|
||||||
|
|
||||||
|
this.from = data.KlaBuDateVon ? ParseTools.parseDateTime(data.KlaBuDateVon) : undefined;
|
||||||
|
this.to = data.KlaBuDateBis ? ParseTools.parseDateTime(data.KlaBuDateBis) : undefined;
|
||||||
|
|
||||||
|
if(data['Von h']) this.fromHour = data['Von h'];
|
||||||
|
if(data['Bis h']) this.toHour = data['Bis h'];
|
||||||
|
|
||||||
|
if(data['Von Wochentag']) this.fromWeekDay = data['Von Wochentag'];
|
||||||
|
if(data['Bis Wochentag']) this.toWeekDay = data['Bis Wochentag'];
|
||||||
|
|
||||||
|
if(data.AktuelleID) this.currentID = Number(data.AktuelleID);
|
||||||
|
|
||||||
|
if(data.Schuljahr) this.schoolYear = data.Schuljahr;
|
||||||
|
|
||||||
|
if(data.AnzahlLeistungen) this.totalPerformances = data.AnzahlLeistungen;
|
||||||
|
|
||||||
|
// Timetable:
|
||||||
|
if(ParseTools.isFilled(data.Name)) this.name = data.Name;
|
||||||
|
if(ParseTools.isFilled(data.Klasse)) this.class = data.Klasse;
|
||||||
|
if(data.Schulnummer) this.schoolNumber = data.Schulnummer;
|
||||||
|
if(ParseTools.isFilled(data.InKlasse)) this.inClass = data.InKlasse;
|
||||||
|
if(ParseTools.isFilled(data.InKursen)) this.inCourses = data.InKursen;
|
||||||
|
if(typeof data.EffPlan !== "undefined") this.effectiveTimetable = !!data.EffPlan;
|
||||||
|
if(typeof data.CompactPlan !== "undefined") this.compactTimetable = !!data.CompactPlan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Head
|
33
classes/parsers/Homework.js
Normal file
33
classes/parsers/Homework.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const PT = require("./ParseTools");
|
||||||
|
const Teacher = require("./Teacher");
|
||||||
|
|
||||||
|
class Homework {
|
||||||
|
#data;
|
||||||
|
|
||||||
|
constructor(data) {
|
||||||
|
this.#data = data;
|
||||||
|
|
||||||
|
this.id = data.ID;
|
||||||
|
this.date = PT.parseDateTime(this.rawDate, "dd.MM.yyyy");
|
||||||
|
this.hour = PT.isFilled(data.Stunde) ? Number(data.Stunde): null;
|
||||||
|
this.class = PT.isFilled(data.Klasse, true);
|
||||||
|
this.course = PT.isFilled(data.Kurs, true);
|
||||||
|
this.course = PT.isFilled(data.Kursschiene, true);
|
||||||
|
this.teacher = PT.isFilled(data.LK, true);
|
||||||
|
//data.Vertretung - value in testing always "" or "0"
|
||||||
|
this.subject = PT.isFilled(data.Fach, true);
|
||||||
|
this.room = PT.isFilled(data.Raum, true);
|
||||||
|
this.note = PT.isFilled(data.Bemerkung, true);
|
||||||
|
this.homework = PT.isFilled(data.Hausaufgaben, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
get rawDate() {
|
||||||
|
return this.#data.Datum;
|
||||||
|
}
|
||||||
|
|
||||||
|
get raw() {
|
||||||
|
return this.#data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Homework
|
22
classes/parsers/Message.js
Normal file
22
classes/parsers/Message.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
const ParseTools = require("./ParseTools");
|
||||||
|
|
||||||
|
class Message {
|
||||||
|
#data;
|
||||||
|
|
||||||
|
constructor(data) {
|
||||||
|
this.#data = data;
|
||||||
|
|
||||||
|
this.content = data.Message;
|
||||||
|
this.timestamp = ParseTools.parseDateTime(data.rawTimestamp, "yyyy-MM-dd HH:mm:ss");
|
||||||
|
this.target = data.Target;
|
||||||
|
this.type = data.Typ;
|
||||||
|
this.course = data.Kurs;
|
||||||
|
this.class = data.Klasse;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rawTimestamp() {
|
||||||
|
return this.#data.Datum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Message
|
23
classes/parsers/ParseTools.js
Normal file
23
classes/parsers/ParseTools.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const { DateTime } = require("luxon");
|
||||||
|
|
||||||
|
class ParseTools {
|
||||||
|
static parseDateTime(date, format="yyyy-MM-dd") {
|
||||||
|
return DateTime.fromFormat(date, format, { zone: "Europe/Berlin" });
|
||||||
|
}
|
||||||
|
|
||||||
|
static isFilled(value, returnValOrNull=false) {
|
||||||
|
const res = value !== "" && value !== "-";
|
||||||
|
return returnValOrNull ? (res ? value : null) : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static daysToArray(days, seperator=", ", format="dd.MM.yyyy") {
|
||||||
|
let res = [];
|
||||||
|
days.split(seperator).forEach(day => {
|
||||||
|
if(day === "") return;
|
||||||
|
res.push(this.parseDateTime(day, format));
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ParseTools
|
24
classes/parsers/SchoolYear.js
Normal file
24
classes/parsers/SchoolYear.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const ParseTools = require("./ParseTools");
|
||||||
|
|
||||||
|
class SchoolYear {
|
||||||
|
#data;
|
||||||
|
|
||||||
|
constructor(data) {
|
||||||
|
this.#data = data;
|
||||||
|
|
||||||
|
this.id = data.ID;
|
||||||
|
this.title = data.SJ;
|
||||||
|
this.from = ParseTools.parseDateTime(this.rawFrom);
|
||||||
|
this.to = ParseTools.parseDateTime(this.rawTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
get rawFrom() {
|
||||||
|
return this.#data.von;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rawTo() {
|
||||||
|
return this.#data.bis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SchoolYear
|
28
classes/parsers/Teacher.js
Normal file
28
classes/parsers/Teacher.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
class Teacher {
|
||||||
|
/**
|
||||||
|
* Creates a Instance of Teacher
|
||||||
|
*
|
||||||
|
* @param {String} teacher - Code for the teacher.
|
||||||
|
* @param {Object} teacherList - Object of teachers.
|
||||||
|
*/
|
||||||
|
constructor(teacher, teacherList) {
|
||||||
|
/** @returns {String} Teacher's code */
|
||||||
|
this.code = teacher;
|
||||||
|
|
||||||
|
teacher = teacherList && teacherList[this.code] ? teacherList[this.code] : {};
|
||||||
|
|
||||||
|
/** @returns {String} First name(s) of the teacher. If unset the teacher's code. */
|
||||||
|
this.firstName = teacher.firstName ?? this.code;
|
||||||
|
/** @returns {String} Last name of the teacher. If unset the teacher's code. */
|
||||||
|
this.lastName = teacher.lastName ?? this.code;
|
||||||
|
|
||||||
|
let name = [];
|
||||||
|
if(teacher.firstName) name.push(teacher.firstName);
|
||||||
|
if(teacher.lastName) name.push(teacher.lastName);
|
||||||
|
|
||||||
|
/** @returns {String} Full name of the teacher. If unset the teacher's code. */
|
||||||
|
this.fullName = name.join(" ") || this.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Teacher
|
33
classes/parsers/TimetableDay.js
Normal file
33
classes/parsers/TimetableDay.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const TimetableHour = require("./TimetableHour");
|
||||||
|
|
||||||
|
class TimetableDay {
|
||||||
|
first = null;
|
||||||
|
last = null;
|
||||||
|
|
||||||
|
constructor(data) {
|
||||||
|
this.hours = [];
|
||||||
|
|
||||||
|
//console.log(data)
|
||||||
|
|
||||||
|
for (let i = 0; i < data[0].length; i++) {
|
||||||
|
const hour = [
|
||||||
|
data[0][i],
|
||||||
|
data[1][i],
|
||||||
|
data[2][i],
|
||||||
|
data[3][i],
|
||||||
|
data[4][i],
|
||||||
|
]
|
||||||
|
|
||||||
|
const hasContent = /[\da-zA-Z]/.test(JSON.stringify(hour));
|
||||||
|
|
||||||
|
if(hasContent) {
|
||||||
|
if(this.first === null) this.first = i;
|
||||||
|
this.last = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hours[i] = hasContent ? new TimetableHour(hour) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TimetableDay;
|
60
classes/parsers/TimetableHour.js
Normal file
60
classes/parsers/TimetableHour.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
const ParseTools = require("./ParseTools");
|
||||||
|
|
||||||
|
const parser = (text) => {
|
||||||
|
if(!ParseTools.isFilled(text)) return null;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
original: text,
|
||||||
|
changes: false,
|
||||||
|
note: null,
|
||||||
|
planned: text,
|
||||||
|
actual: text,
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasBold = text.includes("<b> ");
|
||||||
|
const hasDel = text.includes("<del>");
|
||||||
|
|
||||||
|
if(hasBold) {
|
||||||
|
data.actual = text.split("<b> ")[1].split("</b>")[0];
|
||||||
|
if(hasDel) data.actual = data.actual.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hasDel) {
|
||||||
|
data.planned = text.split("<del>")[1].split("</del>")[0];
|
||||||
|
data.changes = true;
|
||||||
|
if(text.substring(text.search("</del>") + 6) !== "") { // If it has reasoning/note
|
||||||
|
|
||||||
|
if(!hasBold) {
|
||||||
|
data.actual = null;
|
||||||
|
data.note = text.substring(text.search("</del>") + 6);
|
||||||
|
} else data.note = text.substring(text.search("</b>") + 5);
|
||||||
|
|
||||||
|
data.note = data.note.trim();
|
||||||
|
|
||||||
|
if(data.note === "") data.note = null;
|
||||||
|
|
||||||
|
} else data.actual = null;
|
||||||
|
} else data.planned = data.actual;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimetableDay {
|
||||||
|
#data = [];
|
||||||
|
|
||||||
|
anyChanges = false;
|
||||||
|
|
||||||
|
constructor(data) {
|
||||||
|
data.forEach((part, i) => {
|
||||||
|
this.#data[i] = parser(part);
|
||||||
|
if(this.#data[i]?.changes) this.anyChanges = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.teacher = this.#data[0];
|
||||||
|
this.subject = this.#data[1];
|
||||||
|
this.room = this.#data[2];
|
||||||
|
this.exam = this.#data[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TimetableDay;
|
9
classes/parsers/index.js
Normal file
9
classes/parsers/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
Head: require("./Head"),
|
||||||
|
Functions: require("./Functions"),
|
||||||
|
Message: require("./Message"),
|
||||||
|
Homework: require("./Homework"),
|
||||||
|
SchoolYear: require("./SchoolYear"),
|
||||||
|
TimetableDay: require("./TimetableDay"),
|
||||||
|
AbsentSummary: require("./AbsentSummary"),
|
||||||
|
}
|
16
npm-shrinkwrap.json
generated
16
npm-shrinkwrap.json
generated
@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "virtueller-stundenplan",
|
"name": "virtueller-stundenplan",
|
||||||
"version": "0.0.2",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "virtueller-stundenplan",
|
"name": "virtueller-stundenplan",
|
||||||
"version": "0.0.2",
|
"version": "1.0.0",
|
||||||
"license": "CC-BY-NC-4.0",
|
"license": "CC-BY-NC-4.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.8.4"
|
"axios": "^1.8.4",
|
||||||
|
"luxon": "^3.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/asynckit": {
|
"node_modules/asynckit": {
|
||||||
@ -254,6 +255,15 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/luxon": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
|
||||||
|
"integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "virtueller-stundenplan",
|
"name": "virtueller-stundenplan",
|
||||||
"version": "0.0.2",
|
"version": "1.0.0",
|
||||||
"description": "A library to access the data from virtueller-stundenplan.org",
|
"description": "A library to access the data from virtueller-stundenplan.org",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -18,6 +18,7 @@
|
|||||||
},
|
},
|
||||||
"license": "CC-BY-NC-4.0",
|
"license": "CC-BY-NC-4.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.8.4"
|
"axios": "^1.8.4",
|
||||||
|
"luxon": "^3.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user