Skip to content
Permalink
Browse files
Merge pull request #43665 from nirvn/offline_followup
[offline editing] Follow up on string/number list field support
  • Loading branch information
nirvn committed Jun 11, 2021
2 parents 256dc35 + 757c6f4 commit 2066fd6fb9de1c18347166326ee8dbba62ee27cf
Showing with 31 additions and 63 deletions.
  1. +26 −54 src/core/qgsofflineediting.cpp
  2. +0 −5 src/core/qgsofflineediting.h
  3. +5 −4 tests/src/core/testqgsofflineediting.cpp
@@ -41,6 +41,7 @@
#include "qgsproviderregistry.h"
#include "qgsprovidermetadata.h"
#include "qgsmaplayerstylemanager.h"
#include "qgsjsonutils.h"

#include <QDir>
#include <QDomDocument>
@@ -784,7 +785,8 @@ QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlit
else if ( type == QVariant::StringList || type == QVariant::List )
{
ogrType = OFTString;
showWarning( tr( "Field '%1' from layer %2 has been converted from a list to a string of comma-separated values." ).arg( fieldName, layer->name() ) );
ogrSubType = OFSTJSON;
showWarning( tr( "Field '%1' from layer %2 has been converted from a list to a JSON-formatted string value." ).arg( fieldName, layer->name() ) );
}
else
ogrType = OFTString;
@@ -867,7 +869,7 @@ QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlit
QVariant attr = attrs.at( it );
if ( layer->fields().at( it ).type() == QVariant::StringList || layer->fields().at( it ).type() == QVariant::List )
{
attr = convertStringListToString( attr.toStringList() );
attr = QgsJsonUtils::encodeValue( attr );
}
newAttrs[column++] = attr;
}
@@ -1061,11 +1063,25 @@ void QgsOfflineEditing::applyFeaturesAdded( QgsVectorLayer *offlineLayer, QgsVec
QVariant attr = attrs.at( it );
if ( remoteLayer->fields().at( remoteAttributeIndex ).type() == QVariant::StringList )
{
attr = convertStringToStringList( attr.toString() );
if ( attr.type() == QVariant::StringList || attr.type() == QVariant::List )
{
attr = attr.toStringList();
}
else
{
attr = QgsJsonUtils::parseArray( attr.toString(), QVariant::String );
}
}
else if ( remoteLayer->fields().at( remoteAttributeIndex ).type() == QVariant::List )
{
attr = convertStringToList( attr.toString(), remoteLayer->fields().at( remoteAttributeIndex ).subType() );
if ( attr.type() == QVariant::StringList || attr.type() == QVariant::List )
{
attr = attr.toList();
}
else
{
attr = QgsJsonUtils::parseArray( attr.toString(), remoteLayer->fields().at( remoteAttributeIndex ).subType() );
}
}
newAttrs[ remoteAttributeIndex ] = attr;
}
@@ -1078,51 +1094,6 @@ void QgsOfflineEditing::applyFeaturesAdded( QgsVectorLayer *offlineLayer, QgsVec
}
}

QStringList QgsOfflineEditing::convertStringToStringList( const QString &string )
{
QStringList stringList = string.split( QRegularExpression( "(?<!\\\\)\\s*,\\s*" ) );
for ( QString &string : stringList )
{
string.replace( QLatin1String( "\\," ), QLatin1String( "," ) );
}
return stringList;
}

QString QgsOfflineEditing::convertStringListToString( const QStringList &stringList )
{
QStringList modifiedStringList = stringList;
for ( QString &string : modifiedStringList )
{
string.replace( QLatin1String( "," ), QLatin1String( "\\," ) );
}
return modifiedStringList.join( QLatin1Char( ',' ) );
}

QVariantList QgsOfflineEditing::convertStringToList( const QString &string, QVariant::Type type )
{
QVariantList variantList;
const QStringList stringList = string.split( QRegularExpression( "(?<!\\\\)\\s*,\\s*" ) );
for ( const QString &string : stringList )
{
switch ( type )
{
case QVariant::Int:
variantList << string.toInt();
break;
case QVariant::LongLong:
variantList << string.toLongLong();
break;
case QVariant::Double:
variantList << string.toDouble();
break;
default:
// unsupported list type
break;
}
}
return variantList;
}

