mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-16 15:45:04 +08:00
Merge pull request #1251 from jmchilton/workflow_labels_2
Workflow Editor UI for Step Labels
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
define(['mvc/workflow/workflow-connector'], function( Connector ) {
|
||||
define([
|
||||
'mvc/workflow/workflow-connector',
|
||||
'libs/toastr'
|
||||
],
|
||||
function( Connector, Toastr ) {
|
||||
function Workflow( app, canvas_container ) {
|
||||
this.app = app;
|
||||
this.canvas_container = canvas_container;
|
||||
@@ -7,10 +11,49 @@ define(['mvc/workflow/workflow-connector'], function( Connector ) {
|
||||
this.name = null;
|
||||
this.has_changes = false;
|
||||
this.active_form_has_changes = false;
|
||||
this.nodeLabels = {}; // TODO: track and enforce output labels also
|
||||
|
||||
}
|
||||
$.extend( Workflow.prototype, {
|
||||
create_node: function ( type, title_text, tool_id ) {
|
||||
var node = this.app.prebuildNode( type, title_text, tool_id );
|
||||
canLabelNodeWith: function( label ) {
|
||||
if( label ) {
|
||||
return ! (label in this.nodeLabels);
|
||||
} else {
|
||||
// empty labels are non-exclusive, so allow this one.
|
||||
return true;
|
||||
}
|
||||
},
|
||||
registerNodeLabel: function( label ) {
|
||||
if( label ) {
|
||||
this.nodeLabels[label] = true;
|
||||
}
|
||||
},
|
||||
unregisterNodeLabel: function( label ) {
|
||||
if( label ) {
|
||||
delete this.nodeLabels[label];
|
||||
}
|
||||
},
|
||||
updateNodeLabel: function( fromLabel, toLabel ) {
|
||||
if( fromLabel ) {
|
||||
this.unregisterNodeLabel( fromLabel );
|
||||
}
|
||||
if( ! this.canLabelNodeWith( toLabel ) ) {
|
||||
Toastr.warning("Workflow contains duplicate node labels " + toLabel + ". This must be fixed before it can be saved.");
|
||||
}
|
||||
if( toLabel ) {
|
||||
this.registerNodeLabel( toLabel );
|
||||
}
|
||||
},
|
||||
attemptUpdateNodeLabel: function( node, label ) {
|
||||
if( this.canLabelNodeWith( label ) ) {
|
||||
node.setLabel( label );
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
create_node: function ( type, title_text, content_id ) {
|
||||
var node = this.app.prebuildNode( type, title_text, content_id );
|
||||
this.add_node( node );
|
||||
this.fit_canvas_to_nodes();
|
||||
this.app.canvas_manager.draw_overview();
|
||||
@@ -131,7 +174,7 @@ define(['mvc/workflow/workflow-connector'], function( Connector ) {
|
||||
var node_data = {
|
||||
id : node.id,
|
||||
type : node.type,
|
||||
tool_id : node.tool_id,
|
||||
tool_id : node.content_id,
|
||||
tool_state : node.tool_state,
|
||||
tool_errors : node.tool_errors,
|
||||
input_connections : input_connections,
|
||||
@@ -383,4 +426,4 @@ define(['mvc/workflow/workflow-connector'], function( Connector ) {
|
||||
}
|
||||
});
|
||||
return Workflow;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,6 +124,10 @@ define(['mvc/workflow/workflow-view-node'], function( NodeView ) {
|
||||
// Remove active class
|
||||
$(element).removeClass( "toolForm-active" );
|
||||
},
|
||||
setLabel: function(label) {
|
||||
this.app.workflow.updateNodeLabel(this.label, label);
|
||||
this.label = label || null;
|
||||
},
|
||||
init_field_data : function ( data ) {
|
||||
if ( data.type ) {
|
||||
this.type = data.type;
|
||||
@@ -135,7 +139,7 @@ define(['mvc/workflow/workflow-view-node'], function( NodeView ) {
|
||||
this.tooltip = data.tooltip ? data.tooltip : "";
|
||||
this.annotation = data.annotation;
|
||||
this.post_job_actions = data.post_job_actions ? data.post_job_actions : {};
|
||||
this.label = data.label;
|
||||
this.setLabel(data.label);
|
||||
this.uuid = data.uuid;
|
||||
this.workflow_outputs = data.workflow_outputs ? data.workflow_outputs : [];
|
||||
|
||||
@@ -165,6 +169,8 @@ define(['mvc/workflow/workflow-view-node'], function( NodeView ) {
|
||||
this.form_html = data.form_html;
|
||||
this.tool_errors = data.tool_errors;
|
||||
this.annotation = data['annotation'];
|
||||
this.setLabel(data.label);
|
||||
|
||||
if( "post_job_actions" in data ) {
|
||||
// Won't be present in response for data inputs
|
||||
var pja_in = $.parseJSON(data.post_job_actions);
|
||||
@@ -184,6 +190,7 @@ define(['mvc/workflow/workflow-view-node'], function( NodeView ) {
|
||||
unusedView.el.terminal.destroy();
|
||||
} );
|
||||
nodeView.terminalViews = newTerminalViews;
|
||||
node.nodeView.render();
|
||||
// In general workflow editor assumes tool outputs don't change in # or
|
||||
// type (not really valid right?) but adding special logic here for
|
||||
// data collection input parameters that can have their collection
|
||||
|
||||
@@ -11,6 +11,14 @@ define(['mvc/workflow/workflow-view-terminals', 'mvc/workflow/workflow-view-data
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var label = this.node.label;
|
||||
var title;
|
||||
if(label) {
|
||||
title = label;
|
||||
} else {
|
||||
title = this.node.name;
|
||||
}
|
||||
this.$el.find(".nodeTitle").text(title);
|
||||
this.renderToolErrors();
|
||||
this.$el.css( "width", Math.min(250, Math.max( this.$el.width(), this.output_width )));
|
||||
},
|
||||
|
||||
@@ -7,7 +7,9 @@ define([
|
||||
'mvc/workflow/workflow-node',
|
||||
'mvc/tool/tools-form-workflow',
|
||||
'utils/async-save-text',
|
||||
], function( Utils, Globals, Workflow, WorkflowCanvas, Node, ToolsForm, async_save_text ){
|
||||
'libs/toastr',
|
||||
'ui/editable-text'
|
||||
], function( Utils, Globals, Workflow, WorkflowCanvas, Node, ToolsForm, async_save_text, Toastr ){
|
||||
|
||||
// Reset tool search to start state.
|
||||
function reset_tool_search( initValue ) {
|
||||
@@ -44,6 +46,21 @@ function reset_tool_search( initValue ) {
|
||||
}
|
||||
}
|
||||
|
||||
NODE_ICONS = {
|
||||
'tool': 'fa-wrench',
|
||||
'data_input': 'fa-file-o',
|
||||
'data_collection_input': 'fa-folder-o',
|
||||
'pause': 'fa-pause'
|
||||
}
|
||||
|
||||
add_node_icon = function($to_el, nodeType) {
|
||||
var iconStyle = NODE_ICONS[nodeType];
|
||||
if(iconStyle) {
|
||||
var $icon = $('<i class="icon fa"> </i>').addClass(iconStyle);
|
||||
$to_el.before($icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Really a shell of a real backbone view, but refactoring in the right
|
||||
// direction I think.
|
||||
@@ -61,7 +78,33 @@ EditorFormView = Backbone.View.extend({
|
||||
this.setElement($el);
|
||||
|
||||
if (node && node.id != 'no-node') {
|
||||
$el.find('.toolForm:first').after(this._genericStepAttributesTemplate( node ));
|
||||
$el.find('table:first').after(this._genericStepAttributesTemplate( node ));
|
||||
var nodeType = node.type;
|
||||
add_node_icon($el.find('.portlet-title-text'), nodeType);
|
||||
var $titleText = $el.find(".portlet-title-text");
|
||||
$titleText.data('last-value', $titleText.text());
|
||||
$titleText.make_text_editable({
|
||||
on_finish: function( newLabel ){
|
||||
var lastValue = $titleText.data("last-value");
|
||||
if( newLabel == lastValue ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var workflow = workflowView.workflow;
|
||||
if( workflow.attemptUpdateNodeLabel( node, newLabel ) ) {
|
||||
$el.find("input[name='label']").val(newLabel);
|
||||
$titleText.data("last-value", newLabel);
|
||||
$el.find('form').submit();
|
||||
if(newLabel == "") {
|
||||
// If label unset restore default name as title.
|
||||
$titleText.text(node.name);
|
||||
}
|
||||
} else {
|
||||
Toastr.warning("Step label " + newLabel + " already exists, cannot update label.");
|
||||
$titleText.text(lastValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
($el.find( 'form' ).length > 0) && $el.find( 'form' ).ajaxForm( {
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
@@ -525,12 +568,10 @@ EditorFormView = Backbone.View.extend({
|
||||
cc.css( { left: left, top: top } );
|
||||
},
|
||||
|
||||
// Add a new step to the workflow by tool id
|
||||
add_node_for_tool: function ( id, title ) {
|
||||
node = this.workflow.create_node( 'tool', title, id );
|
||||
_moduleInitAjax: function(node, request_data) {
|
||||
$.ajax( {
|
||||
url: this.urls.get_new_module_info,
|
||||
data: { type: "tool", tool_id: id, "_": "true" },
|
||||
data: request_data,
|
||||
global: false,
|
||||
dataType: "json",
|
||||
success: function( data ) {
|
||||
@@ -546,23 +587,21 @@ EditorFormView = Backbone.View.extend({
|
||||
});
|
||||
},
|
||||
|
||||
// Add a new step to the workflow by tool id
|
||||
add_node_for_tool: function ( id, title ) {
|
||||
node = this.workflow.create_node( 'tool', title, id );
|
||||
this._moduleInitAjax(node, { type: "tool", content_id: id, "_": "true" });
|
||||
},
|
||||
|
||||
// Add a new step to the workflow by tool id
|
||||
add_node_for_subworkflow: function ( id, title ) {
|
||||
node = this.workflow.create_node( 'subworkflow', title, id );
|
||||
this._moduleInitAjax(node, { type: "subworkflow", subworkflow_id: id, "_": "true" });
|
||||
},
|
||||
|
||||
add_node_for_module: function ( type, title ) {
|
||||
node = this.workflow.create_node( type, title );
|
||||
$.ajax( {
|
||||
url: this.urls.get_new_module_info,
|
||||
data: { type: type, "_": "true" },
|
||||
dataType: "json",
|
||||
success: function( data ) {
|
||||
node.init_field_data( data );
|
||||
},
|
||||
error: function( x, e ) {
|
||||
var m = "error loading field data"
|
||||
if ( x.status == 0 ) {
|
||||
m += ", server unavailable"
|
||||
}
|
||||
node.error( m );
|
||||
}
|
||||
});
|
||||
this._moduleInitAjax(node, { type: type, "_": "true" });
|
||||
},
|
||||
|
||||
// This function preloads how to display known pja's.
|
||||
@@ -704,22 +743,27 @@ EditorFormView = Backbone.View.extend({
|
||||
return ( this.type_to_type[child] ) && ( parent in this.type_to_type[child] );
|
||||
},
|
||||
|
||||
prebuildNode: function ( type, title_text, tool_id ) {
|
||||
$newNodeElement: function(type, title_text) {
|
||||
var $f = $("<div class='toolForm toolFormInCanvas'></div>");
|
||||
var $title = $("<div class='toolFormTitle unselectable'><span class='nodeTitle'>" + title_text + "</div></div>" );
|
||||
add_node_icon($title.find('.nodeTitle'), type);
|
||||
$f.append( $title );
|
||||
$f.css( "left", $(window).scrollLeft() + 20 );
|
||||
$f.css( "top", $(window).scrollTop() + 20 );
|
||||
var $b = $("<div class='toolFormBody'></div>");
|
||||
$f.append($b);
|
||||
return $f
|
||||
},
|
||||
|
||||
prebuildNode: function ( type, title_text, content_id ) {
|
||||
var self = this;
|
||||
var f = $("<div class='toolForm toolFormInCanvas'></div>");
|
||||
var node = new Node( this, { element: f } );
|
||||
var $f = this.$newNodeElement( type, title_text );
|
||||
var node = new Node( this, { element: $f } );
|
||||
node.type = type;
|
||||
if ( type == 'tool' ) {
|
||||
node.tool_id = tool_id;
|
||||
}
|
||||
var title = $("<div class='toolFormTitle unselectable'>" + title_text + "</div>" );
|
||||
f.append( title );
|
||||
f.css( "left", $(window).scrollLeft() + 20 ); f.css( "top", $(window).scrollTop() + 20 );
|
||||
var b = $("<div class='toolFormBody'></div>");
|
||||
node.content_id = content_id;
|
||||
var tmp = "<div><img height='16' align='middle' src='" + Galaxy.root + "static/images/loading_small_white_bg.gif'/> loading tool info...</div>";
|
||||
b.append( tmp );
|
||||
$f.find(".toolFormBody").append(tmp);
|
||||
node.form_html = tmp;
|
||||
f.append( b );
|
||||
// Fix width to computed width
|
||||
// Now add floats
|
||||
var buttons = $("<div class='buttons' style='float: right;'></div>");
|
||||
@@ -727,17 +771,17 @@ EditorFormView = Backbone.View.extend({
|
||||
node.destroy();
|
||||
}));
|
||||
// Place inside container
|
||||
f.appendTo( "#canvas-container" );
|
||||
$f.appendTo( "#canvas-container" );
|
||||
// Position in container
|
||||
var o = $("#canvas-container").position();
|
||||
var p = $("#canvas-container").parent();
|
||||
var width = f.width();
|
||||
var height = f.height();
|
||||
f.css( { left: ( - o.left ) + ( p.width() / 2 ) - ( width / 2 ), top: ( - o.top ) + ( p.height() / 2 ) - ( height / 2 ) } );
|
||||
buttons.prependTo( title );
|
||||
var width = $f.width();
|
||||
var height = $f.height();
|
||||
$f.css( { left: ( - o.left ) + ( p.width() / 2 ) - ( width / 2 ), top: ( - o.top ) + ( p.height() / 2 ) - ( height / 2 ) } );
|
||||
buttons.prependTo( $f.find(".toolFormTitle" ) );
|
||||
width += ( buttons.width() + 10 );
|
||||
f.css( "width", width );
|
||||
$(f).bind( "dragstart", function() {
|
||||
$f.css( "width", width );
|
||||
$f.bind( "dragstart", function() {
|
||||
self.workflow.activate_node( node );
|
||||
}).bind( "dragend", function() {
|
||||
self.workflow.node_changed( this );
|
||||
|
||||
@@ -112,6 +112,16 @@ def get_class( class_name ):
|
||||
return item_class
|
||||
|
||||
|
||||
def decode_id(app, id):
|
||||
try:
|
||||
# note: use str - occasionally a fully numeric id will be placed in post body and parsed as int via JSON
|
||||
# resulting in error for valid id
|
||||
return app.security.decode_id( str( id ) )
|
||||
except ( ValueError, TypeError ):
|
||||
msg = "Malformed id ( %s ) specified, unable to decode" % ( str( id ) )
|
||||
raise exceptions.MalformedId( msg, id=str( id ) )
|
||||
|
||||
|
||||
def get_object( trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None ):
|
||||
"""
|
||||
Convenience method to get a model object with the specified checks. This is
|
||||
@@ -119,11 +129,7 @@ def get_object( trans, id, class_name, check_ownership=False, check_accessible=F
|
||||
controller mixin code - however whenever possible the managers for a
|
||||
particular model should be used to load objects.
|
||||
"""
|
||||
try:
|
||||
decoded_id = trans.security.decode_id( id )
|
||||
except:
|
||||
raise exceptions.MessageException( "Malformed %s id ( %s ) specified, unable to decode"
|
||||
% ( class_name, str( id ) ), type='error' )
|
||||
decoded_id = decode_id(trans.app, id)
|
||||
try:
|
||||
item_class = get_class( class_name )
|
||||
assert item_class is not None
|
||||
|
||||
@@ -2,12 +2,18 @@ from __future__ import absolute_import
|
||||
|
||||
from collections import namedtuple
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import and_
|
||||
|
||||
from galaxy import model
|
||||
from galaxy import util
|
||||
from galaxy import exceptions
|
||||
from galaxy.model.item_attrs import UsesAnnotations
|
||||
from galaxy.workflow import modules
|
||||
|
||||
from .base import decode_id
|
||||
|
||||
# For WorkflowContentManager
|
||||
from galaxy.util.sanitize_html import sanitize_html
|
||||
from galaxy.workflow.steps import attach_ordered_steps
|
||||
@@ -26,6 +32,38 @@ class WorkflowsManager( object ):
|
||||
def __init__( self, app ):
|
||||
self.app = app
|
||||
|
||||
def get_stored_workflow( self, trans, workflow_id ):
|
||||
""" Use a supplied ID (UUID or encoded stored workflow ID) to find
|
||||
a workflow.
|
||||
"""
|
||||
if util.is_uuid(workflow_id):
|
||||
# see if they have passed in the UUID for a workflow that is attached to a stored workflow
|
||||
workflow_uuid = uuid.UUID(workflow_id)
|
||||
stored_workflow = trans.sa_session.query(trans.app.model.StoredWorkflow).filter( and_(
|
||||
trans.app.model.StoredWorkflow.latest_workflow_id == trans.app.model.Workflow.id,
|
||||
trans.app.model.Workflow.uuid == workflow_uuid
|
||||
)).first()
|
||||
if stored_workflow is None:
|
||||
raise exceptions.ObjectNotFound( "Workflow not found: %s" % workflow_id )
|
||||
else:
|
||||
workflow_id = decode_id( self.app, workflow_id )
|
||||
query = trans.sa_session.query( trans.app.model.StoredWorkflow )
|
||||
stored_workflow = query.get( workflow_id )
|
||||
if stored_workflow is None:
|
||||
raise exceptions.ObjectNotFound( "No such workflow found." )
|
||||
return stored_workflow
|
||||
|
||||
def get_stored_accessible_workflow( self, trans, workflow_id ):
|
||||
stored_workflow = self.get_stored_workflow( trans, workflow_id )
|
||||
|
||||
# check to see if user has permissions to selected workflow
|
||||
if stored_workflow.user != trans.user and not trans.user_is_admin():
|
||||
if trans.sa_session.query(trans.app.model.StoredWorkflowUserShareAssociation).filter_by(user=trans.user, stored_workflow=stored_workflow).count() == 0:
|
||||
message = "Workflow is not owned by or shared with current user"
|
||||
raise exceptions.ItemAccessibilityException( message )
|
||||
|
||||
return stored_workflow
|
||||
|
||||
def check_security( self, trans, has_workflow, check_ownership=True, check_accessible=True):
|
||||
""" check accessibility or ownership of workflows, storedworkflows, and
|
||||
workflowinvocations. Throw an exception or returns True if user has
|
||||
@@ -352,7 +390,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
step_dict = {
|
||||
'id': step.order_index,
|
||||
'type': module.type,
|
||||
'tool_id': module.get_tool_id(),
|
||||
'tool_id': module.get_content_id(),
|
||||
'name': module.get_name(),
|
||||
'tool_state': module.get_state(),
|
||||
'tooltip': module.get_tooltip( static_path=url_for( '/static' ) ),
|
||||
@@ -395,11 +433,13 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
action_arguments=pja.action_arguments
|
||||
)
|
||||
step_dict['post_job_actions'] = pja_dict
|
||||
# workflow outputs
|
||||
outputs = []
|
||||
for output in step.workflow_outputs:
|
||||
outputs.append({"output_name": output.output_name})
|
||||
step_dict['workflow_outputs'] = outputs
|
||||
|
||||
# workflow outputs
|
||||
outputs = []
|
||||
for output in step.workflow_outputs:
|
||||
outputs.append({"output_name": output.output_name})
|
||||
step_dict['workflow_outputs'] = outputs
|
||||
|
||||
# Encode input connections as dictionary
|
||||
input_conn_dict = {}
|
||||
for conn in input_connections:
|
||||
@@ -453,7 +493,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
step_dict = {
|
||||
'id': step.order_index,
|
||||
'type': module.type,
|
||||
'tool_id': module.get_tool_id(),
|
||||
'tool_id': module.get_content_id(),
|
||||
'tool_version': step.tool_version,
|
||||
'name': module.get_name(),
|
||||
'tool_state': module.get_state( secure=False ),
|
||||
|
||||
@@ -11,7 +11,6 @@ from paste.httpexceptions import HTTPBadRequest, HTTPInternalServerError
|
||||
from paste.httpexceptions import HTTPNotImplemented, HTTPRequestRangeNotSatisfiable
|
||||
from galaxy.exceptions import ItemAccessibilityException, ItemDeletionException, ItemOwnershipException
|
||||
from galaxy.exceptions import MessageException
|
||||
from galaxy import exceptions
|
||||
|
||||
from galaxy import web
|
||||
from galaxy import model
|
||||
@@ -94,13 +93,7 @@ class BaseController( object ):
|
||||
|
||||
# ---- parsing query params
|
||||
def decode_id( self, id ):
|
||||
try:
|
||||
# note: use str - occasionally a fully numeric id will be placed in post body and parsed as int via JSON
|
||||
# resulting in error for valid id
|
||||
return self.app.security.decode_id( str( id ) )
|
||||
except ( ValueError, TypeError ):
|
||||
msg = "Malformed id ( %s ) specified, unable to decode" % ( str( id ) )
|
||||
raise exceptions.MalformedId( msg, id=str( id ) )
|
||||
return managers_base.decode_id( self.app, id )
|
||||
|
||||
def encode_all_ids( self, trans, rval, recursive=False ):
|
||||
"""
|
||||
|
||||
@@ -4,11 +4,10 @@ API operations for Workflows
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import uuid
|
||||
import logging
|
||||
import copy
|
||||
import urllib
|
||||
from sqlalchemy import and_, desc, false, or_, true
|
||||
from sqlalchemy import desc, false, or_, true
|
||||
from galaxy import exceptions, util
|
||||
from galaxy.model.item_attrs import UsesAnnotations
|
||||
from galaxy.managers import histories
|
||||
@@ -570,33 +569,10 @@ class WorkflowsAPIController(BaseAPIController, UsesStoredWorkflowMixin, UsesAnn
|
||||
)
|
||||
|
||||
def __get_stored_accessible_workflow( self, trans, workflow_id ):
|
||||
stored_workflow = self.__get_stored_workflow( trans, workflow_id )
|
||||
|
||||
# check to see if user has permissions to selected workflow
|
||||
if stored_workflow.user != trans.user and not trans.user_is_admin():
|
||||
if trans.sa_session.query(trans.app.model.StoredWorkflowUserShareAssociation).filter_by(user=trans.user, stored_workflow=stored_workflow).count() == 0:
|
||||
message = "Workflow is not owned by or shared with current user"
|
||||
raise exceptions.ItemAccessibilityException( message )
|
||||
|
||||
return stored_workflow
|
||||
return self.workflow_manager.get_stored_accessible_workflow( trans, workflow_id )
|
||||
|
||||
def __get_stored_workflow( self, trans, workflow_id ):
|
||||
if util.is_uuid(workflow_id):
|
||||
# see if they have passed in the UUID for a workflow that is attached to a stored workflow
|
||||
workflow_uuid = uuid.UUID(workflow_id)
|
||||
stored_workflow = trans.sa_session.query(trans.app.model.StoredWorkflow).filter( and_(
|
||||
trans.app.model.StoredWorkflow.latest_workflow_id == trans.app.model.Workflow.id,
|
||||
trans.app.model.Workflow.uuid == workflow_uuid
|
||||
)).first()
|
||||
if stored_workflow is None:
|
||||
raise exceptions.ObjectNotFound( "Workflow not found: %s" % workflow_id )
|
||||
else:
|
||||
workflow_id = self.decode_id( workflow_id )
|
||||
query = trans.sa_session.query( trans.app.model.StoredWorkflow )
|
||||
stored_workflow = query.get( workflow_id )
|
||||
if stored_workflow is None:
|
||||
raise exceptions.ObjectNotFound( "No such workflow found." )
|
||||
return stored_workflow
|
||||
return self.workflow_manager.get_stored_workflow( trans, workflow_id )
|
||||
|
||||
def __encode_invocation( self, trans, invocation, view="element", step_details=False ):
|
||||
return self.encode_all_ids(
|
||||
|
||||
@@ -613,7 +613,7 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix
|
||||
return trans.fill_template( "workflow/editor.mako", stored=stored, annotation=self.get_item_annotation_str( trans.sa_session, trans.user, stored ) )
|
||||
|
||||
@web.json
|
||||
def editor_form_post( self, trans, type='tool', tool_id=None, annotation=None, **incoming ):
|
||||
def editor_form_post( self, trans, type='tool', tool_id=None, annotation=None, label=None, **incoming ):
|
||||
"""
|
||||
Accepts a tool state and incoming values, and generates a new tool
|
||||
form and some additional information, packed into a json dictionary.
|
||||
@@ -625,12 +625,14 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix
|
||||
module = module_factory.from_dict( trans, {
|
||||
'type': type,
|
||||
'tool_id': tool_id,
|
||||
'tool_state': tool_state
|
||||
'tool_state': tool_state,
|
||||
'label': label or None,
|
||||
} )
|
||||
# update module state
|
||||
module.update_state( incoming )
|
||||
if type == 'tool':
|
||||
return {
|
||||
'label': module.label,
|
||||
'tool_state': module.get_state(),
|
||||
'data_inputs': module.get_data_inputs(),
|
||||
'data_outputs': module.get_data_outputs(),
|
||||
@@ -641,6 +643,7 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'label': module.label,
|
||||
'tool_state': module.get_state(),
|
||||
'data_inputs': module.get_data_inputs(),
|
||||
'data_outputs': module.get_data_outputs(),
|
||||
@@ -664,7 +667,7 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix
|
||||
return {
|
||||
'type': module.type,
|
||||
'name': module.get_name(),
|
||||
'tool_id': module.get_tool_id(),
|
||||
'tool_id': module.get_content_id(),
|
||||
'tool_state': module.get_state(),
|
||||
'tool_model': tool_model,
|
||||
'tooltip': module.get_tooltip( static_path=url_for( '/static' ) ),
|
||||
|
||||
@@ -42,7 +42,7 @@ class WorkflowModule( object ):
|
||||
# ---- Creating modules from various representations ---------------------
|
||||
|
||||
@classmethod
|
||||
def new( Class, trans, tool_id=None ):
|
||||
def new( Class, trans, content_id=None ):
|
||||
"""
|
||||
Create a new instance of the module with default state
|
||||
"""
|
||||
@@ -73,7 +73,10 @@ class WorkflowModule( object ):
|
||||
def get_name( self ):
|
||||
return self.name
|
||||
|
||||
def get_tool_id( self ):
|
||||
def get_content_id( self ):
|
||||
""" If this component has an identifier external to the step (such
|
||||
as a tool or another workflow) return the identifier for that content.
|
||||
"""
|
||||
return None
|
||||
|
||||
def get_tooltip( self, static_path='' ):
|
||||
@@ -194,9 +197,10 @@ class WorkflowModule( object ):
|
||||
class SimpleWorkflowModule( WorkflowModule ):
|
||||
|
||||
@classmethod
|
||||
def new( Class, trans, tool_id=None ):
|
||||
def new( Class, trans, content_id=None ):
|
||||
module = Class( trans )
|
||||
module.state = Class.default_state()
|
||||
module.label = None
|
||||
return module
|
||||
|
||||
@classmethod
|
||||
@@ -204,12 +208,14 @@ class SimpleWorkflowModule( WorkflowModule ):
|
||||
module = Class( trans )
|
||||
state = loads( d["tool_state"] )
|
||||
module.recover_state( state )
|
||||
module.label = d.get("label", None) or None
|
||||
return module
|
||||
|
||||
@classmethod
|
||||
def from_workflow_step( Class, trans, step ):
|
||||
module = Class( trans )
|
||||
module.recover_state( step.tool_inputs )
|
||||
module.label = step.label
|
||||
return module
|
||||
|
||||
@classmethod
|
||||
@@ -502,12 +508,13 @@ class ToolModule( WorkflowModule ):
|
||||
self.errors[ tool_id ] = 'Tool unavailable'
|
||||
|
||||
@classmethod
|
||||
def new( Class, trans, tool_id=None ):
|
||||
module = Class( trans, tool_id )
|
||||
def new( Class, trans, content_id=None ):
|
||||
module = Class( trans, content_id )
|
||||
if module.tool is None:
|
||||
error_message = "Attempted to create new workflow module for invalid tool_id, no tool with id - %s." % tool_id
|
||||
error_message = "Attempted to create new workflow module for invalid tool_id, no tool with id - %s." % content_id
|
||||
raise Exception( error_message )
|
||||
module.state = module.tool.new_state( trans )
|
||||
module.label = None
|
||||
return module
|
||||
|
||||
@classmethod
|
||||
@@ -516,6 +523,7 @@ class ToolModule( WorkflowModule ):
|
||||
tool_version = str( d.get( 'tool_version', None ) )
|
||||
module = Class( trans, tool_id, tool_version=tool_version )
|
||||
module.state = galaxy.tools.DefaultToolState()
|
||||
module.label = d.get("label", None) or None
|
||||
if module.tool is not None:
|
||||
message = ""
|
||||
if tool_id != module.tool_id:
|
||||
@@ -560,6 +568,7 @@ class ToolModule( WorkflowModule ):
|
||||
module.recover_state( step.tool_inputs )
|
||||
module.errors = step.tool_errors
|
||||
module.workflow_outputs = step.workflow_outputs
|
||||
module.label = step.label or None
|
||||
pjadict = {}
|
||||
for pja in step.post_job_actions:
|
||||
pjadict[pja.action_type] = pja
|
||||
@@ -623,7 +632,7 @@ class ToolModule( WorkflowModule ):
|
||||
return self.tool.name
|
||||
return 'unavailable'
|
||||
|
||||
def get_tool_id( self ):
|
||||
def get_content_id( self ):
|
||||
return self.tool_id
|
||||
|
||||
def get_tool_version( self ):
|
||||
@@ -735,6 +744,7 @@ class ToolModule( WorkflowModule ):
|
||||
# set. We also need to make sure all datasets have a dummy value
|
||||
# for dependencies to see
|
||||
|
||||
self.label = incoming.get("label", None) or None
|
||||
self.post_job_actions = ActionBox.handle_incoming(incoming)
|
||||
|
||||
make_runtime_key = incoming.get( 'make_runtime', None )
|
||||
@@ -951,13 +961,13 @@ class WorkflowModuleFactory( object ):
|
||||
def __init__( self, module_types ):
|
||||
self.module_types = module_types
|
||||
|
||||
def new( self, trans, type, tool_id=None ):
|
||||
def new( self, trans, type, content_id=None ):
|
||||
"""
|
||||
Return module for type and (optional) tool_id intialized with
|
||||
new / default state.
|
||||
"""
|
||||
assert type in self.module_types
|
||||
return self.module_types[type].new( trans, tool_id )
|
||||
return self.module_types[type].new( trans, content_id )
|
||||
|
||||
def from_dict( self, trans, d, **kwargs ):
|
||||
"""
|
||||
|
||||
@@ -235,8 +235,8 @@ class WorkflowProgress( object ):
|
||||
if not hasattr( step, 'module' ):
|
||||
self.module_injector.inject( step )
|
||||
if step_id not in step_states:
|
||||
template = "Workflow invocation [%s] has no step state for step id [%s]. States are [%s]."
|
||||
message = template % (self.workflow_invocation.id, step_id, step_states)
|
||||
template = "Workflow invocation [%s] has no step state for step id [%s]. States ids are %s."
|
||||
message = template % (self.workflow_invocation.id, step_id, step_states.keys())
|
||||
raise Exception(message)
|
||||
runtime_state = step_states[ step_id ].value
|
||||
step.state = step.module.recover_runtime_state( runtime_state )
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"workflow-view-node.js","sources":["../../../src/mvc/workflow/workflow-view-node.js"],"names":["define","TerminalViews","DataViews","Backbone","View","extend","initialize","options","this","node","output_width","Math","max","$el","width","tool_body","find","remove","newInputsDiv","appendTo","terminalViews","outputTerminlViews","render","renderToolErrors","css","min","tool_errors","addClass","removeClass","$","updateMaxWidth","newWidth","addRule","append","addDataInput","input","body","skipResize","terminalView","name","terminalViewClass","input_type","InputCollectionTerminalView","InputTerminalView","el","terminal","destroy","update","destroyInvalidConnections","terminalElement","inputView","DataInputView","nodeView","ib","prepend","terminalElements","addDataOutput","output","OutputCollectionTerminalView","OutputTerminalView","outputView","DataOutputView","updateDataOutput","outputTerminal","output_terminals"],"mappings":"AAAAA,QAAQ,uCAAwC,mCAAoC,SAAUC,EAAeC,GACzG,MAAOC,UAASC,KAAKC,QACjBC,WAAY,SAAUC,GAClBC,KAAKC,KAAOF,EAAQE,KACpBD,KAAKE,aAAeC,KAAKC,IAAI,IAAKJ,KAAKK,IAAIC,SAC3CN,KAAKO,UAAYP,KAAKK,IAAIG,KAAM,iBAChCR,KAAKO,UAAUC,KAAM,OAAQC,SAC7BT,KAAKU,eAAeC,SAAUX,KAAKO,WACnCP,KAAKY,iBACLZ,KAAKa,uBAGTC,OAAQ,WACJd,KAAKe,mBACLf,KAAKK,IAAIW,IAAK,QAASb,KAAKc,IAAI,IAAKd,KAAKC,IAAKJ,KAAKK,IAAIC,QAASN,KAAKE,iBAG1Ea,iBAAkB,WACTf,KAAKC,KAAKiB,YACXlB,KAAKK,IAAIc,SAAU,mBAEnBnB,KAAKK,IAAIe,YAAa,oBAI9BV,aAAc,WACV,MAAOW,GAAE,+BAGbC,eAAgB,SAAUC,GACtBvB,KAAKE,aAAeC,KAAKC,IAAKJ,KAAKE,aAAcqB,IAGrDC,QAAS,WACLxB,KAAKO,UAAUkB,OAAQJ,EAAG,8BAG9BK,aAAc,SAAUC,EAAOC,GAC3B,GAAIC,IAAa,CACXD,KACFA,EAAO5B,KAAKqB,EAAG,WAGfQ,GAAa,EAEjB,IAAIC,GAAe9B,KAAKY,cAAee,EAAMI,MACzCC,EAA0C,sBAApBL,EAAMM,WAAuCxC,EAAcyC,4BAA8BzC,EAAc0C,iBAKjI,KAJIL,GAAoBA,YAAwBE,KAC5CF,EAAaM,GAAGC,SAASC,UACzBR,EAAe,MAEbA,EAKC,CACH,GAAIO,GAAWP,EAAaM,GAAGC,QAC/BA,GAASE,OAAQZ,GACjBU,EAASG,gCAPTV,GAAe,GAAIE,IACf/B,KAAMD,KAAKC,KACX0B,MAAOA,GAOf3B,MAAKY,cAAee,EAAMI,MAASD,CACnC,IAAIW,GAAkBX,EAAaM,GAC/BM,EAAY,GAAIhD,GAAUiD,eAC1BF,gBAAiBA,EACjBd,MAAOA,EACPiB,SAAU5C,KACV6B,WAAYA,IAEZgB,EAAKH,EAAUrC,GAEnB,OADAuB,GAAKH,OAAQoB,EAAGC,QAAShB,EAAaiB,qBAC/BjB,GAGXkB,cAAe,SAAUC,GACrB,GAAIjB,GAAsBiB,EAAkB,WAAIxD,EAAcyD,6BAA+BzD,EAAc0D,mBACvGrB,EAAe,GAAIE,IACnB/B,KAAMD,KAAKC,KACXgD,OAAQA,IAERG,EAAa,GAAI1D,GAAU2D,gBAC3BJ,OAAUA,EACVR,gBAAmBX,EAAaM,GAChCQ,SAAY5C,MAEhBA,MAAKO,UAAUkB,OAAQ2B,EAAW/C,IAAIoB,OAAQK,EAAaiB,sBAG/DO,iBAAkB,SAAUL,GACxB,GAAIM,GAAiBvD,KAAKC,KAAKuD,iBAAkBP,EAAOlB,KACxDwB,GAAehB,OAAQU"}
|
||||
{"version":3,"file":"workflow-view-node.js","sources":["../../../src/mvc/workflow/workflow-view-node.js"],"names":["define","TerminalViews","DataViews","Backbone","View","extend","initialize","options","this","node","output_width","Math","max","$el","width","tool_body","find","remove","newInputsDiv","appendTo","terminalViews","outputTerminlViews","render","title","label","name","text","renderToolErrors","css","min","tool_errors","addClass","removeClass","$","updateMaxWidth","newWidth","addRule","append","addDataInput","input","body","skipResize","terminalView","terminalViewClass","input_type","InputCollectionTerminalView","InputTerminalView","el","terminal","destroy","update","destroyInvalidConnections","terminalElement","inputView","DataInputView","nodeView","ib","prepend","terminalElements","addDataOutput","output","OutputCollectionTerminalView","OutputTerminalView","outputView","DataOutputView","updateDataOutput","outputTerminal","output_terminals"],"mappings":"AAAAA,QAAQ,uCAAwC,mCAAoC,SAAUC,EAAeC,GACzG,MAAOC,UAASC,KAAKC,QACjBC,WAAY,SAAUC,GAClBC,KAAKC,KAAOF,EAAQE,KACpBD,KAAKE,aAAeC,KAAKC,IAAI,IAAKJ,KAAKK,IAAIC,SAC3CN,KAAKO,UAAYP,KAAKK,IAAIG,KAAM,iBAChCR,KAAKO,UAAUC,KAAM,OAAQC,SAC7BT,KAAKU,eAAeC,SAAUX,KAAKO,WACnCP,KAAKY,iBACLZ,KAAKa,uBAGTC,OAAQ,WACJ,GACIC,GADAC,EAAQhB,KAAKC,KAAKe,KAGlBD,GADDC,EACSA,EAEAhB,KAAKC,KAAKgB,KAEtBjB,KAAKK,IAAIG,KAAK,cAAcU,KAAKH,GACjCf,KAAKmB,mBACLnB,KAAKK,IAAIe,IAAK,QAASjB,KAAKkB,IAAI,IAAKlB,KAAKC,IAAKJ,KAAKK,IAAIC,QAASN,KAAKE,iBAG1EiB,iBAAkB,WACTnB,KAAKC,KAAKqB,YACXtB,KAAKK,IAAIkB,SAAU,mBAEnBvB,KAAKK,IAAImB,YAAa,oBAI9Bd,aAAc,WACV,MAAOe,GAAE,+BAGbC,eAAgB,SAAUC,GACtB3B,KAAKE,aAAeC,KAAKC,IAAKJ,KAAKE,aAAcyB,IAGrDC,QAAS,WACL5B,KAAKO,UAAUsB,OAAQJ,EAAG,8BAG9BK,aAAc,SAAUC,EAAOC,GAC3B,GAAIC,IAAa,CACXD,KACFA,EAAOhC,KAAKyB,EAAG,WAGfQ,GAAa,EAEjB,IAAIC,GAAelC,KAAKY,cAAemB,EAAMd,MACzCkB,EAA0C,sBAApBJ,EAAMK,WAAuC3C,EAAc4C,4BAA8B5C,EAAc6C,iBAKjI,KAJIJ,GAAoBA,YAAwBC,KAC5CD,EAAaK,GAAGC,SAASC,UACzBP,EAAe,MAEbA,EAKC,CACH,GAAIM,GAAWN,EAAaK,GAAGC,QAC/BA,GAASE,OAAQX,GACjBS,EAASG,gCAPTT,GAAe,GAAIC,IACflC,KAAMD,KAAKC,KACX8B,MAAOA,GAOf/B,MAAKY,cAAemB,EAAMd,MAASiB,CACnC,IAAIU,GAAkBV,EAAaK,GAC/BM,EAAY,GAAInD,GAAUoD,eAC1BF,gBAAiBA,EACjBb,MAAOA,EACPgB,SAAU/C,KACViC,WAAYA,IAEZe,EAAKH,EAAUxC,GAEnB,OADA2B,GAAKH,OAAQmB,EAAGC,QAASf,EAAagB,qBAC/BhB,GAGXiB,cAAe,SAAUC,GACrB,GAAIjB,GAAsBiB,EAAkB,WAAI3D,EAAc4D,6BAA+B5D,EAAc6D,mBACvGpB,EAAe,GAAIC,IACnBlC,KAAMD,KAAKC,KACXmD,OAAQA,IAERG,EAAa,GAAI7D,GAAU8D,gBAC3BJ,OAAUA,EACVR,gBAAmBV,EAAaK,GAChCQ,SAAY/C,MAEhBA,MAAKO,UAAUsB,OAAQ0B,EAAWlD,IAAIwB,OAAQK,EAAagB,sBAG/DO,iBAAkB,SAAUL,GACxB,GAAIM,GAAiB1D,KAAKC,KAAK0D,iBAAkBP,EAAOnC,KACxDyC,GAAehB,OAAQU"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
|
||||
define(["mvc/workflow/workflow-view-node"],function(a){var b=Backbone.Model.extend({initialize:function(a,b){this.app=a,this.element=b.element,this.input_terminals={},this.output_terminals={},this.tool_errors={},this.workflow_outputs=[]},getWorkflowOutput:function(a){return _.findWhere(this.workflow_outputs,{output_name:a})},isWorkflowOutput:function(a){return void 0!=this.getWorkflowOutput(a)},removeWorkflowOutput:function(a){for(;this.isWorkflowOutput(a);)this.workflow_outputs.splice(this.getWorkflowOutput(a),1)},addWorkflowOutput:function(a){this.isWorkflowOutput(a)||this.workflow_outputs.push({output_name:a})},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(a){var b=[];return $.each(a,function(a,c){c.connectors.length>0&&b.push(c)}),b},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a)if(a[b].connectors.length>0)return!0;return!1},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var a=this.input_terminals;for(var b in a){var c=a[b];if(c.connectors.length>0&&c.isMappedOver())return!0}return!1},_connectedMappedTerminals:function(a){var b=[];return $.each(a,function(a,c){var d=c.mapOver();d.isCollection&&c.connectors.length>0&&b.push(c)}),b},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(a){var b=[];return $.each(a,function(a,c){var d=c.mapOver();d.isCollection&&b.push(c)}),b},hasMappedOverInputTerminals:function(){var a=!1;return _.each(this.input_terminals,function(b){var c=b.mapOver();c.isCollection&&(a=!0)}),a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()}),$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()}),$.each(this.output_terminals,function(a,b){b.destroy()}),this.app.workflow.remove_node(this),$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);!function(b){b.removeChild(a),b.appendChild(a)}(a.parentNode),$(a).removeClass("toolForm-active")},init_field_data:function(b){b.type&&(this.type=b.type),this.name=b.name,this.form_html=b.form_html,this.tool_state=b.tool_state,this.tool_errors=b.tool_errors,this.tooltip=b.tooltip?b.tooltip:"",this.annotation=b.annotation,this.post_job_actions=b.post_job_actions?b.post_job_actions:{},this.label=b.label,this.uuid=b.uuid,this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var c=this,d=new a({el:this.element[0],node:c});c.nodeView=d,$.each(b.data_inputs,function(a,b){d.addDataInput(b)}),b.data_inputs.length>0&&b.data_outputs.length>0&&d.addRule(),$.each(b.data_outputs,function(a,b){d.addDataOutput(b)}),d.render(),this.app.workflow.node_changed(this,!0)},update_field_data:function(a){var b=this;if(nodeView=b.nodeView,this.tool_state=a.tool_state,this.form_html=a.form_html,this.tool_errors=a.tool_errors,this.annotation=a.annotation,"post_job_actions"in a){var c=$.parseJSON(a.post_job_actions);this.post_job_actions=c?c:{}}b.nodeView.renderToolErrors();var d=nodeView.$("div.inputs"),e=nodeView.newInputsDiv(),f={};_.each(a.data_inputs,function(a){var c=b.nodeView.addDataInput(a,e);f[a.name]=c}),_.each(_.difference(_.values(nodeView.terminalViews),_.values(f)),function(a){a.el.terminal.destroy()}),nodeView.terminalViews=f,1==a.data_outputs.length&&"collection_type"in a.data_outputs[0]&&nodeView.updateDataOutput(a.data_outputs[0]),d.replaceWith(e),this.markChanged(),this.redraw()},error:function(a){var b=$(this.element).find(".toolFormBody");b.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+a+"</div>";this.form_html=c,b.html(c),this.app.workflow.node_changed(this)},markChanged:function(){this.app.workflow.node_changed(this)}});return b});
|
||||
define(["mvc/workflow/workflow-view-node"],function(a){var b=Backbone.Model.extend({initialize:function(a,b){this.app=a,this.element=b.element,this.input_terminals={},this.output_terminals={},this.tool_errors={},this.workflow_outputs=[]},getWorkflowOutput:function(a){return _.findWhere(this.workflow_outputs,{output_name:a})},isWorkflowOutput:function(a){return void 0!=this.getWorkflowOutput(a)},removeWorkflowOutput:function(a){for(;this.isWorkflowOutput(a);)this.workflow_outputs.splice(this.getWorkflowOutput(a),1)},addWorkflowOutput:function(a){this.isWorkflowOutput(a)||this.workflow_outputs.push({output_name:a})},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(a){var b=[];return $.each(a,function(a,c){c.connectors.length>0&&b.push(c)}),b},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a)if(a[b].connectors.length>0)return!0;return!1},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var a=this.input_terminals;for(var b in a){var c=a[b];if(c.connectors.length>0&&c.isMappedOver())return!0}return!1},_connectedMappedTerminals:function(a){var b=[];return $.each(a,function(a,c){var d=c.mapOver();d.isCollection&&c.connectors.length>0&&b.push(c)}),b},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(a){var b=[];return $.each(a,function(a,c){var d=c.mapOver();d.isCollection&&b.push(c)}),b},hasMappedOverInputTerminals:function(){var a=!1;return _.each(this.input_terminals,function(b){var c=b.mapOver();c.isCollection&&(a=!0)}),a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()}),$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()}),$.each(this.output_terminals,function(a,b){b.destroy()}),this.app.workflow.remove_node(this),$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);!function(b){b.removeChild(a),b.appendChild(a)}(a.parentNode),$(a).removeClass("toolForm-active")},setLabel:function(a){this.app.workflow.updateNodeLabel(this.label,a),this.label=a||null},init_field_data:function(b){b.type&&(this.type=b.type),this.name=b.name,this.form_html=b.form_html,this.tool_state=b.tool_state,this.tool_errors=b.tool_errors,this.tooltip=b.tooltip?b.tooltip:"",this.annotation=b.annotation,this.post_job_actions=b.post_job_actions?b.post_job_actions:{},this.setLabel(b.label),this.uuid=b.uuid,this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var c=this,d=new a({el:this.element[0],node:c});c.nodeView=d,$.each(b.data_inputs,function(a,b){d.addDataInput(b)}),b.data_inputs.length>0&&b.data_outputs.length>0&&d.addRule(),$.each(b.data_outputs,function(a,b){d.addDataOutput(b)}),d.render(),this.app.workflow.node_changed(this,!0)},update_field_data:function(a){var b=this;if(nodeView=b.nodeView,this.tool_state=a.tool_state,this.form_html=a.form_html,this.tool_errors=a.tool_errors,this.annotation=a.annotation,this.setLabel(a.label),"post_job_actions"in a){var c=$.parseJSON(a.post_job_actions);this.post_job_actions=c?c:{}}b.nodeView.renderToolErrors();var d=nodeView.$("div.inputs"),e=nodeView.newInputsDiv(),f={};_.each(a.data_inputs,function(a){var c=b.nodeView.addDataInput(a,e);f[a.name]=c}),_.each(_.difference(_.values(nodeView.terminalViews),_.values(f)),function(a){a.el.terminal.destroy()}),nodeView.terminalViews=f,b.nodeView.render(),1==a.data_outputs.length&&"collection_type"in a.data_outputs[0]&&nodeView.updateDataOutput(a.data_outputs[0]),d.replaceWith(e),this.markChanged(),this.redraw()},error:function(a){var b=$(this.element).find(".toolFormBody");b.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+a+"</div>";this.form_html=c,b.html(c),this.app.workflow.node_changed(this)},markChanged:function(){this.app.workflow.node_changed(this)}});return b});
|
||||
//# sourceMappingURL=../../../maps/mvc/workflow/workflow-node.js.map
|
||||
@@ -1,2 +1,2 @@
|
||||
define(["mvc/workflow/workflow-view-terminals","mvc/workflow/workflow-view-data"],function(a,b){return Backbone.View.extend({initialize:function(a){this.node=a.node,this.output_width=Math.max(150,this.$el.width()),this.tool_body=this.$el.find(".toolFormBody"),this.tool_body.find("div").remove(),this.newInputsDiv().appendTo(this.tool_body),this.terminalViews={},this.outputTerminlViews={}},render:function(){this.renderToolErrors(),this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){this.node.tool_errors?this.$el.addClass("tool-node-error"):this.$el.removeClass("tool-node-error")},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(c,d){var e=!0;d||(d=this.$(".inputs"),e=!1);var f=this.terminalViews[c.name],g="dataset_collection"==c.input_type?a.InputCollectionTerminalView:a.InputTerminalView;if(!f||f instanceof g||(f.el.terminal.destroy(),f=null),f){var h=f.el.terminal;h.update(c),h.destroyInvalidConnections()}else f=new g({node:this.node,input:c});this.terminalViews[c.name]=f;var i=f.el,j=new b.DataInputView({terminalElement:i,input:c,nodeView:this,skipResize:e}),k=j.$el;return d.append(k.prepend(f.terminalElements())),f},addDataOutput:function(c){var d=c.collection?a.OutputCollectionTerminalView:a.OutputTerminalView,e=new d({node:this.node,output:c}),f=new b.DataOutputView({output:c,terminalElement:e.el,nodeView:this});this.tool_body.append(f.$el.append(e.terminalElements()))},updateDataOutput:function(a){var b=this.node.output_terminals[a.name];b.update(a)}})});
|
||||
define(["mvc/workflow/workflow-view-terminals","mvc/workflow/workflow-view-data"],function(a,b){return Backbone.View.extend({initialize:function(a){this.node=a.node,this.output_width=Math.max(150,this.$el.width()),this.tool_body=this.$el.find(".toolFormBody"),this.tool_body.find("div").remove(),this.newInputsDiv().appendTo(this.tool_body),this.terminalViews={},this.outputTerminlViews={}},render:function(){var a,b=this.node.label;a=b?b:this.node.name,this.$el.find(".nodeTitle").text(a),this.renderToolErrors(),this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){this.node.tool_errors?this.$el.addClass("tool-node-error"):this.$el.removeClass("tool-node-error")},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(c,d){var e=!0;d||(d=this.$(".inputs"),e=!1);var f=this.terminalViews[c.name],g="dataset_collection"==c.input_type?a.InputCollectionTerminalView:a.InputTerminalView;if(!f||f instanceof g||(f.el.terminal.destroy(),f=null),f){var h=f.el.terminal;h.update(c),h.destroyInvalidConnections()}else f=new g({node:this.node,input:c});this.terminalViews[c.name]=f;var i=f.el,j=new b.DataInputView({terminalElement:i,input:c,nodeView:this,skipResize:e}),k=j.$el;return d.append(k.prepend(f.terminalElements())),f},addDataOutput:function(c){var d=c.collection?a.OutputCollectionTerminalView:a.OutputTerminalView,e=new d({node:this.node,output:c}),f=new b.DataOutputView({output:c,terminalElement:e.el,nodeView:this});this.tool_body.append(f.$el.append(e.terminalElements()))},updateDataOutput:function(a){var b=this.node.output_terminals[a.name];b.update(a)}})});
|
||||
//# sourceMappingURL=../../../maps/mvc/workflow/workflow-view-node.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,12 +1,31 @@
|
||||
<form name="${form.name}" action="${h.url_for( controller='workflow', action='editor_form_post' )}" method="post">
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">${form.title}</div>
|
||||
<div class="toolFormBody">
|
||||
<%
|
||||
from xml.sax.saxutils import escape
|
||||
label = module.label
|
||||
if label is not None:
|
||||
title = label
|
||||
else:
|
||||
title = form.title
|
||||
%>
|
||||
<div class="ui-portlet-narrow">
|
||||
<div class="portlet-header">
|
||||
<div class="portlet-title">
|
||||
<span class="portlet-title-text"><b>${title}</b></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="portlet-content">
|
||||
<div class="content">
|
||||
<div class="ui-margin-top"></div>
|
||||
<div>
|
||||
<input type="hidden" name="type" value="${module.type}" />
|
||||
<input type="hidden" name="label" value="${escape(label or '')}" />
|
||||
<table class="ui-table-plain">
|
||||
<thead></thead>
|
||||
<tbody>
|
||||
%if form.inputs:
|
||||
%for input in form.inputs:
|
||||
<%
|
||||
cls = "form-row"
|
||||
cls = "section-row"
|
||||
if input.error:
|
||||
cls += " form-row-error"
|
||||
extra_attributes = ""
|
||||
@@ -16,38 +35,37 @@
|
||||
if input.type:
|
||||
type_attribute = "type=\"input.type\""
|
||||
%>
|
||||
<div class="${cls}">
|
||||
<label>
|
||||
<tr class="${cls}"><td><div class="ui-table-form-element">
|
||||
<div class="ui-table-form-title">
|
||||
${input.label}:
|
||||
</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
|
||||
</div>
|
||||
<div class="ui-table-form-field" style="display: block;">
|
||||
<input ${type_attribute} name="${input.name | h}" value="${input.value | h}" size="30" ${extra_attributes}>
|
||||
%if hasattr( input, "body_html" ):
|
||||
${input.body_html()}
|
||||
</input>
|
||||
%endif
|
||||
</div>
|
||||
%if input.error:
|
||||
<div style="float: left; color: red; font-weight: bold; padding-top: 1px; padding-bottom: 3px;">
|
||||
<div style="width: 300px;"><img style="vertical-align: middle;" src="${h.url_for('/static/style/error_small.png')}"> <span style="vertical-align: middle;">${input.error}</span></div>
|
||||
</div>
|
||||
%endif
|
||||
%if input.error:
|
||||
<div style="float: left; color: red; font-weight: bold; padding-top: 1px; padding-bottom: 3px;">
|
||||
<div style="width: 300px;"><img style="vertical-align: middle;" src="${h.url_for('/static/style/error_small.png')}"> <span style="vertical-align: middle;">${input.error}</span></div>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
%if input.help:
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
${input.help}
|
||||
</div>
|
||||
%endif
|
||||
%if input.help:
|
||||
<div class="ui-table-form-info">
|
||||
${input.help}
|
||||
</div>
|
||||
%endif
|
||||
</div><!-- ui-table-form-field -->
|
||||
|
||||
<div style="clear: both"></div>
|
||||
|
||||
</div>
|
||||
</div></td></tr>
|
||||
%endfor
|
||||
%else:
|
||||
<div class="form-row"><i>No options</i></div>
|
||||
%endif
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div> <!-- content -->
|
||||
</div><!-- portlet-content -->
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -91,7 +91,7 @@ def _python_to_workflow(as_python, conversion_context):
|
||||
as_python["steps"] = steps_as_dict
|
||||
steps = steps_as_dict
|
||||
|
||||
for i, step in steps.iteritems():
|
||||
for step in steps.itervalues():
|
||||
step_type = step.get("type", None)
|
||||
if "run" in step:
|
||||
if step_type is not None:
|
||||
@@ -113,7 +113,7 @@ def _python_to_workflow(as_python, conversion_context):
|
||||
run_to_step_function(conversion_context, step, run_action)
|
||||
del step["run"]
|
||||
|
||||
for i, step in steps.iteritems():
|
||||
for step in steps.itervalues():
|
||||
step_type = step.get("type", "tool")
|
||||
step_type = STEP_TYPE_ALIASES.get(step_type, step_type)
|
||||
if step_type not in STEP_TYPES:
|
||||
@@ -148,14 +148,14 @@ def convert_inputs_to_steps(inputs, steps):
|
||||
if "label" not in input_def and "id" not in input_def:
|
||||
raise Exception("Input must define a label.")
|
||||
|
||||
raw_label = __rip_value(input_def, "label")
|
||||
raw_id = __rip_value(input_def, "id")
|
||||
raw_label = input_def.pop("label", None)
|
||||
raw_id = input_def.pop("id", None)
|
||||
label = raw_label or raw_id
|
||||
|
||||
if not label:
|
||||
raise Exception("Input label must not be empty.")
|
||||
|
||||
input_type = __rip_value(input_def, "type", "data")
|
||||
input_type = input_def.pop("type", "data")
|
||||
if input_type in ["File", "data", "data_input"]:
|
||||
step_type = "data_input"
|
||||
elif input_type in ["collection", "data_collection", "data_collection_input"]:
|
||||
@@ -380,15 +380,6 @@ def __join_prefix(prefix, key):
|
||||
return new_key
|
||||
|
||||
|
||||
def __rip_value(input_def, key, default=None):
|
||||
if key in input_def:
|
||||
value = input_def[key]
|
||||
del input_def[key]
|
||||
else:
|
||||
value = default
|
||||
return value
|
||||
|
||||
|
||||
def __init_connect_dict(step):
|
||||
if "connect" not in step:
|
||||
step["connect"] = {}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
define([
|
||||
"sinon-qunit",
|
||||
"test-app",
|
||||
'utils/utils',
|
||||
"mvc/workflow/workflow-view",
|
||||
"mvc/workflow/workflow-node",
|
||||
"mvc/workflow/workflow-view-node",
|
||||
@@ -11,6 +12,7 @@ define([
|
||||
], function(
|
||||
sinon,
|
||||
testApp,
|
||||
Utils,
|
||||
App,
|
||||
Node,
|
||||
NodeView,
|
||||
@@ -296,8 +298,8 @@ define([
|
||||
setup: function() {
|
||||
this.input_terminal = { destroy: sinon.spy(), redraw: sinon.spy() };
|
||||
this.output_terminal = { destroy: sinon.spy(), redraw: sinon.spy() };
|
||||
this.element = $("<div><div class='toolFormBody'></div></div>");
|
||||
this.app = create_app();
|
||||
this.element = this.app.$newNodeElement( "tool", "newnode" );
|
||||
this.node = new Node( this.app, { element: this.element } );
|
||||
this.node.input_terminals.i1 = this.input_terminal;
|
||||
this.node.output_terminals.o1 = this.output_terminal;
|
||||
@@ -311,22 +313,24 @@ define([
|
||||
f();
|
||||
ok( node_changed_spy.calledWith( node ) );
|
||||
},
|
||||
init_field_data_simple: function() {
|
||||
var data = {
|
||||
init_field_data_simple: function(option_overrides) {
|
||||
var data = Utils.merge(option_overrides, {
|
||||
data_inputs: [ {name: "input1", extensions: [ "data" ] } ],
|
||||
data_outputs: [ {name: "output1", extensions: [ "data" ] } ],
|
||||
};
|
||||
label: null,
|
||||
});
|
||||
this.node.init_field_data( data );
|
||||
},
|
||||
update_field_data_with_new_input: function() {
|
||||
var new_data = {
|
||||
update_field_data_with_new_input: function(option_overrides) {
|
||||
var new_data = Utils.merge(option_overrides, {
|
||||
data_inputs: [
|
||||
{ name: "input1", extensions: [ "data" ] },
|
||||
{ name: "extra_0|input1", extensions: [ "data" ] },
|
||||
],
|
||||
data_outputs: [ {name: "output1", extensions: [ "data" ] } ],
|
||||
post_job_actions: "{}"
|
||||
};
|
||||
post_job_actions: "{}",
|
||||
label: "New Label"
|
||||
});
|
||||
this.node.update_field_data( new_data );
|
||||
}
|
||||
} );
|
||||
@@ -368,6 +372,7 @@ define([
|
||||
tooltip: "tool tooltip",
|
||||
annotation: "tool annotation",
|
||||
workflow_outputs: [ {"output_name": "out1"} ],
|
||||
label: "Cat that data.",
|
||||
};
|
||||
node.init_field_data( data );
|
||||
equal( node.type, "tool" );
|
||||
@@ -376,6 +381,7 @@ define([
|
||||
equal( node.tool_state, "ok" );
|
||||
equal( node.tooltip, "tool tooltip" );
|
||||
equal( node.annotation, "tool annotation" );
|
||||
equal( node.label, "Cat that data." );
|
||||
deepEqual( node.post_job_actions, {} );
|
||||
deepEqual( node.workflow_outputs, [ {"output_name": "out1"} ] );
|
||||
} );
|
||||
@@ -394,9 +400,26 @@ define([
|
||||
equal( test.$( ".input-terminal" ).length, 1 );
|
||||
equal( test.$( ".rule" ).length, 1 );
|
||||
equal( test.$( ".toolFormBody" ).children().length, 3 );
|
||||
equal( test.$( ".nodeTitle" ).text(), "newnode" );
|
||||
ok( test.$( ".toolFormTitle" ).find("i").hasClass("fa-wrench") );
|
||||
} );
|
||||
} );
|
||||
|
||||
test( "node title behavior", function() {
|
||||
var test = this;
|
||||
this.expect_workflow_node_changed( function( ) {
|
||||
// Node created with name newnode
|
||||
equal( test.$( ".nodeTitle" ).text(), "newnode" );
|
||||
// init_field_data_simple doesn't change label, so it should
|
||||
// remain original name.
|
||||
test.init_field_data_simple();
|
||||
equal( test.$( ".nodeTitle" ).text(), "newnode" );
|
||||
// Despite awkward name, update does change the label...
|
||||
test.update_field_data_with_new_input();
|
||||
equal( test.$( ".nodeTitle" ).text(), "New Label" );
|
||||
});
|
||||
});
|
||||
|
||||
test( "update_field_data updated data inputs and outputs", function() {
|
||||
var test = this;
|
||||
this.expect_workflow_node_changed( function( ) {
|
||||
|
||||
@@ -19,7 +19,7 @@ def test_valid_new_tool_has_no_errors():
|
||||
trans = MockTrans()
|
||||
mock_tool = mock.Mock()
|
||||
trans.app.toolbox.tools[ "cat1" ] = mock_tool
|
||||
tool_module = modules.module_factory.new( trans, "tool", tool_id="cat1" )
|
||||
tool_module = modules.module_factory.new( trans, "tool", content_id="cat1" )
|
||||
assert not tool_module.get_errors()
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ def test_cannot_create_tool_modules_for_missing_tools():
|
||||
trans = MockTrans()
|
||||
exception = False
|
||||
try:
|
||||
modules.module_factory.new( trans, "tool", tool_id="cat1" )
|
||||
modules.module_factory.new( trans, "tool", content_id="cat1" )
|
||||
except Exception:
|
||||
exception = True
|
||||
assert exception
|
||||
|
||||
Reference in New Issue
Block a user