LCOV - code coverage report
Current view: top level - core/fieldformatter - qgsvaluerelationfieldformatter.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 6 226 2.7 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsvaluerelationfieldformatter.cpp - QgsValueRelationFieldFormatter
       3                 :            : 
       4                 :            :  ---------------------
       5                 :            :  begin                : 3.12.2016
       6                 :            :  copyright            : (C) 2016 by Matthias Kuhn
       7                 :            :  email                : matthias@opengis.ch
       8                 :            :  ***************************************************************************
       9                 :            :  *                                                                         *
      10                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      11                 :            :  *   it under the terms of the GNU General Public License as published by  *
      12                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      13                 :            :  *   (at your option) any later version.                                   *
      14                 :            :  *                                                                         *
      15                 :            :  ***************************************************************************/
      16                 :            : #include "qgsvaluerelationfieldformatter.h"
      17                 :            : 
      18                 :            : #include "qgis.h"
      19                 :            : #include "qgsproject.h"
      20                 :            : #include "qgsvectorlayer.h"
      21                 :            : #include "qgsexpressionnodeimpl.h"
      22                 :            : #include "qgsapplication.h"
      23                 :            : #include "qgsexpressioncontextutils.h"
      24                 :            : #include "qgsvectorlayerref.h"
      25                 :            : #include "qgspostgresstringutils.h"
      26                 :            : #include "qgsmessagelog.h"
      27                 :            : 
      28                 :            : #include <nlohmann/json.hpp>
      29                 :            : using namespace nlohmann;
      30                 :            : 
      31                 :            : #include <QSettings>
      32                 :            : 
      33                 :          0 : bool orderByKeyLessThan( const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2 )
      34                 :            : {
      35                 :          0 :   return qgsVariantLessThan( p1.key, p2.key );
      36                 :            : }
      37                 :            : 
      38                 :          0 : bool orderByValueLessThan( const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2 )
      39                 :            : {
      40                 :          0 :   return qgsVariantLessThan( p1.value, p2.value );
      41                 :          0 : }
      42                 :            : 
      43                 :          5 : QgsValueRelationFieldFormatter::QgsValueRelationFieldFormatter()
      44                 :         10 : {
      45                 :          5 :   setFlags( flags() | QgsFieldFormatter::CanProvideAvailableValues );
      46                 :          5 : }
      47                 :            : 
      48                 :          5 : QString QgsValueRelationFieldFormatter::id() const
      49                 :            : {
      50                 :         10 :   return QStringLiteral( "ValueRelation" );
      51                 :            : }
      52                 :            : 
      53                 :          0 : QString QgsValueRelationFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
      54                 :            : {
      55                 :          0 :   ValueRelationCache vrCache;
      56                 :            : 
      57                 :          0 :   if ( cache.isValid() )
      58                 :            :   {
      59                 :          0 :     vrCache = cache.value<QgsValueRelationFieldFormatter::ValueRelationCache>();
      60                 :          0 :   }
      61                 :            :   else
      62                 :            :   {
      63                 :          0 :     vrCache = QgsValueRelationFieldFormatter::createCache( config );
      64                 :            :   }
      65                 :            : 
      66                 :          0 :   if ( config.value( QStringLiteral( "AllowMulti" ) ).toBool() )
      67                 :            :   {
      68                 :          0 :     QStringList keyList;
      69                 :            : 
      70                 :          0 :     if ( layer->fields().at( fieldIndex ).type() == QVariant::Map )
      71                 :            :     {
      72                 :            :       //because of json it's stored as QVariantList
      73                 :          0 :       keyList = value.toStringList();
      74                 :          0 :     }
      75                 :            :     else
      76                 :            :     {
      77                 :          0 :       keyList = valueToStringList( value );
      78                 :            :     }
      79                 :            : 
      80                 :          0 :     QStringList valueList;
      81                 :            : 
      82                 :          0 :     for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : std::as_const( vrCache ) )
      83                 :            :     {
      84                 :          0 :       if ( keyList.contains( item.key.toString() ) )
      85                 :            :       {
      86                 :          0 :         valueList << item.value;
      87                 :          0 :       }
      88                 :            :     }
      89                 :            : 
      90                 :          0 :     return valueList.join( QLatin1String( ", " ) ).prepend( '{' ).append( '}' );
      91                 :          0 :   }
      92                 :            :   else
      93                 :            :   {
      94                 :          0 :     if ( value.isNull() )
      95                 :            :     {
      96                 :          0 :       return QgsApplication::nullRepresentation();
      97                 :            :     }
      98                 :            : 
      99                 :          0 :     for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : std::as_const( vrCache ) )
     100                 :            :     {
     101                 :          0 :       if ( item.key == value )
     102                 :            :       {
     103                 :          0 :         return item.value;
     104                 :            :       }
     105                 :            :     }
     106                 :            :   }
     107                 :            : 
     108                 :          0 :   return QStringLiteral( "(%1)" ).arg( value.toString() );
     109                 :          0 : }
     110                 :            : 
     111                 :          0 : QVariant QgsValueRelationFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
     112                 :            : {
     113                 :          0 :   return value.isNull() ? QString() : representValue( layer, fieldIndex, config, cache, value );
     114                 :          0 : }
     115                 :            : 
     116                 :          0 : QVariant QgsValueRelationFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
     117                 :            : {
     118                 :            :   Q_UNUSED( layer )
     119                 :            :   Q_UNUSED( fieldIndex )
     120                 :          0 :   return QVariant::fromValue<ValueRelationCache>( createCache( config ) );
     121                 :            : 
     122                 :          0 : }
     123                 :            : 
     124                 :          0 : QgsValueRelationFieldFormatter::ValueRelationCache QgsValueRelationFieldFormatter::createCache(
     125                 :            :   const QVariantMap &config,
     126                 :            :   const QgsFeature &formFeature,
     127                 :            :   const QgsFeature &parentFormFeature )
     128                 :            : {
     129                 :          0 :   ValueRelationCache cache;
     130                 :            : 
     131                 :          0 :   const QgsVectorLayer *layer = resolveLayer( config, QgsProject::instance() );
     132                 :            : 
     133                 :          0 :   if ( !layer )
     134                 :          0 :     return cache;
     135                 :            : 
     136                 :          0 :   QgsFields fields = layer->fields();
     137                 :          0 :   int ki = fields.indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
     138                 :          0 :   int vi = fields.indexOf( config.value( QStringLiteral( "Value" ) ).toString() );
     139                 :            : 
     140                 :          0 :   QgsFeatureRequest request;
     141                 :            : 
     142                 :          0 :   request.setFlags( QgsFeatureRequest::NoGeometry );
     143                 :          0 :   QgsAttributeIds subsetOfAttributes { ki, vi };
     144                 :            : 
     145                 :          0 :   const QString descriptionExpressionString = config.value( "Description" ).toString();
     146                 :          0 :   QgsExpression descriptionExpression( descriptionExpressionString );
     147                 :          0 :   QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
     148                 :          0 :   descriptionExpression.prepare( &context );
     149                 :          0 :   subsetOfAttributes += descriptionExpression.referencedAttributeIndexes( layer->fields() );
     150                 :          0 :   request.setSubsetOfAttributes( qgis::setToList( subsetOfAttributes ) );
     151                 :            : 
     152                 :          0 :   const QString filterExpression = config.value( QStringLiteral( "FilterExpression" ) ).toString();
     153                 :            : 
     154                 :            :   // Skip the filter and build a full cache if the form scope is required and the feature
     155                 :            :   // is not valid or the attributes required for the filter have no valid value
     156                 :            :   // Note: parent form scope is not checked for usability because it's supposed to
     157                 :            :   //       be used into a coalesce that retrieve the current value of the parent
     158                 :            :   //       from the parent layer when used outside of an embedded form
     159                 :          0 :   if ( ! filterExpression.isEmpty() && ( !( expressionRequiresFormScope( filterExpression ) )
     160                 :          0 :                                          || expressionIsUsable( filterExpression, formFeature ) ) )
     161                 :            :   {
     162                 :          0 :     QgsExpressionContext filterContext = context;
     163                 :          0 :     if ( formFeature.isValid( ) && QgsValueRelationFieldFormatter::expressionRequiresFormScope( filterExpression ) )
     164                 :          0 :       filterContext.appendScope( QgsExpressionContextUtils::formScope( formFeature ) );
     165                 :          0 :     if ( parentFormFeature.isValid() && QgsValueRelationFieldFormatter::expressionRequiresParentFormScope( filterExpression ) )
     166                 :          0 :       filterContext.appendScope( QgsExpressionContextUtils::parentFormScope( parentFormFeature ) );
     167                 :          0 :     request.setExpressionContext( filterContext );
     168                 :          0 :     request.setFilterExpression( filterExpression );
     169                 :          0 :   }
     170                 :            : 
     171                 :          0 :   QgsFeatureIterator fit = layer->getFeatures( request );
     172                 :            : 
     173                 :          0 :   QgsFeature f;
     174                 :          0 :   while ( fit.nextFeature( f ) )
     175                 :            :   {
     176                 :          0 :     QString description;
     177                 :          0 :     if ( descriptionExpression.isValid() )
     178                 :            :     {
     179                 :          0 :       context.setFeature( f );
     180                 :          0 :       description = descriptionExpression.evaluate( &context ).toString();
     181                 :          0 :     }
     182                 :          0 :     cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString(), description ) );
     183                 :          0 :   }
     184                 :            : 
     185                 :          0 :   if ( config.value( QStringLiteral( "OrderByValue" ) ).toBool() )
     186                 :            :   {
     187                 :          0 :     std::sort( cache.begin(), cache.end(), orderByValueLessThan );
     188                 :          0 :   }
     189                 :            :   else
     190                 :            :   {
     191                 :          0 :     std::sort( cache.begin(), cache.end(), orderByKeyLessThan );
     192                 :            :   }
     193                 :            : 
     194                 :          0 :   return cache;
     195                 :          0 : }
     196                 :            : 
     197                 :            : 
     198                 :          0 : QList<QgsVectorLayerRef> QgsValueRelationFieldFormatter::layerDependencies( const QVariantMap &config ) const
     199                 :            : {
     200                 :          0 :   QList<QgsVectorLayerRef> result;
     201                 :          0 :   const QString layerId { config.value( QStringLiteral( "Layer" ) ).toString() };
     202                 :          0 :   const QString layerName { config.value( QStringLiteral( "LayerName" ) ).toString() };
     203                 :          0 :   const QString providerName { config.value( QStringLiteral( "LayerProviderName" ) ).toString() };
     204                 :          0 :   const QString layerSource { config.value( QStringLiteral( "LayerSource" ) ).toString() };
     205                 :          0 :   if ( ! layerId.isEmpty() && ! layerName.isEmpty() && ! providerName.isEmpty() && ! layerSource.isEmpty() )
     206                 :            :   {
     207                 :          0 :     result.append( QgsVectorLayerRef( layerId, layerName, layerSource, providerName ) );
     208                 :          0 :   }
     209                 :          0 :   return result;
     210                 :          0 : }
     211                 :            : 
     212                 :          0 : QVariantList QgsValueRelationFieldFormatter::availableValues( const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context ) const
     213                 :            : {
     214                 :          0 :   QVariantList values;
     215                 :            : 
     216                 :          0 :   if ( auto *lProject = context.project() )
     217                 :            :   {
     218                 :          0 :     const QgsVectorLayer *referencedLayer = qobject_cast<QgsVectorLayer *>( lProject->mapLayer( config[QStringLiteral( "Layer" )].toString() ) );
     219                 :          0 :     if ( referencedLayer )
     220                 :            :     {
     221                 :          0 :       int fieldIndex = referencedLayer->fields().indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
     222                 :          0 :       values = qgis::setToList( referencedLayer->uniqueValues( fieldIndex, countLimit ) );
     223                 :          0 :     }
     224                 :          0 :   }
     225                 :          0 :   return values;
     226                 :          0 : }
     227                 :            : 
     228                 :          0 : QStringList QgsValueRelationFieldFormatter::valueToStringList( const QVariant &value )
     229                 :            : {
     230                 :          0 :   QStringList checkList;
     231                 :          0 :   if ( value.type() == QVariant::StringList )
     232                 :            :   {
     233                 :          0 :     checkList = value.toStringList();
     234                 :          0 :   }
     235                 :            :   else
     236                 :            :   {
     237                 :          0 :     QVariantList valuesList;
     238                 :          0 :     if ( value.type() == QVariant::String )
     239                 :            :     {
     240                 :            :       // This must be an array representation
     241                 :          0 :       auto newVal { value };
     242                 :          0 :       if ( newVal.toString().trimmed().startsWith( '{' ) )
     243                 :            :       {
     244                 :            :         //normal case
     245                 :          0 :         valuesList = QgsPostgresStringUtils::parseArray( newVal.toString() );
     246                 :          0 :       }
     247                 :          0 :       else if ( newVal.toString().trimmed().startsWith( '[' ) )
     248                 :            :       {
     249                 :            :         //fallback, in case it's a json array
     250                 :            :         try
     251                 :            :         {
     252                 :          0 :           for ( auto &element : json::parse( newVal.toString().toStdString() ) )
     253                 :            :           {
     254                 :          0 :             if ( element.is_number_integer() )
     255                 :            :             {
     256                 :          0 :               valuesList.push_back( element.get<int>() );
     257                 :          0 :             }
     258                 :          0 :             else if ( element.is_number_unsigned() )
     259                 :            :             {
     260                 :          0 :               valuesList.push_back( element.get<unsigned>() );
     261                 :          0 :             }
     262                 :          0 :             else if ( element.is_string() )
     263                 :            :             {
     264                 :          0 :               valuesList.push_back( QString::fromStdString( element.get<std::string>() ) );
     265                 :          0 :             }
     266                 :            :           }
     267                 :          0 :         }
     268                 :            :         catch ( json::parse_error &ex )
     269                 :            :         {
     270                 :          0 :           QgsMessageLog::logMessage( QObject::tr( "Cannot parse JSON like string '%1' Error: %2" ).arg( newVal.toString(), ex.what() ) );
     271                 :          0 :         }
     272                 :          0 :       }
     273                 :          0 :     }
     274                 :          0 :     else if ( value.type() == QVariant::List )
     275                 :            :     {
     276                 :          0 :       valuesList = value.toList( );
     277                 :          0 :     }
     278                 :            : 
     279                 :          0 :     checkList.reserve( valuesList.size() );
     280                 :          0 :     for ( const QVariant &listItem : std::as_const( valuesList ) )
     281                 :            :     {
     282                 :          0 :       QString v( listItem.toString( ) );
     283                 :          0 :       if ( ! v.isEmpty() )
     284                 :          0 :         checkList.append( v );
     285                 :          0 :     }
     286                 :          0 :   }
     287                 :          0 :   return checkList;
     288                 :          0 : }
     289                 :            : 
     290                 :            : 
     291                 :          0 : QSet<QString> QgsValueRelationFieldFormatter::expressionFormVariables( const QString &expression )
     292                 :            : {
     293                 :          0 :   std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
     294                 :          0 :   QSet< QString > formVariables = qgis::listToSet( scope->variableNames() );
     295                 :          0 :   const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables();
     296                 :          0 :   formVariables.intersect( usedVariables );
     297                 :          0 :   return formVariables;
     298                 :          0 : }
     299                 :            : 
     300                 :          0 : QSet<QString> QgsValueRelationFieldFormatter::expressionParentFormVariables( const QString &expression )
     301                 :            : {
     302                 :          0 :   std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::parentFormScope() );
     303                 :          0 :   QSet< QString > formVariables = qgis::listToSet( scope->variableNames() );
     304                 :          0 :   const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables();
     305                 :          0 :   formVariables.intersect( usedVariables );
     306                 :          0 :   return formVariables;
     307                 :          0 : }
     308                 :            : 
     309                 :          0 : bool QgsValueRelationFieldFormatter::expressionRequiresFormScope( const QString &expression )
     310                 :            : {
     311                 :          0 :   return !( expressionFormAttributes( expression ).isEmpty() && expressionFormVariables( expression ).isEmpty() );
     312                 :          0 : }
     313                 :            : 
     314                 :          0 : bool QgsValueRelationFieldFormatter::expressionRequiresParentFormScope( const QString &expression )
     315                 :            : {
     316                 :          0 :   return !( expressionParentFormAttributes( expression ).isEmpty() && expressionParentFormVariables( expression ).isEmpty() );
     317                 :          0 : }
     318                 :            : 
     319                 :          0 : QSet<QString> QgsValueRelationFieldFormatter::expressionParentFormAttributes( const QString &expression )
     320                 :            : {
     321                 :          0 :   QSet<QString> attributes;
     322                 :          0 :   QgsExpression exp( expression );
     323                 :          0 :   std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::parentFormScope() );
     324                 :            :   // List of form function names used in the expression
     325                 :          0 :   const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() )
     326                 :          0 :                                      .intersect( exp.referencedFunctions( ) ) );
     327                 :          0 :   const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() );
     328                 :          0 :   QgsExpressionContext context;
     329                 :          0 :   for ( const auto &f : expFunctions )
     330                 :            :   {
     331                 :          0 :     QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()];
     332                 :          0 :     if ( formFunctions.contains( fd->name( ) ) )
     333                 :            :     {
     334                 :          0 :       for ( const auto &param : f->args( )->list() )
     335                 :            :       {
     336                 :          0 :         attributes.insert( param->eval( &exp, &context ).toString() );
     337                 :            :       }
     338                 :          0 :     }
     339                 :            :   }
     340                 :          0 :   return attributes;
     341                 :          0 : }
     342                 :            : 
     343                 :          0 : QSet<QString> QgsValueRelationFieldFormatter::expressionFormAttributes( const QString &expression )
     344                 :            : {
     345                 :          0 :   QSet<QString> attributes;
     346                 :          0 :   QgsExpression exp( expression );
     347                 :          0 :   std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
     348                 :            :   // List of form function names used in the expression
     349                 :          0 :   const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() )
     350                 :          0 :                                      .intersect( exp.referencedFunctions( ) ) );
     351                 :          0 :   const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() );
     352                 :          0 :   QgsExpressionContext context;
     353                 :          0 :   for ( const auto &f : expFunctions )
     354                 :            :   {
     355                 :          0 :     QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()];
     356                 :          0 :     if ( formFunctions.contains( fd->name( ) ) )
     357                 :            :     {
     358                 :          0 :       for ( const auto &param : f->args( )->list() )
     359                 :            :       {
     360                 :          0 :         attributes.insert( param->eval( &exp, &context ).toString() );
     361                 :            :       }
     362                 :          0 :     }
     363                 :            :   }
     364                 :          0 :   return attributes;
     365                 :          0 : }
     366                 :            : 
     367                 :          0 : bool QgsValueRelationFieldFormatter::expressionIsUsable( const QString &expression,
     368                 :            :     const QgsFeature &feature,
     369                 :            :     const QgsFeature &parentFeature )
     370                 :            : {
     371                 :          0 :   const QSet<QString> attrs = expressionFormAttributes( expression );
     372                 :          0 :   for ( auto it = attrs.constBegin() ; it != attrs.constEnd(); it++ )
     373                 :            :   {
     374                 :          0 :     if ( ! feature.attribute( *it ).isValid() )
     375                 :          0 :       return false;
     376                 :          0 :   }
     377                 :            : 
     378                 :          0 :   if ( ! expressionFormVariables( expression ).isEmpty() && feature.geometry().isEmpty( ) )
     379                 :          0 :     return false;
     380                 :            : 
     381                 :          0 :   if ( parentFeature.isValid() )
     382                 :            :   {
     383                 :          0 :     const QSet<QString> parentAttrs = expressionParentFormAttributes( expression );
     384                 :          0 :     for ( auto it = parentAttrs.constBegin() ; it != parentAttrs.constEnd(); it++ )
     385                 :            :     {
     386                 :          0 :       if ( ! parentFeature.attribute( *it ).isValid() )
     387                 :          0 :         return false;
     388                 :          0 :     }
     389                 :          0 :     if ( ! expressionParentFormVariables( expression ).isEmpty() && parentFeature.geometry().isEmpty( ) )
     390                 :          0 :       return false;
     391                 :          0 :   }
     392                 :          0 :   return true;
     393                 :          0 : }
     394                 :            : 
     395                 :          0 : QgsVectorLayer *QgsValueRelationFieldFormatter::resolveLayer( const QVariantMap &config, const QgsProject *project )
     396                 :            : {
     397                 :          0 :   QgsVectorLayerRef ref { config.value( QStringLiteral( "Layer" ) ).toString(),
     398                 :          0 :                           config.value( QStringLiteral( "LayerName" ) ).toString(),
     399                 :          0 :                           config.value( QStringLiteral( "LayerSource" ) ).toString(),
     400                 :          0 :                           config.value( QStringLiteral( "LayerProviderName" ) ).toString() };
     401                 :          0 :   return ref.resolveByIdOrNameOnly( project );
     402                 :          0 : }

Generated by: LCOV version 1.14