Skip to content

Commit

Permalink
Store last modified time in project storage + retrieve it
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Apr 7, 2018
1 parent 0f5ea53 commit ab83455
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 2 deletions.
20 changes: 20 additions & 0 deletions python/core/qgsprojectstorage.sip.in
Expand Up @@ -24,6 +24,20 @@ and registered in QgsProjectStorageRegistry.
#include "qgsprojectstorage.h"
%End
public:

class Metadata
{
%Docstring
Metadata associated with a project
%End

%TypeHeaderCode
#include "qgsprojectstorage.h"
%End
public:
QDateTime lastModified;
};

virtual ~QgsProjectStorage();

virtual QString type() = 0;
Expand Down Expand Up @@ -63,6 +77,12 @@ was successful.
%Docstring
Rename an existing project at the given URI to a different URI. Returns true if renaming
was successful.
%End

virtual bool readProjectMetadata( const QString &uri, QgsProjectStorage::Metadata &metadata /Out/ );
%Docstring
Reads project metadata (e.g. last modified time) if this is supported by the storage implementation.
Returns true if the metadata were read with success.
%End

virtual QString visibleName();
Expand Down
16 changes: 16 additions & 0 deletions src/core/qgsprojectstorage.h
Expand Up @@ -17,7 +17,9 @@
#define QGSPROJECTSTORAGE_H

#include "qgis_core.h"
#include "qgis_sip.h"

#include <QDateTime>
#include <QString>

