From a47a871feae7bc84e2e8642e8dd544203c6671f9 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Fri, 2 Oct 2026 15:21:38 +0200 Subject: [PATCH] Workflows: Take missing-value warnings from the displayed job Describe only sees literal input values, so object references such as cases were always reported as missing, and the warning did not update when a value was edited. Compute these warnings from the job's bindings and refresh them when a binding changes. --- .../Workflow/RimWorkflowValidationTools.cpp | 39 ++++++++++++++++++- .../Workflow/RimWorkflowValidationTools.h | 7 ++++ .../UnitTests/RimWorkflowJobSync-Test.cpp | 27 +++++++++++++ .../UserInterface/RiuMainWindow.cpp | 1 + .../UserInterface/RiuWorkflowEditorWidget.cpp | 38 ++++++++++++++++++ .../UserInterface/RiuWorkflowEditorWidget.h | 1 + docs/workflow-ui-design.md | 6 +++ 7 files changed, 118 insertions(+), 1 deletion(-) diff --git a/ApplicationLibCode/ProjectDataModel/Workflow/RimWorkflowValidationTools.cpp b/ApplicationLibCode/ProjectDataModel/Workflow/RimWorkflowValidationTools.cpp index 5240be190b..156092ae36 100644 --- a/ApplicationLibCode/ProjectDataModel/Workflow/RimWorkflowValidationTools.cpp +++ b/ApplicationLibCode/ProjectDataModel/Workflow/RimWorkflowValidationTools.cpp @@ -91,7 +91,7 @@ RimWorkflowValidationTools::Result RimWorkflowValidationTools::issuesFromDescrib result.issues.push_back( { .task = task.value( "name" ).toString(), .field = field.toString(), .severity = RimWorkflowIssue::Severity::Warning, - .message = QString( "Input '%1' has no value" ).arg( field.toString() ) } ); + .message = missingValueMessage( field.toString() ) } ); } } return result; @@ -110,3 +110,40 @@ QString RimWorkflowValidationTools::taskFromMessage( const QString& message, con } return found; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimWorkflowValidationTools::missingValueMessage( const QString& fieldName ) +{ + return QString( "Input '%1' has no value" ).arg( fieldName ); +} + +//-------------------------------------------------------------------------------------------------- +/// The saved definition only carries literal values, so describe reports object references (cases, +/// well paths, views) as missing, and the result goes stale when the user edits a value. The job's +/// own bindings are the source of truth for the fields they cover. +//-------------------------------------------------------------------------------------------------- +QJsonArray RimWorkflowValidationTools::issuesWithJobValues( const QString& taskName, + const QJsonArray& taskIssues, + const std::map& fieldHasValue ) +{ + QJsonArray result; + for ( const QJsonValue& value : taskIssues ) + { + const QJsonObject issue = value.toObject(); + const QString field = issue.value( "field" ).toString(); + const bool isMissingValueWarning = issue.value( "severity" ).toString() == "warning" && + issue.value( "message" ).toString() == missingValueMessage( field ); + if ( isMissingValueWarning && fieldHasValue.contains( field ) ) continue; + result.append( issue ); + } + + for ( const auto& [field, hasValue] : fieldHasValue ) + { + if ( hasValue ) continue; + result.append( + QJsonObject{ { "task", taskName }, { "field", field }, { "severity", "warning" }, { "message", missingValueMessage( field ) } } ); + } + return result; +} diff --git a/ApplicationLibCode/ProjectDataModel/Workflow/RimWorkflowValidationTools.h b/ApplicationLibCode/ProjectDataModel/Workflow/RimWorkflowValidationTools.h index 66e5f0b0fa..a8522dfc55 100644 --- a/ApplicationLibCode/ProjectDataModel/Workflow/RimWorkflowValidationTools.h +++ b/ApplicationLibCode/ProjectDataModel/Workflow/RimWorkflowValidationTools.h @@ -20,9 +20,11 @@ #include "RimWorkflowDefinition.h" +#include #include #include +#include #include //================================================================================================== @@ -40,4 +42,9 @@ struct Result Result issuesFromHelperResult( const QJsonObject& helperResult, const QStringList& nodeNames ); Result issuesFromDescribe( const QJsonObject& describe, const QJsonObject& describeError, const QStringList& nodeNames ); QString taskFromMessage( const QString& message, const QStringList& nodeNames ); +QString missingValueMessage( const QString& fieldName ); + +// Replace the missing-value warnings of a task with warnings derived from the values of a job. +// `fieldHasValue` maps the task's bound input fields to whether the job gives them a value. +QJsonArray issuesWithJobValues( const QString& taskName, const QJsonArray& taskIssues, const std::map& fieldHasValue ); } // namespace RimWorkflowValidationTools diff --git a/ApplicationLibCode/UnitTests/RimWorkflowJobSync-Test.cpp b/ApplicationLibCode/UnitTests/RimWorkflowJobSync-Test.cpp index ae757a939c..9f9a93099a 100644 --- a/ApplicationLibCode/UnitTests/RimWorkflowJobSync-Test.cpp +++ b/ApplicationLibCode/UnitTests/RimWorkflowJobSync-Test.cpp @@ -241,3 +241,30 @@ TEST( RimWorkflowValidationTools, issuesFromHelperResult ) EXPECT_EQ( RimWorkflowIssue::Severity::Warning, result.issues[0].severity ); EXPECT_EQ( "paths", result.issues[0].field ); } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimWorkflowValidationTools, issuesWithJobValues ) +{ + const QJsonObject missingCase{ { "task", "select" }, + { "field", "case" }, + { "severity", "warning" }, + { "message", RimWorkflowValidationTools::missingValueMessage( "case" ) } }; + const QJsonObject otherIssue{ { "task", "select" }, { "field", "" }, { "severity", "error" }, { "message", "Broken" } }; + + // A value in the job removes the stale warning from describe + QJsonArray issues = RimWorkflowValidationTools::issuesWithJobValues( "select", { missingCase, otherIssue }, { { "case", true } } ); + ASSERT_EQ( 1, issues.size() ); + EXPECT_EQ( "Broken", issues[0].toObject().value( "message" ).toString() ); + + // A missing value in the job gives one warning, even if describe also reported it + issues = RimWorkflowValidationTools::issuesWithJobValues( "select", { missingCase }, { { "case", false } } ); + ASSERT_EQ( 1, issues.size() ); + EXPECT_EQ( "case", issues[0].toObject().value( "field" ).toString() ); + EXPECT_EQ( "warning", issues[0].toObject().value( "severity" ).toString() ); + + // Fields the job does not bind keep the warnings from describe + issues = RimWorkflowValidationTools::issuesWithJobValues( "select", { missingCase }, {} ); + EXPECT_EQ( 1, issues.size() ); +} diff --git a/ApplicationLibCode/UserInterface/RiuMainWindow.cpp b/ApplicationLibCode/UserInterface/RiuMainWindow.cpp index 0550dda191..bed4d9a71b 100644 --- a/ApplicationLibCode/UserInterface/RiuMainWindow.cpp +++ b/ApplicationLibCode/UserInterface/RiuMainWindow.cpp @@ -1621,6 +1621,7 @@ void RiuMainWindow::workflowBindingChanged( const RimWorkflowFieldBinding* bindi { m_workflowEditor->graphView()->setTaskInputValue( task->taskName(), binding->fieldName(), binding->displayValue() ); } + m_workflowEditor->updateTaskIssues(); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/UserInterface/RiuWorkflowEditorWidget.cpp b/ApplicationLibCode/UserInterface/RiuWorkflowEditorWidget.cpp index a414095c07..94ae29f421 100644 --- a/ApplicationLibCode/UserInterface/RiuWorkflowEditorWidget.cpp +++ b/ApplicationLibCode/UserInterface/RiuWorkflowEditorWidget.cpp @@ -31,6 +31,7 @@ #include "Workflow/RimWorkflowPortCompatibility.h" #include "Workflow/RimWorkflowTaskCatalog.h" #include "Workflow/RimWorkflowTaskInput.h" +#include "Workflow/RimWorkflowValidationTools.h" #include "cafCmdFeature.h" #include "cafCmdFeatureManager.h" @@ -260,6 +261,43 @@ void RiuWorkflowEditorWidget::updateTaskInputValues() if ( binding ) m_graphView->setTaskInputValue( task->taskName(), binding->fieldName(), binding->displayValue() ); } } + updateTaskIssues(); +} + +//-------------------------------------------------------------------------------------------------- +/// Show the issues of the workflow, with missing-value warnings taken from the displayed job +//-------------------------------------------------------------------------------------------------- +void RiuWorkflowEditorWidget::updateTaskIssues() +{ + if ( !m_workflow || !m_job ) return; + + for ( const QJsonValue& value : m_workflow->graph().value( "tasks" ).toArray() ) + { + const QJsonObject graphTask = value.toObject(); + const QString taskName = graphTask.value( "name" ).toString(); + RimWorkflowTaskInput* taskInput = m_job->taskInput( taskName ); + + std::map fieldHasValue; + for ( const QJsonValue& field : graphTask.value( "config_fields" ).toArray() ) + { + const QJsonObject schema = field.toObject(); + const QString fieldName = schema.value( "name" ).toString(); + bool hasValue = false; + if ( taskInput ) + { + for ( const RimWorkflowFieldBinding* binding : taskInput->items() ) + { + if ( binding && binding->fieldName() == fieldName ) hasValue = !binding->toJsonValue().isNull(); + } + } + fieldHasValue[fieldName] = hasValue || !schema.value( "required" ).toBool(); + } + + m_graphView->setTaskIssues( taskName, + RimWorkflowValidationTools::issuesWithJobValues( taskName, + graphTask.value( "issues" ).toArray(), + fieldHasValue ) ); + } } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/UserInterface/RiuWorkflowEditorWidget.h b/ApplicationLibCode/UserInterface/RiuWorkflowEditorWidget.h index e881c2da65..0b50b6f8ec 100644 --- a/ApplicationLibCode/UserInterface/RiuWorkflowEditorWidget.h +++ b/ApplicationLibCode/UserInterface/RiuWorkflowEditorWidget.h @@ -57,6 +57,7 @@ public: void refresh(); void updateJobState(); void updateTaskInputValues(); + void updateTaskIssues(); RiuWorkflowGraphView* graphView() const; RiuWorkflowTaskPalette* palette() const; diff --git a/docs/workflow-ui-design.md b/docs/workflow-ui-design.md index 5f31b772cd..a63faccacc 100644 --- a/docs/workflow-ui-design.md +++ b/docs/workflow-ui-design.md @@ -192,6 +192,12 @@ Two layers of validation run: dropped, and a 30 s watchdog stops hung processes. `RimWorkflowValidationTools::issuesFromHelperResult` maps the errors to tasks; anything left over goes to the status line. +- **Missing values** come from the displayed job, not from describe. The saved + definition only carries literal values, so describe would report cases, well + paths and views as missing. The editor replaces describe's "has no value" + warnings with ones computed from the job's bindings + (`RimWorkflowValidationTools::issuesWithJobValues`). They update as soon as + a value is edited. Running a workflow whose last validation failed asks for confirmation first.