19#include <nlohmann/json.hpp>
37#include <QRegularExpression>
38#include <QRegularExpressionMatch>
41#include "moc_qgsprocessinghistoryprovider.cpp"
49 return QStringLiteral(
"processing" );
54 const QString logPath = oldLogPath();
55 if ( !QFile::exists( logPath ) )
58 QFile logFile( logPath );
59 if ( logFile.open( QIODevice::ReadOnly ) )
61 QTextStream in( &logFile );
62 QList<QgsHistoryEntry> entries;
65 const QString line = in.readLine().trimmed();
66 QStringList parts = line.split( QStringLiteral(
"|~|" ) );
67 if ( parts.size() <= 1 )
68 parts = line.split(
'|' );
70 if ( parts.size() == 3 && parts.at( 0 ).startsWith( QLatin1String(
"ALGORITHM" ), Qt::CaseInsensitive ) )
73 details.insert( QStringLiteral(
"python_command" ), parts.at( 2 ) );
75 const thread_local QRegularExpression algIdRegEx( QStringLiteral(
"processing\\.run\\(\"(.*?)\"" ) );
76 const QRegularExpressionMatch match = algIdRegEx.match( parts.at( 2 ) );
77 if ( match.hasMatch() )
78 details.insert( QStringLiteral(
"algorithm_id" ), match.captured( 1 ) );
80 entries.append(
QgsHistoryEntry(
id(), QDateTime::fromString( parts.at( 1 ), QStringLiteral(
"yyyy-MM-d hh:mm:ss" ) ), details ) );
96 , mAlgorithmId( mEntry.entry.value(
"algorithm_id" ).toString() )
97 , mPythonCommand( mEntry.entry.value(
"python_command" ).toString() )
98 , mProcessCommand( mEntry.entry.value(
"process_command" ).toString() )
99 , mProvider( provider )
101 const QVariant parameters = mEntry.entry.value( QStringLiteral(
"parameters" ) );
102 if ( parameters.userType() == QMetaType::Type::QVariantMap )
104 const QVariantMap parametersMap = parameters.toMap();
105 mInputs = parametersMap.value( QStringLiteral(
"inputs" ) ).toMap();
109 bool doubleClicked(
const QgsHistoryWidgetContext & )
override
111 if ( mPythonCommand.isEmpty() )
114 QString execAlgorithmDialogCommand = mPythonCommand;
115 execAlgorithmDialogCommand.replace( QLatin1String(
"processing.run(" ), QLatin1String(
"processing.execAlgorithmDialog(" ) );
118 const QStringList script = {
119 QStringLiteral(
"import processing" ),
120 QStringLiteral(
"from qgis.core import QgsProcessingOutputLayerDefinition, QgsProcessingFeatureSourceDefinition, QgsProperty, QgsCoordinateReferenceSystem, QgsFeatureRequest" ),
121 QStringLiteral(
"from qgis.PyQt.QtCore import QDate, QTime, QDateTime" ),
122 QStringLiteral(
"from qgis.PyQt.QtGui import QColor" ),
123 execAlgorithmDialogCommand
126 mProvider->emitExecute( script.join(
'\n' ) );
132 if ( !mPythonCommand.isEmpty() )
134 QAction *pythonAction =
new QAction(
135 QObject::tr(
"Copy as Python Command" ), menu
138 QObject::connect( pythonAction, &QAction::triggered, menu, [
this] {
139 copyText( mPythonCommand );
141 menu->addAction( pythonAction );
143 if ( !mProcessCommand.isEmpty() )
145 QAction *processAction =
new QAction(
146 QObject::tr(
"Copy as qgis_process Command" ), menu
149 QObject::connect( processAction, &QAction::triggered, menu, [
this] {
150 copyText( mProcessCommand );
152 menu->addAction( processAction );
154 if ( !mInputs.isEmpty() )
156 QAction *inputsAction =
new QAction(
157 QObject::tr(
"Copy as JSON" ), menu
160 QObject::connect( inputsAction, &QAction::triggered, menu, [
this] {
163 menu->addAction( inputsAction );
166 if ( !mPythonCommand.isEmpty() )
168 if ( !menu->isEmpty() )
170 menu->addSeparator();
173 QAction *createTestAction =
new QAction(
174 QObject::tr(
"Create Test…" ), menu
176 QObject::connect( createTestAction, &QAction::triggered, menu, [
this] {
177 mProvider->emitCreateTest( mPythonCommand );
179 menu->addAction( createTestAction );
183 void copyText(
const QString &text )
185 QMimeData *m =
new QMimeData();
187 QApplication::clipboard()->setMimeData( m );
190 QgsHistoryEntry mEntry;
191 QString mAlgorithmId;
192 QString mPythonCommand;
193 QString mProcessCommand;
196 QgsProcessingHistoryProvider *mProvider =
nullptr;
199class ProcessingHistoryPythonCommandNode :
public ProcessingHistoryBaseNode
202 ProcessingHistoryPythonCommandNode(
const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
203 : ProcessingHistoryBaseNode( entry, provider )
206 QVariant data(
int role = Qt::DisplayRole )
const override
210 case Qt::DisplayRole:
212 QString display = mPythonCommand;
213 if ( display.length() > 300 )
215 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
219 case Qt::DecorationRole:
228 QWidget *createWidget(
const QgsHistoryWidgetContext & )
override
230 QgsCodeEditorPython *codeEditor =
new QgsCodeEditorPython();
231 codeEditor->setReadOnly(
true );
232 codeEditor->setCaretLineVisible(
false );
235 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
236 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
239 const QString introText = QStringLiteral(
"\"\"\"\n%1\n\"\"\"\n\n " ).arg( QObject::tr(
"Double-click on the history item or paste the command below to re-run the algorithm" ) );
240 codeEditor->
setText( introText + mPythonCommand );
246class ProcessingHistoryProcessCommandNode :
public ProcessingHistoryBaseNode
249 ProcessingHistoryProcessCommandNode(
const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
250 : ProcessingHistoryBaseNode( entry, provider )
253 QVariant data(
int role = Qt::DisplayRole )
const override
257 case Qt::DisplayRole:
259 QString display = mProcessCommand;
260 if ( display.length() > 300 )
262 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
266 case Qt::DecorationRole:
275 QWidget *createWidget(
const QgsHistoryWidgetContext & )
override
277 QgsCodeEditorShell *codeEditor =
new QgsCodeEditorShell();
278 codeEditor->setReadOnly(
true );
279 codeEditor->setCaretLineVisible(
false );
282 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
283 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
285 codeEditor->
setText( mProcessCommand );
292class ProcessingHistoryJsonNode :
public ProcessingHistoryBaseNode
295 ProcessingHistoryJsonNode(
const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
296 : ProcessingHistoryBaseNode( entry, provider )
302 QVariant data(
int role = Qt::DisplayRole )
const override
306 case Qt::DisplayRole:
308 QString display = mJsonSingleLine;
309 if ( display.length() > 300 )
311 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
315 case Qt::DecorationRole:
324 QWidget *createWidget(
const QgsHistoryWidgetContext & )
override
326 QgsCodeEditorJson *codeEditor =
new QgsCodeEditorJson();
327 codeEditor->setReadOnly(
true );
328 codeEditor->setCaretLineVisible(
false );
331 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
332 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
340 QString mJsonSingleLine;
344class ProcessingHistoryRootNode :
public ProcessingHistoryBaseNode
347 ProcessingHistoryRootNode(
const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
348 : ProcessingHistoryBaseNode( entry, provider )
350 const QVariant parameters = mEntry.entry.value( QStringLiteral(
"parameters" ) );
351 if ( parameters.type() == QVariant::Map )
353 mDescription = QgsProcessingUtils::variantToPythonLiteral( mInputs );
358 mDescription = mPythonCommand;
361 if ( mDescription.length() > 300 )
363 mDescription = QObject::tr(
"%1…" ).arg( mDescription.left( 299 ) );
366 addChild(
new ProcessingHistoryPythonCommandNode( mEntry, mProvider ) );
367 addChild(
new ProcessingHistoryProcessCommandNode( mEntry, mProvider ) );
368 addChild(
new ProcessingHistoryJsonNode( mEntry, mProvider ) );
371 void setEntry(
const QgsHistoryEntry &entry )
376 QVariant data(
int role = Qt::DisplayRole )
const override
378 if ( mAlgorithmInformation.displayName.isEmpty() )
385 case Qt::DisplayRole:
387 const QString algName = mAlgorithmInformation.displayName;
388 if ( !mDescription.isEmpty() )
389 return QStringLiteral(
"[%1] %2 - %3" ).arg( mEntry.timestamp.toString( QStringLiteral(
"yyyy-MM-dd hh:mm" ) ), algName, mDescription );
391 return QStringLiteral(
"[%1] %2" ).arg( mEntry.timestamp.toString( QStringLiteral(
"yyyy-MM-dd hh:mm" ) ), algName );
394 case Qt::DecorationRole:
396 return mAlgorithmInformation.icon;
405 QString html(
const QgsHistoryWidgetContext & )
const override
407 return mEntry.entry.value( QStringLiteral(
"log" ) ).toString();
410 QString mDescription;
411 mutable QgsProcessingAlgorithmInformation mAlgorithmInformation;
418 return new ProcessingHistoryRootNode( entry,
this );
423 if ( ProcessingHistoryRootNode *rootNode =
dynamic_cast<ProcessingHistoryRootNode *
>( node ) )
425 rootNode->setEntry( entry );
429QString QgsProcessingHistoryProvider::oldLogPath()
const
432 return userDir + QStringLiteral(
"/processing.log" );
435void QgsProcessingHistoryProvider::emitExecute(
const QString &commands )
440void QgsProcessingHistoryProvider::emitCreateTest(
const QString &command )
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
void setText(const QString &text) override
void setFoldingVisible(bool folding)
Set whether the folding controls are visible in the editor.
void setLineNumbersVisible(bool visible)
Sets whether line numbers should be visible in the editor.
static QgsHistoryProviderRegistry * historyProviderRegistry()
Returns the global history provider registry, used for tracking history providers.
Base class for history entry "group" nodes, which contain children of their own.
Base class for nodes representing a QgsHistoryEntry.
virtual void populateContextMenu(QMenu *menu, const QgsHistoryWidgetContext &context)
Allows the node to populate a context menu before display to the user.
virtual bool doubleClicked(const QgsHistoryWidgetContext &context)
Called when the node is double-clicked.
Encapsulates a history entry.
bool addEntries(const QList< QgsHistoryEntry > &entries, QgsHistoryProviderRegistry::HistoryEntryOptions options=QgsHistoryProviderRegistry::HistoryEntryOptions())
Adds a list of entries to the history logs.
Contains settings which reflect the context in which a history widget is shown, e....
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
History provider for operations performed through the Processing framework.
void updateNodeForEntry(QgsHistoryEntryNode *node, const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context) override
Updates an existing history node for the given entry.
QString id() const override
Returns the provider's unique id, which is used to associate existing history entries with the provid...
void executePython(const QString &commands)
Emitted when the provider needs to execute python commands in the Processing context.
QgsHistoryEntryNode * createNodeForEntry(const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context) override
Creates a new history node for the given entry.
void createTest(const QString &command)
Emitted when the provider needs to create a Processing test with the given python command.
void portOldLog()
Ports the old text log to the history framework.
QgsProcessingHistoryProvider()
QgsProcessingAlgorithmInformation algorithmInformation(const QString &id) const
Returns basic algorithm information for the algorithm with matching ID.