Contributing – How to write a reader
fynngodau edited this page 3 years ago

ESBDirect welcomes contributions that add compatibility with a more diverse set of timetable software outputs. Under the condition that your substitution plan is generated in HTML format – not an image –, you can easily add support for your timetable software. This document will try to help you with it. If you are unsure about something, do not hesitate to contact the main developer at fynngodau@mailbox.org or to raise an issue in our issue tracker.

Please note that in the source code, "Reader" is used as a synonym for "parser".

Create a class

First, you should create a new class for your new Reader.

The class should be located in the package godau.fynn.dsbdirect.table.reader. It can be a Java or Kotlin class; however, this guide is oriented at Java.

Make sure to create a copyright notice like in the other files at the beginning of your new file to ensure it is licensed under a free license. Any license compatible with GNU GPL v3+ is acceptable.

Your class should be named like the software that generated your substitution plan. Please look for clues what it is called in your substitution plan files. Otherwise, just make up a name based on what your plan looks like or something.

Your class needs to be a subclass of Reader. Extending the FlexibleReader class instead (which is a subclass of Reader) might allow your parser to be more flexible; check its page for further detail. For example, if your class can parse substitution plans created using the "Awesome" substitution plan system, your class name would be Awesome:

public class Awesome extends Reader

Implement addEntries()

The Reader superclass forces you to implement the addEntries() method. In this method, you should figure out which entries are contained in the provided HTML file, and add each entry to the list of entries managed by Reader by calling addEntry(Entry). Reader will check whether there are any duplicates for you, and in case there are any, omit them.

You can access the HTML String in the Reader's html field.

The other readers use Jsoup as a utility for parsing HTML files, and you probably want to do that as well. Please refer to their documentation for details how to use Jsoup; here is a bit of example code that does a for loop for each div tag in the Document d, which is instantiated by passing html to Jsoup.parse(String):

Document d = Jsoup.parse(super.html);

Elements divs = d.getElementsByTag("div");

for (Element div : divs) {
    // …
}

The first thing you will want to read is likely the date that the next entries belong to. If you found a String representation of a date, parse it using SimpleDateFormat.

Date date = new SimpleDateFormat("dd.MM.yyyy").parse("20.02.2020");

As you start aquiring data that make up one entry in this file, you should acquire an Entry.Builder from the superclass using getEntryBuilder(). Do not forget to pass it the date that your entry concerns.

Entry.Builder entryBuilder = getEntryBuilder()
    .setDate(date);

Assign further data you obtain to its corresponding field. All fields are represented using an EntryField enum.

entryBuilder.put(EntryField.CLASS, div.getElementsBy…);

Tip: if you import each EntryField enum constant with this import line at the top of your file, you can access them directly without specifying their class every time:

import static godau.fynn.dsbdirect.model.entry.EntryField.*;

Once all columns have been filled, build the Entry and pass it to the superclass.

addEntry(entryBuilder.build());

This will add it to the list of entries Reader manages for you. As mentioned, Reader will drop any duplicate entries.

In case your plan holds information in a table with more than two columns, chances are that other schools use the same software, but configured it to output their substitution plans in a different configuration and arrangement of columns. In this event, it is recommended to make use of FlexibleReader as a superclass.

Implement getSchoolName()

If contained in the substitution plan file, your implementation of this method should read out and return a String that contains the name of the school that this substitution plan belongs to. If your file does not contain it, simply return null (not ""!).

Implement canParseHtml(String)

If it is possible to identify subsitution plan systems that generate files which your parser can parse very quickly, for example by testing whether a certain advertising String is contained, your class should overwrite this method to do this and return true if the plan can be parsed using this parser.

Implement canParseId(String)

If you did not implement the previous method or it doesn't work for a plan you know, you can hardcode the login ID of your school in this method to automatically have your parser applied for this school.

Deploy your Reader

In order for your reader to be used, you need to add it to the array of Readers in the Readers class. Look for the line that instructs you to add your class above it.

That's already it! The rest like showing it in the prefernces is done automatically.