void QgsOfflineEditing::applyFeaturesRemoved( QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId )
{
Q_ASSERT( remoteLayer );
@@ -1159,16 +1130,15 @@ void QgsOfflineEditing::applyAttributeValueChanges( QgsVectorLayer *offlineLayer
QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
QgsDebugMsgLevel( QStringLiteral( "Offline changeAttributeValue %1 = %2" ).arg( QString( attrLookup[ values.at( i ).attr ] ), values.at( i ).value ), 4 );


int remoteAttributeIndex = attrLookup[ values.at( i ).attr ];
QVariant attr = values.at( i ).value;
if ( remoteLayer->fields().at( remoteAttributeIndex ).type() == QVariant::StringList )
{
attr = convertStringToStringList( attr.toString() );
attr = QgsJsonUtils::parseArray( attr.toString(), QVariant::String );
}
else if ( remoteLayer->fields().at( remoteAttributeIndex ).type() == QVariant::List )
{
attr = convertStringToList( attr.toString(), remoteLayer->fields().at( remoteAttributeIndex ).subType() );
attr = QgsJsonUtils::parseArray( attr.toString(), remoteLayer->fields().at( remoteAttributeIndex ).subType() );
}

remoteLayer->changeAttributeValue( fid, remoteAttributeIndex, attr );
@@ -1733,12 +1703,14 @@ void QgsOfflineEditing::committedAttributeValuesChanges( const QString &qgisLaye
QgsAttributeMap attrMap = cit.value();
for ( QgsAttributeMap::const_iterator it = attrMap.constBegin(); it != attrMap.constEnd(); ++it )
{
QString value = it.value().type() == QVariant::StringList || it.value().type() == QVariant::List ? QgsJsonUtils::encodeValue( it.value() ) : it.value().toString();
value.replace( QLatin1String( "'" ), QLatin1String( "''" ) ); // escape quote
QString sql = QStringLiteral( "INSERT INTO 'log_feature_updates' VALUES ( %1, %2, %3, %4, '%5' )" )
.arg( layerId )
.arg( commitNo )
.arg( fid )
.arg( it.key() ) // attr
.arg( it.value().toString() ); // value
.arg( it.key() ) // attribute
.arg( value );
sqlExec( database.get(), sql );
}
}
@@ -128,11 +128,6 @@ class CORE_EXPORT QgsOfflineEditing : public QObject
void updateFidLookup( QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId );
void copySymbology( QgsVectorLayer *sourceLayer, QgsVectorLayer *targetLayer );

QString convertStringListToString( const QStringList &stringList );
QStringList convertStringToStringList( const QString &string );

QVariantList convertStringToList( const QString &string, QVariant::Type type );

/**
* Updates all relations that reference or are referenced by the source layer to the targetLayer.
*/
@@ -27,6 +27,7 @@
#include "qgsvectorlayerref.h"
#include "qgslayertree.h"
#include "qgsmaplayerstylemanager.h"
#include "qgsjsonutils.h"

/**
* \ingroup UnitTests
@@ -236,13 +237,13 @@ void TestQgsOfflineEditing::createGeopackageAndSynchronizeBack()
QCOMPARE( firstFeatureInAction.attribute( QStringLiteral( "Cabin Crew" ) ).toString(), firstFeatureBeforeAction.attribute( QStringLiteral( "Cabin Crew" ) ).toString() );

//check converted lists values
QCOMPARE( firstFeatureInAction.attribute( QStringLiteral( "StaffNames" ) ), QStringLiteral( "Bob,Alice" ) );
QCOMPARE( firstFeatureInAction.attribute( QStringLiteral( "StaffAges" ) ), QStringLiteral( "22,33" ) );
QCOMPARE( firstFeatureInAction.attribute( QStringLiteral( "StaffNames" ) ), QVariantList() << QStringLiteral( "Bob" ) << QStringLiteral( "Alice" ) );
QCOMPARE( firstFeatureInAction.attribute( QStringLiteral( "StaffAges" ) ), QVariantList() << 22 << 33 );

QgsFeature newFeature( mpLayer->dataProvider()->fields() );
newFeature.setAttribute( QStringLiteral( "Class" ), QStringLiteral( "Superjet" ) );
newFeature.setAttribute( QStringLiteral( "StaffNames" ), QStringLiteral( "Sebastien, Naomi, And\\, many\\, more" ) );
newFeature.setAttribute( QStringLiteral( "StaffAges" ), QStringLiteral( "0,2" ) );
newFeature.setAttribute( QStringLiteral( "StaffNames" ), QgsJsonUtils::parseArray( QStringLiteral( "[ \"Sebastien\", \"Naomi\", \"And, many, more\" ]" ) ) );
newFeature.setAttribute( QStringLiteral( "StaffAges" ), QgsJsonUtils::parseArray( QStringLiteral( "[ 0, 2 ]" ) ) );
mpLayer->startEditing();
mpLayer->addFeature( newFeature );
mpLayer->commitChanges();

0 comments on commit 2066fd6

Please sign in to comment.