KPublicTransport

stopoverreply.cpp
1/*
2 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "stopoverreply.h"
8#include "logging.h"
9#include "reply_p.h"
10#include "requestcontext_p.h"
11#include "stopoverrequest.h"
12#include "backends/abstractbackend.h"
13#include "backends/cache.h"
14#include "datatypes/stopoverutil_p.h"
15
16#include <KPublicTransport/Stopover>
17
18#include <QNetworkReply>
19
20using namespace KPublicTransport;
21
22namespace KPublicTransport {
23class StopoverReplyPrivate : public ReplyPrivate {
24public:
25 void finalizeResult() override;
26 bool needToWaitForAssets() const override;
27
28 StopoverRequest request;
29 StopoverRequest nextRequest;
30 StopoverRequest prevRequest;
31 std::vector<Stopover> result;
32};
33}
34
35void StopoverReplyPrivate::finalizeResult()
36{
37 if (result.empty()) {
38 return;
39 }
41 errorMsg.clear();
42
43 std::sort(result.begin(), result.end(), [this](const auto &lhs, const auto &rhs) {
44 return StopoverUtil::timeLessThan(request, lhs, rhs);
45 });
46
47 for (auto it = result.begin(); it != result.end(); ++it) {
48 for (auto mergeIt = it + 1; mergeIt != result.end();) {
49 if (!StopoverUtil::timeEqual(request, (*it), (*mergeIt))) {
50 break;
51 }
52
53 if (Stopover::isSame(*it, *mergeIt)) {
54 *it = Stopover::merge(*it, *mergeIt);
55 mergeIt = result.erase(mergeIt);
56 } else {
57 ++mergeIt;
58 }
59 }
60 }
61
62 nextRequest.purgeLoops(request);
63 prevRequest.purgeLoops(request);
64}
65
66bool StopoverReplyPrivate::needToWaitForAssets() const
67{
68 return request.downloadAssets();
69}
70
71StopoverReply::StopoverReply(const StopoverRequest &req, QObject *parent)
72 : Reply(new StopoverReplyPrivate, parent)
73{
75 d->request = req;
76 d->nextRequest = req;
77 d->prevRequest = req;
78}
79
80StopoverReply::~StopoverReply() = default;
81
83{
84 Q_D(const StopoverReply);
85 return d->request;
86}
87
88const std::vector<Stopover>& StopoverReply::result() const
89{
90 Q_D(const StopoverReply);
91 return d->result;
92}
93
94std::vector<Stopover>&& StopoverReply::takeResult()
95{
97 return std::move(d->result);
98}
99
100void StopoverReply::addResult(const AbstractBackend *backend, std::vector<Stopover> &&res)
101{
103 // update context for next/prev requests
104 // do this first, before res gets moved from below
105 if (d->request.mode() == StopoverRequest::QueryDeparture && !res.empty()) {
106 // we create a context for later queries here in any case, since we can emulate that generically without backend support
107 auto context = d->nextRequest.context(backend);
108 context.type = RequestContext::Next;
109 for (const auto &dep : res) {
110 context.dateTime = std::max(context.dateTime, dep.scheduledDepartureTime());
111 }
112 d->nextRequest.setContext(backend, std::move(context));
113
114 if (backend->hasCapability(AbstractBackend::CanQueryPreviousDeparture)) {
115 context = d->prevRequest.context(backend);
116 context.type = RequestContext::Previous;
117 context.dateTime = res[0].scheduledDepartureTime();
118 for (const auto &jny : res) {
119 context.dateTime = std::min(context.dateTime, jny.scheduledDepartureTime());
120 }
121 d->prevRequest.setContext(backend, std::move(context));
122 }
123 } else if (d->request.mode() == StopoverRequest::QueryArrival && !res.empty()) {
124 // CanQueryArrivals is assumed here, otherwise this request would not have been possible already
125 auto context = d->nextRequest.context(backend);
126 context.type = RequestContext::Next;
127 for (const auto &dep : res) {
128 context.dateTime = std::max(context.dateTime, dep.scheduledArrivalTime());
129 }
130 d->nextRequest.setContext(backend, std::move(context));
131
132 if (backend->hasCapability(AbstractBackend::CanQueryPreviousDeparture)) {
133 context = d->prevRequest.context(backend);
134 context.type = RequestContext::Previous;
135 context.dateTime = res[0].scheduledArrivalTime();
136 for (const auto &jny : res) {
137 context.dateTime = std::min(context.dateTime, jny.scheduledArrivalTime());
138 }
139 d->prevRequest.setContext(backend, std::move(context));
140 }
141 }
142
143 // if this is a backend with a static timezone, apply this to the result
144 if (backend->timeZone().isValid()) {
145 for (auto &dep : res) {
146 StopoverUtil::applyTimeZone(dep, backend->timeZone());
147 }
148 }
149
150 // augment line information
151 for (auto &dep : res) {
152 dep.applyMetaData(request().downloadAssets());
153 }
154
155 // apply static attributions if @p backend contributed to the results
156 addAttribution(backend->attribution());
157
158 // cache negative hits, positive ones are too short-lived
159 if (res.empty()) {
160 Cache::addNegativeStopoverCacheEntry(backend->backendId(), request().cacheKey());
161 }
162
163 if (d->result.empty()) {
164 d->result = std::move(res);
165 } else {
166 d->result.insert(d->result.end(), res.begin(), res.end());
167 }
168
169 d->pendingOps--;
170 d->emitUpdated(this);
171 d->emitFinishedIfDone(this);
172}
173
175{
176 Q_D(const StopoverReply);
177 if (d->nextRequest.contexts().empty()) {
178 return {};
179 }
180 return d->nextRequest;
181}
182
184{
185 Q_D(const StopoverReply);
186 if (d->prevRequest.contexts().empty()) {
187 return {};
188 }
189 return d->prevRequest;
190}
191
192void StopoverReply::setNextContext(const AbstractBackend *backend, const QVariant &data)
193{
195 auto context = d->nextRequest.context(backend);
196 context.type = RequestContext::Next;
197 context.backendData = data;
198 d->nextRequest.setContext(backend, std::move(context));
199}
200
201void StopoverReply::setPreviousContext(const AbstractBackend *backend, const QVariant &data)
202{
204 auto context = d->prevRequest.context(backend);
205 context.type = RequestContext::Previous;
206 context.backendData = data;
207 d->prevRequest.setContext(backend, std::move(context));
208}
209
210void StopoverReply::addError(const AbstractBackend *backend, Reply::Error error, const QString &errorMsg)
211{
213 Cache::addNegativeStopoverCacheEntry(backend->backendId(), request().cacheKey());
214 } else {
215 qCDebug(Log) << backend->backendId() << error << errorMsg;
216 }
217 Reply::addError(error, errorMsg);
218}
Query response base class.
Definition reply.h:25
Error error() const
Error code.
Definition reply.cpp:46
Error
Error types.
Definition reply.h:31
@ NoError
Nothing went wrong.
Definition reply.h:32
@ NotFoundError
The requested journey/departure/place could not be found.
Definition reply.h:34
Departure or arrival query reply.
StopoverRequest previousRequest() const
Returns a request object for querying departures preceding the ones returned by this reply.
StopoverRequest nextRequest() const
Returns a request object for querying departures following the ones returned by this reply.
StopoverRequest request() const
The request this is the reply for.
std::vector< Stopover > && takeResult()
Returns the found arrival or departure information for moving elsewhere.
const std::vector< Stopover > & result() const
Returns the found arrival or departure information.
Describes an arrival or departure search.
@ QueryDeparture
Search for departures.
@ QueryArrival
Search for arrivals.
bool downloadAssets
Enable downloading of graphic assets such as line logos for the data requested here.
static Stopover merge(const Stopover &lhs, const Stopover &rhs)
Merge two departure instances.
Definition stopover.cpp:189
static bool isSame(const Stopover &lhs, const Stopover &rhs)
Checks if to instances refer to the same departure (which does not necessarily mean they are exactly ...
Definition stopover.cpp:159
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
Query operations and data types for accessing realtime public transport information from online servi...
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:06 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.