🎨 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:
@ -11,6 +11,8 @@ import {
|
|||||||
} from "./api/index.js";
|
} from "./api/index.js";
|
||||||
import auth from "./api/auth.js";
|
import auth from "./api/auth.js";
|
||||||
import { Parser } from "./parser/index.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
|
// Check DSB Mobile credentials are supplied
|
||||||
if (!process.env.DSB_USER || !process.env.DSB_PASSWORD) {
|
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
|
// Initialize the Parser and set it to update the
|
||||||
// substitution plan at the specified update interval
|
// substitution plan at the specified update interval
|
||||||
new Parser(
|
new Parser(
|
||||||
process.env.DSB_USER,
|
new DSBClient(process.env.DSB_USER, process.env.DSB_PASSWORD),
|
||||||
process.env.DSB_PASSWORD,
|
parseSubstitutionPlan,
|
||||||
process.env.UPDATE_INTERVAL || 1 * 60 * 1000 // Default to 1 minute
|
process.env.UPDATE_INTERVAL || 1 * 60 * 1000 // Default to 1 minute
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { log } from "../logs.js";
|
||||||
|
|
||||||
const baseUrl = "https://mobileapi.dsbcontrol.de";
|
const baseUrl = "https://mobileapi.dsbcontrol.de";
|
||||||
|
|
||||||
export async function getAuthtoken(username, password) {
|
export async function getAuthtoken(username, password) {
|
||||||
@ -32,3 +34,37 @@ export async function getTimetables(authtoken) {
|
|||||||
|
|
||||||
return urls;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,18 +1,12 @@
|
|||||||
import Prisma from "@prisma/client";
|
import Prisma from "@prisma/client";
|
||||||
import axios from "axios";
|
|
||||||
import { log, getLogPath } from "../logs.js";
|
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();
|
const prisma = new Prisma.PrismaClient();
|
||||||
|
|
||||||
export class Parser {
|
export class Parser {
|
||||||
dsbUser;
|
constructor(fileProvider, documentParser, interval) {
|
||||||
dsbPassword;
|
this.fileProvider = fileProvider;
|
||||||
constructor(dsbUser, dsbPassword, interval) {
|
this.documentParser = documentParser;
|
||||||
this.dsbUser = dsbUser;
|
|
||||||
this.dsbPassword = dsbPassword;
|
|
||||||
|
|
||||||
// Schedule plan updates
|
// Schedule plan updates
|
||||||
setInterval(() => this.updatePlan(), interval);
|
setInterval(() => this.updatePlan(), interval);
|
||||||
@ -22,15 +16,13 @@ export class Parser {
|
|||||||
async updatePlan() {
|
async updatePlan() {
|
||||||
const startedAt = new Date();
|
const startedAt = new Date();
|
||||||
try {
|
try {
|
||||||
const data = await this.fetchDSB();
|
// Request substitution plan files using the fileProvider
|
||||||
if (!data) throw "DSB request failed!";
|
const files = await this.fileProvider.getFiles();
|
||||||
|
|
||||||
const plans = [];
|
const plans = [];
|
||||||
for (const entry of data) {
|
// Parse them using the provided parser
|
||||||
// Download the substitution plan
|
for (const file of files) {
|
||||||
const file = await this.fetchFile(entry.url);
|
|
||||||
// Parse the substitution plan
|
// Parse the substitution plan
|
||||||
const parsed = parseSubstitutionPlan(file);
|
const parsed = this.documentParser(file);
|
||||||
plans.push(parsed);
|
plans.push(parsed);
|
||||||
}
|
}
|
||||||
// Create a new parse event
|
// Create a new parse event
|
||||||
@ -61,30 +53,6 @@ export class Parser {
|
|||||||
log("Parser / Main", "Parse event failed: " + error);
|
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) {
|
async insertSubstitutions(parsedData, parseEvent) {
|
||||||
const { date, changes } = parsedData;
|
const { date, changes } = parsedData;
|
||||||
const classList = await prisma.class.findMany();
|
const classList = await prisma.class.findMany();
|
||||||
|
Reference in New Issue
Block a user