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