150 lines
4.6 KiB
JavaScript
150 lines
4.6 KiB
JavaScript
import Prisma from "@prisma/client";
|
|
import axios from "axios";
|
|
import { log, getLogPath } from "../logs.js";
|
|
import { getAuthtoken, getTimetables } from "./dsbmobile.js";
|
|
import { parseSubstitutionPlan } from "./untis.js";
|
|
import fs from "fs";
|
|
|
|
const prisma = new Prisma.PrismaClient();
|
|
|
|
const dsbFiles = ["Schüler_Monitor - subst_001", "Schüler Morgen - subst_001"];
|
|
|
|
export class Parser {
|
|
dsbUser;
|
|
dsbPassword;
|
|
constructor(dsbUser, dsbPassword, interval) {
|
|
this.dsbUser = dsbUser;
|
|
this.dsbPassword = dsbPassword;
|
|
|
|
// setInterval(this.updatePlan, interval);
|
|
// this.updatePlan();
|
|
(async () => {
|
|
const event = await prisma.parseEvent.findFirst({
|
|
where: {
|
|
succeeded: true,
|
|
},
|
|
});
|
|
const plan = JSON.parse(fs.readFileSync("tmpplans.json"));
|
|
this.insertSubstitutions(plan, event);
|
|
})();
|
|
}
|
|
async updatePlan() {
|
|
const startedAt = new Date();
|
|
try {
|
|
const data = await this.fetchDSB();
|
|
if (!data) {
|
|
throw "DSB request failed!";
|
|
}
|
|
const plans = [];
|
|
for (const entry of data) {
|
|
const data = await this.fetchFile(entry.url);
|
|
const parsed = parseSubstitutionPlan(data);
|
|
plans.push(parsed);
|
|
}
|
|
const parseEvent = await prisma.parseEvent.create({
|
|
data: {
|
|
logFile: getLogPath(),
|
|
originalData: "",
|
|
duration: new Date() - startedAt,
|
|
succeeded: true,
|
|
},
|
|
});
|
|
for (const plan of plans) {
|
|
await this.insertSubstitutions(plans, parseEvent);
|
|
fs.writeFileSync("tmpfile", JSON.stringify(plan));
|
|
}
|
|
} catch (error) {
|
|
await prisma.parseEvent.create({
|
|
data: {
|
|
logFile: getLogPath(),
|
|
originalData: error.toString(),
|
|
duration: new Date() - startedAt,
|
|
succeeded: false,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
async fetchDSB() {
|
|
try {
|
|
const token = await getAuthtoken(this.dsbUser, this.dsbPassword);
|
|
const response = await getTimetables(token);
|
|
const timetables = response.filter((e) => dsbFiles.includes(e.title));
|
|
return timetables;
|
|
} catch (error) {
|
|
log("Parser / DSB Mobile", "Error getting data: " + error);
|
|
}
|
|
return false;
|
|
}
|
|
async fetchFile(url) {
|
|
const result = await axios.request({
|
|
method: "GET",
|
|
url: url,
|
|
responseEncoding: "binary",
|
|
});
|
|
return result.data;
|
|
}
|
|
async parsePlan(html) {
|
|
return parseSubstitutionPlan(html);
|
|
}
|
|
async insertSubstitutions(parsedData, parseEvent) {
|
|
const { updatedAt, date, changes } = parsedData;
|
|
const classList = await prisma.class.findMany();
|
|
|
|
const knownSubstitutions = await prisma.substitution.findMany({
|
|
where: {
|
|
date: new Date(new Date(date).setUTCHours(0, 0, 0, 0)),
|
|
},
|
|
});
|
|
|
|
for (const change of changes) {
|
|
const classes = this.getSubstitutionClasses(classList, change.class);
|
|
if (classes.length == 0) classes.push(change.class || "unknown");
|
|
|
|
const matchingSubstitution = knownSubstitutions.find((substitution) => {
|
|
return substitution.date == new Date(date).setUTCHours(0, 0, 0, 0) &&
|
|
substitution.type == (change.type == "Entfall")
|
|
? "cancellation"
|
|
: "change" &&
|
|
substitution.lesson == change.lesson &&
|
|
classes.every((el) => substitution.class.includes(el)) &&
|
|
substitution.changedSubject == change.subject;
|
|
});
|
|
|
|
if (!matchingSubstitution) {
|
|
const newSubstitution = await prisma.substitution.create({
|
|
data: {
|
|
class: classes,
|
|
date: new Date(date),
|
|
type: change.type == "Entfall" ? "cancellation" : "change",
|
|
lesson: parseInt(change.lesson),
|
|
changedTeacher: change.changedTeacher,
|
|
changedRoom: change.type == "Raum-Vtr." ? change.room : undefined,
|
|
changedSubject: change.subject,
|
|
notes: change.notes,
|
|
removed: false,
|
|
},
|
|
});
|
|
const substitutionChange = await prisma.substitutionChange.create({
|
|
data: {
|
|
substitutionId: newSubstitution.id,
|
|
type: "addition",
|
|
changes: {},
|
|
parseEventId: parseEvent.id,
|
|
},
|
|
});
|
|
log(
|
|
"Insert / DB",
|
|
`Created new substitution: S:${newSubstitution.id} C:${substitutionChange.id}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
getSubstitutionClasses(classList, classString) {
|
|
const matchingClasses = classList.filter((element) => {
|
|
const regex = new RegExp(element.regex);
|
|
return (classString || "").toLowerCase().match(regex);
|
|
});
|
|
return matchingClasses.map((e) => e.name);
|
|
}
|
|
}
|