Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Tests for represent_attributes
  • Loading branch information
elpaso committed Dec 14, 2021
1 parent f0a337c commit ae8fc0d
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 23 deletions.
28 changes: 20 additions & 8 deletions resources/function_help/json/represent_attributes
Expand Up @@ -2,13 +2,25 @@
"name": "represent_attributes",
"type": "function",
"groups": ["Record and Attributes"],
"description": "Returns a map with the attribute names as keys and the configured representation values as values. The representation value for the attributes depends on the configured widget type for each attribute. Often, this is useful for 'Value Map' widgets.",
"arguments": [
{"arg":"feature", "description": "The feature containing the attributes to be represented. (Optional)" },
{"arg":"layer", "description": "The vector layer the feature belongs to. (Optional)"}
],
"examples": [
{ "expression":"represent_attributes()", "returns":"The representation values for the current feature"},
{ "expression":"represent_attributes(feature, layer)", "returns":"The representation values for the specified feature and layer"}
"description": "Returns a map with the attribute names as keys and the configured representation values as values. The representation value for the attributes depends on the configured widget type for each attribute. Can be used with zero, one or more arguments, see below for details.",
"variants": [
{
"variant": "No parameters",
"variant_description": "If called with no parameters, the function will return the representation of the attributes of the current feature in the current layer.",
"arguments": [],
"examples": [ { "expression": "represent_attributes()", "returns" : "The representation of the attributes for the current feature." } ]
},
{
"variant": "One 'feature' parameter",
"variant_description": "If called with a 'feature' parameter only, the function will return the representation of the attributes of the specified feature from the current layer.",
"arguments": [ { "arg": "feature", "description": "The feature which should be evaluated." } ],
"examples": [ { "expression": "represent_attributes(@atlas_feature)", "returns" : "The representation of the attributes for the specified feature from the current layer." } ]
},
{
"variant": "Layer and feature parameters",
"variant_description": "If called with a 'feature' and a 'layer' parameter, the function will return the representation of the attributes of the specified feature from the specified layer.",
"arguments": [ { "arg": "feature", "description": "The feature which should be evaluated." }, { "arg": "layer", "description": "The layer (or its ID or name)." } ],
"examples": [ { "expression": "represent_attributes(@atlas_feature, 'atlas_layer')", "returns" : "The representation of the attributes for the specified feature from the specified layer." } ]
}
]
}
47 changes: 32 additions & 15 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -1731,26 +1731,43 @@ static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExp
{
QgsVectorLayer *layer = nullptr;
QgsFeature feature;
if ( ( values.size() == 0 || values.at( 0 ).isNull() ) && context )
{
feature = context->feature();
// first step - find current layer
layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
}
else if ( values.size() == 2 && ! values.at( 1 ).isNull() )

if ( values.size() == 2 )
{
feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
layer = QgsExpressionUtils::getVectorLayer( values.at( 1 ), parent );
if ( ! values.at( 0 ).isNull() )
{
feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
}
else if ( context )
{
feature = context->feature();
}

if ( ! values.at( 1 ).isNull() )
{
layer = QgsExpressionUtils::getVectorLayer( values.at( 1 ), parent );
}
else if ( context )
{
layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
}
else
{
parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer is not set" ) );
return QVariant();
}
}
else

// Check if it's a default constructed feature possibly coming from the contex
if ( ! feature.isValid() && feature.attributeCount() == 0 && feature.id() == FID_NULL )
{
parent->setEvalErrorString( QObject::tr( "Layer is not set" ) );
parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature is not set" ) );
return QVariant();
}

if ( !layer )
{
parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function in this context: layer coould not be resolved." ) );
parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
return QVariant();
}

Expand Down Expand Up @@ -7739,10 +7756,10 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
attributesFunc->setIsStatic( false );
functions << attributesFunc;
QgsStaticExpressionFunction *formattedAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true ),
QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true ),
fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
formattedAttributesFunc->setIsStatic( false );
functions << formattedAttributesFunc;
representAttributesFunc->setIsStatic( false );
functions << representAttributesFunc;

QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction(
QStringLiteral( "maptip" ),
Expand Down
70 changes: 70 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -519,6 +519,76 @@ class TestQgsExpression: public QObject
void represent_attributes()
{

QgsVectorLayer layer { QStringLiteral( "Point?field=col1:integer&field=col2:string" ), QStringLiteral( "test_represent_attributes" ), QStringLiteral( "memory" ) };
QVERIFY( layer.isValid() );
QgsFeature f1( layer.dataProvider()->fields(), 1 );
f1.setAttribute( QStringLiteral( "col1" ), 1 );
f1.setAttribute( QStringLiteral( "col2" ), "test1" );
QgsFeature f2( layer.dataProvider()->fields(), 2 );
f2.setAttribute( QStringLiteral( "col1" ), 2 );
f2.setAttribute( QStringLiteral( "col2" ), "test2" );
layer.dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 );

QVariantMap config;
QVariantMap map;
map.insert( QStringLiteral( "one" ), QStringLiteral( "1" ) );
map.insert( QStringLiteral( "two" ), QStringLiteral( "2" ) );

config.insert( QStringLiteral( "map" ), map );
layer.setEditorWidgetSetup( 0, QgsEditorWidgetSetup( QStringLiteral( "ValueMap" ), config ) );

QgsExpressionContext context( { QgsExpressionContextUtils::layerScope( &layer ) } );
context.setFeature( f2 );
QgsExpression expression( "represent_attributes()" );

if ( expression.hasParserError() )
qDebug() << expression.parserErrorString();
QVERIFY( !expression.hasParserError() );

expression.prepare( &context );

QVariantMap result { expression.evaluate( &context ).toMap() };

QCOMPARE( result.value( QStringLiteral( "col1" ) ).toString(), QStringLiteral( "two" ) );
QCOMPARE( result.value( QStringLiteral( "col2" ) ).toString(), QStringLiteral( "test2" ) );

QgsExpressionContext context2( { QgsExpressionContextUtils::layerScope( &layer ) } );
context2.setFeature( f2 );
expression = QgsExpression( "represent_attributes($currentfeature)" );

result = expression.evaluate( &context2 ).toMap();

QVERIFY( !expression.hasEvalError() );

QCOMPARE( result.value( QStringLiteral( "col1" ) ).toString(), QStringLiteral( "two" ) );
QCOMPARE( result.value( QStringLiteral( "col2" ) ).toString(), QStringLiteral( "test2" ) );

QgsProject::instance()->addMapLayer( &layer, false, false );
QgsExpressionContext context3;
context3.setFeature( f2 );
expression = QgsExpression( "represent_attributes($currentfeature, 'test_represent_attributes')" );

result = expression.evaluate( &context3 ).toMap();

QVERIFY( !expression.hasEvalError() );

QCOMPARE( result.value( QStringLiteral( "col1" ) ).toString(), QStringLiteral( "two" ) );
QCOMPARE( result.value( QStringLiteral( "col2" ) ).toString(), QStringLiteral( "test2" ) );

// Test the cached value
QCOMPARE( context3.cachedValue( QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer.id(), QStringLiteral( "col1" ), QStringLiteral( "2" ) ) ).toString(), QStringLiteral( "two" ) );

// Test errors
expression = QgsExpression( "represent_attributes($currentfeature, 'test_represent_attributes')" );
QgsExpressionContext context4;
result = expression.evaluate( &context4 ).toMap();
QVERIFY( expression.hasEvalError() );

context4.setFeature( f2 );
QgsProject::instance()->removeMapLayer( layer.id() );
result = expression.evaluate( &context4 ).toMap();
QVERIFY( expression.hasEvalError() );

};

void represent_value()
Expand Down

0 comments on commit ae8fc0d

Please sign in to comment.