class QIODevice;
Expand All @@ -34,6 +36,14 @@ class QgsReadWriteContext;
class CORE_EXPORT QgsProjectStorage
{
public:

//! Metadata associated with a project
class Metadata
{
public:
QDateTime lastModified;
};

virtual ~QgsProjectStorage();

/**
Expand Down Expand Up @@ -73,6 +83,12 @@ class CORE_EXPORT QgsProjectStorage
*/
virtual bool renameProject( const QString &uri, const QString &uriNew ) { Q_UNUSED( uri ); Q_UNUSED( uriNew ); return false; }

/**
* Reads project metadata (e.g. last modified time) if this is supported by the storage implementation.
* Returns true if the metadata were read with success.
*/
virtual bool readProjectMetadata( const QString &uri, QgsProjectStorage::Metadata &metadata SIP_OUT ) { Q_UNUSED( uri ); Q_UNUSED( metadata ); return false; }

/**
* Returns human-readable name of the storage. Used as the menu item text in QGIS. Empty name
* indicates that the storage does not implement GUI support (showLoadGui() and showSaveGui()).
Expand Down
60 changes: 58 additions & 2 deletions src/providers/postgres/qgspostgresprojectstorage.cpp
Expand Up @@ -6,8 +6,32 @@
#include "qgsreadwritecontext.h"

#include <QIODevice>
#include <QJsonDocument>
#include <QJsonObject>
#include <QUrl>


static bool _parseMetadataDocument( const QJsonDocument &doc, QgsProjectStorage::Metadata &metadata )
{
if ( !doc.isObject() )
return false;

QJsonObject docObj = doc.object();
metadata.lastModified = QDateTime();
if ( docObj.contains( "last_modified_time" ) )
{
QString lastModifiedTimeStr = docObj["last_modified_time"].toString();
if ( !lastModifiedTimeStr.isEmpty() )
{
QDateTime lastModifiedUtc = QDateTime::fromString( lastModifiedTimeStr, Qt::ISODate );
lastModifiedUtc.setTimeSpec( Qt::UTC );
metadata.lastModified = lastModifiedUtc.toLocalTime();
}
}
return true;
}


static bool _projectsTableExists( QgsPostgresConn &conn, const QString &schemaName )
{
QString tableName( "qgis_projects" );
Expand Down Expand Up @@ -123,13 +147,18 @@ bool QgsPostgresProjectStorage::writeProject( const QString &uri, QIODevice *dev
// read from device and write to the table
QByteArray content = device->readAll();

QString metadata = "{ \"last_modified\": 123 }"; // TODO: real metadata
QString metadataExpr = QStringLiteral( "(%1 || (now() at time zone 'utc')::text || %2 || current_user || %3)::jsonb" ).arg(
QgsPostgresConn::quotedValue( "{ \"last_modified_time\": \"" ),
QgsPostgresConn::quotedValue( "\", \"last_modified_user\": \"" ),
QgsPostgresConn::quotedValue( "\" }" )
);

// TODO: would be useful to have QByteArray version of PQexec() to avoid bytearray -> string -> bytearray conversion
QString sql( "INSERT INTO %1.qgis_projects VALUES (%2, %3, E'\\\\x" );
sql = sql.arg( QgsPostgresConn::quotedIdentifier( projectUri.schemaName ),
QgsPostgresConn::quotedValue( projectUri.projectName ),
QgsPostgresConn::quotedValue( metadata ) );
metadataExpr // no need to quote: already quoted
);
sql += QString::fromAscii( content.toHex() );
sql += "') ON CONFLICT (name) DO UPDATE SET content = EXCLUDED.content, metadata = EXCLUDED.metadata;";

Expand Down Expand Up @@ -164,6 +193,33 @@ bool QgsPostgresProjectStorage::removeProject( const QString &uri )
}


bool QgsPostgresProjectStorage::readProjectMetadata( const QString &uri, QgsProjectStorage::Metadata &metadata )
{
QgsPostgresProjectUri projectUri = parseUri( uri );
if ( !projectUri.valid )
return false;

QgsPostgresConn *conn = QgsPostgresConnPool::instance()->acquireConnection( projectUri.connInfo.connectionInfo( false ) );

bool ok = false;
QString sql( QStringLiteral( "SELECT metadata FROM %1.qgis_projects WHERE name = %2" ).arg( QgsPostgresConn::quotedIdentifier( projectUri.schemaName ), QgsPostgresConn::quotedValue( projectUri.projectName ) ) );
QgsPostgresResult result( conn->PQexec( sql ) );
if ( result.PQresultStatus() == PGRES_TUPLES_OK )
{
if ( result.PQntuples() == 1 )
{
QString metadataStr = result.PQgetvalue( 0, 0 );
QJsonDocument doc( QJsonDocument::fromJson( metadataStr.toUtf8() ) );
ok = _parseMetadataDocument( doc, metadata );
}
}

QgsPostgresConnPool::instance()->releaseConnection( conn );

return ok;
}


QgsPostgresProjectUri QgsPostgresProjectStorage::parseUri( const QString &uri )
{
QUrl u = QUrl::fromEncoded( uri.toUtf8() );
Expand Down
2 changes: 2 additions & 0 deletions src/providers/postgres/qgspostgresprojectstorage.h
Expand Up @@ -35,6 +35,8 @@ class QgsPostgresProjectStorage : public QgsProjectStorage

virtual bool removeProject( const QString &uri ) override;

virtual bool readProjectMetadata( const QString &uri, QgsProjectStorage::Metadata &metadata ) override;

private:
static QgsPostgresProjectUri parseUri( const QString &uri );
};
Expand Down
10 changes: 10 additions & 0 deletions tests/src/python/test_project_storage_postgres.py
Expand Up @@ -28,6 +28,7 @@
QgsVectorLayer,
QgsProject,
)
from PyQt5.QtCore import QDateTime
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath

Expand Down Expand Up @@ -101,6 +102,15 @@ def testSaveLoadProject(self):

self.assertEqual(len(prj2.mapLayers()), 1)

# try to see project's metadata

res, metadata = prj_storage.readProjectMetadata(project_uri)
self.assertTrue(res)
time_project = metadata.lastModified
time_now = QDateTime.currentDateTime()
time_diff = time_now.secsTo(time_project)
self.assertTrue(abs(time_diff) < 10)

# try to remove the project

res = prj_storage.removeProject(project_uri)
Expand Down

0 comments on commit ab83455

Please sign in to comment.