galaxy-commits
Threads by month
- ----- 2026 -----
- 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 af2d04e35680: Radio boxes in tool forms now have clickable labels as well. Update unit tests to reflect changes
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 1288137500 14400
# Node ID af2d04e3568078d8d7ad94e229a17983f8282e20
# Parent 6a1ea9b6029416945caba087f2696c64a9a06621
Radio boxes in tool forms now have clickable labels as well. Update unit tests to reflect changes
--- a/lib/galaxy/web/form_builder.py
+++ b/lib/galaxy/web/form_builder.py
@@ -184,16 +184,16 @@ class SelectField(BaseField):
>>> t.add_option( "tuti", 1 )
>>> t.add_option( "fruity", "x" )
>>> print t.get_html()
- <div><input type="radio" name="foo" value="1">tuti</div>
- <div><input type="radio" name="foo" value="x">fruity</div>
+ <div><input type="radio" name="foo" value="1" id="foo|1"><label for="foo|1">tuti</label></div>
+ <div><input type="radio" name="foo" value="x" id="foo|x"><label for="foo|x">fruity</label></div>
>>> t = SelectField( "bar", multiple=True, display="checkboxes" )
>>> t.add_option( "automatic", 3 )
>>> t.add_option( "bazooty", 4, selected=True )
>>> print t.get_html()
<div class="checkUncheckAllPlaceholder" checkbox_name="bar"></div>
- <div><input type="checkbox" name="bar" value="3">automatic</div>
- <div><input type="checkbox" name="bar" value="4" checked>bazooty</div>
+ <div><input type="checkbox" name="bar" value="3" id="bar|3"><label for="bar|3">automatic</label></div>
+ <div><input type="checkbox" name="bar" value="4" id="bar|4" checked='checked'><label for="bar|4">bazooty</label></div>
"""
def __init__( self, name, multiple=None, display=None, refresh_on_change=False, refresh_on_change_values=[], size=None ):
self.name = name
@@ -232,13 +232,14 @@ class SelectField(BaseField):
for text, value, selected in self.options:
style = ""
escaped_value = escape( str( value ), quote=True )
+ uniq_id = "%s%s|%s" % (prefix, self.name, escaped_value)
if len(self.options) > 2 and ctr % 2 == 1:
style = " class=\"odd_row\""
selected_text = ""
if selected:
- selected_text = "checked='checked'"
- rval.append( '<div%s><input type="checkbox" name="%s%s" value="%s" id="%s" %s%s><label for="%s">%s</label></div>' % \
- ( style, prefix, self.name, escaped_value, escaped_value, selected_text, self.get_disabled_str( disabled ), escaped_value, text ) )
+ selected_text = " checked='checked'"
+ rval.append( '<div%s><input type="checkbox" name="%s%s" value="%s" id="%s"%s%s><label for="%s">%s</label></div>' % \
+ ( style, prefix, self.name, escaped_value, uniq_id, selected_text, self.get_disabled_str( disabled ), uniq_id, text ) )
ctr += 1
return "\n".join( rval )
def get_html_radio( self, prefix="", disabled=False ):
@@ -246,18 +247,23 @@ class SelectField(BaseField):
ctr = 0
for text, value, selected in self.options:
style = ""
+ escaped_value = escape( str( value ), quote=True )
+ uniq_id = "%s%s|%s" % (prefix, self.name, escaped_value)
if len(self.options) > 2 and ctr % 2 == 1:
style = " class=\"odd_row\""
- if selected: selected_text = " checked"
- else: selected_text = ""
- rval.append( '<div%s><input type="radio" name="%s%s"%s value="%s"%s%s>%s</div>' % \
+ selected_text = ""
+ if selected:
+ selected_text = " checked='checked'"
+ rval.append( '<div%s><input type="radio" name="%s%s"%s value="%s" id="%s"%s%s><label for="%s">%s</label></div>' % \
( style,
prefix,
self.name,
self.refresh_on_change_text,
- escape( str( value ), quote=True ),
+ escaped_value,
+ uniq_id,
selected_text,
self.get_disabled_str( disabled ),
+ uniq_id,
text ) )
ctr += 1
return "\n".join( rval )
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -474,14 +474,14 @@ class SelectToolParameter( ToolParameter
blah
>>> print p.get_html()
<div class="checkUncheckAllPlaceholder" checkbox_name="blah"></div>
- <div><input type="checkbox" name="blah" value="x">I am X</div>
- <div class="odd_row"><input type="checkbox" name="blah" value="y" checked>I am Y</div>
- <div><input type="checkbox" name="blah" value="z" checked>I am Z</div>
+ <div><input type="checkbox" name="blah" value="x" id="blah|x"><label for="blah|x">I am X</label></div>
+ <div class="odd_row"><input type="checkbox" name="blah" value="y" id="blah|y" checked='checked'><label for="blah|y">I am Y</label></div>
+ <div><input type="checkbox" name="blah" value="z" id="blah|z" checked='checked'><label for="blah|z">I am Z</label></div>
>>> print p.get_html( value=["x","y"])
<div class="checkUncheckAllPlaceholder" checkbox_name="blah"></div>
- <div><input type="checkbox" name="blah" value="x" checked>I am X</div>
- <div class="odd_row"><input type="checkbox" name="blah" value="y" checked>I am Y</div>
- <div><input type="checkbox" name="blah" value="z">I am Z</div>
+ <div><input type="checkbox" name="blah" value="x" id="blah|x" checked='checked'><label for="blah|x">I am X</label></div>
+ <div class="odd_row"><input type="checkbox" name="blah" value="y" id="blah|y" checked='checked'><label for="blah|y">I am Y</label></div>
+ <div><input type="checkbox" name="blah" value="z" id="blah|z"><label for="blah|z">I am Z</label></div>
>>> print p.to_param_dict_string( ["y", "z"] )
y,z
"""
1
0
galaxy-dist commit 49c7938d5827: sample tracking web api cleaned up
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 rc
# Date 1288122647 14400
# Node ID 49c7938d582799125a89f0d6ba38be0ddc6f4b4f
# Parent d73fa5f14b013333b2e49bfbf775db8dd0197e2b
sample tracking web api cleaned up
- added a samples controller
--- /dev/null
+++ b/lib/galaxy/web/api/samples.py
@@ -0,0 +1,44 @@
+"""
+API operations on a sample tracking system.
+"""
+import logging, os, string, shutil, urllib, re, socket
+from cgi import escape, FieldStorage
+from galaxy import util, datatypes, jobs, web, util
+from galaxy.web.base.controller import *
+from galaxy.util.sanitize_html import sanitize_html
+from galaxy.model.orm import *
+from galaxy.util.bunch import Bunch
+
+log = logging.getLogger( __name__ )
+
+class SamplesController( BaseController ):
+
+ @web.expose_api
+ def index( self, trans, **kwd ):
+ """
+ GET /api/requests/{encoded_request_id}/samples
+ Displays a collection (list) of sample of a sequencing request.
+ """
+ try:
+ request_id = trans.security.decode_id( kwd[ 'request_id' ] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed request id ( %s ) specified, unable to decode." % str( encoded_request_id )
+ try:
+ request = trans.sa_session.query( trans.app.model.Request ).get( request_id )
+ except:
+ request = None
+ if not request or not ( trans.user_is_admin() or request.user.id == trans.user.id ):
+ trans.response.status = 400
+ return "Invalid request id ( %s ) specified." % str( request_id )
+ rval = []
+ for sample in request.samples:
+ item = sample.get_api_value()
+ item['url'] = url_for( 'samples',
+ request_id=trans.security.encode_id( request_id ),
+ id=trans.security.encode_id( sample.id ) )
+ item['id'] = trans.security.encode_id( item['id'] )
+ rval.append( item )
+ return rval
+
+
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1767,6 +1767,7 @@ class RequestTypePermissions( object ):
class Sample( object ):
bulk_operations = Bunch( CHANGE_STATE = 'Change state',
SELECT_LIBRARY = 'Select data library and folder' )
+ api_collection_visible_keys = ( 'id', 'name' )
def __init__(self, name=None, desc=None, request=None, form_values=None, bar_code=None, library=None, folder=None):
self.name = name
self.desc = desc
@@ -1818,6 +1819,18 @@ class Sample( object ):
pexpect.TIMEOUT:print_ticks},
timeout=10)
return output.replace(filepath, '').strip()
+ def get_api_value( self, view='collection' ):
+ rval = {}
+ try:
+ visible_keys = self.__getattribute__( 'api_' + view + '_visible_keys' )
+ except AttributeError:
+ raise Exception( 'Unknown API view: %s' % view )
+ for key in visible_keys:
+ try:
+ rval[key] = self.__getattribute__( key )
+ except AttributeError:
+ rval[key] = None
+ return rval
class SampleState( object ):
def __init__(self, name=None, desc=None, request_type=None):
--- a/lib/galaxy/web/buildapp.py
+++ b/lib/galaxy/web/buildapp.py
@@ -104,8 +104,15 @@ def app_factory( global_conf, **kwargs )
# If enabled, add the web API
if asbool( kwargs.get( 'enable_api', False ) ):
add_api_controllers( webapp, app )
- webapp.api_mapper.resource( 'content', 'contents', path_prefix='/api/libraries/:library_id', parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.api_mapper.resource( 'content',
+ 'contents',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.api_mapper.resource( 'library', 'libraries', path_prefix='/api' )
+ webapp.api_mapper.resource( 'sample',
+ 'samples',
+ path_prefix='/api/requests/:request_id',
+ parent_resources=dict( member_name='request', collection_name='requests' ) )
webapp.api_mapper.resource( 'request', 'requests', path_prefix='/api' )
webapp.finalize_config()
# Wrap the webapp in some useful middleware
--- a/lib/galaxy/web/api/requests.py
+++ b/lib/galaxy/web/api/requests.py
@@ -22,23 +22,57 @@ class RequestsController( BaseController
GET /api/requests
Displays a collection (list) of sequencing requests.
"""
- query = trans.sa_session.query( trans.app.model.Request )\
- .filter( and_( trans.app.model.Request.table.c.user_id == trans.user.id \
- and trans.app.model.Request.table.c.deleted == False ) ) \
- .all()
+ # if admin user then return all requests
+ if trans.user_is_admin():
+ query = trans.sa_session.query( trans.app.model.Request )\
+ .filter( trans.app.model.Request.table.c.deleted == False )\
+ .all()
+ else:
+ query = trans.sa_session.query( trans.app.model.Request )\
+ .filter( and_( trans.app.model.Request.table.c.user_id == trans.user.id \
+ and trans.app.model.Request.table.c.deleted == False ) ) \
+ .all()
rval = []
for request in query:
item = request.get_api_value()
item['url'] = url_for( 'requests', id=trans.security.encode_id( request.id ) )
item['id'] = trans.security.encode_id( item['id'] )
+ if trans.user_is_admin():
+ item['user'] = request.user.email
rval.append( item )
return rval
-
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/requests/{encoded_request_id}
+ Displays details of a sequencing request.
+ """
+ try:
+ request_id = trans.security.decode_id( id )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed %s id ( %s ) specified, unable to decode." % ( update_type, str( id ) )
+ try:
+ request = trans.sa_session.query( trans.app.model.Request ).get( request_id )
+ except:
+ request = None
+ if not request or not ( trans.user_is_admin() or request.user.id == trans.user.id ):
+ trans.response.status = 400
+ return "Invalid request id ( %s ) specified." % str( request_id )
+ item = request.get_api_value()
+ item['url'] = url_for( 'requests', id=trans.security.encode_id( request.id ) )
+ item['id'] = trans.security.encode_id( item['id'] )
+ item['user'] = request.user.email
+ item['num_of_samples'] = len(request.samples)
+ return item
+
@web.expose_api
def update( self, trans, id, key, payload, **kwd ):
"""
PUT /api/requests/{encoded_request_id}
- Displays information about a sequencing request.
+ Updates a request state, sample state or sample dataset transfer status
+ depending on the update_type
"""
params = util.Params( kwd )
update_type = None
@@ -124,7 +158,3 @@ class RequestsController( BaseController
new_status=new_status,
error_msg=error_msg )
return status, output
-
-
-
-
1
0
galaxy-dist commit e5f90080a2db: Merge with 29157e97e95453c5f9022d9c571527a6de4032e1
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 rc
# Date 1288149696 14400
# Node ID e5f90080a2db9c16bad2300c5ef0b5c49a558638
# Parent 61c786bd2efb6f9cc4d5fa4d43dfb056ed99b909
# Parent 29157e97e95453c5f9022d9c571527a6de4032e1
Merge with 29157e97e95453c5f9022d9c571527a6de4032e1
1
0
galaxy-dist commit 29157e97e954: Added setup script to configure RabbitMQ
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 rc
# Date 1288148662 14400
# Node ID 29157e97e95453c5f9022d9c571527a6de4032e1
# Parent af2d04e3568078d8d7ad94e229a17983f8282e20
Added setup script to configure RabbitMQ
--- a/transfer_datasets.ini.sample
+++ b/transfer_datasets.ini.sample
@@ -3,13 +3,8 @@
[data_transfer_user_login_info]
#email = datatx(a)bx.psu.edu
#password = datatx
-
-# The following section contains 4 config variables neede for the data transfer.
-# Please copy the exact values for each of these variables from the universe_wsgi.ini
-# configuration file for this installation
+#api_key = Generate it from the User menu in Galaxy
[universe_wsgi_config]
-#host = 127.0.0.1
-#port =
-#database_connection =
-#library_import_dir =
+# The http server section name in the Galaxy config file (universe_wsgi.ini)
+#http_server_section = server:main
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -420,4 +420,4 @@ upload1 = local:///
#queue = galaxy_queue
#exchange = galaxy_exchange
#routing_key = bar_code_scanner
-
+#rabbitmqctl_path = /path/to/rabbitmqctl
--- /dev/null
+++ b/scripts/galaxy_messaging/server/setup_rabbitmq.py
@@ -0,0 +1,34 @@
+#
+# Configuration script for RabbitMQ in Galaxy
+#
+# Requirements:
+# - set the rabbitmqctl_path variable in the 'galaxy_amqp' section in Galaxy config file
+#
+# Usage:
+# $ python setup_rabbitmq.py <Galaxy config file>
+#
+
+import os, sys, csv, ConfigParser
+
+def main( config_file ):
+ config = ConfigParser.ConfigParser()
+ config.read( config_file )
+ rabbitmqctl_path = config.get( 'galaxy_amqp', 'rabbitmqctl_path' )
+ username = config.get( 'galaxy_amqp', 'userid' )
+ password = config.get( 'galaxy_amqp', 'password' )
+ virtual_host = config.get( 'galaxy_amqp', 'virtual_host' )
+
+ cmd_list = [
+ 'add_user %s %s' % ( username, password ),
+ 'add_vhost %s' % config.get( 'galaxy_amqp', 'virtual_host' ),
+ 'set_permissions -p %s %s "%s.*" ".*" ".*"' % ( virtual_host, username, username )
+ ]
+
+ for cmd in cmd_list:
+ retval = os.system( rabbitmqctl_path + ' ' + cmd )
+ if retval:
+ print "Failed command: %s" % cmd
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main( sys.argv[1] )
1
0
galaxy-dist commit c22562b86d0f: Make the production buildbot use Main's tool conf
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 1288123416 14400
# Node ID c22562b86d0f62d361fd2531d22d42cfd6bf07c1
# Parent 49c7938d582799125a89f0d6ba38be0ddc6f4b4f
Make the production buildbot use Main's tool conf
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -54,6 +54,7 @@ def main():
galaxy_test_file_dir = os.path.join( os.getcwd(), galaxy_test_file_dir )
start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ
tool_path = os.environ.get( 'GALAXY_TEST_TOOL_PATH', 'tools' )
+ tool_config_file = os.environ.get( 'GALAXY_TEST_TOOL_CONF', 'tool_conf.xml.sample' )
if start_server:
psu_production = False
galaxy_test_proxy_port = None
@@ -75,6 +76,7 @@ def main():
nginx_upload_store = os.environ.get( 'GALAXY_TEST_NGINX_UPLOAD_STORE', None )
if not nginx_upload_store:
raise Exception( 'Please set GALAXY_TEST_NGINX_UPLOAD_STORE to the path where the nginx upload module places uploaded files' )
+ tool_config_file = 'tool_conf.xml.main'
default_cluster_job_runner = os.environ.get( 'GALAXY_TEST_DEFAULT_CLUSTER_JOB_RUNNER', 'pbs:///' )
file_path = tempfile.mkdtemp( dir=base_file_path )
new_file_path = tempfile.mkdtemp( dir=base_new_file_path )
@@ -142,7 +144,7 @@ def main():
file_path = file_path,
new_file_path = new_file_path,
tool_path = tool_path,
- tool_config_file = "tool_conf.xml.sample",
+ tool_config_file = tool_config_file,
datatype_converters_config_file = "datatype_converters_conf.xml.sample",
tool_parse_help = False,
test_conf = "test.conf",
1
0
galaxy-dist commit 314f8a5d3823: Complete functional test for cuffcompare.
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 1288119881 14400
# Node ID 314f8a5d38237c3f1fafccb522ee45aebfa8464f
# Parent 379d0701f198964fb964c3834cce7414cbc8f888
Complete functional test for cuffcompare.
--- /dev/null
+++ b/test-data/cuffcompare_out1.tmap
@@ -0,0 +1,51 @@
+ref_gene_id ref_id class_code cuff_gene_id cuff_id FMI FPKM FPKM_conf_lo FPKM_conf_hi cov len major_iso_id
+- - u CUFF.1 CUFF.1.1 100 20.607936 0.000000 49.751960 1.317073 41 CUFF.1.1
+- - u CUFF.3 CUFF.3.1 100 27.255658 0.000000 65.800979 1.741935 31 CUFF.3.1
+- - u CUFF.5 CUFF.5.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.5.1
+- - u CUFF.7 CUFF.7.1 100 9.999117 0.000000 19.998234 0.639053 169 CUFF.7.1
+- - u CUFF.9 CUFF.9.1 100 17.776896 9.153835 26.399957 1.136139 404 CUFF.9.1
+- - u CUFF.11 CUFF.11.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.11.1
+Xkr4 Xkr4 c CUFF.13 CUFF.13.1 100 10.695258 0.000000 25.820637 0.683544 79 CUFF.13.1
+Xkr4 Xkr4 i CUFF.15 CUFF.15.1 100 10.695258 0.000000 25.820637 0.683544 79 CUFF.15.1
+Xkr4 Xkr4 i CUFF.17 CUFF.17.1 100 8.710571 0.000000 21.029179 0.556701 97 CUFF.17.1
+Xkr4 Xkr4 i CUFF.19 CUFF.19.1 100 29.337687 3.097262 55.578113 1.875000 72 CUFF.19.1
+Xkr4 Xkr4 i CUFF.21 CUFF.21.1 100 13.851236 0.000000 33.439842 0.885246 61 CUFF.21.1
+Xkr4 Xkr4 i CUFF.23 CUFF.23.1 100 23.470150 0.000000 50.571145 1.500000 54 CUFF.23.1
+Xkr4 Xkr4 i CUFF.25 CUFF.25.1 100 14.567679 5.354270 23.781089 0.931034 290 CUFF.25.1
+Xkr4 Xkr4 i CUFF.27 CUFF.27.1 100 34.253732 0.000000 73.806535 2.189189 37 CUFF.27.1
+- - u CUFF.29 CUFF.29.1 100 107.103219 71.402146 142.804292 6.845070 142 CUFF.29.1
+- - u CUFF.31 CUFF.31.1 100 122.650461 40.883487 204.417435 7.838710 31 CUFF.31.1
+- - u CUFF.33 CUFF.33.1 100 109.527366 26.732460 192.322273 7.000000 27 CUFF.33.1
+- - u CUFF.35 CUFF.35.1 100 96.747183 61.420107 132.074259 6.183206 131 CUFF.35.1
+- - u CUFF.37 CUFF.37.1 100 104.085013 53.596365 154.573660 6.652174 69 CUFF.37.1
+- - u CUFF.39 CUFF.39.1 100 23.912983 0.000000 51.525317 1.528302 53 CUFF.39.1
+- - u CUFF.41 CUFF.41.1 100 10.695258 0.000000 25.820637 0.683544 79 CUFF.41.1
+- - u CUFF.43 CUFF.43.1 100 10.561567 0.000000 25.497879 0.675000 80 CUFF.43.1
+- - u CUFF.45 CUFF.45.1 100 20.708956 2.186303 39.231609 1.323529 102 CUFF.45.1
+- - u CUFF.47 CUFF.47.1 100 20.607936 0.000000 49.751960 1.317073 41 CUFF.47.1
+- - u CUFF.49 CUFF.49.1 100 15.646767 0.000000 46.940300 1.000000 27 CUFF.49.1
+- - u CUFF.51 CUFF.51.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.51.1
+- - u CUFF.53 CUFF.53.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.53.1
+- - u CUFF.55 CUFF.55.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.55.1
+- - u CUFF.57 CUFF.57.1 100 15.646767 0.000000 46.940300 1.000000 27 CUFF.57.1
+- - u CUFF.59 CUFF.59.1 100 15.646767 0.000000 46.940300 1.000000 27 CUFF.59.1
+Xkr4 Xkr4 i CUFF.61 CUFF.61.1 100 45.263860 0.000000 97.530065 2.892857 28 CUFF.61.1
+Xkr4 Xkr4 i CUFF.63 CUFF.63.1 100 15.646767 0.000000 46.940300 1.000000 27 CUFF.63.1
+Xkr4 Xkr4 i CUFF.65 CUFF.65.1 100 15.362280 0.000000 37.087825 0.981818 55 CUFF.65.1
+Xkr4 Xkr4 i CUFF.67 CUFF.67.1 100 12.998852 0.000000 31.382005 0.830769 65 CUFF.67.1
+Xkr4 Xkr4 i CUFF.69 CUFF.69.1 100 10.058636 0.000000 24.283695 0.642857 84 CUFF.69.1
+Xkr4 Xkr4 i CUFF.71 CUFF.71.1 100 8.621688 0.000000 20.814595 0.551020 98 CUFF.71.1
+Xkr4 Xkr4 i CUFF.73 CUFF.73.1 100 15.362280 0.000000 37.087825 0.981818 55 CUFF.73.1
+Xkr4 Xkr4 i CUFF.75 CUFF.75.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.75.1
+Xkr4 Xkr4 i CUFF.77 CUFF.77.1 100 16.248565 0.000000 39.227507 1.038462 52 CUFF.77.1
+Xkr4 Xkr4 i CUFF.79 CUFF.79.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.79.1
+Xkr4 Xkr4 i CUFF.81 CUFF.81.1 100 13.201959 0.000000 31.872349 0.843750 64 CUFF.81.1
+Xkr4 Xkr4 i CUFF.83 CUFF.83.1 100 13.201959 0.000000 28.446269 0.843750 96 CUFF.83.1
+Xkr4 Xkr4 i CUFF.85 CUFF.85.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.85.1
+Xkr4 Xkr4 i CUFF.87 CUFF.87.1 100 17.243375 0.000000 41.629191 1.102041 49 CUFF.87.1
+Xkr4 Xkr4 i CUFF.89 CUFF.89.1 100 16.567165 0.000000 39.996674 1.058824 51 CUFF.89.1
+Xkr4 Xkr4 i CUFF.91 CUFF.91.1 100 31.293533 0.000000 75.549272 2.000000 27 CUFF.91.1
+Xkr4 Xkr4 i CUFF.93 CUFF.93.1 100 21.664754 0.000000 52.303342 1.384615 39 CUFF.93.1
+Xkr4 Xkr4 i CUFF.95 CUFF.95.1 100 46.940300 0.000000 101.142289 3.000000 27 CUFF.95.1
+Xkr4 Xkr4 i CUFF.97 CUFF.97.1 100 21.481154 0.000000 46.285454 1.372881 59 CUFF.97.1
+Xkr4 Xkr4 i CUFF.99 CUFF.99.1 100 14.567679 0.000000 35.169489 0.931034 58 CUFF.99.1
--- /dev/null
+++ b/test-data/cuffcompare_out2.refmap
@@ -0,0 +1,2 @@
+ref_gene_id ref_id class_code cuff_id_list
+Xkr4 Xkr4 c CUFF.13|CUFF.13.1
--- a/tools/ngs_rna/cuffcompare_wrapper.xml
+++ b/tools/ngs_rna/cuffcompare_wrapper.xml
@@ -81,16 +81,15 @@
<param name="use_ref_annotation" value="Yes"/><param name="reference_annotation" value="cuffcompare_in3.gtf" ftype="gtf"/><param name="ignore_nonoverlapping_reference" value="Yes"/>
- ## HACK: need to specify output name and it needs to work (right now it uses the final output file)
- <output name="XXXX" file="cuffcompare_out6.tracking"/>
- <!--
- ## TODO: transcripts combined file as well.
+ <!-- Line diffs are the result of different locations for input files; this cannot be fixed as cuffcompare outputs
+ full input path for each input. -->
+ <output name="transcripts_accuracy" file="cuffcompare_out7.txt" lines_diff="6"/><output name="input1_tmap" file="cuffcompare_out1.tmap"/><output name="input1_refmap" file="cuffcompare_out2.refmap"/><output name="input2_tmap" file="cuffcompare_out3.tmap"/><output name="input2_refmap" file="cuffcompare_out4.refmap"/>
- <output name="transcripts_accuracy" file="cuffcompare_out7.txt"/>
- -->
+ <output name="transcripts_combined" file="cuffcompare_out5.gtf"/>
+ <output name="transcripts_tracking" file="cuffcompare_out6.tracking"/></test></tests>
--- /dev/null
+++ b/test-data/cuffcompare_out4.refmap
@@ -0,0 +1,1 @@
+ref_gene_id ref_id class_code cuff_id_list
--- /dev/null
+++ b/test-data/cuffcompare_out3.tmap
@@ -0,0 +1,51 @@
+ref_gene_id ref_id class_code cuff_gene_id cuff_id FMI FPKM FPKM_conf_lo FPKM_conf_hi cov len major_iso_id
+- - u CUFF.1 CUFF.1.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.1.1
+- - u CUFF.3 CUFF.3.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.3.1
+- - u CUFF.5 CUFF.5.1 100 21.226627 0.000000 59.889707 1.205672 27 CUFF.5.1
+- - u CUFF.7 CUFF.7.1 100 29.709524 19.806349 39.612698 1.687500 576 CUFF.7.1
+- - u CUFF.9 CUFF.9.1 100 34.072933 23.364686 44.781179 1.935341 565 CUFF.9.1
+- - u CUFF.11 CUFF.11.1 100 32.531777 24.582998 40.480555 1.847804 979 CUFF.11.1
+- - u CUFF.13 CUFF.13.1 100 16.582060 0.000000 35.729373 0.941860 86 CUFF.13.1
+- - u CUFF.15 CUFF.15.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.15.1
+- - u CUFF.17 CUFF.17.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.17.1
+- - u CUFF.19 CUFF.19.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.19.1
+- - u CUFF.21 CUFF.21.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.21.1
+- - u CUFF.23 CUFF.23.1 100 16.205195 0.000000 34.917342 0.920455 88 CUFF.23.1
+- - u CUFF.25 CUFF.25.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.25.1
+- - u CUFF.26 CUFF.26.1 100 29.709524 0.000000 71.725135 1.687500 32 CUFF.26.1
+- - u CUFF.29 CUFF.29.1 100 13.581496 0.000000 32.788633 0.771429 70 CUFF.29.1
+- - u CUFF.31 CUFF.31.1 100 22.635827 0.000000 45.271655 1.285714 84 CUFF.31.1
+Xkr4 Xkr4 i CUFF.33 CUFF.33.1 100 23.767619 0.000000 57.380108 1.350000 40 CUFF.33.1
+Xkr4 Xkr4 i CUFF.35 CUFF.35.1 100 11.317914 0.000000 27.323861 0.642857 84 CUFF.35.1
+Xkr4 Xkr4 i CUFF.37 CUFF.37.1 100 11.500461 0.000000 24.780049 0.653226 124 CUFF.37.1
+Xkr4 Xkr4 i CUFF.39 CUFF.39.1 100 52.816931 0.000000 113.804669 3.000000 27 CUFF.39.1
+Xkr4 Xkr4 i CUFF.41 CUFF.41.1 100 43.213852 0.000000 93.112911 2.454545 33 CUFF.41.1
+Xkr4 Xkr4 i CUFF.43 CUFF.43.1 100 23.474191 0.000000 46.948383 1.333333 81 CUFF.43.1
+Xkr4 Xkr4 i CUFF.45 CUFF.45.1 100 20.667495 0.000000 49.895746 1.173913 46 CUFF.45.1
+Xkr4 Xkr4 i CUFF.47 CUFF.47.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.47.1
+Xkr4 Xkr4 i CUFF.49 CUFF.49.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.49.1
+Xkr4 Xkr4 i CUFF.51 CUFF.51.1 100 14.948188 7.228977 22.667399 0.849057 477 CUFF.51.1
+Xkr4 Xkr4 i CUFF.53 CUFF.53.1 100 52.816931 0.000000 113.804669 3.000000 27 CUFF.53.1
+Xkr4 Xkr4 i CUFF.55 CUFF.55.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.55.1
+Xkr4 Xkr4 i CUFF.57 CUFF.57.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.57.1
+Xkr4 Xkr4 i CUFF.59 CUFF.59.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.59.1
+Xkr4 Xkr4 i CUFF.61 CUFF.61.1 100 13.204233 0.000000 31.877838 0.750000 72 CUFF.61.1
+Xkr4 Xkr4 i CUFF.63 CUFF.63.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.63.1
+Xkr4 Xkr4 i CUFF.65 CUFF.65.1 100 31.170648 0.000000 62.341295 1.770492 61 CUFF.65.1
+Xkr4 Xkr4 i CUFF.67 CUFF.67.1 100 15.681351 3.378764 27.983938 0.890700 197 CUFF.67.1
+Xkr4 Xkr4 i CUFF.69 CUFF.69.1 100 18.799247 8.750627 28.847866 1.067797 354 CUFF.69.1
+Xkr4 Xkr4 i CUFF.71 CUFF.71.1 100 22.635827 0.000000 54.647722 1.285714 42 CUFF.71.1
+Xkr4 Xkr4 i CUFF.73 CUFF.73.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.73.1
+Xkr4 Xkr4 i CUFF.75 CUFF.75.1 100 52.816931 0.000000 113.804669 3.000000 27 CUFF.75.1
+Xkr4 Xkr4 i CUFF.77 CUFF.77.1 100 17.605644 0.000000 52.816931 1.000000 27 CUFF.77.1
+Xkr4 Xkr4 i CUFF.79 CUFF.79.1 100 13.390208 0.000000 32.326821 0.760563 71 CUFF.79.1
+Xkr4 Xkr4 i CUFF.81 CUFF.81.1 100 11.211141 1.183592 21.238690 0.636792 212 CUFF.81.1
+Xkr4 Xkr4 i CUFF.83 CUFF.83.1 100 21.126772 0.000000 51.004540 1.200000 45 CUFF.83.1
+Xkr4 Xkr4 i CUFF.85 CUFF.85.1 100 19.014095 0.000000 38.028190 1.080000 100 CUFF.85.1
+Xkr4 Xkr4 i CUFF.87 CUFF.87.1 100 24.170460 0.000000 52.080103 1.372881 59 CUFF.87.1
+Xkr4 Xkr4 i CUFF.89 CUFF.89.1 100 29.709524 0.000000 64.015126 1.687500 48 CUFF.89.1
+Xkr4 Xkr4 i CUFF.91 CUFF.91.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.91.1
+Xkr4 Xkr4 i CUFF.93 CUFF.93.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.93.1
+Xkr4 Xkr4 i CUFF.95 CUFF.95.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.95.1
+Xkr4 Xkr4 i CUFF.97 CUFF.97.1 100 35.211287 0.000000 85.007567 2.000000 27 CUFF.97.1
+Xkr4 Xkr4 i CUFF.99 CUFF.99.1 100 19.602160 0.000000 39.204320 1.113402 97 CUFF.99.1
--- /dev/null
+++ b/test-data/cuffcompare_out7.txt
@@ -0,0 +1,50 @@
+# Cuffcompare v0.9.1 | Command line was:
+#cuffcompare -r cuffcompare_in3.gtf -R cuffcompare_in1.gtf cuffcompare_in2.gtf
+#
+#> Genomic sequence: chr1
+# Query mRNAs : 50 in 50 loci (0 multi-exon transcripts)
+# (0 multi-transcript loci, ~1.0 transcripts per locus)
+# Reference mRNAs : 1 in 1 loci (1 multi-exon)
+# Corresponding super-loci: 1
+#--------------------| Sn | Sp | fSn | fSp
+ Base level: 2.2 2.3 - -
+ Exon level: 0.0 0.0 0.0 0.0
+ Intron level: 0.0 nan 0.0 100.0
+Intron chain level: 0.0 100.0 0.0 100.0
+ Transcript level: 0.0 0.0 0.0 0.0
+ Locus level: 0.0 0.0 0.0 0.0
+ Missed exons: 2/3 ( 66.7%)
+ Wrong exons: 49/50 ( 98.0%)
+ Missed introns: 2/2 (100.0%)
+ Missed loci: 0/1 ( 0.0%)
+ Wrong loci: 49/50 ( 98.0%)
+
+#= Summary for dataset: cuffcompare_in1.gtf :
+# Query mRNAs : 50 in 50 loci (0 multi-exon transcripts)
+# (0 multi-transcript loci, ~1.0 transcripts per locus)
+# Reference mRNAs : 1 in 1 loci (1 multi-exon)
+# Corresponding super-loci: 1
+#--------------------| Sn | Sp | fSn | fSp
+ Base level: 2.2 2.3 - -
+ Exon level: 0.0 0.0 0.0 0.0
+ Intron level: 0.0 nan 0.0 100.0
+Intron chain level: 0.0 100.0 0.0 100.0
+ Transcript level: 0.0 0.0 0.0 0.0
+ Locus level: 0.0 0.0 0.0 0.0
+ Missed exons: 2/3 ( 66.7%)
+ Wrong exons: 49/50 ( 98.0%)
+ Missed introns: 2/2 (100.0%)
+ Missed loci: 0/1 ( 0.0%)
+ Wrong loci: 49/50 ( 98.0%)
+#> Genomic sequence: chr1
+# Query mRNAs : 50 in 50 loci (0 multi-exon transcripts)
+# (0 multi-transcript loci, ~1.0 transcripts per locus)
+# Reference mRNAs : 0 in 0 loci (0 multi-exon)
+
+#= Summary for dataset: cuffcompare_in2.gtf :
+# Query mRNAs : 50 in 50 loci (0 multi-exon transcripts)
+# (0 multi-transcript loci, ~1.0 transcripts per locus)
+# Reference mRNAs : 0 in 0 loci (0 multi-exon)
+
+ Total union super-loci across all input datasets: 87
+ (0 multi-transcript, ~1.1 transcripts per locus)
1
0
galaxy-dist commit e6e68c67dd97: Use a job to import a history from an archive. This makes it possible to move a history from one Galaxy instance to another, subject to the limitations below. The job itself unpacks the archive, and the job's cleanup creates a history from the archive. A history imported via an archive is only visible to the user when it has finished being imported.
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 1288106620 14400
# Node ID e6e68c67dd9759154876546662f59ee280b78a34
# Parent 677bcc65f74f5a460277f089d0cdebf170f02831
Use a job to import a history from an archive. This makes it possible to move a history from one Galaxy instance to another, subject to the limitations below. The job itself unpacks the archive, and the job's cleanup creates a history from the archive. A history imported via an archive is only visible to the user when it has finished being imported.
Currently, there are limitations to this functionality; these will be fixed going forward. These limitations are:
-history archives can be imported only via URL, not file;
-histories must be shared in order for them to be importable via archive;
-tags are not currently imported;
-reproducibility is limited as parameters for imported jobs are not always recovered and set.
--- a/lib/galaxy/web/controllers/history.py
+++ b/lib/galaxy/web/controllers/history.py
@@ -79,7 +79,7 @@ class HistoryListGrid( grids.Grid ):
def get_current_item( self, trans, **kwargs ):
return trans.get_history()
def apply_query_filter( self, trans, query, **kwargs ):
- return query.filter_by( user=trans.user, purged=False )
+ return query.filter_by( user=trans.user, purged=False, importing=False )
class SharedHistoryListGrid( grids.Grid ):
# Custom column types
@@ -465,212 +465,40 @@ class HistoryController( BaseController,
trans.sa_session.flush()
return new_annotation
- def import_archive( self, trans, archived_history=None, gzip=True ):
- """ Import a history. """
+ @web.expose
+ # TODO: Remove require_login when users are warned that, if they are not
+ # logged in, this will remove their current history.
+ @web.require_login( "use Galaxy histories" )
+ def import_archive( self, trans, **kwargs ):
+ """ Import a history from a file archive. """
- def file_in_dir( file_path, a_dir ):
- """ Returns true if file is in directory. """
- abs_file_path = os.path.abspath( file_path )
- return os.path.split( abs_file_path )[0] == a_dir
+ # Set archive source and type.
+ archive_file = kwargs.get( 'archive_file', None )
+ archive_url = kwargs.get( 'archive_url', None )
+ archive_source = None
+ if archive_file:
+ archive_source = archive_file
+ archive_type = 'file'
+ elif archive_url:
+ archive_source = archive_url
+ archive_type = 'url'
- if archived_history is not None:
- try:
- history_archive_file = tarfile.open( archived_history.file.name )
-
- # Unpack archive in temporary directory.
- temp_output_dir = tempfile.mkdtemp()
- history_archive_file.extractall( path=temp_output_dir )
- history_archive_file.close()
-
- #
- # Create history.
- #
- history_attr_file_name = os.path.join( temp_output_dir, 'history_attrs.txt')
- if not file_in_dir( history_attr_file_name, temp_output_dir ):
- raise Exception( "Invalid location for history attributes file: %s" % history_attr_file_name )
- history_attr_in = open( history_attr_file_name, 'rb' )
- history_attr_str = ''
- buffsize = 1048576
- try:
- while True:
- history_attr_str += history_attr_in.read( buffsize )
- if not history_attr_str or len( history_attr_str ) % buffsize != 0:
- break
- except OverflowError:
- pass
- history_attr_in.close()
- history_attrs = from_json_string( history_attr_str )
-
- # Create history.
- new_history = model.History( name='imported from archive: %s' % history_attrs['name'].encode( 'utf-8' ), user=trans.user )
- trans.sa_session.add( new_history )
-
- new_history.hid_counter = history_attrs['hid_counter']
- new_history.genome_build = history_attrs['genome_build']
- trans.sa_session.flush()
-
- # Builds a tag string for a tag, value pair.
- def get_tag_str( tag, value ):
- if not value:
- return tag
- else:
- return tag + ":" + value
-
- # Add annotation, tags.
- if trans.user:
- self.add_item_annotation( trans.sa_session, trans.get_user(), new_history, history_attrs[ 'annotation' ] )
- for tag, value in history_attrs[ 'tags' ].items():
- trans.app.tag_handler.apply_item_tags( trans, trans.user, new_history, get_tag_str( tag, value ) )
-
- #
- # Create datasets.
- #
- datasets_attrs_file_name = os.path.join( temp_output_dir, 'datasets_attrs.txt')
- if not file_in_dir( datasets_attrs_file_name, temp_output_dir ):
- raise Exception( "Invalid location for dataset attributes file: %s" % datasets_attrs_file_name )
- datasets_attr_in = open( datasets_attrs_file_name, 'rb' )
- datasets_attr_str = ''
- buffsize = 1048576
- try:
- while True:
- datasets_attr_str += datasets_attr_in.read( buffsize )
- if not datasets_attr_str or len( datasets_attr_str ) % buffsize != 0:
- break
- except OverflowError:
- pass
- datasets_attr_in.close()
- datasets_attrs = from_json_string( datasets_attr_str )
-
- # Create datasets.
- for dataset_attrs in datasets_attrs:
- metadata = dataset_attrs['metadata']
-
- # Create dataset and HDA.
- hda = model.HistoryDatasetAssociation( name = dataset_attrs['name'].encode( 'utf-8' ),
- extension = dataset_attrs['extension'],
- info = dataset_attrs['info'].encode( 'utf-8' ),
- blurb = dataset_attrs['blurb'],
- peek = dataset_attrs['peek'],
- designation = dataset_attrs['designation'],
- visible = dataset_attrs['visible'],
- dbkey = metadata['dbkey'],
- metadata = metadata,
- history = new_history,
- create_dataset = True,
- sa_session = trans.sa_session )
- hda.state = hda.states.OK
- trans.sa_session.add( hda )
- trans.sa_session.flush()
- new_history.add_dataset( hda, genome_build = None )
- hda.hid = dataset_attrs['hid'] # Overwrite default hid set when HDA added to history.
- permissions = trans.app.security_agent.history_get_default_permissions( new_history )
- trans.app.security_agent.set_all_dataset_permissions( hda.dataset, permissions )
- trans.sa_session.flush()
-
- # Do security check and copy dataset data.
- temp_dataset_file_name = os.path.join( temp_output_dir, dataset_attrs['file_name'] )
- if not file_in_dir( temp_dataset_file_name, os.path.join( temp_output_dir, "datasets" ) ):
- raise Exception( "Invalid dataset path: %s" % temp_dataset_file_name )
- shutil.move( temp_dataset_file_name, hda.file_name )
-
- # Set tags, annotations.
- if trans.user:
- self.add_item_annotation( trans.sa_session, trans.get_user(), hda, dataset_attrs[ 'annotation' ] )
- for tag, value in dataset_attrs[ 'tags' ].items():
- trans.app.tag_handler.apply_item_tags( trans, trans.user, hda, get_tag_str( tag, value ) )
- trans.sa_session.flush()
-
- #
- # Create jobs.
- #
-
- # Read jobs attributes.
- jobs_attr_file_name = os.path.join( temp_output_dir, 'jobs_attrs.txt')
- if not file_in_dir( jobs_attr_file_name, temp_output_dir ):
- raise Exception( "Invalid location for jobs' attributes file: %s" % jobs_attr_file_name )
- jobs_attr_in = open( jobs_attr_file_name, 'rb' )
- jobs_attr_str = ''
- buffsize = 1048576
- try:
- while True:
- jobs_attr_str += jobs_attr_in.read( buffsize )
- if not jobs_attr_str or len( jobs_attr_str ) % buffsize != 0:
- break
- except OverflowError:
- pass
- jobs_attr_in.close()
-
- # Decode jobs attributes.
- def as_hda( obj_dct ):
- """ Hook to 'decode' an HDA; method uses history and HID to get the HDA represented by
- the encoded object. This only works because HDAs are created above. """
- if obj_dct.get( '__HistoryDatasetAssociation__', False ):
- return trans.sa_session.query( model.HistoryDatasetAssociation ) \
- .filter_by( history=new_history, hid=obj_dct['hid'] ).first()
- return obj_dct
- jobs_attrs = from_json_string( jobs_attr_str, object_hook=as_hda )
-
- # Create each job.
- for job_attrs in jobs_attrs:
- imported_job = model.Job()
- imported_job.user = trans.user
- imported_job.session = trans.get_galaxy_session().id
- imported_job.history = new_history
- imported_job.tool_id = job_attrs[ 'tool_id' ]
- imported_job.tool_version = job_attrs[ 'tool_version' ]
- imported_job.set_state( job_attrs[ 'state' ] )
- imported_job.imported = True
- trans.sa_session.add( imported_job )
- trans.sa_session.flush()
-
- class HistoryDatasetAssociationIDEncoder( simplejson.JSONEncoder ):
- """ Custom JSONEncoder for a HistoryDatasetAssociation that encodes an HDA as its ID. """
- def default( self, obj ):
- """ Encode an HDA, default encoding for everything else. """
- if isinstance( obj, model.HistoryDatasetAssociation ):
- return obj.id
- return simplejson.JSONEncoder.default( self, obj )
+ # If no source to create archive from, show form to upload archive or specify URL.
+ if not archive_source:
+ return trans.show_form(
+ web.FormBuilder( web.url_for(), "Import a History from an Archive", submit_text="Submit" ) \
+ .add_input( "text", "Archived History URL", "archive_url", value="", error=None )
+ # TODO: add support for importing via a file.
+ #.add_input( "file", "Archived History File", "archive_file", value=None, error=None )
+ )
- # Set parameters. May be useful to look at metadata.py for creating parameters.
- # TODO: there may be a better way to set parameters, e.g.:
- # for name, value in tool.params_to_strings( incoming, trans.app ).iteritems():
- # job.add_parameter( name, value )
- # to make this work, we'd need to flesh out the HDA objects. The code below is
- # relatively similar.
- for name, value in job_attrs[ 'params' ].items():
- # Transform parameter values when necessary.
- if isinstance( value, model.HistoryDatasetAssociation ):
- # HDA input: use hid to find input.
- input_hda = trans.sa_session.query( model.HistoryDatasetAssociation ) \
- .filter_by( history=new_history, hid=value.hid ).first()
- value = input_hda.id
- #print "added parameter %s-->%s to job %i" % ( name, value, imported_job.id )
- imported_job.add_parameter( name, to_json_string( value, cls=HistoryDatasetAssociationIDEncoder ) )
-
- # TODO: Connect jobs to input datasets.
-
- # Connect jobs to output datasets.
- for output_hid in job_attrs[ 'output_datasets' ]:
- #print "%s job has output dataset %i" % (imported_job.id, output_hid)
- output_hda = trans.sa_session.query( model.HistoryDatasetAssociation ) \
- .filter_by( history=new_history, hid=output_hid ).first()
- if output_hda:
- imported_job.add_output_dataset( output_hda.name, output_hda )
- trans.sa_session.flush()
-
- # Cleanup.
- if os.path.exists( temp_output_dir ):
- shutil.rmtree( temp_output_dir )
-
- return trans.show_ok_message( message="History '%s' has been imported. " % history_attrs['name'] )
- except Exception, e:
- return trans.show_error_message( 'Error importing history archive. ' + str( e ) )
-
- return trans.show_form(
- web.FormBuilder( web.url_for(), "Import a History from an Archive", submit_text="Submit" )
- .add_input( "file", "Archived History File", "archived_history", value=None, error=None )
- )
-
+ # Run job to do import.
+ history_imp_tool = trans.app.toolbox.tools_by_id[ '__IMPORT_HISTORY__' ]
+ incoming = { '__ARCHIVE_SOURCE__' : archive_source, '__ARCHIVE_TYPE__' : archive_type }
+ history_imp_tool.execute( trans, incoming=incoming )
+ return trans.show_message( "Importing history from '%s'. \
+ This history will be visible when the import is complete" % archive_source )
+
@web.expose
def export_archive( self, trans, id=None, gzip=True, include_hidden=False, include_deleted=False ):
""" Export a history to an archive. """
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -608,6 +608,7 @@ class JobWrapper( object ):
if self.app.config.set_metadata_externally:
self.external_output_metadata.cleanup_external_metadata( self.sa_session )
galaxy.tools.imp_exp.JobExportHistoryArchiveWrapper( self.job_id ).cleanup_after_job( self.sa_session )
+ galaxy.tools.imp_exp.JobImportHistoryArchiveWrapper( self.job_id ).cleanup_after_job( self.sa_session )
except:
log.exception( "Unable to cleanup job %d" % self.job_id )
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -227,7 +227,13 @@ class JobExportHistoryArchive( object ):
self.history_attrs_filename = history_attrs_filename
self.datasets_attrs_filename = datasets_attrs_filename
self.jobs_attrs_filename = jobs_attrs_filename
-
+
+class JobImportHistoryArchive( object ):
+ def __init__( self, job=None, history=None, archive_dir=None ):
+ self.job = job
+ self.history = history
+ self.archive_dir=archive_dir
+
class Group( object ):
def __init__( self, name = None ):
self.name = name
@@ -244,6 +250,7 @@ class History( object, UsesAnnotations )
self.name = name or "Unnamed history"
self.deleted = False
self.purged = False
+ self.importing = False
self.genome_build = None
self.published = False
# Relationships
--- a/lib/galaxy/tools/imp_exp/__init__.py
+++ b/lib/galaxy/tools/imp_exp/__init__.py
@@ -2,7 +2,7 @@ import os, shutil, logging, tempfile, si
from galaxy import model
from galaxy.web.framework.helpers import to_unicode
from galaxy.model.item_attrs import UsesAnnotations
-from galaxy.util.json import to_json_string
+from galaxy.util.json import *
from galaxy.web.base.controller import UsesHistory
log = logging.getLogger(__name__)
@@ -26,21 +26,252 @@ def load_history_imp_exp_tools( toolbox
</outputs></tool>
"""
+
+ # Load export tool.
tmp_name = tempfile.NamedTemporaryFile()
tmp_name.write( tool_xml_text )
tmp_name.flush()
history_exp_tool = toolbox.load_tool( tmp_name.name )
toolbox.tools_by_id[ history_exp_tool.id ] = history_exp_tool
log.debug( "Loaded history export tool: %s", history_exp_tool.id )
+
+ # Load import tool.
+ tool_xml = os.path.join( os.getcwd(), "lib/galaxy/tools/imp_exp/imp_history_from_archive.xml" )
+ history_imp_tool = toolbox.load_tool( tool_xml )
+ toolbox.tools_by_id[ history_imp_tool.id ] = history_imp_tool
+ log.debug( "Loaded history import tool: %s", history_imp_tool.id )
+
+class JobImportHistoryArchiveWrapper( object, UsesHistory, UsesAnnotations ):
+ """
+ Class provides support for performing jobs that import a history from
+ an archive.
+ """
+ def __init__( self, job_id ):
+ self.job_id = job_id
+
+ def cleanup_after_job( self, db_session ):
+ """ Set history, datasets, and jobs' attributes and clean up archive directory. """
+
+ #
+ # Helper methods.
+ #
+
+ def file_in_dir( file_path, a_dir ):
+ """ Returns true if file is in directory. """
+ abs_file_path = os.path.abspath( file_path )
+ return os.path.split( abs_file_path )[0] == a_dir
+
+ def read_file_contents( file_path ):
+ """ Read contents of a file. """
+ fp = open( file_path, 'rb' )
+ buffsize = 1048576
+ file_contents = ''
+ try:
+ while True:
+ file_contents += fp.read( buffsize )
+ if not file_contents or len( file_contents ) % buffsize != 0:
+ break
+ except OverflowError:
+ pass
+ fp.close()
+ return file_contents
+
+ def get_tag_str( tag, value ):
+ """ Builds a tag string for a tag, value pair. """
+ if not value:
+ return tag
+ else:
+ return tag + ":" + value
+
+ #
+ # Import history.
+ #
+
+ jiha = db_session.query( model.JobImportHistoryArchive ).filter_by( job_id=self.job_id ).first()
+ if jiha:
+ try:
+ archive_dir = jiha.archive_dir
+ user = jiha.job.user
+
+ #
+ # Create history.
+ #
+ history_attr_file_name = os.path.join( archive_dir, 'history_attrs.txt')
+ history_attr_str = read_file_contents( history_attr_file_name )
+ history_attrs = from_json_string( history_attr_str )
+
+ # Create history.
+ new_history = model.History( name='imported from archive: %s' % history_attrs['name'].encode( 'utf-8' ), \
+ user=user )
+ new_history.importing = True
+ new_history.hid_counter = history_attrs['hid_counter']
+ new_history.genome_build = history_attrs['genome_build']
+ db_session.add( new_history )
+ jiha.history = new_history
+ db_session.flush()
+
+ # Add annotation, tags.
+ if user:
+ self.add_item_annotation( db_session, user, new_history, history_attrs[ 'annotation' ] )
+ """
+ TODO: figure out to how add tags to item.
+ for tag, value in history_attrs[ 'tags' ].items():
+ trans.app.tag_handler.apply_item_tags( trans, trans.user, new_history, get_tag_str( tag, value ) )
+ """
+
+ #
+ # Create datasets.
+ #
+ datasets_attrs_file_name = os.path.join( archive_dir, 'datasets_attrs.txt')
+ datasets_attr_str = read_file_contents( datasets_attrs_file_name )
+ datasets_attrs = from_json_string( datasets_attr_str )
+
+ # Get counts of how often each dataset file is used; a file can
+ # be linked to multiple dataset objects (HDAs).
+ datasets_usage_counts = {}
+ for dataset_attrs in datasets_attrs:
+ temp_dataset_file_name = \
+ os.path.abspath( os.path.join( archive_dir, dataset_attrs['file_name'] ) )
+ if ( temp_dataset_file_name not in datasets_usage_counts ):
+ datasets_usage_counts[ temp_dataset_file_name ] = 0
+ datasets_usage_counts[ temp_dataset_file_name ] += 1
+
+ # Create datasets.
+ for dataset_attrs in datasets_attrs:
+ metadata = dataset_attrs['metadata']
+
+ # Create dataset and HDA.
+ hda = model.HistoryDatasetAssociation( name = dataset_attrs['name'].encode( 'utf-8' ),
+ extension = dataset_attrs['extension'],
+ info = dataset_attrs['info'].encode( 'utf-8' ),
+ blurb = dataset_attrs['blurb'],
+ peek = dataset_attrs['peek'],
+ designation = dataset_attrs['designation'],
+ visible = dataset_attrs['visible'],
+ dbkey = metadata['dbkey'],
+ metadata = metadata,
+ history = new_history,
+ create_dataset = True,
+ sa_session = db_session )
+ hda.state = hda.states.OK
+ db_session.add( hda )
+ db_session.flush()
+ new_history.add_dataset( hda, genome_build = None )
+ hda.hid = dataset_attrs['hid'] # Overwrite default hid set when HDA added to history.
+ # TODO: Is there a way to recover permissions? Is this needed?
+ #permissions = trans.app.security_agent.history_get_default_permissions( new_history )
+ #trans.app.security_agent.set_all_dataset_permissions( hda.dataset, permissions )
+ db_session.flush()
+
+ # Do security check and move/copy dataset data.
+ temp_dataset_file_name = \
+ os.path.abspath( os.path.join( archive_dir, dataset_attrs['file_name'] ) )
+ if not file_in_dir( temp_dataset_file_name, os.path.join( archive_dir, "datasets" ) ):
+ raise Exception( "Invalid dataset path: %s" % temp_dataset_file_name )
+ if datasets_usage_counts[ temp_dataset_file_name ] == 1:
+ shutil.move( temp_dataset_file_name, hda.file_name )
+ else:
+ datasets_usage_counts[ temp_dataset_file_name ] -= 1
+ shutil.copyfile( temp_dataset_file_name, hda.file_name )
+
+ # Set tags, annotations.
+ if user:
+ self.add_item_annotation( db_session, user, hda, dataset_attrs[ 'annotation' ] )
+ # TODO: Set tags.
+ """
+ for tag, value in dataset_attrs[ 'tags' ].items():
+ trans.app.tag_handler.apply_item_tags( trans, trans.user, hda, get_tag_str( tag, value ) )
+ db_session.flush()
+ """
+
+ #
+ # Create jobs.
+ #
+
+ # Read jobs attributes.
+ jobs_attr_file_name = os.path.join( archive_dir, 'jobs_attrs.txt')
+ jobs_attr_str = read_file_contents( jobs_attr_file_name )
+
+ # Decode jobs attributes.
+ def as_hda( obj_dct ):
+ """ Hook to 'decode' an HDA; method uses history and HID to get the HDA represented by
+ the encoded object. This only works because HDAs are created above. """
+ if obj_dct.get( '__HistoryDatasetAssociation__', False ):
+ return db_session.query( model.HistoryDatasetAssociation ) \
+ .filter_by( history=new_history, hid=obj_dct['hid'] ).first()
+ return obj_dct
+ jobs_attrs = from_json_string( jobs_attr_str, object_hook=as_hda )
+
+ # Create each job.
+ for job_attrs in jobs_attrs:
+ imported_job = model.Job()
+ imported_job.user = user
+ # TODO: set session?
+ # imported_job.session = trans.get_galaxy_session().id
+ imported_job.history = new_history
+ imported_job.tool_id = job_attrs[ 'tool_id' ]
+ imported_job.tool_version = job_attrs[ 'tool_version' ]
+ imported_job.set_state( job_attrs[ 'state' ] )
+ imported_job.imported = True
+ db_session.add( imported_job )
+ db_session.flush()
+
+ class HistoryDatasetAssociationIDEncoder( simplejson.JSONEncoder ):
+ """ Custom JSONEncoder for a HistoryDatasetAssociation that encodes an HDA as its ID. """
+ def default( self, obj ):
+ """ Encode an HDA, default encoding for everything else. """
+ if isinstance( obj, model.HistoryDatasetAssociation ):
+ return obj.id
+ return simplejson.JSONEncoder.default( self, obj )
+
+ # Set parameters. May be useful to look at metadata.py for creating parameters.
+ # TODO: there may be a better way to set parameters, e.g.:
+ # for name, value in tool.params_to_strings( incoming, trans.app ).iteritems():
+ # job.add_parameter( name, value )
+ # to make this work, we'd need to flesh out the HDA objects. The code below is
+ # relatively similar.
+ for name, value in job_attrs[ 'params' ].items():
+ # Transform parameter values when necessary.
+ if isinstance( value, model.HistoryDatasetAssociation ):
+ # HDA input: use hid to find input.
+ input_hda = db_session.query( model.HistoryDatasetAssociation ) \
+ .filter_by( history=new_history, hid=value.hid ).first()
+ value = input_hda.id
+ #print "added parameter %s-->%s to job %i" % ( name, value, imported_job.id )
+ imported_job.add_parameter( name, to_json_string( value, cls=HistoryDatasetAssociationIDEncoder ) )
+
+ # TODO: Connect jobs to input datasets.
+
+ # Connect jobs to output datasets.
+ for output_hid in job_attrs[ 'output_datasets' ]:
+ #print "%s job has output dataset %i" % (imported_job.id, output_hid)
+ output_hda = db_session.query( model.HistoryDatasetAssociation ) \
+ .filter_by( history=new_history, hid=output_hid ).first()
+ if output_hda:
+ imported_job.add_output_dataset( output_hda.name, output_hda )
+
+ # Done importing.
+ new_history.importing = False
+
+ db_session.flush()
+
+ # Cleanup.
+ if os.path.exists( archive_dir ):
+ shutil.rmtree( archive_dir )
+ except Exception, e:
+ jiha.job.stderr += "Error cleaning up history import job: %s" % e
+ db_session.flush()
class JobExportHistoryArchiveWrapper( object, UsesHistory, UsesAnnotations ):
- """ Class provides support for performing jobs that export a history to an archive. """
+ """
+ Class provides support for performing jobs that export a history to an
+ archive.
+ """
def __init__( self, job_id ):
self.job_id = job_id
# TODO: should use db_session rather than trans in this method.
def setup_job( self, trans, jeha, include_hidden=False, include_deleted=False ):
- # jeha = job_export_history_archive for the job.
""" Perform setup for job to export a history into an archive. Method generates
attribute files for export, sets the corresponding attributes in the jeha
object, and returns a command line for running the job. The command line
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -1904,6 +1904,9 @@ class SetMetadataTool( Tool ):
class ExportHistoryTool( Tool ):
tool_type = 'export_history'
+
+class ImportHistoryTool( Tool ):
+ tool_type = 'import_history'
# Populate tool_type to ToolClass mappings
tool_types = {}
--- a/lib/galaxy/tools/actions/history_imp_exp.py
+++ b/lib/galaxy/tools/actions/history_imp_exp.py
@@ -1,10 +1,56 @@
+import tempfile
from __init__ import ToolAction
from galaxy.util.odict import odict
-from galaxy.tools.imp_exp import JobExportHistoryArchiveWrapper
+from galaxy.tools.imp_exp import *
import logging
log = logging.getLogger( __name__ )
+class ImportHistoryToolAction( ToolAction ):
+ """Tool action used for importing a history to an archive. """
+
+ def execute( self, tool, trans, incoming = {}, set_output_hid = False, overwrite = True ):
+ #
+ # Create job.
+ #
+ job = trans.app.model.Job()
+ job.session_id = trans.get_galaxy_session().id
+ job.history_id = trans.history.id
+ job.tool_id = tool.id
+ job.user_id = trans.user.id
+ start_job_state = job.state #should be job.states.NEW
+ job.state = job.states.WAITING #we need to set job state to something other than NEW, or else when tracking jobs in db it will be picked up before we have added input / output parameters
+ trans.sa_session.add( job )
+ trans.sa_session.flush() #ensure job.id are available
+
+ #
+ # Setup job and job wrapper.
+ #
+
+ # Add association for keeping track of job, history relationship.
+ archive_dir = tempfile.mkdtemp()
+ jiha = trans.app.model.JobImportHistoryArchive( job=job, archive_dir=archive_dir )
+ trans.sa_session.add( jiha )
+ job_wrapper = JobImportHistoryArchiveWrapper( job )
+
+ #
+ # Add parameters to job_parameter table.
+ #
+
+ # Set additional parameters.
+ incoming[ '__DEST_DIR__' ] = jiha.archive_dir
+ for name, value in tool.params_to_strings( incoming, trans.app ).iteritems():
+ job.add_parameter( name, value )
+
+ job.state = start_job_state #job inputs have been configured, restore initial job state
+ trans.sa_session.flush()
+
+ # Queue the job for execution
+ trans.app.job_queue.put( job.id, tool )
+ trans.log_event( "Added import history job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id )
+
+ return job, odict()
+
class ExportHistoryToolAction( ToolAction ):
"""Tool action used for exporting a history to an archive. """
@@ -30,6 +76,7 @@ class ExportHistoryToolAction( ToolActio
job.session_id = trans.get_galaxy_session().id
job.history_id = trans.history.id
job.tool_id = tool.id
+ job.user_id = trans.user.id
start_job_state = job.state #should be job.states.NEW
job.state = job.states.WAITING #we need to set job state to something other than NEW, or else when tracking jobs in db it will be picked up before we have added input / output parameters
trans.sa_session.add( job )
--- /dev/null
+++ b/lib/galaxy/tools/imp_exp/imp_history_from_archive.xml
@@ -0,0 +1,10 @@
+<tool id="__IMPORT_HISTORY__" name="Import History" version="0.1" tool_type="import_history">
+ <type class="ImportHistoryTool" module="galaxy.tools"/>
+ <action module="galaxy.tools.actions.history_imp_exp" class="ImportHistoryToolAction"/>
+ <command interpreter="python">unpack_tar_gz_archive.py $__ARCHIVE_SOURCE__ $__DEST_DIR__ --$__ARCHIVE_TYPE__</command>
+ <inputs>
+ <param name="__ARCHIVE_SOURCE__" type="text"/>
+ <param name="__ARCHIVE_TYPE__" type="text"/>
+ <param name="__DEST_DIR__" type="text"/>
+ </inputs>
+</tool>
--- a/templates/root/index.mako
+++ b/templates/root/index.mako
@@ -53,10 +53,10 @@
galaxy_main.location = "${h.url_for( controller='history', action='delete_current' )}";
}
},
- ##"Other Actions": null,
- ##"Import from File": function() {
- ## galaxy_main.location = "${h.url_for( controller='history', action='import_archive' )}";
- ##}
+ "Other Actions": null,
+ "Import from File": function() {
+ galaxy_main.location = "${h.url_for( controller='history', action='import_archive' )}";
+ }
});
// Init tool options.
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -77,6 +77,7 @@ History.table = Table( "history", metada
Column( "hid_counter", Integer, default=1 ),
Column( "deleted", Boolean, index=True, default=False ),
Column( "purged", Boolean, index=True, default=False ),
+ Column( "importing", Boolean, index=True, default=False ),
Column( "genome_build", TrimmedString( 40 ) ),
Column( "importable", Boolean, default=False ),
Column( "slug", TEXT, index=True ),
@@ -384,6 +385,13 @@ JobExportHistoryArchive.table = Table( "
Column( "jobs_attrs_filename", TEXT )
)
+JobImportHistoryArchive.table = Table( "job_import_history_archive", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
+ Column( "archive_dir", TEXT )
+ )
+
PostJobAction.table = Table("post_job_action", metadata,
Column("id", Integer, primary_key=True),
Column("workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True, nullable=False),
@@ -1220,6 +1228,9 @@ assign_mapper( context, JobExportHistory
properties=dict( job = relation( Job ),
history = relation( History ),
dataset = relation( Dataset ) ) )
+
+assign_mapper( context, JobImportHistoryArchive, JobImportHistoryArchive.table,
+ properties=dict( job = relation( Job ), history = relation( History ) ) )
assign_mapper( context, PostJobAction, PostJobAction.table,
properties=dict(workflow_step = relation( WorkflowStep, backref='post_job_actions', primaryjoin=(WorkflowStep.table.c.id == PostJobAction.table.c.workflow_step_id))))
--- /dev/null
+++ b/lib/galaxy/model/migrate/versions/0060_history_archive_import.py
@@ -0,0 +1,72 @@
+"""
+Migration script to create column and table for importing histories from
+file archives.
+"""
+
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from migrate import *
+from migrate.changeset import *
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) )
+
+# Columns to add.
+
+importing_col = Column( "importing", Boolean, index=True, default=False )
+ldda_parent_col = Column( "ldda_parent_id", Integer, ForeignKey( "library_dataset_dataset_association.id" ), index=True )
+
+# Table to add.
+
+JobImportHistoryArchive_table = Table( "job_import_history_archive", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True ),
+ Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
+ Column( "archive_dir", TEXT )
+ )
+
+def upgrade():
+ print __doc__
+ metadata.reflect()
+
+ # Add column to history table and initialize.
+ try:
+ History_table = Table( "history", metadata, autoload=True )
+ importing_col.create( History_table )
+ assert importing_col is History_table.c.importing
+
+ # Initialize column to false.
+ if migrate_engine.name == 'mysql' or migrate_engine.name == 'sqlite':
+ default_false = "0"
+ elif migrate_engine.name == 'postgres':
+ default_false = "false"
+ db_session.execute( "UPDATE history SET importing=%s" % default_false )
+ except Exception, e:
+ print str(e)
+ log.debug( "Adding column 'importing' to history table failed: %s" % str( e ) )
+
+ # Create job_import_history_archive table.
+ try:
+ JobImportHistoryArchive_table.create()
+ except Exception, e:
+ log.debug( "Creating job_import_history_archive table failed: %s" % str( e ) )
+
+def downgrade():
+ metadata.reflect()
+
+ # Drop 'importing' column from history table.
+ try:
+ History_table = Table( "history", metadata, autoload=True )
+ importing_col = History_table.c.importing
+ importing_col.drop()
+ except Exception, e:
+ log.debug( "Dropping column 'importing' from history table failed: %s" % ( str( e ) ) )
+
+ # Drop job_import_history_archive table.
+ try:
+ JobImportHistoryArchive_table.drop()
+ except Exception, e:
+ log.debug( "Dropping job_import_history_archive table failed: %s" % str( e ) )
--- /dev/null
+++ b/lib/galaxy/tools/imp_exp/unpack_tar_gz_archive.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+"""
+Unpack a tar or tar.gz archive into a directory.
+
+usage: %prog archive_source dest_dir
+ --[url|file] source type, either a URL or a file.
+"""
+
+import sys, optparse, tarfile, tempfile, urllib2, math
+
+# Set max size of archive/file that will be handled to be 100 GB. This is
+# arbitrary and should be adjusted as needed.
+MAX_SIZE = 100 * math.pow( 2, 30 )
+
+def url_to_file( url, dest_file ):
+ """
+ Transfer a file from a remote URL to a temporary file.
+ """
+ try:
+ url_reader = urllib2.urlopen( url )
+ CHUNK = 10 * 1024 # 10k
+ total = 0
+ fp = open( dest_file, 'wb')
+ while True:
+ chunk = url_reader.read( CHUNK )
+ if not chunk:
+ break
+ fp.write( chunk )
+ total += CHUNK
+ if total > MAX_SIZE:
+ break
+ fp.close()
+ return dest_file
+ except Exception, e:
+ print "Exception getting file from URL: %s" % e, sys.stderr
+ return None
+
+def unpack_archive( archive_file, dest_dir ):
+ """
+ Unpack a tar and/or gzipped archive into a destination directory.
+ """
+ archive_fp = tarfile.open( archive_file, mode='r:gz' )
+ archive_fp.extractall( path=dest_dir )
+ archive_fp.close()
+
+if __name__ == "__main__":
+ # Parse command line.
+ parser = optparse.OptionParser()
+ parser.add_option( '-U', '--url', dest='is_url', action="store_true", help='Source is a URL.' )
+ parser.add_option( '-F', '--file', dest='is_file', action="store_true", help='Source is a URL.' )
+ (options, args) = parser.parse_args()
+ is_url = bool( options.is_url )
+ is_file = bool( options.is_file )
+ archive_source, dest_dir = args
+
+ try:
+ # Get archive from URL.
+ if is_url:
+ archive_file = url_to_file( archive_source, tempfile.NamedTemporaryFile( dir=dest_dir ).name )
+ elif is_file:
+ archive_file = archive_source
+
+ # Unpack archive.
+ unpack_archive( archive_file, dest_dir )
+ except Exception, e:
+ print "Error unpacking tar/gz archive: %s" % e, sys.stderr
1
0
galaxy-dist commit d73fa5f14b01: fixed select all checkbox in grid. the footer now updates when all items are selected
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 rc
# Date 1288121253 14400
# Node ID d73fa5f14b013333b2e49bfbf775db8dd0197e2b
# Parent 314f8a5d38237c3f1fafccb522ee45aebfa8464f
fixed select all checkbox in grid. the footer now updates when all items are selected
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -611,34 +611,27 @@
});
}
- function check_all_items()
- {
+ function check_all_items() {
var chk_all = document.getElementById('check_all');
var checks = document.getElementsByTagName('input');
//var boxLength = checks.length;
var total = 0;
- if ( chk_all.checked == true )
- {
- for ( i=0; i < checks.length; i++ )
- {
- if ( checks[i].name.indexOf( 'id' ) != -1)
- {
+ if ( chk_all.checked == true ) {
+ for ( i=0; i < checks.length; i++ ) {
+ if ( checks[i].name.indexOf( 'id' ) != -1) {
checks[i].checked = true;
total++;
}
}
}
- else
- {
- for ( i=0; i < checks.length; i++ )
- {
- if ( checks[i].name.indexOf( 'id' ) != -1)
- {
+ else {
+ for ( i=0; i < checks.length; i++ ) {
+ if ( checks[i].name.indexOf( 'id' ) != -1) {
checks[i].checked = false
}
}
}
-
+ init_grid_elements();
}
</script></%def>
1
0
galaxy-dist commit 87a6d4a29eee: fixed mutation viz functional tests
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 rc
# Date 1288111817 14400
# Node ID 87a6d4a29eeea038c669f086d254aaa4140a15bb
# Parent e6e68c67dd9759154876546662f59ee280b78a34
fixed mutation viz functional tests
--- a/test-data/mutation_data1_zoom3x.svg
+++ b/test-data/mutation_data1_zoom3x.svg
@@ -5,21 +5,21 @@
<g id="viewport">
-<text y="4" x="12" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">A</tspan></text>
+<text y="3" x="12" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">A</tspan></text>
-<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="1" x="14" fill="blue" />
+<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="0" x="14" fill="blue" />
-<text y="4" x="22" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">C</tspan></text>
+<text y="3" x="22" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">C</tspan></text>
-<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="1" x="24" fill="green" />
+<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="0" x="24" fill="green" />
-<text y="4" x="32" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">G</tspan></text>
+<text y="3" x="32" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">G</tspan></text>
-<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="1" x="34" fill="orange" />
+<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="0" x="34" fill="orange" />
-<text y="4" x="42" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">T</tspan></text>
+<text y="3" x="42" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">T</tspan></text>
-<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="1" x="44" fill="red" />
+<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="0" x="44" fill="red" /><text y="25" x="23" stroke="none" transform="rotate(-90 23,25)" fill="black"><tspan style="font-family:Verdana;font-size:25%">s1</tspan></text>
--- a/test-data/mutation_data1_interactive.svg
+++ b/test-data/mutation_data1_interactive.svg
@@ -234,21 +234,21 @@ function handleMouseUp(evt) {
<g id="viewport">
-<text y="4" x="12" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">A</tspan></text>
+<text y="3" x="12" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">A</tspan></text>
-<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="1" x="14" fill="blue" />
+<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="0" x="14" fill="blue" />
-<text y="4" x="22" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">C</tspan></text>
+<text y="3" x="22" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">C</tspan></text>
-<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="1" x="24" fill="green" />
+<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="0" x="24" fill="green" />
-<text y="4" x="32" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">G</tspan></text>
+<text y="3" x="32" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">G</tspan></text>
-<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="1" x="34" fill="orange" />
+<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="0" x="34" fill="orange" />
-<text y="4" x="42" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">T</tspan></text>
+<text y="3" x="42" stroke="none" fill="black"><tspan style="font-family:Verdana;font-size:20%">T</tspan></text>
-<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="1" x="44" fill="red" />
+<rect fill-opacity="0.5" height="3" width="4" stroke="none" y="0" x="44" fill="red" /><text y="35" x="23" stroke="none" transform="rotate(-90 23,35)" fill="black"><tspan style="font-family:Verdana;font-size:25%">s1</tspan></text>
1
0
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1288101623 14400
# Node ID 677bcc65f74f5a460277f089d0cdebf170f02831
# Parent 9e27a8ad881dc4451add6d518468a73dbc9028bc
sample_tracking:
- added web api methods to update requests, samples using restful api
- removed the old ad-doc web api
- modified the data transfer code to use this api
- code cleanup
--- a/scripts/galaxy_messaging/server/galaxyweb_interface.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import ConfigParser
-import sys, os
-import array
-import time
-import optparse,array
-import shutil, traceback
-import urllib,urllib2, cookielib
-
-assert sys.version_info[:2] >= ( 2, 4 )
-new_path = [ os.path.join( os.getcwd(), "lib" ) ]
-new_path.extend( sys.path[1:] ) # remove scripts/ from the path
-sys.path = new_path
-
-from galaxy import eggs
-import pkg_resources
-
-import pkg_resources
-pkg_resources.require( "pycrypto" )
-
-from Crypto.Cipher import Blowfish
-from Crypto.Util.randpool import RandomPool
-from Crypto.Util import number
-
-
-class GalaxyWebInterface(object):
- def __init__(self, server_host, server_port, datatx_email, datatx_password, config_id_secret):
- self.server_host = server_host
- self.server_port = server_port
- self.datatx_email = datatx_email
- self.datatx_password = datatx_password
- self.config_id_secret = config_id_secret
- # create url
- self.base_url = "http://%s:%s" % (self.server_host, self.server_port)
- # login
- url = "%s/user/login?email=%s&password=%s&login_button=Login" % (self.base_url, self.datatx_email, self.datatx_password)
- cj = cookielib.CookieJar()
- self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
- #print url
- f = self.opener.open(url)
- if f.read().find("ogged in as "+self.datatx_email) == -1:
- # if the user doesnt exist, create the user
- url = "%s/user/create?email=%s&username=%s&password=%s&confirm=%s&create_user_button=Submit" % ( self.base_url, self.datatx_email, self.datatx_email, self.datatx_password, self.datatx_password )
- f = self.opener.open(url)
- if f.read().find("ogged in as "+self.datatx_email) == -1:
- raise Exception("The "+self.datatx_email+" user could not login to Galaxy")
-
- def add_to_library(self, server_dir, library_id, folder_id, dbkey=''):
- '''
- This method adds the dataset file to the target data library & folder
- by opening the corresponding url in Galaxy server running.
- '''
- params = urllib.urlencode(dict( cntrller='library_admin',
- tool_id='upload1',
- tool_state='None',
- library_id=self.encode_id(library_id),
- folder_id=self.encode_id(folder_id),
- upload_option='upload_directory',
- file_type='auto',
- server_dir=os.path.basename(server_dir),
- dbkey=dbkey,
- show_dataset_id='True',
- runtool_btn='Upload to library'))
- url = self.base_url+"/library_common/upload_library_dataset"
- print url
- print params
- try:
- f = self.opener.open(url, params)
- if f.read().find("Data Library") == -1:
- raise Exception("Dataset could not be uploaded to the data library. URL: %s, PARAMS=%s" % (url, params))
- except:
- return 'ERROR', url, params
-
- def import_to_history(self, ldda_id, library_id, folder_id):
- params = urllib.urlencode(dict( cntrller='library_admin',
- show_deleted='False',
- library_id=self.encode_id(library_id),
- folder_id=self.encode_id(folder_id),
- ldda_ids=self.encode_id(ldda_id),
- do_action='import_to_history',
- use_panels='False'))
- #url = "http://lion.bx.psu.edu:8080/library_common/act_on_multiple_datasets?library…"
- url = self.base_url+"/library_common/act_on_multiple_datasets"
- f = self.opener.open(url, params)
- x = f.read()
- if x.find("1 dataset(s) have been imported into your history.") == -1:
- raise Exception("Dataset could not be imported into history")
-
- def run_workflow(self, workflow_id, hid, workflow_step):
- input = str(workflow_step)+'|input'
- params = urllib.urlencode({'id':self.encode_id(workflow_id),
- 'run_workflow': 'Run workflow',
- input: hid})
- url = self.base_url+"/workflow/run"
- f = self.opener.open(url, params)
-
- def logout(self):
- # finally logout
- f = self.opener.open(self.base_url+'/user/logout')
-
- def encode_id(self, obj_id ):
- id_cipher = Blowfish.new( self.config_id_secret )
- # Convert to string
- s = str( obj_id )
- # Pad to a multiple of 8 with leading "!"
- s = ( "!" * ( 8 - len(s) % 8 ) ) + s
- # Encrypt
- return id_cipher.encrypt( s ).encode( 'hex' )
-
- def update_request_state(self, request_id):
- params = urllib.urlencode(dict( cntrller='requests_admin',
- request_id=request_id))
- url = self.base_url + "/requests_common/update_request_state"
- f = self.opener.open(url, params)
- print url
- print params
- x = f.read()
-
-
-
-
-
-
--- /dev/null
+++ b/scripts/api/requests_update_state.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+
+import os, sys, traceback
+sys.path.insert( 0, os.path.dirname( __file__ ) )
+from common import update
+
+try:
+ data = {}
+ data[ 'update_type' ] = 'request_state'
+except IndexError:
+ print 'usage: %s key url' % os.path.basename( sys.argv[0] )
+ sys.exit( 1 )
+
+update( sys.argv[1], sys.argv[2], data, return_formatted=True )
+
--- a/lib/galaxy/web/buildapp.py
+++ b/lib/galaxy/web/buildapp.py
@@ -106,6 +106,7 @@ def app_factory( global_conf, **kwargs )
add_api_controllers( webapp, app )
webapp.api_mapper.resource( 'content', 'contents', path_prefix='/api/libraries/:library_id', parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.api_mapper.resource( 'library', 'libraries', path_prefix='/api' )
+ webapp.api_mapper.resource( 'request', 'requests', path_prefix='/api' )
webapp.finalize_config()
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
--- /dev/null
+++ b/scripts/api/sample_dataset_update_status.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+
+import os, sys, traceback
+sys.path.insert( 0, os.path.dirname( __file__ ) )
+from common import display
+from common import submit
+from common import update
+try:
+ data = {}
+ data[ 'update_type' ] = 'sample_dataset_transfer_status'
+ data[ 'sample_dataset_ids' ] = sys.argv[3].split(',')
+ data[ 'new_status' ] = sys.argv[4]
+except IndexError:
+ print 'usage: %s key url sample_dataset_ids new_state [error msg]' % os.path.basename( sys.argv[0] )
+ sys.exit( 1 )
+try:
+ data[ 'error_msg' ] = sys.argv[5]
+except IndexError:
+ data[ 'error_msg' ] = ''
+print data
+update( sys.argv[1], sys.argv[2], data, return_formatted=True )
--- a/scripts/galaxy_messaging/server/amqp_consumer.py
+++ b/scripts/galaxy_messaging/server/amqp_consumer.py
@@ -14,24 +14,22 @@ import sys, os
import optparse
import xml.dom.minidom
import subprocess
+import urllib2
from galaxydb_interface import GalaxyDbInterface
-new_path = [ os.path.join( os.getcwd(), "scripts/galaxy_messaging/server" ) ]
-new_path.extend( sys.path[1:] ) # remove scripts/ from the path
-sys.path = new_path
-from galaxyweb_interface import GalaxyWebInterface
+api_path = [ os.path.join( os.getcwd(), "scripts/api" ) ]
+sys.path.extend( api_path )
+import common as api
assert sys.version_info[:2] >= ( 2, 4 )
new_path = [ os.path.join( os.getcwd(), "lib" ) ]
new_path.extend( sys.path[1:] ) # remove scripts/ from the path
sys.path = new_path
-
-
from galaxy import eggs
+from galaxy.web.api.requests import RequestsController
import pkg_resources
pkg_resources.require( "amqplib" )
-
from amqplib import client_0_8 as amqp
import logging
@@ -43,7 +41,12 @@ formatter = logging.Formatter("%(asctime
fh.setFormatter(formatter)
log.addHandler(fh)
+# data transfer script
+data_transfer_script = os.path.join( os.getcwd(),
+ "scripts/galaxy_messaging/server/data_transfer.py" )
+
global dbconnstr
+global webconfig
global config
def get_value(dom, tag_name):
@@ -71,84 +74,98 @@ def get_value_index(dom, tag_name, index
rc = rc + node.data
return rc
-def recv_callback(msg):
- global config
- global webconfig
+def start_data_transfer(message):
+ # fork a new process to transfer datasets
+ cmd = '%s "%s" "%s" "%s"' % ( "python",
+ data_transfer_script,
+ message.body,
+ sys.argv[1] ) # Galaxy config file name
+ pid = subprocess.Popen(cmd, shell=True).pid
+ log.debug('Started process (%i): %s' % (pid, str(cmd)))
+
+def update_sample_state(message):
+ dom = xml.dom.minidom.parseString(message.body)
+ barcode = get_value(dom, 'barcode')
+ state = get_value(dom, 'state')
+ log.debug('Barcode: ' + barcode)
+ log.debug('State: ' + state)
+ # update the sample state in Galaxy db
+ galaxydb = GalaxyDbInterface(dbconnstr)
+ sample_id = galaxydb.get_sample_id(field_name='bar_code', value=barcode)
+ if sample_id == -1:
+ log.debug('Invalid barcode.')
+ return
+ galaxydb.change_state(sample_id, state)
+ # after updating the sample state, update request status
+ request_id = galaxydb.get_request_id(sample_id)
+ update_request( request_id )
+
+def update_request( request_id ):
+ http_server_section = webconfig.get( "universe_wsgi_config", "http_server_section" )
+ encoded_request_id = api.encode_id( config.get( "app:main", "id_secret" ), request_id )
+ api_key = webconfig.get( "data_transfer_user_login_info", "api_key" )
+ data = dict( update_type=RequestsController.update_types.REQUEST )
+ url = "http://%s:%s/api/requests/%s" % ( config.get(http_server_section, "host"),
+ config.get(http_server_section, "port"),
+ encoded_request_id )
+ log.debug( 'Updating request %i' % request_id )
+ try:
+ api.update( api_key, url, data, return_formatted=False )
+ except urllib2.URLError, e:
+ log.debug( 'ERROR(update_request (%s)): %s' % ( str((self.api_key, url, data)), str(e) ) )
+
+def recv_callback(message):
# check the meesage type.
- msg_type = msg.properties['application_headers'].get('msg_type')
- log.debug('\nMESSAGE RECVD: '+str(msg_type))
+ msg_type = message.properties['application_headers'].get('msg_type')
+ log.debug( 'MESSAGE RECVD: ' + str( msg_type ) )
if msg_type == 'data_transfer':
- log.debug('DATA TRANSFER')
- # fork a new process to transfer datasets
- transfer_script = os.path.join(os.getcwd(),
- "scripts/galaxy_messaging/server/data_transfer.py")
- cmd = '%s "%s" "%s" "%s"' % ("python",
- transfer_script,
- msg.body,
- config.get("app:main", "id_secret") )
- pid = subprocess.Popen(cmd, shell=True).pid
- log.debug('Started process (%i): %s' % (pid, str(cmd)))
+ log.debug( 'DATA TRANSFER' )
+ start_data_transfer( message )
elif msg_type == 'sample_state_update':
- log.debug('SAMPLE STATE UPDATE')
- dom = xml.dom.minidom.parseString(msg.body)
- barcode = get_value(dom, 'barcode')
- state = get_value(dom, 'state')
- log.debug('Barcode: '+barcode)
- log.debug('State: '+state)
- # update the galaxy db
- galaxydb = GalaxyDbInterface(dbconnstr)
- sample_id = galaxydb.get_sample_id(field_name='bar_code', value=barcode)
- if sample_id == -1:
- log.debug('Invalid barcode.')
- return
- galaxydb.change_state(sample_id, state)
- # update the request state
- galaxyweb = GalaxyWebInterface(webconfig.get("universe_wsgi_config", "host"),
- webconfig.get("universe_wsgi_config", "port"),
- webconfig.get("data_transfer_user_login_info", "email"),
- webconfig.get("data_transfer_user_login_info", "password"),
- config.get("app:main", "id_secret"))
- galaxyweb.update_request_state(galaxydb.get_request_id(sample_id))
- galaxyweb.logout()
+ log.debug( 'SAMPLE STATE UPDATE' )
+ update_sample_state( message )
def main():
if len(sys.argv) < 2:
- print 'Usage: python amqp_consumer.py <Galaxy config file>'
+ print 'Usage: python amqp_consumer.py <Galaxy configuration file>'
return
global config
config = ConfigParser.ConfigParser()
- config.read(sys.argv[1])
+ config.read( sys.argv[1] )
global dbconnstr
dbconnstr = config.get("app:main", "database_connection")
amqp_config = {}
for option in config.options("galaxy_amqp"):
amqp_config[option] = config.get("galaxy_amqp", option)
- log.debug(str(amqp_config))
+ log.debug("PID: " + str(os.getpid()) + ", " + str(amqp_config))
# web server config
global webconfig
webconfig = ConfigParser.ConfigParser()
webconfig.read('transfer_datasets.ini')
-
-
-
-
-
+ # connect
conn = amqp.Connection(host=amqp_config['host']+":"+amqp_config['port'],
userid=amqp_config['userid'],
password=amqp_config['password'],
virtual_host=amqp_config['virtual_host'],
insist=False)
chan = conn.channel()
- chan.queue_declare(queue=amqp_config['queue'], durable=True, exclusive=True, auto_delete=False)
- chan.exchange_declare(exchange=amqp_config['exchange'], type="direct", durable=True, auto_delete=False,)
- chan.queue_bind(queue=amqp_config['queue'],
- exchange=amqp_config['exchange'],
- routing_key=amqp_config['routing_key'])
+ chan.queue_declare( queue=amqp_config['queue'],
+ durable=True,
+ exclusive=True,
+ auto_delete=False)
+ chan.exchange_declare( exchange=amqp_config['exchange'],
+ type="direct",
+ durable=True,
+ auto_delete=False,)
+ chan.queue_bind( queue=amqp_config['queue'],
+ exchange=amqp_config['exchange'],
+ routing_key=amqp_config['routing_key'])
- chan.basic_consume(queue=amqp_config['queue'],
- no_ack=True,
- callback=recv_callback,
- consumer_tag="testtag")
+ chan.basic_consume( queue=amqp_config['queue'],
+ no_ack=True,
+ callback=recv_callback,
+ consumer_tag="testtag")
+ log.debug('Connected to rabbitmq server - '+amqp_config['host']+":"+amqp_config['port'])
while True:
chan.wait()
chan.basic_cancel("testtag")
@@ -157,3 +174,5 @@ def main():
if __name__ == '__main__':
main()
+
+
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1584,6 +1584,7 @@ class Request( object ):
SUBMITTED = 'In Progress',
REJECTED = 'Rejected',
COMPLETE = 'Complete' )
+ api_collection_visible_keys = ( 'id', 'name', 'state' )
def __init__( self, name=None, desc=None, request_type=None, user=None, form_values=None, notification=None ):
self.name = name
self.desc = desc
@@ -1663,7 +1664,7 @@ class Request( object ):
# Send email
if trans.app.config.smtp_server is not None and self.notification and self.notification[ 'email' ]:
host = trans.request.host.split( ':' )[0]
- if host in [ 'localhost', '127.0.0.1' ]:
+ if host in [ 'localhost', '127.0.0.1', '0.0.0.0' ]:
host = socket.getfqdn()
body = """
Galaxy Sample Tracking Notification
@@ -1704,8 +1705,8 @@ All samples in state: %(sample_state
s.sendmail( frm, to, message )
s.quit()
comments = "Email notification sent to %s." % ", ".join( to ).strip().strip( ',' )
- except:
- comments = "Email notification failed."
+ except Exception,e:
+ comments = "Email notification failed. (%s)" % str(e)
# update the request history with the email notification event
elif not trans.app.config.smtp_server:
comments = "Email notification failed as SMTP server not set in config file"
@@ -1714,6 +1715,18 @@ All samples in state: %(sample_state
trans.sa_session.add( event )
trans.sa_session.flush()
return comments
+ def get_api_value( self, view='collection' ):
+ rval = {}
+ try:
+ visible_keys = self.__getattribute__( 'api_' + view + '_visible_keys' )
+ except AttributeError:
+ raise Exception( 'Unknown API view: %s' % view )
+ for key in visible_keys:
+ try:
+ rval[key] = self.__getattribute__( key )
+ except AttributeError:
+ rval[key] = None
+ return rval
class RequestEvent( object ):
def __init__(self, request=None, request_state=None, comment=''):
@@ -1734,7 +1747,7 @@ class RequestType( object ):
self.sample_form = sample_form
self.datatx_info = datatx_info
@property
- def state( self ):
+ def final_sample_state( self ):
# The states mapper for this object orders ascending
return self.states[-1]
@@ -1747,12 +1760,6 @@ class RequestTypePermissions( object ):
class Sample( object ):
bulk_operations = Bunch( CHANGE_STATE = 'Change state',
SELECT_LIBRARY = 'Select data library and folder' )
- transfer_status = Bunch( NOT_STARTED = 'Not started',
- IN_QUEUE = 'In queue',
- TRANSFERRING = 'Transferring dataset',
- ADD_TO_LIBRARY = 'Adding to data library',
- COMPLETE = 'Complete',
- ERROR = 'Error' )
def __init__(self, name=None, desc=None, request=None, form_values=None, bar_code=None, library=None, folder=None):
self.name = name
self.desc = desc
@@ -1776,21 +1783,21 @@ class Sample( object ):
def untransferred_dataset_files( self ):
untransferred_datasets = []
for dataset in self.datasets:
- if dataset.status == self.transfer_status.NOT_STARTED:
+ if dataset.status == SampleDataset.transfer_status.NOT_STARTED:
untransferred_datasets.append( dataset )
return untransferred_datasets
@property
def inprogress_dataset_files( self ):
inprogress_datasets = []
for dataset in self.datasets:
- if dataset.status not in [ self.transfer_status.NOT_STARTED, self.transfer_status.COMPLETE ]:
+ if dataset.status not in [ SampleDataset.transfer_status.NOT_STARTED, SampleDataset.transfer_status.COMPLETE ]:
inprogress_datasets.append( dataset )
return inprogress_datasets
@property
def transferred_dataset_files( self ):
transferred_datasets = []
for dataset in self.datasets:
- if dataset.status == self.transfer_status.COMPLETE:
+ if dataset.status == SampleDataset.transfer_status.COMPLETE:
transferred_datasets.append( dataset )
return transferred_datasets
def dataset_size( self, filepath ):
@@ -1818,6 +1825,12 @@ class SampleEvent( object ):
self.comment = comment
class SampleDataset( object ):
+ transfer_status = Bunch( NOT_STARTED = 'Not started',
+ IN_QUEUE = 'In queue',
+ TRANSFERRING = 'Transferring dataset',
+ ADD_TO_LIBRARY = 'Adding to data library',
+ COMPLETE = 'Complete',
+ ERROR = 'Error' )
def __init__(self, sample=None, name=None, file_path=None,
status=None, error_msg=None, size=None):
self.sample = sample
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -275,7 +275,7 @@ class RequestsCommon( BaseController, Us
request_type = request.type
name = util.restore_text( params.get( 'name', '' ) )
desc = util.restore_text( params.get( 'desc', '' ) )
- notification = dict( email=[ user.email ], sample_states=[ request_type.state.id ], body='', subject='' )
+ notification = dict( email=[ user.email ], sample_states=[ request_type.final_sample_state.id ], body='', subject='' )
values = []
for index, field in enumerate( request_type.request_form.fields ):
field_type = field[ 'type' ]
@@ -416,11 +416,7 @@ class RequestsCommon( BaseController, Us
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 ) )
- for sample_id in selected_samples:
- sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
- event = trans.model.SampleEvent( sample, new_state, sample_event_comment )
- trans.sa_session.add( event )
- trans.sa_session.flush()
+ 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',
@@ -486,6 +482,22 @@ class RequestsCommon( BaseController, Us
status=status,
message=message )
@web.expose
+ def update_sample_state(self, trans, cntrller, sample_ids, new_state, comment=None ):
+ for sample_id in sample_ids:
+ try:
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
+ except:
+ if cntrller == 'api':
+ trans.response.status = 400
+ return "Malformed 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 )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ if cntrller == 'api':
+ return 200, 'Done'
+ @web.expose
@web.require_login( "delete sequencing requests" )
def delete_request( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
@@ -640,6 +652,8 @@ class RequestsCommon( BaseController, Us
event = trans.model.RequestEvent( request, request.states.SUBMITTED, message )
trans.sa_session.add( event )
trans.sa_session.flush()
+ if cntrller == 'api':
+ return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='manage_request',
cntrller=cntrller,
@@ -647,7 +661,7 @@ class RequestsCommon( BaseController, Us
status=status,
message=message ) )
final_state = False
- request_type_state = request.type.state
+ request_type_state = request.type.final_sample_state
if common_state.id == request_type_state.id:
# since all the samples are in the final state, change the request state to 'Complete'
comments = "All samples of this request are in the last sample state (%s). " % request_type_state.name
@@ -656,7 +670,7 @@ class RequestsCommon( BaseController, Us
else:
comments = "All samples are in %s state. " % common_state.name
state = request.states.SUBMITTED
- event = trans.model.RequestEvent(request, state, comments)
+ 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
@@ -666,6 +680,8 @@ class RequestsCommon( BaseController, Us
message = comments + retval
else:
message = comments
+ if cntrller == 'api':
+ return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='manage_request',
cntrller=cntrller,
--- a/scripts/galaxy_messaging/server/data_transfer.py
+++ b/scripts/galaxy_messaging/server/data_transfer.py
@@ -19,25 +19,26 @@ import urllib,urllib2, cookielib, shutil
import logging, time, datetime
import xml.dom.minidom
-sp = sys.path[0]
+log = logging.getLogger("datatx_"+str(os.getpid()))
+log.setLevel(logging.DEBUG)
+fh = logging.FileHandler("data_transfer.log")
+fh.setLevel(logging.DEBUG)
+formatter = logging.Formatter("%(asctime)s - %(name)s - %(message)s")
+fh.setFormatter(formatter)
+log.addHandler(fh)
-from galaxydb_interface import GalaxyDbInterface
-
-assert sys.version_info[:2] >= ( 2, 4 )
-new_path = [ sp ]
-new_path.extend( sys.path )
-sys.path = new_path
-
-from galaxyweb_interface import GalaxyWebInterface
+api_path = [ os.path.join( os.getcwd(), "scripts/api" ) ]
+sys.path.extend( api_path )
+import common as api
assert sys.version_info[:2] >= ( 2, 4 )
new_path = [ os.path.join( os.getcwd(), "lib" ) ]
new_path.extend( sys.path[1:] ) # remove scripts/ from the path
sys.path = new_path
-
from galaxy.util.json import from_json_string, to_json_string
-from galaxy.model import Sample
+from galaxy.model import SampleDataset
+from galaxy.web.api.requests import RequestsController
from galaxy import eggs
import pkg_resources
pkg_resources.require( "pexpect" )
@@ -46,28 +47,19 @@ import pexpect
pkg_resources.require( "simplejson" )
import simplejson
-log = logging.getLogger("datatx_"+str(os.getpid()))
-log.setLevel(logging.DEBUG)
-fh = logging.FileHandler("data_transfer.log")
-fh.setLevel(logging.DEBUG)
-formatter = logging.Formatter("%(asctime)s - %(name)s - %(message)s")
-fh.setFormatter(formatter)
-log.addHandler(fh)
-
-
class DataTransfer(object):
- def __init__(self, msg, config_id_secret):
+ def __init__(self, msg, config_file):
log.info(msg)
self.dom = xml.dom.minidom.parseString(msg)
- self.host = self.get_value(self.dom, 'data_host')
- self.username = self.get_value(self.dom, 'data_user')
- self.password = self.get_value(self.dom, 'data_password')
+ self.sequencer_host = self.get_value(self.dom, 'data_host')
+ self.sequencer_username = self.get_value(self.dom, 'data_user')
+ self.sequencer_password = self.get_value(self.dom, 'data_password')
+ self.request_id = self.get_value(self.dom, 'request_id')
self.sample_id = self.get_value(self.dom, 'sample_id')
self.library_id = self.get_value(self.dom, 'library_id')
self.folder_id = self.get_value(self.dom, 'folder_id')
self.dataset_files = []
- self.config_id_secret = config_id_secret
count=0
while True:
dataset_id = self.get_value_index(self.dom, 'dataset_id', count)
@@ -82,21 +74,25 @@ class DataTransfer(object):
count=count+1
try:
# Retrieve the upload user login information from the config file
+ transfer_datasets_config = ConfigParser.ConfigParser()
+ transfer_datasets_config.read('transfer_datasets.ini')
+ self.data_transfer_user_email = transfer_datasets_config.get("data_transfer_user_login_info", "email")
+ self.data_transfer_user_password = transfer_datasets_config.get("data_transfer_user_login_info", "password")
+ self.api_key = transfer_datasets_config.get("data_transfer_user_login_info", "api_key")
+ self.http_server_section = transfer_datasets_config.get("universe_wsgi_config", "http_server_section")
config = ConfigParser.ConfigParser()
- config.read('transfer_datasets.ini')
- self.datatx_email = config.get("data_transfer_user_login_info", "email")
- self.datatx_password = config.get("data_transfer_user_login_info", "password")
- self.server_host = config.get("universe_wsgi_config", "host")
- self.server_port = config.get("universe_wsgi_config", "port")
- self.database_connection = config.get("universe_wsgi_config", "database_connection")
- self.import_dir = config.get("universe_wsgi_config", "library_import_dir")
+ config.read( config_file )
+ self.server_host = config.get(self.http_server_section, "host")
+ self.server_port = config.get(self.http_server_section, "port")
+ self.database_connection = config.get("app:main", "database_connection")
+ self.import_dir = config.get("app:main", "library_import_dir")
+ self.config_id_secret = config.get("app:main", "id_secret")
+
# create the destination directory within the import directory
self.server_dir = os.path.join( self.import_dir, 'datatx_'+str(os.getpid())+'_'+datetime.date.today().strftime("%d%b%Y") )
os.mkdir(self.server_dir)
if not os.path.exists(self.server_dir):
raise Exception
- # connect to db
- self.galaxydb = GalaxyDbInterface(self.database_connection)
except:
log.error(traceback.format_exc())
log.error('FATAL ERROR')
@@ -114,7 +110,7 @@ class DataTransfer(object):
# add the dataset to the given library
self.add_to_library()
# update the data transfer status in the db
- self.update_status(Sample.transfer_status.COMPLETE)
+ self.update_status(SampleDataset.transfer_status.COMPLETE)
# cleanup
#self.cleanup()
sys.exit(0)
@@ -149,15 +145,15 @@ class DataTransfer(object):
def print_ticks(d):
pass
for i, df in enumerate(self.dataset_files):
- self.update_status(Sample.transfer_status.TRANSFERRING, df['dataset_id'])
+ self.update_status(SampleDataset.transfer_status.TRANSFERRING, df['dataset_id'])
try:
- cmd = "scp %s@%s:'%s' '%s/%s'" % ( self.username,
- self.host,
- df['file'].replace(' ', '\ '),
- self.server_dir.replace(' ', '\ '),
- df['name'].replace(' ', '\ '))
+ cmd = "scp %s@%s:'%s' '%s/%s'" % ( self.sequencer_username,
+ self.sequencer_host,
+ df['file'].replace(' ', '\ '),
+ self.server_dir.replace(' ', '\ '),
+ df['name'].replace(' ', '\ ') )
log.debug(cmd)
- output = pexpect.run(cmd, events={'.ssword:*': self.password+'\r\n',
+ output = pexpect.run(cmd, events={'.ssword:*': self.sequencer_password+'\r\n',
pexpect.TIMEOUT:print_ticks},
timeout=10)
log.debug(output)
@@ -176,17 +172,21 @@ class DataTransfer(object):
This method adds the dataset file to the target data library & folder
by opening the corresponding url in Galaxy server running.
'''
+ self.update_status(SampleDataset.transfer_status.ADD_TO_LIBRARY)
try:
- self.update_status(Sample.transfer_status.ADD_TO_LIBRARY)
- log.debug("dir:%s, lib:%s, folder:%s" % (self.server_dir, str(self.library_id), str(self.folder_id)))
- galaxyweb = GalaxyWebInterface(self.server_host, self.server_port,
- self.datatx_email, self.datatx_password,
- self.config_id_secret)
- retval = galaxyweb.add_to_library(self.server_dir, self.library_id, self.folder_id)
- log.debug(str(retval))
- galaxyweb.logout()
- except Exception, e:
- log.debug(e)
+ data = {}
+ data[ 'folder_id' ] = api.encode_id( self.config_id_secret, '%s.%s' % ( 'folder', self.folder_id ) )
+ data[ 'file_type' ] = 'auto'
+ data[ 'server_dir' ] = self.server_dir
+ data[ 'dbkey' ] = ''
+ data[ 'upload_option' ] = 'upload_directory'
+ data[ 'create_type' ] = 'file'
+ url = "http://%s:%s/api/libraries/%s/contents" % ( self.server_host,
+ self.server_port,
+ api.encode_id( self.config_id_secret, self.library_id ) )
+ log.debug(str((self.api_key, url, data)))
+ retval = api.submit( self.api_key, url, data, return_formatted=False )
+ except:
self.error_and_exit(str(e))
def update_status(self, status, dataset_id='All', msg=''):
@@ -195,12 +195,27 @@ class DataTransfer(object):
'''
try:
log.debug('Setting status "%s" for dataset "%s" of sample "%s"' % ( status, str(dataset_id), str(self.sample_id) ) )
+ sample_dataset_ids = []
if dataset_id == 'All':
for dataset in self.dataset_files:
- self.galaxydb.set_sample_dataset_status(dataset['dataset_id'], status, msg)
+ sample_dataset_ids.append( api.encode_id( self.config_id_secret, dataset['dataset_id'] ) )
else:
- self.galaxydb.set_sample_dataset_status(dataset_id, status, msg)
+ sample_dataset_ids.append( api.encode_id( self.config_id_secret, dataset_id ) )
+ # update the transfer status
+ data = {}
+ data[ 'update_type' ] = RequestsController.update_types.SAMPLE_DATASET
+ data[ 'sample_dataset_ids' ] = sample_dataset_ids
+ data[ 'new_status' ] = status
+ data[ 'error_msg' ] = msg
+ url = "http://%s:%s/api/requests/%s" % ( self.server_host,
+ self.server_port,
+ api.encode_id( self.config_id_secret, self.request_id ) )
+ log.debug(str((self.api_key, url, data)))
+ retval = api.update( self.api_key, url, data, return_formatted=False )
log.debug('done.')
+ except urllib2.URLError, e:
+ log.debug( 'ERROR(sample_dataset_transfer_status (%s)): %s' % ( url, str(e) ) )
+ log.error(traceback.format_exc())
except:
log.error(traceback.format_exc())
log.error('FATAL ERROR')
--- a/templates/admin/requests/rename_datasets.mako
+++ b/templates/admin/requests/rename_datasets.mako
@@ -32,7 +32,7 @@
<tbody>
%for id in id_list:
<% sample_dataset = trans.sa_session.query( trans.model.SampleDataset ).get( trans.security.decode_id( id ) ) %>
- %if sample_dataset.status == trans.app.model.Sample.transfer_status.NOT_STARTED:
+ %if sample_dataset.status == trans.app.model.SampleDataset.transfer_status.NOT_STARTED:
<tr><td><% rename_datasets_for_sample_select_field = build_rename_datasets_for_sample_select_field( trans, sample_dataset ) %>
--- a/scripts/api/common.py
+++ b/scripts/api/common.py
@@ -10,6 +10,11 @@ import pkg_resources
pkg_resources.require( "simplejson" )
import simplejson
+pkg_resources.require( "pycrypto" )
+from Crypto.Cipher import Blowfish
+from Crypto.Util.randpool import RandomPool
+from Crypto.Util import number
+
def make_url( api_key, url, args=None ):
# Adds the API Key to the URL if it's not already there.
if args is None:
@@ -36,7 +41,15 @@ def post( api_key, url, data ):
req = urllib2.Request( url, headers = { 'Content-Type': 'application/json' }, data = simplejson.dumps( data ) )
return simplejson.loads( urllib2.urlopen( req ).read() )
-def display( api_key, url ):
+def put( api_key, url, data ):
+ # Do the actual PUT
+ url = make_url( api_key, url )
+ req = urllib2.Request( url, headers = { 'Content-Type': 'application/json' }, data = simplejson.dumps( data ))
+ req.get_method = lambda: 'PUT'
+ return simplejson.loads( urllib2.urlopen( req ).read() )
+
+
+def display( api_key, url, return_formatted=True ):
# Sends an API GET request and acts as a generic formatter for the JSON response.
try:
r = get( api_key, url )
@@ -47,6 +60,8 @@ def display( api_key, url ):
if type( r ) == unicode:
print 'error: %s' % r
return None
+ if not return_formatted:
+ return r
elif type( r ) == list:
# Response is a collection as defined in the REST style.
print 'Collection Members'
@@ -68,7 +83,7 @@ def display( api_key, url ):
else:
print 'response is unknown type: %s' % type( r )
-def submit( api_key, url, data ):
+def submit( api_key, url, data, return_formatted=True ):
# Sends an API POST request and acts as a generic formatter for the JSON response.
# 'data' will become the JSON payload read by Galaxy.
try:
@@ -77,6 +92,8 @@ def submit( api_key, url, data ):
print e
print e.read( 1024 )
sys.exit( 1 )
+ if not return_formatted:
+ return r
print 'Response'
print '--------'
if type( r ) == list:
@@ -96,3 +113,45 @@ def submit( api_key, url, data ):
print i
else:
print r
+
+def update( api_key, url, data, return_formatted=True ):
+ # Sends an API PUT request and acts as a generic formatter for the JSON response.
+ # 'data' will become the JSON payload read by Galaxy.
+ try:
+ r = put( api_key, url, data )
+ except urllib2.HTTPError, e:
+ print e
+ print e.read( 1024 )
+ sys.exit( 1 )
+ if not return_formatted:
+ return r
+ print 'Response'
+ print '--------'
+ if type( r ) == list:
+ # Currently the only implemented responses are lists of dicts, because
+ # submission creates some number of collection elements.
+ for i in r:
+ if type( i ) == dict:
+ if 'url' in i:
+ print i.pop( 'url' )
+ else:
+ print '----'
+ if 'name' in i:
+ print ' name: %s' % i.pop( 'name' )
+ for k, v in i.items():
+ print ' %s: %s' % ( k, v )
+ else:
+ print i
+ else:
+ print r
+
+# utility method to encode ID's
+def encode_id( config_id_secret, obj_id ):
+ id_cipher = Blowfish.new( config_id_secret )
+ # Convert to string
+ s = str( obj_id )
+ # Pad to a multiple of 8 with leading "!"
+ s = ( "!" * ( 8 - len(s) % 8 ) ) + s
+ # Encrypt
+ return id_cipher.encrypt( s ).encode( 'hex' )
+
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -129,9 +129,9 @@ class DataTransferGrid( grids.Grid ):
visible=False,
filterable="standard" ) )
operations = [
- grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
- grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
- grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
+ 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 ] ) ),
]
def apply_query_filter( self, trans, query, **kwd ):
sample_id = kwd.get( 'sample_id', None )
@@ -506,7 +506,7 @@ class RequestsAdmin( BaseController, Use
name = self.__dataset_name( sample, filepath.split( '/' )[-1] )
sample_dataset = trans.model.SampleDataset( sample=sample,
file_path=filepath,
- status=sample.transfer_status.NOT_STARTED,
+ status=trans.app.model.SampleDataset.transfer_status.NOT_STARTED,
name=name,
error_msg='',
size=sample.dataset_size( filepath ) )
@@ -575,6 +575,7 @@ class RequestsAdmin( BaseController, Use
<data_host>%(DATA_HOST)s</data_host><data_user>%(DATA_USER)s</data_user><data_password>%(DATA_PASSWORD)s</data_password>
+ <request_id>%(REQUEST_ID)s</request_id><sample_id>%(SAMPLE_ID)s</sample_id><library_id>%(LIBRARY_ID)s</library_id><folder_id>%(FOLDER_ID)s</folder_id>
@@ -588,16 +589,17 @@ class RequestsAdmin( BaseController, Use
</dataset>'''
datasets = ''
for sample_dataset in selected_sample_datasets:
- if sample_dataset.status == sample.transfer_status.NOT_STARTED:
+ if sample_dataset.status == trans.app.model.SampleDataset.transfer_status.NOT_STARTED:
datasets = datasets + dataset_xml % dict( ID=str( sample_dataset.id ),
NAME=sample_dataset.name,
FILE=sample_dataset.file_path )
- sample_dataset.status = sample.transfer_status.IN_QUEUE
+ sample_dataset.status = trans.app.model.SampleDataset.transfer_status.IN_QUEUE
trans.sa_session.add( sample_dataset )
trans.sa_session.flush()
data = xml % dict( DATA_HOST=datatx_info['host'],
DATA_USER=datatx_info['username'],
DATA_PASSWORD=datatx_info['password'],
+ REQUEST_ID=str(sample.request.id),
SAMPLE_ID=str(sample.id),
LIBRARY_ID=str(sample.library.id),
FOLDER_ID=str(sample.folder.id),
@@ -644,6 +646,25 @@ class RequestsAdmin( BaseController, Use
sample_id=trans.security.encode_id( sample.id ),
status=status,
message=message) )
+ @web.expose
+ def update_sample_dataset_status(self, trans, cntrller, sample_dataset_ids, new_status, error_msg=None ):
+ # check if the new status is a valid transfer status
+ possible_status_list = [v[1] for v in trans.app.model.SampleDataset.transfer_status.items()]
+ if new_status not in possible_status_list:
+ trans.response.status = 400
+ return "The requested transfer status ( %s ) is not a valid transfer status." % new_status
+ for id in util.listify( sample_dataset_ids ):
+ try:
+ sd_id = trans.security.decode_id( id )
+ sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( sd_id )
+ except:
+ trans.response.status = 400
+ return "Invalid sample dataset id ( %s ) specified." % str( id )
+ sample_dataset.status = new_status
+ sample_dataset.error_msg = error_msg
+ trans.sa_session.add( sample_dataset )
+ trans.sa_session.flush()
+ return 200, 'Done'
# Request Type Stuff
@web.expose
@web.require_admin
--- a/scripts/galaxy_messaging/server/galaxydb_interface.py
+++ b/scripts/galaxy_messaging/server/galaxydb_interface.py
@@ -28,7 +28,7 @@ class GalaxyDbInterface(object):
def __init__(self, dbstr):
self.dbstr = dbstr
self.db_engine = create_engine(self.dbstr)
- self.db_engine.echo = True
+ self.db_engine.echo = False
self.metadata = MetaData(self.db_engine)
self.session = sessionmaker(bind=self.db_engine)
self.event_table = Table('sample_event', self.metadata, autoload=True )
--- /dev/null
+++ b/scripts/api/sample_update_state.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+
+import os, sys, traceback
+sys.path.insert( 0, os.path.dirname( __file__ ) )
+from common import display
+from common import submit
+from common import update
+
+try:
+ data = {}
+ data[ 'update_type' ] = 'sample_state'
+ data[ 'sample_ids' ] = sys.argv[3].split(',')
+ data[ 'new_state' ] = sys.argv[4]
+except IndexError:
+ print 'usage: %s key url sample_ids new_state [comment]' % os.path.basename( sys.argv[0] )
+ sys.exit( 1 )
+try:
+ data[ 'comment' ] = sys.argv[5]
+except IndexError:
+ data[ 'comment' ] = ''
+
+update( sys.argv[1], sys.argv[2], data, return_formatted=True )
--- /dev/null
+++ b/lib/galaxy/web/api/requests.py
@@ -0,0 +1,130 @@
+"""
+API operations on a sample tracking system.
+"""
+import logging, os, string, shutil, urllib, re, socket
+from cgi import escape, FieldStorage
+from galaxy import util, datatypes, jobs, web, util
+from galaxy.web.base.controller import *
+from galaxy.util.sanitize_html import sanitize_html
+from galaxy.model.orm import *
+from galaxy.util.bunch import Bunch
+
+log = logging.getLogger( __name__ )
+
+class RequestsController( BaseController ):
+ update_types = Bunch( REQUEST = 'request_state',
+ SAMPLE = 'sample_state',
+ SAMPLE_DATASET = 'sample_dataset_transfer_status' )
+ update_type_values = [v[1] for v in update_types.items()]
+ @web.expose_api
+ def index( self, trans, **kwd ):
+ """
+ GET /api/requests
+ Displays a collection (list) of sequencing requests.
+ """
+ query = trans.sa_session.query( trans.app.model.Request )\
+ .filter( and_( trans.app.model.Request.table.c.user_id == trans.user.id \
+ and trans.app.model.Request.table.c.deleted == False ) ) \
+ .all()
+ rval = []
+ for request in query:
+ item = request.get_api_value()
+ item['url'] = url_for( 'requests', id=trans.security.encode_id( request.id ) )
+ item['id'] = trans.security.encode_id( item['id'] )
+ rval.append( item )
+ return rval
+
+ @web.expose_api
+ def update( self, trans, id, key, payload, **kwd ):
+ """
+ PUT /api/requests/{encoded_request_id}
+ Displays information about a sequencing request.
+ """
+ params = util.Params( kwd )
+ update_type = None
+ if 'update_type' not in payload:
+ trans.response.status = 400
+ return "Missing required 'update_type' parameter. Please consult the API documentation for help."
+ else:
+ update_type = payload.pop( 'update_type' )
+ if update_type not in self.update_type_values:
+ trans.response.status = 400
+ return "Invalid value for 'update_type' parameter ( %s ) specified. Please consult the API documentation for help." % update_type
+ try:
+ request_id = trans.security.decode_id( id )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed %s id ( %s ) specified, unable to decode." % ( update_type, str( id ) )
+ try:
+ request = trans.sa_session.query( trans.app.model.Request ).get( request_id )
+ except:
+ request = None
+ if not request or not ( trans.user_is_admin() or request.user.id == trans.user.id ):
+ trans.response.status = 400
+ return "Invalid request id ( %s ) specified." % str( request_id )
+ # check update type
+ if update_type == 'request_state':
+ return self.__update_request_state( trans, encoded_request_id=id )
+ elif update_type == 'sample_state':
+ return self.__update_sample_state( trans, request.type, **payload )
+ elif update_type == 'sample_dataset_transfer_status':
+ # update sample_dataset transfer status
+ return self.__update_sample_dataset_status( trans, **payload )
+
+
+ def __update_request_state( self, trans, encoded_request_id ):
+ requests_common_cntrller = trans.webapp.controllers['requests_common']
+ status, output = requests_common_cntrller.update_request_state( trans,
+ cntrller='api',
+ request_id=encoded_request_id )
+ return status, output
+ def __update_sample_state( self, trans, request_type, **payload ):
+ # only admin user may update sample state in Galaxy sample tracking
+ if not trans.user_is_admin():
+ trans.response.status = 400
+ return "only an admin user may update sample state in Galaxy sample tracking."
+ if 'sample_ids' not in payload or 'new_state' not in payload:
+ trans.response.status = 400
+ return "Missing one or more required parameters: 'sample_ids' and 'new_state'."
+ sample_ids = payload.pop( 'sample_ids' )
+ new_state_name = payload.pop( 'new_state' )
+ comment = payload.get( 'comment', '' )
+ # check if the new state is a valid sample state
+ possible_states = request_type.states
+ new_state = None
+ for state in possible_states:
+ if state.name == new_state_name:
+ new_state = state
+ if not new_state:
+ trans.response.status = 400
+ return "Invalid sample state requested ( %s )." % new_state
+ requests_common_cntrller = trans.webapp.controllers['requests_common']
+ status, output = requests_common_cntrller.update_sample_state( trans,
+ cntrller='api',
+ sample_ids=sample_ids,
+ new_state=new_state,
+ comment=comment )
+ return status, output
+
+ def __update_sample_dataset_status( self, trans, **payload ):
+ # only admin user may transfer sample datasets in Galaxy sample tracking
+ if not trans.user_is_admin():
+ trans.response.status = 400
+ return "Only an admin user may transfer sample datasets in Galaxy sample tracking and thus update transfer status."
+ if 'sample_dataset_ids' not in payload or 'new_status' not in payload:
+ trans.response.status = 400
+ return "Missing one or more required parameters: 'sample_dataset_ids' and 'new_status'."
+ sample_dataset_ids = payload.pop( 'sample_dataset_ids' )
+ new_status = payload.pop( 'new_status' )
+ error_msg = payload.get( 'error_msg', '' )
+ requests_admin_cntrller = trans.webapp.controllers['requests_admin']
+ status, output = requests_admin_cntrller.update_sample_dataset_status( trans,
+ cntrller='api',
+ sample_dataset_ids=sample_dataset_ids,
+ new_status=new_status,
+ error_msg=error_msg )
+ return status, output
+
+
+
+
--- a/templates/admin/requests/dataset.mako
+++ b/templates/admin/requests/dataset.mako
@@ -58,7 +58,7 @@
<label>Transfer status:</label><div style="float: left; width: 250px; margin-right: 10px;">
${sample_dataset.status}
- %if sample_dataset.status == sample.transfer_status.ERROR:
+ %if sample_dataset.status == trans.app.model.SampleDataset.transfer_status.ERROR:
<br/>
${sample_dataset.error_msg}
%endif
--- a/scripts/api/display.py
+++ b/scripts/api/display.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-import os, sys
+import os, sys, urllib2
sys.path.insert( 0, os.path.dirname( __file__ ) )
from common import display
@@ -9,3 +9,6 @@ try:
except TypeError:
print 'usage: %s key url' % os.path.basename( sys.argv[0] )
sys.exit( 1 )
+except urllib2.URLError, e:
+ print str(e)
+ sys.exit( 1 )
1
0