Skip to content

Commit

Permalink
[feature][processing] Add dedicated parameter type for database schemas
Browse files Browse the repository at this point in the history
Allows selection from the existing database schema for a specific
database provider connection (the provider must implement the connections API)
  • Loading branch information
nyalldawson committed Mar 14, 2020
1 parent 465f8a5 commit 93c917f
Show file tree
Hide file tree
Showing 9 changed files with 462 additions and 0 deletions.
Expand Up @@ -874,6 +874,13 @@ Evaluates the parameter with matching ``name`` to a color, or returns an invalid
%Docstring
Evaluates the parameter with matching ``name`` to a connection name string.

.. versionadded:: 3.14
%End

QString parameterAsSchema( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context );
%Docstring
Evaluates the parameter with matching ``name`` to a database schema name string.

.. versionadded:: 3.14
%End

Expand Down
Expand Up @@ -199,6 +199,8 @@ their acceptable ranges, defaults, etc.
sipType = sipType_QgsProcessingParameterDateTime;
else if ( sipCpp->type() == QgsProcessingParameterProviderConnection::typeName() )
sipType = sipType_QgsProcessingParameterProviderConnection;
else if ( sipCpp->type() == QgsProcessingParameterDatabaseSchema::typeName() )
sipType = sipType_QgsProcessingParameterDatabaseSchema;
else
sipType = nullptr;
%End
Expand Down Expand Up @@ -1203,6 +1205,20 @@ Evaluates the parameter with matching ``definition`` to a connection name string
%Docstring
Evaluates the parameter with matching ``definition`` and ``value`` to a connection name string.

.. versionadded:: 3.14
%End

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

.. versionadded:: 3.14
%End

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

.. versionadded:: 3.14
%End

Expand Down Expand Up @@ -3689,6 +3705,79 @@ Creates a new parameter using the definition from a script code.
};


class QgsProcessingParameterDatabaseSchema : QgsProcessingParameterDefinition
{
%Docstring
A database schema parameter for processing algorithms, allowing users to select from existing schemas
on a registered database connection.

QgsProcessingParameterDatabaseSchema should be evaluated by calling :py:func:`QgsProcessingAlgorithm.parameterAsSchema()`

.. versionadded:: 3.14
%End

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

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

The ``connectionParameterName`` specifies the name of the parent QgsProcessingParameterProviderConnection 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

static QgsProcessingParameterDatabaseSchema *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
5 changes: 5 additions & 0 deletions src/core/processing/qgsprocessingalgorithm.cpp
Expand Up @@ -739,6 +739,11 @@ QDateTime QgsProcessingAlgorithm::parameterAsDateTime( const QVariantMap &parame
return QgsProcessingParameters::parameterAsDateTime( parameterDefinition( name ), parameters, context );
}

QString QgsProcessingAlgorithm::parameterAsSchema( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context )
{
return QgsProcessingParameters::parameterAsSchema( parameterDefinition( name ), parameters, context );
}

QString QgsProcessingAlgorithm::invalidSourceError( const QVariantMap &parameters, const QString &name )
{
if ( !parameters.contains( name ) )
Expand Down
7 changes: 7 additions & 0 deletions src/core/processing/qgsprocessingalgorithm.h
Expand Up @@ -864,6 +864,13 @@ class CORE_EXPORT QgsProcessingAlgorithm
*/
QString parameterAsConnectionName( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context );

/**
* Evaluates the parameter with matching \a name to a database schema name string.
*
* \since QGIS 3.14
*/
QString parameterAsSchema( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context );

/**
* Evaluates the parameter with matching \a name to a DateTime, or returns an invalid date time if the parameter was not set.
*
Expand Down
145 changes: 145 additions & 0 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -1789,6 +1789,21 @@ QString QgsProcessingParameters::parameterAsConnectionName( const QgsProcessingP
return parameterAsString( definition, value, context );
}

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

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

QString QgsProcessingParameters::parameterAsSchema( 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 schema
// 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 @@ -1957,6 +1972,8 @@ QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromScriptCo
return QgsProcessingParameterDateTime::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "providerconnection" ) )
return QgsProcessingParameterProviderConnection::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "databaseschema" ) )
return QgsProcessingParameterDatabaseSchema::fromScriptCode( name, description, isOptional, definition );

return nullptr;
}
Expand Down Expand Up @@ -6628,3 +6645,131 @@ QgsProcessingParameterProviderConnection *QgsProcessingParameterProviderConnecti
return new QgsProcessingParameterProviderConnection( name, description, provider, defaultValue, isOptional );
}


//
// QgsProcessingParameterDatabaseSchema
//

QgsProcessingParameterDatabaseSchema::QgsProcessingParameterDatabaseSchema( const QString &name, const QString &description, const QString &parentLayerParameterName, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
, mParentConnectionParameterName( parentLayerParameterName )
{

}


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

bool QgsProcessingParameterDatabaseSchema::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 QgsProcessingParameterDatabaseSchema::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 QgsProcessingParameterDatabaseSchema::asScriptCode() const
{
QString code = QStringLiteral( "##%1=" ).arg( mName );
if ( mFlags & FlagOptional )
code += QStringLiteral( "optional " );
code += QStringLiteral( "databaseschema " );

code += mParentConnectionParameterName + ' ';

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

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

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

code += ')';

return code;
}
}
return QString();
}

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

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

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

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

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

QgsProcessingParameterDatabaseSchema *QgsProcessingParameterDatabaseSchema::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
{
QString parent;
QString def = definition;

QRegularExpression re( QStringLiteral( "(.*?)\\s+(.*)$" ) );
QRegularExpressionMatch m = re.match( def );
if ( m.hasMatch() )
{
parent = m.captured( 1 ).trimmed();
def = m.captured( 2 );
}
else
{
parent = def;
def.clear();
}

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

1 comment on commit 93c917f

@havatv
Copy link
Contributor

@havatv havatv commented on 93c917f 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.