Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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 14, 2020
1 parent 71a90aa commit 513966c
Show file tree
Hide file tree
Showing 6 changed files with 507 additions and 1 deletion.
107 changes: 107 additions & 0 deletions python/core/auto_generated/processing/qgsprocessingparameters.sip.in
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

};





Expand Down
168 changes: 167 additions & 1 deletion src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -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 )
Expand All @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Copy link
Contributor

@havatv havatv commented on 513966c Mar 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.