🎨 Make the parser modular

- Move some DSB related functions from parser/index to parser/dsbmobile
- Make the dsb parser a fileProvider with the DSBClient class
- Initialize the parser with a fileProvider and documentParser instance
This commit is contained in:
2023-06-02 16:59:20 +02:00
parent 54fa25f3dd
commit c6120e5f68
3 changed files with 48 additions and 42 deletions

View File

@ -11,6 +11,8 @@ import {
} from "./api/index.js";
import auth from "./api/auth.js";
import { Parser } from "./parser/index.js";
import { DSBClient } from "./parser/dsbmobile.js";
import { parseSubstitutionPlan } from "./parser/untis.js";
// Check DSB Mobile credentials are supplied
if (!process.env.DSB_USER || !process.env.DSB_PASSWORD) {
@ -28,8 +30,8 @@ app.use(express.urlencoded({ extended: true }));
// Initialize the Parser and set it to update the
// substitution plan at the specified update interval
new Parser(
process.env.DSB_USER,
process.env.DSB_PASSWORD,
new DSBClient(process.env.DSB_USER, process.env.DSB_PASSWORD),
parseSubstitutionPlan,
process.env.UPDATE_INTERVAL || 1 * 60 * 1000 // Default to 1 minute
);

View File

@ -1,4 +1,6 @@
import axios from "axios";
import { log } from "../logs.js";
const baseUrl = "https://mobileapi.dsbcontrol.de";
export async function getAuthtoken(username, password) {
@ -32,3 +34,37 @@ export async function getTimetables(authtoken) {
return urls;
}
// List of files that include timetable data
const dsbFiles = ["Schüler_Monitor - subst_001", "Schüler Morgen - subst_001"];
export class DSBClient {
constructor(dsbUser, dsbPassword) {
this.dsbUser = dsbUser;
this.dsbPassword = dsbPassword;
}
async getFiles() {
try {
// Get authtoken
const token = await getAuthtoken(this.dsbUser, this.dsbPassword);
// Fetch available files
const response = await getTimetables(token);
// Filter files that should be parsed
const timetables = response.filter((e) => dsbFiles.includes(e.title));
// Fetch the contents
const files = [];
for (let timetable of timetables) {
const result = await axios.request({
method: "GET",
url: timetable.url,
responseEncoding: "binary",
});
files.push(result.data);
}
return files;
} catch (error) {
log("Parser / DSB Mobile", "Error getting data: " + error);
}
}
}

View File

@ -1,18 +1,12 @@
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";
const dsbFiles = ["Schüler_Monitor - subst_001", "Schüler Morgen - subst_001"];
const prisma = new Prisma.PrismaClient();
export class Parser {
dsbUser;
dsbPassword;
constructor(dsbUser, dsbPassword, interval) {
this.dsbUser = dsbUser;
this.dsbPassword = dsbPassword;
constructor(fileProvider, documentParser, interval) {
this.fileProvider = fileProvider;
this.documentParser = documentParser;
// Schedule plan updates
setInterval(() => this.updatePlan(), interval);
@ -22,15 +16,13 @@ export class Parser {
async updatePlan() {
const startedAt = new Date();
try {
const data = await this.fetchDSB();
if (!data) throw "DSB request failed!";
// Request substitution plan files using the fileProvider
const files = await this.fileProvider.getFiles();
const plans = [];
for (const entry of data) {
// Download the substitution plan
const file = await this.fetchFile(entry.url);
// Parse them using the provided parser
for (const file of files) {
// Parse the substitution plan
const parsed = parseSubstitutionPlan(file);
const parsed = this.documentParser(file);
plans.push(parsed);
}
// Create a new parse event
@ -61,30 +53,6 @@ export class Parser {
log("Parser / Main", "Parse event failed: " + error);
}
}
async fetchDSB() {
try {
const token = await getAuthtoken(this.dsbUser, this.dsbPassword);
// Fetch available files
const response = await getTimetables(token);
// Filter files that should be parsed
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 { date, changes } = parsedData;
const classList = await prisma.class.findMany();