diff --git a/python/gui/auto_generated/history/qgshistoryproviderregistry.sip.in b/python/gui/auto_generated/history/qgshistoryproviderregistry.sip.in index ee9cabaa27e..3f30ea60138 100644 --- a/python/gui/auto_generated/history/qgshistoryproviderregistry.sip.in +++ b/python/gui/auto_generated/history/qgshistoryproviderregistry.sip.in @@ -125,7 +125,7 @@ Constructor for HistoryEntryOptions. Qgis::HistoryProviderBackends storageBackends; }; - bool addEntry( const QString &providerId, const QVariantMap &entry, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); + long long addEntry( const QString &providerId, const QVariantMap &entry, bool &ok /Out/, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); %Docstring Adds an ``entry`` to the history logs. @@ -133,16 +133,48 @@ The entry will be tagged with the current date/time as the timestamp. The ``providerId`` specifies the history provider responsible for this entry. Entry options are specified via the ``options`` argument. + +:param providerId: associated :py:func:`QgsAbstractHistoryProvider.id()` +:param entry: entry to add +:param options: options + +:return: - ID of newly added entry. + - ok: will be set to ``True`` if entry was successfully added %End - bool addEntry( const QgsHistoryEntry &entry, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); + long long addEntry( const QgsHistoryEntry &entry, bool &ok /Out/, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); %Docstring Adds an ``entry`` to the history logs. + +:param entry: entry to add +:param options: options + +:return: - ID of newly added entry. + - ok: will be set to ``True`` if entry was successfully added %End bool addEntries( const QList< QgsHistoryEntry > &entries, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); %Docstring Adds a list of ``entries`` to the history logs. +%End + + QgsHistoryEntry entry( long long id, bool &ok, Qgis::HistoryProviderBackend backend = Qgis::HistoryProviderBackend::LocalProfile ) const; +%Docstring +Returns the entry with matching ID, from the specified ``backend``. + +:param id: ID of entry to find +:param ok: will be set to ``True`` if entry was found +:param backend: associated backend + +:return: matching entry if found +%End + + bool updateEntry( long long id, const QVariantMap &entry, Qgis::HistoryProviderBackend backend = Qgis::HistoryProviderBackend::LocalProfile ); +%Docstring +Updates the existing entry with matching ``id``. + +This method allows the content of an entry to be updated, e.g. to add additional properties +to the content. (Such as recording the results of after a long-running operation completes). %End QList< QgsHistoryEntry > queryEntries( const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime(), diff --git a/src/gui/history/qgshistoryproviderregistry.cpp b/src/gui/history/qgshistoryproviderregistry.cpp index 5faa6484e90..c2e46065872 100644 --- a/src/gui/history/qgshistoryproviderregistry.cpp +++ b/src/gui/history/qgshistoryproviderregistry.cpp @@ -111,13 +111,15 @@ QStringList QgsHistoryProviderRegistry::providerIds() const return mProviders.keys(); } -bool QgsHistoryProviderRegistry::addEntry( const QString &providerId, const QVariantMap &entry, QgsHistoryProviderRegistry::HistoryEntryOptions options ) +long long QgsHistoryProviderRegistry::addEntry( const QString &providerId, const QVariantMap &entry, bool &ok, QgsHistoryProviderRegistry::HistoryEntryOptions options ) { - return addEntry( QgsHistoryEntry( providerId, QDateTime::currentDateTime(), entry ), options ); + return addEntry( QgsHistoryEntry( providerId, QDateTime::currentDateTime(), entry ), ok, options ); } -bool QgsHistoryProviderRegistry::addEntry( const QgsHistoryEntry &entry, HistoryEntryOptions options ) +long long QgsHistoryProviderRegistry::addEntry( const QgsHistoryEntry &entry, bool &ok, HistoryEntryOptions options ) { + ok = true; + long long id = -1; if ( options.storageBackends & Qgis::HistoryProviderBackend::LocalProfile ) { QDomDocument xmlDoc; @@ -130,24 +132,92 @@ bool QgsHistoryProviderRegistry::addEntry( const QgsHistoryEntry &entry, History if ( !runEmptyQuery( query ) ) { QgsDebugMsg( QStringLiteral( "Couldn't story history entry in database!" ) ); - return false; + ok = false; + return -1; } + id = static_cast< int >( sqlite3_last_insert_rowid( mLocalDB.get() ) ); } - return true; + return id; } bool QgsHistoryProviderRegistry::addEntries( const QList &entries, HistoryEntryOptions options ) { + bool ok = true; if ( options.storageBackends & Qgis::HistoryProviderBackend::LocalProfile ) { runEmptyQuery( QStringLiteral( "BEGIN TRANSACTION;" ) ); for ( const QgsHistoryEntry &entry : entries ) - addEntry( entry, options ); + addEntry( entry, ok, options ); runEmptyQuery( QStringLiteral( "COMMIT TRANSACTION;" ) ); } - return true; + return ok; +} + +QgsHistoryEntry QgsHistoryProviderRegistry::entry( long long id, bool &ok, Qgis::HistoryProviderBackend backend ) const +{ + ok = false; + switch ( backend ) + { + case Qgis::HistoryProviderBackend::LocalProfile: + { + if ( !mLocalDB ) + { + QgsDebugMsg( QStringLiteral( "Cannot open database to query history entries" ) ); + return QgsHistoryEntry( QVariantMap() ); + } + + QString sql = QStringLiteral( "SELECT provider_id, xml, timestamp FROM history WHERE id=%1" ).arg( id ); + + int nErr; + sqlite3_statement_unique_ptr statement = mLocalDB.prepare( sql, nErr ); + + if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW ) + { + QDomDocument doc; + if ( !doc.setContent( statement.columnAsText( 1 ) ) ) + { + QgsDebugMsg( QStringLiteral( "Cannot read history entry" ) ); + return QgsHistoryEntry( QVariantMap() ); + } + + ok = true; + return QgsHistoryEntry( + statement.columnAsText( 0 ), + QDateTime::fromString( statement.columnAsText( 2 ), QStringLiteral( "yyyy-MM-dd HH:mm:ss" ) ), + QgsXmlUtils::readVariant( doc.documentElement() ).toMap() + ); + } + + QgsDebugMsg( QStringLiteral( "Cannot find history item with matching ID" ) ); + return QgsHistoryEntry( QVariantMap() ); + } + } + BUILTIN_UNREACHABLE +} + +bool QgsHistoryProviderRegistry::updateEntry( long long id, const QVariantMap &entry, Qgis::HistoryProviderBackend backend ) +{ + switch ( backend ) + { + case Qgis::HistoryProviderBackend::LocalProfile: + { + QDomDocument xmlDoc; + xmlDoc.appendChild( QgsXmlUtils::writeVariant( entry, xmlDoc ) ); + const QString entryXml = xmlDoc.toString(); + + QString query = qgs_sqlite3_mprintf( "UPDATE history SET xml='%q' WHERE id = %d;", + entryXml.toUtf8().constData(), id ); + if ( !runEmptyQuery( query ) ) + { + QgsDebugMsg( QStringLiteral( "Couldn't update history entry in database!" ) ); + return false; + } + return true; + } + } + BUILTIN_UNREACHABLE } QList QgsHistoryProviderRegistry::queryEntries( const QDateTime &start, const QDateTime &end, const QString &providerId, Qgis::HistoryProviderBackends backends ) const diff --git a/src/gui/history/qgshistoryproviderregistry.h b/src/gui/history/qgshistoryproviderregistry.h index c2ad09e2fe4..3de06d2331f 100644 --- a/src/gui/history/qgshistoryproviderregistry.h +++ b/src/gui/history/qgshistoryproviderregistry.h @@ -162,19 +162,51 @@ class GUI_EXPORT QgsHistoryProviderRegistry : public QObject * * The \a providerId specifies the history provider responsible for this entry. * Entry options are specified via the \a options argument. + * + * \param providerId associated QgsAbstractHistoryProvider::id() + * \param entry entry to add + * \param ok will be set to TRUE if entry was successfully added + * \param options options + * + * \returns ID of newly added entry. */ - bool addEntry( const QString &providerId, const QVariantMap &entry, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); + long long addEntry( const QString &providerId, const QVariantMap &entry, bool &ok SIP_OUT, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); /** * Adds an \a entry to the history logs. + * + * \param entry entry to add + * \param ok will be set to TRUE if entry was successfully added + * \param options options + * + * \returns ID of newly added entry. */ - bool addEntry( const QgsHistoryEntry &entry, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); + long long addEntry( const QgsHistoryEntry &entry, bool &ok SIP_OUT, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); /** * Adds a list of \a entries to the history logs. */ bool addEntries( const QList< QgsHistoryEntry > &entries, QgsHistoryProviderRegistry::HistoryEntryOptions options = QgsHistoryProviderRegistry::HistoryEntryOptions() ); + /** + * Returns the entry with matching ID, from the specified \a backend. + * + * \param id ID of entry to find + * \param ok will be set to TRUE if entry was found + * \param backend associated backend + * + * \returns matching entry if found + */ + QgsHistoryEntry entry( long long id, bool &ok, Qgis::HistoryProviderBackend backend = Qgis::HistoryProviderBackend::LocalProfile ) const; + + /** + * Updates the existing entry with matching \a id. + * + * This method allows the content of an entry to be updated, e.g. to add additional properties + * to the content. (Such as recording the results of after a long-running operation completes). + */ + bool updateEntry( long long id, const QVariantMap &entry, Qgis::HistoryProviderBackend backend = Qgis::HistoryProviderBackend::LocalProfile ); + /** * Queries history entries which occurred between the specified \a start and \a end times. * diff --git a/tests/src/python/test_qgshistoryproviderregistry.py b/tests/src/python/test_qgshistoryproviderregistry.py index 00b825c4280..7475b9de15c 100644 --- a/tests/src/python/test_qgshistoryproviderregistry.py +++ b/tests/src/python/test_qgshistoryproviderregistry.py @@ -91,10 +91,12 @@ class TestQgsHistoryProviderRegistry(unittest.TestCase): reg = QgsHistoryProviderRegistry(useMemoryDatabase=True) self.assertFalse(reg.queryEntries()) - reg.addEntry('my provider', {'some var': 5, 'other_var': [1, 2, 3], 'final_var': {'a': 'b'}}, - QgsHistoryProviderRegistry.HistoryEntryOptions()) - reg.addEntry('my provider 2', {'some var': 6}, - QgsHistoryProviderRegistry.HistoryEntryOptions()) + id_1, ok = reg.addEntry('my provider', {'some var': 5, 'other_var': [1, 2, 3], 'final_var': {'a': 'b'}}, + QgsHistoryProviderRegistry.HistoryEntryOptions()) + self.assertTrue(ok) + id_2, ok = reg.addEntry('my provider 2', {'some var': 6}, + QgsHistoryProviderRegistry.HistoryEntryOptions()) + self.assertTrue(ok) self.assertEqual(len(reg.queryEntries()), 2) self.assertEqual(reg.queryEntries()[0].providerId, 'my provider') @@ -105,8 +107,22 @@ class TestQgsHistoryProviderRegistry(unittest.TestCase): self.assertEqual(reg.queryEntries()[1].timestamp.date(), QDateTime.currentDateTime().date()) self.assertEqual(reg.queryEntries()[1].entry, {'some var': 6}) + entry, ok = reg.entry(1111) + self.assertFalse(ok) + entry, ok = reg.entry(id_1) + self.assertTrue(ok) + self.assertEqual(entry.providerId, 'my provider') + self.assertEqual(entry.timestamp.date(), QDateTime.currentDateTime().date()) + self.assertEqual(entry.entry, {'some var': 5, 'other_var': [1, 2, 3], 'final_var': {'a': 'b'}}) + entry, ok = reg.entry(id_2) + self.assertTrue(ok) + self.assertEqual(entry.providerId, 'my provider 2') + self.assertEqual(entry.timestamp.date(), QDateTime.currentDateTime().date()) + self.assertEqual(entry.entry, {'some var': 6}) + entry = QgsHistoryEntry('my provider 3', QDateTime(2021, 1, 2, 3, 4, 5), {'var': 7}) - reg.addEntry(entry) + id_3, ok = reg.addEntry(entry) + self.assertTrue(ok) self.assertEqual(len(reg.queryEntries()), 3) self.assertEqual(reg.queryEntries()[0].providerId, 'my provider') self.assertEqual(reg.queryEntries()[0].timestamp.date(), QDateTime.currentDateTime().date()) @@ -144,6 +160,13 @@ class TestQgsHistoryProviderRegistry(unittest.TestCase): self.assertEqual(len(entries), 1) self.assertEqual(entries[0].providerId, 'my provider 3') + # update an entry + self.assertTrue(reg.updateEntry(id_1, {'new_props': 54})) + entry, ok = reg.entry(id_1) + self.assertEqual(entry.providerId, 'my provider') + self.assertEqual(entry.timestamp.date(), QDateTime.currentDateTime().date()) + self.assertEqual(entry.entry, {'new_props': 54}) + self.assertTrue(reg.clearHistory(Qgis.HistoryProviderBackend.LocalProfile)) self.assertFalse(reg.queryEntries())