Commit d9cfd328 authored by Volker Krause's avatar Volker Krause
Browse files

Let ApplicationController provide the import feedback

This allows to cover more things than just reservation data, and allows us
to also provide better error feedback. As a side-effect, this decouples
ReservationManager and PkPassManager from each other.
parent 7cb85638
......@@ -75,18 +75,22 @@ private Q_SLOTS:
QVERIFY(resSpy.isValid());
ApplicationController appController;
QSignalSpy infoSpy(&appController, &ApplicationController::infoMessage);
appController.setPkPassManager(&passMgr);
appController.setReservationManager(&resMgr);
appController.importData(readFile(QLatin1String(SOURCE_DIR "/data/4U8465-v1.json")));
QCOMPARE(resSpy.size(), 1);
QCOMPARE(passSpy.size(), 0);
QCOMPARE(infoSpy.size(), 1);
appController.importData(readFile(QLatin1String(SOURCE_DIR "/data/boardingpass-v1.pkpass")));
QCOMPARE(resSpy.size(), 1);
QCOMPARE(resSpy.size(), 2);
QCOMPARE(passSpy.size(), 1);
QCOMPARE(infoSpy.size(), 2);
appController.importData("M1DOE/JOHN EXXX007 TXLBRUSN 2592 110Y");
QCOMPARE(resSpy.size(), 2);
QCOMPARE(resSpy.size(), 3);
QCOMPARE(passSpy.size(), 1);
QCOMPARE(infoSpy.size(), 3);
// TODO PDF
}
......@@ -103,15 +107,18 @@ private Q_SLOTS:
QVERIFY(resSpy.isValid());
ApplicationController appController;
QSignalSpy infoSpy(&appController, &ApplicationController::infoMessage);
appController.setPkPassManager(&passMgr);
appController.setReservationManager(&resMgr);
appController.importFromUrl(QUrl::fromLocalFile(QLatin1String(SOURCE_DIR "/data/4U8465-v1.json")));
QCOMPARE(resSpy.size(), 1);
QCOMPARE(passSpy.size(), 0);
QCOMPARE(infoSpy.size(), 1);
appController.importFromUrl(QUrl::fromLocalFile(QLatin1String(SOURCE_DIR "/data/boardingpass-v1.pkpass")));
QCOMPARE(resSpy.size(), 1);
QCOMPARE(resSpy.size(), 2);
QCOMPARE(passSpy.size(), 1);
QCOMPARE(infoSpy.size(), 2);
// TODO PDF
}
......@@ -137,6 +144,7 @@ private Q_SLOTS:
HealthCertificateManager healthCertMgr;
ApplicationController appController;
QSignalSpy infoSpy(&appController, &ApplicationController::infoMessage);
appController.setPkPassManager(&passMgr);
appController.setReservationManager(&resMgr);
appController.setDocumentManager(&docMgr);
......@@ -146,17 +154,19 @@ private Q_SLOTS:
appController.importFromUrl(QUrl::fromLocalFile(QLatin1String(SOURCE_DIR "/data/4U8465-v1.json")));
appController.importFromUrl(QUrl::fromLocalFile(QLatin1String(SOURCE_DIR "/data/boardingpass-v1.pkpass")));
QCOMPARE(resMgr.batches().size(), 1);
QCOMPARE(resMgr.batches().size(), 2);
QCOMPARE(passMgr.passes().size(), 1);
QCOMPARE(infoSpy.size(), 2);
QTemporaryFile tmp;
QVERIFY(tmp.open());
tmp.close();
appController.exportToFile(tmp.fileName());
QCOMPARE(infoSpy.size(), 3);
KItinerary::File f(tmp.fileName());
QVERIFY(f.open(KItinerary::File::Read));
QCOMPARE(f.reservations().size(), 1);
QCOMPARE(f.reservations().size(), 2);
QCOMPARE(f.passes().size(), 1);
clearPasses(&passMgr);
......@@ -166,8 +176,9 @@ private Q_SLOTS:
QCOMPARE(passMgr.passes().size(), 0);
appController.importFromUrl(QUrl::fromLocalFile(tmp.fileName()));
QCOMPARE(resMgr.batches().size(), 1);
QCOMPARE(resMgr.batches().size(), 2);
QCOMPARE(passMgr.passes().size(), 1);
QCOMPARE(infoSpy.size(), 4);
clearPasses(&passMgr);
clearReservations(&resMgr);
......@@ -177,8 +188,9 @@ private Q_SLOTS:
QFile bundle(tmp.fileName());
QVERIFY(bundle.open(QFile::ReadOnly));
appController.importData(bundle.readAll());
QCOMPARE(resMgr.batches().size(), 1);
QCOMPARE(resMgr.batches().size(), 2);
QCOMPARE(passMgr.passes().size(), 1);
QCOMPARE(infoSpy.size(), 5);
}
};
......
......@@ -4,6 +4,7 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <applicationcontroller.h>
#include <reservationmanager.h>
#include <pkpassmanager.h>
......@@ -112,9 +113,12 @@ private Q_SLOTS:
clearPasses(&passMgr);
ReservationManager mgr;
mgr.setPkPassManager(&passMgr);
clearReservations(&mgr);
ApplicationController ctrl;
ctrl.setPkPassManager(&passMgr);
ctrl.setReservationManager(&mgr);
QSignalSpy addSpy(&mgr, &ReservationManager::reservationAdded);
QVERIFY(addSpy.isValid());
QSignalSpy updateSpy(&mgr, &ReservationManager::reservationChanged);
......
......@@ -6,6 +6,7 @@
#include "modelverificationpoint.h"
#include <applicationcontroller.h>
#include <favoritelocationmodel.h>
#include <locationinformation.h>
#include <pkpassmanager.h>
......@@ -112,8 +113,10 @@ private Q_SLOTS:
clearPasses(&mgr);
ReservationManager resMgr;
clearReservations(&resMgr);
ApplicationController ctrl;
ctrl.setPkPassManager(&mgr);
ctrl.setReservationManager(&resMgr);
resMgr.setPkPassManager(&mgr);
TimelineModel model;
QAbstractItemModelTester tester(&model);
model.setReservationManager(&resMgr);
......
......@@ -27,6 +27,8 @@
#include <KItinerary/File>
#include <KItinerary/JsonLdDocument>
#include <KPkPass/Pass>
#include <KAboutData>
#include <KLocalizedString>
......@@ -153,6 +155,8 @@ void ApplicationController::setReservationManager(ReservationManager* resMgr)
void ApplicationController::setPkPassManager(PkPassManager* pkPassMgr)
{
m_pkPassMgr = pkPassMgr;
connect(m_pkPassMgr, &PkPassManager::passAdded, this, &ApplicationController::importPass);
connect(m_pkPassMgr, &PkPassManager::passUpdated, this, &ApplicationController::importPass);
}
void ApplicationController::setDocumentManager(DocumentManager* docMgr)
......@@ -295,6 +299,7 @@ void ApplicationController::importFromUrl(const QUrl &url)
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
if (reply->error() != QNetworkReply::NoError) {
qCDebug(Log) << reply->url() << reply->errorString();
Q_EMIT infoMessage(i18n("Download failed: %1", reply->errorString()));
return;
}
importData(reply->readAll());
......@@ -315,10 +320,12 @@ void ApplicationController::importLocalFile(const QUrl &url)
QFile f(url.isLocalFile() ? url.toLocalFile() : url.toString());
if (!f.open(QFile::ReadOnly)) {
qCWarning(Log) << "Failed to open" << f.fileName() << f.errorString();
Q_EMIT infoMessage(i18n("Import failed: %1", f.errorString()));
return;
}
if (f.size() > 4000000 && !f.fileName().endsWith(QLatin1String(".itinerary"))) {
qCWarning(Log) << "File too large, ignoring" << f.fileName() << f.size();
Q_EMIT infoMessage(i18n("Import failed: File too large."));
return;
}
......@@ -411,7 +418,7 @@ void ApplicationController::exportToFile(const QString &filePath)
File f(QUrl(filePath).isLocalFile() ? QUrl(filePath).toLocalFile() : filePath);
if (!f.open(File::Write)) {
qCWarning(Log) << f.errorString();
// TODO show error in UI
Q_EMIT infoMessage(i18n("Export failed: %1", f.errorString()));
return;
}
......@@ -424,6 +431,7 @@ void ApplicationController::exportToFile(const QString &filePath)
exporter.exportHealthCertificates(m_healthCertMgr);
exporter.exportLiveData();
exporter.exportSettings();
Q_EMIT infoMessage(i18n("Export completed."));
}
void ApplicationController::exportTripToGpx(const QString &tripGroupId, const QString &filePath)
......@@ -435,6 +443,7 @@ void ApplicationController::exportTripToGpx(const QString &tripGroupId, const QS
QFile f(QUrl(filePath).isLocalFile() ? QUrl(filePath).toLocalFile() : filePath);
if (!f.open(QFile::WriteOnly)) {
qCWarning(Log) << f.errorString() << f.fileName();
Q_EMIT infoMessage(i18n("Export failed: %1", f.errorString()));
return;
}
GpxExport exporter(&f);
......@@ -447,14 +456,15 @@ void ApplicationController::exportTripToGpx(const QString &tripGroupId, const QS
const auto transferAfter = m_transferMgr->transfer(batchId, Transfer::After);
exporter.writeReservation(res, m_liveDataMgr->journey(batchId), transferBefore, transferAfter);
}
Q_EMIT infoMessage(i18n("Export completed."));
}
void ApplicationController::importBundle(const QUrl &url)
{
KItinerary::File f(url.isLocalFile() ? url.toLocalFile() : url.toString());
if (!f.open(File::Read)) {
// TODO show error in the ui
qCWarning(Log) << "Failed to open bundle file:" << url << f.errorString();
Q_EMIT infoMessage(i18n("Import failed: %1", f.errorString()));
return;
}
......@@ -468,8 +478,8 @@ void ApplicationController::importBundle(const QByteArray &data)
buffer.open(QBuffer::ReadOnly);
KItinerary::File f(&buffer);
if (!f.open(File::Read)) {
// TODO show error in the ui
qCWarning(Log) << "Failed to open bundle data:" << f.errorString();
Q_EMIT infoMessage(i18n("Import failed: %1", f.errorString()));
return;
}
......@@ -479,20 +489,25 @@ void ApplicationController::importBundle(const QByteArray &data)
void ApplicationController::importBundle(KItinerary::File *file)
{
Importer importer(file);
importer.importReservations(m_resMgr);
importer.importPasses(m_pkPassMgr);
importer.importDocuments(m_docMgr);
importer.importFavoriteLocations(m_favLocModel);
importer.importTransfers(m_resMgr, m_transferMgr);
importer.importHealthCertificates(m_healthCertMgr);
importer.importLiveData(m_liveDataMgr);
importer.importSettings();
{
QSignalBlocker blocker(this); // suppress infoMessage()
importer.importReservations(m_resMgr);
importer.importPasses(m_pkPassMgr);
importer.importDocuments(m_docMgr);
importer.importFavoriteLocations(m_favLocModel);
importer.importTransfers(m_resMgr, m_transferMgr);
importer.importHealthCertificates(m_healthCertMgr);
importer.importLiveData(m_liveDataMgr);
importer.importSettings();
}
// favorite locations
auto favLocs = FavoriteLocation::fromJson(QJsonDocument::fromJson(file->customData(QStringLiteral("org.kde.itinerary/favorite-locations"), QStringLiteral("locations"))).array());
if (!favLocs.empty()) {
m_favLocModel->setFavoriteLocations(std::move(favLocs));
}
Q_EMIT infoMessage(i18n("Import completed."));
}
QVector<QString> ApplicationController::importReservationOrHealthCertificate(const QByteArray &data, const QString &fileName)
......@@ -503,6 +518,7 @@ QVector<QString> ApplicationController::importReservationOrHealthCertificate(con
engine.setData(data, fileName);
const auto resIds = m_resMgr->importReservations(JsonLdDocument::fromJson(engine.extract()));
if (!resIds.isEmpty()) {
Q_EMIT infoMessage(i18np("One reservation imported.", "%1 reservations imported.", resIds.size()));
return resIds;
}
......@@ -529,6 +545,17 @@ void ApplicationController::importHealthCertificateRecursive(const ExtractorDocu
}
}
void ApplicationController::importPass(const QString &passId)
{
const auto pass = m_pkPassMgr->pass(passId);
KItinerary::ExtractorEngine engine;
engine.setContent(QVariant::fromValue<KPkPass::Pass*>(pass), u"application/vnd.apple.pkpass");
const auto resIds = m_resMgr->importReservations(JsonLdDocument::fromJson(engine.extract()));
if (!resIds.isEmpty()) {
Q_EMIT infoMessage(i18np("One reservation imported.", "%1 reservations imported.", resIds.size()));
}
}
void ApplicationController::addDocument(const QString &batchId, const QUrl &url)
{
if (!url.isValid()) {
......
......@@ -81,11 +81,15 @@ public:
Q_SIGNALS:
void clipboardContentChanged();
/** Human readable information message to be shown as passive notification. */
void infoMessage(const QString &msg);
private:
void importLocalFile(const QUrl &url);
void importBundle(KItinerary::File *file);
QVector<QString> importReservationOrHealthCertificate(const QByteArray &data, const QString &fileName = {});
void importHealthCertificateRecursive(const KItinerary::ExtractorDocumentNode &node);
void importPass(const QString &passId);
static ApplicationController *s_instance;
......
......@@ -248,7 +248,6 @@ int main(int argc, char **argv)
s_pkPassManager = &passMgr;
ReservationManager resMgr;
resMgr.setPkPassManager(&passMgr);
s_reservationManager = &resMgr;
DocumentManager docMgr;
......
......@@ -149,7 +149,7 @@ Kirigami.ApplicationWindow {
}
Connections {
target: ReservationManager
target: ApplicationController
function onInfoMessage(msg) { showPassiveNotification(msg, "short"); }
}
......
......@@ -19,8 +19,6 @@
#include <KItinerary/SortUtil>
#include <KItinerary/Visit>
#include <KPkPass/Pass>
#ifdef Q_OS_ANDROID
#include <KMime/Message>
#endif
......@@ -75,14 +73,6 @@ ReservationManager::ReservationManager(QObject* parent)
ReservationManager::~ReservationManager() = default;
void ReservationManager::setPkPassManager(PkPassManager* mgr)
{
m_passMgr = mgr;
connect(mgr, &PkPassManager::passAdded, this, &ReservationManager::passAdded);
connect(mgr, &PkPassManager::passUpdated, this, &ReservationManager::passUpdated);
connect(mgr, &PkPassManager::passRemoved, this, &ReservationManager::passRemoved);
}
bool ReservationManager::isEmpty() const
{
return m_batchToResMap.empty();
......@@ -204,7 +194,6 @@ QVector<QString> ReservationManager::importReservations(const QVector<QVariant>
ids.push_back(addReservation(res));
}
Q_EMIT infoMessage(i18np("One reservation imported.", "%1 reservations imported.", ids.size()));
return ids;
}
......@@ -335,27 +324,6 @@ void ReservationManager::removeBatch(const QString &batchId)
removeReservation(batchId);
}
void ReservationManager::passAdded(const QString& passId)
{
const auto pass = m_passMgr->pass(passId);
ExtractorEngine engine;
engine.setContent(QVariant::fromValue<KPkPass::Pass*>(pass), u"application/vnd.apple.pkpass");
const auto data = engine.extract();
const auto res = JsonLdDocument::fromJson(data);
importReservations(res);
}
void ReservationManager::passUpdated(const QString& passId)
{
passAdded(passId);
}
void ReservationManager::passRemoved(const QString& passId)
{
Q_UNUSED(passId)
// TODO
}
void ReservationManager::loadBatches()
{
Q_ASSERT(m_batches.empty());
......
......@@ -38,8 +38,6 @@ public:
explicit ReservationManager(QObject *parent = nullptr);
~ReservationManager();
void setPkPassManager(PkPassManager *mgr);
Q_INVOKABLE bool isEmpty() const;
Q_INVOKABLE QVariant reservation(const QString &id) const;
......@@ -91,18 +89,11 @@ Q_SIGNALS:
void batchRenamed(const QString &oldBatchId, const QString &newBatchId);
void batchRemoved(const QString &batchId);
/** Human readable information message. */
void infoMessage(const QString &msg);
private:
static QString reservationsBasePath();
static QString batchesBasePath();
void storeReservation(const QString &resId, const QVariant &res) const;
void passAdded(const QString &passId);
void passUpdated(const QString &passId);
void passRemoved(const QString &passId);
void loadBatches();
void initialBatchCreate();
void storeBatch(const QString &batchId) const;
......@@ -120,8 +111,6 @@ private:
std::vector<QString> m_batches;
QHash<QString, QStringList> m_batchToResMap; // ### QStringList for direct consumption by QML
QHash<QString, QString> m_resToBatchMap;
PkPassManager *m_passMgr = nullptr;
};
#endif // RESERVATIONMANAGER_H
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment