File indexing completed on 2024-12-08 07:19:11
0001 /* 0002 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "stopoverreply.h" 0008 #include "logging.h" 0009 #include "reply_p.h" 0010 #include "requestcontext_p.h" 0011 #include "stopoverrequest.h" 0012 #include "backends/abstractbackend.h" 0013 #include "backends/cache.h" 0014 #include "datatypes/stopoverutil_p.h" 0015 0016 #include <KPublicTransport/Stopover> 0017 0018 #include <QNetworkReply> 0019 0020 using namespace KPublicTransport; 0021 0022 namespace KPublicTransport { 0023 class StopoverReplyPrivate : public ReplyPrivate { 0024 public: 0025 void finalizeResult() override; 0026 bool needToWaitForAssets() const override; 0027 0028 StopoverRequest request; 0029 StopoverRequest nextRequest; 0030 StopoverRequest prevRequest; 0031 std::vector<Stopover> result; 0032 }; 0033 } 0034 0035 void StopoverReplyPrivate::finalizeResult() 0036 { 0037 if (result.empty()) { 0038 return; 0039 } 0040 error = Reply::NoError; 0041 errorMsg.clear(); 0042 0043 std::sort(result.begin(), result.end(), [this](const auto &lhs, const auto &rhs) { 0044 return StopoverUtil::timeLessThan(request, lhs, rhs); 0045 }); 0046 0047 for (auto it = result.begin(); it != result.end(); ++it) { 0048 for (auto mergeIt = it + 1; mergeIt != result.end();) { 0049 if (!StopoverUtil::timeEqual(request, (*it), (*mergeIt))) { 0050 break; 0051 } 0052 0053 if (Stopover::isSame(*it, *mergeIt)) { 0054 *it = Stopover::merge(*it, *mergeIt); 0055 mergeIt = result.erase(mergeIt); 0056 } else { 0057 ++mergeIt; 0058 } 0059 } 0060 } 0061 0062 nextRequest.purgeLoops(request); 0063 prevRequest.purgeLoops(request); 0064 } 0065 0066 bool StopoverReplyPrivate::needToWaitForAssets() const 0067 { 0068 return request.downloadAssets(); 0069 } 0070 0071 StopoverReply::StopoverReply(const StopoverRequest &req, QObject *parent) 0072 : Reply(new StopoverReplyPrivate, parent) 0073 { 0074 Q_D(StopoverReply); 0075 d->request = req; 0076 d->nextRequest = req; 0077 d->prevRequest = req; 0078 } 0079 0080 StopoverReply::~StopoverReply() = default; 0081 0082 StopoverRequest StopoverReply::request() const 0083 { 0084 Q_D(const StopoverReply); 0085 return d->request; 0086 } 0087 0088 const std::vector<Stopover>& StopoverReply::result() const 0089 { 0090 Q_D(const StopoverReply); 0091 return d->result; 0092 } 0093 0094 std::vector<Stopover>&& StopoverReply::takeResult() 0095 { 0096 Q_D(StopoverReply); 0097 return std::move(d->result); 0098 } 0099 0100 void StopoverReply::addResult(const AbstractBackend *backend, std::vector<Stopover> &&res) 0101 { 0102 Q_D(StopoverReply); 0103 // update context for next/prev requests 0104 // do this first, before res gets moved from below 0105 if (d->request.mode() == StopoverRequest::QueryDeparture && !res.empty()) { 0106 // we create a context for later queries here in any case, since we can emulate that generically without backend support 0107 auto context = d->nextRequest.context(backend); 0108 context.type = RequestContext::Next; 0109 for (const auto &dep : res) { 0110 context.dateTime = std::max(context.dateTime, dep.scheduledDepartureTime()); 0111 } 0112 d->nextRequest.setContext(backend, std::move(context)); 0113 } 0114 0115 // if this is a backend with a static timezone, apply this to the result 0116 if (backend->timeZone().isValid()) { 0117 for (auto &dep : res) { 0118 StopoverUtil::applyTimeZone(dep, backend->timeZone()); 0119 } 0120 } 0121 0122 // augment line information 0123 for (auto &dep : res) { 0124 dep.applyMetaData(request().downloadAssets()); 0125 } 0126 0127 // apply static attributions if @p backend contributed to the results 0128 addAttribution(backend->attribution()); 0129 0130 // cache negative hits, positive ones are too short-lived 0131 if (res.empty()) { 0132 Cache::addNegativeDepartureCacheEntry(backend->backendId(), request().cacheKey()); 0133 } 0134 0135 if (d->result.empty()) { 0136 d->result = std::move(res); 0137 } else { 0138 d->result.insert(d->result.end(), res.begin(), res.end()); 0139 } 0140 0141 d->pendingOps--; 0142 d->emitUpdated(this); 0143 d->emitFinishedIfDone(this); 0144 } 0145 0146 StopoverRequest StopoverReply::nextRequest() const 0147 { 0148 Q_D(const StopoverReply); 0149 if (d->nextRequest.contexts().empty()) { 0150 return {}; 0151 } 0152 return d->nextRequest; 0153 } 0154 0155 StopoverRequest StopoverReply::previousRequest() const 0156 { 0157 Q_D(const StopoverReply); 0158 if (d->prevRequest.contexts().empty()) { 0159 return {}; 0160 } 0161 return d->prevRequest; 0162 } 0163 0164 void StopoverReply::setNextContext(const AbstractBackend *backend, const QVariant &data) 0165 { 0166 Q_D(StopoverReply); 0167 auto context = d->nextRequest.context(backend); 0168 context.type = RequestContext::Next; 0169 context.backendData = data; 0170 d->nextRequest.setContext(backend, std::move(context)); 0171 } 0172 0173 void StopoverReply::setPreviousContext(const AbstractBackend *backend, const QVariant &data) 0174 { 0175 Q_D(StopoverReply); 0176 auto context = d->prevRequest.context(backend); 0177 context.type = RequestContext::Previous; 0178 context.backendData = data; 0179 d->prevRequest.setContext(backend, std::move(context)); 0180 } 0181 0182 void StopoverReply::addError(const AbstractBackend *backend, Reply::Error error, const QString &errorMsg) 0183 { 0184 if (error == Reply::NotFoundError) { 0185 Cache::addNegativeDepartureCacheEntry(backend->backendId(), request().cacheKey()); 0186 } else { 0187 qCDebug(Log) << backend->backendId() << error << errorMsg; 0188 } 0189 Reply::addError(error, errorMsg); 0190 }