Files
Timetable-V2/server/parser/index.js

154 lines
4.7 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 html = fs.readFileSync("plan.html");
const plan = parseSubstitutionPlan(html);
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}`
);
// console.log("Oh no...", change);
} else {
console.log("ok");
}
}
}
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);
}
}