Make log of each separate child algorithm available for retrieval after

running model
This commit is contained in:
Nyall Dawson 2024-04-19 11:15:56 +10:00
parent 172a1c2b03
commit d4369f714d
5 changed files with 181 additions and 112 deletions

View File

@ -67,6 +67,20 @@ Returns the outputs generated by the child algorithm.
Sets the ``outputs`` used for the child algorithm. Sets the ``outputs`` used for the child algorithm.
.. seealso:: :py:func:`outputs` .. seealso:: :py:func:`outputs`
%End
QString htmlLog() const;
%Docstring
Returns the HTML formatted contents of logged messages which occurred while running the child.
.. seealso:: :py:func:`setHtmlLog`
%End
void setHtmlLog( const QString &log );
%Docstring
Sets the HTML formatted contents of logged messages which occurred while running the child.
.. seealso:: :py:func:`htmlLog`
%End %End
bool operator==( const QgsProcessingModelChildAlgorithmResult &other ) const; bool operator==( const QgsProcessingModelChildAlgorithmResult &other ) const;

View File

@ -67,6 +67,20 @@ Returns the outputs generated by the child algorithm.
Sets the ``outputs`` used for the child algorithm. Sets the ``outputs`` used for the child algorithm.
.. seealso:: :py:func:`outputs` .. seealso:: :py:func:`outputs`
%End
QString htmlLog() const;
%Docstring
Returns the HTML formatted contents of logged messages which occurred while running the child.
.. seealso:: :py:func:`setHtmlLog`
%End
void setHtmlLog( const QString &log );
%Docstring
Sets the HTML formatted contents of logged messages which occurred while running the child.
.. seealso:: :py:func:`htmlLog`
%End %End
bool operator==( const QgsProcessingModelChildAlgorithmResult &other ) const; bool operator==( const QgsProcessingModelChildAlgorithmResult &other ) const;

View File

@ -326,6 +326,7 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
QVariantMap finalResults; QVariantMap finalResults;
QSet< QString > executed; QSet< QString > executed;
bool executedAlg = true; bool executedAlg = true;
int previousHtmlLogLength = 0;
while ( executedAlg && executed.count() < toExecute.count() ) while ( executedAlg && executed.count() < toExecute.count() )
{ {
executedAlg = false; executedAlg = false;
@ -503,13 +504,8 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
childResults.insert( childId, results ); childResults.insert( childId, results );
childResult.setOutputs( results ); childResult.setOutputs( results );
context.modelChildResults().insert( childId, childResult ); if ( runResult )
if ( !runResult )
{ {
throw QgsProcessingException( error );
}
if ( feedback && !skipGenericLogging ) if ( feedback && !skipGenericLogging )
{ {
const QVariantMap displayOutputs = QgsProcessingUtils::removePointerValuesFromMap( results ); const QVariantMap displayOutputs = QgsProcessingUtils::removePointerValuesFromMap( results );
@ -620,8 +616,31 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
childAlg.reset( nullptr ); childAlg.reset( nullptr );
modelFeedback.setCurrentStep( executed.count() ); modelFeedback.setCurrentStep( executed.count() );
if ( feedback && !skipGenericLogging ) if ( feedback && !skipGenericLogging )
{
feedback->pushInfo( QObject::tr( "OK. Execution took %1 s (%n output(s)).", nullptr, results.count() ).arg( childTime.elapsed() / 1000.0 ) ); feedback->pushInfo( QObject::tr( "OK. Execution took %1 s (%n output(s)).", nullptr, results.count() ).arg( childTime.elapsed() / 1000.0 ) );
} }
}
// trim out just the portion of the overall log which relates to this child
const QString thisAlgorithmHtmlLog = feedback->htmlLog().mid( previousHtmlLogLength );
previousHtmlLogLength = feedback->htmlLog().length();
if ( !runResult )
{
const QString formattedException = QStringLiteral( "<span style=\"color:red\">%1</span><br/>" ).arg( error.toHtmlEscaped() ).replace( '\n', QLatin1String( "<br>" ) );
const QString formattedRunTime = QStringLiteral( "<span style=\"color:red\">%1</span><br/>" ).arg( QObject::tr( "Failed after %1 s." ).arg( childTime.elapsed() / 1000.0 ).toHtmlEscaped() ).replace( '\n', QLatin1String( "<br>" ) );
childResult.setHtmlLog( thisAlgorithmHtmlLog + formattedException + formattedRunTime );
context.modelChildResults().insert( childId, childResult );
throw QgsProcessingException( error );
}
else
{
childResult.setHtmlLog( thisAlgorithmHtmlLog );
context.modelChildResults().insert( childId, childResult );
}
}
if ( feedback && feedback->isCanceled() ) if ( feedback && feedback->isCanceled() )
break; break;

View File

@ -86,9 +86,24 @@ class CORE_EXPORT QgsProcessingModelChildAlgorithmResult
*/ */
void setOutputs( const QVariantMap &outputs ) { mOutputs = outputs; } void setOutputs( const QVariantMap &outputs ) { mOutputs = outputs; }
/**
* Returns the HTML formatted contents of logged messages which occurred while running the child.
*
* \see setHtmlLog()
*/
QString htmlLog() const { return mHtmlLog; }
/**
* Sets the HTML formatted contents of logged messages which occurred while running the child.
*
* \see htmlLog()
*/
void setHtmlLog( const QString &log ) { mHtmlLog = log; }
bool operator==( const QgsProcessingModelChildAlgorithmResult &other ) const bool operator==( const QgsProcessingModelChildAlgorithmResult &other ) const
{ {
return mExecutionStatus == other.mExecutionStatus return mExecutionStatus == other.mExecutionStatus
&& mHtmlLog == other.mHtmlLog
&& mInputs == other.mInputs && mInputs == other.mInputs
&& mOutputs == other.mOutputs; && mOutputs == other.mOutputs;
} }
@ -102,6 +117,7 @@ class CORE_EXPORT QgsProcessingModelChildAlgorithmResult
Qgis::ProcessingModelChildAlgorithmExecutionStatus mExecutionStatus = Qgis::ProcessingModelChildAlgorithmExecutionStatus::NotExecuted; Qgis::ProcessingModelChildAlgorithmExecutionStatus mExecutionStatus = Qgis::ProcessingModelChildAlgorithmExecutionStatus::NotExecuted;
QVariantMap mInputs; QVariantMap mInputs;
QVariantMap mOutputs; QVariantMap mOutputs;
QString mHtmlLog;
}; };

