From 65d93d4b6d9504247b4fd24881d38a031bc89b45 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Mon, 2 Jan 2017 16:28:03 +0100 Subject: [PATCH] Merge changes --- QtClient/JoplinQtClient/application.cpp | 15 +++- QtClient/JoplinQtClient/database.cpp | 19 +---- QtClient/JoplinQtClient/models/basemodel.cpp | 11 +++ QtClient/JoplinQtClient/models/basemodel.h | 1 + QtClient/JoplinQtClient/models/change.cpp | 87 ++++++++++++++++++++ QtClient/JoplinQtClient/models/change.h | 11 +++ QtClient/JoplinQtClient/models/folder.cpp | 2 +- QtClient/JoplinQtClient/synchronizer.cpp | 8 +- 8 files changed, 133 insertions(+), 21 deletions(-) diff --git a/QtClient/JoplinQtClient/application.cpp b/QtClient/JoplinQtClient/application.cpp index c33ec464b4..6bfcfea48d 100755 --- a/QtClient/JoplinQtClient/application.cpp +++ b/QtClient/JoplinQtClient/application.cpp @@ -3,6 +3,7 @@ #include "models/folder.h" #include "database.h" #include "models/foldermodel.h" +#include "models/change.h" #include "services/folderservice.h" #include "settings.h" #include "uuid.h" @@ -21,6 +22,18 @@ Application::Application(int &argc, char **argv) : jop::db().initialize("D:/Web/www/joplin/QtClient/data/notes.sqlite"); +// QVector changes = Change::all(); +// foreach (Change change, changes) { +// qDebug() << change.value("item_id").toString() << change.value("type").toInt() << change.mergedFields(); +// } + +// qDebug() << "====================================="; + +// changes = Change::mergedChanges(changes); +// foreach (Change change, changes) { +// qDebug() << change.value("item_id").toString() << change.value("type").toInt() << change.mergedFields(); +// } + // This is linked to where the QSettings will be saved. In other words, // if these values are changed, the settings will be reset and saved // somewhere else. @@ -101,7 +114,7 @@ void Application::afterSessionInitialization() { QString sessionId = settings.value("sessionId").toString(); qDebug() << "Session:" << sessionId; api_.setSessionId(sessionId); - //synchronizer_.start(); + synchronizer_.start(); } void Application::view_currentFolderChanged() { diff --git a/QtClient/JoplinQtClient/database.cpp b/QtClient/JoplinQtClient/database.cpp index 1a3527c6fb..0f2a0cefc5 100755 --- a/QtClient/JoplinQtClient/database.cpp +++ b/QtClient/JoplinQtClient/database.cpp @@ -14,7 +14,7 @@ void Database::initialize(const QString &path) { db_.setDatabaseName(path); if (!db_.open()) { - qDebug() << "Error: connection with database fail"; + qFatal("Error: connection with database fail"); } else { qDebug() << "Database: connection ok"; } @@ -22,13 +22,6 @@ void Database::initialize(const QString &path) { upgrade(); } -//QSqlQuery Database::query(const QString &sql) const { -// QSqlQuery output(db_); -// output.prepare(sql); -// //log(sql); -// return output; -//} - QSqlDatabase &Database::database() { return db_; } @@ -86,16 +79,6 @@ QSqlQuery Database::buildSqlQuery(Database::QueryType type, const QString &table } } - //log(sql, query); - -// qDebug() <<"SQL:"< i(query.boundValues()); -// while (i.hasNext()) { -// i.next(); -// qDebug() << i.key() << ":" << i.value().toString(); -// } - return query; } diff --git a/QtClient/JoplinQtClient/models/basemodel.cpp b/QtClient/JoplinQtClient/models/basemodel.cpp index 7ceb567e98..0838faeb60 100755 --- a/QtClient/JoplinQtClient/models/basemodel.cpp +++ b/QtClient/JoplinQtClient/models/basemodel.cpp @@ -11,6 +11,10 @@ QHash BaseModel::cache_; BaseModel::BaseModel() {} +BaseModel::BaseModel(const QSqlQuery &query) { + loadSqlQuery(query); +} + QStringList BaseModel::changedFields() const { QStringList output; for (QHash::const_iterator it = changedFields_.begin(); it != changedFields_.end(); ++it) { @@ -169,11 +173,18 @@ QVector BaseModel::tableFields(jop::Table table) { if (BaseModel::tableFields_.contains(table)) return BaseModel::tableFields_[table]; QVector output; + if (table == jop::FoldersTable) { output.push_back(createField("id", QMetaType::QString )); output.push_back(createField("title", QMetaType::QString )); output.push_back(createField("created_time", QMetaType::Int )); output.push_back(createField("updated_time", QMetaType::Int )); + } else if (table == jop::ChangesTable) { + output.push_back(createField("id", QMetaType::Int )); + output.push_back(createField("type", QMetaType::Int )); + output.push_back(createField("item_id", QMetaType::QString )); + output.push_back(createField("item_type", QMetaType::Int )); + output.push_back(createField("item_field", QMetaType::QString )); } BaseModel::tableFields_[table] = output; diff --git a/QtClient/JoplinQtClient/models/basemodel.h b/QtClient/JoplinQtClient/models/basemodel.h index ec0e966532..9321b6cdb4 100755 --- a/QtClient/JoplinQtClient/models/basemodel.h +++ b/QtClient/JoplinQtClient/models/basemodel.h @@ -40,6 +40,7 @@ public: }; BaseModel(); + BaseModel(const QSqlQuery& query); QStringList changedFields() const; static int count(jop::Table table); bool save(); diff --git a/QtClient/JoplinQtClient/models/change.cpp b/QtClient/JoplinQtClient/models/change.cpp index 2181515a4e..6a2b14e52e 100755 --- a/QtClient/JoplinQtClient/models/change.cpp +++ b/QtClient/JoplinQtClient/models/change.cpp @@ -1,9 +1,96 @@ #include "change.h" +#include "database.h" using namespace jop; Change::Change() {} +Change::Change(const QSqlQuery &query) { + loadSqlQuery(query); +} + Table Change::table() const { return jop::ChangesTable; } + +QVector Change::all(int limit) { + QString sql = QString("SELECT %1 FROM %2 ORDER BY id ASC LIMIT %3") + .arg(BaseModel::tableFieldNames(jop::ChangesTable).join(",")) + .arg(BaseModel::tableName(jop::ChangesTable)) + .arg(QString::number(limit)); + + QSqlQuery q(sql); + jop::db().execQuery(q); + + QVector output; + + while (q.next()) { + Change change(q); + output.push_back(change); + } + + return output; +} + +QVector Change::mergedChanges(const QVector& changes) { + QStringList createdItems; + QStringList deletedItems; + QHash itemChanges; + + foreach (Change change, changes) { + QString itemId = change.value("item_id").toString(); + Change::Type type = (Change::Type)change.value("type").toInt(); + + if (type == Change::Create) { + createdItems.push_back(itemId); + } else if (type == Change::Delete) { + deletedItems.push_back(itemId); + } + + if (itemChanges.contains(itemId) && type == Change::Update) { + // Merge all the "Update" event into one. + Change& existingChange = itemChanges[itemId]; + existingChange.addMergedField(change.value("item_field").toString()); + } else { + itemChanges[itemId] = change; + } + } + + QVector output; + + for (QHash::iterator it = itemChanges.begin(); it != itemChanges.end(); ++it) { + QString itemId = it.key(); + Change& change = it.value(); + + if (createdItems.contains(itemId) && deletedItems.contains(itemId)) { + // Item both created then deleted - skip + continue; + } + + if (deletedItems.contains(itemId)) { + // Item was deleted at some point - just return one 'delete' event + change.setValue("type", Change::Delete); + } else if (createdItems.contains(itemId)) { + // Item was created then updated - just return one 'create' event with the latest changes + change.setValue("type", Change::Create); + } + + output.push_back(change); + } + + return output; +} + +void Change::addMergedField(const QString &name) { + if (mergedFields_.contains(name)) return; + mergedFields_.push_back(name); +} + +QStringList Change::mergedFields() const { + QStringList output(mergedFields_); + QString itemField = value("item_field").toString(); + if (!mergedFields_.contains(itemField)) { + output.push_back(itemField); + } + return output; +} diff --git a/QtClient/JoplinQtClient/models/change.h b/QtClient/JoplinQtClient/models/change.h index c8adee3274..89c772d5d5 100755 --- a/QtClient/JoplinQtClient/models/change.h +++ b/QtClient/JoplinQtClient/models/change.h @@ -14,8 +14,19 @@ public: enum Type { Undefined, Create, Update, Delete }; Change(); + Change(const QSqlQuery& query); Table table() const; + static QVector all(int limit = 100); + static QVector mergedChanges(const QVector &changes); + + void addMergedField(const QString& name); + QStringList mergedFields() const; + +private: + + QStringList mergedFields_; + }; } diff --git a/QtClient/JoplinQtClient/models/folder.cpp b/QtClient/JoplinQtClient/models/folder.cpp index e59cf19e4a..9528f76c8c 100755 --- a/QtClient/JoplinQtClient/models/folder.cpp +++ b/QtClient/JoplinQtClient/models/folder.cpp @@ -24,7 +24,7 @@ int Folder::count() { } QVector Folder::all(const QString &orderBy) { - QSqlQuery q("SELECT " + BaseModel::tableFieldNames(jop::FoldersTable).join(",") + " FROM folders ORDER BY " + orderBy); + QSqlQuery q("SELECT " + BaseModel::tableFieldNames(jop::FoldersTable).join(",") + " FROM " + BaseModel::tableName(jop::FoldersTable) + " ORDER BY " + orderBy); jop::db().execQuery(q); QVector output; diff --git a/QtClient/JoplinQtClient/synchronizer.cpp b/QtClient/JoplinQtClient/synchronizer.cpp index 1bb52cbd00..919865f786 100755 --- a/QtClient/JoplinQtClient/synchronizer.cpp +++ b/QtClient/JoplinQtClient/synchronizer.cpp @@ -1,6 +1,7 @@ #include "synchronizer.h" #include "models/folder.h" #include "models/note.h" +#include "models/change.h" using namespace jop; @@ -12,7 +13,12 @@ Synchronizer::Synchronizer(WebApi& api, Database &database) : api_(api), db_(dat void Synchronizer::start() { qDebug() << "Starting synchronizer..."; - QSqlQuery query; + QVector changes = Change::all(); + foreach (Change& change, changes) { + + } + +// QSqlQuery query; // std::vector folders; // query = db_.query("SELECT " + Folder::dbFields().join(',') + " FROM folders WHERE synced = 0");