File indexing completed on 2024-05-12 04:42:52

0001 /*
0002     SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "stopoverquerymodel.h"
0008 #include "abstractquerymodel_p.h"
0009 #include "logging.h"
0010 #include "datatypes/stopoverutil_p.h"
0011 
0012 #include <KPublicTransport/Attribution>
0013 #include <KPublicTransport/Manager>
0014 #include <KPublicTransport/Stopover>
0015 #include <KPublicTransport/StopoverReply>
0016 
0017 #include <QDateTime>
0018 #include <QDebug>
0019 
0020 using namespace KPublicTransport;
0021 
0022 namespace KPublicTransport {
0023 class StopoverQueryModelPrivate : public AbstractQueryModelPrivate
0024 {
0025 public:
0026     void doQuery() override;
0027     void doClearResults() override;
0028     void mergeResults(const std::vector<Stopover> &newDepartures);
0029 
0030     std::vector<Stopover> m_departures;
0031 
0032     StopoverRequest m_request;
0033     StopoverRequest m_nextRequest;
0034     StopoverRequest m_prevRequest;
0035 
0036     Q_DECLARE_PUBLIC(StopoverQueryModel)
0037 };
0038 }
0039 
0040 void StopoverQueryModelPrivate::doQuery()
0041 {
0042     Q_Q(StopoverQueryModel);
0043     if (!m_manager || !m_request.isValid()) {
0044         return;
0045     }
0046 
0047     setLoading(true);
0048     m_nextRequest = {};
0049     m_prevRequest = {};
0050     Q_EMIT q->canQueryPrevNextChanged();
0051 
0052     auto reply = m_manager->queryStopover(m_request);
0053     monitorReply(reply);
0054     QObject::connect(reply, &KPublicTransport::StopoverReply::finished, q, [reply, q, this]{
0055         if (reply->error() == KPublicTransport::StopoverReply::NoError) {
0056             m_nextRequest = reply->nextRequest();
0057             m_prevRequest = reply->previousRequest();
0058             Q_EMIT q->canQueryPrevNextChanged();
0059         }
0060     });
0061     QObject::connect(reply, &KPublicTransport::StopoverReply::updated,q, [reply, this]() {
0062         mergeResults(reply->takeResult());
0063     });
0064 }
0065 
0066 void StopoverQueryModelPrivate::doClearResults()
0067 {
0068     m_departures.clear();
0069 }
0070 
0071 void StopoverQueryModelPrivate::mergeResults(const std::vector<Stopover> &newDepartures)
0072 {
0073     Q_Q(StopoverQueryModel);
0074     for (const auto &dep : newDepartures) {
0075         auto it = std::lower_bound(m_departures.begin(), m_departures.end(), dep, [this](const auto &lhs, const auto &rhs) {
0076             return StopoverUtil::timeLessThan(m_request, lhs, rhs);
0077         });
0078 
0079         bool found = false;
0080         while (it != m_departures.end() && StopoverUtil::timeEqual(m_request, dep, *it)) {
0081             if (Stopover::isSame(dep, *it)) {
0082                 *it = Stopover::merge(*it, dep);
0083                 found = true;
0084                 const auto row = std::distance(m_departures.begin(), it);
0085                 const auto idx = q->index(row, 0);
0086                 Q_EMIT q->dataChanged(idx, idx);
0087                 break;
0088             } else {
0089                 ++it;
0090             }
0091         }
0092         if (found) {
0093             continue;
0094         }
0095 
0096         const auto row = std::distance(m_departures.begin(), it);
0097         q->beginInsertRows({}, row, row);
0098         m_departures.insert(it, dep);
0099         q->endInsertRows();
0100     }
0101 }
0102 
0103 
0104 StopoverQueryModel::StopoverQueryModel(QObject *parent)
0105     : AbstractQueryModel(new StopoverQueryModelPrivate, parent)
0106 {
0107     connect(this, &AbstractQueryModel::loadingChanged, this, &StopoverQueryModel::canQueryPrevNextChanged);
0108 }
0109 
0110 StopoverQueryModel::~StopoverQueryModel() = default;
0111 
0112 StopoverRequest StopoverQueryModel::request() const
0113 {
0114     Q_D(const StopoverQueryModel);
0115     return d->m_request;
0116 }
0117 
0118 void StopoverQueryModel::setRequest(const StopoverRequest &req)
0119 {
0120     Q_D(StopoverQueryModel);
0121     d->m_request = req;
0122     Q_EMIT requestChanged();
0123     d->query();
0124 }
0125 
0126 bool StopoverQueryModel::canQueryNext() const
0127 {
0128     Q_D(const StopoverQueryModel);
0129     return !d->m_loading && !d->m_departures.empty() && d->m_nextRequest.isValid();
0130 }
0131 
0132 void StopoverQueryModel::queryNext()
0133 {
0134     Q_D(StopoverQueryModel);
0135     if (!canQueryNext()) {
0136         qCWarning(Log) << "Cannot query next journeys";
0137         return;
0138     }
0139 
0140     d->setLoading(true);
0141     auto reply = d->m_manager->queryStopover(d->m_nextRequest);
0142     d->monitorReply(reply);
0143     QObject::connect(reply, &KPublicTransport::StopoverReply::finished, this, [reply, this] {
0144         Q_D(StopoverQueryModel);
0145         if (reply->error() == KPublicTransport::StopoverReply::NoError) {
0146             d->m_nextRequest = reply->nextRequest();
0147         } else {
0148             d->m_nextRequest = {};
0149         }
0150         Q_EMIT canQueryPrevNextChanged();
0151     });
0152     QObject::connect(reply, &KPublicTransport::StopoverReply::updated, this, [reply, this]() {
0153         Q_D(StopoverQueryModel);
0154         d->mergeResults(reply->takeResult());
0155     });
0156 }
0157 
0158 bool StopoverQueryModel::canQueryPrevious() const
0159 {
0160     Q_D(const StopoverQueryModel);
0161     return !d->m_loading && !d->m_departures.empty() && d->m_prevRequest.isValid();
0162 }
0163 
0164 void StopoverQueryModel::queryPrevious()
0165 {
0166     Q_D(StopoverQueryModel);
0167     if (!canQueryPrevious()) {
0168         qCWarning(Log) << "Cannot query previous journeys";
0169         return;
0170     }
0171 
0172     d->setLoading(true);
0173     auto reply = d->m_manager->queryStopover(d->m_prevRequest);
0174     d->monitorReply(reply);
0175     QObject::connect(reply, &KPublicTransport::StopoverReply::finished, this, [reply, this] {
0176         Q_D(StopoverQueryModel);
0177         if (reply->error() == KPublicTransport::StopoverReply::NoError) {
0178             d->m_prevRequest = reply->previousRequest();
0179         } else {
0180             d->m_prevRequest = {};
0181         }
0182         Q_EMIT canQueryPrevNextChanged();
0183     });
0184     QObject::connect(reply, &KPublicTransport::StopoverReply::updated, this, [reply, this]() {
0185         Q_D(StopoverQueryModel);
0186         d->mergeResults(reply->takeResult());
0187     });
0188 }
0189 
0190 int StopoverQueryModel::rowCount(const QModelIndex& parent) const
0191 {
0192     Q_D(const StopoverQueryModel);
0193     if (parent.isValid()) {
0194         return 0;
0195     }
0196     return d->m_departures.size();
0197 }
0198 
0199 QVariant StopoverQueryModel::data(const QModelIndex& index, int role) const
0200 {
0201     Q_D(const StopoverQueryModel);
0202     if (!index.isValid()) {
0203         return {};
0204     }
0205 
0206     switch (role) {
0207         case DepartureRole:
0208             return QVariant::fromValue(d->m_departures[index.row()]);
0209     }
0210 
0211     return {};
0212 }
0213 
0214 QHash<int, QByteArray> StopoverQueryModel::roleNames() const
0215 {
0216     auto r = QAbstractListModel::roleNames();
0217     r.insert(DepartureRole, "departure");
0218     return r;
0219 }
0220 
0221 const std::vector<Stopover>& StopoverQueryModel::departures() const
0222 {
0223     Q_D(const StopoverQueryModel);
0224     return d->m_departures;
0225 }
0226 
0227 #include "moc_stopoverquerymodel.moc"