Skip to content
Permalink
Browse files

[feature][processing] Add dedicated parameter type for database tables

Allows selection of an existing database table for a specific
database provider connection (the provider must implement the connections API)
  • Loading branch information
nyalldawson committed Mar 13, 2020
1 parent 71a90aa commit 513966ca804df8631dac381731b99792af0e0b16
@@ -201,6 +201,8 @@ their acceptable ranges, defaults, etc.
sipType = sipType_QgsProcessingParameterProviderConnection;
else if ( sipCpp->type() == QgsProcessingParameterDatabaseSchema::typeName() )
sipType = sipType_QgsProcessingParameterDatabaseSchema;
else if ( sipCpp->type() == QgsProcessingParameterDatabaseTable::typeName() )
sipType = sipType_QgsProcessingParameterDatabaseTable;
else
sipType = nullptr;
%End
@@ -1219,6 +1221,20 @@ Evaluates the parameter with matching ``definition`` to a database schema name.
%Docstring
Evaluates the parameter with matching ``definition`` and ``value`` to a database schema name.

.. versionadded:: 3.14
%End

static QString parameterAsDatabaseTableName( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context );
%Docstring
Evaluates the parameter with matching ``definition`` to a database table name.

.. versionadded:: 3.14
%End

static QString parameterAsDatabaseTableName( const QgsProcessingParameterDefinition *definition, const QVariant &value, const QgsProcessingContext &context );
%Docstring
Evaluates the parameter with matching ``definition`` and ``value`` to a database table name.

.. versionadded:: 3.14
%End

@@ -3778,6 +3794,97 @@ Creates a new parameter using the definition from a script code.
};


class QgsProcessingParameterDatabaseTable : QgsProcessingParameterDefinition
{
%Docstring
A database table name parameter for processing algorithms, allowing users to select from existing database tables
on a registered database connection.

QgsProcessingParameterDatabaseTable should be evaluated by calling :py:func:`QgsProcessingAlgorithm.parameterAsDatabaseTableName()`

.. versionadded:: 3.14
%End

%TypeHeaderCode
#include "qgsprocessingparameters.h"
%End
public:

QgsProcessingParameterDatabaseTable( const QString &name, const QString &description,
const QString &connectionParameterName = QString(),
const QString &schemaParameterName = QString(),
const QVariant &defaultValue = QVariant(),
bool optional = false );
%Docstring
Constructor for QgsProcessingParameterDatabaseTable.

The ``connectionParameterName`` specifies the name of the parent QgsProcessingParameterProviderConnection parameter.
The ``schemaParameterName`` specifies the name of the parent QgsProcessingParameterDatabaseSchema parameter.

.. warning::

The provider must support the connection API methods in its QgsProviderMetadata implementation
in order for the model to work correctly. This is only implemented for a subset of current data providers.
%End

static QString typeName();
%Docstring
Returns the type name for the parameter class.
%End
virtual QgsProcessingParameterDefinition *clone() const /Factory/;

virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;

virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;

virtual QString asScriptCode() const;

virtual QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const;

virtual QVariantMap toVariantMap() const;

virtual bool fromVariantMap( const QVariantMap &map );

virtual QStringList dependsOnOtherParameters() const;


QString parentConnectionParameterName() const;
%Docstring
Returns the name of the parent connection parameter, or an empty string if this is not set.

.. seealso:: :py:func:`setParentConnectionParameterName`
%End

void setParentConnectionParameterName( const QString &name );
%Docstring
Sets the ``name`` of the parent connection parameter. Use an empty string if this is not required.

.. seealso:: :py:func:`parentConnectionParameterName`
%End

QString parentSchemaParameterName() const;
%Docstring
Returns the name of the parent schema parameter, or an empty string if this is not set.

.. seealso:: :py:func:`setParentSchemaParameterName`
%End

void setParentSchemaParameterName( const QString &name );
%Docstring
Sets the ``name`` of the parent schema parameter. Use an empty string if this is not required.

.. seealso:: :py:func:`parentSchemaParameterName`
%End

static QgsProcessingParameterDatabaseTable *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) /Factory/;
%Docstring
Creates a new parameter using the definition from a script code.
%End

};





