Commit 54da1a48 authored by Volker Krause's avatar Volker Krause
Browse files

Start of OpenJourneyPlanner support

Only location queries work so far, the rest is mostly scaffolding still.

We need this as a replacement for the deactivated SBB Hafas endpoint. Due
to the close similarity of OJP and TRIAS we might also get support for
that as a side-effect. That in turn could help with the broken VVO EFA
endpoint.
parent 5f59be02
Pipeline #79045 passed with stage
in 17 seconds
......@@ -3,7 +3,7 @@ Upstream-Name: KPublicTransport
Upstream-Contact: Volker Krause <vkrause@kde.org>
Source: https://invent.kde.org/libraries/kpublictransport
Files: autotests/data/departures/* autotests/data/deutschebahn/* autotests/data/efa/* autotests/data/hafas/* autotests/data/journeys/* autotests/data/navitia/* autotests/data/otp/* autotests/data/a11y-cloud/* autotests/data/oebb/* autotests/data/ivvass/*.json
Files: autotests/data/departures/* autotests/data/deutschebahn/* autotests/data/efa/* autotests/data/hafas/* autotests/data/journeys/* autotests/data/navitia/* autotests/data/otp/* autotests/data/a11y-cloud/* autotests/data/oebb/* autotests/data/ivvass/*.json autotests/data/ojp/* autotests/data/ojp-request/*.xml
Copyright: none
License: CC0-1.0
......
......@@ -34,6 +34,8 @@ ecm_add_test(otpparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport)
ecm_add_test(oebbparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport)
ecm_add_test(ivvassparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport)
ecm_add_test(gbfsreadertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport)
ecm_add_test(ojprequesttest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport)
ecm_add_test(ojpparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport)
ecm_add_test(publictransportmanagertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport)
ecm_add_test(cachetest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport)
......
<?xml version="1.0" encoding="UTF-8"?>
<siri:OJP xmlns:siri="http://www.siri.org.uk/siri" xmlns:ojp="http://www.vdv.de/ojp" version="1.0">
<siri:OJPRequest>
<siri:ServiceRequest>
<siri:RequestRef>KPublicTransport</siri:RequestRef>
<ojp:OJPLocationInformationRequest>
<ojp:InitialInput>
<ojp:GeoRestriction>
<ojp:Circle>
<ojp:Center>
<siri:Longitude>7.78</siri:Longitude>
<siri:Latitude>46.1</siri:Latitude>
</ojp:Center>
<ojp:Radius>500</ojp:Radius>
</ojp:Circle>
</ojp:GeoRestriction>
</ojp:InitialInput>
<ojp:Restrictions>
<ojp:Type>stop</ojp:Type>
<ojp:NumberOfResults>23</ojp:NumberOfResults>
</ojp:Restrictions>
</ojp:OJPLocationInformationRequest>
</siri:ServiceRequest>
</siri:OJPRequest>
</siri:OJP>
<?xml version="1.0" encoding="UTF-8"?>
<siri:OJP xmlns:siri="http://www.siri.org.uk/siri" xmlns:ojp="http://www.vdv.de/ojp" version="1.0">
<siri:OJPRequest>
<siri:ServiceRequest>
<siri:RequestRef>KPublicTransport</siri:RequestRef>
<ojp:OJPLocationInformationRequest>
<ojp:InitialInput>
<ojp:LocationName>Randa</ojp:LocationName>
</ojp:InitialInput>
<ojp:Restrictions>
<ojp:Type>stop</ojp:Type>
<ojp:NumberOfResults>23</ojp:NumberOfResults>
</ojp:Restrictions>
</ojp:OJPLocationInformationRequest>
</siri:ServiceRequest>
</siri:OJPRequest>
</siri:OJP>
<?xml version="1.0" encoding="UTF-8"?>
<siri:OJP xmlns:siri="http://www.siri.org.uk/siri" xmlns:ojp="http://www.vdv.de/ojp" version="1.0">
<siri:OJPRequest>
<siri:ServiceRequest>
<siri:RequestRef>KPublicTransport</siri:RequestRef>
<ojp:OJPStopEventRequest>
<ojp:Location>
<ojp:PlaceRef>
<ojp:StopPlaceRef>8501687</ojp:StopPlaceRef>
</ojp:PlaceRef>
<ojp:DepArrTime>2020-09-06T20:54:00Z</ojp:DepArrTime>
</ojp:Location>
<ojp:Params>
<ojp:StopEventType>arrival</ojp:StopEventType>
<ojp:IncludePreviousCalls>false</ojp:IncludePreviousCalls>
<ojp:IncludeOnwardCalls>false</ojp:IncludeOnwardCalls>
<ojp:IncludeOperatingDays>false</ojp:IncludeOperatingDays>
<ojp:IncludeRealtimeData>true</ojp:IncludeRealtimeData>
<ojp:NumberOfResults>4</ojp:NumberOfResults>
</ojp:Params>
</ojp:OJPStopEventRequest>
</siri:ServiceRequest>
</siri:OJPRequest>
</siri:OJP>
<?xml version="1.0" encoding="UTF-8"?>
<siri:OJP xmlns:siri="http://www.siri.org.uk/siri" xmlns:ojp="http://www.vdv.de/ojp" version="1.0">
<siri:OJPRequest>
<siri:ServiceRequest>
<siri:RequestRef>KPublicTransport</siri:RequestRef>
<ojp:OJPStopEventRequest>
<ojp:Location>
<ojp:PlaceRef>
<ojp:StopPlaceRef>8501687</ojp:StopPlaceRef>
</ojp:PlaceRef>
<ojp:DepArrTime>2020-09-06T20:54:00Z</ojp:DepArrTime>
</ojp:Location>
<ojp:Params>
<ojp:StopEventType>departure</ojp:StopEventType>
<ojp:IncludePreviousCalls>false</ojp:IncludePreviousCalls>
<ojp:IncludeOnwardCalls>false</ojp:IncludeOnwardCalls>
<ojp:IncludeOperatingDays>false</ojp:IncludeOperatingDays>
<ojp:IncludeRealtimeData>true</ojp:IncludeRealtimeData>
<ojp:NumberOfResults>4</ojp:NumberOfResults>
</ojp:Params>
</ojp:OJPStopEventRequest>
</siri:ServiceRequest>
</siri:OJPRequest>
</siri:OJP>
[
{
"identifier": {
"uic": "8501687"
},
"latitude": 46.09992980957031,
"locality": "Randa",
"longitude": 7.781459808349609,
"name": "Randa",
"type": "Stop"
}
]
<?xml version="1.0" encoding="UTF-8"?>
<siri:OJP xmlns:siri="http://www.siri.org.uk/siri" xmlns:ojp="http://www.vdv.de/ojp" version="1.0"><siri:OJPResponse><siri:ServiceDelivery><siri:ResponseTimestamp>2021-09-06T17:38:46Z</siri:ResponseTimestamp><siri:ProducerRef>OJPCH_Prod</siri:ProducerRef><siri:Status>true</siri:Status><ojp:OJPLocationInformationDelivery><siri:ResponseTimestamp>2021-09-06T17:38:46Z</siri:ResponseTimestamp><siri:Status>true</siri:Status><ojp:CalcTime>58</ojp:CalcTime><ojp:Location><ojp:Location><ojp:StopPlace><ojp:StopPlaceRef>8501687</ojp:StopPlaceRef><ojp:StopPlaceName><ojp:Text>Randa</ojp:Text></ojp:StopPlaceName><ojp:TopographicPlaceRef>23024287:1</ojp:TopographicPlaceRef></ojp:StopPlace><ojp:LocationName><ojp:Text xml:lang="de">Randa</ojp:Text></ojp:LocationName><ojp:GeoPosition><siri:Longitude>7.78146</siri:Longitude><siri:Latitude>46.09993</siri:Latitude></ojp:GeoPosition></ojp:Location><ojp:Complete>true</ojp:Complete><ojp:Probability>0.449999988</ojp:Probability></ojp:Location></ojp:OJPLocationInformationDelivery></siri:ServiceDelivery></siri:OJPResponse></siri:OJP>
[
{
"disruptionEffect": "NormalService",
"route": {
"direction": "Brig Bahnhofplatz",
"line": {
"name": "R"
}
}
},
{
"disruptionEffect": "NormalService",
"route": {
"direction": "Zermatt",
"line": {
"name": "R"
}
}
}
]
<?xml version="1.0" encoding="UTF-8"?>
<siri:OJP xmlns:siri="http://www.siri.org.uk/siri" xmlns:ojp="http://www.vdv.de/ojp" version="1.0"><siri:OJPResponse><siri:ServiceDelivery><siri:ResponseTimestamp>2021-09-06T20:01:21Z</siri:ResponseTimestamp><siri:ProducerRef>OJPCH_Prod</siri:ProducerRef><siri:Status>true</siri:Status><ojp:OJPStopEventDelivery><siri:ResponseTimestamp>2021-09-06T20:01:21Z</siri:ResponseTimestamp><siri:Status>true</siri:Status><ojp:CalcTime>27</ojp:CalcTime><ojp:StopEventResponseContext><ojp:Places><ojp:Location><ojp:StopPlace><ojp:StopPlaceRef>8501687</ojp:StopPlaceRef><ojp:StopPlaceName><ojp:Text>Randa</ojp:Text></ojp:StopPlaceName><ojp:PrivateCode><ojp:System>EFA</ojp:System><ojp:Value>107501:0:</ojp:Value></ojp:PrivateCode><ojp:TopographicPlaceRef>23024287:1</ojp:TopographicPlaceRef></ojp:StopPlace><ojp:LocationName><ojp:Text xml:lang="de">Randa</ojp:Text></ojp:LocationName><ojp:GeoPosition><siri:Longitude>7.78146</siri:Longitude><siri:Latitude>46.09993</siri:Latitude></ojp:GeoPosition></ojp:Location><ojp:Location><ojp:TopographicPlace><ojp:TopographicPlaceCode>23024287:1</ojp:TopographicPlaceCode><ojp:TopographicPlaceName><ojp:Text>Randa</ojp:Text></ojp:TopographicPlaceName></ojp:TopographicPlace><ojp:LocationName><ojp:Text>Randa</ojp:Text></ojp:LocationName><ojp:GeoPosition><siri:Longitude>0.0</siri:Longitude><siri:Latitude>0.0</siri:Latitude><siri:Precision>20000000</siri:Precision></ojp:GeoPosition></ojp:Location></ojp:Places></ojp:StopEventResponseContext><ojp:StopEventResult><ojp:ResultId>ID-C6CE73E2-9AF6-49F4-8827-6AABAB2242A0</ojp:ResultId><ojp:StopEvent><ojp:ThisCall><ojp:CallAtStop><siri:StopPointRef>8501687</siri:StopPointRef><ojp:StopPointName><ojp:Text>Randa</ojp:Text></ojp:StopPointName><ojp:ServiceDeparture><ojp:TimetabledTime>2021-09-06T20:27:00Z</ojp:TimetabledTime><ojp:EstimatedTime>2021-09-06T20:27:00Z</ojp:EstimatedTime></ojp:ServiceDeparture><ojp:Order>3</ojp:Order></ojp:CallAtStop></ojp:ThisCall><ojp:Service><ojp:OperatingDayRef>2021-09-06</ojp:OperatingDayRef><ojp:JourneyRef>ojp:91003:Y:R:j21:25</ojp:JourneyRef><siri:LineRef>ojp:91003:Y</siri:LineRef><siri:DirectionRef>R</siri:DirectionRef><ojp:Mode><ojp:PtMode>rail</ojp:PtMode><siri:RailSubmode>regionalRail</siri:RailSubmode><ojp:Name><ojp:Text xml:lang="de">Zug</ojp:Text></ojp:Name><ojp:ShortName><ojp:Text xml:lang="de">R</ojp:Text></ojp:ShortName></ojp:Mode><ojp:PublishedLineName><ojp:Text>R</ojp:Text></ojp:PublishedLineName><ojp:OperatorRef>ojp:93</ojp:OperatorRef><ojp:Attribute><ojp:Text><ojp:Text xml:lang="de">Maskenpflicht für Reisende ab 12 Jahren</ojp:Text></ojp:Text><ojp:Code>A__OM</ojp:Code></ojp:Attribute><ojp:OriginStopPointRef>8501689</ojp:OriginStopPointRef><ojp:OriginText><ojp:Text xml:lang="de">Zermatt</ojp:Text></ojp:OriginText><ojp:DestinationStopPointRef>8515296</ojp:DestinationStopPointRef><ojp:DestinationText><ojp:Text xml:lang="de">Brig Bahnhofplatz</ojp:Text></ojp:DestinationText></ojp:Service><ojp:Extension><ojp:TransportTypeName><ojp:Text xml:lang="de">Regio</ojp:Text></ojp:TransportTypeName><ojp:PublishedJourneyNumber><ojp:Text xml:lang="de">280</ojp:Text></ojp:PublishedJourneyNumber></ojp:Extension></ojp:StopEvent></ojp:StopEventResult><ojp:StopEventResult><ojp:ResultId>ID-5FB6F039-8918-4C8E-8AE9-FE3E633C59E4</ojp:ResultId><ojp:StopEvent><ojp:ThisCall><ojp:CallAtStop><siri:StopPointRef>8501687</siri:StopPointRef><ojp:StopPointName><ojp:Text>Randa</ojp:Text></ojp:StopPointName><ojp:ServiceDeparture><ojp:TimetabledTime>2021-09-06T21:29:00Z</ojp:TimetabledTime><ojp:EstimatedTime>2021-09-06T21:29:00Z</ojp:EstimatedTime></ojp:ServiceDeparture><ojp:Order>8</ojp:Order></ojp:CallAtStop></ojp:ThisCall><ojp:Service><ojp:OperatingDayRef>2021-09-06</ojp:OperatingDayRef><ojp:JourneyRef>ojp:91003:Y:H:j21:123</ojp:JourneyRef><siri:LineRef>ojp:91003:Y</siri:LineRef><siri:DirectionRef>H</siri:DirectionRef><ojp:Mode><ojp:PtMode>rail</ojp:PtMode><siri:RailSubmode>regionalRail</siri:RailSubmode><ojp:Name><ojp:Text xml:lang="de">Zug</ojp:Text></ojp:Name><ojp:ShortName><ojp:Text xml:lang="de">R</ojp:Text></ojp:ShortName></ojp:Mode><ojp:PublishedLineName><ojp:Text>R</ojp:Text></ojp:PublishedLineName><ojp:OperatorRef>ojp:93</ojp:OperatorRef><ojp:Attribute><ojp:Text><ojp:Text xml:lang="de">Maskenpflicht für Reisende ab 12 Jahren</ojp:Text></ojp:Text><ojp:Code>A__OM</ojp:Code></ojp:Attribute><ojp:OriginStopPointRef>8515296</ojp:OriginStopPointRef><ojp:OriginText><ojp:Text xml:lang="de">Brig Bahnhofplatz</ojp:Text></ojp:OriginText><ojp:DestinationStopPointRef>8501689</ojp:DestinationStopPointRef><ojp:DestinationText><ojp:Text xml:lang="de">Zermatt</ojp:Text></ojp:DestinationText></ojp:Service><ojp:Extension><ojp:TransportTypeName><ojp:Text xml:lang="de">Regio</ojp:Text></ojp:TransportTypeName><ojp:PublishedJourneyNumber><ojp:Text xml:lang="de">277</ojp:Text></ojp:PublishedJourneyNumber></ojp:Extension></ojp:StopEvent></ojp:StopEventResult></ojp:OJPStopEventDelivery></siri:ServiceDelivery></siri:OJPResponse></siri:OJP>
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "backends/openjourneyplannerparser.h"
#include <KPublicTransport/Journey>
#include <KPublicTransport/Location>
#include <KPublicTransport/Stopover>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QLocale>
#include <QTest>
#define s(x) QStringLiteral(x)
using namespace KPublicTransport;
class OjpParserTest : public QObject
{
Q_OBJECT
private:
QByteArray readFile(const QString &fn)
{
QFile f(fn);
f.open(QFile::ReadOnly);
return f.readAll();
}
private Q_SLOTS:
void initTestCase()
{
qputenv("TZ", "UTC");
}
void testParseLocations_data()
{
QTest::addColumn<QString>("inFileName");
QTest::addColumn<QString>("refFileName");
QTest::newRow("ch-location-by-coord")
<< s(SOURCE_DIR "/data/ojp/ch-location-by-coord.xml")
<< s(SOURCE_DIR "/data/ojp/ch-location-by-coord.json");
}
void testParseLocations()
{
QFETCH(QString, inFileName);
QFETCH(QString, refFileName);
OpenJourneyPlannerParser p;
const auto res = p.parseLocationInformationResponse(readFile(inFileName));
const auto jsonRes = Location::toJson(res);
const auto ref = QJsonDocument::fromJson(readFile(refFileName)).array();
if (jsonRes != ref) {
qDebug().noquote() << QJsonDocument(jsonRes).toJson();
}
QVERIFY(!jsonRes.empty());
QCOMPARE(jsonRes, ref);
}
void testParseStopover_data()
{
QTest::addColumn<QString>("inFileName");
QTest::addColumn<QString>("refFileName");
QTest::newRow("ch-stopover-departure")
<< s(SOURCE_DIR "/data/ojp/ch-stopover-departure.xml")
<< s(SOURCE_DIR "/data/ojp/ch-stopover-departure.json");
}
void testParseStopover()
{
QFETCH(QString, inFileName);
QFETCH(QString, refFileName);
OpenJourneyPlannerParser p;
const auto res = p.parseStopEventResponse(readFile(inFileName));
const auto jsonRes = Stopover::toJson(res);
const auto ref = QJsonDocument::fromJson(readFile(refFileName)).array();
if (jsonRes != ref) {
qDebug().noquote() << QJsonDocument(jsonRes).toJson();
}
QVERIFY(!jsonRes.empty());
QCOMPARE(jsonRes, ref);
}
};
QTEST_GUILESS_MAIN(OjpParserTest)
#include "ojpparsertest.moc"
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "backends/openjourneyplannerrequestbuilder.cpp"
#include <KPublicTransport/JourneyRequest>
#include <KPublicTransport/LocationRequest>
#include <KPublicTransport/StopoverRequest>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QTest>
#define s(x) QStringLiteral(x)
using namespace KPublicTransport;
class OjpRequestTest : public QObject
{
Q_OBJECT
private:
QByteArray readFile(const QString &fn)
{
QFile f(fn);
f.open(QFile::ReadOnly);
return f.readAll();
}
private Q_SLOTS:
void initTestCase()
{
qputenv("TZ", "UTC");
}
void testLocationRequest_data()
{
QTest::addColumn<LocationRequest>("request");
QTest::addColumn<QString>("refFileName");
Location loc;
loc.setCoordinate(46.1, 7.78);
LocationRequest req;
req.setLocation(loc);
req.setMaximumDistance(500);
req.setMaximumResults(23);
QTest::newRow("location-by-coord") << req << s(SOURCE_DIR "/data/ojp-request/location-by-coord.xml");
loc = {};
loc.setName(s("Randa"));
req.setLocation(loc);
QTest::newRow("location-by-name") << req << s(SOURCE_DIR "/data/ojp-request/location-by-name.xml");
}
void testLocationRequest()
{
QFETCH(LocationRequest, request);
QFETCH(QString, refFileName);
OpenJourneyPlannerRequestBuilder builder;
builder.setTestMode(true);
const auto res = builder.buildLocationInformationRequest(request);
const auto ref = readFile(refFileName);
if (res != ref) {
qDebug().noquote() << res;
}
QVERIFY(!res.isEmpty());
QCOMPARE(res, ref);
}
void testStopoverRequest_data()
{
QTest::addColumn<StopoverRequest>("request");
QTest::addColumn<QString>("refFileName");
Location loc;
loc.setIdentifier(QStringLiteral("uic"), QStringLiteral("8501687"));
StopoverRequest req;
req.setStop(loc);
req.setMode(StopoverRequest::QueryDeparture);
req.setDateTime(QDateTime({2020, 9, 6}, {20, 54}, Qt::UTC));
req.setMaximumResults(4);
QTest::newRow("stopover-departure") << req << s(SOURCE_DIR "/data/ojp-request/stopover-departure.xml");
req.setMode(StopoverRequest::QueryArrival);
QTest::newRow("stopover-arrival") << req << s(SOURCE_DIR "/data/ojp-request/stopover-arrival.xml");
}
void testStopoverRequest()
{
QFETCH(StopoverRequest, request);
QFETCH(QString, refFileName);
OpenJourneyPlannerRequestBuilder builder;
builder.setTestMode(true);
const auto res = builder.buildStopEventRequest(request);
const auto ref = readFile(refFileName);
if (res != ref) {
qDebug().noquote() << res;
}
QVERIFY(!res.isEmpty());
QCOMPARE(res, ref);
}
};
QTEST_GUILESS_MAIN(OjpRequestTest)
#include "ojprequesttest.moc"
......@@ -43,6 +43,9 @@ target_sources(KPublicTransport PRIVATE
backends/navitiaparser.cpp
backends/oebbbackend.cpp
backends/oebbvehiclelayoutparser.cpp
backends/openjourneyplannerbackend.cpp
backends/openjourneyplannerparser.cpp
backends/openjourneyplannerrequestbuilder.cpp
backends/opentripplannergraphqlbackend.cpp
backends/opentripplannerparser.cpp
backends/opentripplannerrestbackend.cpp
......
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "openjourneyplannerbackend.h"
#include "openjourneyplannerparser.h"
#include "openjourneyplannerrequestbuilder.h"
#include <KPublicTransport/Journey>
#include <KPublicTransport/JourneyReply>
#include <KPublicTransport/JourneyRequest>
#include <KPublicTransport/Location>
#include <KPublicTransport/LocationReply>
#include <KPublicTransport/LocationRequest>
#include <KPublicTransport/Stopover>
#include <KPublicTransport/StopoverReply>
#include <KPublicTransport/StopoverRequest>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
using namespace KPublicTransport;
AbstractBackend::Capabilities OpenJourneyPlannerBackend::capabilities() const
{
return m_endpoint.scheme() == QLatin1String("https") ? AbstractBackend::Secure : AbstractBackend::NoCapability;
}
bool OpenJourneyPlannerBackend::needsLocationQuery(const Location &loc, AbstractBackend::QueryType type) const
{
return loc.identifier(QStringLiteral("uic")).isEmpty(); // ### TODO configure identifier type
}
bool OpenJourneyPlannerBackend::queryLocation(const LocationRequest &request, LocationReply *reply, QNetworkAccessManager *nam) const
{
if ((request.types() & Location::Stop) == 0) {
return false;
}
OpenJourneyPlannerRequestBuilder builder;
const auto postData = builder.buildLocationInformationRequest(request);
const auto netReq = networkRequest();
logRequest(request, netReq, postData);
const auto netReply = nam->post(netReq, postData);
QObject::connect(netReply, &QNetworkReply::finished, reply, [this, netReply, reply]() {
netReply->deleteLater();
const auto data = netReply->readAll();
logReply(reply, netReply, data);
OpenJourneyPlannerParser p;
auto locs = p.parseLocationInformationResponse(data);
// TODO caching, error handling
addResult(reply, std::move(locs));
});
return true;
}
bool OpenJourneyPlannerBackend::queryStopover(const StopoverRequest &request, StopoverReply *reply, QNetworkAccessManager *nam) const
{
OpenJourneyPlannerRequestBuilder builder;
const auto postData = builder.buildStopEventRequest(request);
const auto netReq = networkRequest();
logRequest(request, netReq, postData);
const auto netReply = nam->post(netReq, postData);
QObject::connect(netReply, &QNetworkReply::finished, reply, [this, netReply, reply]() {
netReply->deleteLater();
const auto data = netReply->readAll();
logReply(reply, netReply, data);
OpenJourneyPlannerParser p;
auto stops = p.parseStopEventResponse(data);
// TODO
addError(reply, Reply::NetworkError, netReply->errorString());
});
return true;
}
bool OpenJourneyPlannerBackend::queryJourney(const JourneyRequest &request, JourneyReply *reply, QNetworkAccessManager *nam) const
{
OpenJourneyPlannerRequestBuilder builder;
builder.setTestMode(true); // ### for development only
const auto postData = builder.buildTripRequest(request);
const auto netReq = networkRequest();
logRequest(request, netReq, postData);
qDebug().noquote() << postData; // ### for development only
// TODO
return false;
}
QNetworkRequest OpenJourneyPlannerBackend::networkRequest() const
{
QNetworkRequest req(m_endpoint);
req.setHeader(QNetworkRequest::ContentTypeHeader, QByteArray("application/xml"));
if (!m_authorization.isEmpty()) {
req.setRawHeader("Authorization", m_authorization.toUtf8());
}
return req;
}
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KPUBLICTRANSPORT_OPENJOURNEYPLANNERBACKEND_H
#define KPUBLICTRANSPORT_OPENJOURNEYPLANNERBACKEND_H
#include "abstractbackend.h"
#include <QUrl>
class QNetworkRequest;
namespace KPublicTransport {
/** Backend for OpenJourneyPlanner services
* Due to its similarities, this might also be viable for TRIAS-based services.
*/
class OpenJourneyPlannerBackend : public AbstractBackend
{
Q_GADGET
Q_PROPERTY(QUrl endpoint MEMBER m_endpoint)
Q_PROPERTY(QString authorization MEMBER m_authorization)
public:
static inline constexpr const char* type() { return "openJourneyPlanner"; }
AbstractBackend::Capabilities capabilities() const override;
bool needsLocationQuery(const Location &loc, AbstractBackend::QueryType type) const override;
bool queryLocation(const LocationRequest &request, LocationReply *reply, QNetworkAccessManager *nam) const override;
bool queryStopover(const StopoverRequest &request, StopoverReply *reply, QNetworkAccessManager *nam) const override;
bool queryJourney(const JourneyRequest &request, JourneyReply * reply, QNetworkAccessManager *nam) const override;
private:
QNetworkRequest networkRequest() const;
QUrl m_endpoint;
QString m_authorization;
};
}
#endif // KPUBLICTRANSPORT_OPENJOURNEYPLANNERBACKEND_H
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "openjourneyplannerparser.h"
#include "scopedxmlstreamreader.h"
#include <KPublicTransport/Journey>
#include <KPublicTransport/Location>
#include <KPublicTransport/Stopover>
#include <QByteArray>
#include <QDebug>
#include <QXmlStreamReader>
using namespace KPublicTransport;
std::vector<Location> OpenJourneyPlannerParser::parseLocationInformationResponse(const QByteArray &responseData) const
{
QXmlStreamReader reader(responseData);
ScopedXmlStreamReader r(reader);
while (r.readNextElement()) {
if (r.isElement("OJPLocationInformationDelivery")) {
return parseLocationInformationDelivery(r.subReader());
}
}
return {};
}
std::vector<Stopover> OpenJourneyPlannerParser::parseStopEventResponse(const QByteArray &responseData) const
{
qDebug().noquote() << responseData; // ### for development only
QXmlStreamReader reader(responseData);
ScopedXmlStreamReader r(reader);
while (r.readNextElement()) {
if (r.isElement("OJPStopEventDelivery")) {
return parseStopEventDelivery(r.subReader());
}
}
return {};
}
std::vector<Journey> OpenJourneyPlannerParser::parseTripResponse(const QByteArray &responseData) const
{
qDebug().noquote() << responseData;
return {};
}
std::vector<Location> OpenJourneyPlannerParser::parseLocationInformationDelivery(ScopedXmlStreamReader &&r) const
{
std::vector<Location> l;
while (r.readNextSibling()) {
if (r.isElement("Location")) {
auto loc = parseLocationInformationLocationOuter(r.subReader());
if (!loc.isEmpty()) {
l.push_back(std::move(loc));
}
}
}
return l;
}
Location OpenJourneyPlannerParser::parseLocationInformationLocationOuter(ScopedXmlStreamReader &&r) const
{
Location loc;
while (r.readNextSibling()) {