View File

@ -69,7 +69,7 @@ class DummyRaiseExceptionAlgorithm : public QgsProcessingAlgorithm
QString displayName() const override { return mName; } QString displayName() const override { return mName; }
QVariantMap processAlgorithm( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * ) override QVariantMap processAlgorithm( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * ) override
{ {
throw QgsProcessingException( QString() ); throw QgsProcessingException( QStringLiteral( "something bad happened" ) );
} }
static bool postProcessAlgorithmCalled; static bool postProcessAlgorithmCalled;
QVariantMap postProcessAlgorithm( QgsProcessingContext &, QgsProcessingFeedback * ) final QVariantMap postProcessAlgorithm( QgsProcessingContext &, QgsProcessingFeedback * ) final
@ -2330,12 +2330,14 @@ void TestQgsProcessingModelAlgorithm::modelWithChildException()
QgsProcessingModelChildAlgorithm algWhichRaisesException; QgsProcessingModelChildAlgorithm algWhichRaisesException;
algWhichRaisesException.setChildId( QStringLiteral( "raise" ) ); algWhichRaisesException.setChildId( QStringLiteral( "raise" ) );
algWhichRaisesException.setDescription( QStringLiteral( "my second step" ) );
algWhichRaisesException.setAlgorithmId( "dummy4:raise" ); algWhichRaisesException.setAlgorithmId( "dummy4:raise" );
algWhichRaisesException.setDependencies( {QgsProcessingModelChildDependency( QStringLiteral( "buffer" ) )} ); algWhichRaisesException.setDependencies( {QgsProcessingModelChildDependency( QStringLiteral( "buffer" ) )} );
m.addChildAlgorithm( algWhichRaisesException ); m.addChildAlgorithm( algWhichRaisesException );
// run and check context details // run and check context details
QgsProcessingContext context; QgsProcessingContext context;
context.setLogLevel( Qgis::ProcessingLogLevel::ModelDebug );
QgsProcessingFeedback feedback; QgsProcessingFeedback feedback;
QVariantMap params; QVariantMap params;
QgsVectorLayer *layer3111 = new QgsVectorLayer( "Point?crs=epsg:3111", "v1", "memory" ); QgsVectorLayer *layer3111 = new QgsVectorLayer( "Point?crs=epsg:3111", "v1", "memory" );
@ -2359,9 +2361,13 @@ void TestQgsProcessingModelAlgorithm::modelWithChildException()
QCOMPARE( context.modelChildResults().value( "buffer" ).executionStatus(), Qgis::ProcessingModelChildAlgorithmExecutionStatus::Success ); QCOMPARE( context.modelChildResults().value( "buffer" ).executionStatus(), Qgis::ProcessingModelChildAlgorithmExecutionStatus::Success );
QCOMPARE( context.modelChildResults().value( "buffer" ).inputs().value( "INPUT" ).toString(), QStringLiteral( "v1" ) ); QCOMPARE( context.modelChildResults().value( "buffer" ).inputs().value( "INPUT" ).toString(), QStringLiteral( "v1" ) );
QCOMPARE( context.modelChildResults().value( "buffer" ).inputs().value( "OUTPUT" ).toString(), QStringLiteral( "memory:Buffered" ) ); QCOMPARE( context.modelChildResults().value( "buffer" ).inputs().value( "OUTPUT" ).toString(), QStringLiteral( "memory:Buffered" ) );
QCOMPARE( context.modelChildResults().value( "buffer" ).htmlLog().left( 50 ), QStringLiteral( "<span style=\"color:#777\">Prepare algorithm: buffer" ) );
QCOMPARE( context.modelChildResults().value( "buffer" ).htmlLog().right( 21 ), QStringLiteral( "s (1 output(s)).<br/>" ) );
QVERIFY( context.temporaryLayerStore()->mapLayer( context.modelChildResults().value( "buffer" ).outputs().value( "OUTPUT" ).toString() ) ); QVERIFY( context.temporaryLayerStore()->mapLayer( context.modelChildResults().value( "buffer" ).outputs().value( "OUTPUT" ).toString() ) );
QCOMPARE( context.modelChildResults().value( "raise" ).executionStatus(), Qgis::ProcessingModelChildAlgorithmExecutionStatus::Failed ); QCOMPARE( context.modelChildResults().value( "raise" ).executionStatus(), Qgis::ProcessingModelChildAlgorithmExecutionStatus::Failed );
QCOMPARE( context.modelChildResults().value( "raise" ).htmlLog().left( 49 ), QStringLiteral( "<span style=\"color:#777\">Prepare algorithm: raise" ) );
QVERIFY( context.modelChildResults().value( "raise" ).htmlLog().contains( QStringLiteral( "Error encountered while running my second step: something bad happened" ) ) );
} }
void TestQgsProcessingModelAlgorithm::modelDependencies() void TestQgsProcessingModelAlgorithm::modelDependencies()