@@ -1794,7 +1794,7 @@ QString QgsProcessingParameters::parameterAsSchema( const QgsProcessingParameter
if ( !definition )
return QString();

return parameterAsConnectionName( definition, parameters.value( definition->name() ), context );
return parameterAsSchema( definition, parameters.value( definition->name() ), context );
}

QString QgsProcessingParameters::parameterAsSchema( const QgsProcessingParameterDefinition *definition, const QVariant &value, const QgsProcessingContext &context )
@@ -1804,6 +1804,21 @@ QString QgsProcessingParameters::parameterAsSchema( const QgsProcessingParameter
return parameterAsString( definition, value, context );
}

QString QgsProcessingParameters::parameterAsDatabaseTableName( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, const QgsProcessingContext &context )
{
if ( !definition )
return QString();

return parameterAsDatabaseTableName( definition, parameters.value( definition->name() ), context );
}

QString QgsProcessingParameters::parameterAsDatabaseTableName( const QgsProcessingParameterDefinition *definition, const QVariant &value, const QgsProcessingContext &context )
{
// for now it's just treated identical to strings, but in future we may want flexibility to amend this (e.g. if we want to embed connection details into the table name
// parameter values, such as via a delimiter separated string)
return parameterAsString( definition, value, context );
}

QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromVariantMap( const QVariantMap &map )
{
QString type = map.value( QStringLiteral( "parameter_type" ) ).toString();
@@ -1974,6 +1989,8 @@ QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromScriptCo
return QgsProcessingParameterProviderConnection::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "databaseschema" ) )
return QgsProcessingParameterDatabaseSchema::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "databasetable" ) )
return QgsProcessingParameterDatabaseTable::fromScriptCode( name, description, isOptional, definition );

return nullptr;
}
@@ -6773,3 +6790,152 @@ QgsProcessingParameterDatabaseSchema *QgsProcessingParameterDatabaseSchema::from

return new QgsProcessingParameterDatabaseSchema( name, description, parent, def.isEmpty() ? QVariant() : def, isOptional );
}

//
// QgsProcessingParameterDatabaseTable
//

QgsProcessingParameterDatabaseTable::QgsProcessingParameterDatabaseTable( const QString &name, const QString &description,
const QString &connectionParameterName,
const QString &schemaParameterName,
const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
, mParentConnectionParameterName( connectionParameterName )
, mParentSchemaParameterName( schemaParameterName )
{

}


QgsProcessingParameterDefinition *QgsProcessingParameterDatabaseTable::clone() const
{
return new QgsProcessingParameterDatabaseTable( *this );
}

bool QgsProcessingParameterDatabaseTable::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const
{
if ( !input.isValid() && !mDefault.isValid() )
return mFlags & FlagOptional;

if ( ( input.type() == QVariant::String && input.toString().isEmpty() )
|| ( !input.isValid() && mDefault.type() == QVariant::String && mDefault.toString().isEmpty() ) )
return mFlags & FlagOptional;

return true;
}

QString QgsProcessingParameterDatabaseTable::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( !value.isValid() )
return QStringLiteral( "None" );

if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );

return QgsProcessingUtils::stringToPythonLiteral( value.toString() );
}

QString QgsProcessingParameterDatabaseTable::asScriptCode() const
{
QString code = QStringLiteral( "##%1=" ).arg( mName );
if ( mFlags & FlagOptional )
code += QStringLiteral( "optional " );
code += QStringLiteral( "databasetable " );

code += ( mParentConnectionParameterName.isEmpty() ? QStringLiteral( "none" ) : mParentConnectionParameterName ) + ' ';
code += ( mParentSchemaParameterName.isEmpty() ? QStringLiteral( "none" ) : mParentSchemaParameterName ) + ' ';

code += mDefault.toString();
return code.trimmed();
}

