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 }