galaxy-commits
Threads by month
- ----- 2025 -----
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
galaxy-dist commit 4053c425b536: No longer pre-generate menus on page load for popup-style menus. Instead, create menu dynamically when needed, greatly improving load time especially on data libraries with large number of potential menus.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kanwei Li <kanwei(a)gmail.com>
# Date 1288748718 14400
# Node ID 4053c425b536b9878f6f8bdfac75453adec7bdf3
# Parent e000a6800e1cbfa15a190bb2aff90a5956882a43
No longer pre-generate menus on page load for popup-style menus. Instead, create menu dynamically when needed, greatly improving load time especially on data libraries with large number of potential menus.
Remove the creation of a background element that closes the active menu clicked. Instead, bind an event to close active menus to the document object of current and all other framesets. Tested in IE.
Update Tool Search/Recently Used code to reflect new changes.
Made some small tweaks to pass JSlint
--- a/templates/root/index.mako
+++ b/templates/root/index.mako
@@ -46,10 +46,8 @@
"Export to File": function() {
galaxy_main.location = "${h.url_for( controller='history', action='export_archive' )}";
},
- "Delete": function()
- {
- if ( confirm( "Really delete the current history?" ) )
- {
+ "Delete": function() {
+ if ( confirm( "Really delete the current history?" ) ) {
galaxy_main.location = "${h.url_for( controller='history', action='delete_current' )}";
}
},
@@ -59,98 +57,108 @@
}
});
+ var menu_options = {}; // Holds dictionary of { label: toggle_fn }
+
+ SHOW_TOOL = "Show Tool Search";
+ HIDE_TOOL = "Hide Tool Search";
+ SHOW_RECENT = "Show Recently Used";
+ HIDE_RECENT = "Hide Recently Used";
+
+ var toggle_tool_search_fn = function() {
+ // Show/hide menu and update vars, user preferences.
+ var menu = $("#galaxy_tools").contents().find('#tool-search'),
+ pref_value, menu_option_text, old_text;
+ if (menu.is(":visible")) {
+ // Hide menu.
+ pref_value = "False";
+ menu_option_text = SHOW_TOOL;
+ old_text = HIDE_TOOL;
+
+ // Reset search.
+ reset_tool_search(true);
+ } else {
+ // Show menu.
+ pref_value = "True";
+ menu_option_text = HIDE_TOOL;
+ old_text = SHOW_TOOL;
+ }
+ menu.toggle();
+
+ // Update menu option.
+ delete menu_options[old_text];
+ menu_options[menu_option_text] = toggle_tool_search_fn;
+ make_popupmenu( $("#tools-options-button"), menu_options );
+ galaxy_async.set_user_pref("show_tool_search", pref_value);
+ };
+
+ var toggle_recently_used_fn = function() {
+ // Show/hide menu.
+ var ru_menu = $('#galaxy_tools').contents().find('#recently_used_wrapper'),
+ ru_menu_body = ru_menu.find(".toolSectionBody"),
+ pref_value, old_text, menu_option_text;
+ if (ru_menu.hasClass("user_pref_visible")) {
+ // Hide menu.
+ ru_menu_body.slideUp();
+ ru_menu.slideUp();
+
+ // Set vars used below and in tool menu frame.
+ pref_value = "False";
+ old_text = HIDE_RECENT;
+ menu_option_text = SHOW_RECENT;
+ } else {
+ // "Show" menu.
+ if (!$('#galaxy_tools').contents().find('#tool-search-query').hasClass("search_active")) {
+ // Default.
+ ru_menu.slideDown();
+ } else {
+ // Search active: tf there are matching tools in RU menu, show menu.
+ if ( ru_menu.find(".toolTitle.search_match").length !== 0 ) {
+ ru_menu.slideDown();
+ ru_menu_body.slideDown();
+ }
+ }
+ // Set vars used below and in tool menu frame.
+ pref_value = "True";
+ old_text = SHOW_RECENT;
+ menu_option_text = HIDE_RECENT;
+ }
+
+ // Update menu class and option.
+ ru_menu.toggleClass("user_pref_hidden user_pref_visible");
+ delete menu_options[old_text];
+ menu_options[menu_option_text] = toggle_recently_used_fn;
+ make_popupmenu( $("#tools-options-button"), menu_options );
+ galaxy_async.set_user_pref("show_recently_used_menu", pref_value);
+ };
+
// Init tool options.
- make_popupmenu( $("#tools-options-button"), {
- ## Search tools menu item.
- %if trans.app.toolbox_search.enabled:
- <%
- show_tool_search = False
- if trans.user:
- show_tool_search = trans.user.preferences.get( "show_tool_search", "False" )
-
- if show_tool_search == "True":
- initial_text = "Hide Search"
- else:
- initial_text = "Search Tools"
- %>
- "${initial_text}": function() {
- // Show/hide menu and update vars, user preferences.
- var menu = $("#galaxy_tools").contents().find('#tool-search');
- if (menu.is(":visible"))
- {
- // Hide menu.
- pref_value = "False";
- menu_option_text = "Search Tools";
- menu.toggle();
-
- // Reset search.
- reset_tool_search(true);
- }
- else
- {
- // Show menu.
- pref_value = "True";
- menu_option_text = "Hide Search";
- menu.toggle();
- }
-
- // Update menu option.
- $("#tools-options-button-menu").find("li").eq(0).text(menu_option_text);
-
- galaxy_async.set_user_pref("show_tool_search", pref_value);
- },
- %endif
- ## Recently used tools menu.
- %if trans.user:
- <%
- if trans.user.preferences.get( 'show_recently_used_menu', 'False' ) == 'True':
- action = "Hide"
- else:
- action = "Show"
- %>
- "${action} Recently Used": function() {
- // Show/hide menu.
- var ru_menu = $('#galaxy_tools').contents().find('#recently_used_wrapper');
- var ru_menu_body = ru_menu.find(".toolSectionBody");
- var pref_value = null;
- var menu_option_text = null;
- if (ru_menu.hasClass("user_pref_visible"))
- {
- // Hide menu.
- ru_menu_body.slideUp();
- ru_menu.slideUp();
-
- // Set vars used below and in tool menu frame.
- pref_value = "False";
- menu_option_text = "Show Recently Used";
- }
- else
- {
- // "Show" menu.
- if (!$('#galaxy_tools').contents().find('#tool-search-query').hasClass("search_active"))
- // Default.
- ru_menu.slideDown();
- else
- // Search active: tf there are matching tools in RU menu, show menu.
- if ( ru_menu.find(".toolTitle.search_match").length != 0 )
- {
- ru_menu.slideDown();
- ru_menu_body.slideDown();
- }
-
- // Set vars used below and in tool menu frame.
- pref_value = "True";
- menu_option_text = "Hide Recently Used";
- }
-
- // Update menu class and option.
- ru_menu.toggleClass("user_pref_hidden user_pref_visible");
- $("#tools-options-button-menu").find("li").eq(1).text(menu_option_text);
-
- galaxy_async.set_user_pref("show_recently_used_menu", pref_value);
- }
- %endif
- });
+ ## Search tools menu item.
+ %if trans.app.toolbox_search.enabled:
+ <%
+ show_tool_search = False
+ if trans.user:
+ show_tool_search = trans.user.preferences.get( "show_tool_search", "False" )
+
+ if show_tool_search == "True":
+ action = "HIDE_TOOL"
+ else:
+ action = "SHOW_TOOL"
+ %>
+ menu_options[ ${action} ] = toggle_tool_search_fn;
+ %endif
+ ## Recently used tools menu.
+ %if trans.user:
+ <%
+ if trans.user.preferences.get( 'show_recently_used_menu', 'False' ) == 'True':
+ action = "HIDE_RECENT"
+ else:
+ action = "SHOW_RECENT"
+ %>
+ menu_options[ ${action} ] = toggle_recently_used_fn;
+ %endif
+
+
+ make_popupmenu( $("#tools-options-button"), menu_options );
});
</script></%def>
--- a/static/scripts/galaxy.base.js
+++ b/static/scripts/galaxy.base.js
@@ -7,7 +7,7 @@ if (!Array.indexOf) {
}
}
return -1;
- }
+ };
}
// Returns the number of keys (elements) in an array/dictionary.
@@ -39,93 +39,89 @@ function obj_length(obj) {
});
};
-function ensure_popup_helper() {
- // And the helper below the popup menus
- if ( $( "#popup-helper" ).length === 0 ) {
- $( "<div id='popup-helper'/>" ).css( {
- background: 'white', opacity: 0, zIndex: 15000,
- position: 'absolute', top: 0, left: 0, width: '100%', height: '100%'
- } ).appendTo( "body" ).hide();
- }
-}
-
-function attach_popupmenu( button_element, wrapper ) {
- var clean = function() {
- wrapper.unbind().hide();
- $("#popup-helper").unbind( "click.popupmenu" ).hide();
- // $(document).unbind( "click.popupmenu" );
- };
- var click_handler = function( e ) {
- // var o = $(button_element).offset();
- $("#popup-helper").bind( "click.popupmenu", clean ).show();
- // $(document).bind( "click.popupmenu", clean );
- // Show off screen to get size right
- wrapper.click( clean ).css( { left: 0, top: -1000 } ).show();
- // console.log( e.pageX, $(document).scrollLeft() + $(window).width(), $(menu_element).width() );
- var x = e.pageX - wrapper.width() / 2 ;
- x = Math.min( x, $(document).scrollLeft() + $(window).width() - $(wrapper).width() - 20 );
- x = Math.max( x, $(document).scrollLeft() + 20 );
- // console.log( e.pageX, $(document).scrollLeft() + $(window).width(), $(menu_element).width() );
-
-
- wrapper.css( {
- top: e.pageY - 5,
- left: x
- } );
- return false;
- };
- $(button_element).bind("click", click_handler);
+// Toggle popup menu options using regular expression on option names.
+function show_hide_popupmenu_options( menu, option_name_re, show ) {
+ show = (show === undefined ? true : show );
+ var re = new RegExp(option_name_re);
+ $(menu).find("li").each( function() {
+ if ( re.exec($(this).text()) ) {
+ if (show) {
+ $(this).show();
+ } else {
+ $(this).hide();
+ }
+ }
+ });
}
function make_popupmenu( button_element, options ) {
- ensure_popup_helper();
- // var container_element = $(button_element);
- // if ( container_element.parent().hasClass( "combo-button" ) ) {
- // container_element = container_element.parent();
- // }
- // container_element).css( "position", "relative" );
- var menu_element = $( "<ul id='" + button_element.attr('id') + "-menu'></ul>" );
- if (obj_length(options) <= 0) {
- $("<li/>").html("No options").appendTo(menu_element);
- }
- $.each( options, function( k, v ) {
- if (v) {
- $("<li/>").html(k).click(v).appendTo(menu_element);
- } else {
- $("<li class='head'/>").html(k).appendTo(menu_element);
- }
+ button_element.bind("click.show_popup", function(e) {
+ // Close existing visible menus
+ $(".popmenu-wrapper").remove();
+
+
+ // Need setTimeouts so clicks don't interfere with each other
+ setTimeout( function() {
+ // Dynamically generate the wrapper holding all the selectable options of the menu.
+ var menu_element = $( "<ul id='" + button_element.attr('id') + "-menu'></ul>" );
+ if (obj_length(options) <= 0) {
+ $("<li>No Options.</li>").appendTo(menu_element);
+ }
+ $.each( options, function( k, v ) {
+ if (v) {
+ $("<li/>").html(k).click(v).appendTo(menu_element);
+ } else {
+ $("<li class='head'/>").html(k).appendTo(menu_element);
+ }
+ });
+ var wrapper = $( "<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'>" );
+ wrapper.append( menu_element )
+ .append( "<div class='overlay-border'>" )
+ .appendTo( "body" );
+
+ var x = e.pageX - wrapper.width() / 2 ;
+ x = Math.min( x, $(document).scrollLeft() + $(window).width() - $(wrapper).width() - 20 );
+ x = Math.max( x, $(document).scrollLeft() + 20 );
+
+ wrapper.css( {
+ top: e.pageY - 5,
+ left: x
+ } );
+ }, 10);
+
+ setTimeout( function() {
+ // Bind click event to current window and all frames to remove any visible menus
+ // Bind to document object instead of window object for IE compat
+ var close_popup = function(el) {
+ $(el).bind("click.close_popup", function() {
+ $(".popmenu-wrapper").remove();
+ el.unbind("click.close_popup");
+ });
+ };
+ close_popup( $(window.document) ); // Current frame
+ close_popup( $(window.top.document) ); // Parent frame
+ for (var frame_id = window.top.frames.length; frame_id--;) { // Sibling frames
+ var frame = $(window.top.frames[frame_id].document);
+ close_popup(frame);
+ }
+ }, 50);
+
+ return false;
});
- var wrapper = $( "<div class='popmenu-wrapper'>" );
- wrapper.append( menu_element )
- .append( "<div class='overlay-border'>" )
- .css( "position", "absolute" )
- .appendTo( "body" )
- .hide();
- attach_popupmenu( button_element, wrapper );
- return menu_element;
-}
-
-// Toggle popup menu options using regular expression on option names.
-function show_hide_popupmenu_options( menu, option_name_re, show ) {
- var show = (show === undefined ? true : show );
- var re = new RegExp(option_name_re);
- $(menu).find("li").each( function() {
- if ( re.exec( $(this).text() ) )
- if (show)
- $(this).show();
- else
- $(this).hide();
- });
+
}
function make_popup_menus() {
jQuery( "div[popupmenu]" ).each( function() {
var options = {};
- $(this).find( "a" ).each( function() {
- var confirmtext = $(this).attr( "confirm" ),
- href = $(this).attr( "href" ),
- target = $(this).attr( "target" );
- options[ $(this).text() ] = function() {
+ var menu = $(this);
+ menu.find( "a" ).each( function() {
+ var link = $(this),
+ link_dom = link.get(0);
+ var confirmtext = link_dom.getAttribute( "confirm" ),
+ href = link_dom.getAttribute( "href" ),
+ target = link_dom.getAttribute( "target" );
+ options[ link.text() ] = function() {
if ( !confirmtext || confirm( confirmtext ) ) {
var f = window;
if ( target == "_parent" ) {
@@ -137,14 +133,18 @@ function make_popup_menus() {
}
};
});
- var b = $( "#" + $(this).attr( 'popupmenu' ) );
- b.find("a").bind("click", function(e) {
+ var box = $( "#" + menu.attr( 'popupmenu' ) );
+
+ // For menus with clickable link text, make clicking on the link go through instead
+ // of activating the popup menu
+ box.find("a").bind("click", function(e) {
e.stopPropagation(); // Stop bubbling so clicking on the link goes through
return true;
});
- make_popupmenu( b, options );
- $(this).remove();
- b.addClass( "popup" ).show();
+
+ make_popupmenu(box, options);
+ box.addClass("popup");
+ menu.remove();
});
}
@@ -160,15 +160,17 @@ function naturalSort(a, b) {
xD = (new Date(x)).getTime(),
yD = xD ? (new Date(y)).getTime() : null;
// natural sorting of dates
- if ( yD )
- if ( xD < yD ) return -1;
- else if ( xD > yD ) return 1;
+ if ( yD ) {
+ if ( xD < yD ) { return -1; }
+ else if ( xD > yD ) { return 1; }
+ }
// natural sorting through split numeric strings and default strings
- for( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {
+ var oFxNcL, oFyNcL;
+ for ( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {
oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];
oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];
- if (oFxNcL < oFyNcL) return -1;
- else if (oFxNcL > oFyNcL) return 1;
+ if (oFxNcL < oFyNcL) { return -1; }
+ else if (oFxNcL > oFyNcL) { return 1; }
}
return 0;
}
@@ -176,14 +178,17 @@ function naturalSort(a, b) {
// Replace select box with a text input box + autocomplete.
function replace_big_select_inputs(min_length, max_length) {
// To do replace, jQuery's autocomplete plugin must be loaded.
- if (!jQuery().autocomplete)
+ if (!jQuery().autocomplete) {
return;
+ }
// Set default for min_length and max_length
- if (min_length === undefined)
+ if (min_length === undefined) {
min_length = 20;
- if (max_length === undefined)
+ }
+ if (max_length === undefined) {
max_length = 3000;
+ }
$('select').each( function() {
var select_elt = $(this);
@@ -194,7 +199,7 @@ function replace_big_select_inputs(min_l
}
// Skip multi-select because widget cannot handle multi-select.
- if (select_elt.attr('multiple') == true) {
+ if (select_elt.attr('multiple') === true) {
return;
}
@@ -240,13 +245,14 @@ function replace_big_select_inputs(min_l
});
// Set initial text if it's empty.
- if ( start_value == '' || start_value == '?') {
+ if ( start_value === '' || start_value === '?') {
text_input_elt.attr('value', 'Click to Search or Select');
}
// Sort dbkey options list only.
- if (select_elt.attr('name') == 'dbkey')
+ if (select_elt.attr('name') == 'dbkey') {
select_options = select_options.sort(naturalSort);
+ }
// Do autocomplete.
var autocomplete_options = { selectFirst: false, autoFill: false, mustMatch: false, matchContains: true, max: max_length, minChars : 0, hideForLessThanMinChars : false };
@@ -265,7 +271,7 @@ function replace_big_select_inputs(min_l
}
else {
// If there is a non-empty start value, use that; otherwise unknown.
- if (start_value != "") {
+ if (start_value !== "") {
text_input_elt.attr('value', start_value);
} else {
// This is needed to make the DB key work.
@@ -283,9 +289,9 @@ function replace_big_select_inputs(min_l
// Get refresh vals.
var ref_on_change_vals = select_elt.attr('refresh_on_change_values'),
last_selected_value = select_elt.attr("last_selected_value");
- if (ref_on_change_vals !== undefined)
+ if (ref_on_change_vals !== undefined) {
ref_on_change_vals = ref_on_change_vals.split(',');
-
+ }
// Function that attempts to refresh based on the value in the text element.
var try_refresh_fn = function() {
// Get new value and see if it can be matched.
@@ -371,10 +377,11 @@ function async_save_text(click_to_edit_e
},
success: function(processed_text) {
// Set new text and call finish method.
- if (processed_text != "")
+ if (processed_text !== "") {
text_elt.text(processed_text);
- else
+ } else {
text_elt.html("<em>None</em>");
+ }
if (on_finish) {
on_finish(t);
}
@@ -415,15 +422,16 @@ function init_history_items(historywrapp
// If Mozilla, hide scrollbars in hidden items since they cause animation bugs
if ( $.browser.mozilla ) {
$( "div.historyItemBody" ).each( function() {
- if ( ! $(this).is( ":visible" ) ) $(this).find( "pre.peek" ).css( "overflow", "hidden" );
- })
+ if ( !$(this).is(":visible") ) { $(this).find( "pre.peek" ).css( "overflow", "hidden" ); }
+ });
}
historywrapper.each( function() {
- var id = this.id;
- var body = $(this).children( "div.historyItemBody" );
- var peek = body.find( "pre.peek" )
+ var id = this.id,
+ body = $(this).children( "div.historyItemBody" ),
+ peek = body.find( "pre.peek" );
$(this).find( ".historyItemTitleBar > .historyItemTitle" ).wrap( "<a href='javascript:void(0);'></a>" ).click( function() {
+ var prefs;
if ( body.is(":visible") ) {
// Hiding stuff here
if ( $.browser.mozilla ) { peek.css( "overflow", "hidden" ); }
@@ -431,7 +439,7 @@ function init_history_items(historywrapp
if (!nochanges) { // Ignore embedded item actions
// Save setting
- var prefs = $.jStore.store("history_expand_state");
+ prefs = $.jStore.store("history_expand_state");
if (prefs) {
delete prefs[id];
$.jStore.store("history_expand_state", prefs);
@@ -445,7 +453,7 @@ function init_history_items(historywrapp
if (!nochanges) {
// Save setting
- var prefs = $.jStore.store("history_expand_state");
+ prefs = $.jStore.store("history_expand_state");
if (prefs === undefined) { prefs = {}; }
prefs[id] = true;
$.jStore.store("history_expand_state", prefs);
@@ -470,7 +478,7 @@ function init_history_items(historywrapp
});
$.jStore.store("history_expand_state", prefs);
}).show();
- }
+ };
if (noinit) {
action();
@@ -497,7 +505,7 @@ function reset_tool_search( initValue )
// Function may be called in top frame or in tool_menu_frame;
// in either case, get the tool menu frame.
var tool_menu_frame = $("#galaxy_tools").contents();
- if (tool_menu_frame.length == 0) {
+ if (tool_menu_frame.length === 0) {
tool_menu_frame = $(document);
}
@@ -532,7 +540,7 @@ function reset_tool_search( initValue )
var GalaxyAsync = function(log_action) {
this.url_dict = {};
this.log_action = (log_action === undefined ? false : log_action);
-}
+};
GalaxyAsync.prototype.set_func_url = function( func_name, url ) {
this.url_dict[func_name] = url;
@@ -566,36 +574,6 @@ GalaxyAsync.prototype.log_user_action =
});
};
-// Add to trackster browser functionality
-$(".trackster-add").live("click", function() {
- var dataset = this,
- dataset_jquery = $(this);
- $.ajax({
- url: dataset_jquery.attr("data-url"),
- dataType: "html",
- error: function() { alert( "Could not add this dataset to browser." ); },
- success: function(table_html) {
- var parent = window.parent;
- parent.show_modal("Add to Browser:", table_html, {
- "Cancel": function() {
- parent.hide_modal();
- },
- "Insert into selected": function() {
- $(parent.document).find('input[name=id]:checked').each(function() {
- var vis_id = $(this).val();
- parent.location = dataset_jquery.attr("action-url") + "&id=" + vis_id;
- });
- },
- "Insert into new browser": function() {
- parent.location = dataset_jquery.attr("new-url");
- }
- });
- }
- });
-});
-
-
-
$(document).ready( function() {
$("select[refresh_on_change='true']").change( function() {
var select_field = $(this),
--- a/static/scripts/packed/galaxy.base.js
+++ b/static/scripts/packed/galaxy.base.js
@@ -1,1 +1,1 @@
-if(!Array.indexOf){Array.prototype.indexOf=function(c){for(var b=0,a=this.length;b<a;b++){if(this[b]==c){return b}}return -1}}function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scr
ollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).bind("click",c)}function make_popupmenu(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")+"-menu'></ul>");if(obj_length(b)<=0){$("<li/>").html("No options").appendTo(a)}$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d);return a}function show_hide_popupmenu_options(d,c,a){var a=(a===undefined?true:a);var b=new RegExp(c);$(d).find("li").each(function(){if(b.exec($(this).text())){if(a){$(this).show()}else{$(this).hide()}}})}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("ta
rget");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));a.find("a").bind("click",function(b){b.stopPropagation();return true});make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function naturalSort(i,g){var n=/(-?[0-9\.]+)/g,j=i.toString().toLowerCase()||"",f=g.toString().toLowerCase()||"",k=String.fromCharCode(0),l=j.replace(n,k+"$1"+k).split(k),e=f.replace(n,k+"$1"+k).split(k),d=(new Date(j)).getTime(),m=d?(new Date(f)).getTime():null;if(m){if(d<m){return -1}else{if(d>m){return 1}}}for(var h=0,c=Math.max(l.length,e.length);h<c;h++){oFxNcL=parseFloat(l[h])||l[h];oFyNcL=parseFloat(e[h])||e[h];if(oFxNcL<oFyNcL){return -1}else{if(oFxNcL>oFyNcL){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var d=$(this);var
g=d.find("option").length;if((g<a)||(g>b)){return}if(d.attr("multiple")==true){return}if(d.hasClass("no-autocomplete")){return}var m=d.attr("value");var c=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",d.attr("name"));c.attr("id",d.attr("id"));c.click(function(){var n=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(n);$(this).select()});var e=[];var i={};d.children("option").each(function(){var o=$(this).text();var n=$(this).attr("value");e.push(o);i[o]=n;i[n]=n;if(n==m){c.attr("value",o)}});if(m==""||m=="?"){c.attr("value","Click to Search or Select")}if(d.attr("name")=="dbkey"){e=e.sort(naturalSort)}var f={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(e,f);d.replaceWith(c);var k=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){c.attr("value",n)}else{if(m!=""){c.attr("value",m)}else{c.
attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_dbkeys",function(){k()});if(d.attr("refresh_on_change")=="true"){var h=d.attr("refresh_on_change_values"),l=d.attr("last_selected_value");if(h!==undefined){h=h.split(",")}var j=function(){var n=i[c.attr("value")];if(l!==n&&n!==null&&n!==undefined){if(h!==undefined&&$.inArray(n,h)===-1&&$.inArray(l,h)===-1){return}c.attr("value",n);$(window).trigger("refresh_on_change");c.parents("form").submit()}};c.bind("result",j);c.keyup(function(n){if(n.keyCode===13){j()}});c.keydown(function(n){if(n.keyCode===13){return false}})}})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(
function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!=""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStore.store("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id;var h=$(this).children("div.historyItemBody");var i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").cl
ick(function(){if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){var k=$.jStore.store("history_expand_state");if(k){delete k[j];$.jStore.store("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){var k=$.jStore.store("history_expand_state");if(k===undefined){k={}}k[j]=true;$.jStore.store("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStore.store("history_expand_state");if(h===undefined){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",h)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function rese
t_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length==0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools");b.css("font-style","italic")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};Ga
laxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(".trackster-add").live("click",function(){var b=this,a=$(this);$.ajax({url:a.attr("data-url"),dataType:"html",error:function(){alert("Could not add this dataset to browser.")},success:function(c){var d=window.parent;d.show_modal("Add to Browser:",c,{Cancel:function(){d.hide_modal()},"Insert into selected":function(){$(d.document).find("input[name=id]:checked").each(function(){var e=$(this).val();d.location=a.attr("action-url")+"&id="+e})},"Insert into new browser":function(){d.location=a.attr("new-url")}})}})});$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_val
ue");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus();replace_big_select_inputs(20,1500)});
+if(!Array.indexOf){Array.prototype.indexOf=function(c){for(var b=0,a=this.length;b<a;b++){if(this[b]==c){return b}}return -1}}function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function show_hide_popupmenu_options(d,c,a){a=(a===undefined?true:a);var b=new RegExp(c);$(d).find("li").each(function(){if(b.exec($(this).text())){if(a){$(this).show()}else{$(this).hide()}}})}function make_popupmenu(b,a){b.bind("click.show_popup",function(c){$(".popmenu-wrapper").remove();setTimeout(function(){var e=$("<ul id='"+b.attr("id")+"-menu'></ul>");if(obj_length(a)<=0){$("<li>No Options.</li>").appendTo(e)}$.each(a,function(h,g){if(g){$("<li/>").html(h).click(g).appendTo(e)}else{$("<li class='head'/
>").html(h).appendTo(e)}});var f=$("<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'>");f.append(e).append("<div class='overlay-border'>").appendTo("body");var d=c.pageX-f.width()/2;d=Math.min(d,$(document).scrollLeft()+$(window).width()-$(f).width()-20);d=Math.max(d,$(document).scrollLeft()+20);f.css({top:c.pageY-5,left:d})},10);setTimeout(function(){var e=function(g){$(g).bind("click.close_popup",function(){$(".popmenu-wrapper").remove();g.unbind("click.close_popup")})};e($(window.document));e($(window.top.document));for(var d=window.top.frames.length;d--;){var f=$(window.top.frames[d].document);e(f)}},50);return false})}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var a={};var c=$(this);c.find("a").each(function(){var f=$(this),h=f.get(0);var d=h.getAttribute("confirm"),e=h.getAttribute("href"),g=h.getAttribute("target");a[f.text()]=function(){if(!d||confirm(d)){var i=window;if(g=="_parent"){i=window.parent}else{if(g=="_
top"){i=window.top}}i.location=e}}});var b=$("#"+c.attr("popupmenu"));b.find("a").bind("click",function(d){d.stopPropagation();return true});make_popupmenu(b,a);b.addClass("popup");c.remove()})}function naturalSort(j,h){var p=/(-?[0-9\.]+)/g,k=j.toString().toLowerCase()||"",g=h.toString().toLowerCase()||"",l=String.fromCharCode(0),n=k.replace(p,l+"$1"+l).split(l),e=g.replace(p,l+"$1"+l).split(l),d=(new Date(k)).getTime(),o=d?(new Date(g)).getTime():null;if(o){if(d<o){return -1}else{if(d>o){return 1}}}var m,f;for(var i=0,c=Math.max(n.length,e.length);i<c;i++){m=parseFloat(n[i])||n[i];f=parseFloat(e[i])||e[i];if(m<f){return -1}else{if(m>f){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var d=$(this);var g=d.find("option").length;if((g<a)||(g>b)){return}if(d.attr("multiple")===true){return}if(d.hasClass("no-autocomplete")){return}var m=d.attr("value");var c
=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",d.attr("name"));c.attr("id",d.attr("id"));c.click(function(){var n=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(n);$(this).select()});var e=[];var i={};d.children("option").each(function(){var o=$(this).text();var n=$(this).attr("value");e.push(o);i[o]=n;i[n]=n;if(n==m){c.attr("value",o)}});if(m===""||m==="?"){c.attr("value","Click to Search or Select")}if(d.attr("name")=="dbkey"){e=e.sort(naturalSort)}var f={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(e,f);d.replaceWith(c);var k=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){c.attr("value",n)}else{if(m!==""){c.attr("value",m)}else{c.attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_dbkeys",function(){k()});if(d.attr("refresh_on_change")=="true
"){var h=d.attr("refresh_on_change_values"),l=d.attr("last_selected_value");if(h!==undefined){h=h.split(",")}var j=function(){var n=i[c.attr("value")];if(l!==n&&n!==null&&n!==undefined){if(h!==undefined&&$.inArray(n,h)===-1&&$.inArray(l,h)===-1){return}c.attr("value",n);$(window).trigger("refresh_on_change");c.parents("form").submit()}};c.bind("result",j);c.keyup(function(n){if(n.keyCode===13){j()}});c.keydown(function(n){if(n.keyCode===13){return false}})}})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]
=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!==""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStore.store("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id,h=$(this).children("div.historyItemBody"),i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){var k;if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){k=$.jStore.store("history_expand_state");i
f(k){delete k[j];$.jStore.store("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){k=$.jStore.store("history_expand_state");if(k===undefined){k={}}k[j]=true;$.jStore.store("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStore.store("history_expand_state");if(h===undefined){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",h)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length===0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("searc
h_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools");b.css("font-style","italic")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({ur
l:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus();replace_big_select_inputs(20,1500)});
1
0
galaxy-dist commit 37477f7d10e7: Data Libraries: Pre-generate and cache variables so that expensive functions like jQuery.siblings, jQuery.filter and jQuery.find only have to be called a minimum amount of times. Provides significant speedup to loading of large data libraries.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kanwei Li <kanwei(a)gmail.com>
# Date 1288749068 14400
# Node ID 37477f7d10e7407186bd189fdec9e114e4b8f3d2
# Parent 4053c425b536b9878f6f8bdfac75453adec7bdf3
Data Libraries: Pre-generate and cache variables so that expensive functions like jQuery.siblings, jQuery.filter and jQuery.find only have to be called a minimum amount of times. Provides significant speedup to loading of large data libraries.
--- a/templates/library/common/browse_library.mako
+++ b/templates/library/common/browse_library.mako
@@ -68,7 +68,7 @@
}
}
};
-
+
var save_folder_state = function() {
var state = {};
$("tr.folderRow").each( function() {
@@ -79,12 +79,17 @@
};
$("#library-grid").each(function() {
- // Recursively fill in children and descendents of each row
- var process_row = function(q, parents) {
+
+ var child_of_parent_cache = {};
+ // Recursively fill in children and descendents of each row
+ var process_row = function(q, parents) {
// Find my index
- var index = q.parent().children().index(q);
+ var parent = q.parent(),
+ this_level = child_of_parent_cache[parent] || (child_of_parent_cache[parent] = parent.children());
+
+ var index = this_level.index(q);
// Find my immediate children
- var children = q.siblings().filter("[parent='" + index + "']");
+ var children = $(par_child_dict[index]);
// Recursively handle them
var descendents = children;
children.each( function() {
@@ -103,8 +108,7 @@
}
save_folder_state();
};
- $(q).find("span.expandLink").click(expand_fn);
- $(q).find("span.expandLink a").click(expand_fn);
+ $("." + q.attr("id") + "-click").click(expand_fn);
// Check/uncheck boxes in subfolders.
q.children("td").children("input[type=checkbox]").click( function() {
if ( $(this).is(":checked") ) {
@@ -112,15 +116,32 @@
} else {
descendents.find("input[type=checkbox]").attr("checked", false);
// If you uncheck a lower level checkbox, uncheck the boxes above it
- // (since deselecting a child means the parent is not fully selected any
- // more).
+ // (since deselecting a child means the parent is not fully selected any more).
parents.children("td").children("input[type=checkbox]").attr("checked", false);
}
});
// return descendents for use by parent
return descendents;
- }
- $(this).find("tbody tr").not("[parent]").each( function() {
+ }
+
+ // Initialize dict[parent_id] = rows_which_have_that_parent_id_as_parent_attr
+ var par_child_dict = {},
+ no_parent = [];
+
+ $(this).find("tbody tr").each( function() {
+ if (this.hasAttribute("parent")) {
+ var parent = this.getAttribute("parent");
+ if (par_child_dict[parent] !== undefined) {
+ par_child_dict[parent].push(this);
+ } else {
+ par_child_dict[parent] = [this];
+ }
+ } else {
+ no_parent.push(this);
+ }
+ });
+
+ $(no_parent).each( function() {
descendents = process_row( $(this), $([]) );
descendents.hide();
});
@@ -338,7 +359,8 @@
info_association, inherited = folder.get_info_association( restrict=True )
%>
%if not root_folder and ( not folder.deleted or show_deleted ):
- <tr id="folder-${trans.security.encode_id(folder.id)}" class="folderRow libraryOrFolderRow"
+ <% encoded_id = trans.security.encode_id(folder.id) %>
+ <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow"
%if parent is not None:
parent="${parent}"
style="display: none;"
@@ -349,9 +371,9 @@
%if folder.deleted:
<span class="libraryItem-error">
%endif
- <span class="expandLink"><span class="rowIcon"></span>
+ <span class="expandLink folder-${encoded_id}-click"><span class="rowIcon"></span><div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup">
- <a href="javascript:void(0);">${folder.name}</a>
+ <a class="folder-${encoded_id}-click" href="javascript:void(0);">${folder.name}</a></div>
%if folder.deleted:
</span>
1
0
galaxy-dist commit 27c152bb441a: More sample tracking bug fixes - tweaked permissions on displaying buttons, and fixed exceptions thrown when ddata transfer congid file is incorrect.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1288730067 14400
# Node ID 27c152bb441a0136720dbbc0c7cb293e581b8f5f
# Parent 6497a8cfd12e477e9c92dd183c4abb81d469ba51
More sample tracking bug fixes - tweaked permissions on displaying buttons, and fixed exceptions thrown when ddata transfer congid file is incorrect.
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -541,7 +541,7 @@ class RequestsAdmin( BaseController, Use
return sample.request.name + '_' + sample.name + '_' + name
if opt == options.EXPERIMENT_NAME:
return sample.request.name + '_' + name
- def __setup_datatx_user( self, trans, library, folder ):
+ def __setup_datatx_user( self, trans, sample ):
"""
Sets up the datatx user:
- Checks if the user exists, if not creates them.
@@ -550,9 +550,25 @@ class RequestsAdmin( BaseController, Use
"""
# Retrieve the upload user login information from the config file
config = ConfigParser.ConfigParser()
- config.read( 'transfer_datasets.ini' )
- email = config.get( "data_transfer_user_login_info", "email" )
- password = config.get( "data_transfer_user_login_info", "password" )
+ ok = True
+ try:
+ config.read( 'transfer_datasets.ini' )
+ except Exception, e:
+ message = "Error attempting to read config file named 'transfer_datasets.ini'. Make sure this file is correct."
+ ok = False
+ try:
+ email = config.get( "data_transfer_user_login_info", "email" )
+ password = config.get( "data_transfer_user_login_info", "password" )
+ except Exception, e:
+ message = "The 'data_transfer_user_login_info' section is missing from the 'transfer_datasets.ini'. Make sure this file is correct."
+ ok = False
+ if not ok:
+ status = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=trans.security.encode_id( sample.id ),
+ status=status,
+ message=message ) )
# check if the user already exists
datatx_user = trans.sa_session.query( trans.model.User ) \
.filter( trans.model.User.table.c.email==email ) \
@@ -570,14 +586,14 @@ class RequestsAdmin( BaseController, Use
datatx_user_private_role = trans.app.security_agent.get_private_user_role( datatx_user )
# Make sure this user has LIBRARY_ADD permissions on the target library and folder.
# If not, give them permission.
- if not trans.app.security_agent.can_add_library_item( datatx_user_roles, library ):
+ if not trans.app.security_agent.can_add_library_item( datatx_user_roles, sample.library ):
lp = trans.model.LibraryPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
- library,
+ sample.library,
datatx_user_private_role )
trans.sa_session.add( lp )
- if not trans.app.security_agent.can_add_library_item( datatx_user_roles, folder ):
+ if not trans.app.security_agent.can_add_library_item( datatx_user_roles, sample.folder ):
lfp = trans.model.LibraryFolderPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
- folder,
+ sample.folder,
datatx_user_private_role )
trans.sa_session.add( lfp )
trans.sa_session.flush()
@@ -646,7 +662,7 @@ class RequestsAdmin( BaseController, Use
message=message) )
def __start_datatx( self, trans, sample, selected_sample_datasets ):
- datatx_user = self.__setup_datatx_user( trans, sample.library, sample.folder )
+ datatx_user = self.__setup_datatx_user( trans, sample )
# Validate sequencer information
datatx_info = sample.request.type.datatx_info
if not datatx_info['host'] or not datatx_info['username'] or not datatx_info['password']:
@@ -660,7 +676,7 @@ class RequestsAdmin( BaseController, Use
action='manage_datasets',
sample_id=trans.security.encode_id( sample.id ),
status=status,
- message=message) )
+ message=message ) )
@web.expose
def update_sample_dataset_status(self, trans, cntrller, sample_dataset_ids, new_status, error_msg=None ):
# check if the new status is a valid transfer status
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -120,20 +120,25 @@
if sample:
trans.sa_session.refresh( sample.request )
is_complete = sample.request.is_complete
+ is_rejected = request.is_rejected
is_submitted = sample.request.is_submitted
is_unsubmitted = sample.request.is_unsubmitted
+ display_checkboxes = editing_samples and ( is_complete or is_rejected or is_submitted )
+ display_bar_code = request.samples and ( is_complete or is_rejected or is_submitted )
+ display_datasets = request.samples and ( is_complete or is_rejected or is_submitted )
else:
is_complete = False
is_submitted = False
is_unsubmitted = False
+ display_checkboxes = False
%><%
- if is_submitted and editing_samples and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids:
+ if display_checkboxes and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids:
checked_str = "checked"
else:
checked_str = ""
%>
- %if is_submitted and editing_samples:
+ %if display_checkboxes:
<td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" ${checked_str}/><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"/></td>
%endif
<td valign="top">
@@ -142,12 +147,14 @@
<i>${' (required)' }</i></div></td>
- %if sample and is_submitted or is_complete:
- %if is_admin:
- <td valign="top"><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
- %else:
- ${current_sample['barcode']}
- %endif
+ %if display_bar_code:
+ <td valign="top">
+ %if is_admin:
+ <input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/>
+ %else:
+ ${current_sample['barcode']}
+ %endif
+ </td>
%endif
%if sample:
%if is_unsubmitted:
@@ -160,7 +167,7 @@
%endif
<td valign="top">${current_sample['library_select_field'].get_html()}</td><td valign="top">${current_sample['folder_select_field'].get_html()}</td>
- %if is_submitted or is_complete:
+ %if display_datasets:
<%
if sample:
label = str( len( sample.datasets ) )
@@ -182,11 +189,15 @@
trans.sa_session.refresh( request )
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
is_complete = request.is_complete
+ is_rejected = request.is_rejected
is_submitted = request.is_submitted
is_unsubmitted = request.is_unsubmitted
can_add_samples = request.is_unsubmitted
can_delete_samples = request.samples and not is_complete
can_edit_samples = request.samples and ( is_admin or not is_complete )
+ display_checkboxes = editing_samples and ( is_complete or is_rejected or is_submitted )
+ display_bar_code = request.samples and ( is_complete or is_rejected or is_submitted )
+ display_datasets = request.samples and ( is_complete or is_rejected or is_submitted )
%>
${grid_header}
%if render_buttons and ( can_add_samples or can_edit_samples ):
@@ -202,22 +213,22 @@
<table class="grid"><thead><tr>
- %if is_submitted and editing_samples:
+ %if display_checkboxes:
<th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'/><input type="hidden" name=select_all_samples_checkbox value="true"/></th>
%endif
<th>Name</th>
- %if is_submitted or is_complete:
+ %if display_bar_code:
<th>Barcode</th>
%endif
<th>State</th><th>Data Library</th><th>Folder</th>
- %if is_submitted or is_complete:
+ %if display_datasets:
<th>Datasets Selected</th><th>Datasets Transferred</th>
%endif
<th>
- %if editing_samples:
+ %if can_delete_samples:
Delete
%endif
</th>
@@ -245,7 +256,7 @@
except:
sample = None
%>
- %if not is_complete and editing_samples:
+ %if editing_samples:
<tr>${render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
%elif sample:
<tr>
1
0
galaxy-dist commit 667043341e81: Significant cleanup for the Sample Tracking UI. Things are more streamlined and Galaxy UI standards are now followed.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1288649810 14400
# Node ID 667043341e81261a7fe587daa475110486b15292
# Parent c4d8ffb3109e8cfb8143dc87c88dee3337bb7569
Significant cleanup for the Sample Tracking UI. Things are more streamlined and Galaxy UI standards are now followed.
--- a/templates/requests/common/sample_events.mako
+++ b/templates/requests/common/sample_events.mako
@@ -5,10 +5,7 @@
<h2>Events for Sample "${sample.name}"</h2><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">
- <span>Browse this request</span></a>
- </li>
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li></ul><h3>Sequencing Request "${sample.request.name}"</h3>
--- /dev/null
+++ b/templates/requests/common/edit_samples.mako
@@ -0,0 +1,240 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/requests/common/common.mako" import="common_javascripts" />
+<%namespace file="/requests/common/common.mako" import="render_samples_grid" />
+<%namespace file="/requests/common/common.mako" import="render_request_type_sample_form_grids" />
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
+</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${common_javascripts()}
+ ${local_javascripts()}
+</%def>
+
+<%def name="local_javascripts()">
+ <script type="text/javascript">
+ // Looks for changes in sample states using an async request. Keeps
+ // calling itself (via setTimeout) until all samples are in a terminal
+ // state.
+ var updater = function ( sample_states ) {
+ // Check if there are any items left to track
+ var empty = true;
+ for ( i in sample_states ) {
+ empty = false;
+ break;
+ }
+ if ( ! empty ) {
+ setTimeout( function() { updater_callback( sample_states ) }, 1000 );
+ }
+ };
+
+ var updater_callback = function ( sample_states ) {
+ // Build request data
+ var ids = []
+ var states = []
+ $.each( sample_states, function ( id, state ) {
+ ids.push( id );
+ states.push( state );
+ });
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
+ dataType: "json",
+ data: { ids: ids.join( "," ), states: states.join( "," ) },
+ success : function ( data ) {
+ $.each( data, function( id, val ) {
+ // Replace HTML
+ var cell1 = $("#sampleState-" + id);
+ cell1.html( val.html_state );
+ var cell2 = $("#sampleDatasets-" + id);
+ cell2.html( val.html_datasets );
+ sample_states[ parseInt( id ) ] = val.state;
+ });
+ updater( sample_states );
+ },
+ error: function() {
+ // Just retry, like the old method, should try to be smarter
+ updater( sample_states );
+ }
+ });
+ };
+ </script>
+</%def>
+
+<%
+ from galaxy.web.framework.helpers import time_ago
+
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = request.is_complete
+ is_unsubmitted = request.is_unsubmitted
+ can_add_samples = is_unsubmitted
+ can_edit_or_delete_samples = is_unsubmitted and request.samples
+ can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_reject_or_transfer = is_admin and request.is_submitted
+%>
+
+<br/><br/>
+
+<ul class="manage-table-actions">
+ %if not editing_samples and can_edit_or_delete_samples:
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples='True' )}">Edit samples</a></li>
+ %endif
+ %if editing_samples and can_add_samples:
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
+ %endif
+ %if is_unsubmitted:
+ <li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
+ %endif
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if can_reject_or_transfer:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div>
+</ul>
+
+%if request.samples_without_library_destinations:
+ <br/>
+ <font color="red"><b><i>Select a target data library and folder for all samples before starting the sequence run</i></b></font>
+ <br/>
+%endif
+
+%if request.is_rejected:
+ <br/>
+ <font color="red"><b><i>Reason for rejection: </i></b></font><b>${request.last_comment}</b>
+ <br/>
+%endif
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolFormBody">
+ <form id="edit_samples" name="edit_samples" action="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples=editing_samples )}" method="post">
+ %if current_samples:
+ <%
+ if editing_samples:
+ grid_header = '<h3>Edit Current Samples of Request "%s"</h3>' % request.name
+ else:
+ grid_header = '<h3>Add Samples to Request "%s"</h3>' % request.name
+ %>
+ ${render_samples_grid( cntrller, request, current_samples, action='edit_samples', editing_samples=editing_samples, encoded_selected_sample_ids=encoded_selected_sample_ids, render_buttons=False, grid_header=grid_header )}
+ %if editing_samples and len( sample_operation_select_field.options ) > 1 and not ( is_unsubmitted or is_complete ):
+ <div class="form-row" style="background-color:#FAFAFA;">
+ For selected samples:
+ ${sample_operation_select_field.get_html()}
+ </div>
+ <% sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True ) %>
+ %if sample_operation_selected_value != 'none' and encoded_selected_sample_ids:
+ <div class="form-row" style="background-color:#FAFAFA;">
+ %if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE:
+ ## sample_operation_selected_value == 'Change state'
+ <div class="form-row">
+ <label>Change current state</label>
+ ${sample_state_id_select_field.get_html()}
+ <label>Comments</label>
+ <input type="text" name="sample_event_comment" value=""/>
+ <div class="toolParamHelp" style="clear: both;">
+ Optional
+ </div>
+ </div>
+ %elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
+ <% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %>
+ <div class="form-row">
+ <label>Select data library:</label>
+ ${libraries_select_field.get_html()}
+ </div>
+ %if libraries_selected_value != 'none':
+ <div class="form-row">
+ <label>Select folder:</label>
+ ${folders_select_field.get_html()}
+ </div>
+ %endif
+ %endif
+ </div>
+ %endif
+ %endif
+ ## Render the other grids
+ <% trans.sa_session.refresh( request.type.sample_form ) %>
+ %for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
+ ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), editing_samples=editing_samples )}
+ %endfor
+ %else:
+ <label>There are no samples.</label>
+ %endif
+ %if not editing_samples and is_unsubmitted:
+ ## The user is adding a new sample
+ %if current_samples:
+ <p/>
+ <div class="form-row">
+ <label> Copy <input type="text" name="num_sample_to_copy" value="1" size="3"/> samples from sample ${sample_copy.get_html()}</label>
+ <div class="toolParamHelp" style="clear: both;">
+ Select the sample from which the new sample should be copied or leave selection as <b>None</b> to add a new "generic" sample.
+ </div>
+ </div>
+ %endif
+ <p/>
+ <div class="form-row">
+ %if ( request.samples or current_samples ) and ( editing_samples or len( current_samples ) > len( request.samples ) ):
+ <input type="submit" name="add_sample_button" value="Add sample"/>
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Click the <b>Add sample</b> button for each new sample and click the <b>Save</b> button when you have finished adding samples.
+ </div>
+ %else:
+ <input type="submit" name="add_sample_button" value="Add sample"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Click the <b>Add sample</b> button for each new sample.
+ </div>
+ %endif
+ </div>
+ %elif editing_samples:
+ <p/>
+ <div class="form-row">
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Click the <b>Save</b> button when you have finished editing the samples
+ </div>
+ %endif
+ %if request.samples and request.is_submitted:
+ <script type="text/javascript">
+ // Updater
+ updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
+ </script>
+ %endif
+ </form>
+</div>
+%if is_unsubmitted and not editing_samples:
+ <p/>
+ ##<div class="toolForm">
+ ##<div class="toolFormTitle">Import samples from csv file</div>
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> Import samples from csv file</h4>
+ <div style="display:none;">
+ <div class="toolFormBody">
+ <form id="import" name="import" action="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples=editing_samples )}" enctype="multipart/form-data" method="post" >
+ <div class="form-row">
+ <input type="file" name="file_data" />
+ <input type="submit" name="import_samples_button" value="Import samples"/>
+ <div class="toolParamHelp" style="clear: both;">
+ The csv file must be in the following format:<br/>
+ SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ ##</div>
+%endif
--- a/test/functional/test_forms_and_requests.py
+++ b/test/functional/test_forms_and_requests.py
@@ -225,15 +225,12 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed_after_submit = [ 'Unsubmitted' ]
for sample_name, field_values in sample_value_tuples:
strings_displayed_after_submit.append( sample_name )
- for field_value in field_values:
- strings_displayed_after_submit.append( field_value )
# Add samples to the request
self.add_samples( cntrller='requests',
request_id=self.security.encode_id( request_one.id ),
request_name=request_one.name,
sample_value_tuples=sample_value_tuples,
- strings_displayed=[ 'Sequencing Request "%s"' % request_one.name,
- 'There are no samples.' ],
+ strings_displayed=[ 'There are no samples.' ],
strings_displayed_after_submit=strings_displayed_after_submit )
def test_030_edit_basic_request_info( self ):
"""Testing editing the basic information of a sequence run request"""
@@ -277,11 +274,11 @@ class TestFormsAndRequests( TwillTestCas
self.check_request_grid( cntrller='requests_admin',
state=request_one.states.SUBMITTED,
strings_displayed=[ request_one.name ] )
- self.visit_url( "%s/requests_common/manage_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) )
- self.check_page_for_string( 'Sequencing Request "%s"' % request_one.name )
+ self.visit_url( "%s/requests_common/view_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) )
+ # TODO: add some string for checking on the page above...
# Set bar codes for the samples
bar_codes = [ '1234567890', '0987654321' ]
- strings_displayed_after_submit=[ 'Changes made to the samples are saved.' ]
+ strings_displayed_after_submit=[ 'Changes made to the samples have been saved.' ]
for bar_code in bar_codes:
strings_displayed_after_submit.append( bar_code )
self.add_bar_codes( request_id=self.security.encode_id( request_one.id ),
@@ -333,7 +330,7 @@ class TestFormsAndRequests( TwillTestCas
test_field_name1,
test_field_name2,
test_field_name3 ],
- strings_displayed_after_submit=[ "The request has been created" ] )
+ strings_displayed_after_submit=[ "The request has been created." ] )
global request_two
request_two = get_request_by_name( name )
# Make sure the request is showing in the 'new' filter
@@ -349,15 +346,12 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed_after_submit = [ 'Unsubmitted' ]
for sample_name, field_values in sample_value_tuples:
strings_displayed_after_submit.append( sample_name )
- for field_value in field_values:
- strings_displayed_after_submit.append( field_value )
# Add samples to the request
self.add_samples( cntrller='requests_admin',
request_id=self.security.encode_id( request_two.id ),
request_name=request_two.name,
sample_value_tuples=sample_value_tuples,
- strings_displayed=[ 'Sequencing Request "%s"' % request_two.name,
- 'There are no samples.' ],
+ strings_displayed=[ 'There are no samples.' ],
strings_displayed_after_submit=strings_displayed_after_submit )
# Submit the request
self.submit_request( cntrller='requests_admin',
@@ -394,6 +388,7 @@ class TestFormsAndRequests( TwillTestCas
% ( request_two.name, request_two.states.REJECTED )
def test_055_reset_data_for_later_test_runs( self ):
"""Reseting data to enable later test runs to pass"""
+ """
# Logged in as admin_user
##################
# Delete request_type permissions
@@ -438,3 +433,4 @@ class TestFormsAndRequests( TwillTestCas
# Manually delete the group from the database
refresh( group )
delete( group )
+ """
--- a/templates/admin/requests/rename_datasets.mako
+++ b/templates/admin/requests/rename_datasets.mako
@@ -10,7 +10,7 @@
<a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=trans.security.encode_id( sample.id ) )}">Browse datasets</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller='requests_admin', id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller='requests_admin', id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li></ul>
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -158,9 +158,9 @@ class RequestsAdmin( BaseController, Use
action='edit_basic_request_info',
cntrller='requests_admin',
**kwd ) )
- if operation == "manage_request":
+ if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
**kwd ) )
if operation == "request_events":
@@ -186,14 +186,14 @@ class RequestsAdmin( BaseController, Use
return self.request_grid( trans, **kwd )
@web.expose
@web.require_admin
- def reject( self, trans, **kwd ):
+ def reject_request( self, trans, **kwd ):
params = util.Params( kwd )
request_id = params.get( 'id', '' )
status = params.get( 'status', 'done' )
message = params.get( 'message', 'done' )
if params.get( 'cancel_reject_button', False ):
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
id=request_id ) )
try:
@@ -292,7 +292,6 @@ class RequestsAdmin( BaseController, Use
return invalid_id_redirect( trans, 'requests_admin', sample_id )
request_id = trans.security.encode_id( sample.request.id )
library_id = trans.security.encode_id( sample.library.id )
- self.datatx_grid.title = 'Datasets of sample "%s"' % sample.name
self.datatx_grid.global_actions = [ grids.GridAction( "Refresh",
dict( controller='requests_admin',
action='manage_datasets',
@@ -303,14 +302,14 @@ class RequestsAdmin( BaseController, Use
request_id=request_id,
folder_path=sample.request.type.datatx_info[ 'data_dir' ],
sample_id=sample_id ) ),
- grids.GridAction( 'Data library "%s"' % sample.library.name,
- dict( controller='library_common',
- action='browse_library',
- cntrller='library_admin',
- id=library_id ) ),
+ #grids.GridAction( 'Data library "%s"' % sample.library.name,
+ # dict( controller='library_common',
+ # action='browse_library',
+ # cntrller='library_admin',
+ # id=library_id ) ),
grids.GridAction( "Browse this request",
dict( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
id=request_id ) ) ]
return self.datatx_grid( trans, **kwd )
--- a/templates/requests/common/edit_basic_request_info.mako
+++ b/templates/requests/common/edit_basic_request_info.mako
@@ -1,14 +1,25 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
+<%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ can_add_samples = request.is_unsubmitted
+%>
+
<br/><br/><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse all requests</a>
- </li>
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request Actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ %if can_add_samples:
+ <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div></ul>
%if message:
--- /dev/null
+++ b/templates/requests/common/view_request.mako
@@ -0,0 +1,157 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/requests/common/common.mako" import="common_javascripts" />
+<%namespace file="/requests/common/common.mako" import="render_samples_grid" />
+<%namespace file="/requests/common/common.mako" import="render_request_type_sample_form_grids" />
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
+</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${common_javascripts()}
+</%def>
+
+<%
+ from galaxy.web.framework.helpers import time_ago
+
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_unsubmitted = request.is_unsubmitted
+ can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_add_samples = is_unsubmitted
+%>
+
+<br/><br/>
+
+<ul class="manage-table-actions">
+ %if is_unsubmitted:
+ <li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
+ %endif
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div>
+</ul>
+
+%if request.is_rejected:
+ <font color="red"><b><i>Reason for rejection: </i></b></font><b>${request.last_comment}</b>
+ <br/><br/>
+%endif
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Sequencing request "${request.name}"</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ <label>Current state:</label>
+ ${request.state}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Description:</label>
+ ${request.desc}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>User:</label>
+ %if is_admin:
+ ${request.user.email}
+ %elif request.user.username:
+ ${request.user.username}
+ %else:
+ Unknown
+ %endif
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> More</h4>
+ <div style="display:none;">
+ %for index, rd in enumerate( request_widgets ):
+ <%
+ field_label = rd[ 'label' ]
+ field_value = rd[ 'value' ]
+ %>
+ <div class="form-row">
+ <label>${field_label}:</label>
+ %if field_label == 'State':
+ <a href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">${field_value}</a>
+ %else:
+ ${field_value}
+ %endif
+ </div>
+ <div style="clear: both"></div>
+ %endfor
+ <div class="form-row">
+ <label>Date created:</label>
+ ${request.create_time}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Last updated:</label>
+ ${time_ago( request.update_time )}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Email recipients:</label>
+ <%
+ if request.notification:
+ emails = ', '.join( request.notification[ 'email' ] )
+ else:
+ emails = ''
+ %>
+ ${emails}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Send email when state changes to:</label>
+ <%
+ if request.notification:
+ states = []
+ for ss in request.type.states:
+ if ss.id in request.notification[ 'sample_states' ]:
+ states.append( ss.name )
+ states = ', '.join( states )
+ else:
+ states = ''
+ %>
+ ${states}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Sequencer configuration:</label>
+ ${request.type.name}
+ <div style="clear: both"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<p/>
+%if current_samples:
+ <% grid_header = '<h3>Samples</h3>' %>
+ ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=can_edit_request, grid_header=grid_header )}
+%else:
+ There are no samples.
+ %if can_add_samples:
+ <ul class="manage-table-actions">
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
+ </ul>
+ %endif
+%endif
+## Render the other grids
+<% trans.sa_session.refresh( request.type.sample_form ) %>
+%for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
+ ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), editing_samples=False )}
+%endfor
--- a/lib/galaxy/web/controllers/library.py
+++ b/lib/galaxy/web/controllers/library.py
@@ -24,13 +24,11 @@ class LibraryListGrid( grids.Grid ):
columns = [
NameColumn( "Name",
key="name",
- model_class=model.Library,
link=( lambda library: dict( operation="browse", id=library.id ) ),
attach_popup=False,
filterable="advanced" ),
DescriptionColumn( "Description",
key="description",
- model_class=model.Library,
attach_popup=False,
filterable="advanced" ),
]
--- a/templates/admin/forms/show_form_read_only.mako
+++ b/templates/admin/forms/show_form_read_only.mako
@@ -81,10 +81,10 @@
<form name="library" action="${h.url_for( controller='forms', action='manage' )}" method="post" >
%if form_definition.type == trans.app.model.FormDefinition.types.SAMPLE:
%if not len(form_definition.layout):
- ${render_grid( 0, '', form_definition.fields_of_grid( None ) )}
+ ${render_grid( 0, '', form_definition.grid_fields( None ) )}
%else:
%for grid_index, grid_name in enumerate(form_definition.layout):
- ${render_grid( grid_index, grid_name, form_definition.fields_of_grid( grid_index ) )}
+ ${render_grid( grid_index, grid_name, form_definition.grid_fields( grid_index ) )}
%endfor
%endif
%else:
--- a/templates/library/common/browse_library.mako
+++ b/templates/library/common/browse_library.mako
@@ -346,7 +346,6 @@
><td style="padding-left: ${folder_pad}px;"><input type="checkbox" class="folderCheckbox"/>
-
%if folder.deleted:
<span class="libraryItem-error">
%endif
@@ -354,7 +353,6 @@
<div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup"><a href="javascript:void(0);">${folder.name}</a></div>
-
%if folder.deleted:
</span>
%endif
--- a/templates/admin/requests/reject.mako
+++ b/templates/admin/requests/reject.mako
@@ -8,16 +8,16 @@
<h2>Reject Sequencing Request "${request.name}"</h2><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Events</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">View history</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Browse this request</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Browse this request</a></li></ul><div class="toolForm"><div class="toolFormTitle">Reject request</div>
- <form name="event" action="${h.url_for( controller='requests_admin', action='reject', id=trans.security.encode_id( request.id ) )}" method="post" >
+ <form name="event" action="${h.url_for( controller='requests_admin', action='reject_request', id=trans.security.encode_id( request.id ) )}" method="post" ><div class="form-row">
Rejecting this request will move the request state to <b>Rejected</b>.
</div>
--- a/templates/admin/requests/get_data.mako
+++ b/templates/admin/requests/get_data.mako
@@ -75,7 +75,7 @@
<a class="action-button" href="${h.url_for( controller='requests_admin', action='view_request_type', id=trans.security.encode_id( request.type.id ) )}">Sequencer configuration "${request.type.name}"</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a></li></ul>
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1488,25 +1488,23 @@ class MetadataFile( object ):
return os.path.abspath( os.path.join( path, "metadata_%d.dat" % self.id ) )
class FormDefinition( object ):
- types = Bunch( REQUEST = 'Sequencing Request Form',
- SAMPLE = 'Sequencing Sample Form',
- LIBRARY_INFO_TEMPLATE = 'Library information template',
- USER_INFO = 'User Information' )
- def __init__(self, name=None, desc=None, fields=[],
- form_definition_current=None, form_type=None, layout=None):
+ types = Bunch( REQUEST = 'Sequencing Request Form',
+ SAMPLE = 'Sequencing Sample Form',
+ LIBRARY_INFO_TEMPLATE = 'Library information template',
+ USER_INFO = 'User Information' )
+ def __init__( self, name=None, desc=None, fields=[], form_definition_current=None, form_type=None, layout=None ):
self.name = name
self.desc = desc
self.fields = fields
self.form_definition_current = form_definition_current
self.type = form_type
self.layout = layout
- def fields_of_grid(self, grid_index):
- '''
- This method returns the list of fields belonging to the given grid.
- '''
+ def grid_fields( self, grid_index ):
+ # Returns a dictionary whose keys are integers corresponding to field positions
+ # on the grid and whose values are the field.
gridfields = {}
- for i, f in enumerate(self.fields):
- if str(f['layout']) == str(grid_index):
+ for i, f in enumerate( self.fields ):
+ if str( f[ 'layout' ] ) == str( grid_index ):
gridfields[i] = f
return gridfields
def get_widgets( self, user, contents=[], **kwd ):
--- a/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -10,11 +10,10 @@ log = logging.getLogger( __name__ )
class UserRequestsGrid( RequestsGrid ):
operations = [ operation for operation in RequestsGrid.operations ]
- operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted and item.is_unsubmitted ) ) )
- operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.is_new ) ) )
+ operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: item.is_unsubmitted and not item.deleted ) ) )
+ operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.is_new and not item.deleted ) ) )
operations.append( grids.GridOperation( "Undelete", allow_multiple=True, condition=( lambda item: item.deleted ) ) )
def apply_query_filter( self, trans, query, **kwd ):
- # gvk ( 9/28/10 ) TODO: is this method needed?
return query.filter_by( user=trans.user )
class Requests( BaseController ):
@@ -37,9 +36,9 @@ class Requests( BaseController ):
action='edit_basic_request_info',
cntrller='requests',
**kwd ) )
- if operation == "manage_request":
+ if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests',
**kwd ) )
if operation == "delete":
@@ -70,12 +69,12 @@ class Requests( BaseController ):
message = "%d requests (highlighted in red) were rejected. Click on the request name for details." % rejected
kwd[ 'status' ] = status
kwd[ 'message' ] = message
- # show the create request button to the user, only when the user has permissions
- # to at least one request_type (sequencer configuration)
+ # Allow the user to create a new request only if they have permission to access a
+ # (sequencer configuration) request type.
if len( trans.user.accessible_request_types( trans ) ):
self.request_grid.global_actions = [ grids.GridAction( "Create new request", dict( controller='requests_common',
- action='create_request',
- cntrller='requests' ) ) ]
+ action='create_request',
+ cntrller='requests' ) ) ]
else:
self.request_grid.global_actions = []
# Render the list view
--- a/templates/requests/common/find_samples.mako
+++ b/templates/requests/common/find_samples.mako
@@ -76,7 +76,7 @@
<i>User: ${sample.request.user.email}</i>
%endif
<div class="toolParamHelp" style="clear: both;">
- <a href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Sequencing request: ${sample.request.name} | Type: ${sample.request.type.name} | State: ${sample.request.state}</a>
+ <a href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Sequencing request: ${sample.request.name} | Type: ${sample.request.type.name} | State: ${sample.request.state}</a></div></div><br/>
--- a/lib/galaxy/web/controllers/forms.py
+++ b/lib/galaxy/web/controllers/forms.py
@@ -86,7 +86,7 @@ class Forms( BaseController ):
status='error',
message="Invalid form ID") )
if operation == "view":
- return self.__view( trans, **kwd )
+ return self.view_form_definition( trans, **kwd )
elif operation == "delete":
return self.__delete( trans, **kwd )
elif operation == "undelete":
@@ -94,10 +94,12 @@ class Forms( BaseController ):
elif operation == "edit":
return self.edit( trans, **kwd )
return self.forms_grid( trans, **kwd )
- def __view(self, trans, **kwd):
+ @web.expose
+ def view_form_definition( self, trans, **kwd ):
+ form_definition_current_id = kwd.get( 'id', None )
try:
- fdc = trans.sa_session.query( trans.app.model.FormDefinitionCurrent )\
- .get( trans.security.decode_id(kwd['id']) )
+ fdc = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ) \
+ .get( trans.security.decode_id( form_definition_current_id ) )
except:
return trans.response.send_redirect( web.url_for( controller='forms',
action='manage',
--- /dev/null
+++ b/templates/requests/common/common.mako
@@ -0,0 +1,332 @@
+<%namespace file="/requests/common/sample_state.mako" import="render_sample_state" />
+
+<%def name="javascripts()">
+ ${self.common_javascripts()}
+</%def>
+
+<%def name="common_javascripts()">
+ <script type="text/javascript">
+ function showContent(vThis)
+ {
+ // http://www.javascriptjunkie.com
+ // alert(vSibling.className + " " + vDef_Key);
+ vParent = vThis.parentNode;
+ vSibling = vParent.nextSibling;
+ while (vSibling.nodeType==3) {
+ // Fix for Mozilla/FireFox Empty Space becomes a TextNode or Something
+ vSibling = vSibling.nextSibling;
+ };
+ if(vSibling.style.display == "none")
+ {
+ vThis.src="/static/images/fugue/toggle.png";
+ vThis.alt = "Hide";
+ vSibling.style.display = "block";
+ } else {
+ vSibling.style.display = "none";
+ vThis.src="/static/images/fugue/toggle-expand.png";
+ vThis.alt = "Show";
+ }
+ return;
+ }
+ $(document).ready(function(){
+ //hide the all of the element with class msg_body
+ $(".msg_body").hide();
+ //toggle the component with class msg_body
+ $(".msg_head").click(function(){
+ $(this).next(".msg_body").slideToggle(0);
+ });
+ });
+
+ function checkAllFields()
+ {
+ var chkAll = document.getElementById('checkAll');
+ var checks = document.getElementsByTagName('input');
+ var boxLength = checks.length;
+ var allChecked = false;
+ var totalChecked = 0;
+ if ( chkAll.checked == true )
+ {
+ for ( i=0; i < boxLength; i++ )
+ {
+ if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
+ {
+ checks[i].checked = true;
+ }
+ }
+ }
+ else
+ {
+ for ( i=0; i < boxLength; i++ )
+ {
+ if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
+ {
+ checks[i].checked = false
+ }
+ }
+ }
+ }
+ </script>
+</%def>
+
+<%def name="render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )">
+ <%
+ if sample:
+ is_complete = sample.request.is_complete
+ is_submitted = sample.request.is_submitted
+ is_unsubmitted = sample.request.is_unsubmitted
+ else:
+ is_complete = False
+ is_submitted = False
+ is_unsubmitted = False
+ %>
+ <%
+ if is_admin and is_submitted and editing_samples and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids:
+ checked_str = "checked"
+ else:
+ checked_str = ""
+ %>
+ %if is_admin and is_submitted and editing_samples:
+ <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" ${checked_str}/><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"/></td>
+ %endif
+ <td>
+ <input type="text" name="sample_${current_sample_index}_name" value="${current_sample['name']}" size="10"/>
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${' (required)' }</i>
+ </div>
+ </td>
+ %if sample and is_submitted or is_complete:
+ <td><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
+ %endif
+ %if sample:
+ %if is_unsubmitted:
+ <td>Unsubmitted</td>
+ %else:
+ <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
+ %endif
+ %else:
+ <td></td>
+ %endif
+ <td>${current_sample['library_select_field'].get_html()}</td>
+ <td>${current_sample['folder_select_field'].get_html()}</td>
+ %if is_submitted or is_complete:
+ <%
+ if sample:
+ label = str( len( sample.datasets ) )
+ else:
+ label = 'add'
+ %>
+ <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ %endif
+ %if sample and ( is_admin or is_unsubmitted ):
+ ## Delete button
+ <td><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td>
+ %endif
+</%def>
+
+<%def name="render_samples_grid( cntrller, request, current_samples, action, editing_samples=False, encoded_selected_sample_ids=[], render_buttons=False, grid_header='<h3>Samples</h3>' )">
+ ## Displays the "Samples" grid
+ <%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = request.is_complete
+ is_submitted = request.is_submitted
+ is_unsubmitted = request.is_unsubmitted
+ can_add_samples = request.is_unsubmitted
+ can_edit_or_delete_samples = request.samples and ( is_admin or request.is_unsubmitted )
+ %>
+ ${grid_header}
+ %if render_buttons and ( can_add_samples or can_edit_or_delete_samples ):
+ <ul class="manage-table-actions">
+ %if can_add_samples:
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
+ %endif
+ %if can_edit_or_delete_samples:
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples='True' )}">Edit samples</a></li>
+ %endif
+ </ul>
+ %endif
+ <table class="grid">
+ <thead>
+ <tr>
+ %if is_admin and is_submitted and editing_samples:
+ <th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'/><input type="hidden" name=select_all_samples_checkbox value="true"/></th>
+ %endif
+ <th>Name</th>
+ %if is_submitted or is_complete:
+ <th>Barcode</th>
+ %endif
+ <th>State</th>
+ <th>Data Library</th>
+ <th>Folder</th>
+ %if is_submitted or is_complete:
+ <th>Datasets Selected</th>
+ <th>Datasets Transferred</th>
+ %endif
+ <th>
+ %if editing_samples:
+ Delete
+ %endif
+ </th>
+ </tr>
+ <thead>
+ <tbody>
+ <% trans.sa_session.refresh( request ) %>
+ ## current_samples is a dictionary whose keys are:
+ ## name, barcode, library, folder, field_values, library_select_field, folder_select_field
+ %for current_sample_index, current_sample in enumerate( current_samples ):
+ <%
+ current_sample_name = current_sample[ 'name' ]
+ current_sample_barcode = current_sample[ 'barcode' ]
+ current_sample_library = current_sample[ 'library' ]
+ if current_sample_library:
+ if cntrller == 'requests':
+ library_cntrller = 'library'
+ elif is_admin:
+ library_cntrller = 'library_admin'
+ else:
+ library_cntrller = None
+ current_sample_folder = current_sample[ 'folder' ]
+ try:
+ sample = request.samples[ current_sample_index ]
+ except:
+ sample = None
+ %>
+ %if editing_samples:
+ <tr>${render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
+ %elif sample:
+ <tr>
+ <td>${current_sample_name}</td>
+ %if is_submitted or is_complete:
+ <td>${current_sample_barcode}</td>
+ %endif
+ %if is_unsubmitted:
+ <td>Unsubmitted</td>
+ %else:
+ <td><a id="sampleState-${sample.id}" href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${render_sample_state( sample )}</a></td>
+ %endif
+ %if current_sample_library and library_cntrller is not None:
+ <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller=library_cntrller, id=trans.security.encode_id( current_sample_library.id ) )}">${current_sample_library.name}</a></td>
+ %else:
+ <td></td>
+ %endif
+ %if current_sample_folder:
+ <td>${current_sample_folder.name}</td>
+ %else:
+ <td></td>
+ %endif
+ %if is_submitted or is_complete:
+ <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a></td>
+ <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a></td>
+ %endif
+ </tr>
+ %else:
+ <tr>${render_editable_sample_row( is_admin, None, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
+ %endif
+ %endfor
+ </tbody>
+ </table>
+</%def>
+
+<%def name="render_sample_form( index, sample_name, sample_values, fields_dict, display_only )">
+ <tr>
+ <td>${sample_name}</td>
+ %for field_index, field in fields_dict.items():
+ <%
+ field_type = field[ 'type' ]
+ %>
+ <td>
+ %if display_only:
+ %if sample_values[field_index]:
+ %if field_type == 'WorkflowField':
+ %if str(sample_values[field_index]) != 'none':
+ <% workflow = trans.sa_session.query( trans.app.model.StoredWorkflow ).get( int( sample_values[ field_index ] ) ) %>
+ <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id( workflow.id ) )}">${workflow.name}</a>
+ %endif
+ %else:
+ ${sample_values[ field_index ]}
+ %endif
+ %else:
+ <i>None</i>
+ %endif
+ %else:
+ %if field_type == 'TextField':
+ <input type="text" name="sample_${index}_field_${field_index}" value="${sample_values[field_index]}" size="7"/>
+ %elif field_type == 'SelectField':
+ <select name="sample_${index}_field_${field_index}" last_selected_value="2">
+ %for option_index, option in enumerate(field['selectlist']):
+ %if option == sample_values[field_index]:
+ <option value="${option}" selected>${option}</option>
+ %else:
+ <option value="${option}">${option}</option>
+ %endif
+ %endfor
+ </select>
+ %elif field_type == 'WorkflowField':
+ <select name="sample_${index}_field_${field_index}">
+ %if str(sample_values[field_index]) == 'none':
+ <option value="none" selected>Select one</option>
+ %else:
+ <option value="none">Select one</option>
+ %endif
+ %for option_index, option in enumerate(request.user.stored_workflows):
+ %if not option.deleted:
+ %if str(option.id) == str(sample_values[field_index]):
+ <option value="${option.id}" selected>${option.name}</option>
+ %else:
+ <option value="${option.id}">${option.name}</option>
+ %endif
+ %endif
+ %endfor
+ </select>
+ %elif field_type == 'CheckboxField':
+ <input type="checkbox" name="sample_${index}_field_${field_index}" value="Yes"/>
+ %endif
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${'('+field['required']+')' }</i>
+ </div>
+ %endif
+ </td>
+ %endfor
+ </tr>
+</%def>
+
+<%def name="render_request_type_sample_form_grids( grid_index, grid_name, fields_dict, editing_samples )">
+ <%
+ if not grid_name:
+ grid_name = "Sample form layout " + grid_index
+ %>
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
+ <div style="display:none;">
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Name</th>
+ %for index, field in fields_dict.items():
+ <th>
+ ${field['label']}
+ ## TODO: help comments in the grid header are UGLY!
+ ## If they are needed display them more appropriately,
+ ## if they are not, delete this commented code.
+ ##<div class="toolParamHelp" style="clear: both;">
+ ## <i>${field['helptext']}</i>
+ ##</div>
+ </th>
+ %endfor
+ <th></th>
+ </tr>
+ <thead>
+ <tbody>
+ <% trans.sa_session.refresh( request ) %>
+ %for sample_index, sample in enumerate( current_samples ):
+ <%
+ if editing_samples or sample_index >= len( request.samples ):
+ display_only = False
+ else:
+ display_only = True
+ %>
+ ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict, display_only )}
+ %endfor
+ </tbody>
+ </table>
+ </div>
+</%def>
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -1489,17 +1489,17 @@ class TwillTestCase( unittest.TestCase )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def add_samples( self, cntrller, request_id, request_name, sample_value_tuples, strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests_common/manage_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id ) )
+ self.visit_url( "%s/requests_common/edit_samples?cntrller=%s&id=%s&editing_samples=False" % ( self.url, cntrller, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
# Simulate clicking the add-sample_button on the form. (gvk: 9/21/10 - TODO : There must be a bug in the mako template
# because twill cannot find any forms on the page, but I cannot find it although I've spent time cleaning up the
# template code and looking for any problems.
- url = "%s/requests_common/manage_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id )
+ url = "%s/requests_common/edit_samples?cntrller=%s&id=%s&editing_samples=False" % ( self.url, cntrller, request_id )
# This should work, but although twill does not thorw any exceptions, the button click never occurs
- # There are multiple forms on this page, and we'll only be using the form named manage_request.
+ # There are multiple forms on this page, and we'll only be using the form named edit_samples.
# for sample_index, sample_value_tuple in enumerate( sample_value_tuples ):
- # # Add the following form value to the already populated hidden field so that the manage_request
+ # # Add the following form value to the already populated hidden field so that the edit_samples
# # form is the current form
# tc.fv( "1", "id", request_id )
# tc.submit( 'add_sample_button' )
@@ -1530,7 +1530,7 @@ class TwillTestCase( unittest.TestCase )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def reject_request( self, request_id, request_name, comment, strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests_admin/reject?id=%s" % ( self.url, request_id ) )
+ self.visit_url( "%s/requests_admin/reject_request?id=%s" % ( self.url, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
tc.fv( "1", "comment", comment )
@@ -1540,7 +1540,7 @@ class TwillTestCase( unittest.TestCase )
def add_bar_codes( self, request_id, request_name, bar_codes, samples, strings_displayed_after_submit=[] ):
# We have to simulate the form submission here since twill barfs on the page
# gvk - 9/22/10 - TODO: make sure the mako template produces valid html
- url = "%s/requests_common/manage_request?cntrller=requests_admin&id=%s&managing_samples=True" % ( self.url, request_id )
+ url = "%s/requests_common/edit_samples?cntrller=requests_admin&id=%s&editing_samples=True" % ( self.url, request_id )
for index, field_value in enumerate( bar_codes ):
sample_field_name = "sample_%i_name" % index
sample_field_value = samples[ index ].name.replace( ' ', '+' )
@@ -1555,15 +1555,15 @@ class TwillTestCase( unittest.TestCase )
strings_displayed=[], strings_displayed_after_submit=[] ):
# We have to simulate the form submission here since twill barfs on the page
# gvk - 9/22/10 - TODO: make sure the mako template produces valid html
- url = "%s/requests_common/manage_request?cntrller=requests_admin&id=%s" % ( self.url, request_id )
+ url = "%s/requests_common/edit_samples?cntrller=requests_admin&id=%s" % ( self.url, request_id )
url += "&comment=%s&sample_state_id=%s" % ( comment, self.security.encode_id( new_sample_state_id ) )
# select_sample_%i=true must be included twice for each sample to simulate a CheckboxField checked setting.
for sample_id in sample_ids:
url += "&select_sample_%i=true&select_sample_%i=true" % ( sample_id, sample_id )
url += "&sample_operation=Change%20state&refresh=true"
- url += "&change_state_button=Save"
+ url += "&save_changes_button=Save&editing_samples=True"
self.visit_url( url )
- self.check_page_for_string( 'Sequencing Request "%s"' % request_name )
+ self.check_page_for_string( 'Edit Current Samples of Request "%s"' % request_name )
for sample_id, sample_name in zip( sample_ids, sample_names ):
self.visit_url( "%s/requests_common/sample_events?cntrller=requests_admin&sample_id=%s" % ( self.url, self.security.encode_id( sample_id ) ) )
self.check_page_for_string( 'Events for Sample "%s"' % sample_name )
--- a/templates/requests/common/events.mako
+++ b/templates/requests/common/events.mako
@@ -1,20 +1,36 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
-<h2>History of Sequencing Request "${request.name}"</h2>
+<%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_add_samples = request.is_unsubmitted
+%>
+
+<br/><br/><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse all requests</a>
- </li>
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request Actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ %if can_add_samples:
+ <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
+ %endif
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div></ul>
%if message:
${render_msg( message, status )}
%endif
+<h2>History of Sequencing Request "${request.name}"</h2>
+
<div class="toolForm"><table class="grid"><thead>
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -70,14 +70,14 @@ class RequestsGrid( grids.Grid ):
columns = [
NameColumn( "Name",
key="name",
- link=( lambda item: iff( item.deleted, None, dict( operation="manage_request", id=item.id ) ) ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="view_request", id=item.id ) ) ),
attach_popup=True,
filterable="advanced" ),
DescriptionColumn( "Description",
key='desc',
filterable="advanced" ),
SamplesColumn( "Samples",
- link=( lambda item: iff( item.deleted, None, dict( operation="manage_request", id=item.id ) ) ) ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="edit_samples", id=item.id ) ) ) ),
TypeColumn( "Sequencer",
link=( lambda item: iff( item.deleted, None, dict( operation="view_type", id=item.type.id ) ) ) ),
grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
@@ -148,7 +148,7 @@ class RequestsCommon( BaseController, Us
except TypeError, e:
# We must have an email address rather than an encoded user id
# This is because the galaxy.base.js creates a search+select box
- # when there are more than 20 items in a selectfield
+ # when there are more than 20 items in a SelectField.
user = trans.sa_session.query( trans.model.User ) \
.filter( trans.model.User.table.c.email==util.restore_text( user_id ) ) \
.first()
@@ -178,7 +178,8 @@ class RequestsCommon( BaseController, Us
message=message ,
status='done' ) )
elif params.get( 'add_sample_button', False ):
- return self.__add_sample( trans, cntrller, request, **kwd )
+ request_id = trans.security.encode_id( request.id )
+ return self.add_sample( trans, cntrller, request_id, **kwd )
request_type_select_field = self.__build_request_type_id_select_field( trans, selected_value=request_type_id )
# Widgets to be rendered on the request form
widgets = []
@@ -194,7 +195,7 @@ class RequestsCommon( BaseController, Us
widgets += request_type.request_form.get_widgets( user, **kwd )
# In case there is an error on the form, make sure to populate widget fields with anything the user
# may have already entered.
- self.populate_widgets_from_kwd( trans, widgets, **kwd )
+ widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd )
if request_type is not None or status == 'error':
# Either the user selected a request_type or an error exists on the form.
if is_admin:
@@ -214,6 +215,29 @@ class RequestsCommon( BaseController, Us
message=message,
status=status )
@web.expose
+ @web.require_login( "view request" )
+ def view_request( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ request_id = params.get( 'id', None )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
+ sample_state_id = params.get( 'sample_state_id', None )
+ # Get the user entered sample information
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
+ request_widgets = self.__get_request_widgets( trans, request.id )
+ return trans.fill_template( '/requests/common/view_request.mako',
+ cntrller=cntrller,
+ request=request,
+ request_widgets=request_widgets,
+ current_samples=current_samples,
+ status=status,
+ message=message )
+ @web.expose
@web.require_login( "edit sequencing requests" )
def edit_basic_request_info( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
@@ -226,7 +250,7 @@ class RequestsCommon( BaseController, Us
return invalid_id_redirect( trans, cntrller, request_id )
name = util.restore_text( params.get( 'name', '' ) )
desc = util.restore_text( params.get( 'desc', '' ) )
- if params.get( 'edit_basic_request_info_button', False ) or params.get( 'edit_samples_button', False ):
+ if params.get( 'edit_basic_request_info_button', False ):
if not name:
status = 'error'
message = 'Enter the name of the request'
@@ -244,7 +268,7 @@ class RequestsCommon( BaseController, Us
widgets = widgets + request.type.request_form.get_widgets( request.user, request.values.content, **kwd )
# In case there is an error on the form, make sure to populate widget fields with anything the user
# may have already entered.
- self.populate_widgets_from_kwd( trans, widgets, **kwd )
+ widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd )
return trans.fill_template( 'requests/common/edit_basic_request_info.mako',
cntrller=cntrller,
request_type=request.type,
@@ -371,8 +395,8 @@ class RequestsCommon( BaseController, Us
status=status,
message=message ) )
@web.expose
- @web.require_login( "sequencing request page" )
- def manage_request( self, trans, cntrller, **kwd ):
+ @web.require_login( "manage samples" )
+ def edit_samples( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
message = util.restore_text( params.get( 'message', '' ) )
@@ -382,80 +406,64 @@ class RequestsCommon( BaseController, Us
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, cntrller, request_id )
- sample_state_id = params.get( 'sample_state_id', None )
+ # This method is called when the user is adding new samples as well as
+ # editing existing samples, so we use the editing_samples flag to keep
+ # track of what's occurring.
+ # TODO: CRITICAL: We need another round of code fixes to abstract out
+ # adding samples vs editing samples. We need to eliminate the need for
+ # this editing_samples flag since it is not maintainable. Greg will do
+ # this work as soon as possible.
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ if params.get( 'cancel_changes_button', False ):
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ id=request_id,
+ editing_samples=editing_samples ) )
+ # Get all libraries for which the current user has permission to add items.
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
# Get the user entered sample information
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- selected_value = params.get( 'sample_operation', 'none' )
- if selected_value != 'none' and not selected_samples:
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
+ encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
+ if not encoded_selected_sample_ids and sample_operation != 'none':
+ # Probably occurred due to refresh_on_change...is there a better approach?
+ kwd[ 'sample_operation' ] = 'none'
message = 'Select at least one sample before selecting an operation.'
- status = 'error'
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id,
- status=status,
- message=message ) )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
- sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True )
+ kwd[ 'message' ] = message
+ handle_error( **kwd )
if params.get( 'import_samples_button', False ):
# Import sample field values from a csv file
return self.__import_samples( trans, cntrller, request, current_samples, libraries, **kwd )
elif params.get( 'add_sample_button', False ):
- return self.__add_sample( trans, cntrller, request, **kwd )
+ return self.add_sample( trans, cntrller, request_id, **kwd )
elif params.get( 'save_samples_button', False ):
- return self.__save_sample( trans, cntrller, request, current_samples, **kwd )
- elif params.get( 'edit_samples_button', False ):
- managing_samples = True
- elif params.get( 'cancel_changes_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id ) )
- pass
- elif params.get( 'change_state_button', False ):
- sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
- new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
- self.update_sample_state(trans, cntrller, selected_samples, new_state, comment=sample_event_comment )
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller=cntrller,
- action='update_request_state',
- request_id=request_id ) )
- elif params.get( 'cancel_change_state_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id ) )
- elif params.get( 'change_lib_button', False ):
- library_id = params.get( 'sample_0_library_id', None )
- try:
- library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
- except:
- invalid_id_redirect( trans, cntrller, library_id )
- folder_id = params.get( 'sample_0_folder_id', None )
- try:
- folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
- except:
- invalid_id_redirect( trans, cntrller, folder_id )
- for sample_id in selected_samples:
- sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
- sample.library = library
- sample.folder = folder
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- trans.sa_session.refresh( request )
- message = 'Changes made to the selected samples have been saved. '
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id,
- status=status,
- message=message ) )
- elif params.get( 'cancel_change_lib_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=trans.security.encode_id( request.id ) ) )
+ if encoded_selected_sample_ids:
+ # This gets tricky because we need the list of samples to include the same number
+ # of objects that that current_samples ( i.e., request.samples ) has. We'll first
+ # get the set of samples corresponding to the checked sample ids.
+ samples = []
+ selected_samples = []
+ for encoded_sample_id in encoded_selected_sample_ids:
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( encoded_sample_id ) )
+ selected_samples.append( sample )
+ # Now build the list of samples, inserting None for samples that have not been checked.
+ for sample in request.samples:
+ if sample in selected_samples:
+ samples.append( sample )
+ else:
+ samples.append( None )
+ # The __save_samples method requires samples widgets, not sample objects
+ samples = self.__get_sample_widgets( trans, request, samples, **kwd )
+ else:
+ samples = current_samples
+ return self.__save_samples( trans, cntrller, request, samples, **kwd )
request_widgets = self.__get_request_widgets( trans, request.id )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
libraries_select_field, folders_select_field = self.__build_library_and_folder_select_fields( trans,
@@ -464,12 +472,13 @@ class RequestsCommon( BaseController, Us
libraries,
None,
**kwd )
- # Build the sample_state_id_select_field SelectField
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, sample_operation )
+ sample_state_id = params.get( 'sample_state_id', None )
sample_state_id_select_field = self.__build_sample_state_id_select_field( trans, request, sample_state_id )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
- selected_samples=selected_samples,
+ encoded_selected_sample_ids=encoded_selected_sample_ids,
request_widgets=request_widgets,
current_samples=current_samples,
sample_copy=sample_copy,
@@ -478,7 +487,7 @@ class RequestsCommon( BaseController, Us
libraries_select_field=libraries_select_field,
folders_select_field=folders_select_field,
sample_state_id_select_field=sample_state_id_select_field,
- managing_samples=managing_samples,
+ editing_samples=editing_samples,
status=status,
message=message )
@web.expose
@@ -489,7 +498,7 @@ class RequestsCommon( BaseController, Us
except:
if cntrller == 'api':
trans.response.status = 400
- return "Malformed sample id ( %s ) specified, unable to decode." % str( sample_id )
+ return "Invalid sample id ( %s ) specified, unable to decode." % str( sample_id )
else:
return invalid_id_redirect( trans, cntrller, sample_id )
event = trans.model.SampleEvent( sample, new_state, comment )
@@ -516,7 +525,7 @@ class RequestsCommon( BaseController, Us
if ok_for_now:
request.deleted = True
trans.sa_session.add( request )
- # delete all the samples belonging to this request
+ # Delete all the samples belonging to this request
for s in request.samples:
s.deleted = True
trans.sa_session.add( s )
@@ -546,7 +555,7 @@ class RequestsCommon( BaseController, Us
if ok_for_now:
request.deleted = False
trans.sa_session.add( request )
- # undelete all the samples belonging to this request
+ # Undelete all the samples belonging to this request
for s in request.samples:
s.deleted = False
trans.sa_session.add( s )
@@ -655,9 +664,10 @@ class RequestsCommon( BaseController, Us
if cntrller == 'api':
return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=request_id,
+ editing_samples=True,
status=status,
message=message ) )
final_state = False
@@ -673,8 +683,8 @@ class RequestsCommon( BaseController, Us
event = trans.model.RequestEvent( request, state, comments )
trans.sa_session.add( event )
trans.sa_session.flush()
- # check if an email notification is configured to be sent when the samples
- # are in this state
+ # See if an email notification is configured to be sent when the samples
+ # are in this state.
retval = request.send_email_notification( trans, common_state, final_state )
if retval:
message = comments + retval
@@ -683,113 +693,10 @@ class RequestsCommon( BaseController, Us
if cntrller == 'api':
return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
- id=trans.security.encode_id(request.id),
- status='done',
- message=message ) )
- def __save_sample( self, trans, cntrller, request, current_samples, **kwd ):
- # Save all the new/unsaved samples entered by the user
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
- is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
- selected_value = params.get( 'sample_operation', 'none' )
- # Check for duplicate sample names
- message = ''
- for index in range( len( current_samples ) - len( request.samples ) ):
- sample_index = index + len( request.samples )
- current_sample = current_samples[ sample_index ]
- sample_name = current_sample[ 'name' ]
- if not sample_name.strip():
- message = 'Enter the name of sample number %i' % sample_index
- break
- count = 0
- for i in range( len( current_samples ) ):
- if sample_name == current_samples[ i ][ 'name' ]:
- count += 1
- if count > 1:
- message = "This request has %i samples with the name (%s). Samples belonging to a request must have unique names." % ( count, sample_name )
- break
- if message:
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- request_widgets = self.__get_request_widgets( trans, request.id )
- sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
- status = 'error'
- return trans.fill_template( '/requests/common/manage_request.mako',
- cntrller=cntrller,
- request=request,
- selected_samples=selected_samples,
- request_widgets=request_widgets,
- current_samples=current_samples,
- sample_copy=sample_copy,
- managing_samples=managing_samples,
- sample_operation_select_field=sample_operation_select_field,
- status=status,
- message=message )
- if not managing_samples:
- for index in range( len( current_samples ) - len( request.samples ) ):
- sample_index = len( request.samples )
- current_sample = current_samples[ sample_index ]
- form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
- trans.sa_session.add( form_values )
- trans.sa_session.flush()
- s = trans.model.Sample( current_sample[ 'name' ],
- '',
- request,
- form_values,
- current_sample[ 'barcode' ],
- current_sample[ 'library' ],
- current_sample[ 'folder' ] )
- trans.sa_session.add( s )
- trans.sa_session.flush()
- else:
- message = 'Changes made to the samples are saved. '
- for sample_index in range( len( current_samples ) ):
- sample = request.samples[ sample_index ]
- current_sample = current_samples[ sample_index ]
- sample.name = current_sample[ 'name' ]
- sample.library = current_sample[ 'library' ]
- sample.folder = current_sample[ 'folder' ]
- if request.is_submitted:
- bc_message = self.__validate_barcode( trans, sample, current_sample[ 'barcode' ] )
- if bc_message:
- status = 'error'
- message += bc_message
- else:
- if not sample.bar_code:
- # If this is a 'new' (still in its first state) sample
- # change the state to the next
- if sample.state.id == request.type.states[0].id:
- event = trans.model.SampleEvent( sample,
- request.type.states[1],
- 'Sample added to the system' )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- # Now check if all the samples' barcode has been entered.
- # If yes then send notification email if configured
- common_state = request.samples_have_common_state
- if common_state:
- if common_state.id == request.type.states[1].id:
- event = trans.model.RequestEvent( request,
- request.states.SUBMITTED,
- "All samples are in %s state." % common_state.name )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- request.send_email_notification( trans, request.type.states[1] )
- sample.bar_code = current_samples[sample_index]['barcode']
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
- form_values.content = current_sample[ 'field_values' ]
- trans.sa_session.add( form_values )
- trans.sa_session.flush()
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=trans.security.encode_id( request.id ),
+ id=request_id,
+ editing_samples=True,
status=status,
message=message ) )
@web.expose
@@ -876,25 +783,33 @@ class RequestsCommon( BaseController, Us
cntrller=cntrller,
events_list=events_list,
sample=sample )
- def __add_sample( self, trans, cntrller, request, **kwd ):
+ @web.expose
+ @web.require_login( "add sample" )
+ def add_sample( self, trans, cntrller, request_id, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
# Get the widgets for rendering the request form
request_widgets = self.__get_request_widgets( trans, request.id )
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
if not current_samples:
# Form field names are zero-based.
sample_index = 0
else:
sample_index = len( current_samples )
if params.get( 'add_sample_button', False ):
+ # Get all libraries for which the current user has permission to add items
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
num_samples_to_add = int( params.get( 'num_sample_to_copy', 1 ) )
# See if the user has selected a sample to copy.
copy_sample_index = int( params.get( 'copy_sample_index', -1 ) )
for index in range( num_samples_to_add ):
+ id_index = len( current_samples ) + 1
if copy_sample_index != -1:
# The user has selected a sample to copy.
library_id = current_samples[ copy_sample_index][ 'library_select_field' ].get_selected( return_value=True )
@@ -902,7 +817,7 @@ class RequestsCommon( BaseController, Us
name = current_samples[ copy_sample_index ][ 'name' ] + '_%i' % ( len( current_samples ) + 1 )
field_values = [ val for val in current_samples[ copy_sample_index ][ 'field_values' ] ]
else:
- # The user has not selected a sample to copy (may just be adding a sample).
+ # The user has not selected a sample to copy, just adding a new generic sample.
library_id = None
folder_id = None
name = 'Sample_%i' % ( len( current_samples ) + 1 )
@@ -910,7 +825,7 @@ class RequestsCommon( BaseController, Us
# Build the library_select_field and folder_select_field for the new sample being added.
library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
user=request.user,
- sample_index=len( current_samples ),
+ sample_index=id_index,
libraries=libraries,
sample=None,
library_id=library_id,
@@ -926,101 +841,21 @@ class RequestsCommon( BaseController, Us
field_values=field_values,
library_select_field=library_select_field,
folder_select_field=folder_select_field ) )
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- selected_value = params.get( 'sample_operation', 'none' )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
+ encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, sample_operation )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
- selected_samples=selected_samples,
+ encoded_selected_sample_ids=encoded_selected_sample_ids,
request_widgets=request_widgets,
current_samples=current_samples,
sample_operation_select_field=sample_operation_select_field,
sample_copy=sample_copy,
- managing_samples=managing_samples,
+ editing_samples=False,
message=message,
status=status )
- def __get_sample_info( self, trans, request, **kwd ):
- """
- Retrieves all user entered sample information and returns a
- list of all the samples and their field values.
- """
- params = util.Params( kwd )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
- # Bet all data libraries accessible to this user
- libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
- # Build the list of widgets which will be used to render each sample row on the request page
- current_samples = []
- for index, sample in enumerate( request.samples ):
- library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
- request.user,
- index,
- libraries,
- sample,
- **kwd )
- current_samples.append( dict( name=sample.name,
- barcode=sample.bar_code,
- library=sample.library,
- folder=sample.folder,
- field_values=sample.values.content,
- library_select_field=library_select_field,
- folder_select_field=folder_select_field ) )
- if not managing_samples:
- sample_index = len( request.samples )
- else:
- sample_index = 0
- while True:
- library_id = params.get( 'sample_%i_library_id' % sample_index, None )
- folder_id = params.get( 'sample_%i_folder_id' % sample_index, None )
- if params.get( 'sample_%i_name' % sample_index, False ):
- # Data library
- try:
- library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
- #library_id = library.id
- except:
- library = None
- if library is not None:
- # Folder
- try:
- folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
- #folder_id = folder.id
- except:
- if library:
- folder = library.root_folder
- else:
- folder = None
- else:
- folder = None
- sample_info = dict( name=util.restore_text( params.get( 'sample_%i_name' % sample_index, '' ) ),
- barcode=util.restore_text( params.get( 'sample_%i_barcode' % sample_index, '' ) ),
- library=library,
- folder=folder)
- sample_info[ 'field_values' ] = []
- for field_index in range( len( request.type.sample_form.fields ) ):
- sample_info[ 'field_values' ].append( util.restore_text( params.get( 'sample_%i_field_%i' % ( sample_index, field_index ), '' ) ) )
- if not managing_samples:
- sample_info[ 'library_select_field' ], sample_info[ 'folder_select_field' ] = self.__build_library_and_folder_select_fields( trans,
- request.user,
- sample_index,
- libraries,
- None,
- library_id,
- folder_id,
- **kwd )
- current_samples.append( sample_info )
- else:
- sample_info[ 'library_select_field' ], sample_info[ 'folder_select_field' ] = self.__build_library_and_folder_select_fields( trans,
- request.user,
- sample_index,
- libraries,
- request.samples[ sample_index ],
- **kwd )
- current_samples[ sample_index ] = sample_info
- sample_index += 1
- else:
- break
- return current_samples, managing_samples, libraries
@web.expose
@web.require_login( "delete sample from sequencing request" )
def delete_sample( self, trans, cntrller, **kwd ):
@@ -1032,7 +867,7 @@ class RequestsCommon( BaseController, Us
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, cntrller, request_id )
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
sample_index = int( params.get( 'sample_id', 0 ) )
sample_name = current_samples[sample_index]['name']
sample = request.has_sample( sample_name )
@@ -1042,9 +877,10 @@ class RequestsCommon( BaseController, Us
trans.sa_session.flush()
message = 'Sample (%s) has been deleted.' % sample_name
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( request.id ),
+ editing_samples=True,
status=status,
message=message ) )
@web.expose
@@ -1059,12 +895,12 @@ class RequestsCommon( BaseController, Us
sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
except:
return invalid_id_redirect( trans, cntrller, sample_id )
- # check if a library and folder has been set for this sample yet.
+ # See if a library and folder have been set for this sample.
if not sample.library or not sample.folder:
status = 'error'
message = "Set a data library and folder for sequencing request (%s) to transfer datasets." % sample.name
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( sample.request.id ),
status=status,
@@ -1101,7 +937,6 @@ class RequestsCommon( BaseController, Us
SampleName,DataLibrary,DataLibraryFolder,Field1,Field2....
"""
params = util.Params( kwd )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
file_obj = params.get( 'file_data', '' )
try:
reader = csv.reader( file_obj.file )
@@ -1137,23 +972,211 @@ class RequestsCommon( BaseController, Us
folder_select_field=folder_select_field,
field_values=row[3:] ) )
except Exception, e:
- status = 'error'
- message = 'Error thrown when importing samples file: %s' % str( e )
+ if str( e ) == "'unicode' object has no attribute 'file'":
+ message = "Select a file"
+ else:
+ message = 'Error attempting to create samples from selected file: %s.' % str( e )
+ message += ' Make sure the selected csv file uses the format: SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...'
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='add_sample',
cntrller=cntrller,
- id=trans.security.encode_id( request.id ),
- status=status,
+ request_id=trans.security.encode_id( request.id ),
+ add_sample_button='Add sample',
+ status='error',
message=message ) )
request_widgets = self.__get_request_widgets( trans, request.id )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
request_widgets=request_widgets,
current_samples=current_samples,
sample_copy=sample_copy,
- managing_samples=managing_samples )
+ editing_samples=False )
+ def __save_samples( self, trans, cntrller, request, samples, **kwd ):
+ # Here we handle saving all new samples added by the user as well as saving
+ # changes to any subset of the request's samples.
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ sample_operation = params.get( 'sample_operation', 'none' )
+ # Check for duplicate sample names within the request
+ self.__validate_sample_names( trans, cntrller, request, samples, **kwd )
+ if editing_samples:
+ library = None
+ folder = None
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
+ # Here we handle saving changes to single samples as well as saving changes to
+ # selected sets of samples. If samples are selected, the sample_operation param
+ # will have a value other than 'none', and the samples param will be a list of
+ # encoded sample ids. There are currently only 2 multi-select operations;
+ # 'Change state' and 'Select data library and folder'. If sample_operation is
+ # 'none, then the samples param will be a list of sample objects.
+ if sample_operation == 'Change state':
+ sample_state_id = params.get( 'sample_state_id', None )
+ if sample_state_id in [ None, 'none' ]:
+ message = "Select a new state from the <b>Change current state</b> list before clicking the <b>Save</b> button."
+ kwd[ 'message' ] = message
+ del kwd[ 'save_changes_button' ]
+ handle_error( **kwd )
+ sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
+ new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
+ # Send the encoded sample_ids to update_sample_state.
+ # TODO: make changes necessary to just send the samples...
+ encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
+ # Make sure all samples have a unique barcode if the state is changing
+ for sample_index in range( len( samples ) ):
+ current_sample = samples[ sample_index ]
+ if current_sample is None:
+ # We have a None value because the user did not select this sample
+ # on which to perform the action.
+ continue
+ request_sample = request.samples[ sample_index ]
+ bc_message = self.__validate_barcode( trans, request_sample, current_sample[ 'barcode' ] )
+ if bc_message:
+ #status = 'error'
+ message += bc_message
+ kwd[ 'message' ] = message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ self.update_sample_state( trans, cntrller, encoded_selected_sample_ids, new_state, comment=sample_event_comment )
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ cntrller=cntrller,
+ action='update_request_state',
+ request_id=trans.security.encode_id( request.id ) ) )
+ elif sample_operation == 'Select data library and folder':
+ library_id = params.get( 'sample_0_library_id', 'none' )
+ folder_id = params.get( 'sample_0_folder_id', 'none' )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ self.__update_samples( trans, request, samples, **kwd )
+ # See if all the samples' barcodes are in the same state,
+ # and if so send email if configured to.
+ common_state = request.samples_have_common_state
+ if common_state and common_state.id == request.type.states[1].id:
+ event = trans.model.RequestEvent( request,
+ request.states.SUBMITTED,
+ "All samples are in %s state." % common_state.name )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ request.send_email_notification( trans, request.type.states[1] )
+ message = 'Changes made to the samples have been saved. '
+ else:
+ # Saving a newly created sample.
+ for index in range( len( samples ) - len( request.samples ) ):
+ sample_index = len( request.samples )
+ current_sample = samples[ sample_index ]
+ form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
+ trans.sa_session.add( form_values )
+ trans.sa_session.flush()
+ s = trans.model.Sample( current_sample[ 'name' ],
+ '',
+ request,
+ form_values,
+ current_sample[ 'barcode' ],
+ current_sample[ 'library' ],
+ current_sample[ 'folder' ] )
+ trans.sa_session.add( s )
+ trans.sa_session.flush()
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ id=trans.security.encode_id( request.id ),
+ editing_samples=editing_samples,
+ status=status,
+ message=message ) )
+ def __update_samples( self, trans, request, sample_widgets, **kwd ):
+ # Determine if the values in kwd require updating the request's samples. The list of
+ # sample_widgets must have the same number of objects as request.samples, but some of
+ # the objects can be None. Those that are not None correspond to samples selected by
+ # the user for performing an action on multiple samples simultaneously.
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
+ params = util.Params( kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ if sample_operation != 'none':
+ # These values will be in kwd if the user checked 1 or more checkboxes for performing this action
+ # on a set of samples.
+ library_id = params.get( 'sample_0_library_id', 'none' )
+ folder_id = params.get( 'sample_0_folder_id', 'none' )
+ for index, obj in enumerate( sample_widgets ):
+ if obj is not None:
+ # obj will be None if the user checked sample check boxes and selected an action
+ # to perform on multiple samples, but did not select certain samples.
+ sample_updated = False
+ # If this sample has values in kwd, then kwd will include a
+ # key whose value is this sample's ( possibly changed ) name. An
+ # example of this key is 'sample_0_name'.
+ for k, v in kwd.items():
+ name_key = 'sample_%i_name' % index
+ if k == name_key:
+ sample_updated = True
+ break
+ if sample_updated:
+ id_index = index + 1
+ if sample_operation == 'none':
+ # We are handling changes to a single sample.
+ library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
+ folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
+ # Update the corresponding sample's values as well as the sample_widget.
+ sample = request.samples[ index ]
+ sample.name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ # The bar_code field requires special handling because after a request is submitted, the
+ # state of a sample cannot be changed without a bar_code assocaited with the sample.
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ if not bar_code and not sample.bar_code:
+ # If this is a 'new' (still in its first state) sample, create an event
+ if sample.state.id == request.states[0].id:
+ event = trans.model.SampleEvent( sample,
+ request.type.states[1],
+ 'Sample added to the system' )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ elif bar_code:
+ bc_message = self.__validate_barcode( trans, sample, bar_code )
+ if bc_message:
+ kwd[ 'message' ] = bc_message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ sample.bar_code = bar_code
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ sample.library = library
+ sample.folder = folder
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
+ form_values.content = field_values
+ trans.sa_session.add_all( ( sample, form_values ) )
+ trans.sa_session.flush()
+ def __get_library_and_folder( self, trans, library_id, folder_id ):
+ try:
+ library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
+ except:
+ library = None
+ if library and folder_id == 'none':
+ folder = library.root_folder
+ elif library and folder_id != 'none':
+ try:
+ folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
+ except:
+ if library:
+ folder = library.root_folder
+ else:
+ folder = None
+ else:
+ folder = None
+ return library, folder
# ===== Methods for handling form definition widgets =====
def __get_request_widgets( self, trans, id ):
"""Get the widgets for the request"""
@@ -1179,27 +1202,104 @@ class RequestsCommon( BaseController, Us
value=request.values.content[ index ],
helptext=field[ 'helptext' ] + ' (' + required_label + ')' ) )
return request_widgets
- def __get_samples_widgets( self, trans, request, libraries, **kwd ):
- """Get the widgets for all of the samples currently associated with the request"""
- # The current_samples_widgets list is a list of dictionaries
- current_samples_widgets = []
- for index, sample in enumerate( request.samples ):
- # Build the library_select_field and folder_select_field for each existing sample
- library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
+ def __get_sample_widgets( self, trans, request, samples, **kwd ):
+ """
+ Returns a list of dictionaries, each representing the widgets that define a sample on a form.
+ The widgets are populated from kwd based on the set of samples received. The set of samples
+ corresponds to a reques.samples list, but if the user checked specific check boxes on the form,
+ those samples that were not check will have None objects in the list of samples. In this case,
+ the corresponding sample_widget is populated from the db rather than kwd.
+ """
+ params = util.Params( kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ # This method is called when the user is adding new samples as well as
+ # editing existing samples, so we use the editing_samples flag to keep
+ # track of what's occurring.
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ sample_widgets = []
+ if sample_operation != 'none':
+ # The sample_operatin param has a value other than 'none', and a specified
+ # set of samples was received.
+ library_id = util.restore_text( params.get( 'sample_0_library_id', 'none' ) )
+ folder_id = util.restore_text( params.get( 'sample_0_folder_id', 'none' ) )
+ # Build the list of widgets which will be used to render each sample row on the request page
+ if not request:
+ return sample_widgets
+ # Get the list of libraries for which the current user has permission to add items.
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
+ # Build the list if sample widgets, populating the values from kwd.
+ for index, sample in enumerate( samples ):
+ id_index = index + 1
+ if sample is None:
+ # Id sample is None, then we'll use the sample from the request object since it will
+ # not have updated =values from kwd.
+ sample = request.samples[ index ]
+ name = sample.name
+ bar_code = sample.bar_code
+ library = sample.library
+ folder = sample.folder
+ field_values = sample.values.content,
+ else:
+ # Update the sample attributes from kwd
+ name = util.restore_text( params.get( 'sample_%i_name' % index, sample.name ) )
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, sample.bar_code ) )
+ library_id = util.restore_text( params.get( 'sample_%i_library_id' % id_index, '' ) )
+ if not library_id and sample.library:
+ library_id = trans.security.encode_id( sample.library.id )
+ folder_id = util.restore_text( params.get( 'sample_%i_folder_id' % id_index, '' ) )
+ if not folder_id and sample.folder:
+ folder_id = trans.security.encode_id( sample.folder.id )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans=trans,
user=request.user,
- sample_index=index,
+ sample_index=id_index,
libraries=libraries,
sample=sample,
+ library_id=library_id,
+ folder_id=folder_id,
**kwd )
- # Append the dictionary for the current sample to the current_samples_widgets list
- current_samples_widgets.append( dict( name=sample.name,
- barcode=sample.bar_code,
- library=sample.library,
- folder=sample.folder,
- field_values=sample.values.content,
- library_select_field=library_select_field,
- folder_select_field=folder_select_field ) )
- return current_samples_widgets
+ sample_widgets.append( dict( name=name,
+ barcode=bar_code,
+ library=library,
+ folder=folder,
+ field_values=field_values,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ # There may be additional new samples on the form that have not yet been associated with the request.
+ # TODO: factor this code so it is not duplicating what's above.
+ index = len( samples )
+ while True:
+ name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ if not name:
+ break
+ id_index = index + 1
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ library_id = util.restore_text( params.get( 'sample_%i_library_id' % id_index, '' ) )
+ folder_id = util.restore_text( params.get( 'sample_%i_folder_id' % id_index, '' ) )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans=trans,
+ user=request.user,
+ sample_index=id_index,
+ libraries=libraries,
+ sample=None,
+ library_id=library_id,
+ folder_id=folder_id,
+ **kwd )
+ sample_widgets.append( dict( name=name,
+ barcode=bar_code,
+ library=library,
+ folder=folder,
+ field_values=field_values,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ index += 1
+ return sample_widgets
# ===== Methods for building SelectFields used on various request forms =====
def __build_copy_sample_select_field( self, trans, current_samples ):
copy_sample_index_select_field = SelectField( 'copy_sample_index' )
@@ -1240,26 +1340,30 @@ class RequestsCommon( BaseController, Us
# The libraries dictionary looks like: { library : '1,2' }, library : '3' }. Its keys are the libraries that
# should be displayed for the current user and its values are strings of comma-separated folder ids that should
# NOT be displayed.
- #
- # TODO: all object ids received in the params must be encoded.
params = util.Params( kwd )
library_select_field_name= "sample_%i_library_id" % sample_index
folder_select_field_name = "sample_%i_folder_id" % sample_index
if not library_id:
- library_id = params.get( library_select_field_name, 'none' )
+ library_id = params.get( library_select_field_name, None )
+ if not folder_id:
+ folder_id = params.get( folder_select_field_name, None )
selected_library = None
selected_hidden_folder_ids = []
showable_folders = []
- if sample and sample.library and library_id == 'none':
- library_id = str( sample.library.id )
+ if library_id not in [ None, 'none' ]:
+ # If we have a selected library, get the list of it's folders that are not accessible to the current user
+ for library, hidden_folder_ids in libraries.items():
+ encoded_id = trans.security.encode_id( library.id )
+ if encoded_id == str( library_id ):
+ selected_library = library
+ selected_hidden_folder_ids = hidden_folder_ids.split( ',' )
+ break
+ elif sample and sample.library and library_id == 'none':
+ # The user previously selected a library but is now resetting the selection to 'none'
+ selected_library = None
+ elif sample and sample.library:
+ library_id = trans.security.encode_id( sample.library.id )
selected_library = sample.library
- # If we have a selected library, get the list of it's folders that are not accessible to the current user
- for library, hidden_folder_ids in libraries.items():
- encoded_id = trans.security.encode_id( library.id )
- if encoded_id == str( library_id ):
- selected_library = library
- selected_hidden_folder_ids = hidden_folder_ids.split( ',' )
- break
# sample_%i_library_id SelectField with refresh on change enabled
library_select_field = build_select_field( trans,
libraries.keys(),
@@ -1275,20 +1379,12 @@ class RequestsCommon( BaseController, Us
selected_library,
[ trans.app.security_agent.permitted_actions.LIBRARY_ADD ],
selected_hidden_folder_ids )
- if sample:
- # The user is editing the request, and may have previously selected a folder
- if sample.folder:
- selected_folder_id = sample.folder.id
+ if folder_id:
+ selected_folder_id = folder_id
+ elif sample and sample.folder:
+ selected_folder_id = trans.security.encode_id( sample.folder.id )
else:
- # If a library is selected but not a folder, use the library's root folder
- if sample.library:
- selected_folder_id = sample.library.root_folder.id
- else:
- # The user just selected a folder
- selected_folder_id = params.get( folder_select_field_name, 'none' )
- elif folder_id:
- # TODO: not sure when this would be passed
- selected_folder_id = folder_id
+ selected_folder_id = trans.security.encode_id( selected_library.root_folder.id )
else:
selected_folder_id = 'none'
# TODO: Change the name of the library root folder to "Library root" to clarify to the
@@ -1328,9 +1424,35 @@ class RequestsCommon( BaseController, Us
message += '<b>' + ef + '</b> '
return message
return None
+ def __validate_sample_names( self, trans, cntrller, request, current_samples, **kwd ):
+ # Check for duplicate sample names for all samples of the request.
+ editing_samples = util.string_as_bool( kwd.get( 'editing_samples', False ) )
+ message = ''
+ for index in range( len( current_samples ) - len( request.samples ) ):
+ sample_index = index + len( request.samples )
+ current_sample = current_samples[ sample_index ]
+ sample_name = current_sample[ 'name' ]
+ if not sample_name.strip():
+ message = 'Enter the name of sample number %i' % sample_index
+ break
+ count = 0
+ for i in range( len( current_samples ) ):
+ if sample_name == current_samples[ i ][ 'name' ]:
+ count += 1
+ if count > 1:
+ message = "You tried to add %i samples with the name (%s). Samples belonging to a request must have unique names." % ( count, sample_name )
+ break
+ if message:
+ del kwd[ 'save_samples_button' ]
+ kwd[ 'message' ] = message
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
def __validate_barcode( self, trans, sample, barcode ):
"""
- Makes sure that the barcode about to be assigned to a sample is gobally unique.
+ Makes sure that the barcode about to be assigned to a sample is globally unique.
That is, barcodes must be unique across requests in Galaxy sample tracking.
"""
message = ''
@@ -1338,13 +1460,18 @@ class RequestsCommon( BaseController, Us
for index in range( len( sample.request.samples ) ):
# Check for empty bar code
if not barcode.strip():
- message = 'Fill in the barcode for sample (%s).' % sample.name
- break
+ if sample.state.id == sample.request.type.states[0].id:
+ # The user has not yet filled in the barcode value, but the sample is
+ # 'new', so all is well.
+ break
+ else:
+ message = "Fill in the barcode for sample (%s) before changing it's state." % sample.name
+ break
# TODO: Add a unique constraint to sample.bar_code table column
# Make sure bar code is unique
- for sample_has_bar_code in trans.sa_session.query( trans.model.Sample ) \
- .filter( trans.model.Sample.table.c.bar_code == barcode ):
- if sample_has_bar_code and sample_has_bar_code.id != sample.id:
+ for sample_with_barcode in trans.sa_session.query( trans.model.Sample ) \
+ .filter( trans.model.Sample.table.c.bar_code == barcode ):
+ if sample_with_barcode and sample_with_barcode.id != sample.id:
message = '''The bar code (%s) associated with the sample (%s) belongs to another sample.
Bar codes must be unique across all samples, so use a different bar code
for this sample.''' % ( barcode, sample.name )
@@ -1360,15 +1487,15 @@ class RequestsCommon( BaseController, Us
elif len( email ) > 255:
error = "(%s) exceeds maximum allowable length. " % str( email )
return error
- # ===== Other miscellaneoud utility methods =====
- def __get_selected_samples( self, trans, request, **kwd ):
- selected_samples = []
+ # ===== Other miscellaneous utility methods =====
+ def __get_encoded_selected_sample_ids( self, trans, request, **kwd ):
+ encoded_selected_sample_ids = []
for sample in request.samples:
if CheckboxField.is_checked( kwd.get( 'select_sample_%i' % sample.id, '' ) ):
- selected_samples.append( trans.security.encode_id( sample.id ) )
- return selected_samples
+ encoded_selected_sample_ids.append( trans.security.encode_id( sample.id ) )
+ return encoded_selected_sample_ids
-# ===== Miscellaneoud utility methods outside of the RequestsCommon class =====
+# ===== Miscellaneous utility methods outside of the RequestsCommon class =====
def invalid_id_redirect( trans, cntrller, obj_id, action='browse_requests' ):
status = 'error'
message = "Invalid request id (%s)" % str( obj_id )
--- a/templates/requests/common/dataset_transfer.mako
+++ b/templates/requests/common/dataset_transfer.mako
@@ -1,47 +1,46 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
+<br/><br/>
+
+<ul class="manage-table-actions">
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Refresh</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( sample.library.id ) )}">Target Data Library</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li>
+</ul>
+
%if message:
${render_msg( message, status )}
%endif
-<h2>Datasets of Sample "${sample.name}"</h2>
-
-<ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Refresh</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( sample.library.id ) )}">${sample.library.name} Data Library</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a>
- </li>
-</ul>
-
-%if dataset_files:
- <div class="form-row">
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- <th>Size</th>
- <th>Status</th>
- </tr>
- <thead>
- <tbody>
- %for dataset_file in dataset_files:
- <tr>
- <td>${dataset_file.name}</td>
- <td>${dataset_file.size}</td>
- <td>${dataset_file.status}</td>
- </tr>
- %endfor
- </tbody>
- </table>
+<div class="toolForm">
+ <div class="toolFormTitle">Sample "${sample.name}"</div>
+ <div class="toolFormBody">
+ %if dataset_files:
+ <div class="form-row">
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Size</th>
+ <th>Status</th>
+ </tr>
+ <thead>
+ <tbody>
+ %for dataset_file in dataset_files:
+ <tr>
+ <td>${dataset_file.name}</td>
+ <td>${dataset_file.size}</td>
+ <td>${dataset_file.status}</td>
+ </tr>
+ %endfor
+ </tbody>
+ </table>
+ </div>
+ %else:
+ <div class="form-row">
+ There are no datasets associated with this sample.
+ </div>
+ %endif
</div>
-%else:
- <div class="form-row">
- There are no datasets associated with this sample.
- </div>
-%endif
+</div>
--- a/templates/requests/common/manage_request.mako
+++ /dev/null
@@ -1,615 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-<%namespace file="/requests/common/sample_state.mako" import="render_sample_state" />
-<%namespace file="/requests/common/sample_datasets.mako" import="render_sample_datasets" />
-
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "library" )}
-</%def>
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- <script type="text/javascript">
- function showContent(vThis)
- {
- // http://www.javascriptjunkie.com
- // alert(vSibling.className + " " + vDef_Key);
- vParent = vThis.parentNode;
- vSibling = vParent.nextSibling;
- while (vSibling.nodeType==3) {
- // Fix for Mozilla/FireFox Empty Space becomes a TextNode or Something
- vSibling = vSibling.nextSibling;
- };
- if(vSibling.style.display == "none")
- {
- vThis.src="/static/images/fugue/toggle.png";
- vThis.alt = "Hide";
- vSibling.style.display = "block";
- } else {
- vSibling.style.display = "none";
- vThis.src="/static/images/fugue/toggle-expand.png";
- vThis.alt = "Show";
- }
- return;
- }
-
- $(document).ready(function(){
- //hide the all of the element with class msg_body
- $(".msg_body").hide();
- //toggle the component with class msg_body
- $(".msg_head").click(function(){
- $(this).next(".msg_body").slideToggle(0);
- });
- });
-
- // Looks for changes in sample states using an async request. Keeps
- // calling itself (via setTimeout) until all samples are in a terminal
- // state.
- var updater = function ( sample_states ) {
- // Check if there are any items left to track
- var empty = true;
- for ( i in sample_states ) {
- empty = false;
- break;
- }
- if ( ! empty ) {
- setTimeout( function() { updater_callback( sample_states ) }, 1000 );
- }
- };
-
- var updater_callback = function ( sample_states ) {
- // Build request data
- var ids = []
- var states = []
- $.each( sample_states, function ( id, state ) {
- ids.push( id );
- states.push( state );
- });
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
- dataType: "json",
- data: { ids: ids.join( "," ), states: states.join( "," ) },
- success : function ( data ) {
- $.each( data, function( id, val ) {
- // Replace HTML
- var cell1 = $("#sampleState-" + id);
- cell1.html( val.html_state );
- var cell2 = $("#sampleDatasets-" + id);
- cell2.html( val.html_datasets );
- sample_states[ parseInt( id ) ] = val.state;
- });
- updater( sample_states );
- },
- error: function() {
- // Just retry, like the old method, should try to be smarter
- updater( sample_states );
- }
- });
- };
-
- function checkAllFields()
- {
- var chkAll = document.getElementById('checkAll');
- var checks = document.getElementsByTagName('input');
- var boxLength = checks.length;
- var allChecked = false;
- var totalChecked = 0;
- if ( chkAll.checked == true )
- {
- for ( i=0; i < boxLength; i++ )
- {
- if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
- {
- checks[i].checked = true;
- }
- }
- }
- else
- {
- for ( i=0; i < boxLength; i++ )
- {
- if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
- {
- checks[i].checked = false
- }
- }
- }
- }
-
- function stopRKey(evt) {
- var evt = (evt) ? evt : ((event) ? event : null);
- var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
- if ((evt.keyCode == 13) && (node.type=="text")) {return false;}
- }
- document.onkeypress = stopRKey
- </script>
-</%def>
-
-<% is_admin = cntrller == 'requests_admin' and trans.user_is_admin() %>
-
-<div class="grid-header">
- <h2>Sequencing Request "${request.name}"</h2>
-
- <ul class="manage-table-actions">
- <li><a class="action-button" id="seqreq-${request.id}-popup" class="menubutton">Sequencing Request Actions</a></li>
- <div popupmenu="seqreq-${request.id}-popup">
- %if request.is_unsubmitted and request.samples:
- <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
- %endif
- <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">History</a>
- %if is_admin:
- %if request.is_submitted:
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
- %endif
- %endif
- </div>
- <li><a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse requests</a></li>
- </ul>
-
- <div class="toolParamHelp" style="clear: both;">
- <b>Sequencer</b>: ${request.type.name}
- %if is_admin:
- | <b>User</b>: ${request.user.email}
- %endif
- %if request.is_submitted:
- | <b>State</b>: <i>${request.state}</i>
- %else:
- | <b>State</b>: ${request.state}
- %endif
- </div>
-</div>
-
-%if request.samples_without_library_destinations:
- ${render_msg( "Select a target data library and folder for all the samples before starting the sequence run", "warning" )}
-%endif
-
-%if request.is_rejected:
- ${render_msg( "Reason for rejection: " + request.last_comment, "warning" )}
-%endif
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Request Information</h4>
-<div style="display:none;">
- <div class="form-row">
- <ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit request informaton</a>
- </li>
- </ul>
- </div>
- <table class="grid" border="0">
- <tbody>
- <tr>
- <td valign="top" width="50%">
- <div class="form-row">
- <label>Description:</label>
- ${request.desc}
- </div>
- <div style="clear: both"></div>
- %for index, rd in enumerate( request_widgets ):
- <%
- field_label = rd[ 'label' ]
- field_value = rd[ 'value' ]
- %>
- <div class="form-row">
- <label>${field_label}:</label>
- %if field_label == 'State':
- <a href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">${field_value}</a>
- %else:
- ${field_value}
- %endif
- </div>
- <div style="clear: both"></div>
- %endfor
- </td>
- <td valign="top" width="50%">
- <div class="form-row">
- <label>Date created:</label>
- ${request.create_time}
- </div>
- <div class="form-row">
- <label>Date updated:</label>
- ${request.update_time}
- </div>
- <div class="form-row">
- <label>Email notification recipients:</label>
- <%
- if request.notification:
- emails = ', '.join( request.notification[ 'email' ] )
- else:
- emails = ''
- %>
- ${emails}
- </div>
- <div style="clear: both"></div>
- <div class="form-row">
- <label>Email notification on sample states:</label>
- <%
- if request.notification:
- states = []
- for ss in request.type.states:
- if ss.id in request.notification[ 'sample_states' ]:
- states.append( ss.name )
- states = ', '.join( states )
- else:
- states = ''
- %>
- ${states}
- </div>
- <div style="clear: both"></div>
- </td>
- </tr>
- </tbody>
- </table>
-</div>
-<br/>
-<form id="manage_request" name="manage_request" action="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ), managing_samples=managing_samples )}" method="post">
- %if current_samples:
- <% sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True ) %>
- ## first render the basic info grid
- ${render_basic_info_grid()}
- %if not request.is_new and not managing_samples and len( sample_operation_select_field.options ) > 1:
- <div class="form-row" style="background-color:#FAFAFA;">
- For selected samples:
- ${sample_operation_select_field.get_html()}
- </div>
- %if sample_operation_selected_value != 'none' and selected_samples:
- <div class="form-row" style="background-color:#FAFAFA;">
- %if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE:
- ## sample_operation_selected_value == 'Change state'
- <div class="form-row">
- <label>Change current state</label>
- ${sample_state_id_select_field.get_html()}
- <label>Comments</label>
- <input type="text" name="sample_event_comment" value=""/>
- <div class="toolParamHelp" style="clear: both;">
- Optional
- </div>
- </div>
- <div class="form-row">
- <input type="submit" name="change_state_button" value="Save"/>
- <input type="submit" name="cancel_change_state_button" value="Cancel"/>
- </div>
- %elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
- <% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %>
- <div class="form-row">
- <label>Select data library:</label>
- ${libraries_select_field.get_html()}
- </div>
- %if libraries_selected_value != 'none':
- <div class="form-row">
- <label>Select folder:</label>
- ${folders_select_field.get_html()}
- </div>
- <div class="form-row">
- <input type="submit" name="change_lib_button" value="Save"/>
- <input type="submit" name="cancel_change_lib_button" value="Cancel"/>
- </div>
- %endif
- %endif
- </div>
- %endif
- %endif
- ## Render the other grids
- <% trans.sa_session.refresh( request.type.sample_form ) %>
- %for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
- ${render_grid( grid_index, grid_name, request.type.sample_form.fields_of_grid( grid_index ) )}
- %endfor
- %else:
- <label>There are no samples.</label>
- %endif
- %if request.samples and request.is_submitted:
- <script type="text/javascript">
- // Updater
- updater({${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
- </script>
- %endif
- %if not managing_samples:
- <table class="grid">
- <tbody>
- <tr>
- <div class="form-row">
- %if request.is_unsubmitted:
- <td>
- %if current_samples:
- <label>Copy </label>
- <input type="integer" name="num_sample_to_copy" value="1" size="3"/>
- <label>samples from sample</label>
- ${sample_copy.get_html()}
- %endif
- <input type="submit" name="add_sample_button" value="Add New"/>
- </td>
- %endif
- <td>
- %if current_samples and len( current_samples ) <= len( request.samples ):
- <input type="submit" name="edit_samples_button" value="Edit samples"/>
- %endif
- </td>
- </div>
- </tr>
- </tbody>
- </table>
- %endif
- %if request.samples or current_samples:
- %if managing_samples:
- <div class="form-row">
- <input type="submit" name="save_samples_button" value="Save"/>
- <input type="submit" name="cancel_changes_button" value="Cancel"/>
- </div>
- %elif len( current_samples ) > len( request.samples ):
- <div class="form-row">
- <input type="submit" name="save_samples_button" value="Save"/>
- <input type="submit" name="cancel_changes_button" value="Cancel"/>
- </div>
- %endif
- %endif
-</form>
-<br/>
-%if request.is_unsubmitted:
- <form id="import" name="import" action="${h.url_for( controller='requests_common', action='manage_request', managing_samples=managing_samples, id=trans.security.encode_id( request.id ) )}" enctype="multipart/form-data" method="post" >
- <h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Import samples</h4>
- <div style="display:none;">
- <input type="file" name="file_data" />
- <input type="submit" name="import_samples_button" value="Import samples"/>
- <br/>
- <div class="toolParamHelp" style="clear: both;">
- The csv file must be in the following format:<br/>
- SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...
- </div>
- </div>
- </form>
-%endif
-
-<%def name="render_grid( grid_index, grid_name, fields_dict )">
- <br/>
- <% if not grid_name:
- grid_name = "Grid "+ grid_index
- %>
- <div>
- %if managing_samples or len( current_samples ) > len( request.samples ):
- <h4><img src="/static/images/fugue/toggle.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
- <div>
- %else:
- <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
- <div style="display:none;">
- %endif
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- %for index, field in fields_dict.items():
- <th>
- ${field['label']}
- <div class="toolParamHelp" style="clear: both;">
- <i>${field['helptext']}</i>
- </div>
- </th>
- %endfor
- <th></th>
- </tr>
- <thead>
- <tbody>
- <% trans.sa_session.refresh( request ) %>
- %for sample_index, sample in enumerate( current_samples ):
- %if managing_samples:
- <tr>${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}</tr>
- %else:
- <tr>
- %if sample_index in range( len( request.samples ) ):
- ${render_sample( sample_index, sample['name'], sample['field_values'], fields_dict )}
- %else:
- ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}
- %endif
- </tr>
- %endif
- %endfor
- </tbody>
- </table>
- </div>
- </div>
-</%def>
-
-## This function displays the "Basic Information" grid
-<%def name="render_basic_info_grid()">
- <h3>Sample Information</h3>
- <table class="grid">
- <thead>
- <tr>
- <th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'><input type="hidden" name=select_all_samples_checkbox value="true"></th>
- <th>Name</th>
- <th>Barcode</th>
- <th>State</th>
- <th>Data Library</th>
- <th>Folder</th>
- %if request.is_submitted or request.is_complete:
- <th>Datasets Transferred</th>
- %endif
- <th></th>
- </tr>
- <thead>
- <tbody>
- <% trans.sa_session.refresh( request ) %>
- %for sample_index, info in enumerate( current_samples ):
- <%
- if sample_index in range( len(request.samples ) ):
- sample = request.samples[sample_index]
- else:
- sample = None
- %>
- %if managing_samples:
- <tr>${show_basic_info_form( sample_index, sample, info )}</tr>
- %else:
- <tr>
- %if sample_index in range( len( request.samples ) ):
- %if trans.security.encode_id( sample.id ) in selected_samples:
- <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" checked><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
- %else:
- <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true"><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
- %endif
- <td>${info['name']}</td>
- <td>${info['barcode']}</td>
- %if sample.request.is_unsubmitted:
- <td>Unsubmitted</td>
- %else:
- <td><a id="sampleState-${sample.id}" href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${render_sample_state( sample )}</a></td>
- %endif
- %if info['library']:
- %if cntrller == 'requests':
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
- %elif is_admin:
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library_admin', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
- %endif
- %else:
- <td></td>
- %endif
- %if info['folder']:
- <td>${info['folder'].name}</td>
- %else:
- <td></td>
- %endif
- %if request.is_submitted or request.is_complete:
- <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">
- ${render_sample_datasets( sample )}
- </a></td>
- %endif
- %else:
- ${show_basic_info_form( sample_index, sample, info )}
- %endif
- %if request.is_unsubmitted or request.is_rejected:
- <td>
- %if sample:
- %if sample.request.is_unsubmitted:
- <a class="action-button" href="${h.url_for( controller='requests_common', cntrller=cntrller, action='delete_sample', request_id=trans.security.encode_id( request.id ), sample_id=sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a>
- %endif
- %endif
- </td>
- %endif
- </tr>
- %endif
- %endfor
- </tbody>
- </table>
-</%def>
-
-<%def name="show_basic_info_form( sample_index, sample, info )">
- <td></td>
- <td>
- <input type="text" name="sample_${sample_index}_name" value="${info['name']}" size="10"/>
- <div class="toolParamHelp" style="clear: both;">
- <i>${' (required)' }</i>
- </div>
- </td>
- %if cntrller == 'requests':
- %if sample:
- %if sample.request.is_unsubmitted:
- <td></td>
- %else:
- <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
- %endif
- %else:
- <td></td>
- %endif
- %elif is_admin:
- %if sample:
- %if sample.request.is_unsubmitted:
- <td></td>
- %else:
- <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
- %endif
- %else:
- <td></td>
- %endif
- %endif
- %if sample:
- %if sample.request.is_unsubmitted:
- <td>Unsubmitted</td>
- %else:
- <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
- %endif
- %else:
- <td></td>
- %endif
- <td>${info['library_select_field'].get_html()}</td>
- <td>${info['folder_select_field'].get_html()}</td>
- %if request.is_submitted or request.is_complete:
- <%
- if sample:
- label = str( len( sample.datasets ) )
- else:
- label = 'Add'
- %>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
- %endif
-</%def>
-
-<%def name="render_sample( index, sample_name, sample_values, fields_dict )">
- <td>
- ${sample_name}
- </td>
- %for field_index, field in fields_dict.items():
- <td>
- %if sample_values[field_index]:
- %if field['type'] == 'WorkflowField':
- %if str(sample_values[field_index]) != 'none':
- <% workflow = trans.sa_session.query( trans.app.model.StoredWorkflow ).get( int(sample_values[field_index]) ) %>
- <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
- %endif
- %else:
- ${sample_values[field_index]}
- %endif
- %else:
- <i>None</i>
- %endif
- </td>
- %endfor
-</%def>
-
-<%def name="render_sample_form( index, sample_name, sample_values, fields_dict )">
- <td>${sample_name}</td>
- %for field_index, field in fields_dict.items():
- <td>
- %if field['type'] == 'TextField':
- <input type="text" name="sample_${index}_field_${field_index}" value="${sample_values[field_index]}" size="7"/>
- %elif field['type'] == 'SelectField':
- <select name="sample_${index}_field_${field_index}" last_selected_value="2">
- %for option_index, option in enumerate(field['selectlist']):
- %if option == sample_values[field_index]:
- <option value="${option}" selected>${option}</option>
- %else:
- <option value="${option}">${option}</option>
- %endif
- %endfor
- </select>
- %elif field['type'] == 'WorkflowField':
- <select name="sample_${index}_field_${field_index}">
- %if str(sample_values[field_index]) == 'none':
- <option value="none" selected>Select one</option>
- %else:
- <option value="none">Select one</option>
- %endif
- %for option_index, option in enumerate(request.user.stored_workflows):
- %if not option.deleted:
- %if str(option.id) == str(sample_values[field_index]):
- <option value="${option.id}" selected>${option.name}</option>
- %else:
- <option value="${option.id}">${option.name}</option>
- %endif
- %endif
- %endfor
- </select>
- %elif field['type'] == 'CheckboxField':
- <input type="checkbox" name="sample_${index}_field_${field_index}" value="Yes"/>
- %endif
- <div class="toolParamHelp" style="clear: both;">
- <i>${'('+field['required']+')' }</i>
- </div>
- </td>
- %endfor
-</%def>
1
0
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288645775 14400
# Node ID 695c89309bcb2f5d31bfb8b9d8b0124ff3abf90f
# Parent cf7ec71c561323940ce95c8148d2008508920092
Cleanup of debug info.
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -476,7 +476,6 @@ function Workflow( canvas_container ) {
});
},
rectify_workflow_outputs : function() {
- console.log("RECTIFICATION!");
// Find out if we're using workflow_outputs or not.
var using_workflow_outputs = false;
$.each( this.nodes, function ( k, node ) {
@@ -493,7 +492,6 @@ function Workflow( canvas_container ) {
if (node.type == 'tool'){
var node_changed = false;
if (node.post_job_actions == null){
- console.log("CREATED FOR NEW NODE");
node.post_job_actions = {};
}
var pjas_to_rem = [];
1
0
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288646507 14400
# Node ID 9285b39ae2f3dfd5017808b4d9b657094fec802b
# Parent 695c89309bcb2f5d31bfb8b9d8b0124ff3abf90f
Packing editor.
Tweak to reload.
--- a/static/scripts/packed/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js
@@ -1,1 +1,1 @@
-function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}OutputTerminal.prototype=new Terminal();function InputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.act
ion_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){if(f[b]=="input"||issubtype(f[b],this.datatypes[c])){return true}}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;this.handle1.connect(this);this.handle2=a;this.handle2.connect(this)},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};var h=n(this.handle1.element)+5;var g=i(t
his.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(d,a,b){var c=this;$(d).each(function(){var f=this.terminal=new InputTerminal(this,b);f.node=c;f.name=a;$(this).bind
("dropstart",function(g){g.dragProxy.terminal.connectors[0].inner_color="#BBFFBB"}).bind("dropend",function(g){g.dragProxy.terminal.connectors[0].inner_color="#FFFFFF"}).bind("drop",function(g){(new Connector(g.dragTarget.terminal,g.dropTarget.terminal)).redraw()}).bind("hover",function(){if(f.connectors.length>0){var g=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='buttons'></div>").append($("<img/>").attr("src",image_path+"/delete_icon.png").click(function(){$.each(f.connectors,function(i,h){h.destroy()});g.remove()}))).bind("mouseleave",function(){$(this).remove()});g.css({top:$(this).offset().top-2,left:$(this).offset().left-g.width(),"padding-right":$(this).width()}).show()}});c.input_terminals[a]=f})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j){workflow.check_changes_in_active_form();v
ar i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var k=new Connector();k.dragging=true;k.connect(this.terminal,i.terminal);$.dropManage({filter:function(h){return this.terminal.can_accept(f)}}).addClass("input-terminal-active");return i}).bind("drag",function(i){var h=function(){var k=$(i.dragProxy).offsetParent().offset(),j=i.offsetX-k.left,l=i.offsetY-k.top;$(i.dragProxy).css({left:j,top:l});i.dragProxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h){h.dragProxy.terminal.connectors[0].destroy();$(h.dragProxy).remove();$.dropManage().removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:fun
ction(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});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(h){var g=this.element;if(h.type){this.type=h.type}this.name=h.name;this.form_html=h.form_html;this.tool_state=h.tool_state;this.tool_errors=h.tool_errors;this.tooltip=h.tooltip?h.tooltip:"";this.annotation=h.annotation;this.post_job_actions=h.post_job_actions;if(h.workflow_outputs){this.workflow_outputs=h.workflow_outputs}else{this.workflow_outputs=[]}if(this.tool_errors){g.addClass("tool-node-error")}else{g.removeClass("tool-node-error")}var d=this;var c=Math.max(150,g.width());var a=g.find(".toolFormBody");a.find("div").remove();var i=$("<div class='inputs'></div
>").appendTo(a);$.each(h.data_inputs,function(k,f){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,f.name,f.extensions);var b=$("<div class='form-row dataRow input-data-row' name='"+f.name+"'>"+f.label+"</div>");b.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(b);c=Math.max(c,b.outerWidth());b.css({position:"",left:"",top:"",display:""});$("body").remove(b);i.append(b.prepend(j))});if((h.data_inputs.length>0)&&(h.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(h.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");d.enable_output_terminal(j,b.name,b.extensions);var f=b.name;if(b.extensions.indexOf("input")<0){f=f+" ("+b.extensions.join(", ")+")"}var m=$("<div class='form-row dataRow'>"+f+"</div>");if(d.type=="tool"){var l=$("<div class='callout'></div>").css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",image_path+"/
fugue/asterisk-small-outline.png").click(function(){if($.inArray(b.name,d.workflow_outputs)!=-1){d.workflow_outputs.splice($.inArray(b.name,d.workflow_outputs),1);l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{d.workflow_outputs.push(b.name);l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tipsy({delayIn:500,fallback:"Flag this as a workflow output. All non-flagged outputs will be hidden."});l.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});l.show();m.append(l);if($.inArray(b.name,d.workflow_outputs)==-1){l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}m.bind("hover",function(){l.find("img").attr("src",image_path+"/fugue/asterisk-small-yellow.png")});m.bind("mouseleave",function(){l.find("img").attr("src",image_path+"/fugue/asterisk-small.png");if($.inArray(b.name,d.workf
low_outputs)==-1){l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}})}m.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(m);c=Math.max(c,m.outerWidth()+17);m.css({position:"",left:"",top:"",display:""});$("body").remove(m);a.append(m.append(j))});g.css("width",Math.min(250,Math.max(g.width(),c)));workflow.node_changed(this)},update_field_data:function(f){var c=$(this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;this.annotation=f.annotation;this.post_job_actions=$.parseJSON(f.post_job_actions);if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var g=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=g.find("div.input-data-row");$.each(f.data_inputs,function(k,h){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,h.n
ame,h.extensions);g.find("div[name="+h.name+"]").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){j[0].terminal.connectors[0]=i;i.handle2=j[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+h.name+"'>"+h.label+"</div>").prepend(j))});g.replaceWith(b);g.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);th
is.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(h,i){g[i.name]=null;$.each(i.connectors,function(j,k){g[i.name]={id:k.handle1.node.id,output_name:k.handle1.name}})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={job_id:h.id,action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job
_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(a){wf=this;var b=0;wf.name=a.name;$.each(a.steps,function(f,d){var c=prebuild_node("tool",d.name,d.tool_id);c.init_field_data(d);if(d.position){c.element.css({top:d.position.top,left:d.position.left})}c.id=d.id;wf.nodes[c.id]=c;b=Math.max(b,parseInt(f))});wf.id_counter=b+1;$.each(a.steps,function(f,d){var c=wf.nodes[f];$.each(d.input_connections,function(h,g){if(g){var i=wf.nodes[g.id];var j=new Connector();j.connect(i.output_terminals[g.output_name],c.input_terminals[h]);j.redraw()}})})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_no
de!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30
;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math
.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+image_path+"/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<img/>").attr("src",image_path+"/delete_icon.png").c
lick(function(b){g.destroy()}).hover(function(){$(this).attr("src",image_path+"/delete_icon_dark.png")},function(){$(this).attr("src",image_path+"/delete_icon.png")}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o){var f=$(this).offsetParent().offset(),b=o.offsetX-f.left,p=o.offsetY-f.top;$(this).css({left:b,top:p});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];retur
n(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){thi
s.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(g){var h=$(this).offset();var f=b.cc.position();c=f.top-h.top;d=f.left-h.left}).bind("drag",function(f){a(f.offsetX+d,f.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k){var j=b.cc.width(),g=b.cc.height(),f=b.oc.width(),h=b.oc.height(),i=$(this).offsetParent().offset(),m=k.offsetX-i.left,l=k.offsetY-i.top;a(-(m/f*j),-(l/h*g))}).bind("dragend",function(){workflow.fit_canvas_to_nodes
();b.draw_overview()});$("#overview-border").bind("drag",function(g){var i=$(this).offsetParent();var h=i.offset();var f=Math.max(i.width()-(g.offsetX-h.left),i.height()-(g.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(f){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("hei
ght",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
+function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}OutputTerminal.prototype=new Terminal();function InputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.act
ion_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){if(f[b]=="input"||issubtype(f[b],this.datatypes[c])){return true}}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;this.handle1.connect(this);this.handle2=a;this.handle2.connect(this)},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};var h=n(this.handle1.element)+5;var g=i(t
his.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(d,a,b){var c=this;$(d).each(function(){var f=this.terminal=new InputTerminal(this,b);f.node=c;f.name=a;$(this).bind
("dropstart",function(g){g.dragProxy.terminal.connectors[0].inner_color="#BBFFBB"}).bind("dropend",function(g){g.dragProxy.terminal.connectors[0].inner_color="#FFFFFF"}).bind("drop",function(g){(new Connector(g.dragTarget.terminal,g.dropTarget.terminal)).redraw()}).bind("hover",function(){if(f.connectors.length>0){var g=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='buttons'></div>").append($("<img/>").attr("src",image_path+"/delete_icon.png").click(function(){$.each(f.connectors,function(i,h){h.destroy()});g.remove()}))).bind("mouseleave",function(){$(this).remove()});g.css({top:$(this).offset().top-2,left:$(this).offset().left-g.width(),"padding-right":$(this).width()}).show()}});c.input_terminals[a]=f})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j){workflow.check_changes_in_active_form();v
ar i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var k=new Connector();k.dragging=true;k.connect(this.terminal,i.terminal);$.dropManage({filter:function(h){return this.terminal.can_accept(f)}}).addClass("input-terminal-active");return i}).bind("drag",function(i){var h=function(){var k=$(i.dragProxy).offsetParent().offset(),j=i.offsetX-k.left,l=i.offsetY-k.top;$(i.dragProxy).css({left:j,top:l});i.dragProxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h){h.dragProxy.terminal.connectors[0].destroy();$(h.dragProxy).remove();$.dropManage().removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:fun
ction(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});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(h){var g=this.element;if(h.type){this.type=h.type}this.name=h.name;this.form_html=h.form_html;this.tool_state=h.tool_state;this.tool_errors=h.tool_errors;this.tooltip=h.tooltip?h.tooltip:"";this.annotation=h.annotation;this.post_job_actions=h.post_job_actions;if(h.workflow_outputs){this.workflow_outputs=h.workflow_outputs}else{this.workflow_outputs=[]}if(this.tool_errors){g.addClass("tool-node-error")}else{g.removeClass("tool-node-error")}var d=this;var c=Math.max(150,g.width());var a=g.find(".toolFormBody");a.find("div").remove();var i=$("<div class='inputs'></div
>").appendTo(a);$.each(h.data_inputs,function(k,f){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,f.name,f.extensions);var b=$("<div class='form-row dataRow input-data-row' name='"+f.name+"'>"+f.label+"</div>");b.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(b);c=Math.max(c,b.outerWidth());b.css({position:"",left:"",top:"",display:""});$("body").remove(b);i.append(b.prepend(j))});if((h.data_inputs.length>0)&&(h.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(h.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");d.enable_output_terminal(j,b.name,b.extensions);var f=b.name;if(b.extensions.indexOf("input")<0){f=f+" ("+b.extensions.join(", ")+")"}var m=$("<div class='form-row dataRow'>"+f+"</div>");if(d.type=="tool"){var l=$("<div class='callout'></div>").css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",image_path+"/
fugue/asterisk-small-outline.png").click(function(){if($.inArray(b.name,d.workflow_outputs)!=-1){d.workflow_outputs.splice($.inArray(b.name,d.workflow_outputs),1);l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{d.workflow_outputs.push(b.name);l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tipsy({delayIn:500,fallback:"Flag this as a workflow output. All non-flagged outputs will be hidden."});l.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});l.show();m.append(l);if($.inArray(b.name,d.workflow_outputs)==-1){l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}m.bind("hover",function(){l.find("img").attr("src",image_path+"/fugue/asterisk-small-yellow.png")});m.bind("mouseleave",function(){l.find("img").attr("src",image_path+"/fugue/asterisk-small.png");if($.inArray(b.name,d.workf
low_outputs)==-1){l.find("img").attr("src",image_path+"/fugue/asterisk-small-outline.png")}else{l.find("img").attr("src",image_path+"/fugue/asterisk-small.png")}})}m.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(m);c=Math.max(c,m.outerWidth()+17);m.css({position:"",left:"",top:"",display:""});$("body").remove(m);a.append(m.append(j))});g.css("width",Math.min(250,Math.max(g.width(),c)));workflow.node_changed(this)},update_field_data:function(f){var c=$(this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;this.annotation=f.annotation;this.post_job_actions=$.parseJSON(f.post_job_actions);if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var g=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=g.find("div.input-data-row");$.each(f.data_inputs,function(k,h){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,h.n
ame,h.extensions);g.find("div[name="+h.name+"]").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){j[0].terminal.connectors[0]=i;i.handle2=j[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+h.name+"'>"+h.label+"</div>").prepend(j))});g.replaceWith(b);g.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);th
is.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var a=false;$.each(this.nodes,function(b,c){if(c.workflow_outputs&&c.workflow_outputs.length>0){a=true}});if(a==false){return true}wf=this;$.each(this.nodes,function(b,f){if(f.type=="tool"){var d=false;if(f.post_job_actions==null){f.post_job_actions={}}var c=[];$.each(f.post_job_actions,function(h,g){if(g.action_type=="HideDatasetAction"){c.push(h)}});if(c.length>0&&f==workflow.active_node){$.each(c,function(g,h){d=true;delete f.post_job_actions[h]})}$.each(f.output_terminals,function(h,i){var g=true;$.each(f.workflow_outputs,function(k,l){if(i.name==l){g=false}});if(g==true){d=true;var j={action_type:"HideDatasetAction",output_name:i.name,action_arguments
:{}};f.post_job_actions["HideDatasetAction"+i.name]=null;f.post_job_actions["HideDatasetAction"+i.name]=j}});if(wf.active_node==f&&d==true){wf.reload_active_node()}}})},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(h,i){g[i.name]=null;$.each(i.connectors,function(j,k){g[i.name]={id:k.handle1.node.id,output_name:k.handle1.name}})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(a){wf=this;var b=
0;wf.name=a.name;$.each(a.steps,function(f,d){var c=prebuild_node("tool",d.name,d.tool_id);c.init_field_data(d);if(d.position){c.element.css({top:d.position.top,left:d.position.left})}c.id=d.id;wf.nodes[c.id]=c;b=Math.max(b,parseInt(f))});wf.id_counter=b+1;$.each(a.steps,function(f,d){var c=wf.nodes[f];$.each(d.input_connections,function(h,g){if(g){var i=wf.nodes[g.id];var j=new Connector();j.connect(i.output_terminals[g.output_name],c.input_terminals[h]);j.redraw()}})})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){th
is.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;
$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g
+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+image_path+"/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<img/>").attr("src",image_path+"/delete_icon.png").click(func
tion(b){g.destroy()}).hover(function(){$(this).attr("src",image_path+"/delete_icon_dark.png")},function(){$(this).attr("src",image_path+"/delete_icon.png")}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o){var f=$(this).offsetParent().offset(),b=o.offsetX-f.left,p=o.offsetY-f.top;$(this).css({left:b,top:p});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to
_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;th
is.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(g){var h=$(this).offset();var f=b.cc.position();c=f.top-h.top;d=f.left-h.left}).bind("drag",function(f){a(f.offsetX+d,f.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k){var j=b.cc.width(),g=b.cc.height(),f=b.oc.width(),h=b.oc.height(),i=$(this).offsetParent().offset(),m=k.offsetX-i.left,l=k.offsetY-i.top;a(-(m/f*j),-(l/h*g))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw
_overview()});$("#overview-border").bind("drag",function(g){var i=$(this).offsetParent();var h=i.offset();var f=Math.max(i.width()-(g.offsetX-h.left),i.height()-(g.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(f){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$
.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}});
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -614,8 +614,11 @@ function Workflow( canvas_container ) {
}
},
reload_active_node : function() {
- this.clear_active_node();
- this.activate_node(node);
+ if (this.active_node){
+ var node = this.active_node;
+ this.clear_active_node();
+ this.activate_node(node);
+ }
},
clear_active_node : function() {
if ( this.active_node ) {
1
0
galaxy-dist commit c4d8ffb3109e: Increment js version to get around cached editors.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288647270 14400
# Node ID c4d8ffb3109e8cfb8143dc87c88dee3337bb7569
# Parent 9285b39ae2f3dfd5017808b4d9b657094fec802b
Increment js version to get around cached editors.
--- a/lib/galaxy/web/framework/helpers/__init__.py
+++ b/lib/galaxy/web/framework/helpers/__init__.py
@@ -44,7 +44,7 @@ def js( *args ):
TODO: This has a hardcoded "?v=X" to defeat caching. This should be done
in a better way.
"""
- return "\n".join( [ javascript_include_tag( "/static/scripts/" + name + ".js?v=7" ) for name in args ] )
+ return "\n".join( [ javascript_include_tag( "/static/scripts/" + name + ".js?v=8" ) for name in args ] )
# Hashes
1
0
galaxy-dist commit b7ac22fab158: First pass of bug fixes, anbd additional UI cleanup for Sample Tracking.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1288713844 14400
# Node ID b7ac22fab1588a565a07acc4972564c2b9198489
# Parent 667043341e81261a7fe587daa475110486b15292
First pass of bug fixes, anbd additional UI cleanup for Sample Tracking.
--- a/templates/requests/common/edit_samples.mako
+++ b/templates/requests/common/edit_samples.mako
@@ -73,9 +73,10 @@
is_complete = request.is_complete
is_unsubmitted = request.is_unsubmitted
can_add_samples = is_unsubmitted
- can_edit_or_delete_samples = is_unsubmitted and request.samples
+ can_edit_or_delete_samples = request.samples and not is_complete
can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
can_reject_or_transfer = is_admin and request.is_submitted
+ can_submit = request.samples and is_unsubmitted
%><br/><br/>
@@ -87,7 +88,7 @@
%if editing_samples and can_add_samples:
<li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li>
%endif
- %if is_unsubmitted:
+ %if can_submit:
<li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
%endif
<li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
@@ -209,12 +210,12 @@
Click the <b>Save</b> button when you have finished editing the samples
</div>
%endif
- %if request.samples and request.is_submitted:
- <script type="text/javascript">
- // Updater
- updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
- </script>
- %endif
+ ##%if request.samples and request.is_submitted:
+ ## <script type="text/javascript">
+ ## // Updater
+ ## updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
+ ## </script>
+ ##%endif
</form></div>
%if is_unsubmitted and not editing_samples:
--- a/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -36,6 +36,12 @@ class Requests( BaseController ):
action='edit_basic_request_info',
cntrller='requests',
**kwd ) )
+ if operation == "edit_samples":
+ kwd[ 'editing_samples' ] = True
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller='requests',
+ **kwd ) )
if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='view_request',
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -46,17 +46,6 @@ class RequestsGrid( grids.Grid ):
.filter( model.RequestEvent.table.c.id.in_( select( columns=[ func.max( model.RequestEvent.table.c.id ) ],
from_obj=model.RequestEvent.table,
group_by=model.RequestEvent.table.c.request_id ) ) )
- def get_accepted_filters( self ):
- """ Returns a list of accepted filters for this column. """
- # TODO: is this method necessary?
- accepted_filter_labels_and_vals = [ model.Request.states.get( state ) for state in model.Request.states ]
- accepted_filter_labels_and_vals.append( "All" )
- accepted_filters = []
- for val in accepted_filter_labels_and_vals:
- label = val.lower()
- args = { self.key: val }
- accepted_filters.append( grids.GridColumnFilter( label, args ) )
- return accepted_filters
# Grid definition
title = "Sequencing Requests"
@@ -64,7 +53,6 @@ class RequestsGrid( grids.Grid ):
model_class = model.Request
default_sort_key = "-update_time"
num_rows_per_page = 50
- preserve_state = True
use_paging = True
default_filter = dict( state="All", deleted="False" )
columns = [
@@ -379,14 +367,24 @@ class RequestsCommon( BaseController, Us
sample_event_comment = ""
event = trans.model.RequestEvent( request, request.states.SUBMITTED, sample_event_comment )
trans.sa_session.add( event )
- # change the state of each of the samples of thus request
- new_state = request.type.states[0]
+ # Change the state of each of the samples of this request
+ # request.type.states is the list of SampleState objects configured
+ # by the admin for this RequestType.
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ # Samples will not have an associated SampleState until the request is submitted, at which
+ # time all samples of the request will be set to the first SampleState configured for the
+ # request's RequestType configured by the admin.
+ initial_sample_state_after_request_submitted = request.type.states[0]
for sample in request.samples:
- event = trans.model.SampleEvent( sample, new_state, 'Samples created.' )
+ event_comment = 'Request submitted and sample state set to %s.' % request.type.states[0].name
+ event = trans.model.SampleEvent( sample,
+ initial_sample_state_after_request_submitted,
+ event_comment )
trans.sa_session.add( event )
trans.sa_session.add( request )
trans.sa_session.flush()
- request.send_email_notification( trans, new_state )
+ request.send_email_notification( trans, initial_sample_state_after_request_submitted )
message = 'The request has been submitted.'
return trans.response.send_redirect( web.url_for( controller=cntrller,
action='browse_requests',
@@ -519,8 +517,6 @@ class RequestsCommon( BaseController, Us
try:
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
except:
- message += "Invalid request ID (%s). " % str( id )
- status = 'error'
ok_for_now = False
if ok_for_now:
request.deleted = True
@@ -549,8 +545,6 @@ class RequestsCommon( BaseController, Us
try:
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
except:
- message += "Invalid request ID (%s). " % str( id )
- status = 'error'
ok_for_now = False
if ok_for_now:
request.deleted = False
@@ -903,6 +897,7 @@ class RequestsCommon( BaseController, Us
action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( sample.request.id ),
+ editing_samples=True,
status=status,
message=message ) )
if is_admin:
@@ -1024,7 +1019,7 @@ class RequestsCommon( BaseController, Us
if sample_state_id in [ None, 'none' ]:
message = "Select a new state from the <b>Change current state</b> list before clicking the <b>Save</b> button."
kwd[ 'message' ] = message
- del kwd[ 'save_changes_button' ]
+ del kwd[ 'save_samples_button' ]
handle_error( **kwd )
sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
@@ -1056,32 +1051,38 @@ class RequestsCommon( BaseController, Us
folder_id = params.get( 'sample_0_folder_id', 'none' )
library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
self.__update_samples( trans, request, samples, **kwd )
- # See if all the samples' barcodes are in the same state,
- # and if so send email if configured to.
- common_state = request.samples_have_common_state
- if common_state and common_state.id == request.type.states[1].id:
- event = trans.model.RequestEvent( request,
- request.states.SUBMITTED,
- "All samples are in %s state." % common_state.name )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- request.send_email_notification( trans, request.type.states[1] )
+ # Samples will not have an associated SampleState until the request is submitted, at which
+ # time all samples of the request will be set to the first SampleState configured for the
+ # request's RequestType defined by the admin.
+ if request.is_submitted:
+ # See if all the samples' barcodes are in the same state, and if so send email if configured to.
+ common_state = request.samples_have_common_state
+ if common_state and common_state.id == request.type.states[1].id:
+ event = trans.model.RequestEvent( request,
+ request.states.SUBMITTED,
+ "All samples are in %s state." % common_state.name )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ request.send_email_notification( trans, request.type.states[1] )
message = 'Changes made to the samples have been saved. '
else:
- # Saving a newly created sample.
+ # Saving a newly created sample. The sample will not have an associated SampleState
+ # until the request is submitted, at which time all samples of the request will be
+ # set to the first SampleState configured for the request's RequestType configured
+ # by the admin ( i.e., the sample's SampleState would be set to request.type.states[0] ).
for index in range( len( samples ) - len( request.samples ) ):
sample_index = len( request.samples )
current_sample = samples[ sample_index ]
form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
trans.sa_session.add( form_values )
trans.sa_session.flush()
- s = trans.model.Sample( current_sample[ 'name' ],
- '',
- request,
- form_values,
- current_sample[ 'barcode' ],
- current_sample[ 'library' ],
- current_sample[ 'folder' ] )
+ s = trans.model.Sample( name=current_sample[ 'name' ],
+ desc='',
+ request=request,
+ form_values=form_values,
+ bar_code='',
+ library=current_sample[ 'library' ],
+ folder=current_sample[ 'folder' ] )
trans.sa_session.add( s )
trans.sa_session.flush()
return trans.response.send_redirect( web.url_for( controller='requests_common',
@@ -1113,49 +1114,55 @@ class RequestsCommon( BaseController, Us
if obj is not None:
# obj will be None if the user checked sample check boxes and selected an action
# to perform on multiple samples, but did not select certain samples.
- sample_updated = False
- # If this sample has values in kwd, then kwd will include a
- # key whose value is this sample's ( possibly changed ) name. An
- # example of this key is 'sample_0_name'.
- for k, v in kwd.items():
- name_key = 'sample_%i_name' % index
- if k == name_key:
- sample_updated = True
- break
- if sample_updated:
- id_index = index + 1
- if sample_operation == 'none':
- # We are handling changes to a single sample.
- library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
- folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
- # Update the corresponding sample's values as well as the sample_widget.
- sample = request.samples[ index ]
- sample.name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
- # The bar_code field requires special handling because after a request is submitted, the
- # state of a sample cannot be changed without a bar_code assocaited with the sample.
- bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
- if not bar_code and not sample.bar_code:
- # If this is a 'new' (still in its first state) sample, create an event
- if sample.state.id == request.states[0].id:
- event = trans.model.SampleEvent( sample,
- request.type.states[1],
- 'Sample added to the system' )
+ sample = request.samples[ index ]
+ # See if any values in kwd are different from the values already associated with this sample.
+ id_index = index + 1
+ if sample_operation == 'none':
+ # We are handling changes to a single sample.
+ library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
+ folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
+ # Update the corresponding sample's values as well as the sample_widget.
+ name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ # The bar_code field requires special handling because after a request is submitted, the
+ # state of a sample cannot be changed without a bar_code associated with the sample. Bar
+ # codes can only be added to a sample after the request is submitted. Also, a samples will
+ # not have an associated SampleState until the request is submitted, at which time the sample
+ # is automatically associated with the first SamplesState configured by the admin for the
+ # request's RequestType.
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ if bar_code:
+ bc_message = self.__validate_barcode( trans, sample, bar_code )
+ if bc_message:
+ kwd[ 'message' ] = bc_message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ if not sample.bar_code:
+ # If the sample's associated SampleState is still the initial state
+ # configured by the admin for the request's RequestType, this must be
+ # the first time a bar code was added to the sample, so change it's state
+ # to the next associated SampleState.
+ if sample.state.id == request.type.states[0].id:
+ event = trans.app.model.SampleEvent(sample,
+ request.type.states[1],
+ 'Bar code associated with the sample' )
trans.sa_session.add( event )
trans.sa_session.flush()
- elif bar_code:
- bc_message = self.__validate_barcode( trans, sample, bar_code )
- if bc_message:
- kwd[ 'message' ] = bc_message
- del kwd[ 'save_samples_button' ]
- handle_error( **kwd )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
+ form_values.content = field_values
+ if sample.name != name or \
+ sample.bar_code != bar_code or \
+ sample.library != library or \
+ sample.folder != folder or \
+ form_values.content != field_values:
+ # Information about this sample has been changed.
+ sample.name = name
sample.bar_code = bar_code
- library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
sample.library = library
sample.folder = folder
- field_values = []
- for field_index in range( len( request.type.sample_form.fields ) ):
- field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
- form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
form_values.content = field_values
trans.sa_session.add_all( ( sample, form_values ) )
trans.sa_session.flush()
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -9,8 +9,6 @@ import logging, os, pexpect, ConfigParse
log = logging.getLogger( __name__ )
-
-
class AdminRequestsGrid( RequestsGrid ):
class UserColumn( grids.TextColumn ):
def get_value( self, trans, grid, request ):
@@ -51,6 +49,7 @@ class RequestTypeGrid( grids.Grid ):
return request_type.sample_form.name
# Grid definition
+ webapp = "galaxy"
title = "Sequencer Configurations"
template = "admin/requests/grid.mako"
model_class = model.RequestType
@@ -103,6 +102,7 @@ class DataTransferGrid( grids.Grid ):
def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.status
# Grid definition
+ webapp = "galaxy"
title = "Sample Datasets"
template = "admin/requests/grid.mako"
model_class = model.SampleDataset
@@ -129,9 +129,19 @@ class DataTransferGrid( grids.Grid ):
visible=False,
filterable="standard" ) )
operations = [
- grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
- grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
- grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
+ grids.GridOperation( "Transfer",
+ allow_multiple=True,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) ),
+ grids.GridOperation( "Rename",
+ allow_multiple=True,
+ allow_popup=False,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) ),
+ grids.GridOperation( "Delete",
+ allow_multiple=True,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) )
]
def apply_query_filter( self, trans, query, **kwd ):
sample_id = kwd.get( 'sample_id', None )
@@ -158,6 +168,12 @@ class RequestsAdmin( BaseController, Use
action='edit_basic_request_info',
cntrller='requests_admin',
**kwd ) )
+ if operation == "edit_samples":
+ kwd[ 'editing_samples' ] = True
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller='requests_admin',
+ **kwd ) )
if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='view_request',
@@ -273,7 +289,7 @@ class RequestsAdmin( BaseController, Use
break
if no_datasets_transferred:
status = 'error'
- message = 'A dataset can be renamed only if it is in the "Not Started" state.'
+ message = 'A dataset can be renamed only if it has been transferred.'
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
sample_id=trans.security.encode_id( selected_sample_datasets[0].sample.id ),
@@ -282,7 +298,7 @@ class RequestsAdmin( BaseController, Use
return trans.fill_template( '/admin/requests/rename_datasets.mako',
sample=selected_sample_datasets[0].sample,
id_list=id_list )
- elif operation == "start transfer":
+ elif operation == "transfer":
self.__start_datatx( trans, selected_sample_datasets[0].sample, selected_sample_datasets )
# Render the grid view
sample_id = params.get( 'sample_id', None )
@@ -292,7 +308,7 @@ class RequestsAdmin( BaseController, Use
return invalid_id_redirect( trans, 'requests_admin', sample_id )
request_id = trans.security.encode_id( sample.request.id )
library_id = trans.security.encode_id( sample.library.id )
- self.datatx_grid.global_actions = [ grids.GridAction( "Refresh",
+ self.datatx_grid.global_actions = [ grids.GridAction( "Refresh page",
dict( controller='requests_admin',
action='manage_datasets',
sample_id=sample_id ) ),
@@ -302,11 +318,11 @@ class RequestsAdmin( BaseController, Use
request_id=request_id,
folder_path=sample.request.type.datatx_info[ 'data_dir' ],
sample_id=sample_id ) ),
- #grids.GridAction( 'Data library "%s"' % sample.library.name,
- # dict( controller='library_common',
- # action='browse_library',
- # cntrller='library_admin',
- # id=library_id ) ),
+ grids.GridAction( "Browse target data library",
+ dict( controller='library_common',
+ action='browse_library',
+ cntrller='library_admin',
+ id=library_id ) ),
grids.GridAction( "Browse this request",
dict( controller='requests_common',
action='view_request',
--- a/templates/requests/common/view_request.mako
+++ b/templates/requests/common/view_request.mako
@@ -18,15 +18,18 @@
from galaxy.web.framework.helpers import time_ago
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = request.is_complete
is_unsubmitted = request.is_unsubmitted
can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
can_add_samples = is_unsubmitted
+ can_edit_or_delete_samples = request.samples and not is_complete
+ can_submit = request.samples and is_unsubmitted
%><br/><br/><ul class="manage-table-actions">
- %if is_unsubmitted:
+ %if can_submit:
<li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
%endif
<li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
@@ -66,13 +69,12 @@
</div><div class="form-row"><label>User:</label>
- %if is_admin:
- ${request.user.email}
- %elif request.user.username:
- ${request.user.username}
- %else:
- Unknown
- %endif
+ ${request.user.email}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Sequencer configuration:</label>
+ ${request.type.name}
<div style="clear: both"></div></div><div class="form-row">
@@ -129,11 +131,6 @@
${states}
<div style="clear: both"></div></div>
- <div class="form-row">
- <label>Sequencer configuration:</label>
- ${request.type.name}
- <div style="clear: both"></div>
- </div></div></div></div>
@@ -141,7 +138,7 @@
<p/>
%if current_samples:
<% grid_header = '<h3>Samples</h3>' %>
- ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=can_edit_request, grid_header=grid_header )}
+ ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=can_edit_or_delete_samples, grid_header=grid_header )}
%else:
There are no samples.
%if can_add_samples:
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -752,11 +752,18 @@
%if grid.global_actions:
<ul class="manage-table-actions">
- %for action in grid.global_actions:
- <li>
- <a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a>
- </li>
- %endfor
+ %if len( grid.global_actions ) < 4:
+ %for action in grid.global_actions:
+ <li><a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a></li>
+ %endfor
+ %else:
+ <li><a class="action-button" id="action-8675309-popup" class="menubutton">Actions</a></li>
+ <div popupmenu="action-8675309-popup">
+ %for action in grid.global_actions:
+ <a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a>
+ %endfor
+ </div>
+ %endif
</ul>
%endif
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -88,26 +88,26 @@
%if is_admin and is_submitted and editing_samples:
<td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" ${checked_str}/><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"/></td>
%endif
- <td>
+ <td valign="top"><input type="text" name="sample_${current_sample_index}_name" value="${current_sample['name']}" size="10"/><div class="toolParamHelp" style="clear: both;"><i>${' (required)' }</i></div></td>
%if sample and is_submitted or is_complete:
- <td><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
+ <td valign="top"><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
%endif
%if sample:
%if is_unsubmitted:
<td>Unsubmitted</td>
%else:
- <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
+ <td valign="top"><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
%endif
%else:
<td></td>
%endif
- <td>${current_sample['library_select_field'].get_html()}</td>
- <td>${current_sample['folder_select_field'].get_html()}</td>
+ <td valign="top">${current_sample['library_select_field'].get_html()}</td>
+ <td valign="top">${current_sample['folder_select_field'].get_html()}</td>
%if is_submitted or is_complete:
<%
if sample:
@@ -115,12 +115,12 @@
else:
label = 'add'
%>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ <td valign="top"><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ <td valign="top"><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
%endif
%if sample and ( is_admin or is_unsubmitted ):
## Delete button
- <td><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td>
+ <td valign="top"><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td>
%endif
</%def>
@@ -132,7 +132,7 @@
is_submitted = request.is_submitted
is_unsubmitted = request.is_unsubmitted
can_add_samples = request.is_unsubmitted
- can_edit_or_delete_samples = request.samples and ( is_admin or request.is_unsubmitted )
+ can_edit_or_delete_samples = request.samples and not is_complete
%>
${grid_header}
%if render_buttons and ( can_add_samples or can_edit_or_delete_samples ):
1
0
galaxy-dist commit cf7ec71c5613: Shift management of the interaction between workflow outputs and HideDatasetActions to the front end editor.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1288645730 14400
# Node ID cf7ec71c561323940ce95c8148d2008508920092
# Parent 1efe19f6f3d1a75bda9c198633dd8102d3063410
Shift management of the interaction between workflow outputs and HideDatasetActions to the front end editor.
This resolves the issue with multiple HideDatasetActions being created.
Existing workflows displaying multiple HideDatasetActions per step on the Run Workflow screen will persist.
These extra HideDatasetActions are harmless, but a simple edit workflow -> save will remove them.
--- a/lib/galaxy/web/controllers/workflow.py
+++ b/lib/galaxy/web/controllers/workflow.py
@@ -1270,13 +1270,6 @@ class WorkflowController( BaseController
workflow_invocation = model.WorkflowInvocation()
workflow_invocation.workflow = workflow
outputs = odict()
- # Find out if there are any workflow outputs defined, as that influences our actions.
- use_workflow_outputs = False
- for step in workflow.steps:
- if step.type == 'tool' or step.type is None:
- if step.workflow_outputs:
- use_workflow_outputs = True
- break
for i, step in enumerate( workflow.steps ):
# Execute module
job = None
@@ -1294,24 +1287,6 @@ class WorkflowController( BaseController
job, out_data = tool.execute( trans, step.state.inputs )
outputs[ step.id ] = out_data
# Create new PJA associations with the created job, to be run on completion.
- if use_workflow_outputs:
- # We're using outputs. Check the step for outputs to be displayed. Create PJAs to hide the rest upon completion.
- step_outputs = [s.output_name for s in step.workflow_outputs]
- for output in tool.outputs.keys():
- if output not in step_outputs:
- # Necessary, unfortunately, to clean up workflows that might have more than one at this point.
- for pja in step.post_job_actions:
- if pja.action_type == "HideDatasetAction" and pja.output_name == output:
- step.post_job_actions.remove(pja)
- trans.sa_session.delete(pja)
- # Create a PJA for hiding this output.
- n_pja = PostJobAction('HideDatasetAction', step, output, {})
- else:
- # Remove any HideDatasetActions, step is flagged for output.
- for pja in step.post_job_actions:
- if pja.action_type == "HideDatasetAction" and pja.output_name == output:
- step.post_job_actions.remove(pja)
- trans.sa_session.delete(pja)
for pja in step.post_job_actions:
if pja.action_type in ActionBox.immediate_actions:
ActionBox.execute(trans.app, trans.sa_session, pja, job)
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -475,6 +475,63 @@ function Workflow( canvas_container ) {
wf.remove_node( v );
});
},
+ rectify_workflow_outputs : function() {
+ console.log("RECTIFICATION!");
+ // Find out if we're using workflow_outputs or not.
+ var using_workflow_outputs = false;
+ $.each( this.nodes, function ( k, node ) {
+ if (node.workflow_outputs && node.workflow_outputs.length > 0){
+ using_workflow_outputs = true;
+ }
+ });
+ if (using_workflow_outputs == false){
+ //We're done, leave PJAs alone.
+ return true;
+ }
+ wf = this;
+ $.each(this.nodes, function (k, node ){
+ if (node.type == 'tool'){
+ var node_changed = false;
+ if (node.post_job_actions == null){
+ console.log("CREATED FOR NEW NODE");
+ node.post_job_actions = {};
+ }
+ var pjas_to_rem = [];
+ $.each(node.post_job_actions, function(pja_id, pja){
+ if (pja.action_type == "HideDatasetAction"){
+ pjas_to_rem.push(pja_id);
+ }
+ });
+ if (pjas_to_rem.length > 0 && node == workflow.active_node)
+ $.each(pjas_to_rem, function(i, pja_name){
+ node_changed = true;
+ delete node.post_job_actions[pja_name];
+ })
+ $.each(node.output_terminals, function(ot_id, ot){
+ var create_pja = true;
+ $.each(node.workflow_outputs, function(i, wo_name){
+ if (ot.name == wo_name){
+ create_pja = false;
+ }
+ });
+ if (create_pja == true){
+ node_changed = true;
+ var pja = {
+ action_type : "HideDatasetAction",
+ output_name : ot.name,
+ action_arguments : {}
+ }
+ node.post_job_actions['HideDatasetAction'+ot.name] = null;
+ node.post_job_actions['HideDatasetAction'+ot.name] = pja;
+ }
+ });
+ // lastly, if this is the active node, and we made changes, reload the display at right.
+ if (wf.active_node == node && node_changed == true) {
+ wf.reload_active_node();
+ }
+ }
+ });
+ },
to_simple : function () {
var nodes = {};
$.each( this.nodes, function ( i, node ) {
@@ -491,7 +548,6 @@ function Workflow( canvas_container ) {
if (node.post_job_actions){
$.each( node.post_job_actions, function ( i, act ) {
var pja = {
- job_id : act.id,
action_type : act.action_type,
output_name : act.output_name,
action_arguments : act.action_arguments
@@ -559,6 +615,10 @@ function Workflow( canvas_container ) {
this.active_form_has_changes = false;
}
},
+ reload_active_node : function() {
+ this.clear_active_node();
+ this.activate_node(node);
+ },
clear_active_node : function() {
if ( this.active_node ) {
this.active_node.make_inactive();
--- a/templates/workflow/editor.mako
+++ b/templates/workflow/editor.mako
@@ -627,6 +627,7 @@
}
return;
}
+ workflow.rectify_workflow_outputs();
var savefn = function(callback) {
$.ajax( {
url: "${h.url_for( action='save_workflow' )}",
1
0
galaxy-dist commit cbb895e2b272: If a job's input fails to set metadata, fail the job rather than leaving it in the 'new' state indefinitely.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1288376593 14400
# Node ID cbb895e2b2729d324c1693882de7939360e683a4
# Parent 23844e86e6167c79737a7e0396c39e350e95d1c4
If a job's input fails to set metadata, fail the job rather than leaving it in the 'new' state indefinitely.
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -241,6 +241,9 @@ class JobQueue( object ):
elif idata.state == idata.states.ERROR:
JobWrapper( job, self ).fail( "input data %d is in error state" % ( idata.hid ) )
return JOB_INPUT_ERROR
+ elif idata.state == idata.states.FAILED_METADATA:
+ JobWrapper( job, self ).fail( "input data %d failed to properly set metadata" % ( idata.hid ) )
+ return JOB_INPUT_ERROR
elif idata.state != idata.states.OK and not ( idata.state == idata.states.SETTING_METADATA and job.tool_id is not None and job.tool_id == self.app.datatypes_registry.set_external_metadata_tool.id ):
# need to requeue
return JOB_WAIT
1
0