[processing] When converting models to Python, use friendlier (more

descriptive) names when storing child algorithm results

Makes for much easier to understand scripts
This commit is contained in:
Nyall Dawson 2019-02-14 12:12:12 +10:00
parent 3ceea2a050
commit d342ce939b
8 changed files with 79 additions and 38 deletions

View File

@ -298,7 +298,7 @@ Loads this child from a QVariant.
.. seealso:: :py:func:`toVariant`
%End
QStringList asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters, int currentIndent, int indentSize ) const;
QStringList asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters, int currentIndent, int indentSize, const QMap<QString, QString> &friendlyChildNames ) const;
%Docstring
Attempts to convert the child to executable Python code, and returns a list of the generated lines of code.
@ -307,6 +307,8 @@ The ``outputType`` argument specifies the type of script to generate.
Additional parameters to be passed to the child algorithm are specified in the ``extraParameters`` argument.
The ``currentIndent`` and ``indentSize`` are used to set the base line indent and size of further indented lines respectively.
The ``friendlyChildNames`` argument gives a map of child id to a friendly algorithm name, to be used in the code to identify that algorithm instead of the raw child id.
%End
};

View File

@ -240,9 +240,11 @@ Loads this source from a QVariantMap.
.. seealso:: :py:func:`toVariant`
%End
QString asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsProcessingParameterDefinition *definition ) const;
QString asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlydChildNames ) const;
%Docstring
Attempts to convert the source to executable Python code.
The ``friendlyChildNames`` argument gives a map of child id to a friendly algorithm name, to be used in the code to identify that algorithm instead of the raw child id.
%End
};

View File

