Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] Allow running model files direct from standalone qgis_proce…
…ss tool
  • Loading branch information
nyalldawson committed Apr 15, 2020
1 parent 91b29aa commit bfadf47
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 16 deletions.
64 changes: 50 additions & 14 deletions src/process/qgsprocess.cpp
Expand Up @@ -25,6 +25,7 @@
#include "qgssettings.h"
#include "qgsapplication.h"
#include "qgsprocessingparametertype.h"
#include "processing/models/qgsprocessingmodelalgorithm.h"

#if defined(Q_OS_UNIX) and !defined(Q_OS_ANDROID)
#include "sigwatch.h"
Expand Down Expand Up @@ -55,7 +56,7 @@ void ConsoleFeedback::pushInfo( const QString &info )

void ConsoleFeedback::pushCommandInfo( const QString &info )
{
std::cerr << info.toLocal8Bit().constData() << '\n';
std::cout << info.toLocal8Bit().constData() << '\n';
}

void ConsoleFeedback::pushDebugInfo( const QString &info )
Expand Down Expand Up @@ -186,7 +187,7 @@ int QgsProcessingExec::run( const QStringList &args )
{
if ( args.size() < 3 )
{
std::cerr << QStringLiteral( "Algorithm ID not specified\n" ).toLocal8Bit().constData();
std::cerr << QStringLiteral( "Algorithm ID or model file not specified\n" ).toLocal8Bit().constData();
return 1;
}

Expand All @@ -197,7 +198,7 @@ int QgsProcessingExec::run( const QStringList &args )
{
if ( args.size() < 3 )
{
std::cerr << QStringLiteral( "Algorithm ID not specified\n" ).toLocal8Bit().constData();
std::cerr << QStringLiteral( "Algorithm ID or model file not specified\n" ).toLocal8Bit().constData();
return 1;
}

Expand Down Expand Up @@ -240,12 +241,12 @@ void QgsProcessingExec::showUsage( const QString &appName )

msg << "QGIS Processing Executor - " << VERSION << " '" << RELEASE_NAME << "' ("
<< Qgis::version() << ")\n"
<< "Usage: " << appName << " [command] [algorithm id] [parameters]\n"
<< "Usage: " << appName << " [command] [algorithm id or path to model file] [parameters]\n"
<< "\nAvailable commands:\n"
<< "\tplugins\tlist available and active plugins\n"
<< "\tlist\tlist all available processing algorithms\n"
<< "\thelp\tshow help for an algorithm. The algorithm id argument must be specified.\n"
<< "\trun\truns an algorithm. The algorithm id argument and parameter values must be specified. Parameter values are specified via the --PARAMETER=VALUE syntax\n";
<< "\thelp\tshow help for an algorithm. The algorithm id or a path to a model file must be specified.\n"
<< "\trun\truns an algorithm. The algorithm id or a path to a model file and parameter values must be specified. Parameter values are specified via the --PARAMETER=VALUE syntax\n";

std::cout << msg.join( QString() ).toLocal8Bit().constData();
}
Expand Down Expand Up @@ -310,11 +311,27 @@ void QgsProcessingExec::listPlugins()

int QgsProcessingExec::showAlgorithmHelp( const QString &id )
{
const QgsProcessingAlgorithm *alg = QgsApplication::processingRegistry()->algorithmById( id );
if ( ! alg )
std::unique_ptr< QgsProcessingModelAlgorithm > model;
const QgsProcessingAlgorithm *alg = nullptr;
if ( QFile::exists( id ) && QFileInfo( id ).suffix() == QLatin1String( "model3" ) )
{
std::cerr << QStringLiteral( "Algorithm %1 not found!\n" ).arg( id ).toLocal8Bit().constData();
return 1;
model = qgis::make_unique< QgsProcessingModelAlgorithm >();
if ( !model->fromFile( id ) )
{
std::cerr << QStringLiteral( "File %1 is not a valid Processing model!\n" ).arg( id ).toLocal8Bit().constData();
return 1;
}

alg = model.get();
}
else
{
alg = QgsApplication::processingRegistry()->algorithmById( id );
if ( ! alg )
{
std::cerr << QStringLiteral( "Algorithm %1 not found!\n" ).arg( id ).toLocal8Bit().constData();
return 1;
}
}

std::cout << QStringLiteral( "%1 (%2)\n" ).arg( alg->displayName(), alg->id() ).toLocal8Bit().constData();
Expand Down Expand Up @@ -381,11 +398,27 @@ int QgsProcessingExec::showAlgorithmHelp( const QString &id )

int QgsProcessingExec::execute( const QString &id, const QVariantMap &params )
{
const QgsProcessingAlgorithm *alg = QgsApplication::processingRegistry()->algorithmById( id );
if ( ! alg )
std::unique_ptr< QgsProcessingModelAlgorithm > model;
const QgsProcessingAlgorithm *alg = nullptr;
if ( QFile::exists( id ) && QFileInfo( id ).suffix() == QLatin1String( "model3" ) )
{
std::cerr << QStringLiteral( "Algorithm %1 not found!\n" ).arg( id ).toLocal8Bit().constData();
return 1;
model = qgis::make_unique< QgsProcessingModelAlgorithm >();
if ( !model->fromFile( id ) )
{
std::cerr << QStringLiteral( "File %1 is not a valid Processing model!\n" ).arg( id ).toLocal8Bit().constData();
return 1;
}

alg = model.get();
}
else
{
alg = QgsApplication::processingRegistry()->algorithmById( id );
if ( ! alg )
{
std::cerr << QStringLiteral( "Algorithm %1 not found!\n" ).arg( id ).toLocal8Bit().constData();
return 1;
}
}

std::cout << "\n----------------\n";
Expand Down Expand Up @@ -450,6 +483,9 @@ int QgsProcessingExec::execute( const QString &id, const QVariantMap &params )

for ( auto it = res.constBegin(); it != res.constEnd(); ++it )
{
if ( it.key() == QLatin1String( "CHILD_INPUTS" ) || it.key() == QLatin1String( "CHILD_RESULTS" ) )
continue;

QVariant res = it.value();
if ( res.type() == QVariant::List || res.type() == QVariant::StringList )
{
Expand Down
19 changes: 17 additions & 2 deletions tests/src/python/test_qgsprocessexecutable.py
Expand Up @@ -80,7 +80,7 @@ def testAlgorithmList(self):
def testAlgorithmHelpNoAlg(self):
rc, output, err = self.run_process(['help'])
self.assertEqual(rc, 1)
self.assertIn('algorithm id not specified', err.lower())
self.assertIn('algorithm id or model file not specified', err.lower())
self.assertFalse(output)

def testAlgorithmHelp(self):
Expand All @@ -93,7 +93,7 @@ def testAlgorithmHelp(self):
def testAlgorithmRunNoAlg(self):
rc, output, err = self.run_process(['run'])
self.assertEqual(rc, 1)
self.assertIn('algorithm id not specified', err.lower())
self.assertIn('algorithm id or model file not specified', err.lower())
self.assertFalse(output)

def testAlgorithmRunNoArgs(self):
Expand All @@ -112,6 +112,21 @@ def testAlgorithmRun(self):
self.assertIn('OUTPUT:\t' + output_file, output)
self.assertTrue(os.path.exists(output_file))

def testModelHelp(self):
rc, output, err = self.run_process(['help', TEST_DATA_DIR + '/test_model.model3'])
self.assertEqual(rc, 0)
self.assertIn('model description', output.lower())
self.assertFalse(err)

def testModelRun(self):
output_file = self.TMP_DIR + '/model_output.shp'
rc, output, err = self.run_process(['run', TEST_DATA_DIR + '/test_model.model3', '--FEATS={}'.format(TEST_DATA_DIR + '/polys.shp'), '--native:centroids_1:CENTROIDS={}'.format(output_file)])
self.assertEqual(rc, 0)
self.assertFalse(err)
self.assertIn('0...10...20...30...40...50...60...70...80...90', output.lower())
self.assertIn('results', output.lower())
self.assertTrue(os.path.exists(output_file))


if __name__ == '__main__':
# look for qgis bin path
Expand Down

0 comments on commit bfadf47

Please sign in to comment.