mirror of
https://github.com/OPM/ResInsight.git
synced 2026-11-13 09:37:29 -06:00
Add a per-workflow Vertical Layout toggle to the workflow editor toolbar and canvas context menu. In vertical mode ranks are stacked top to bottom, task inputs sit on the top edge and outputs on the bottom edge, config boxes are placed above their task and control edges run between the sides.
211 lines
8.9 KiB
C++
211 lines
8.9 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2026 Equinor ASA
|
|
//
|
|
// ResInsight is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
|
// for more details.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
#pragma once
|
|
|
|
#include "RimWorkflowDefinition.h"
|
|
|
|
#include "cafFilePath.h"
|
|
#include "cafPdmChildArrayField.h"
|
|
#include "cafPdmField.h"
|
|
#include "cafPdmObject.h"
|
|
|
|
#include <QDateTime>
|
|
#include <QJsonObject>
|
|
#include <QMap>
|
|
|
|
#include <expected>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
class RimWorkflowJob;
|
|
class QTemporaryDir;
|
|
|
|
//==================================================================================================
|
|
/// A taskmaestro workflow: a YAML folder, a workflow registered by an installed package, or a new
|
|
/// workflow that has not been saved yet. Editable workflows keep their definition in memory with
|
|
/// undo/redo until saved. A nested workflow (a task wrapping a whole workflow) is a read-only child
|
|
/// workflow drawn from its description; it is not saved in the project.
|
|
//==================================================================================================
|
|
class RimWorkflow : public caf::PdmObject
|
|
{
|
|
CAF_PDM_HEADER_INIT;
|
|
|
|
public:
|
|
enum class Source
|
|
{
|
|
YamlDirectory,
|
|
Registered,
|
|
Unsaved,
|
|
Nested
|
|
};
|
|
|
|
struct RunSource
|
|
{
|
|
QStringList baseArgs{};
|
|
QString extraPythonPath{};
|
|
};
|
|
|
|
RimWorkflow();
|
|
~RimWorkflow() override;
|
|
|
|
QString name() const;
|
|
void setWorkflowDirectory( const QString& directory );
|
|
QString workflowDirectory() const;
|
|
QJsonObject graph() const;
|
|
QString loadError() const;
|
|
|
|
bool inlineConfigValues() const;
|
|
void setInlineConfigValues( bool inlineValues );
|
|
|
|
bool verticalLayout() const;
|
|
void setVerticalLayout( bool vertical );
|
|
|
|
QStringList hiddenTasks() const;
|
|
bool isTaskHidden( const QString& taskName ) const;
|
|
void setTaskHidden( const QString& taskName, bool hidden );
|
|
void showAllTasks();
|
|
|
|
QStringList collapsedTasks() const;
|
|
bool isTaskCollapsed( const QString& taskName ) const;
|
|
void setTaskCollapsed( const QString& taskName, bool collapsed );
|
|
void setCollapsedTasks( const QStringList& taskNames );
|
|
|
|
// Nested workflows. A task path holds the nested task names from the root workflow down.
|
|
bool isNestedTask( const QString& taskName ) const;
|
|
RimWorkflow* nestedWorkflow( const QString& taskName );
|
|
RimWorkflow* parentWorkflow() const;
|
|
QString nestedTaskName() const;
|
|
QStringList nestedTaskPath() const;
|
|
|
|
bool loadFromDirectory( QString* errorMessage = nullptr );
|
|
bool loadFromRegistered( const QJsonObject& catalogEntry, QString* errorMessage = nullptr );
|
|
void setUnsavedDefinition( const RimWorkflowDefinition& definition,
|
|
const QString& sourceDirectory = {},
|
|
const QString& targetDirectory = {} );
|
|
|
|
Source source() const;
|
|
QString registeredId() const;
|
|
QString sourceDirectory() const;
|
|
const RimWorkflowDefinition& definition() const;
|
|
bool isEditable() const;
|
|
QString readOnlyReason() const;
|
|
bool isDirty() const;
|
|
bool isLocked() const;
|
|
int revision() const;
|
|
|
|
// Editing
|
|
std::expected<void, QString> applyEdit( const std::expected<RimWorkflowDefinition, QString>& result,
|
|
const QMap<QString, QString>& renames = {} );
|
|
bool canUndo() const;
|
|
bool canRedo() const;
|
|
|
|
// Task renames (old name -> new name) that undo() and redo() would apply
|
|
QMap<QString, QString> undoRenames() const;
|
|
QMap<QString, QString> redoRenames() const;
|
|
void undo();
|
|
void redo();
|
|
|
|
// Saving and running
|
|
std::expected<void, QString> save();
|
|
QStringList saveInPlaceWarnings() const;
|
|
std::expected<void, QString> saveAs( const QString& directory );
|
|
std::expected<QString, QString> editDirectory();
|
|
QByteArray definitionJsonForSave() const;
|
|
std::expected<RunSource, QString> prepareRunSource( const QString& runDirectory );
|
|
QJsonObject inputValuesForSave() const;
|
|
|
|
// Validation of the current revision (static checks plus `taskmaestro workflow describe`)
|
|
void setValidationResult( int revision, const std::vector<RimWorkflowIssue>& issues, bool describeSucceeded );
|
|
std::vector<RimWorkflowIssue> validationIssues() const;
|
|
bool isValidationCurrent() const;
|
|
bool lastValidationFailed() const;
|
|
|
|
std::vector<RimWorkflowJob*> jobs() const;
|
|
void addJob( RimWorkflowJob* job );
|
|
void syncJobs( const QMap<QString, QString>& renames = {} );
|
|
|
|
static QString findPythonExecutable();
|
|
|
|
// Copy the Python modules and packages of a workflow folder. Existing files are kept.
|
|
static std::expected<void, QString> copyPythonModules( const QString& sourceDirectory, const QString& targetDirectory );
|
|
|
|
protected:
|
|
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
|
|
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
|
|
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
|
|
|
private:
|
|
struct UndoEntry
|
|
{
|
|
RimWorkflowDefinition definition;
|
|
QMap<QString, QString> renames;
|
|
};
|
|
|
|
bool setLoadError( const QString& message, QString* errorMessage );
|
|
void setDefinition( const RimWorkflowDefinition& definition, Source source );
|
|
void definitionChanged( const QMap<QString, QString>& renames );
|
|
void notifyDefinitionChanged();
|
|
void rebuildGraph();
|
|
void updateUiName();
|
|
void ensureDefaultJob();
|
|
void loadNested();
|
|
|
|
std::optional<QStringList> describeNestedArguments();
|
|
std::optional<QStringList> describeNestedYamlArguments( const RimWorkflow& parent );
|
|
void setNestedDescription( const QJsonObject& output, RimWorkflowDefinition definition );
|
|
|
|
QMap<QString, QJsonObject> nestedEntries() const;
|
|
|
|
std::expected<QJsonObject, QString> saveToDirectory( const QString& directory, bool backup );
|
|
|
|
void recordModificationTime();
|
|
static QMap<QString, QString> invertedRenames( const QMap<QString, QString>& renames );
|
|
|
|
private:
|
|
caf::PdmField<QString> m_name;
|
|
caf::PdmField<QString> m_description;
|
|
caf::PdmField<caf::FilePath> m_workflowDirectory;
|
|
caf::PdmField<QString> m_registeredId;
|
|
caf::PdmField<bool> m_editable;
|
|
caf::PdmField<QString> m_readOnlyReason;
|
|
caf::PdmField<QString> m_loadError;
|
|
caf::PdmChildArrayField<RimWorkflowJob*> m_jobs;
|
|
caf::PdmField<bool> m_inlineConfigValues;
|
|
caf::PdmField<bool> m_verticalLayout;
|
|
caf::PdmField<std::vector<QString>> m_hiddenTasks;
|
|
caf::PdmField<std::vector<QString>> m_collapsedTasks;
|
|
caf::PdmChildArrayField<RimWorkflow*> m_nestedWorkflows;
|
|
QJsonObject m_graph;
|
|
|
|
Source m_source = Source::YamlDirectory;
|
|
QString m_sourceDirectory;
|
|
RimWorkflowDefinition m_definition;
|
|
RimWorkflowDefinition m_savedDefinition;
|
|
int m_revision = 0;
|
|
std::vector<UndoEntry> m_undoStack;
|
|
std::vector<UndoEntry> m_redoStack;
|
|
std::vector<RimWorkflowIssue> m_validationIssues;
|
|
int m_validationRevision = -1;
|
|
bool m_describeSucceeded = true;
|
|
QDateTime m_loadedModificationTime;
|
|
std::unique_ptr<QTemporaryDir> m_editDirectory;
|
|
QString m_nestedTaskName;
|
|
QMap<QString, QJsonObject> m_nestedEntries; // `workflow:` entries of a nested YAML workflow
|
|
};
|