Initial getting from fsinfoscreen.
This commit is contained in:
commit
688d0ae5d8
4 changed files with 140 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/bayernfahrplan
|
14
dub.json
Normal file
14
dub.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "bayernfahrplan",
|
||||||
|
"authors": [
|
||||||
|
"Johannes Loher",
|
||||||
|
"Oliver Rümpelein"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"requests": "~>0.3.1",
|
||||||
|
"kxml": "~>1.0.1"
|
||||||
|
},
|
||||||
|
"description": "A minimal D application.",
|
||||||
|
"copyright": "Copyright © 2016, Johannes Loher",
|
||||||
|
"license": "proprietary"
|
||||||
|
}
|
90
source/app.d
Normal file
90
source/app.d
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import std.algorithm : filter, map, startsWith;
|
||||||
|
import std.array : array, empty, front, replace;
|
||||||
|
import std.conv : to;
|
||||||
|
import std.datetime : dur, TimeOfDay, Clock;
|
||||||
|
import std.getopt : defaultGetoptPrinter, getopt;
|
||||||
|
import std.json : JSONValue;
|
||||||
|
import std.regex : ctRegex, matchAll;
|
||||||
|
import std.stdio : File, stdout, writeln;
|
||||||
|
import std.string : strip;
|
||||||
|
import std.typecons : tuple;
|
||||||
|
import std.format : format;
|
||||||
|
|
||||||
|
import kxml.xml : readDocument, XmlNode;
|
||||||
|
|
||||||
|
import requests : postContent;
|
||||||
|
|
||||||
|
import substitution;
|
||||||
|
|
||||||
|
auto parseTime(in string input)
|
||||||
|
{
|
||||||
|
auto matches = matchAll(input, ctRegex!(`(?P<hours>\d+):(?P<minutes>\d+)`));
|
||||||
|
auto actualTime = TimeOfDay(matches.front["hours"].to!int, matches.front["minutes"].to!int);
|
||||||
|
matches.popFront;
|
||||||
|
if (!matches.empty)
|
||||||
|
{
|
||||||
|
auto expectedTime = TimeOfDay(matches.front["hours"].to!int,
|
||||||
|
matches.front["minutes"].to!int);
|
||||||
|
return tuple(expectedTime, actualTime - expectedTime);
|
||||||
|
|
||||||
|
}
|
||||||
|
return tuple(actualTime, dur!"minutes"(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getRowContents(XmlNode[] rows)
|
||||||
|
{
|
||||||
|
return rows.map!(row => row.parseXPath("//td")[1 .. $ - 1].map!((column) {
|
||||||
|
auto link = column.parseXPath("//a");
|
||||||
|
if (!link.empty)
|
||||||
|
return link.front.getCData.replace("...", "");
|
||||||
|
return column.getCData;}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args)
|
||||||
|
{
|
||||||
|
string fileName;
|
||||||
|
string busStop = "Universität Regensburg";
|
||||||
|
string substitutionFileName = "replacement.txt";
|
||||||
|
auto helpInformation = getopt(args,
|
||||||
|
"file|f", "The file that the data is written to.", &fileName,
|
||||||
|
"stop|s", "The bus stop for which to fetch data.", &busStop,
|
||||||
|
"replacement-file|r", "The file that contais the direction name replacement info.", &substitutionFileName);
|
||||||
|
if (helpInformation.helpWanted)
|
||||||
|
{
|
||||||
|
defaultGetoptPrinter("Some information about the program.", helpInformation.options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto content = postContent("http://txt.bayern-fahrplan.de/textversion/bcl_abfahrtstafel",
|
||||||
|
["limit" : "20",
|
||||||
|
"useRealtime" : "1",
|
||||||
|
"name_dm" : busStop,
|
||||||
|
"mode" : "direct",
|
||||||
|
"type_dm" : "any",
|
||||||
|
"itdLPxx_bcl" : "true"]);
|
||||||
|
|
||||||
|
auto currentTime = Clock.currTime;
|
||||||
|
loadSubstitutionFile(substitutionFileName);
|
||||||
|
JSONValue j = ["time" : "%02s:%02s".format(currentTime.hour, currentTime.minute)];
|
||||||
|
j.object["departures"] = readDocument(cast(string) content.data)
|
||||||
|
.parseXPath(`//table[@id="departureMonitor"]/tbody/tr`)[1 .. $]
|
||||||
|
.getRowContents
|
||||||
|
.filter!(row => !row.empty)
|
||||||
|
.map!(a => ["departure" : a[0].parseTime[0].to!string[0 .. $ - 3],
|
||||||
|
"delay" : a[0].parseTime[1].total!"minutes".to!string,
|
||||||
|
"line" : a[1],
|
||||||
|
"direction" : a[2].substitute])
|
||||||
|
.array.JSONValue;
|
||||||
|
|
||||||
|
if (fileName !is null)
|
||||||
|
{
|
||||||
|
auto output = File(fileName, "w");
|
||||||
|
scope(exit) output.close;
|
||||||
|
output.writeln(j.toPrettyString.replace("\\/", "/"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
j.toPrettyString.replace("\\/", "/").writeln;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
source/substitution.d
Normal file
35
source/substitution.d
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
module substitution;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void loadSubstitutionFile(string fileName)
|
||||||
|
{
|
||||||
|
import std.file : slurp, exists, isFile;
|
||||||
|
import std.array : assocArray;
|
||||||
|
import std.algorithm.iteration : each;
|
||||||
|
|
||||||
|
if (fileName.exists && fileName.isFile)
|
||||||
|
{
|
||||||
|
auto data = slurp!(string, string)(fileName, `"%s" = "%s"`);
|
||||||
|
map = (string[string]).init;
|
||||||
|
data.each!(pair => map[pair[0]] = pair[1]);
|
||||||
|
foreach (pair; data)
|
||||||
|
{
|
||||||
|
map[pair[0]] = pair[1];
|
||||||
|
}
|
||||||
|
map.rehash;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
map = (string[string]).init;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto substitute(string s)
|
||||||
|
{
|
||||||
|
return s in map ? map[s] : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
string[string] map;
|
Loading…
Reference in a new issue