mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 02:04:04 +08:00
Merge.
This commit is contained in:
@@ -18,4 +18,4 @@ a4113cc1cb5eaa68091c9a73375f00555b66dd11 release_2013.01.13
|
||||
81fbe25bd02edcd53065e8e4476dd1dfb5a72cf2 latest_2013.11.04
|
||||
2a756ca2cb1826db7796018e77d12e2dd7b67603 latest_2014.02.10
|
||||
ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11
|
||||
7e4d21621ce12e13ebbdf9fd3259df58c3ef124c latest_2014.08.11
|
||||
7e22f35798522100ff03e1fdd4eced962b292360 latest_2014.08.11
|
||||
|
||||
@@ -24,7 +24,7 @@ from galaxy.web.base.controller import BaseUIController, SharableMixin, UsesStor
|
||||
from galaxy.web.framework import form
|
||||
from galaxy.web.framework.helpers import grids, time_ago
|
||||
from galaxy.web.framework.helpers import to_unicode
|
||||
from galaxy.workflow.modules import module_factory
|
||||
from galaxy.workflow.modules import module_factory, is_tool_module_type
|
||||
from galaxy.workflow.run import invoke
|
||||
from galaxy.workflow.run import WorkflowRunConfig
|
||||
from galaxy.workflow.extract import summarize
|
||||
@@ -836,8 +836,8 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix
|
||||
steps_by_external_id = {}
|
||||
errors = []
|
||||
for key, step_dict in data['steps'].iteritems():
|
||||
is_input = step_dict[ 'type' ] in [ 'data_input', 'data_collection_input' ]
|
||||
if not is_input and step_dict['tool_id'] not in trans.app.toolbox.tools_by_id:
|
||||
is_tool = is_tool_module_type( step_dict[ 'type' ] )
|
||||
if is_tool and step_dict['tool_id'] not in trans.app.toolbox.tools_by_id:
|
||||
errors.append("Step %s requires tool '%s'." % (step_dict['id'], step_dict['tool_id']))
|
||||
if errors:
|
||||
return dict( name=workflow.name,
|
||||
|
||||
@@ -71,9 +71,18 @@ class WorkflowModule( object ):
|
||||
## ---- Configuration time -----------------------------------------------
|
||||
|
||||
def get_state( self ):
|
||||
""" Return a serializable representation of the persistable state of
|
||||
the step - for tools it DefaultToolState.encode returns a string and
|
||||
for inputs a json description is dumped out.
|
||||
"""
|
||||
return None
|
||||
|
||||
def get_errors( self ):
|
||||
""" It seems like this is effectively just used as boolean - some places
|
||||
in the tool shed self.errors is set to boolean, other places 'unavailable',
|
||||
likewise in Galaxy it stores a list containing a string with an unrecognized
|
||||
tool id error message.
|
||||
"""
|
||||
return None
|
||||
|
||||
def get_data_inputs( self ):
|
||||
@@ -292,6 +301,9 @@ class ToolModule( WorkflowModule ):
|
||||
@classmethod
|
||||
def new( Class, trans, tool_id=None ):
|
||||
module = Class( trans, tool_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
|
||||
raise Exception( error_message )
|
||||
module.state = module.tool.new_state( trans, all_pages=True )
|
||||
return module
|
||||
|
||||
@@ -536,5 +548,14 @@ class WorkflowModuleFactory( object ):
|
||||
type = step.type
|
||||
return self.module_types[type].from_workflow_step( trans, step )
|
||||
|
||||
module_types = dict( data_input=InputDataModule, data_collection_input=InputDataCollectionModule, tool=ToolModule )
|
||||
|
||||
def is_tool_module_type( module_type ):
|
||||
return not module_type or module_type == "tool"
|
||||
|
||||
|
||||
module_types = dict(
|
||||
data_input=InputDataModule,
|
||||
data_collection_input=InputDataCollectionModule,
|
||||
tool=ToolModule,
|
||||
)
|
||||
module_factory = WorkflowModuleFactory( module_types )
|
||||
|
||||
@@ -71,7 +71,7 @@ class WorkflowInvoker( object ):
|
||||
self.trans.sa_session.add( workflow_invocation )
|
||||
|
||||
# Not flushing in here, because web controller may create multiple
|
||||
# invokations.
|
||||
# invocations.
|
||||
return self.outputs
|
||||
|
||||
def _invoke_step( self, step ):
|
||||
@@ -211,10 +211,9 @@ class WorkflowInvoker( object ):
|
||||
"""
|
||||
replacement = None
|
||||
if prefixed_name in step.input_connections_by_name:
|
||||
outputs = self.outputs
|
||||
connection = step.input_connections_by_name[ prefixed_name ]
|
||||
if input.multiple:
|
||||
replacement = [ outputs[ c.output_step.id ][ c.output_name ] for c in connection ]
|
||||
replacement = [ self._replacement_for_connection( c ) for c in connection ]
|
||||
# If replacement is just one dataset collection, replace tool
|
||||
# input with dataset collection - tool framework will extract
|
||||
# datasets properly.
|
||||
@@ -222,9 +221,12 @@ class WorkflowInvoker( object ):
|
||||
if isinstance( replacement[ 0 ], model.HistoryDatasetCollectionAssociation ):
|
||||
replacement = replacement[ 0 ]
|
||||
else:
|
||||
replacement = outputs[ connection[ 0 ].output_step.id ][ connection[ 0 ].output_name ]
|
||||
replacement = self._replacement_for_connection( connection[ 0 ] )
|
||||
return replacement
|
||||
|
||||
def _replacement_for_connection( self, connection ):
|
||||
return self.outputs[ connection.output_step.id ][ connection.output_name ]
|
||||
|
||||
def _populate_state( self ):
|
||||
# Build the state for each step
|
||||
for step in self.workflow.steps:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
define(["libs/underscore"],function(b){var e=b.extend;var c=2,a=5;var d=function(i,h,f,g){this.slots={};this.start_end_dct={};this.w_scale=i;this.mode=h;this.include_label=(h==="Pack");this.max_rows=f;this.measureText=g};e(d.prototype,{slot_features:function(m){var p=this.w_scale,h=this.start_end_dct,x=[],z=[],n=0,y=this.max_rows;for(var v=0,w=m.length;v<w;v++){var k=m[v],o=k[0];if(this.slots[o]!==undefined){n=Math.max(n,this.slots[o]);z.push(this.slots[o])}else{x.push(v)}}var q=function(E,F){for(var D=0;D<=y;D++){var B=false,G=h[D];if(G!==undefined){for(var A=0,C=G.length;A<C;A++){var i=G[A];if(F>i[0]&&E<i[1]){B=true;break}}}if(!B){return D}}return -1};for(var v=0,w=x.length;v<w;v++){var k=m[x[v]],o=k[0],t=k[1],f=k[2],r=k[3],g=Math.floor(t*p),l=Math.ceil(f*p),u=this.measureText(r).width,j;if(r!==undefined&&this.include_label){u+=(c+a);if(g-u>=0){g-=u;j="left"}else{l+=u;j="right"}}var s=q(g,l);if(s>=0){if(h[s]===undefined){h[s]=[]}h[s].push([g,l]);this.slots[o]=s;n=Math.max(n,s)}}return n+1}});return{FeatureSlotter:d}});
|
||||
define(["libs/underscore"],function(c){var f=c.extend;var d=2,b=5;var a=function(h,g){this.slot=h;this.feature=g};var e=function(j,i,g,h){this.slots={};this.start_end_dct={};this.w_scale=j;this.mode=i;this.include_label=(i==="Pack");this.max_rows=g;this.measureText=h};f(e.prototype,{_get_draw_coords:function(j){var h=Math.floor(j[1]*this.w_scale),i=Math.ceil(j[2]*this.w_scale),g=j[3],k;if(g!==undefined&&this.include_label){var l=this.measureText(g).width+(d+b);if(h-l>=0){h-=l;k="left"}else{i+=l;k="right"}}return[h,i]},_find_slot:function(j){var o=j[0],n=j[1];for(var l=0;l<=this.max_rows;l++){var p=false,m=this.start_end_dct[l];if(m!==undefined){for(var g=0,h=m.length;g<h;g++){var i=m[g];if(n>i[0]&&o<i[1]){p=true;break}}}if(!p){return l}}return -1},slot_features:function(h){var q=this.start_end_dct,v=[],m=0,x,l;for(var o=0,t=h.length;o<t;o++){x=h[o];l=x[0];var g=this.slots[l];if(g){if(x[1]<g.feature[1]||g.feature[2]<x[2]){console.log(x[3],g.slot,this._find_slot(this._get_draw_coords(x)));var s=this._get_draw_coords(g.feature),p=this._get_draw_coords(x),j=this.start_end_dct[g.slot];for(var n=0;n<j.length;n++){var w=j[n];if(w[0]===s[0]&&w[1]===s[1]){j[n]=p}}}m=Math.max(m,this.slots[l].slot)}else{v.push(o)}}for(var o=0,t=v.length;o<t;o++){x=h[v[o]];l=x[0];var r=this._get_draw_coords(x);var u=this._find_slot(r);if(u>=0){if(q[u]===undefined){q[u]=[]}q[u].push(r);this.slots[l]=new a(u,x);m=Math.max(m,u)}}return m+1}});return{FeatureSlotter:e}});
|
||||
@@ -396,7 +396,7 @@ _.extend(FeaturePainter.prototype, {
|
||||
feature_end = feature[2],
|
||||
// Slot valid only if features are slotted and this feature is slotted;
|
||||
// feature may not be due to lack of space.
|
||||
slot = (slots && slots[feature_uid] !== undefined ? slots[feature_uid] : null);
|
||||
slot = (slots && slots[feature_uid] !== undefined ? slots[feature_uid].slot : null);
|
||||
|
||||
// Draw feature if (a) mode is dense or feature is slotted (as it must be for all non-dense modes) and
|
||||
// (b) there's overlap between the feature and drawing region.
|
||||
|
||||
@@ -6,6 +6,14 @@ var extend = _.extend;
|
||||
var LABEL_SPACING = 2,
|
||||
PACK_SPACING = 5;
|
||||
|
||||
/**
|
||||
* Hold slotting information for a feature.
|
||||
*/
|
||||
var SlottedInfo = function(slot, feature) {
|
||||
this.slot = slot;
|
||||
this.feature = feature;
|
||||
};
|
||||
|
||||
/**
|
||||
* FeatureSlotter determines slots in which to draw features for vertical
|
||||
* packing.
|
||||
@@ -28,118 +36,148 @@ var FeatureSlotter = function (w_scale, mode, max_rows, measureText) {
|
||||
* the largest slot required for the passed set of features is returned
|
||||
*/
|
||||
extend( FeatureSlotter.prototype, {
|
||||
slot_features: function( features ) {
|
||||
var w_scale = this.w_scale,
|
||||
start_end_dct = this.start_end_dct,
|
||||
undone = [],
|
||||
slotted = [],
|
||||
highest_slot = 0,
|
||||
max_rows = this.max_rows;
|
||||
|
||||
// If feature already exists in slots (from previously seen tiles), use the same slot,
|
||||
// otherwise if not seen, add to "undone" list for slot calculation.
|
||||
|
||||
// TODO: Should calculate zoom tile index, which will improve performance
|
||||
// by only having to look at a smaller subset
|
||||
// if (this.start_end_dct[0] === undefined) { this.start_end_dct[0] = []; }
|
||||
for (var i = 0, len = features.length; i < len; i++) {
|
||||
var feature = features[i],
|
||||
feature_uid = feature[0];
|
||||
if (this.slots[feature_uid] !== undefined) {
|
||||
highest_slot = Math.max(highest_slot, this.slots[feature_uid]);
|
||||
slotted.push(this.slots[feature_uid]);
|
||||
/**
|
||||
* Get drawing coordinate for a feature.
|
||||
*/
|
||||
_get_draw_coords: function(feature) {
|
||||
// Get initial draw coordinates using w_scale.
|
||||
var draw_start = Math.floor(feature[1] * this.w_scale),
|
||||
draw_end = Math.ceil(feature[2] * this.w_scale),
|
||||
f_name = feature[3],
|
||||
text_align;
|
||||
|
||||
// Update start, end drawing locations to include feature name.
|
||||
// Try to put the name on the left, if not, put on right.
|
||||
if (f_name !== undefined && this.include_label ) {
|
||||
// Add gap for label spacing and extra pack space padding
|
||||
// TODO: Fix constants
|
||||
var text_len = this.measureText(f_name).width + (LABEL_SPACING + PACK_SPACING);
|
||||
if (draw_start - text_len >= 0) {
|
||||
draw_start -= text_len;
|
||||
text_align = "left";
|
||||
} else {
|
||||
undone.push(i);
|
||||
draw_end += text_len;
|
||||
text_align = "right";
|
||||
}
|
||||
}
|
||||
|
||||
// Slot unslotted features.
|
||||
|
||||
// Find the first slot such that current feature doesn't overlap any other features in that slot.
|
||||
// Returns -1 if no slot was found.
|
||||
var find_slot = function(f_start, f_end) {
|
||||
// TODO: Fix constants
|
||||
for (var slot_num = 0; slot_num <= max_rows; slot_num++) {
|
||||
var has_overlap = false,
|
||||
slot = start_end_dct[slot_num];
|
||||
if (slot !== undefined) {
|
||||
// Iterate through features already in slot to see if current feature will fit.
|
||||
for (var k = 0, k_len = slot.length; k < k_len; k++) {
|
||||
var s_e = slot[k];
|
||||
if (f_end > s_e[0] && f_start < s_e[1]) {
|
||||
// There is overlap
|
||||
has_overlap = true;
|
||||
break;
|
||||
|
||||
/*
|
||||
if (slot_num < 0) {
|
||||
|
||||
TODO: this is not yet working --
|
||||
console.log(feature_uid, "looking for slot with text on the right");
|
||||
// Slot not found. If text was on left, try on right and see
|
||||
// if slot can be found.
|
||||
// TODO: are there any checks we need to do to ensure that text
|
||||
// will fit on tile?
|
||||
if (text_align === "left") {
|
||||
draw_start -= text_len;
|
||||
draw_end -= text_len;
|
||||
text_align = "right";
|
||||
slot_num = find_slot(draw_start, draw_end);
|
||||
}
|
||||
if (slot_num >= 0) {
|
||||
console.log(feature_uid, "found slot with text on the right");
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
return [draw_start, draw_end];
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the first slot such that current feature doesn't overlap any other features in that slot.
|
||||
* Returns -1 if no slot was found.
|
||||
*/
|
||||
_find_slot: function(draw_coords) {
|
||||
// TODO: Use a data structure for faster searching of available slots.
|
||||
var draw_start = draw_coords[0],
|
||||
draw_end = draw_coords[1];
|
||||
for (var slot_num = 0; slot_num <= this.max_rows; slot_num++) {
|
||||
var has_overlap = false,
|
||||
slot = this.start_end_dct[slot_num];
|
||||
if (slot !== undefined) {
|
||||
// Iterate through features already in slot to see if current feature will fit.
|
||||
for (var k = 0, k_len = slot.length; k < k_len; k++) {
|
||||
var s_e = slot[k];
|
||||
if (draw_end > s_e[0] && draw_start < s_e[1]) {
|
||||
// There is overlap
|
||||
has_overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!has_overlap) {
|
||||
return slot_num;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Slot features.
|
||||
*/
|
||||
slot_features: function( features ) {
|
||||
var start_end_dct = this.start_end_dct,
|
||||
undone = [],
|
||||
highest_slot = 0,
|
||||
feature,
|
||||
feature_uid;
|
||||
|
||||
// Loop through features to (a) find those that are not yet slotted and (b) update
|
||||
// those that are slotted if new information is availabe. For (a), features already
|
||||
// slotted (based on slotting from other tiles) will retain their current slot.
|
||||
for (var i = 0, len = features.length; i < len; i++) {
|
||||
feature = features[i];
|
||||
feature_uid = feature[0];
|
||||
var slotted_info = this.slots[feature_uid];
|
||||
|
||||
// Separate and handle slotted vs. unslotted features.
|
||||
if (slotted_info) {
|
||||
// Feature is slotted; if feature now has larger start/end coordinates,
|
||||
// update drawing coordinates.
|
||||
if (feature[1] < slotted_info.feature[1] || slotted_info.feature[2] < feature[2]) {
|
||||
console.log(feature[3], slotted_info.slot, this._find_slot(this._get_draw_coords(feature)));
|
||||
// Feature has changed (e.g. a single read now has its pair), so recalculate its
|
||||
// drawing coordinates.
|
||||
var old_draw_coords = this._get_draw_coords(slotted_info.feature),
|
||||
new_draw_coords = this._get_draw_coords(feature),
|
||||
slotted_coords = this.start_end_dct[slotted_info.slot];
|
||||
for (var k = 0; k < slotted_coords.length; k++) {
|
||||
var dc = slotted_coords[k];
|
||||
if (dc[0] === old_draw_coords[0] && dc[1] === old_draw_coords[1]) {
|
||||
// Replace old drawing coordinates with new ones.
|
||||
slotted_coords[k] = new_draw_coords;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!has_overlap) {
|
||||
return slot_num;
|
||||
}
|
||||
highest_slot = Math.max(highest_slot, this.slots[feature_uid].slot);
|
||||
}
|
||||
else {
|
||||
undone.push(i);
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
// Slot unslotted features.
|
||||
|
||||
// Do slotting.
|
||||
for (var i = 0, len = undone.length; i < len; i++) {
|
||||
var feature = features[undone[i]],
|
||||
feature_uid = feature[0],
|
||||
feature_start = feature[1],
|
||||
feature_end = feature[2],
|
||||
feature_name = feature[3],
|
||||
// Where to start, end drawing on screen.
|
||||
f_start = Math.floor( feature_start * w_scale ),
|
||||
f_end = Math.ceil( feature_end * w_scale ),
|
||||
text_len = this.measureText(feature_name).width,
|
||||
text_align;
|
||||
|
||||
// Update start, end drawing locations to include feature name.
|
||||
// Try to put the name on the left, if not, put on right.
|
||||
if (feature_name !== undefined && this.include_label ) {
|
||||
// Add gap for label spacing and extra pack space padding
|
||||
// TODO: Fix constants
|
||||
text_len += (LABEL_SPACING + PACK_SPACING);
|
||||
if (f_start - text_len >= 0) {
|
||||
f_start -= text_len;
|
||||
text_align = "left";
|
||||
} else {
|
||||
f_end += text_len;
|
||||
text_align = "right";
|
||||
}
|
||||
}
|
||||
feature = features[undone[i]];
|
||||
feature_uid = feature[0];
|
||||
var draw_coords = this._get_draw_coords(feature);
|
||||
|
||||
// Find slot.
|
||||
var slot_num = find_slot(f_start, f_end);
|
||||
var slot_num = this._find_slot(draw_coords);
|
||||
|
||||
/*
|
||||
if (slot_num < 0) {
|
||||
|
||||
TODO: this is not yet working --
|
||||
console.log(feature_uid, "looking for slot with text on the right");
|
||||
// Slot not found. If text was on left, try on right and see
|
||||
// if slot can be found.
|
||||
// TODO: are there any checks we need to do to ensure that text
|
||||
// will fit on tile?
|
||||
if (text_align === "left") {
|
||||
f_start -= text_len;
|
||||
f_end -= text_len;
|
||||
text_align = "right";
|
||||
slot_num = find_slot(f_start, f_end);
|
||||
}
|
||||
if (slot_num >= 0) {
|
||||
console.log(feature_uid, "found slot with text on the right");
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
// Do slotting.
|
||||
if (slot_num >= 0) {
|
||||
// Add current feature to slot.
|
||||
if (start_end_dct[slot_num] === undefined) {
|
||||
start_end_dct[slot_num] = [];
|
||||
}
|
||||
start_end_dct[slot_num].push([f_start, f_end]);
|
||||
this.slots[feature_uid] = slot_num;
|
||||
start_end_dct[slot_num].push(draw_coords);
|
||||
this.slots[feature_uid] = new SlottedInfo(slot_num, feature);
|
||||
highest_slot = Math.max(highest_slot, slot_num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
from galaxy import eggs
|
||||
eggs.require( "mock" )
|
||||
|
||||
|
||||
import mock
|
||||
|
||||
from galaxy.workflow import modules
|
||||
from .workflow_support import MockTrans
|
||||
|
||||
|
||||
def test_input_has_no_errors():
|
||||
trans = MockTrans()
|
||||
input_step_module = modules.module_factory.new( trans, 'data_input' )
|
||||
assert not input_step_module.get_errors()
|
||||
|
||||
|
||||
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" )
|
||||
assert not tool_module.get_errors()
|
||||
|
||||
|
||||
def test_missing_tool_has_errors():
|
||||
trans = MockTrans()
|
||||
tool_dict = { "type": "tool", "tool_id": "cat1" }
|
||||
tool_module = modules.module_factory.from_dict( trans, tool_dict )
|
||||
assert tool_module.get_errors()
|
||||
|
||||
|
||||
def test_cannot_create_tool_modules_for_missing_tools():
|
||||
trans = MockTrans()
|
||||
exception = False
|
||||
try:
|
||||
modules.module_factory.new( trans, 'tool', tool_id="cat1" )
|
||||
except Exception:
|
||||
exception = True
|
||||
assert exception
|
||||
@@ -17,3 +17,14 @@ class TestApp( object ):
|
||||
"sqlite:///:memory:",
|
||||
create_tables=True
|
||||
)
|
||||
self.toolbox = TestToolbox()
|
||||
|
||||
|
||||
class TestToolbox( object ):
|
||||
|
||||
def __init__( self ):
|
||||
self.tools = {}
|
||||
|
||||
def get_tool( self, tool_id ):
|
||||
# Real tool box returns None of missing tool also
|
||||
return self.tools.get( tool_id, None )
|
||||
|
||||
Reference in New Issue
Block a user