galaxy-commits
Threads by month
- ----- 2026 -----
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 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 569483091092: Keep the sample's bar code incormation when editn the sample row in the non-admin user view.
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 1288731289 14400
# Node ID 56948309109207f932a113e464409432c7a38df7
# Parent 27c152bb441a0136720dbbc0c7cb293e581b8f5f
Keep the sample's bar code incormation when editn the sample row in the non-admin user view.
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -153,6 +153,7 @@
<input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/>
%else:
${current_sample['barcode']}
+ <input type="hidden" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}"/>
%endif
</td>
%endif
@@ -261,7 +262,7 @@
%elif sample:
<tr><td>${current_sample_name}</td>
- %if is_submitted or is_complete:
+ %if display_bar_code:
<td>${current_sample_barcode}</td>
%endif
%if is_unsubmitted:
1
0
galaxy-dist commit e000a6800e1c: trackster: Fix summary_tree data provider to reflect bottom level not being indexed anymore.
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 1288748135 14400
# Node ID e000a6800e1cbfa15a190bb2aff90a5956882a43
# Parent 56948309109207f932a113e464409432c7a38df7
trackster: Fix summary_tree data provider to reflect bottom level not being indexed anymore.
- Fix a bug causing no display modes to appear.
- Update "Visualize in Trackster" init code in root/history.mako
Fix small typo in root/tool_menu.mako
--- a/templates/root/tool_menu.mako
+++ b/templates/root/tool_menu.mako
@@ -32,7 +32,7 @@
<div class="toolTitleNoSection">
%endif
<% encoded_id = key.lstrip( 'workflow_' ) %>
- <a id="link-${workflow.id}" href="${ h.url_for( controller='workflow', action='run', id=encoded_id, check_user=False )}" target="_parent"}">${_(workflow.name)}</a>
+ <a id="link-${workflow.id}" href="${ h.url_for( controller='workflow', action='run', id=encoded_id, check_user=False )}" target="_parent">${_(workflow.name)}</a></div></%def>
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -677,7 +677,7 @@ var TiledTrack = function() {
track.draw();
};
var mode_mapping = {};
- for (var i, len = track.display_modes.length; i < len; i++) {
+ for (var i = 0, len = track.display_modes.length; i < len; i++) {
var mode = track.display_modes[i];
mode_mapping[mode] = function(mode) {
return function() { change_mode(mode); };
--- a/lib/galaxy/visualization/tracks/data_providers.py
+++ b/lib/galaxy/visualization/tracks/data_providers.py
@@ -102,14 +102,14 @@ class SummaryTreeDataProvider( TracksDat
level = ceil( log( resolution, st.block_size ) ) - 1
level = int(max( level, 0 ))
- if level <= 0:
+ if level <= 1:
return None
stats = st.chrom_stats[chrom]
results = st.query(chrom, int(start), int(end), level)
if results == "detail":
return None
- elif results == "draw" or level <= 1:
+ elif results == "draw":
return "no_detail"
else:
return results, stats[level]["max"], stats[level]["avg"], stats[level]["delta"]
--- a/templates/root/history.mako
+++ b/templates/root/history.mako
@@ -157,29 +157,33 @@ TERMINAL_STATES = ${ h.to_json_string(TE
// Trackster links
function init_trackster_links() {
- $("a.trackster").live( "click", function() {
- var link = $(this),
- hid = link.attr("id").split("_")[1]; // visualize_{id}
-
+ // Add to trackster browser functionality
+ $(".trackster-add").live("click", function() {
+ var dataset = this,
+ dataset_jquery = $(this);
$.ajax({
- url: "${h.url_for( controller='tracks', action='list_tracks' )}",
- data: {'hid': hid},
- error: function() { alert( "Visualization error" ); },
- success: function(html) {
- show_modal("Add Track — Select Dataset(s)", html, {
- "New Browser": function() {
- hide_modal();
+ 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": function() {
- 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;
+ });
},
- "Cancel": function() {
- hide_modal();
+ "Insert into new browser": function() {
+ parent.location = dataset_jquery.attr("new-url");
}
});
}
});
- });
+ });
}
init_trackster_links();
--- a/static/scripts/packed/trackster.js
+++ b/static/scripts/packed/trackster.js
@@ -1,1 +1,1 @@
-var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=50,CONNECTOR_COLOR="#ccc",DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",FILTERABLE_CLASS="filterable",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=5,CACHED_DATA=5,DUMMY_CANVAS=document.createElement("canvas"),RIGHT_STRAND,LEFT_STRAND;if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(DUMMY_CANVAS)}CONTEXT=DUMMY_CANVAS.getContext("2d");PX_PER_CHAR=CONTEXT.measureText("A").width;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPa
ttern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src=image_path+"/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function round_1000(a){return Math.round(a*1000)/1000}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var View=function(a,d,c,b){this.container=a;this.vis_id=c;this.dbkey=b;this.ti
tle=d;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide();this.nav_container=$("<div/>").addClass("nav-container").appendTo(c);this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);this.nav=$("<div/>").addClass("nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.nav);this.overview_viewport=$("<div/>").addClas
s("overview-viewport").appendTo(this.overview);this.overview_close=$("<a href='javascript:void(0);'>Close Overview</a>").addClass("overview-close").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div />").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("<form/>").attr("action",function(){}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.wh
ich)!==27){a.go_to($(this).val())}$(this).hide();a.location_span.show();a.chrom_select.show();return false}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keypress focusout",b).appendTo(this.chrom_form);this.location_span=$("<span/>").addClass("location").appendTo(this.chrom_form);this.location_span.bind("click",function(){a.location_span.hide();a.chrom_select.hide();a.nav_input.css("display","inline-block");a.nav_input.select();a.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.chrom_form)}this.zo_link=$("<a/>").click(function(){a.zoom_out();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom-out.png" />').appendTo(this.chrom_form);this.zi_link=$("<a/>").click(function(){a.zoom_in();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom.png" />').appendTo(this.chrom_form);$.ajax({url:chrom_url,data:(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:
this.dbkey}),dataType:"json",success:function(e){if(e.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=e.chrom_info;var h='<option value="">Select Chrom/Contig</option>';for(var g=0,d=a.chrom_data.length;g<d;g++){var f=a.chrom_data[g].chrom;h+='<option value="'+f+'">'+f+"</option>"}a.chrom_select.html(h);a.intro_div.show();a.chrom_select.bind("change",function(){a.change_chrom(a.chrom_select.val())})},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("dblclick",function(d){a.zoom_in(d.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(d){this.current_x=d.offsetX}).bind("drag",function(d){var g=d.offsetX-this.current_x;this.current_x=d.offsetX;var f=Math.round(g/a.viewport_container.width()*(a.max_high-a.max_low));a.move_delta(-f)});this.overview_close.bind("click",function(){for(var e=0,d=a.tracks.length;e<d;e++){a.tracks[e].is_overview=false}$(this).siblings().filter("canvas").remove();
$(this).parent().css("height",a.overview_box.height());a.overview_highlight.hide();$(this).hide()});this.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX;this.enable_pan=(d.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(g){if(!this.enable_pan||this.in_reordering){return}var d=$(this);var i=g.offsetX-this.current_x;var f=d.scrollTop()-(g.clientY-this.current_height);d.scrollTop(f);this.current_height=g.clientY;this.current_x=g.offsetX;var h=Math.round(i/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltrack.bind("dragstart",function(d){this.drag_origin_x=d.clientX;this.drag_origin_pos=d.clientX/a.viewport_container.width()*(a.high-a.low)+a.low;this.drag_div=$("<div />").css({height:a.content_div.height()+30,top:"0px",position:"absolute","background-color":"#cfc",border:"1px solid #6a6",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag
",function(i){var f=Math.min(i.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(i.clientX,this.drag_origin_x)-a.container.offset().left,h=(a.high-a.low),g=a.viewport_container.width();a.update_location(Math.round(f/g*h)+a.low,Math.round(d/g*h)+a.low);this.drag_div.css({left:f+"px",width:(d-f)+"px"})}).bind("dragend",function(j){var f=Math.min(j.clientX,this.drag_origin_x),d=Math.max(j.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),i=a.low;a.low=Math.round(f/g*h)+i;a.high=Math.round(d/g*h)+i;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack));$(window).bind("resize",function(){a.resize_window()});$(document).bind("redraw",function(){a.redraw()});this.reset();$(window).trigger("resize")},update_location:function(a,b){this.location_span.text(commatize(a)+" - "+commatize(b));this.nav_input.val(this.chrom+":"+commatize(a)+"-"+c
ommatize(b))},change_chrom:function(e,b,g){var d=this;var f=$.grep(d.chrom_data,function(j,k){return j.chrom===e})[0];if(f===undefined){return}if(e!==d.chrom){d.chrom=e;if(!d.chrom){d.intro_div.show()}else{d.intro_div.hide()}d.chrom_select.val(d.chrom);d.max_high=f.len;d.reset();d.redraw(true);for(var h=0,a=d.tracks.length;h<a;h++){var c=d.tracks[h];if(c.init){c.init()}}}if(b!==undefined&&g!==undefined){d.low=Math.max(b,0);d.high=Math.min(g,d.max_high)}d.reset_overview();d.redraw()},go_to:function(f){var j=this,a,d,b=f.split(":"),h=b[0],i=b[1];if(i!==undefined){try{var g=i.split("-");a=parseInt(g[0].replace(/,/g,""),10);d=parseInt(g[1].replace(/,/g,""),10)}catch(c){return false}}j.change_chrom(h,a,d)},move_delta:function(c){var a=this;var b=a.high-a.low;if(a.low-c<a.max_low){a.low=a.max_low;a.high=a.max_low+b}else{if(a.high-c>a.max_high){a.high=a.max_high;a.low=a.max_high-b}else{a.high-=c;a.low-=c}}a.redraw()},add_track:function(a){a.view=this;a.track_id=this.track_id_counte
r;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1;this.num_tracks+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)];this.num_tracks-=1},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(h){var g=this.high-this.low,f=this.low,b=this.high;if(f<this.max_low){f=this.max_low}if(b>this.max_high){b=this.max_high}if(this.high!==0&&g<this.min_separation){b=f+this.min_separation}this.low=Math.floor(f);this.high=Math.ceil(b);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));var a=(this.low/(this.max_high-this.max
_low)*this.overview_viewport.width())||0;var e=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var j=13;this.overview_box.css({left:a,width:Math.max(j,e)}).show();if(e<j){this.overview_box.css("left",a-(j-e)/2)}if(this.overview_highlight){this.overview_highlight.css({left:a,width:e})}this.update_location(this.low,this.high);if(!h){for(var c=0,d=this.tracks.length;c<d;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(c=0,d=this.label_tracks.length;c<d;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);th
is.high=Math.round(c+a);this.redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.nav_container.height()-45);this.nav_container.width(this.container.width());this.redraw()},reset_overview:function(){this.overview_viewport.find("canvas").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide()}});var Filter=function(b,a,c){this.name=b;this.index=a;this.value=c};var NumberFilter=function(b,a){this.name=b;this.index=a;this.low=-Number.MAX_VALUE;this.high=Number.MAX_VALUE;this.slider_min=Number.MAX_VALUE;this.slider_max=-Number.MAX_VALUE;this.slider=null;this.slider_label=null};$.extend(NumberFilter.prototype,{applies_to:function(a){if(a.length>this.index){return true}return false},keep:function(a){if(!this.applies_to(a)){return true}return(a[this.index]>=this.low&&a[this.index]<=this.high)},update_attrs:function(b
){var a=false;if(!this.applies_to(b)){return a}if(b[this.index]<this.slider_min){this.slider_min=b[this.index];a=true}if(b[this.index]>this.slider_max){this.slider_max=b[this.index];a=false}return a},update_ui_elt:function(){var b=this.slider.slider("option","min"),a=this.slider.slider("option","max");if(this.slider_min<b||this.slider_max>a){this.slider.slider("option","min",this.slider_min);this.slider.slider("option","max",this.slider_max);this.slider.slider("option","values",[this.slider_min,this.slider_max])}}});var get_filters=function(a){var g=[];for(var d=0;d<a.length;d++){var f=a[d];var c=f.name,e=f.type,b=f.index;if(e=="int"||e=="float"){g[d]=new NumberFilter(c,b)}else{g[d]=new Filter(c,b,e)}}return g};var Track=function(b,a,d,c){this.name=b;this.view=a;this.parent_element=d;this.filters=(c!==undefined?get_filters(c):[]);this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track").css("position","relative");i
f(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);if(this.view.editor){this.drag_div=$("<div class='draghandle' />").appendTo(this.header_div)}this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name);this.name_div.attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase())}this.filtering_div=$("<div class='track-filters'>").appendTo(this.container_div);this.filtering_div.hide();this.filtering_div.bind("drag",function(i){i.stopPropagation()});var b=$("<table class='filters'>").appendTo(this.filtering_div);var c=this;for(var e=0;e<this.filters.length;e++){var a=this.filters[e];var f=$("<tr>").appendTo(b);var g=$("<th class='filter-info'>").appendTo(f);var j=$("<span class='name'>").appendTo(g);j.text(a.name+" ");var d=$("<span class='values'>").appendTo(g);var h=$("<td>").appendTo(f);a.control_element=$("<div id='"+a.name+"-filter-control' style='width: 200
px; position: relative'>").appendTo(h);a.control_element.slider({range:true,min:Number.MAX_VALUE,max:-Number.MIN_VALUE,values:[0,0],slide:function(k,l){var i=l.values;d.text("["+i[0]+"-"+i[1]+"]");a.low=i[0];a.high=i[1];c.draw(true)},change:function(i,k){a.control_element.slider("option","slide").call(a.control_element,i,k)}});a.slider=a.control_element;a.slider_label=d}this.content_div=$("<div class='track-content'>").appendTo(this.container_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.initial_canvas=undefined;a.content_div.css("height","auto");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"||d.kind==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR);if(d.message){var f=a.view.tracks.indexOf(a);var e=$("
<a href='javascript:void(0);'></a>").attr("id",f+"_error");e.text("Click to view error");$("#"+f+"_error").live("click",function(){show_modal("Trackster Error","<pre>"+d.message+"</pre>",{Close:hide_modal})});a.content_div.append(e)}}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.data!==undefined&&(d.data===null||d.data.length===0)){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){var b=this,j=b.view;if(b.hidden){return}if(b.display_modes!==undefined){if(b.mode_div===undefined){b.mode_div=$("<div class='right-float menubutton popup' />").appendTo(b.header_div);var e=
b.display_modes[0];b.mode=e;b.mode_div.text(e);var c=function(i){b.mode_div.text(i);b.mode=i;b.tile_cache.clear();b.draw()};var a={};for(var f,h=b.display_modes.length;f<h;f++){var g=b.display_modes[f];a[g]=function(i){return function(){c(i)}}(g)}make_popupmenu(b.mode_div,a)}else{b.mode_div.hide()}}var d={};d["Set as overview"]=function(){j.overview_viewport.find("canvas").remove();b.is_overview=true;b.set_overview();for(var i in j.tracks){if(j.tracks[i]!==b){j.tracks[i].is_overview=false}}};d["Edit configuration"]=function(){var l=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},i=function(){b.update_options(b.track_id);hide_modal();$(window).unbind("keypress.check_enter_esc")},k=function(m){if((m.keyCode||m.which)===27){l()}else{if((m.keyCode||m.which)===13){i()}}};$(window).bind("keypress.check_enter_esc",k);show_modal("Configure Track",b.gen_options(b.track_id),{Cancel:l,OK:i})};if(b.filters.length>0){d["Show filters"]=function(){var i;if(!b.filterin
g_div.is(":visible")){i="Hide filters";b.filters_visible=true}else{i="Show filters";b.filters_visible=false}$("#"+b.name_div.attr("id")+"-menu").find("li").eq(2).text(i);b.filtering_div.toggle()}}d.Remove=function(){j.remove_track(b);if(j.num_tracks===0){$("#no-tracks").show()}};b.popup_menu=make_popupmenu(b.name_div,d);show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters",false)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(a){var k=this.view.low,g=this.view.high,h=g-k,f=this.view.resolution;var n=$("<div style='position: relative;'></div>"),o=this.content_div.width()/h;this.content_div.append(n);this.max_height=0;var b=Math.floor(k/f/DENSITY);var j={};while((b*DENSITY*f)<g){var l=this.content_div.width()+"_"+o+"_"+b;var e=this.tile_cache.get(l);if(!a&&e){var i=b*DENSITY*f;var d=(i-k)*o;if(this.left_offset){d-=this.left_offset}e.css({left:d});this.show_tile(e,n)}else{this.delayed_draw(this,l,k,g,b,f,n,o,j)}b+=1}var c=this;var m=setInterval(function(
){if(obj_length(j)===0){if(c.content_div.children().length>1){c.content_div.children(":first").remove()}for(var p=0;p<c.filters.length;p++){c.filters[p].update_ui_elt()}clearInterval(m)}},50)},delayed_draw:function(c,h,g,e,b,d,i,j,f){var a=setTimeout(function(){if(g<=c.view.high&&e>=c.view.low){var k=c.draw_tile(d,b,i,j);if(k){if(!c.initial_canvas&&!window.G_vmlCanvasManager){c.initial_canvas=$(k).clone();var n=k.get(0).getContext("2d");var l=c.initial_canvas.get(0).getContext("2d");var m=n.getImageData(0,0,n.canvas.width,n.canvas.height);l.putImageData(m,0,0);c.set_overview()}c.tile_cache.set(h,k);c.show_tile(k,i)}}delete f[a]},50);f[a]=true},show_tile:function(a,c){var b=this;c.append(a);b.max_height=Math.max(b.max_height,a.height());b.content_div.css("height",b.max_height+"px");if(a.hasClass(FILTERABLE_CLASS)){show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters");if(b.filters_visible){b.filtering_div.show()}}else{show_hide_popupmenu_options(b.popup_menu,"(Show|H
ide) filters",false);b.filtering_div.hide()}},set_overview:function(){var a=this.view;if(this.initial_canvas&&this.is_overview){a.overview_close.show();a.overview_viewport.append(this.initial_canvas);a.overview_highlight.show().height(this.initial_canvas.height());a.overview_viewport.height(this.initial_canvas.height()+a.overview_box.height())}$(window).trigger("resize")}});var LabelTrack=function(a,b){this.track_type="LabelTrack";this.hidden=true;Track.call(this,null,a,b);this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.
append(b)}});var ReferenceTrack=function(a){this.track_type="ReferenceTrack";this.hidden=true;Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.left_offset=200;this.height_px=12;this.container_div.addClass("reference-track");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,j=f+"_"+b;var e=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(e)}e=$(e);var n=e.get(0).getContext("2d");if(o>PX_PER_CHAR){if(this.data_
cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o),i=Math.round(o/2);n.fillText(m[h],a+this.left_offset+i,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=80;this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={color:"black",min_value:undefined,max_value:undefined,mode:this.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.verti
cal_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");var e=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=e.min;a.prefs.max_value=e.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=e.total_frequency;a.container_div.find(".yaxislabel").remove();var f=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(round_1000(a.prefs.min_value));var d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(round_1000(a.prefs.max_value));d.css({position:"absolute",top:"22px",left:"10px"});d.prependTo(a.container_div);f.css({position:"absolute",top:a.height_px+11+"px",left:"10px"});f.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b
+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){var h=g.data;c.data_cache.set(e,h);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*o,a=DENSITY*o,w=o+"_"+r;var b=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(b)}b=$(b);if(this.data_cache.get(w)===undefined){this.get_data(o,r);return}var v=this.data_cache.get(w);if(!v){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),j=false,k=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,t=this.total_frequency,d=this.height_px,l=this.mode;n.beginPath();n.fillStyle=this.prefs.colo
r;var u,h,f;if(v.length>1){f=Math.ceil((v[1][0]-v[0][0])*e)}else{f=10}for(var p=0,q=v.length;p<q;p++){u=Math.round((v[p][0]-s)*e);h=v[p][1];if(h===null){if(j&&l==="Filled"){n.lineTo(u,d)}j=false;continue}if(h<k){h=k}else{if(h>g){h=g}}if(l==="Histogram"){h=Math.round(d-(h-k)/m*d);n.fillRect(u,h,f,d-h)}else{if(l==="Intensity"){h=255-Math.floor((h-k)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(u,0,f,d)}else{h=Math.round(d-(h-k)/m*d);if(j){n.lineTo(u,h)}else{j=true;if(l==="Filled"){n.moveTo(u,d);n.lineTo(u,h)}else{n.moveTo(u,h)}}}}}if(l==="Filled"){if(j){n.lineTo(u,d)}n.fill()}else{n.stroke()}c.append(b);return b},gen_options:function(m){var a=$("<div />").addClass("form-row");var e="track_"+m+"_color",b=$("<label />").attr("for",e).text("Color:"),c=$("<input />").attr("id",e).attr("name",e).val(this.prefs.color),h="track_"+m+"_minval",l=$("<label></label>").attr("for",h).text("Min value:"),d=(this.prefs.min_value===undefined?"":this.prefs.min_value),k=$("<input></inp
ut>").attr("id",h).val(d),j="track_"+m+"_maxval",g=$("<label></label>").attr("for",j).text("Max value:"),i=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",j).val(i);return a.append(l).append(k).append(g).append(f).append(b).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_color").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!==this.prefs.color){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.color=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,e,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container,e);TiledTrack.call(this);this.height_px=
0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=30;this.default_font="9px Monaco, Lucida Console, monospace";this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.left_offset=200;this.prefs={block_color:"#444",label_color:"black",show_counts:true}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="initial";this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution,mode:a.mode},function(c){a.mode_div.show();a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.
resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,b,q){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=q;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,y=[],h=0,n=this.view.max_low;var A=[];if(this.inc_slots[a].mode!==q){delete this.inc_slots[a];this.inc_slots[a]={mode:q,w_scale:m};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var v=0,w=g.length;v<w;v++){var f=g[v],l=f[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);A.push(this.inc_slots[a][l])}else{y.push(v)}}for(var v=0,w=y.length;v<w;v++){var f=g[y[v]],l=f[0],r=f[1],c=f[2],p=f[3],d=Math.floor((r-n)*m),e=Math.ceil((c-n)*m);if(p!==undefined&&!b){var s=CONTEXT.measureText(p).width;if(d-s<0){e+=s}else{d-=s}}var u=0;while(u<=MAX_FEATURE_DEPTH){var o=true;if(this.s_e_by_tile[a][u]!==undefined){for(var t=0,z=this.s_e_by_tile[a][u].length;t<z;t++){var x=this.s_e_by
_tile[a][u][t];if(e>x[0]&&d<x[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][u]===undefined){this.s_e_by_tile[a][u]=[]}this.s_e_by_tile[a][u].push([d,e]);this.inc_slots[a][l]=u;h=Math.max(h,u);break}u++}}return h},rect_or_text:function(r,l,t,b,q,f,i,e){r.textAlign="center";var k=0,p=Math.round(l/2);for(var m=0,s=i.length;m<s;m++){var j=i[m],d="MIDNSHP"[j[0]],n=j[1];if(d==="H"||d==="S"){k-=n}var g=q+k,w=Math.floor(Math.max(0,(g-t)*l)),h=Math.floor(Math.max(0,(g+n-t)*l));switch(d){case"S":case"H":case"M":var o=f.slice(k,n);if((this.mode==="Pack"||this.mode==="Auto")&&f!==undefined&&l>PX_PER_CHAR){r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+1,h-w,9);r.fillStyle=CONNECTOR_COLOR;for(var u=0,a=o.length;u<a;u++){if(g+u>=t&&g+u<=b){var v=Math.floor(Math.max(0,(g+u-t)*l));r.fillText(o[u],v+this.left_offset+p,e+9)}}}else{r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+4,h-w,3)}break;case"N":r.fillStyle=CONNECTOR_COLOR;r.fillRect(w+this.left_
offset,e+5,h-w,1);break;case"D":r.fillStyle="red";r.fillRect(w+this.left_offset,e+4,h-w,3);break;case"P":case"I":break}k+=n}},draw_tile:function(ag,o,s,av){var N=o*DENSITY*ag,al=(o+1)*DENSITY*ag,M=al-N;var an=(!this.initial_canvas?"initial":N+"_"+al);var I=this.data_cache.get(an);var e;if(I===undefined||(this.mode!=="Auto"&&I.dataset_type==="summary_tree")){this.data_queue[[N,al]]=true;this.get_data(N,al);return}var a=Math.ceil(M*av),ai=this.prefs.label_color,l=this.prefs.block_color,r=this.mode,z=25,ae=(r==="Squish")||(r==="Dense")&&(r!=="Pack")||(r==="Auto"&&(I.extra_info==="no_detail")),W=this.left_offset,au,D,aw;var q=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(q)}q=$(q);if(I.dataset_type==="summary_tree"){D=this.summary_draw_height}else{if(r==="Dense"){D=z;aw=10}else{aw=(ae?this.vertical_nodetail_px:this.vertical_detail_px);var A=(av<0.0001?1/this.view.zoom_res:av);D=this.incremental_slots(A,I.data,ae,r)*aw+z;au=this.inc
_slots[A]}}q.css({position:"absolute",top:0,left:(N-this.view.low)*av-W});q.get(0).width=a+W;q.get(0).height=D;s.parent().css("height",Math.max(this.height_px,D)+"px");var J=q.get(0).getContext("2d");J.fillStyle=l;J.font=this.default_font;J.textAlign="right";this.container_div.find(".yaxislabel").remove();if(I.dataset_type=="summary_tree"){var Y=I.data,L=I.max,b=Math.ceil(I.delta*av);var p=$("<div />").addClass("yaxislabel").text(L);p.css({position:"absolute",top:"22px",left:"10px"});p.prependTo(this.container_div);for(var ap=0,H=Y.length;ap<H;ap++){var aa=Math.floor((Y[ap][0]-N)*av);var Z=Y[ap][1];if(!Z){continue}var am=Z/L*this.summary_draw_height;J.fillStyle="black";J.fillRect(aa+W,this.summary_draw_height-am,b,am);if(this.prefs.show_counts&&J.measureText(Z).width<b){J.fillStyle="#bbb";J.textAlign="center";J.fillText(Z,aa+W+(b/2),this.summary_draw_height-5)}}e="Summary";s.append(q);return q}if(I.message){q.css({border:"solid red","border-width":"2px 2px 2px 0px"});J.fillS
tyle="red";J.textAlign="left";J.fillText(I.message,100+W,aw)}var ad=false;if(I.data){ad=true;for(var ar=0;ar<this.filters.length;ar++){if(!this.filters[ar].applies_to(I.data[0])){ad=false}}}if(ad){q.addClass(FILTERABLE_CLASS)}var at=I.data;var ao=0;for(var ap=0,H=at.length;ap<H;ap++){var S=at[ap],R=S[0],aq=S[1],ac=S[2],O=S[3];if(au[R]===undefined){continue}var ab=false;var U;for(var ar=0;ar<this.filters.length;ar++){U=this.filters[ar];U.update_attrs(S);if(!U.keep(S)){ab=true;break}}if(ab){continue}if(aq<=al&&ac>=N){var af=Math.floor(Math.max(0,(aq-N)*av)),K=Math.ceil(Math.min(a,Math.max(0,(ac-N)*av))),X=(r==="Dense"?1:(1+au[R]))*aw;var G,aj,P=null,ax=null;if(I.dataset_type==="bai"){var v=S[4];J.fillStyle=l;if(S[5] instanceof Array){var E=Math.floor(Math.max(0,(S[5][0]-N)*av)),Q=Math.ceil(Math.min(a,Math.max(0,(S[5][1]-N)*av))),C=Math.floor(Math.max(0,(S[6][0]-N)*av)),w=Math.ceil(Math.min(a,Math.max(0,(S[6][1]-N)*av)));if(S[5][1]>=N&&S[5][0]<=al){this.rect_or_text(J,av,N,al,S
[5][0],S[5][2],v,X)}if(S[6][1]>=N&&S[6][0]<=al){this.rect_or_text(J,av,N,al,S[6][0],S[6][2],v,X)}if(C>Q){J.fillStyle=CONNECTOR_COLOR;J.fillRect(Q+W,X+5,C-Q,1)}}else{J.fillStyle=l;this.rect_or_text(J,av,N,al,aq,O,v,X)}if(r!=="Dense"&&!ae&&aq>N){J.fillStyle=this.prefs.label_color;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(R,K+2+W,X+8)}else{J.textAlign="right";J.fillText(R,af-2+W,X+8)}J.fillStyle=l}}else{if(I.dataset_type==="interval_index"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var F=S[4],V=S[5],ah=S[6],h=S[7];if(V&&ah){P=Math.floor(Math.max(0,(V-N)*av));ax=Math.ceil(Math.min(a,Math.max(0,(ah-N)*av)))}if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}if(h){if(F){if(F=="+"){J.fillStyle=RIGHT_STRAND}else{if(F=="-"){J.fillStyle=LEFT_STRAND}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}for(var an=0
,g=h.length;an<g;an++){var u=h[an],d=Math.floor(Math.max(0,(u[0]-N)*av)),T=Math.ceil(Math.min(a,Math.max((u[1]-N)*av)));if(d>T){continue}G=5;aj=3;J.fillRect(d+W,X+aj,T-d,G);if(P!==undefined&&!(d>ax||T<P)){G=9;aj=1;var ak=Math.max(d,P),B=Math.min(T,ax);J.fillRect(ak+W,X+aj,B-ak,G)}}}else{G=9;aj=1;J.fillRect(af+W,X+aj,K-af,G);if(S.strand){if(S.strand=="+"){J.fillStyle=RIGHT_STRAND_INV}else{if(S.strand=="-"){J.fillStyle=LEFT_STRAND_INV}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}}}}else{if(I.dataset_type==="vcf"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var t=S[4],n=S[5],c=S[6];G=9;aj=1;J.fillRect(af+W,X,K-af,G);if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}var m=t+" / "+n;if(aq>N&&J.measureText(m).width<(K-af)){J.fillStyle="white";J.textAlign="center";J.fillText(m,W+af+(K-af)/2,X+8);J.fillStyle=l}}}}}ao++}}return q},
gen_options:function(i){var a=$("<div />").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label />").attr("for",e).text("Block color:"),l=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label />").attr("for",j).text("Text color:"),h=$("<input />").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_co
unts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,e,c){FeatureTrack.call(this,d,b,a,e,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
+var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=50,CONNECTOR_COLOR="#ccc",DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",FILTERABLE_CLASS="filterable",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=5,CACHED_DATA=5,DUMMY_CANVAS=document.createElement("canvas"),RIGHT_STRAND,LEFT_STRAND;if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(DUMMY_CANVAS)}CONTEXT=DUMMY_CANVAS.getContext("2d");PX_PER_CHAR=CONTEXT.measureText("A").width;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPa
ttern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src=image_path+"/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function round_1000(a){return Math.round(a*1000)/1000}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var View=function(a,d,c,b){this.container=a;this.vis_id=c;this.dbkey=b;this.ti
tle=d;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide();this.nav_container=$("<div/>").addClass("nav-container").appendTo(c);this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);this.nav=$("<div/>").addClass("nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.nav);this.overview_viewport=$("<div/>").addClas
s("overview-viewport").appendTo(this.overview);this.overview_close=$("<a href='javascript:void(0);'>Close Overview</a>").addClass("overview-close").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div />").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("<form/>").attr("action",function(){}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.wh
ich)!==27){a.go_to($(this).val())}$(this).hide();a.location_span.show();a.chrom_select.show();return false}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keypress focusout",b).appendTo(this.chrom_form);this.location_span=$("<span/>").addClass("location").appendTo(this.chrom_form);this.location_span.bind("click",function(){a.location_span.hide();a.chrom_select.hide();a.nav_input.css("display","inline-block");a.nav_input.select();a.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.chrom_form)}this.zo_link=$("<a/>").click(function(){a.zoom_out();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom-out.png" />').appendTo(this.chrom_form);this.zi_link=$("<a/>").click(function(){a.zoom_in();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom.png" />').appendTo(this.chrom_form);$.ajax({url:chrom_url,data:(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:
this.dbkey}),dataType:"json",success:function(e){if(e.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=e.chrom_info;var h='<option value="">Select Chrom/Contig</option>';for(var g=0,d=a.chrom_data.length;g<d;g++){var f=a.chrom_data[g].chrom;h+='<option value="'+f+'">'+f+"</option>"}a.chrom_select.html(h);a.intro_div.show();a.chrom_select.bind("change",function(){a.change_chrom(a.chrom_select.val())})},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("dblclick",function(d){a.zoom_in(d.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(d){this.current_x=d.offsetX}).bind("drag",function(d){var g=d.offsetX-this.current_x;this.current_x=d.offsetX;var f=Math.round(g/a.viewport_container.width()*(a.max_high-a.max_low));a.move_delta(-f)});this.overview_close.bind("click",function(){for(var e=0,d=a.tracks.length;e<d;e++){a.tracks[e].is_overview=false}$(this).siblings().filter("canvas").remove();
$(this).parent().css("height",a.overview_box.height());a.overview_highlight.hide();$(this).hide()});this.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX;this.enable_pan=(d.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(g){if(!this.enable_pan||this.in_reordering){return}var d=$(this);var i=g.offsetX-this.current_x;var f=d.scrollTop()-(g.clientY-this.current_height);d.scrollTop(f);this.current_height=g.clientY;this.current_x=g.offsetX;var h=Math.round(i/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltrack.bind("dragstart",function(d){this.drag_origin_x=d.clientX;this.drag_origin_pos=d.clientX/a.viewport_container.width()*(a.high-a.low)+a.low;this.drag_div=$("<div />").css({height:a.content_div.height()+30,top:"0px",position:"absolute","background-color":"#cfc",border:"1px solid #6a6",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag
",function(i){var f=Math.min(i.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(i.clientX,this.drag_origin_x)-a.container.offset().left,h=(a.high-a.low),g=a.viewport_container.width();a.update_location(Math.round(f/g*h)+a.low,Math.round(d/g*h)+a.low);this.drag_div.css({left:f+"px",width:(d-f)+"px"})}).bind("dragend",function(j){var f=Math.min(j.clientX,this.drag_origin_x),d=Math.max(j.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),i=a.low;a.low=Math.round(f/g*h)+i;a.high=Math.round(d/g*h)+i;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack));$(window).bind("resize",function(){a.resize_window()});$(document).bind("redraw",function(){a.redraw()});this.reset();$(window).trigger("resize")},update_location:function(a,b){this.location_span.text(commatize(a)+" - "+commatize(b));this.nav_input.val(this.chrom+":"+commatize(a)+"-"+c
ommatize(b))},change_chrom:function(e,b,g){var d=this;var f=$.grep(d.chrom_data,function(j,k){return j.chrom===e})[0];if(f===undefined){return}if(e!==d.chrom){d.chrom=e;if(!d.chrom){d.intro_div.show()}else{d.intro_div.hide()}d.chrom_select.val(d.chrom);d.max_high=f.len;d.reset();d.redraw(true);for(var h=0,a=d.tracks.length;h<a;h++){var c=d.tracks[h];if(c.init){c.init()}}}if(b!==undefined&&g!==undefined){d.low=Math.max(b,0);d.high=Math.min(g,d.max_high)}d.reset_overview();d.redraw()},go_to:function(f){var j=this,a,d,b=f.split(":"),h=b[0],i=b[1];if(i!==undefined){try{var g=i.split("-");a=parseInt(g[0].replace(/,/g,""),10);d=parseInt(g[1].replace(/,/g,""),10)}catch(c){return false}}j.change_chrom(h,a,d)},move_delta:function(c){var a=this;var b=a.high-a.low;if(a.low-c<a.max_low){a.low=a.max_low;a.high=a.max_low+b}else{if(a.high-c>a.max_high){a.high=a.max_high;a.low=a.max_high-b}else{a.high-=c;a.low-=c}}a.redraw()},add_track:function(a){a.view=this;a.track_id=this.track_id_counte
r;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1;this.num_tracks+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)];this.num_tracks-=1},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(h){var g=this.high-this.low,f=this.low,b=this.high;if(f<this.max_low){f=this.max_low}if(b>this.max_high){b=this.max_high}if(this.high!==0&&g<this.min_separation){b=f+this.min_separation}this.low=Math.floor(f);this.high=Math.ceil(b);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));var a=(this.low/(this.max_high-this.max
_low)*this.overview_viewport.width())||0;var e=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var j=13;this.overview_box.css({left:a,width:Math.max(j,e)}).show();if(e<j){this.overview_box.css("left",a-(j-e)/2)}if(this.overview_highlight){this.overview_highlight.css({left:a,width:e})}this.update_location(this.low,this.high);if(!h){for(var c=0,d=this.tracks.length;c<d;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(c=0,d=this.label_tracks.length;c<d;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);th
is.high=Math.round(c+a);this.redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.nav_container.height()-45);this.nav_container.width(this.container.width());this.redraw()},reset_overview:function(){this.overview_viewport.find("canvas").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide()}});var Filter=function(b,a,c){this.name=b;this.index=a;this.value=c};var NumberFilter=function(b,a){this.name=b;this.index=a;this.low=-Number.MAX_VALUE;this.high=Number.MAX_VALUE;this.slider_min=Number.MAX_VALUE;this.slider_max=-Number.MAX_VALUE;this.slider=null;this.slider_label=null};$.extend(NumberFilter.prototype,{applies_to:function(a){if(a.length>this.index){return true}return false},keep:function(a){if(!this.applies_to(a)){return true}return(a[this.index]>=this.low&&a[this.index]<=this.high)},update_attrs:function(b
){var a=false;if(!this.applies_to(b)){return a}if(b[this.index]<this.slider_min){this.slider_min=b[this.index];a=true}if(b[this.index]>this.slider_max){this.slider_max=b[this.index];a=false}return a},update_ui_elt:function(){var b=this.slider.slider("option","min"),a=this.slider.slider("option","max");if(this.slider_min<b||this.slider_max>a){this.slider.slider("option","min",this.slider_min);this.slider.slider("option","max",this.slider_max);this.slider.slider("option","values",[this.slider_min,this.slider_max])}}});var get_filters=function(a){var g=[];for(var d=0;d<a.length;d++){var f=a[d];var c=f.name,e=f.type,b=f.index;if(e=="int"||e=="float"){g[d]=new NumberFilter(c,b)}else{g[d]=new Filter(c,b,e)}}return g};var Track=function(b,a,d,c){this.name=b;this.view=a;this.parent_element=d;this.filters=(c!==undefined?get_filters(c):[]);this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track").css("position","relative");i
f(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);if(this.view.editor){this.drag_div=$("<div class='draghandle' />").appendTo(this.header_div)}this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name);this.name_div.attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase())}this.filtering_div=$("<div class='track-filters'>").appendTo(this.container_div);this.filtering_div.hide();this.filtering_div.bind("drag",function(i){i.stopPropagation()});var b=$("<table class='filters'>").appendTo(this.filtering_div);var c=this;for(var e=0;e<this.filters.length;e++){var a=this.filters[e];var f=$("<tr>").appendTo(b);var g=$("<th class='filter-info'>").appendTo(f);var j=$("<span class='name'>").appendTo(g);j.text(a.name+" ");var d=$("<span class='values'>").appendTo(g);var h=$("<td>").appendTo(f);a.control_element=$("<div id='"+a.name+"-filter-control' style='width: 200
px; position: relative'>").appendTo(h);a.control_element.slider({range:true,min:Number.MAX_VALUE,max:-Number.MIN_VALUE,values:[0,0],slide:function(k,l){var i=l.values;d.text("["+i[0]+"-"+i[1]+"]");a.low=i[0];a.high=i[1];c.draw(true)},change:function(i,k){a.control_element.slider("option","slide").call(a.control_element,i,k)}});a.slider=a.control_element;a.slider_label=d}this.content_div=$("<div class='track-content'>").appendTo(this.container_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.initial_canvas=undefined;a.content_div.css("height","auto");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"||d.kind==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR);if(d.message){var f=a.view.tracks.indexOf(a);var e=$("
<a href='javascript:void(0);'></a>").attr("id",f+"_error");e.text("Click to view error");$("#"+f+"_error").live("click",function(){show_modal("Trackster Error","<pre>"+d.message+"</pre>",{Close:hide_modal})});a.content_div.append(e)}}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.data!==undefined&&(d.data===null||d.data.length===0)){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){var b=this,j=b.view;if(b.hidden){return}if(b.display_modes!==undefined){if(b.mode_div===undefined){b.mode_div=$("<div class='right-float menubutton popup' />").appendTo(b.header_div);var e=
b.display_modes[0];b.mode=e;b.mode_div.text(e);var c=function(i){b.mode_div.text(i);b.mode=i;b.tile_cache.clear();b.draw()};var a={};for(var f=0,h=b.display_modes.length;f<h;f++){var g=b.display_modes[f];a[g]=function(i){return function(){c(i)}}(g)}make_popupmenu(b.mode_div,a)}else{b.mode_div.hide()}}var d={};d["Set as overview"]=function(){j.overview_viewport.find("canvas").remove();b.is_overview=true;b.set_overview();for(var i in j.tracks){if(j.tracks[i]!==b){j.tracks[i].is_overview=false}}};d["Edit configuration"]=function(){var l=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},i=function(){b.update_options(b.track_id);hide_modal();$(window).unbind("keypress.check_enter_esc")},k=function(m){if((m.keyCode||m.which)===27){l()}else{if((m.keyCode||m.which)===13){i()}}};$(window).bind("keypress.check_enter_esc",k);show_modal("Configure Track",b.gen_options(b.track_id),{Cancel:l,OK:i})};if(b.filters.length>0){d["Show filters"]=function(){var i;if(!b.filter
ing_div.is(":visible")){i="Hide filters";b.filters_visible=true}else{i="Show filters";b.filters_visible=false}$("#"+b.name_div.attr("id")+"-menu").find("li").eq(2).text(i);b.filtering_div.toggle()}}d.Remove=function(){j.remove_track(b);if(j.num_tracks===0){$("#no-tracks").show()}};b.popup_menu=make_popupmenu(b.name_div,d);show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters",false)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(a){var k=this.view.low,g=this.view.high,h=g-k,f=this.view.resolution;var n=$("<div style='position: relative;'></div>"),o=this.content_div.width()/h;this.content_div.append(n);this.max_height=0;var b=Math.floor(k/f/DENSITY);var j={};while((b*DENSITY*f)<g){var l=this.content_div.width()+"_"+o+"_"+b;var e=this.tile_cache.get(l);if(!a&&e){var i=b*DENSITY*f;var d=(i-k)*o;if(this.left_offset){d-=this.left_offset}e.css({left:d});this.show_tile(e,n)}else{this.delayed_draw(this,l,k,g,b,f,n,o,j)}b+=1}var c=this;var m=setInterval(functio
n(){if(obj_length(j)===0){if(c.content_div.children().length>1){c.content_div.children(":first").remove()}for(var p=0;p<c.filters.length;p++){c.filters[p].update_ui_elt()}clearInterval(m)}},50)},delayed_draw:function(c,h,g,e,b,d,i,j,f){var a=setTimeout(function(){if(g<=c.view.high&&e>=c.view.low){var k=c.draw_tile(d,b,i,j);if(k){if(!c.initial_canvas&&!window.G_vmlCanvasManager){c.initial_canvas=$(k).clone();var n=k.get(0).getContext("2d");var l=c.initial_canvas.get(0).getContext("2d");var m=n.getImageData(0,0,n.canvas.width,n.canvas.height);l.putImageData(m,0,0);c.set_overview()}c.tile_cache.set(h,k);c.show_tile(k,i)}}delete f[a]},50);f[a]=true},show_tile:function(a,c){var b=this;c.append(a);b.max_height=Math.max(b.max_height,a.height());b.content_div.css("height",b.max_height+"px");if(a.hasClass(FILTERABLE_CLASS)){show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters");if(b.filters_visible){b.filtering_div.show()}}else{show_hide_popupmenu_options(b.popup_menu,"(Show
|Hide) filters",false);b.filtering_div.hide()}},set_overview:function(){var a=this.view;if(this.initial_canvas&&this.is_overview){a.overview_close.show();a.overview_viewport.append(this.initial_canvas);a.overview_highlight.show().height(this.initial_canvas.height());a.overview_viewport.height(this.initial_canvas.height()+a.overview_box.height())}$(window).trigger("resize")}});var LabelTrack=function(a,b){this.track_type="LabelTrack";this.hidden=true;Track.call(this,null,a,b);this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_di
v.append(b)}});var ReferenceTrack=function(a){this.track_type="ReferenceTrack";this.hidden=true;Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.left_offset=200;this.height_px=12;this.container_div.addClass("reference-track");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,j=f+"_"+b;var e=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(e)}e=$(e);var n=e.get(0).getContext("2d");if(o>PX_PER_CHAR){if(this.dat
a_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o),i=Math.round(o/2);n.fillText(m[h],a+this.left_offset+i,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=80;this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={color:"black",min_value:undefined,max_value:undefined,mode:this.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.ver
tical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");var e=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=e.min;a.prefs.max_value=e.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=e.total_frequency;a.container_div.find(".yaxislabel").remove();var f=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(round_1000(a.prefs.min_value));var d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(round_1000(a.prefs.max_value));d.css({position:"absolute",top:"22px",left:"10px"});d.prependTo(a.container_div);f.css({position:"absolute",top:a.height_px+11+"px",left:"10px"});f.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=
(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){var h=g.data;c.data_cache.set(e,h);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*o,a=DENSITY*o,w=o+"_"+r;var b=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(b)}b=$(b);if(this.data_cache.get(w)===undefined){this.get_data(o,r);return}var v=this.data_cache.get(w);if(!v){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),j=false,k=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,t=this.total_frequency,d=this.height_px,l=this.mode;n.beginPath();n.fillStyle=this.prefs.co
lor;var u,h,f;if(v.length>1){f=Math.ceil((v[1][0]-v[0][0])*e)}else{f=10}for(var p=0,q=v.length;p<q;p++){u=Math.round((v[p][0]-s)*e);h=v[p][1];if(h===null){if(j&&l==="Filled"){n.lineTo(u,d)}j=false;continue}if(h<k){h=k}else{if(h>g){h=g}}if(l==="Histogram"){h=Math.round(d-(h-k)/m*d);n.fillRect(u,h,f,d-h)}else{if(l==="Intensity"){h=255-Math.floor((h-k)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(u,0,f,d)}else{h=Math.round(d-(h-k)/m*d);if(j){n.lineTo(u,h)}else{j=true;if(l==="Filled"){n.moveTo(u,d);n.lineTo(u,h)}else{n.moveTo(u,h)}}}}}if(l==="Filled"){if(j){n.lineTo(u,d)}n.fill()}else{n.stroke()}c.append(b);return b},gen_options:function(m){var a=$("<div />").addClass("form-row");var e="track_"+m+"_color",b=$("<label />").attr("for",e).text("Color:"),c=$("<input />").attr("id",e).attr("name",e).val(this.prefs.color),h="track_"+m+"_minval",l=$("<label></label>").attr("for",h).text("Min value:"),d=(this.prefs.min_value===undefined?"":this.prefs.min_value),k=$("<input></i
nput>").attr("id",h).val(d),j="track_"+m+"_maxval",g=$("<label></label>").attr("for",j).text("Max value:"),i=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",j).val(i);return a.append(l).append(k).append(g).append(f).append(b).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_color").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!==this.prefs.color){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.color=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,e,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container,e);TiledTrack.call(this);this.height_p
x=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=30;this.default_font="9px Monaco, Lucida Console, monospace";this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.left_offset=200;this.prefs={block_color:"#444",label_color:"black",show_counts:true}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="initial";this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution,mode:a.mode},function(c){a.mode_div.show();a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.vie
w.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,b,q){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=q;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,y=[],h=0,n=this.view.max_low;var A=[];if(this.inc_slots[a].mode!==q){delete this.inc_slots[a];this.inc_slots[a]={mode:q,w_scale:m};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var v=0,w=g.length;v<w;v++){var f=g[v],l=f[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);A.push(this.inc_slots[a][l])}else{y.push(v)}}for(var v=0,w=y.length;v<w;v++){var f=g[y[v]],l=f[0],r=f[1],c=f[2],p=f[3],d=Math.floor((r-n)*m),e=Math.ceil((c-n)*m);if(p!==undefined&&!b){var s=CONTEXT.measureText(p).width;if(d-s<0){e+=s}else{d-=s}}var u=0;while(u<=MAX_FEATURE_DEPTH){var o=true;if(this.s_e_by_tile[a][u]!==undefined){for(var t=0,z=this.s_e_by_tile[a][u].length;t<z;t++){var x=this.s_e_
by_tile[a][u][t];if(e>x[0]&&d<x[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][u]===undefined){this.s_e_by_tile[a][u]=[]}this.s_e_by_tile[a][u].push([d,e]);this.inc_slots[a][l]=u;h=Math.max(h,u);break}u++}}return h},rect_or_text:function(r,l,t,b,q,f,i,e){r.textAlign="center";var k=0,p=Math.round(l/2);for(var m=0,s=i.length;m<s;m++){var j=i[m],d="MIDNSHP"[j[0]],n=j[1];if(d==="H"||d==="S"){k-=n}var g=q+k,w=Math.floor(Math.max(0,(g-t)*l)),h=Math.floor(Math.max(0,(g+n-t)*l));switch(d){case"S":case"H":case"M":var o=f.slice(k,n);if((this.mode==="Pack"||this.mode==="Auto")&&f!==undefined&&l>PX_PER_CHAR){r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+1,h-w,9);r.fillStyle=CONNECTOR_COLOR;for(var u=0,a=o.length;u<a;u++){if(g+u>=t&&g+u<=b){var v=Math.floor(Math.max(0,(g+u-t)*l));r.fillText(o[u],v+this.left_offset+p,e+9)}}}else{r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+4,h-w,3)}break;case"N":r.fillStyle=CONNECTOR_COLOR;r.fillRect(w+this.lef
t_offset,e+5,h-w,1);break;case"D":r.fillStyle="red";r.fillRect(w+this.left_offset,e+4,h-w,3);break;case"P":case"I":break}k+=n}},draw_tile:function(ag,o,s,av){var N=o*DENSITY*ag,al=(o+1)*DENSITY*ag,M=al-N;var an=(!this.initial_canvas?"initial":N+"_"+al);var I=this.data_cache.get(an);var e;if(I===undefined||(this.mode!=="Auto"&&I.dataset_type==="summary_tree")){this.data_queue[[N,al]]=true;this.get_data(N,al);return}var a=Math.ceil(M*av),ai=this.prefs.label_color,l=this.prefs.block_color,r=this.mode,z=25,ae=(r==="Squish")||(r==="Dense")&&(r!=="Pack")||(r==="Auto"&&(I.extra_info==="no_detail")),W=this.left_offset,au,D,aw;var q=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(q)}q=$(q);if(I.dataset_type==="summary_tree"){D=this.summary_draw_height}else{if(r==="Dense"){D=z;aw=10}else{aw=(ae?this.vertical_nodetail_px:this.vertical_detail_px);var A=(av<0.0001?1/this.view.zoom_res:av);D=this.incremental_slots(A,I.data,ae,r)*aw+z;au=this.i
nc_slots[A]}}q.css({position:"absolute",top:0,left:(N-this.view.low)*av-W});q.get(0).width=a+W;q.get(0).height=D;s.parent().css("height",Math.max(this.height_px,D)+"px");var J=q.get(0).getContext("2d");J.fillStyle=l;J.font=this.default_font;J.textAlign="right";this.container_div.find(".yaxislabel").remove();if(I.dataset_type=="summary_tree"){var Y=I.data,L=I.max,b=Math.ceil(I.delta*av);var p=$("<div />").addClass("yaxislabel").text(L);p.css({position:"absolute",top:"22px",left:"10px"});p.prependTo(this.container_div);for(var ap=0,H=Y.length;ap<H;ap++){var aa=Math.floor((Y[ap][0]-N)*av);var Z=Y[ap][1];if(!Z){continue}var am=Z/L*this.summary_draw_height;J.fillStyle="black";J.fillRect(aa+W,this.summary_draw_height-am,b,am);if(this.prefs.show_counts&&J.measureText(Z).width<b){J.fillStyle="#bbb";J.textAlign="center";J.fillText(Z,aa+W+(b/2),this.summary_draw_height-5)}}e="Summary";s.append(q);return q}if(I.message){q.css({border:"solid red","border-width":"2px 2px 2px 0px"});J.fil
lStyle="red";J.textAlign="left";J.fillText(I.message,100+W,aw)}var ad=false;if(I.data){ad=true;for(var ar=0;ar<this.filters.length;ar++){if(!this.filters[ar].applies_to(I.data[0])){ad=false}}}if(ad){q.addClass(FILTERABLE_CLASS)}var at=I.data;var ao=0;for(var ap=0,H=at.length;ap<H;ap++){var S=at[ap],R=S[0],aq=S[1],ac=S[2],O=S[3];if(au[R]===undefined){continue}var ab=false;var U;for(var ar=0;ar<this.filters.length;ar++){U=this.filters[ar];U.update_attrs(S);if(!U.keep(S)){ab=true;break}}if(ab){continue}if(aq<=al&&ac>=N){var af=Math.floor(Math.max(0,(aq-N)*av)),K=Math.ceil(Math.min(a,Math.max(0,(ac-N)*av))),X=(r==="Dense"?1:(1+au[R]))*aw;var G,aj,P=null,ax=null;if(I.dataset_type==="bai"){var v=S[4];J.fillStyle=l;if(S[5] instanceof Array){var E=Math.floor(Math.max(0,(S[5][0]-N)*av)),Q=Math.ceil(Math.min(a,Math.max(0,(S[5][1]-N)*av))),C=Math.floor(Math.max(0,(S[6][0]-N)*av)),w=Math.ceil(Math.min(a,Math.max(0,(S[6][1]-N)*av)));if(S[5][1]>=N&&S[5][0]<=al){this.rect_or_text(J,av,N,al
,S[5][0],S[5][2],v,X)}if(S[6][1]>=N&&S[6][0]<=al){this.rect_or_text(J,av,N,al,S[6][0],S[6][2],v,X)}if(C>Q){J.fillStyle=CONNECTOR_COLOR;J.fillRect(Q+W,X+5,C-Q,1)}}else{J.fillStyle=l;this.rect_or_text(J,av,N,al,aq,O,v,X)}if(r!=="Dense"&&!ae&&aq>N){J.fillStyle=this.prefs.label_color;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(R,K+2+W,X+8)}else{J.textAlign="right";J.fillText(R,af-2+W,X+8)}J.fillStyle=l}}else{if(I.dataset_type==="interval_index"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var F=S[4],V=S[5],ah=S[6],h=S[7];if(V&&ah){P=Math.floor(Math.max(0,(V-N)*av));ax=Math.ceil(Math.min(a,Math.max(0,(ah-N)*av)))}if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}if(h){if(F){if(F=="+"){J.fillStyle=RIGHT_STRAND}else{if(F=="-"){J.fillStyle=LEFT_STRAND}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}for(var an
=0,g=h.length;an<g;an++){var u=h[an],d=Math.floor(Math.max(0,(u[0]-N)*av)),T=Math.ceil(Math.min(a,Math.max((u[1]-N)*av)));if(d>T){continue}G=5;aj=3;J.fillRect(d+W,X+aj,T-d,G);if(P!==undefined&&!(d>ax||T<P)){G=9;aj=1;var ak=Math.max(d,P),B=Math.min(T,ax);J.fillRect(ak+W,X+aj,B-ak,G)}}}else{G=9;aj=1;J.fillRect(af+W,X+aj,K-af,G);if(S.strand){if(S.strand=="+"){J.fillStyle=RIGHT_STRAND_INV}else{if(S.strand=="-"){J.fillStyle=LEFT_STRAND_INV}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}}}}else{if(I.dataset_type==="vcf"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var t=S[4],n=S[5],c=S[6];G=9;aj=1;J.fillRect(af+W,X,K-af,G);if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}var m=t+" / "+n;if(aq>N&&J.measureText(m).width<(K-af)){J.fillStyle="white";J.textAlign="center";J.fillText(m,W+af+(K-af)/2,X+8);J.fillStyle=l}}}}}ao++}}return q
},gen_options:function(i){var a=$("<div />").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label />").attr("for",e).text("Block color:"),l=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label />").attr("for",j).text("Text color:"),h=$("<input />").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_
counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,e,c){FeatureTrack.call(this,d,b,a,e,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
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 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 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 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
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
galaxy-dist commit 2c934a168af9: More tweaking of lines_diff for cuffdiff test.
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 jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1288633591 14400
# Node ID 2c934a168af968dc84300412220c2f2cb74bf676
# Parent 9415f380f2e7d4aca44f74923b6984ab3bcc1dfb
More tweaking of lines_diff for cuffdiff test.
--- a/tools/ngs_rna/cuffdiff_wrapper.xml
+++ b/tools/ngs_rna/cuffdiff_wrapper.xml
@@ -78,11 +78,11 @@
TODO: can we find datasets that lead to deterministic behavior?
--><output name="isoforms_exp" file="cuffdiff_out1.txt" lines_diff="14"/>
- <output name="genes_exp" file="cuffdiff_out2.txt" lines_diff="4"/>
+ <output name="genes_exp" file="cuffdiff_out2.txt" lines_diff="10"/><output name="tss_groups_exp" file="cuffdiff_out3.txt"/><output name="cds_exp_fpkm_tracking" file="cuffdiff_out4.txt"/>
- <output name="isoforms_fpkm_tracking" file="cuffdiff_out5.txt" lines_diff="8"/>
- <output name="genes_fpkm_tracking" file="cuffdiff_out6.txt" lines_diff="4"/>
+ <output name="isoforms_fpkm_tracking" file="cuffdiff_out5.txt" lines_diff="16"/>
+ <output name="genes_fpkm_tracking" file="cuffdiff_out6.txt" lines_diff="8"/><output name="tss_groups_fpkm_tracking" file="cuffdiff_out7.txt"/><output name="cds_fpkm_tracking" file="cuffdiff_out8.txt"/><output name="splicing_diff" file="cuffdiff_out9.txt"/>
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