@ -361,23 +361,42 @@ QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::Pyth
QString indent = QString( ' ' ).repeated( indentSize );
QString currentIndent;
auto safeName = []( const QString & name )->QString
QMap< QString, QString> friendlyChildNames;
auto safeName = []( const QString & name, bool capitalize )->QString
{
QString n = name.toLower().trimmed();
QRegularExpression rx( QStringLiteral( "[^\\sa-z_A-Z0-9]" ) );
n.replace( rx, QString() );
QRegularExpression rx2( QStringLiteral( "^\\d*" ) ); // name can't start in a digit
n.replace( rx2, QString() );
return QgsStringUtils::capitalize( n, QgsStringUtils::UpperCamelCase );
if ( !capitalize )
n = n.replace( ' ', '_' );
return capitalize ? QgsStringUtils::capitalize( n, QgsStringUtils::UpperCamelCase ) : n;
};
const QString algorithmClassName = safeName( name() );
auto uniqueSafeName = [&friendlyChildNames, &safeName ]( const QString & name, bool capitalize )->QString
{
const QString base = safeName( name, capitalize );
QString candidate = base;
int i = 1;
while ( friendlyChildNames.contains( candidate ) )
{
i++;
candidate = QStringLiteral( "%1_%2" ).arg( base ).arg( i );
}
return candidate;
};
const QString algorithmClassName = safeName( name(), true );
QSet< QString > toExecute;
for ( auto childIt = mChildAlgorithms.constBegin(); childIt != mChildAlgorithms.constEnd(); ++childIt )
{
if ( childIt->isActive() && childIt->algorithm() )
{
toExecute.insert( childIt->childId() );
friendlyChildNames.insert( childIt->childId(), uniqueSafeName( childIt->description().isEmpty() ? childIt->childId() : childIt->description(), !childIt->description().isEmpty() ) );
}
}
const int totalSteps = toExecute.count();
@ -462,6 +481,7 @@ QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::Pyth
lines << currentIndent + QStringLiteral( "results = {}" );
lines << currentIndent + QStringLiteral( "outputs = {}" );
lines << QString();
QSet< QString > executed;
bool executedAlg = true;
@ -536,7 +556,7 @@ QStringList QgsProcessingModelAlgorithm::asPythonCode( const QgsProcessing::Pyth
}
}
lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize );
lines << child.asPythonCode( outputType, childParams, currentIndent.size(), indentSize, friendlyChildNames );
currentStep++;
if ( currentStep < totalSteps )
{

View File

@ -162,7 +162,7 @@ bool QgsProcessingModelChildAlgorithm::loadVariant( const QVariant &child )
return true;
}
QStringList QgsProcessingModelChildAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters, int currentIndent, int indentSize ) const
QStringList QgsProcessingModelChildAlgorithm::asPythonCode( const QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters, int currentIndent, int indentSize, const QMap<QString, QString> &friendlyChildNames ) const
{
QStringList lines;
const QString baseIndent = QString( ' ' ).repeated( currentIndent );
@ -181,7 +181,7 @@ QStringList QgsProcessingModelChildAlgorithm::asPythonCode( const QgsProcessing:
const auto parts = paramIt.value();
for ( const QgsProcessingModelChildParameterSource &source : parts )
{
QString part = source.asPythonCode( outputType, def );
QString part = source.asPythonCode( outputType, def, friendlyChildNames );
if ( !part.isEmpty() )
sourceParts << part;
}
@ -207,11 +207,11 @@ QStringList QgsProcessingModelChildAlgorithm::asPythonCode( const QgsProcessing:
}
lines << baseIndent + QStringLiteral( "}" );
lines << baseIndent + QStringLiteral( "outputs['%1'] = processing.run('%2', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ).arg( mId, mAlgorithmId );
lines << baseIndent + QStringLiteral( "outputs['%1'] = processing.run('%2', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ).arg( friendlyChildNames.value( mId, mId ), mAlgorithmId );
for ( auto outputIt = mModelOutputs.constBegin(); outputIt != mModelOutputs.constEnd(); ++outputIt )
{
lines << baseIndent + QStringLiteral( "results['%1:%2'] = outputs['%1']['%3']" ).arg( mId, outputIt.key(), outputIt.value().childOutputName() );
lines << baseIndent + QStringLiteral( "results['%1:%2'] = outputs['%3']['%4']" ).arg( mId, outputIt.key(), friendlyChildNames.value( mId, mId ), outputIt.value().childOutputName() );
}
return lines;

View File

@ -285,8 +285,10 @@ class CORE_EXPORT QgsProcessingModelChildAlgorithm : public QgsProcessingModelCo
* Additional parameters to be passed to the child algorithm are specified in the \a extraParameters argument.
*
* The \a currentIndent and \a indentSize are used to set the base line indent and size of further indented lines respectively.
*
* The \a friendlyChildNames argument gives a map of child id to a friendly algorithm name, to be used in the code to identify that algorithm instead of the raw child id.
*/
QStringList asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters, int currentIndent, int indentSize ) const;
QStringList asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsStringMap &extraParameters, int currentIndent, int indentSize, const QMap<QString, QString> &friendlyChildNames ) const;
private:

View File

@ -147,7 +147,7 @@ bool QgsProcessingModelChildParameterSource::loadVariant( const QVariantMap &map
return true;
}
QString QgsProcessingModelChildParameterSource::asPythonCode( const QgsProcessing::PythonOutputType, const QgsProcessingParameterDefinition *definition ) const
QString QgsProcessingModelChildParameterSource::asPythonCode( const QgsProcessing::PythonOutputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlydChildNames ) const
{
switch ( mSource )
{
@ -155,7 +155,7 @@ QString QgsProcessingModelChildParameterSource::asPythonCode( const QgsProcessin
return QStringLiteral( "parameters['%1']" ).arg( mParameterName );
case ChildOutput:
return QStringLiteral( "outputs['%1']['%2']" ).arg( mChildId, mOutputName );
return QStringLiteral( "outputs['%1']['%2']" ).arg( friendlydChildNames.value( mChildId, mChildId ), mOutputName );
case StaticValue:
if ( definition )

View File

@ -215,8 +215,10 @@ class CORE_EXPORT QgsProcessingModelChildParameterSource
/**
* Attempts to convert the source to executable Python code.
*
* The \a friendlyChildNames argument gives a map of child id to a friendly algorithm name, to be used in the code to identify that algorithm instead of the raw child id.
*/
QString asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsProcessingParameterDefinition *definition ) const;
QString asPythonCode( QgsProcessing::PythonOutputType outputType, const QgsProcessingParameterDefinition *definition, const QMap< QString, QString > &friendlydChildNames ) const;
private:

View File

@ -6081,56 +6081,60 @@ void TestQgsProcessing::modelerAlgorithm()
QCOMPARE( svSource.staticValue().toInt(), 5 );
svSource.setStaticValue( 7 );
QCOMPARE( svSource.staticValue().toInt(), 7 );
QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "7" ) );
QMap< QString, QString > friendlyNames;
QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "7" ) );
svSource = QgsProcessingModelChildParameterSource::fromModelParameter( "a" );
// check that calling setStaticValue flips source to StaticValue
QCOMPARE( svSource.source(), QgsProcessingModelChildParameterSource::ModelParameter );
QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "parameters['a']" ) );
QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "parameters['a']" ) );
svSource.setStaticValue( 7 );
QCOMPARE( svSource.staticValue().toInt(), 7 );
QCOMPARE( svSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "7" ) );
QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "7" ) );
// model parameter source
QgsProcessingModelChildParameterSource mpSource = QgsProcessingModelChildParameterSource::fromModelParameter( "a" );
QCOMPARE( mpSource.source(), QgsProcessingModelChildParameterSource::ModelParameter );
QCOMPARE( mpSource.parameterName(), QStringLiteral( "a" ) );
QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "parameters['a']" ) );
QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "parameters['a']" ) );
mpSource.setParameterName( "b" );
QCOMPARE( mpSource.parameterName(), QStringLiteral( "b" ) );
QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "parameters['b']" ) );
QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "parameters['b']" ) );
mpSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
// check that calling setParameterName flips source to ModelParameter
QCOMPARE( mpSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "5" ) );
QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "5" ) );
mpSource.setParameterName( "c" );
QCOMPARE( mpSource.parameterName(), QStringLiteral( "c" ) );
QCOMPARE( mpSource.source(), QgsProcessingModelChildParameterSource::ModelParameter );
QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "parameters['c']" ) );
QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "parameters['c']" ) );
// child alg output source
QgsProcessingModelChildParameterSource oSource = QgsProcessingModelChildParameterSource::fromChildOutput( "a", "b" );
QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::ChildOutput );
QCOMPARE( oSource.outputChildId(), QStringLiteral( "a" ) );
QCOMPARE( oSource.outputName(), QStringLiteral( "b" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "outputs['a']['b']" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "outputs['a']['b']" ) );
// with friendly name
friendlyNames.insert( QStringLiteral( "a" ), QStringLiteral( "alga" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "outputs['alga']['b']" ) );
oSource.setOutputChildId( "c" );
QCOMPARE( oSource.outputChildId(), QStringLiteral( "c" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "outputs['c']['b']" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "outputs['c']['b']" ) );
oSource.setOutputName( "d" );
QCOMPARE( oSource.outputName(), QStringLiteral( "d" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "outputs['c']['d']" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "outputs['c']['d']" ) );
oSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
// check that calling setOutputChildId flips source to ChildOutput
QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "5" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "5" ) );
oSource.setOutputChildId( "c" );
QCOMPARE( oSource.outputChildId(), QStringLiteral( "c" ) );
QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::ChildOutput );
oSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
// check that calling setOutputName flips source to ChildOutput
QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "5" ) );
QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "5" ) );
oSource.setOutputName( "d" );
QCOMPARE( oSource.outputName(), QStringLiteral( "d" ) );
QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::ChildOutput );
@ -6139,18 +6143,18 @@ void TestQgsProcessing::modelerAlgorithm()
QgsProcessingModelChildParameterSource expSource = QgsProcessingModelChildParameterSource::fromExpression( "1+2" );
QCOMPARE( expSource.source(), QgsProcessingModelChildParameterSource::Expression );
QCOMPARE( expSource.expression(), QStringLiteral( "1+2" ) );
QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "QgsExpression('1+2').evaluate()" ) );
QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "QgsExpression('1+2').evaluate()" ) );
expSource.setExpression( "1+3" );
QCOMPARE( expSource.expression(), QStringLiteral( "1+3" ) );
QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "QgsExpression('1+3').evaluate()" ) );
QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "QgsExpression('1+3').evaluate()" ) );
expSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
// check that calling setExpression flips source to Expression
QCOMPARE( expSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "5" ) );
QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "5" ) );
expSource.setExpression( "1+4" );
QCOMPARE( expSource.expression(), QStringLiteral( "1+4" ) );
QCOMPARE( expSource.source(), QgsProcessingModelChildParameterSource::Expression );
QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr ), QStringLiteral( "QgsExpression('1+4').evaluate()" ) );
QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "QgsExpression('1+4').evaluate()" ) );
// source equality operator
QVERIFY( QgsProcessingModelChildParameterSource::fromStaticValue( 5 ) ==
@ -6187,11 +6191,11 @@ void TestQgsProcessing::modelerAlgorithm()
QVERIFY( child.setAlgorithmId( QStringLiteral( "native:centroids" ) ) );
QVERIFY( child.algorithm() );
QCOMPARE( child.algorithm()->id(), QStringLiteral( "native:centroids" ) );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, QgsStringMap(), 4, 2 ).join( '\n' ), QStringLiteral( " alg_params = {\n }\n outputs[''] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, QgsStringMap(), 4, 2, friendlyNames ).join( '\n' ), QStringLiteral( " alg_params = {\n }\n outputs[''] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
QgsStringMap extraParams;
extraParams[QStringLiteral( "SOMETHING" )] = QStringLiteral( "SOMETHING_ELSE" );
extraParams[QStringLiteral( "SOMETHING2" )] = QStringLiteral( "SOMETHING_ELSE2" );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2 ).join( '\n' ), QStringLiteral( " alg_params = {\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs[''] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames ).join( '\n' ), QStringLiteral( " alg_params = {\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs[''] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
// bit of a hack -- but try to simulate an algorithm not originally available!
child.mAlgorithm.reset();
QVERIFY( !child.algorithm() );
@ -6234,7 +6238,7 @@ void TestQgsProcessing::modelerAlgorithm()
QCOMPARE( child.parameterSources().value( QStringLiteral( "b" ) ).at( 0 ).staticValue().toInt(), 7 );
QCOMPARE( child.parameterSources().value( QStringLiteral( "b" ) ).at( 1 ).staticValue().toInt(), 9 );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2 ).join( '\n' ), QStringLiteral( " # desc\n alg_params = {\n 'a': 5,\n 'b': [7,9],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames ).join( '\n' ), QStringLiteral( " # desc\n alg_params = {\n 'a': 5,\n 'b': [7,9],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
QgsProcessingModelOutput testModelOut;
testModelOut.setChildId( QStringLiteral( "my_id" ) );
@ -6270,7 +6274,13 @@ void TestQgsProcessing::modelerAlgorithm()
QCOMPARE( child.modelOutput( "a" ).description(), QStringLiteral( "my output" ) );
child.modelOutput( "a" ).setDescription( QStringLiteral( "my output 2" ) );
QCOMPARE( child.modelOutput( "a" ).description(), QStringLiteral( "my output 2" ) );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2 ).join( '\n' ), QStringLiteral( " # desc\n alg_params = {\n 'a': 5,\n 'b': [7,9],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n results['my_id:a'] = outputs['my_id']['']" ) );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames ).join( '\n' ), QStringLiteral( " # desc\n alg_params = {\n 'a': 5,\n 'b': [7,9],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n results['my_id:a'] = outputs['my_id']['']" ) );
// ensure friendly name is used if present
child.addParameterSources( QStringLiteral( "b" ), QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "a", "out" ) );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames ).join( '\n' ), QStringLiteral( " # desc\n alg_params = {\n 'a': 5,\n 'b': outputs['alga']['out'],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n results['my_id:a'] = outputs['my_id']['']" ) );
friendlyNames.remove( "a" );
QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames ).join( '\n' ), QStringLiteral( " # desc\n alg_params = {\n 'a': 5,\n 'b': outputs['a']['out'],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n results['my_id:a'] = outputs['my_id']['']" ) );
// no existent
child.modelOutput( "b" ).setDescription( QStringLiteral( "my output 3" ) );
@ -6982,6 +6992,7 @@ void TestQgsProcessing::modelExecution()
QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_maxy" ).value.toDouble(), 46.8719, 0.001 );
model2.setName( QStringLiteral( "2my model" ) );
model2.childAlgorithm( "cx1" ).setDescription( "first step in my model" );
QStringList actualParts = model2.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, 2 );
QgsDebugMsg( actualParts.join( '\n' ) );
QStringList expectedParts = QStringLiteral( "from qgis.core import QgsProcessing\n"
@ -7007,6 +7018,8 @@ void TestQgsProcessing::modelExecution()
" feedback = QgsProcessingMultiStepFeedback(3, model_feedback)\n"
" results = {}\n"
" outputs = {}\n"
"\n"
" # first step in my model\n"
" alg_params = {\n"
" 'DISSOLVE': False,\n"
" 'DISTANCE': parameters['DIST'],\n"
@ -7016,15 +7029,15 @@ void TestQgsProcessing::modelExecution()
" 'SEGMENTS': QgsExpression('@myvar*2').evaluate(),\n"
" 'OUTPUT': parameters['cx1:MODEL_OUT_LAYER']\n"
" }\n"
" outputs['cx1'] = processing.run('native:buffer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n"
" results['cx1:MODEL_OUT_LAYER'] = outputs['cx1']['OUTPUT']\n"
" outputs['FirstStepInMyModel'] = processing.run('native:buffer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n"
" results['cx1:MODEL_OUT_LAYER'] = outputs['FirstStepInMyModel']['OUTPUT']\n"
"\n"
" feedback.setCurrentStep(1)\n"
" if feedback.isCanceled():\n"
" return {}\n"
"\n"
" alg_params = {\n"
" 'INPUT': outputs['cx1']['OUTPUT'],\n"
" 'INPUT': outputs['FirstStepInMyModel']['OUTPUT'],\n"
" 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT\n"
" }\n"
" outputs['cx2'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n"
@ -7035,7 +7048,7 @@ void TestQgsProcessing::modelExecution()
"\n"
" alg_params = {\n"
" 'EXPRESSION': 'true',\n"
" 'INPUT': outputs['cx1']['OUTPUT'],\n"
" 'INPUT': outputs['FirstStepInMyModel']['OUTPUT'],\n"
" 'OUTPUT': parameters['MY_OUT'],\n"
" 'OUTPUT': parameters['cx3:MY_OUT']\n"
" }\n"