QString QgsProcessingParameterDatabaseTable::asPythonString( const QgsProcessing::PythonOutputType outputType ) const
{
switch ( outputType )
{
case QgsProcessing::PythonQgsProcessingAlgorithmSubclass:
{
QString code = QStringLiteral( "QgsProcessingParameterDatabaseTable('%1', '%2'" ).arg( name(), description() );
if ( mFlags & FlagOptional )
code += QStringLiteral( ", optional=True" );

code += QStringLiteral( ", connectionParameterName='%1'" ).arg( mParentConnectionParameterName );
code += QStringLiteral( ", schemaParameterName='%1'" ).arg( mParentSchemaParameterName );
QgsProcessingContext c;
code += QStringLiteral( ", defaultValue=%1" ).arg( valueAsPythonString( mDefault, c ) );

code += ')';

return code;
}
}
return QString();
}

QStringList QgsProcessingParameterDatabaseTable::dependsOnOtherParameters() const
{
QStringList depends;
if ( !mParentConnectionParameterName.isEmpty() )
depends << mParentConnectionParameterName;
if ( !mParentSchemaParameterName.isEmpty() )
depends << mParentSchemaParameterName;
return depends;
}

QString QgsProcessingParameterDatabaseTable::parentConnectionParameterName() const
{
return mParentConnectionParameterName;
}

void QgsProcessingParameterDatabaseTable::setParentConnectionParameterName( const QString &name )
{
mParentConnectionParameterName = name;
}

QString QgsProcessingParameterDatabaseTable::parentSchemaParameterName() const
{
return mParentSchemaParameterName;
}

void QgsProcessingParameterDatabaseTable::setParentSchemaParameterName( const QString &name )
{
mParentSchemaParameterName = name;
}

QVariantMap QgsProcessingParameterDatabaseTable::toVariantMap() const
{
QVariantMap map = QgsProcessingParameterDefinition::toVariantMap();
map.insert( QStringLiteral( "mParentConnectionParameterName" ), mParentConnectionParameterName );
map.insert( QStringLiteral( "mParentSchemaParameterName" ), mParentSchemaParameterName );
return map;
}

bool QgsProcessingParameterDatabaseTable::fromVariantMap( const QVariantMap &map )
{
QgsProcessingParameterDefinition::fromVariantMap( map );
mParentConnectionParameterName = map.value( QStringLiteral( "mParentConnectionParameterName" ) ).toString();
mParentSchemaParameterName = map.value( QStringLiteral( "mParentSchemaParameterName" ) ).toString();
return true;
}

QgsProcessingParameterDatabaseTable *QgsProcessingParameterDatabaseTable::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
{
QString connection;
QString schema;
QString def = definition;

QRegularExpression re( QStringLiteral( "(.*?)\\s+(.*+)\\b\\s*(.*)$" ) );
QRegularExpressionMatch m = re.match( def );
if ( m.hasMatch() )
{
connection = m.captured( 1 ).trimmed();
if ( connection == QLatin1String( "none" ) )
connection.clear();
schema = m.captured( 2 ).trimmed();
if ( schema == QLatin1String( "none" ) )
schema.clear();
def = m.captured( 3 );
}

return new QgsProcessingParameterDatabaseTable( name, description, connection, schema, def.isEmpty() ? QVariant() : def, isOptional );
}

1 comment on commit 513966c

@havatv

This comment has been minimized.

Copy link
Contributor

@havatv havatv commented on 513966c Mar 14, 2020

Great addition!
It would be nice to have an ALG decorator for it. Could it be added to python/processing/algfactory.py (and python/plugins/processing/core/parameters.py)?

Please sign in to comment.
You can’t perform that action at this time.