
me.vertretungsplan.parser.DSBMobileParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of parser Show documentation
Show all versions of parser Show documentation
Java library for parsing schools' substitution schedules. Supports multiple different systems mainly used in the German-speaking countries.
/*
* substitution-schedule-parser - Java library for parsing schools' substitution schedules
* Copyright (c) 2016 Johan v. Forstner
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package me.vertretungsplan.parser;
import me.vertretungsplan.exception.CredentialInvalidException;
import me.vertretungsplan.objects.AdditionalInfo;
import me.vertretungsplan.objects.SubstitutionSchedule;
import me.vertretungsplan.objects.SubstitutionScheduleData;
import me.vertretungsplan.objects.SubstitutionScheduleDay;
import me.vertretungsplan.objects.authentication.SchoolNumberPasswordAuthenticationData;
import me.vertretungsplan.objects.credential.SchoolNumberPasswordCredential;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.ContentType;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.ISODateTimeFormat;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class DSBMobileParser extends UntisCommonParser {
private static final String URL = "https://app.dsbcontrol.de/JsonHandlerWeb.ashx/GetData";
private static final String ENCODING = "UTF-8";
private JSONObject data;
public DSBMobileParser(SubstitutionScheduleData scheduleData, CookieProvider cookieProvider) {
super(scheduleData, cookieProvider);
data = scheduleData.getData();
}
private static String decode(String input) throws IOException {
byte[] inputBytes = Base64.decodeBase64(input);
InputStream is = new GZIPInputStream(new ByteArrayInputStream(inputBytes));
return IOUtils.toString(is, ENCODING);
}
private static String encode(String input) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
OutputStream gzipOs = new GZIPOutputStream(os);
gzipOs.write(input.getBytes(ENCODING));
gzipOs.close();
byte[] outputBytes = os.toByteArray();
return Base64.encodeBase64String(outputBytes);
}
@Override
public SubstitutionSchedule getSubstitutionSchedule() throws IOException, JSONException,
CredentialInvalidException {
if (credential == null || !(credential instanceof SchoolNumberPasswordCredential)
|| ((SchoolNumberPasswordCredential) credential).getPassword() == null
|| ((SchoolNumberPasswordCredential) credential).getPassword().isEmpty()) {
throw new IOException("no login");
}
String login = ((SchoolNumberPasswordAuthenticationData) scheduleData.getAuthenticationData()).getSchoolNumber();
String password = ((SchoolNumberPasswordCredential) credential).getPassword();
JSONObject data = getDataFromApi(login, password);
int resultcode = data.getInt("Resultcode");
if (resultcode == 1) {
throw new CredentialInvalidException();
} else if (resultcode != 0) throw new IOException("Resultcode " + resultcode);
JSONObject timetablePage = getPageByType(data, "timetable");
if (timetablePage == null) throw new IOException("no timetable page found");
JSONArray timetableModules = timetablePage.getJSONObject("Root").getJSONArray("Childs" /* sic! */);
SubstitutionSchedule v = SubstitutionSchedule.fromData(scheduleData);
List usedUrls = new ArrayList<>();
for (int i = 0; i < timetableModules.length(); i++) {
JSONArray timetableParts = timetableModules.getJSONObject(i).getJSONArray("Childs" /* sic! */);
for (int j = 0; j < timetableParts.length(); j++) {
JSONObject timetablePart = timetableParts.getJSONObject(j);
if (timetablePart.getInt("ConType") != 6) continue;
String url = timetablePart.getString("Detail");
loadScheduleFromUrl(v, url, usedUrls);
if (timetablePart.has("Date") && v.getLastChangeString() == null) {
v.setLastChange(DateTimeFormat.forPattern("dd.MM.yyyy HH:mm").parseLocalDateTime(
timetablePart.getString("Date")));
}
}
}
JSONObject newsPage = getPageByType(data, "news");
if (newsPage != null) {
JSONArray newsItems = newsPage.getJSONObject("Root").getJSONArray("Childs" /* sic! */);
List infos = new ArrayList<>(newsItems.length());
for (int i = 0; i < newsItems.length(); i++) {
JSONObject newsItem = newsItems.getJSONObject(i);
if (!newsItem.getString("Id").equals("00000000-0000-0000-0000-000000000000")) {
AdditionalInfo info = new AdditionalInfo();
info.setHasInformation(false);
info.setTitle(newsItem.getString("Title") + " (" + newsItem.getString("Date") + ")");
String message = newsItem.getString("Detail");
message = message.replaceAll("-+\\s*\\n+\\s*-+", "");
info.setText(message);
infos.add(info);
}
}
v.getAdditionalInfos().addAll(infos);
}
v.setClasses(getAllClasses());
v.setTeachers(getAllTeachers());
v.setWebsite("http://mobile.dsbcontrol.de/");
return v;
}
private JSONObject getPageByType(JSONObject data, String type) throws JSONException {
JSONArray resultMenuItems = data.getJSONArray("ResultMenuItems");
JSONArray content = resultMenuItems.getJSONObject(0).getJSONArray("Childs" /* sic! */);
for (int i = 0; i < content.length(); i++) {
JSONObject child = content.getJSONObject(i);
if (child.getString("MethodName").equals(type)) {
return child;
}
}
return null;
}
private JSONObject getDataFromApi(String login, String password) throws JSONException, IOException {
JSONObject json = new JSONObject();
json.put("AppId", "");
json.put("PushId", "");
json.put("UserId", login);
json.put("UserPw", password);
json.put("AppVersion", "0.8");
json.put("Device", "WebApp");
json.put("OsVersion",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36");
json.put("Language", "de");
json.put("Date", ISODateTimeFormat.dateTime().print(DateTime.now()));
json.put("LastUpdate", ISODateTimeFormat.dateTime().print(DateTime.now()));
json.put("BundleId", "de.heinekingmedia.inhouse.dsbmobile.web");
JSONObject payload = new JSONObject();
JSONObject req = new JSONObject();
req.put("Data", encode(json.toString()));
req.put("DataType", 1);
payload.put("req", req);
JSONObject responseJson = new JSONObject(
httpPost(URL, ENCODING, payload.toString(), ContentType.APPLICATION_JSON));
String data = decode(responseJson.getString("d"));
return new JSONObject(data);
}
private void loadScheduleFromUrl(SubstitutionSchedule v, String url, List usedUrls)
throws IOException, JSONException {
usedUrls.add(url);
String html = httpGet(url, data.has("encoding") ? data.getString("encoding") : "UTF-8");
Document doc = Jsoup.parse(html);
if (doc.title().toLowerCase().contains("untis") || doc.html().toLowerCase().contains("untis")) {
if (doc.select(".mon_head").size() > 1) {
for (int j = 0; j < doc.select(".mon_head").size(); j++) {
Document doc2 = Document.createShell(doc.baseUri());
doc2.body().appendChild(doc.select(".mon_head").get(j).clone());
Element next = doc.select(".mon_head").get(j).nextElementSibling();
if (next != null && next.tagName().equals("center")) {
doc2.body().appendChild(next.select(".mon_title").first().clone());
if (next.select("table:has(tr.list)").size() > 0)
doc2.body().appendChild(next.select("table:has(tr.list)").first());
if (next.select("table.info").size() > 0)
doc2.body().appendChild(next.select("table.info").first());
} else {
doc2.body().appendChild(doc.select(".mon_title").get(j).clone());
doc2.body().appendChild(doc.select("table:has(tr.list)").get(j).clone());
}
SubstitutionScheduleDay day = parseMonitorVertretungsplanTag(doc2, data);
v.addDay(day);
}
} else {
SubstitutionScheduleDay day = parseMonitorVertretungsplanTag(doc, data);
v.addDay(day);
}
} else if (doc.html().toLowerCase().contains("created by davinci")) {
Elements titles = doc.select("h2");
Elements tables = doc.select("h2 + p + table");
if (titles.size() != tables.size()) throw new IOException("Anzahl Überschriften != Anzahl Tabellen");
for (int i = 0; i < titles.size(); i++) {
SubstitutionScheduleDay day = new SubstitutionScheduleDay();
String date = titles.get(i).text();
day.setDateString(date);
day.setDate(ParserUtils.parseDate(date));
DaVinciParser.parseDaVinciTable(tables.get(i), day, colorProvider);
v.addDay(day);
}
} else {
throw new IOException("Kein Untis- oder DaVinci-Vertretungsplan?");
}
if (doc.select("meta[http-equiv=refresh]").size() > 0) {
Element meta = doc.select("meta[http-equiv=refresh]").first();
String attr = meta.attr("content").toLowerCase();
String redirectUrl = url.substring(0, url.lastIndexOf("/") + 1) + attr.substring(attr.indexOf("url=") + 4);
if (!usedUrls.contains(redirectUrl)) {
loadScheduleFromUrl(v, redirectUrl, usedUrls);
}
}
}
@Override
public List getAllTeachers() {
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy