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"); // Workaround no currect match possible for subsitutions of this // type beacuse they don't have a class and a subject attribute if (change.type == "Sondereins." && !change.subject) { change.subject = change.notes; } 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.sort().join(",") == substitution.class.sort().join(",") && 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.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); } }