File indexing completed on 2024-05-12 04:42:37
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "ivvassbackend.h" 0008 #include "ivvassparser.h" 0009 #include "ivvassproductmap.h" 0010 #include "cache.h" 0011 #include "ifopt/ifoptutil.h" 0012 0013 #include <KPublicTransport/Journey> 0014 #include <KPublicTransport/JourneyReply> 0015 #include <KPublicTransport/JourneyRequest> 0016 #include <KPublicTransport/Location> 0017 #include <KPublicTransport/LocationReply> 0018 #include <KPublicTransport/LocationRequest> 0019 #include <KPublicTransport/Stopover> 0020 #include <KPublicTransport/StopoverReply> 0021 #include <KPublicTransport/StopoverRequest> 0022 0023 #include <QDebug> 0024 #include <QNetworkAccessManager> 0025 #include <QNetworkReply> 0026 #include <QNetworkRequest> 0027 #include <QUrlQuery> 0028 0029 using namespace KPublicTransport; 0030 0031 AbstractBackend::Capabilities IvvAssBackend::capabilities() const 0032 { 0033 return m_endpoint.scheme() == QLatin1String("https") ? Capability::Secure : Capability::NoCapability; 0034 } 0035 0036 bool IvvAssBackend::needsLocationQuery(const Location &loc, AbstractBackend::QueryType type) const 0037 { 0038 Q_UNUSED(type); 0039 return !loc.hasCoordinate() && loc.identifier(IfoptUtil::identifierType()).isEmpty(); 0040 } 0041 0042 bool IvvAssBackend::queryLocation(const LocationRequest &req, LocationReply *reply, QNetworkAccessManager *nam) const 0043 { 0044 if ((req.types() & Location::Stop) == 0 && !req.hasCoordinate()) { 0045 return false; // name based search is only available for stops 0046 } 0047 0048 QUrlQuery query; 0049 if (req.hasCoordinate()) { 0050 query.addQueryItem(QStringLiteral("eID"), QStringLiteral("tx_ekap_here")); 0051 query.addQueryItem(QStringLiteral("lat"), QString::number(req.latitude())); 0052 query.addQueryItem(QStringLiteral("lon"), QString::number(req.longitude())); 0053 } else { 0054 query.addQueryItem(QStringLiteral("eID"), QStringLiteral("tx_vrsinfo_ass2_objects")); 0055 query.addQueryItem(QStringLiteral("t"), QStringLiteral("s")); // s: stops, a: addresses, p: pois 0056 query.addQueryItem(QStringLiteral("q"), req.name()); 0057 } 0058 query.addQueryItem(QStringLiteral("sc"), QString::number(req.maximumResults())); // station count 0059 query.addQueryItem(QStringLiteral("ac"), QStringLiteral("0")); // address count 0060 query.addQueryItem(QStringLiteral("pc"), QStringLiteral("0")); // poi count 0061 0062 QUrl url(m_endpoint); 0063 url.setQuery(query); 0064 0065 QNetworkRequest netRequest(url); 0066 applySslConfiguration(netRequest); 0067 logRequest(req, netRequest); 0068 auto netReply = nam->get(netRequest); 0069 netReply->setParent(reply); 0070 QObject::connect(netReply, &QNetworkReply::finished, reply, [this, reply, netReply]() { 0071 netReply->deleteLater(); 0072 const auto data = netReply->readAll(); 0073 logReply(reply, netReply, data); 0074 0075 if (netReply->error() != QNetworkReply::NoError) { 0076 addError(reply, Reply::NetworkError, netReply->errorString()); 0077 return; 0078 } 0079 0080 IvvAssParser p(timeZone(), backendId()); 0081 auto result = p.parseLocations(data); 0082 if (p.errorMessage.isEmpty()) { 0083 Cache::addLocationCacheEntry(backendId(), reply->request().cacheKey(), result, {}); 0084 addResult(reply, std::move(result)); 0085 } else { 0086 addError(reply, Reply::UnknownError, std::move(p.errorMessage)); 0087 } 0088 }); 0089 0090 return true; 0091 } 0092 0093 bool IvvAssBackend::queryStopover(const StopoverRequest &req, StopoverReply *reply, QNetworkAccessManager *nam) const 0094 { 0095 QUrlQuery query; 0096 query.addQueryItem(QStringLiteral("eID"), QStringLiteral("tx_vrsinfo_ass2_timetable")); 0097 0098 const auto ifopt = req.stop().identifier(IfoptUtil::identifierType()); 0099 if (ifopt.isEmpty()) { 0100 query.addQueryItem(QStringLiteral("r"), QString::number(req.stop().latitude()) + QLatin1Char(',') + QString::number(req.stop().longitude())); 0101 } else { 0102 query.addQueryItem(QStringLiteral("i"), ifopt); 0103 } 0104 query.addQueryItem(QStringLiteral("c"), QString::number(req.maximumResults())); 0105 IvvAssProductMap::lineModesToQuery(req.lineModes(), query); 0106 0107 auto dt = req.dateTime(); 0108 if (timeZone().isValid()) { 0109 dt = dt.toTimeZone(timeZone()); 0110 } 0111 dt.setTimeZone(QTimeZone::LocalTime); 0112 query.addQueryItem(QStringLiteral("t"), dt.toString(Qt::ISODate)); 0113 0114 QUrl url(m_endpoint); 0115 url.setQuery(query); 0116 0117 QNetworkRequest netRequest(url); 0118 applySslConfiguration(netRequest); 0119 logRequest(req, netRequest); 0120 auto netReply = nam->get(netRequest); 0121 netReply->setParent(reply); 0122 QObject::connect(netReply, &QNetworkReply::finished, reply, [this, reply, netReply]() { 0123 netReply->deleteLater(); 0124 const auto data = netReply->readAll(); 0125 logReply(reply, netReply, data); 0126 0127 if (netReply->error() != QNetworkReply::NoError) { 0128 addError(reply, Reply::NetworkError, netReply->errorString()); 0129 return; 0130 } 0131 0132 IvvAssParser p(timeZone(), backendId()); 0133 auto result = p.parseStopovers(data); 0134 if (p.errorMessage.isEmpty()) { 0135 addResult(reply, this, std::move(result)); 0136 } else { 0137 addError(reply, Reply::UnknownError, std::move(p.errorMessage)); 0138 } 0139 }); 0140 0141 return true; 0142 } 0143 0144 static QString locationParameter(const Location &loc) 0145 { 0146 if (loc.hasCoordinate()) { 0147 return QString::number(loc.latitude()) + QLatin1Char(',') + QString::number(loc.longitude()); 0148 } 0149 return loc.identifier(IfoptUtil::identifierType()); 0150 } 0151 0152 bool IvvAssBackend::queryJourney(const JourneyRequest &req, JourneyReply *reply, QNetworkAccessManager *nam) const 0153 { 0154 QUrlQuery query; 0155 query.addQueryItem(QStringLiteral("eID"), QStringLiteral("tx_vrsinfo_ass2_router")); 0156 query.addQueryItem(QStringLiteral("f"), locationParameter(req.from())); 0157 query.addQueryItem(QStringLiteral("t"), locationParameter(req.to())); 0158 0159 auto dt = req.dateTime(); 0160 if (timeZone().isValid()) { 0161 dt = dt.toTimeZone(timeZone()); 0162 } 0163 dt.setTimeZone(QTimeZone::LocalTime); 0164 query.addQueryItem(req.dateTimeMode() == JourneyRequest::Departure ? QStringLiteral("d") : QStringLiteral("a"), dt.toString(Qt::ISODate)); 0165 query.addQueryItem(QStringLiteral("c"), QString::number(req.maximumResults())); 0166 query.addQueryItem(QStringLiteral("s"), QStringLiteral("t")); 0167 IvvAssProductMap::lineModesToQuery(req.lineModes(), query); 0168 0169 QString options; 0170 options.reserve(4); 0171 if (req.includeIntermediateStops()) { 0172 options += QLatin1Char('v'); // "vias" 0173 } 0174 if (req.includePaths()) { 0175 options += QLatin1Char('d'); // walking directions 0176 options += QLatin1Char('p'); // paths 0177 } 0178 options += QLatin1Char('a'); // load/demand 0179 query.addQueryItem(QStringLiteral("o"), options); 0180 0181 QUrl url(m_endpoint); 0182 url.setQuery(query); 0183 0184 QNetworkRequest netRequest(url); 0185 applySslConfiguration(netRequest); 0186 logRequest(req, netRequest); 0187 auto netReply = nam->get(netRequest); 0188 netReply->setParent(reply); 0189 QObject::connect(netReply, &QNetworkReply::finished, reply, [this, reply, netReply]() { 0190 netReply->deleteLater(); 0191 const auto data = netReply->readAll(); 0192 logReply(reply, netReply, data); 0193 0194 if (netReply->error() != QNetworkReply::NoError) { 0195 addError(reply, Reply::NetworkError, netReply->errorString()); 0196 return; 0197 } 0198 0199 IvvAssParser p(timeZone(), backendId()); 0200 auto result = p.parseJourneys(data); 0201 if (p.errorMessage.isEmpty()) { 0202 addResult(reply, this, std::move(result)); 0203 } else { 0204 addError(reply, Reply::UnknownError, std::move(p.errorMessage)); 0205 } 0206 }); 0207 0208 return true; 0209 }