lists.galaxyproject.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2024
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
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008
December
November
October
September
August
List overview
Download
galaxy-dev
November 2009
----- 2024 -----
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
galaxy-dev@lists.galaxyproject.org
26 participants
233 discussions
Start a n
N
ew thread
[hg] galaxy 2957: AMQP messaging server and client files.
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/5b2d593d9aed
changeset: 2957:5b2d593d9aed user: rc date: Wed Nov 04 10:04:51 2009 -0500 description: AMQP messaging server and client files. diffstat: scripts/galaxy_messaging/amqp_consumer.py | 94 ------------- scripts/galaxy_messaging/client/amqp_publisher.py | 87 ++++++++++++ scripts/galaxy_messaging/client/galaxy_amq.ini.sample | 32 ++++ scripts/galaxy_messaging/client/report.bat.sample | 1 + scripts/galaxy_messaging/client/scan.bat.sample | 1 + scripts/galaxy_messaging/client/scan.sh.sample | 1 + scripts/galaxy_messaging/client/scanner.py | 92 +++++++++++++ scripts/galaxy_messaging/client/scanner_interface.py | 76 ++++++++++ scripts/galaxy_messaging/galaxydb_interface.py | 151 --------------------- scripts/galaxy_messaging/server/amqp_consumer.py | 94 +++++++++++++ scripts/galaxy_messaging/server/galaxydb_interface.py | 149 +++++++++++++++++++++ 11 files changed, 533 insertions(+), 245 deletions(-) diffs (829 lines): diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/amqp_consumer.py --- a/scripts/galaxy_messaging/amqp_consumer.py Wed Nov 04 09:32:09 2009 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -''' -Galaxy Messaging with AMQP (RabbitMQ) -Galaxy uses AMQ protocol to receive messages from external sources like -bar code scanners. Galaxy has been tested against RabbitMQ AMQP implementation. -For Galaxy to receive messages from a message queue the RabbitMQ server has -to be set up with a user account and other parameters listed in the [galaxy:amq] -section in the universe_wsgi.ini config file -Once the RabbitMQ server has been setup and started with the given parameters, -this script can be run to receive messages and update the Galaxy database accordingly -''' - -import ConfigParser -import sys, os -import optparse -import xml.dom.minidom -from galaxydb_interface import GalaxyDbInterface - -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 -pkg_resources.require( "amqplib" ) - -from amqplib import client_0_8 as amqp - -import logging -logging.basicConfig(level=logging.DEBUG) -log = logging.getLogger( 'GalaxyAMQP' ) - - -galaxy_config_file = 'universe_wsgi.ini' -global dbconnstr - -def get_value(dom, tag_name): - ''' - This method extracts the tag value from the xml message - ''' - nodelist = dom.getElementsByTagName(tag_name)[0].childNodes - rc = "" - for node in nodelist: - if node.nodeType == node.TEXT_NODE: - rc = rc + node.data - return rc - -def recv_callback(msg): - 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 - galaxy = GalaxyDbInterface(dbconnstr) - sample_id = galaxy.get_sample_id(field_name='bar_code', value=barcode) - if sample_id == -1: - log.debug('Invalid barcode.') - return - galaxy.change_state(sample_id, state) - -def main(): - config = ConfigParser.ConfigParser() - config.read(galaxy_config_file) - 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)) - 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.basic_consume(queue=amqp_config['queue'], - no_ack=True, - callback=recv_callback, - consumer_tag="testtag") - while True: - chan.wait() - chan.basic_cancel("testtag") - chan.close() - conn.close() - -if __name__ == '__main__': - main() \ No newline at end of file diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/client/amqp_publisher.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/client/amqp_publisher.py Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,87 @@ +''' +This script gets barcode data from a barcode scanner using serial communication +and sends the state representated by the barcode scanner & the barcode string +to the Galaxy LIMS RabbitMQ server. The message is sent in XML which has 2 tags, +barcode & state. The state of the scanner should be set in the galaxy_amq.ini +file as a configuration variable. +''' + +from amqplib import client_0_8 as amqp +import ConfigParser +import sys, os +import serial +import array +import time +import optparse + + +xml = \ +''' <sample> + <barcode>%(BARCODE)s</barcode> + <state>%(STATE)s</state> + </sample>''' + + +def handle_scan(states, amqp_config, barcode): + if states.get(barcode[:2], None): + values = dict( BARCODE=barcode[2:], + STATE=states.get(barcode[:2]) ) + print values + data = xml % values + print data + 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() + msg = amqp.Message(data) + msg.properties["delivery_mode"] = 2 + chan.basic_publish(msg, + exchange=amqp_config['exchange'], + routing_key=amqp_config['routing_key']) + chan.close() + conn.close() + +def recv_data(states, amqp_config, s): + while True: + bytes = s.inWaiting() + if bytes: + print '%i bytes recvd' % bytes + msg = s.read(bytes) + print msg + handle_scan(states, amqp_config, msg.strip()) + + +def main(): + parser = optparse.OptionParser() + parser.add_option('-c', '--config-file', help='config file with all the AMQP config parameters', + dest='config_file', action='store') + parser.add_option('-p', '--port', help='Name of the port where the scanner is connected', + dest='port', action='store') + (opts, args) = parser.parse_args() + config = ConfigParser.ConfigParser() + config.read(opts.config_file) + amqp_config = {} + states = {} + for option in config.options("galaxy:amqp"): + amqp_config[option] = config.get("galaxy:amqp", option) + count = 1 + while True: + section = 'scanner%i' % count + if config.has_section(section): + states[config.get(section, 'prefix')] = config.get(section, 'state') + count = count + 1 + else: + break + print amqp_config + print states + s = serial.Serial(int(opts.port)) + print 'Port %s is open: %s' %( opts.port, s.isOpen()) + recv_data(states, amqp_config, s) + s.close() + print 'Port %s is open: %s' %( opts.port, s.isOpen()) + + +if __name__ == '__main__': + main() diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/client/galaxy_amq.ini.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/client/galaxy_amq.ini.sample Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,32 @@ +# Galaxy Message Queue +# Galaxy uses AMQ protocol to receive messages from external sources like +# bar code scanners. Galaxy has been tested against RabbitMQ AMQP implementation. +# For Galaxy to receive messages from a message queue the RabbitMQ server has +# to be set up with a user account and other parameters listed below. The 'host' +# and 'port' fields should point to where the RabbitMQ server is running. + +#[galaxy:amqp] +#host = 127.0.0.1 +#port = 5672 +#userid = galaxy +#password = galaxy +#virtual_host = galaxy_messaging_engine +#queue = galaxy_queue +#exchange = galaxy_exchange +#routing_key = bar_code_scanner + +# The following section(s) 'scanner#' is for specifying the state of the +# sample this scanner represents. This state name should be one of the +# possible states created for this request type in Galaxy +# If there multiple scanners attached to this host the add as many "scanner#" +# sections below each with the name & prefix of the bar code scanner and +# the state it represents +#[scanner1] +#name = +#state = +#prefix = + +#[scanner2] +#name = +#state = +#prefix = \ No newline at end of file diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/client/report.bat.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/client/report.bat.sample Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,1 @@ +python scanner.py -p 2 -c galaxy_amq.ini -r \ No newline at end of file diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/client/scan.bat.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/client/scan.bat.sample Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,1 @@ +python amqp_publisher.py -p 2 -c galaxy_amq.ini \ No newline at end of file diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/client/scan.sh.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/client/scan.sh.sample Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,1 @@ +python amqp_publisher.py -p 3 -c galaxy_amq.ini \ No newline at end of file diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/client/scanner.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/client/scanner.py Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,92 @@ +import sys, os +import serial +import array +import time +import optparse +import ConfigParser, logging +from scanner_interface import ScannerInterface + +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger( 'Scanner' ) + +# command prefix: SYN M CR +cmd = [22, 77, 13] +response = { 6: 'ACK', 5: 'ENQ', 21: 'NAK' } +image_scanner_report = 'RPTSCN.' +get_prefix1 = 'PREBK2?.' +get_prefix2 = ':4820:PREBK2?.' +set_prefix = 'PREBK2995859.' +clear_prefix = 'PRECA2.' + +def get_prefix_cmd(name): + return ':' + name + ':' + 'PREBK2?.' + +def set_prefix_cmd(name, prefix): + prefix_str = '' + for c in prefix: + prefix_str = prefix_str + hex(ord(c))[2:] + return ':' + name + ':' + 'PREBK299' + prefix_str + '!' + +def read_config_file(config_file): + config = ConfigParser.ConfigParser() + config.read(config_file) + count = 1 + scanners_list = [] + while True: + section = 'scanner%i' % count + if config.has_section(section): + scanner = dict(name=config.get(section, 'name'), + prefix=config.get(section, 'prefix'), + state=config.get(section, 'state')) + scanners_list.append(scanner) + count = count + 1 + else: + return scanners_list + +def main(): + usage = "python %s -p PORT -c CONFIG_FILE [ OPTION ]" % sys.argv[0] + parser = optparse.OptionParser(usage=usage) + parser.add_option('-p', '--port', help='Name of the port where the scanner is connected', + dest='port', action='store') + parser.add_option('-c', '--config-file', help='config file with all the AMQP config parameters', + dest='config_file', action='store') + parser.add_option('-r', '--report', help='scanner report', + dest='report', action='store_true', default=False) + parser.add_option('-i', '--install', help='install the scanners', + dest='install', action='store_true', default=False) + (opts, args) = parser.parse_args() + # validate + if not opts.port: + parser.print_help() + sys.exit(0) + if ( opts.report or opts.install ) and not opts.config_file: + parser.print_help() + sys.exit(0) + + # create the scanner interface + si = ScannerInterface(opts.port) + if opts.install: + scanners_list = read_config_file(opts.config_file) + for scanner in scanners_list: + msg = set_prefix_cmd(scanner['name'], scanner['prefix']) + si.send(msg) + response = si.recv() + if not response: + log.error("Scanner %s could not be installed." % scanner['name']) + elif opts.report: + si.send(image_scanner_report) + rep = si.recv() + log.info(rep) + scanners_list = read_config_file(opts.config_file) + for scanner in scanners_list: + msg = get_prefix_cmd(scanner['name']) + si.send(msg) + response = si.recv() + if response: + log.info('PREFIX for scanner %s: %s' % (scanner['name'], chr(int(response[8:12][:2], 16))+chr(int(response[8:12][2:], 16)) )) + si.close() + + + +if __name__ == "__main__": + main() diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/client/scanner_interface.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/client/scanner_interface.py Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,76 @@ +import sys, os +import serial +import array +import time +import optparse +import ConfigParser +import logging + +logging.basicConfig(level=logging.INFO) +log = logging.getLogger( 'ScannerInterface' ) + +class ScannerInterface( object ): + cmdprefix = [22, 77, 13] + response = { 6: 'ACK', 5: 'ENQ', 21: 'NAK' } + + def __init__( self, port ): + if os.name in ['posix', 'mac']: + self.port = port + elif os.name == 'nt': + self.port = int(port) + if self.port: + self.open() + + def open(self): + try: + self.serial_conn = serial.Serial(self.port) + except serial.SerialException: + log.exception('Unable to open port: %s' % str(self.port)) + sys.exit(1) + log.debug('Port %s is open: %s' %( str(self.port), self.serial_conn.isOpen() ) ) + + def is_open(self): + return self.serial_conn.isOpen() + + def close(self): + self.serial_conn.close() + log.debug('Port %s is open: %s' %( str(self.port), self.serial_conn.isOpen() ) ) + + def send(self, msg): + message = self.cmdprefix + map(ord, msg) + byte_array = array.array('B', message) + log.debug('Sending message to %s: %s' % ( str(self.port), message) ) + bytes = self.serial_conn.write( byte_array.tostring() ) + log.debug('%i bytes out of %i bytes sent to the scanner' % ( bytes, len(message) ) ) + + def recv(self): + time.sleep(1) + self.serial_conn.flush() + nbytes = self.serial_conn.inWaiting() + log.debug('%i bytes received' % nbytes) + if nbytes: + msg = self.serial_conn.read(nbytes) + byte_array = map(ord, msg) + log.debug('Message received [%s]: %s' % (self.response.get(byte_array[len(byte_array)-2], byte_array[len(byte_array)-2]), + msg)) + return msg + else: + log.error('Error!') + return None + + def setup_recv(self, callback): + self.recv_callback = callback + + def wait(self): + nbytes = self.serial_conn.inWaiting() + if nbytes: + msg = self.serial_conn.read(nbytes) + byte_array = map(ord, msg) + log.debug('Message received [%s]: %s' % (self.response.get(byte_array[len(byte_array)-2], byte_array[len(byte_array)-2], + msg))) + if self.recv_callback: + self.recv_callback(msg) + return + + + \ No newline at end of file diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/galaxydb_interface.py --- a/scripts/galaxy_messaging/galaxydb_interface.py Wed Nov 04 09:32:09 2009 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ -#/usr/bin/python - -from datetime import datetime, timedelta -import sys -import optparse -import os -import time -import logging - -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 -pkg_resources.require( "psycopg2" ) -import psycopg2 -pkg_resources.require( "SQLAlchemy >= 0.4" ) -from sqlalchemy import * -from sqlalchemy.orm import sessionmaker - -logging.basicConfig(level=logging.DEBUG) -log = logging.getLogger( 'GalaxyDbInterface' ) - -class GalaxyDbInterface(object): - - def __init__(self, dbstr): - self.dbstr = dbstr - self.db_engine = create_engine(self.dbstr) -# self.db_engine.echo = True - self.metadata = MetaData(self.db_engine) - self.session = sessionmaker(bind=self.db_engine) - self.event_table = Table('sample_event', self.metadata, autoload=True ) - self.sample_table = Table('sample', self.metadata, autoload=True ) - self.request_table = Table('request', self.metadata, autoload=True ) - self.state_table = Table('sample_state', self.metadata, autoload=True ) - - def get_sample_id(self, field_name='bar_code', value=None): - if not value: - return -1 - sample_id = -1 - if field_name =='name': - stmt = select(columns=[self.sample_table.c.id], - whereclause=self.sample_table.c.name==value) - result = stmt.execute() - sample_id = result.fetchone()[0] - elif field_name == 'bar_code': - stmt = select(columns=[self.sample_table.c.id], - whereclause=self.sample_table.c.bar_code==value) - result = stmt.execute() - x = result.fetchone() - if x: - sample_id = x[0] - log.debug('Sample ID: %i' % sample_id) - return sample_id - log.warning('This sample %s %s does not belong to any sample in the database.' % (field_name, value)) - return -1 - - def current_state(self, sample_id): - ''' - This method returns the current state of the sample for the given sample_id - ''' - stmt = select(columns=[self.event_table.c.sample_state_id], - whereclause=self.event_table.c.sample_id==sample_id, - order_by=self.event_table.c.update_time.desc()) - result = stmt.execute() - all_states = result.fetchall() - current_state_id = all_states[0][0] - return current_state_id - - def all_possible_states(self, sample_id): - subsubquery = select(columns=[self.sample_table.c.request_id], - whereclause=self.sample_table.c.id==sample_id) - self.request_id = subsubquery.execute().fetchall()[0][0] - log.debug('REQUESTID: %i' % self.request_id) - subquery = select(columns=[self.request_table.c.request_type_id], - whereclause=self.request_table.c.id==self.request_id) - request_type_id = subquery.execute().fetchall()[0][0] - log.debug('REQUESTTYPEID: %i' % request_type_id) - query = select(columns=[self.state_table.c.id, self.state_table.c.name], - whereclause=self.state_table.c.request_type_id==request_type_id, - order_by=self.state_table.c.id.asc()) - states = query.execute().fetchall() - log.debug('POSSIBLESTATES: '+ str(states)) - return states - - def change_state(self, sample_id, new_state=None): - ''' - This method changes the state of the sample to the the 'new_state' - ''' - if not new_state: - return - new_state_id = -1 - # find the state_id for this new state in the list of possible states - possible_states = self.all_possible_states(sample_id) - for state_id, state_name in possible_states: - if new_state == state_name: - new_state_id = state_id - if new_state_id == -1: - return - log.debug('Updating sample_id %i state to %s' % (sample_id, new_state)) - d = timedelta(hours=4) - i = self.event_table.insert() - i.execute(update_time=datetime.now()+d, - create_time=datetime.now()+d, - sample_id=sample_id, - sample_state_id=int(new_state_id), - comment='bar code scanner') - # if all the samples for this request are in the final state - # then change the request state to 'Complete' - result = select(columns=[self.sample_table.c.id], - whereclause=self.sample_table.c.request_id==self.request_id).execute() - sample_id_list = result.fetchall() - request_complete = True - for sid in sample_id_list: - current_state_id = self.current_state(sid[0]) - if current_state_id != possible_states[-1][0]: - request_complete = False - break - if request_complete: - request_state = 'Complete' - else: - request_state = 'Submitted' - log.debug('Updating request_id %i state to "%s"' % (self.request_id, request_state)) - d = timedelta(hours=4) - i = self.request_table.update(whereclause=self.request_table.c.id==self.request_id, - values={self.request_table.c.state: request_state}) - i.execute() - - - -if __name__ == '__main__': - print '''This file should not be run directly. To start the Galaxy AMQP Listener: - %sh run_galaxy_listener.sh''' -# dbstr = 'postgres://postgres:postgres@localhost/galaxy_ft' -# -# parser = optparse.OptionParser() -# parser.add_option('-n', '--name', help='name of the sample field', dest='name', \ -# action='store', default='bar_code') -# parser.add_option('-v', '--value', help='value of the sample field', dest='value', \ -# action='store') -# parser.add_option('-s', '--state', help='new state of the sample', dest='state', \ -# action='store') -# (opts, args) = parser.parse_args() -# -# gs = GalaxyDbInterface(dbstr) -# sample_id = gs.get_sample_id(field_name=opts.name, value=opts.value) -# gs.change_state(sample_id, opts.state) - - - diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/server/amqp_consumer.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/server/amqp_consumer.py Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,94 @@ +''' +Galaxy Messaging with AMQP (RabbitMQ) +Galaxy uses AMQ protocol to receive messages from external sources like +bar code scanners. Galaxy has been tested against RabbitMQ AMQP implementation. +For Galaxy to receive messages from a message queue the RabbitMQ server has +to be set up with a user account and other parameters listed in the [galaxy:amq] +section in the universe_wsgi.ini config file +Once the RabbitMQ server has been setup and started with the given parameters, +this script can be run to receive messages and update the Galaxy database accordingly +''' + +import ConfigParser +import sys, os +import optparse +import xml.dom.minidom +from galaxydb_interface import GalaxyDbInterface + +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 +pkg_resources.require( "amqplib" ) + +from amqplib import client_0_8 as amqp + +import logging +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger( 'GalaxyAMQP' ) + + +galaxy_config_file = 'universe_wsgi.ini' +global dbconnstr + +def get_value(dom, tag_name): + ''' + This method extracts the tag value from the xml message + ''' + nodelist = dom.getElementsByTagName(tag_name)[0].childNodes + rc = "" + for node in nodelist: + if node.nodeType == node.TEXT_NODE: + rc = rc + node.data + return rc + +def recv_callback(msg): + 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 + galaxy = GalaxyDbInterface(dbconnstr) + sample_id = galaxy.get_sample_id(field_name='bar_code', value=barcode) + if sample_id == -1: + log.debug('Invalid barcode.') + return + galaxy.change_state(sample_id, state) + +def main(): + config = ConfigParser.ConfigParser() + config.read(galaxy_config_file) + 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)) + 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.basic_consume(queue=amqp_config['queue'], + no_ack=True, + callback=recv_callback, + consumer_tag="testtag") + while True: + chan.wait() + chan.basic_cancel("testtag") + chan.close() + conn.close() + +if __name__ == '__main__': + main() \ No newline at end of file diff -r 984b1eb6c428 -r 5b2d593d9aed scripts/galaxy_messaging/server/galaxydb_interface.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/galaxy_messaging/server/galaxydb_interface.py Wed Nov 04 10:04:51 2009 -0500 @@ -0,0 +1,149 @@ +#/usr/bin/python + +from datetime import datetime +import sys +import optparse +import os +import time +import logging + +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 +pkg_resources.require( "psycopg2" ) +import psycopg2 +pkg_resources.require( "SQLAlchemy >= 0.4" ) +from sqlalchemy import * +from sqlalchemy.orm import sessionmaker + +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger( 'GalaxyDbInterface' ) + +class GalaxyDbInterface(object): + + def __init__(self, dbstr): + self.dbstr = dbstr + self.db_engine = create_engine(self.dbstr) +# self.db_engine.echo = True + self.metadata = MetaData(self.db_engine) + self.session = sessionmaker(bind=self.db_engine) + self.event_table = Table('sample_event', self.metadata, autoload=True ) + self.sample_table = Table('sample', self.metadata, autoload=True ) + self.request_table = Table('request', self.metadata, autoload=True ) + self.state_table = Table('sample_state', self.metadata, autoload=True ) + + def get_sample_id(self, field_name='bar_code', value=None): + if not value: + return -1 + sample_id = -1 + if field_name =='name': + stmt = select(columns=[self.sample_table.c.id], + whereclause=self.sample_table.c.name==value) + result = stmt.execute() + sample_id = result.fetchone()[0] + elif field_name == 'bar_code': + stmt = select(columns=[self.sample_table.c.id], + whereclause=self.sample_table.c.bar_code==value) + result = stmt.execute() + x = result.fetchone() + if x: + sample_id = x[0] + log.debug('Sample ID: %i' % sample_id) + return sample_id + log.warning('This sample %s %s does not belong to any sample in the database.' % (field_name, value)) + return -1 + + def current_state(self, sample_id): + ''' + This method returns the current state of the sample for the given sample_id + ''' + stmt = select(columns=[self.event_table.c.sample_state_id], + whereclause=self.event_table.c.sample_id==sample_id, + order_by=self.event_table.c.update_time.desc()) + result = stmt.execute() + all_states = result.fetchall() + current_state_id = all_states[0][0] + return current_state_id + + def all_possible_states(self, sample_id): + subsubquery = select(columns=[self.sample_table.c.request_id], + whereclause=self.sample_table.c.id==sample_id) + self.request_id = subsubquery.execute().fetchall()[0][0] + log.debug('REQUESTID: %i' % self.request_id) + subquery = select(columns=[self.request_table.c.request_type_id], + whereclause=self.request_table.c.id==self.request_id) + request_type_id = subquery.execute().fetchall()[0][0] + log.debug('REQUESTTYPEID: %i' % request_type_id) + query = select(columns=[self.state_table.c.id, self.state_table.c.name], + whereclause=self.state_table.c.request_type_id==request_type_id, + order_by=self.state_table.c.id.asc()) + states = query.execute().fetchall() + log.debug('POSSIBLESTATES: '+ str(states)) + return states + + def change_state(self, sample_id, new_state=None): + ''' + This method changes the state of the sample to the the 'new_state' + ''' + if not new_state: + return + new_state_id = -1 + # find the state_id for this new state in the list of possible states + possible_states = self.all_possible_states(sample_id) + for state_id, state_name in possible_states: + if new_state == state_name: + new_state_id = state_id + if new_state_id == -1: + return + log.debug('Updating sample_id %i state to %s' % (sample_id, new_state)) + i = self.event_table.insert() + i.execute(update_time=datetime.utcnow(), + create_time=datetime.utcnow(), + sample_id=sample_id, + sample_state_id=int(new_state_id), + comment='bar code scanner') + # if all the samples for this request are in the final state + # then change the request state to 'Complete' + result = select(columns=[self.sample_table.c.id], + whereclause=self.sample_table.c.request_id==self.request_id).execute() + sample_id_list = result.fetchall() + request_complete = True + for sid in sample_id_list: + current_state_id = self.current_state(sid[0]) + if current_state_id != possible_states[-1][0]: + request_complete = False + break + if request_complete: + request_state = 'Complete' + else: + request_state = 'Submitted' + log.debug('Updating request_id %i state to "%s"' % (self.request_id, request_state)) + i = self.request_table.update(whereclause=self.request_table.c.id==self.request_id, + values={self.request_table.c.state: request_state}) + i.execute() + + + +if __name__ == '__main__': + print '''This file should not be run directly. To start the Galaxy AMQP Listener: + %sh run_galaxy_listener.sh''' + dbstr = 'postgres://postgres:postgres@localhost/galaxy_uft' + + parser = optparse.OptionParser() + parser.add_option('-n', '--name', help='name of the sample field', dest='name', \ + action='store', default='bar_code') + parser.add_option('-v', '--value', help='value of the sample field', dest='value', \ + action='store') + parser.add_option('-s', '--state', help='new state of the sample', dest='state', \ + action='store') + (opts, args) = parser.parse_args() + + gs = GalaxyDbInterface(dbstr) + sample_id = gs.get_sample_id(field_name=opts.name, value=opts.value) + gs.change_state(sample_id, opts.state) + + +
1
0
0
0
[hg] galaxy 2948: Pack recently modified scripts.
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/de764999c5af
changeset: 2948:de764999c5af user: jeremy goecks <jeremy.goecks(a)emory.edu> date: Tue Nov 03 13:00:36 2009 -0500 description: Pack recently modified scripts. diffstat: static/scripts/packed/autocomplete_tagging.js | 2 +- static/scripts/packed/trackster.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diffs (16 lines): diff -r f776fa6045ba -r de764999c5af static/scripts/packed/autocomplete_tagging.js --- a/static/scripts/packed/autocomplete_tagging.js Tue Nov 03 12:58:13 2009 -0500 +++ b/static/scripts/packed/autocomplete_tagging.js Tue Nov 03 13:00:36 2009 -0500 @@ -1,1 +1,1 @@ -var ac_tag_area_id_gen=1;jQuery.fn.autocomplete_tagging=function(c){var e={get_toggle_link_text_fn:function(u){var w="";var v=o(u);if(v!=0){w=v+(v!=0?" Tags":" Tag")}else{w="Add tags"}return w},tag_click_fn:function(u,v){},editable:true,input_size:20,in_form:false,tags:{},use_toggle_link:true,item_id:"",add_tag_img:"",add_tag_img_rollover:"",delete_tag_img:"",ajax_autocomplete_tag_url:"",ajax_retag_url:"",ajax_delete_tag_url:"",ajax_add_tag_url:""};var p=jQuery.extend(e,c);var k="tag-area-"+(ac_tag_area_id_gen)++;var m=$("<div>").attr("id",k).addClass("tag-area");this.append(m);var o=function(u){if(u.length){return u.length}var v=0;for(element in u){v++}return v};var b=function(){var u=p.get_toggle_link_text_fn(p.tags);var v=$("<a href='/history/tags'>").text(u).addClass("toggle-link");v.click(function(){var w=(m.css("display")=="none");var x;if(w){x=function(){var y=o(p.tags);if(y==0){m.click()}}}else{x=function(){m.blur()}}m.slideToggle("fast",x);return false});return v};v ar s=b();if(p.use_toggle_link){this.prepend(s)}var t=function(u){var v=new Array();for(key in u){v[v.length]=key+"-->"+u[key]}return"{"+v.join(",")+"}"};var a=function(v,u){return v+((u!=""&&u)?":"+u:"")};var h=function(u){return u.split(":")};var i=function(u){var v=$("<img src='"+p.add_tag_img+"' rollover='"+p.add_tag_img_rollover+"'/>").addClass("add-tag-button");v.click(function(){$(this).hide();m.click();return false});return v};var j=function(u){var v=$("<img src='"+p.delete_tag_img+"'/>").addClass("delete-tag-img");v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)});v.click(function(){var D=$(this).parent();var C=D.find(".tag-name").eq(0);var B=C.text();var z=h(B);var F=z[0];var y=z[1];var E=D.prev();D.remove();delete p.tags[F];var A=p.get_toggle_link_text_fn(p.tags);s.text(A);$.ajax({url:p.ajax_delete_tag_url,data:{tag_name:F},error:function(){p.tags[F]=y;if(E.hasClass("tag-button")){E .after(D)}else{m.prepend(D)}var G=p.get_toggle_link_text_fn(p.tags);alert("Remove tag failed");s.text(G);v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)})},success:function(){}});return true});var w=$("<span>").text(u).addClass("tag-name");w.click(function(){tag_name_and_value=u.split(":");p.tag_click_fn(tag_name_and_value[0],tag_name_and_value[1]);return true});var x=$("<span></span>").addClass("tag-button");x.append(w);if(p.editable){x.append(v)}return x};var d=function(v){var u;if(p.in_form){u=$("<textarea id='history-tag-input' rows='1' cols='"+p.input_size+"' value='"+escape(v)+"'></textarea>")}else{u=$("<input id='history-tag-input' type='text' size='"+p.input_size+"' value='"+escape(v)+"'></input>")}u.keyup(function(D){if(D.keyCode==27){$(this).trigger("blur")}else{if((D.keyCode==13)||(D.keyCode==188)||(D.keyCode==32)){new_value=this.value;if(return_key_pressed_for_autocomplete==true) {return_key_pressed_for_autocomplete=false;return false}if(new_value.indexOf(": ",new_value.length-2)!=-1){this.value=new_value.substring(0,new_value.length-1);return false}if((D.keyCode==188)||(D.keyCode==32)){new_value=new_value.substring(0,new_value.length-1)}new_value=new_value.replace(/^\s+|\s+$/g,"");if(new_value.length<3){return false}this.value="";var A=j(new_value);var z=m.children(".tag-button");if(z.length!=0){var E=z.slice(z.length-1);E.after(A)}else{m.prepend(A)}var y=new_value.split(":");p.tags[y[0]]=y[1];var B=p.get_toggle_link_text_fn(p.tags);s.text(B);var C=$(this);$.ajax({url:p.ajax_add_tag_url,data:{new_tag:new_value},error:function(){A.remove();delete p.tags[y[0]];var F=p.get_toggle_link_text_fn(p.tags);s.text(F);alert("Add tag failed")},success:function(){C.flushCache()}});return false}}});var w=function(A,z,y,C,B){tag_name_and_value=C.split(":");return(tag_name_and_value.length==1?tag_name_and_value[0]:tag_name_and_value[1])};var x={selectFirst:false,fo rmatItem:w,autoFill:false,highlight:false};u.autocomplete(p.ajax_autocomplete_tag_url,x);u.addClass("tag-input");return u};for(tag_name in p.tags){var q=p.tags[tag_name];var l=a(tag_name,q);var g=j(l,s,p.tags);m.append(g)}var n=d("");var f=i(n);m.blur(function(u){r=o(p.tags);if(r!=0){f.show();n.hide();m.removeClass("active-tag-area")}else{}});if(p.editable){m.append(f);m.append(n);n.hide();m.click(function(w){var v=$(this).hasClass("active-tag-area");if($(w.target).hasClass("delete-tag-img")&&!v){return false}if($(w.target).hasClass("tag-name")&&!v){return false}$(this).addClass("active-tag-area");f.hide();n.show();n.focus();var u=function(y){var x=m.attr("id");if(($(y.target).attr("id")!=x)&&($(y.target).parents().filter(x).length==0)){m.blur();$(document).unbind("click",u)}};$(window).click(u);return false})}if(p.use_toggle_link){m.hide()}else{var r=o(p.tags);if(r==0){f.hide();n.show()}}return this.addClass("tag-element")}; \ No newline at end of file +var ac_tag_area_id_gen=1;jQuery.fn.autocomplete_tagging=function(c){var e={get_toggle_link_text_fn:function(u){var w="";var v=o(u);if(v!=0){w=v+(v!=0?" Tags":" Tag")}else{w="Add tags"}return w},tag_click_fn:function(u,v){},editable:true,input_size:20,in_form:false,tags:{},use_toggle_link:true,item_id:"",add_tag_img:"",add_tag_img_rollover:"",delete_tag_img:"",ajax_autocomplete_tag_url:"",ajax_retag_url:"",ajax_delete_tag_url:"",ajax_add_tag_url:""};var p=jQuery.extend(e,c);var k="tag-area-"+(ac_tag_area_id_gen)++;var m=$("<div>").attr("id",k).addClass("tag-area");this.append(m);var o=function(u){if(u.length){return u.length}var v=0;for(element in u){v++}return v};var b=function(){var u=p.get_toggle_link_text_fn(p.tags);var v=$("<a href='/history/tags'>").text(u).addClass("toggle-link");v.click(function(){var w=(m.css("display")=="none");var x;if(w){x=function(){var y=o(p.tags);if(y==0){m.click()}}}else{x=function(){m.blur()}}m.slideToggle("fast",x);return false});return v};v ar s=b();if(p.use_toggle_link){this.prepend(s)}var t=function(u){var v=new Array();for(key in u){v[v.length]=key+"-->"+u[key]}return"{"+v.join(",")+"}"};var a=function(v,u){return v+((u!=""&&u)?":"+u:"")};var h=function(u){return u.split(":")};var i=function(u){var v=$("<img src='"+p.add_tag_img+"' rollover='"+p.add_tag_img_rollover+"'/>").addClass("add-tag-button");v.click(function(){$(this).hide();m.click();return false});return v};var j=function(u){var v=$("<img src='"+p.delete_tag_img+"'/>").addClass("delete-tag-img");v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)});v.click(function(){var D=$(this).parent();var C=D.find(".tag-name").eq(0);var B=C.text();var z=h(B);var F=z[0];var y=z[1];var E=D.prev();D.remove();delete p.tags[F];var A=p.get_toggle_link_text_fn(p.tags);s.text(A);$.ajax({url:p.ajax_delete_tag_url,data:{tag_name:F},error:function(){p.tags[F]=y;if(E.hasClass("tag-button")){E .after(D)}else{m.prepend(D)}var G=p.get_toggle_link_text_fn(p.tags);alert("Remove tag failed");s.text(G);v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)})},success:function(){}});return true});var w=$("<span>").text(u).addClass("tag-name");w.click(function(){tag_name_and_value=u.split(":");p.tag_click_fn(tag_name_and_value[0],tag_name_and_value[1]);return true});var x=$("<span></span>").addClass("tag-button");x.append(w);if(p.editable){x.append(v)}return x};var d=function(v){var u;if(p.in_form){u=$("<textarea id='history-tag-input' rows='1' cols='"+p.input_size+"' value='"+escape(v)+"'></textarea>")}else{u=$("<input id='history-tag-input' type='text' size='"+p.input_size+"' value='"+escape(v)+"'></input>")}u.keyup(function(D){if(D.keyCode==27){$(this).trigger("blur")}else{if((D.keyCode==13)||(D.keyCode==188)||(D.keyCode==32)){new_value=this.value;if(return_key_pressed_for_autocomplete==true) {return_key_pressed_for_autocomplete=false;return false}if(new_value.indexOf(": ",new_value.length-2)!=-1){this.value=new_value.substring(0,new_value.length-1);return false}if((D.keyCode==188)||(D.keyCode==32)){new_value=new_value.substring(0,new_value.length-1)}new_value=new_value.replace(/^\s+|\s+$/g,"");if(new_value.length<2){return false}this.value="";var A=j(new_value);var z=m.children(".tag-button");if(z.length!=0){var E=z.slice(z.length-1);E.after(A)}else{m.prepend(A)}var y=new_value.split(":");p.tags[y[0]]=y[1];var B=p.get_toggle_link_text_fn(p.tags);s.text(B);var C=$(this);$.ajax({url:p.ajax_add_tag_url,data:{new_tag:new_value},error:function(){A.remove();delete p.tags[y[0]];var F=p.get_toggle_link_text_fn(p.tags);s.text(F);alert("Add tag failed")},success:function(){C.flushCache()}});return false}}});var w=function(A,z,y,C,B){tag_name_and_value=C.split(":");return(tag_name_and_value.length==1?tag_name_and_value[0]:tag_name_and_value[1])};var x={selectFirst:false,fo rmatItem:w,autoFill:false,highlight:false};u.autocomplete(p.ajax_autocomplete_tag_url,x);u.addClass("tag-input");return u};for(tag_name in p.tags){var q=p.tags[tag_name];var l=a(tag_name,q);var g=j(l,s,p.tags);m.append(g)}var n=d("");var f=i(n);m.blur(function(u){r=o(p.tags);if(r!=0){f.show();n.hide();m.removeClass("active-tag-area")}else{}});if(p.editable){m.append(f);m.append(n);n.hide();m.click(function(w){var v=$(this).hasClass("active-tag-area");if($(w.target).hasClass("delete-tag-img")&&!v){return false}if($(w.target).hasClass("tag-name")&&!v){return false}$(this).addClass("active-tag-area");f.hide();n.show();n.focus();var u=function(y){var x=m.attr("id");if(($(y.target).attr("id")!=x)&&($(y.target).parents().filter(x).length==0)){m.blur();$(document).unbind("click",u)}};$(window).click(u);return false})}if(p.use_toggle_link){m.hide()}else{var r=o(p.tags);if(r==0){f.hide();n.show()}}return this.addClass("tag-element")}; \ No newline at end of file diff -r f776fa6045ba -r de764999c5af static/scripts/packed/trackster.js --- a/static/scripts/packed/trackster.js Tue Nov 03 12:58:13 2009 -0500 +++ b/static/scripts/packed/trackster.js Tue Nov 03 13:00:36 2009 -0500 @@ -1,1 +1,1 @@ -var DENSITY=1000,DATA_ERROR="There was an error in indexing this dataset.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES=5,CACHED_DATA=20,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(l eft_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.obj_cache={};this.key_ary=[]};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c}});var View=function(b,a){this.chrom=b;this.tracks=[];this.max_low=0;this.max_high=a;this.center=(this.max_high-this.max_low)/2;this.span=this.max_high-this.max_low;this.zoom_factor=2;this.zoom_level=0};$.extend(View.prototype,{add_track:function(a){a.view=this;this.tracks.push(a);if(a.init){a.init()}},redraw:function(){var d=this.span/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e =this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));for(var c=0,a=this.tracks.length;c<a;c++){this.tracks[c].draw()}$("#bottom-spacer").remove();$("#viewport").append('<div id="bottom-spacer" style="height: 200px;"></div>')},zoom_in:function(a){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/$(document).width()*(this.high-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:function(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.make_container()};$.extend(Track.prototype,{make_container:function(){thi s.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div class='track'></div>").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)}});var TiledTrack=function(){this.tile_cache=new Cache(CACHED_TILES)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var h=this.view.low,d=this.view.high,e=d-h;var c=Math.pow(10,Math.ceil(Math.log(e/DENSITY)/Math.log(10)));c=Math.max(c,0.1);c=Math.min(c,1000000);var j=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(j);var k=this.content_div.width()/e;var g;var a=Math.floor(h/c/DENSITY);while((a*DENSITY*c)<d){var i=this.view.zoom_level+"_"+a;var b=this.tile_cache.get(i);if(b){var f=a*DENSITY*c;b.css({left:(f-this.view.low)*k});j.append(b)}else{g=this.draw_tile(c,a,j,k)}if(g){this.tile_cache.set(i,g)}a+=1}}});var LabelTrack=function(a){Track.ca ll(this,null,a);this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,b,a){Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.track_type="line";this.height_px=(a?a:100);this.container_div.addClass("line-track");this.dataset_id=b;this.cache=new Cache(CACHED_DATA)};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;a.content_div.text(DATA_LOADING);$.getJSON(data_url,{stats:true,track_type:a.track_type,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dat aset_id},function(c){if(!c||c=="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(c=="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(c=="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.min_value=c.min;a.max_value=c.max;a.vertical_range=a.max_value-a.min_value;var d=$("<div class='yaxislabel'>"+a.min_value+"</div>");var b=$("<div class='yaxislabel'>"+a.max_value+"</div>");b.css({position:"relative",top:"35px"});b.prependTo(a.container_div);d.css({position:"relative",top:a.height_px+32+"px",});d.prependTo(a.container_div);a.draw()}}}})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;$.getJSON(data_url,{track_type:this.track_type,chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id},function(g){c.cache[e]=g;$(document).trigger("redra w")})},draw_tile:function(d,a,m,o){if(!this.vertical_range){return}var h=a*DENSITY*d,b=DENSITY*d,c=$("<canvas class='tile'></canvas>"),l=d+"_"+a;if(!this.cache[l]){this.get_data(d,a);return}var g=this.cache[l];c.css({position:"absolute",top:0,left:(h-this.view.low)*o});c.get(0).width=Math.ceil(b*o);c.get(0).height=this.height_px;var n=c.get(0).getContext("2d");var e=false;n.beginPath();for(var f=0;f<g.length-1;f++){var k=g[f][0]-h;var j=g[f][1];if(isNaN(j)){e=false}else{k=k*o;j=(j-this.min_value)/this.vertical_range*this.height_px;if(e){n.lineTo(k,j)}else{n.moveTo(k,j);e=true}}}n.stroke();m.append(c);return c}});var FeatureTrack=function(c,b,a){Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.track_type="feature";this.height_px=(a?a:100);this.container_div.addClass("feature-track");this.dataset_id=b;this.zo_slots={};this.show_labels_scale=0.001;this.showing_labels=false;this.vertical_gap=10;this.base_color="#2C3143"};$.extend(FeatureTrack.prototype,TiledTrack.pro totype,{init:function(){var a=this;a.content_div.text(DATA_LOADING);$.getJSON(data_url,{track_type:a.track_type,low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom},function(b){if(b=="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(b.length===0||b=="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(b=="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.values=b;a.calc_slots();a.slots=a.zo_slots;a.draw()}}}})},calc_slots:function(o){var c=[],b=this.container_div.width()/(this.view.high-this.view.low),g=this.show_labels_scale,a=this.view.max_high,e=this.view.max_low;if(o){this.zi_slots={}}var m=$("<canvas></canvas>").get(0).getContext("2d");for(var f=0,h=this.values.length;f<h;f++){var k,l,n=this.values[f];if(o){k=Math.floor(Math.max(e,(n.star t-e)*g));k-=m.measureText(n.name).width;l=Math.ceil(Math.min(a,(n.end-e)*g))}else{k=Math.floor(Math.max(e,(n.start-e)*b));l=Math.ceil(Math.min(a,(n.end-e)*b))}var d=0;while(true){if(c[d]===undefined||c[d]<k){c[d]=l;if(o){this.zi_slots[n.name]=d}else{this.zo_slots[n.name]=d}break}d++}}this.height_px=c.length*this.vertical_gap+15;this.content_div.css("height",this.height_px+"px")},draw_tile:function(w,B,g,n){if(!this.values){return null}if(n>this.show_labels_scale&&!this.showing_labels){this.showing_labels=true;if(!this.zi_slots){this.calc_slots(true)}this.slots=this.zi_slots}else{if(n<=this.show_labels_scale&&this.showing_labels){this.showing_labels=false;this.slots=this.zo_slots}}var C=B*DENSITY*w,c=(B+1)*DENSITY*w,q=DENSITY*w;var u=Math.ceil(q*n),t=this.height_px,s=$("<canvas class='tile'></canvas>");s.css({position:"absolute",top:0,left:(C-this.view.low)*n});s.get(0).width=u;s.get(0).height=t;var v=s.get(0).getContext("2d");v.fillStyle=this.base_color;v.font="10px monospac e";v.textAlign="right";var y=0;for(var z=0,A=this.values.length;z<A;z++){var f=this.values[z];if(f.start<=c&&f.end>=C){var e=Math.floor(Math.max(0,(f.start-C)*n)),h=Math.ceil(Math.min(u,(f.end-C)*n)),d=this.slots[f.name]*this.vertical_gap;var a,G,b=null,o=null;if(f.thick_start&&f.thick_end){b=Math.floor(Math.max(0,(f.thick_start-C)*n));o=Math.ceil(Math.min(u,(f.thick_end-C)*n))}if(!this.showing_labels){v.fillRect(e,d+5,h-e,1)}else{if(v.fillText){v.fillText(f.name,e-1,d+8)}var E=f.blocks;if(E){if(f.strand){if(f.strand=="+"){v.fillStyle=RIGHT_STRAND}else{if(f.strand=="-"){v.fillStyle=LEFT_STRAND}}v.fillRect(e,d,h-e,10);v.fillStyle=this.base_color}for(var x=0,F=E.length;x<F;x++){var m=E[x],l=Math.floor(Math.max(0,(m[0]-C)*n)),D=Math.ceil(Math.min(u,(m[1]-C)*n));a=5;G=3;v.fillRect(l,d+G,D-l,a);if(b&&(l<o||D>b)){a=9;G=1;var r=Math.max(l,b),p=Math.min(D,o);v.fillRect(r,d+G,p-r,a)}}}else{a=9;G=1;v.fillRect(e,d+G,h-e,a);if(f.strand){if(f.strand=="+"){v.fillStyle=RIGHT_STRAND_INV}els e{if(f.strand=="-"){v.fillStyle=LEFT_STRAND_INV}}v.fillRect(e,d,h-e,10);v.fillStyle=this.base_color}}}y++}}g.append(s);return s}}); \ No newline at end of file +var DENSITY=1000,DATA_ERROR="There was an error in indexing this dataset.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES=10,CACHED_DATA=20,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern( left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.obj_cache={};this.key_ary=[]};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c}});var View=function(b,a){this.chrom=b;this.tracks=[];this.max_low=0;this.max_high=a;this.center=(this.max_high-this.max_low)/2;this.span=this.max_high-this.max_low;this.zoom_factor=2;this.zoom_level=0};$.extend(View.prototype,{add_track:function(a){a.view=this;this.tracks.push(a);if(a.init){a.init()}},redraw:function(){var d=this.span/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){ e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));for(var c=0,a=this.tracks.length;c<a;c++){this.tracks[c].draw()}$("#bottom-spacer").remove();$("#viewport").append('<div id="bottom-spacer" style="height: 200px;"></div>')},zoom_in:function(a){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/$(document).width()*(this.high-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:function(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.make_container()};$.extend(Track.prototype,{make_container:function(){th is.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div class='track'></div>").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)}});var TiledTrack=function(){this.tile_cache=new Cache(CACHED_TILES)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var h=this.view.low,d=this.view.high,e=d-h;var c=Math.pow(10,Math.ceil(Math.log(e/DENSITY)/Math.log(10)));c=Math.max(c,0.1);c=Math.min(c,1000000);var j=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(j);var k=this.content_div.width()/e;var g;var a=Math.floor(h/c/DENSITY);while((a*DENSITY*c)<d){var i=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var b=this.tile_cache.get(i);if(b){var f=a*DENSITY*c;b.css({left:(f-this.view.low)*k});j.append(b)}else{g=this.draw_tile(c,a,j,k);if(g){this.tile_cache.set(i,g)}}a+=1}}});var LabelTrack=function(a){Track.call(this,null,a);this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,b,a){Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.track_type="line";this.height_px=(a?a:100);this.container_div.addClass("line-track");this.dataset_id=b;this.cache=new Cache(CACHED_DATA)};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;a.content_div.text(DATA_LOADING);$.getJSON(data_url,{stats:true,track_type:a.track_type,chrom:a.view.chrom,low: null,high:null,dataset_id:a.dataset_id},function(c){if(!c||c=="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(c=="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(c=="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.min_value=c.min;a.max_value=c.max;a.vertical_range=a.max_value-a.min_value;var d=$("<div class='yaxislabel'>"+a.min_value+"</div>");var b=$("<div class='yaxislabel'>"+a.max_value+"</div>");b.css({position:"relative",top:"35px"});b.prependTo(a.container_div);d.css({position:"relative",top:a.height_px+32+"px",});d.prependTo(a.container_div);a.draw()}}}})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;$.getJSON(data_url,{track_type:this.track_type,chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id},function(g){c.cache[ e]=g;$(document).trigger("redraw")})},draw_tile:function(d,a,m,o){if(!this.vertical_range){return}var h=a*DENSITY*d,b=DENSITY*d,c=$("<canvas class='tile'></canvas>"),l=d+"_"+a;if(!this.cache[l]){this.get_data(d,a);return}var g=this.cache[l];c.css({position:"absolute",top:0,left:(h-this.view.low)*o});c.get(0).width=Math.ceil(b*o);c.get(0).height=this.height_px;var n=c.get(0).getContext("2d");var e=false;n.beginPath();for(var f=0;f<g.length-1;f++){var k=g[f][0]-h;var j=g[f][1];if(isNaN(j)){e=false}else{k=k*o;j=(j-this.min_value)/this.vertical_range*this.height_px;if(e){n.lineTo(k,j)}else{n.moveTo(k,j);e=true}}}n.stroke();m.append(c);return c}});var FeatureTrack=function(c,b,a){Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.track_type="feature";this.height_px=(a?a:100);this.container_div.addClass("feature-track");this.dataset_id=b;this.zo_slots={};this.show_labels_scale=0.001;this.showing_labels=false;this.vertical_gap=10;this.base_color="#2C3143"};$.extend(Featur eTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;a.content_div.text(DATA_LOADING);$.getJSON(data_url,{track_type:a.track_type,low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom},function(b){if(b=="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(b.length===0||b=="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(b=="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.values=b;a.calc_slots();a.slots=a.zo_slots;a.draw()}}}})},calc_slots:function(o){var c=[],b=this.content_div.width()/(this.view.high-this.view.low),g=this.show_labels_scale,a=this.view.max_high,e=this.view.max_low;if(o){this.zi_slots={}}var m=$("<canvas></canvas>").get(0).getContext("2d");for(var f=0,h=this.values.length;f<h;f++){var k,l,n=this.values[f];if(o){k= Math.floor((n.start-e)*g);k-=m.measureText(n.name).width;l=Math.ceil((n.end-e)*g)}else{k=Math.floor((n.start-e)*b);l=Math.ceil((n.end-e)*b)}var d=0;while(true){if(c[d]===undefined||c[d]<k){c[d]=l;if(o){this.zi_slots[n.name]=d}else{this.zo_slots[n.name]=d}break}d++}}this.height_px=c.length*this.vertical_gap+15;this.content_div.css("height",this.height_px+"px")},draw_tile:function(w,B,g,n){if(!this.values){return null}if(n>this.show_labels_scale&&!this.showing_labels){this.showing_labels=true;if(!this.zi_slots){this.calc_slots(true)}this.slots=this.zi_slots}else{if(n<=this.show_labels_scale&&this.showing_labels){this.showing_labels=false;this.slots=this.zo_slots}}var C=B*DENSITY*w,c=(B+1)*DENSITY*w,q=DENSITY*w;var u=Math.ceil(q*n),t=this.height_px,s=$("<canvas class='tile'></canvas>");s.css({position:"absolute",top:0,left:(C-this.view.low)*n});s.get(0).width=u;s.get(0).height=t;var v=s.get(0).getContext("2d");v.fillStyle=this.base_color;v.font="10px monospace";v.textAlign="rig ht";var y=0;for(var z=0,A=this.values.length;z<A;z++){var f=this.values[z];if(f.start<=c&&f.end>=C){var e=Math.floor(Math.max(0,(f.start-C)*n)),h=Math.ceil(Math.min(u,(f.end-C)*n)),d=this.slots[f.name]*this.vertical_gap;var a,G,b=null,o=null;if(f.thick_start&&f.thick_end){b=Math.floor(Math.max(0,(f.thick_start-C)*n));o=Math.ceil(Math.min(u,(f.thick_end-C)*n))}if(!this.showing_labels){v.fillRect(e,d+5,h-e,1)}else{if(v.fillText){v.fillText(f.name,e-1,d+8)}var E=f.blocks;if(E){if(f.strand){if(f.strand=="+"){v.fillStyle=RIGHT_STRAND}else{if(f.strand=="-"){v.fillStyle=LEFT_STRAND}}v.fillRect(e,d,h-e,10);v.fillStyle=this.base_color}for(var x=0,F=E.length;x<F;x++){var m=E[x],l=Math.floor(Math.max(0,(m[0]-C)*n)),D=Math.ceil(Math.min(u,(m[1]-C)*n));a=5;G=3;v.fillRect(l,d+G,D-l,a);if(b&&(l<o||D>b)){a=9;G=1;var r=Math.max(l,b),p=Math.min(D,o);v.fillRect(r,d+G,p-r,a)}}}else{a=9;G=1;v.fillRect(e,d+G,h-e,a);if(f.strand){if(f.strand=="+"){v.fillStyle=RIGHT_STRAND_INV}else{if(f.strand=="-") {v.fillStyle=LEFT_STRAND_INV}}v.fillRect(e,d,h-e,10);v.fillStyle=this.base_color}}}y++}}g.append(s);return s}}); \ No newline at end of file
1
0
0
0
[hg] galaxy 2949: imported patch alchemy05_fixes_01
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/133252175425
changeset: 2949:133252175425 user: Nate Coraor <nate(a)bx.psu.edu> date: Tue Nov 03 12:52:00 2009 -0500 description: imported patch alchemy05_fixes_01 diffstat: eggs.ini | 10 +- lib/galaxy/model/mapping.py | 2 +- lib/galaxy/model/migrate/check.py | 2 +- lib/galaxy/model/migrate/versions/0003_security_and_libraries.py | 4 +- lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py | 2 +- lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py | 4 +- lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py | 2 +- lib/galaxy/model/migrate/versions/0008_galaxy_forms.py | 4 +- lib/galaxy/model/migrate/versions/0009_request_table.py | 2 +- lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py | 4 +- lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py | 4 +- lib/galaxy/model/migrate/versions/0012_user_address.py | 4 +- lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py | 2 +- lib/galaxy/model/migrate/versions/0017_library_item_indexes.py | 2 +- lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py | 2 +- lib/galaxy/model/migrate/versions/0019_request_library_folder.py | 4 +- lib/galaxy/model/migrate/versions/0020_library_upload_job.py | 4 +- lib/galaxy/model/orm/ext/assignmapper.py | 38 ++++++--- lib/galaxy/web/controllers/forms.py | 2 +- lib/galaxy/web/controllers/history.py | 2 +- lib/galaxy/web/controllers/page.py | 4 +- lib/galaxy/web/controllers/workflow.py | 22 ++-- lib/galaxy/web/framework/helpers/grids.py | 4 +- 23 files changed, 73 insertions(+), 57 deletions(-) diffs (502 lines): diff -r 80915982fdb2 -r 133252175425 eggs.ini --- a/eggs.ini Tue Nov 03 11:28:34 2009 -0500 +++ b/eggs.ini Tue Nov 03 12:52:00 2009 -0500 @@ -28,6 +28,7 @@ [eggs:noplatform] amqplib = 0.6.1 Beaker = 1.4 +decorator = 3.1.2 docutils = 0.4 elementtree = 1.2.6_20050316 lrucache = 0.2 @@ -41,8 +42,8 @@ PasteScript = 1.3.6 Routes = 1.6.3 simplejson = 1.5 -SQLAlchemy = 0.4.7p1 -sqlalchemy_migrate = 0.4.5 +SQLAlchemy = 0.5.6 +sqlalchemy_migrate = 0.5.4 Tempita = 0.1 twill = 0.9 WebError = 0.8a @@ -77,6 +78,7 @@ guppy =
http://pypi.python.org/packages/source/g/guppy/guppy-0.1.8.tar.gz
amqplib =
http://py-amqplib.googlecode.com/files/amqplib-0.6.1.tgz
Beaker =
http://cheeseshop.python.org/packages/source/B/Beaker/Beaker-1.4.tar.gz
+decorator =
http://pypi.python.org/packages/source/d/decorator/decorator-3.1.2.tar.gz
docutils =
http://downloads.sourceforge.net/docutils/docutils-0.4.tar.gz
elementtree =
http://effbot.org/downloads/elementtree-1.2.6-20050316.tar.gz
lrucache =
http://evan.prodromou.name/lrucache/lrucache-0.2.tar.gz
@@ -90,8 +92,8 @@ PSI =
http://pypi.python.org/packages/source/P/PSI/PSI-0.3b1.1.tar.gz
Routes =
http://pypi.python.org/packages/source/R/Routes/Routes-1.6.3.tar.gz
simplejson =
http://cheeseshop.python.org/packages/source/s/simplejson/simplejson-1.5.ta…
-SQLAlchemy =
http://pypi.python.org/packages/source/S/SQLAlchemy/SQLAlchemy-0.4.7p1.tar.…
-sqlalchemy_migrate =
http://pypi.python.org/packages/source/s/sqlalchemy-migrate/sqlalchemy-migr…
+SQLAlchemy =
http://pypi.python.org/packages/source/S/SQLAlchemy/SQLAlchemy-0.5.6.tar.gz
+sqlalchemy_migrate =
http://pypi.python.org/packages/source/s/sqlalchemy-migrate/sqlalchemy-migr…
Tempita =
http://pypi.python.org/packages/source/T/Tempita/Tempita-0.1.tar.gz
twill =
http://darcs.idyll.org/~t/projects/twill-0.9.tar.gz
WebError =
http://pypi.python.org/packages/source/W/WebError/WebError-0.8a.tar.gz
diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/mapping.py Tue Nov 03 12:52:00 2009 -0500 @@ -18,7 +18,7 @@ from sqlalchemy.ext.associationproxy import association_proxy metadata = MetaData() -context = Session = scoped_session( sessionmaker( autoflush=False, transactional=False ) ) +context = Session = scoped_session( sessionmaker( autoflush=False, autocommit=True ) ) # For backward compatibility with "context.current" context.current = Session diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/check.py --- a/lib/galaxy/model/migrate/check.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/check.py Tue Nov 03 12:52:00 2009 -0500 @@ -7,7 +7,7 @@ from migrate.versioning import repository, schema from sqlalchemy import * -from sqlalchemy.exceptions import NoSuchTableError +from sqlalchemy.exc import NoSuchTableError log = logging.getLogger( __name__ ) diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0003_security_and_libraries.py --- a/lib/galaxy/model/migrate/versions/0003_security_and_libraries.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0003_security_and_libraries.py Tue Nov 03 12:52:00 2009 -0500 @@ -1,6 +1,6 @@ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * from migrate.changeset import * @@ -20,7 +20,7 @@ from galaxy.model.custom_types import * metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) if migrate_engine.name == 'postgres': #
http://blog.pythonisito.com/2008/01/cascading-drop-table-with-sqlalchemy.ht…
diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py --- a/lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0004_indexes_and_defaults.py Tue Nov 03 12:52:00 2009 -0500 @@ -12,7 +12,7 @@ log.addHandler( handler ) metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) User_table = Table( "galaxy_user", metadata, autoload=True ) HistoryDatasetAssociation_table = Table( "history_dataset_association", metadata, autoload=True ) diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py --- a/lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0005_cleanup_datasets_fix.py Tue Nov 03 12:52:00 2009 -0500 @@ -23,7 +23,7 @@ metadata = MetaData( migrate_engine ) -context = scoped_session( sessionmaker( autoflush=False, transactional=False ) ) +context = scoped_session( sessionmaker( autoflush=False, autocommit=True ) ) ## classes @@ -662,7 +662,7 @@ log.debug( "Fixing a discrepancy concerning deleted shared history items." ) affected_items = 0 start_time = time.time() - for dataset in context.query( Dataset ).filter( and_( Dataset.c.deleted == True, Dataset.c.purged == False ) ): + for dataset in context.query( Dataset ).filter( and_( Dataset.deleted == True, Dataset.purged == False ) ): for dataset_instance in dataset.history_associations + dataset.library_associations: if not dataset_instance.deleted: dataset.deleted = False diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py --- a/lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0006_change_qual_datatype.py Tue Nov 03 12:52:00 2009 -0500 @@ -16,7 +16,7 @@ log.addHandler( handler ) metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) def display_migration_details(): print "========================================" diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0008_galaxy_forms.py --- a/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py Tue Nov 03 12:52:00 2009 -0500 @@ -11,7 +11,7 @@ """ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * from migrate.changeset import * @@ -31,7 +31,7 @@ from galaxy.model.custom_types import * metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) def display_migration_details(): print "========================================" diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0009_request_table.py --- a/lib/galaxy/model/migrate/versions/0009_request_table.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0009_request_table.py Tue Nov 03 12:52:00 2009 -0500 @@ -9,7 +9,7 @@ from migrate.changeset import * import sys, logging from galaxy.model.custom_types import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * log = logging.getLogger( __name__ ) log.setLevel(logging.DEBUG) diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py --- a/lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0010_hda_display_at_authz_table.py Tue Nov 03 12:52:00 2009 -0500 @@ -10,7 +10,7 @@ """ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * from migrate.changeset import * @@ -30,7 +30,7 @@ from galaxy.model.custom_types import * metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) def display_migration_details(): print "========================================" diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py --- a/lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0011_v0010_mysql_index_fix.py Tue Nov 03 12:52:00 2009 -0500 @@ -5,7 +5,7 @@ """ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * from migrate.changeset import * @@ -25,7 +25,7 @@ from galaxy.model.custom_types import * metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) def display_migration_details(): print "========================================" diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0012_user_address.py --- a/lib/galaxy/model/migrate/versions/0012_user_address.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0012_user_address.py Tue Nov 03 12:52:00 2009 -0500 @@ -6,7 +6,7 @@ """ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * from migrate.changeset import * import datetime @@ -24,7 +24,7 @@ log.addHandler( handler ) metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) def display_migration_details(): print "========================================" diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py --- a/lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0013_change_lib_item_templates_to_forms.py Tue Nov 03 12:52:00 2009 -0500 @@ -17,7 +17,7 @@ """ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * from migrate.changeset import * import sys, logging diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0017_library_item_indexes.py --- a/lib/galaxy/model/migrate/versions/0017_library_item_indexes.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0017_library_item_indexes.py Tue Nov 03 12:52:00 2009 -0500 @@ -16,7 +16,7 @@ log.addHandler( handler ) metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) LibraryFolder_table = Table( "library_folder", metadata, autoload=True ) LibraryDatasetDatasetAssociation_table = Table( "library_dataset_dataset_association", metadata, autoload=True ) LibraryDataset_table = Table( "library_dataset", metadata, autoload=True ) diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py --- a/lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0018_ordered_tags_and_page_tags.py Tue Nov 03 12:52:00 2009 -0500 @@ -5,7 +5,7 @@ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * import migrate.changeset diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0019_request_library_folder.py --- a/lib/galaxy/model/migrate/versions/0019_request_library_folder.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0019_request_library_folder.py Tue Nov 03 12:52:00 2009 -0500 @@ -1,6 +1,6 @@ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * from migrate.changeset import * import datetime @@ -18,7 +18,7 @@ log.addHandler( handler ) metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) def display_migration_details(): print "========================================" diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/migrate/versions/0020_library_upload_job.py --- a/lib/galaxy/model/migrate/versions/0020_library_upload_job.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/migrate/versions/0020_library_upload_job.py Tue Nov 03 12:52:00 2009 -0500 @@ -1,6 +1,6 @@ from sqlalchemy import * from sqlalchemy.orm import * -from sqlalchemy.exceptions import * +from sqlalchemy.exc import * from migrate import * from migrate.changeset import * import datetime @@ -18,7 +18,7 @@ log.addHandler( handler ) metadata = MetaData( migrate_engine ) -db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) def display_migration_details(): print "" diff -r 80915982fdb2 -r 133252175425 lib/galaxy/model/orm/ext/assignmapper.py --- a/lib/galaxy/model/orm/ext/assignmapper.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/model/orm/ext/assignmapper.py Tue Nov 03 12:52:00 2009 -0500 @@ -15,22 +15,36 @@ from sqlalchemy import util, exceptions import types -from sqlalchemy.orm import mapper, Query +from sqlalchemy.orm import Query +from sqlalchemy.orm import mapper as sqla_mapper -def _monkeypatch_session_method(name, session, class_, make_list=False): - def do(self, *args, **kwargs): - if make_list: - self = [ self ] - return getattr(session, name)( self, *args, **kwargs ) +def _monkeypatch_session_method( name, session, class_ ): + # TODO: eliminate this method by fixing the session flushes + def do( self, *args, **kwargs ): + if self not in session.deleted: + session.add( self ) + return session.flush() try: do.__name__ = name except: pass - if not hasattr(class_, name): - setattr(class_, name, do) - + if not hasattr( class_, name ): + setattr( class_, name, do ) +def session_mapper( scoped_session, class_, *args, **kwargs ): + def mapper( cls, *arg, **kw ): + validate = kw.pop( 'validate', False ) + if cls.__init__ is object.__init__: + def __init__( self, **kwargs ): + for key, value in kwargs.items(): + if validate: + if not cls_mapper.has_property( key ): + raise TypeError( "Invalid __init__ argument: '%s'" % key ) + setattr( self, key, value ) + cls.__init__ = __init__ + cls.query = scoped_session.query_property() + _monkeypatch_session_method( 'flush', scoped_session, cls ) + return sqla_mapper( cls, *arg, **kw ) + return mapper( class_, *args, **kwargs ) def assign_mapper( session, class_, *args, **kwargs ): - m = class_.mapper = session.mapper( class_, *args, **kwargs ) - for name in ( 'flush', ): - _monkeypatch_session_method( name, session, class_, make_list=True ) + m = class_.mapper = session_mapper( session, class_, *args, **kwargs ) return m diff -r 80915982fdb2 -r 133252175425 lib/galaxy/web/controllers/forms.py --- a/lib/galaxy/web/controllers/forms.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/web/controllers/forms.py Tue Nov 03 12:52:00 2009 -0500 @@ -450,7 +450,7 @@ # create corresponding row in the form_definition_current table fd.form_definition_current = fdc fdc.latest_form = fd - trans.sa_session.save_or_update( fdc ) + trans.sa_session.add( fdc ) trans.sa_session.flush() msg = "The new form named '%s' has been created. " % (fd.name) return fd, msg diff -r 80915982fdb2 -r 133252175425 lib/galaxy/web/controllers/history.py --- a/lib/galaxy/web/controllers/history.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/web/controllers/history.py Tue Nov 03 12:52:00 2009 -0500 @@ -726,7 +726,7 @@ share.history = history share.user = send_to_user session = trans.sa_session - session.save_or_update( share ) + session.add( share ) session.flush() if history not in shared_histories: shared_histories.append( history ) diff -r 80915982fdb2 -r 133252175425 lib/galaxy/web/controllers/page.py --- a/lib/galaxy/web/controllers/page.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/web/controllers/page.py Tue Nov 03 12:52:00 2009 -0500 @@ -135,7 +135,7 @@ page_revision.content = "" # Persist session = trans.sa_session - session.save_or_update( page ) + session.add( page ) session.flush() # Display the management page ## trans.set_message( "Page '%s' created" % page.title ) @@ -240,4 +240,4 @@ raise web.httpexceptions.HTTPNotFound() return trans.fill_template( "page/display.mako", page=page ) - \ No newline at end of file + diff -r 80915982fdb2 -r 133252175425 lib/galaxy/web/controllers/workflow.py --- a/lib/galaxy/web/controllers/workflow.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/web/controllers/workflow.py Tue Nov 03 12:52:00 2009 -0500 @@ -30,7 +30,7 @@ user = trans.get_user() workflows = trans.sa_session.query( model.StoredWorkflow ) \ .filter_by( user=user, deleted=False ) \ - .order_by( desc( model.StoredWorkflow.c.update_time ) ) \ + .order_by( desc( model.StoredWorkflow.table.c.update_time ) ) \ .all() shared_by_others = trans.sa_session \ .query( model.StoredWorkflowUserShareAssociation ) \ @@ -53,13 +53,13 @@ user = trans.get_user() workflows = trans.sa_session.query( model.StoredWorkflow ) \ .filter_by( user=user, deleted=False ) \ - .order_by( desc( model.StoredWorkflow.c.update_time ) ) \ + .order_by( desc( model.StoredWorkflow.table.c.update_time ) ) \ .all() shared_by_others = trans.sa_session \ .query( model.StoredWorkflowUserShareAssociation ) \ .filter_by( user=user ) \ - .filter( model.StoredWorkflow.c.deleted == False ) \ - .order_by( desc( model.StoredWorkflow.c.update_time ) ) \ + .filter( model.StoredWorkflow.deleted == False ) \ + .order_by( desc( model.StoredWorkflow.table.c.update_time ) ) \ .all() return trans.fill_template( "workflow/list_for_run.mako", workflows = workflows, @@ -91,7 +91,7 @@ share.stored_workflow = stored share.user = other session = trans.sa_session - session.save_or_update( share ) + session.add( share ) session.flush() trans.set_message( "Workflow '%s' shared with user '%s'" % ( stored.name, other.email ) ) return trans.response.send_redirect( url_for( controller='workflow', action='sharing', id=id ) ) @@ -142,7 +142,7 @@ share.stored_workflow = stored share.user = trans.user session = trans.sa_session - session.save_or_update( share ) + session.add( share ) session.flush() # Redirect to load galaxy frames. return trans.response.send_redirect( url_for( controller='workflow' ) ) @@ -180,7 +180,7 @@ new_stored.user = user # Persist session = trans.sa_session - session.save_or_update( new_stored ) + session.add( new_stored ) session.flush() # Display the management page trans.set_message( 'Clone created with name "%s"' % new_stored.name ) @@ -205,7 +205,7 @@ stored_workflow.latest_workflow = workflow # Persist session = trans.sa_session - session.save_or_update( stored_workflow ) + session.add( stored_workflow ) session.flush() # Display the management page trans.set_message( "Workflow '%s' created" % stored_workflow.name ) @@ -514,7 +514,7 @@ stored.name = workflow_name workflow.stored_workflow = stored stored.latest_workflow = workflow - trans.sa_session.save_or_update( stored ) + trans.sa_session.add( stored ) trans.sa_session.flush() # Index page with message return trans.show_message( "Workflow '%s' created from current history." % workflow_name ) @@ -656,12 +656,12 @@ ids_in_menu = set( [ x.stored_workflow_id for x in user.stored_workflow_menu_entries ] ) workflows = trans.sa_session.query( model.StoredWorkflow ) \ .filter_by( user=user, deleted=False ) \ - .order_by( desc( model.StoredWorkflow.c.update_time ) ) \ + .order_by( desc( model.StoredWorkflow.table.c.update_time ) ) \ .all() shared_by_others = trans.sa_session \ .query( model.StoredWorkflowUserShareAssociation ) \ .filter_by( user=user ) \ - .filter( model.StoredWorkflow.c.deleted == False ) \ + .filter( model.StoredWorkflow.deleted == False ) \ .all() return trans.fill_template( "workflow/configure_menu.mako", workflows=workflows, diff -r 80915982fdb2 -r 133252175425 lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/web/framework/helpers/grids.py Tue Nov 03 12:52:00 2009 -0500 @@ -111,10 +111,10 @@ if sort_key.startswith( "-" ): sort_key = sort_key[1:] sort_order = 'desc' - query = query.order_by( self.model_class.c.get( sort_key ).desc() ) + query = query.order_by( self.model_class.table.c.get( sort_key ).desc() ) else: sort_order = 'asc' - query = query.order_by( self.model_class.c.get( sort_key ).asc() ) + query = query.order_by( self.model_class.table.c.get( sort_key ).asc() ) extra_url_args['sort'] = encoded_sort_key # There might be a current row
1
0
0
0
[hg] galaxy 2946: Fix new functional tests script return code (f...
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/80915982fdb2
changeset: 2946:80915982fdb2 user: Nate Coraor <nate(a)bx.psu.edu> date: Tue Nov 03 11:28:34 2009 -0500 description: Fix new functional tests script return code (for buildbot) diffstat: scripts/functional_tests.py | 11 +++++++---- 1 files changed, 7 insertions(+), 4 deletions(-) diffs (34 lines): diff -r c96e886f883f -r 80915982fdb2 scripts/functional_tests.py --- a/scripts/functional_tests.py Tue Nov 03 10:26:42 2009 -0500 +++ b/scripts/functional_tests.py Tue Nov 03 11:28:34 2009 -0500 @@ -155,7 +155,7 @@ log.info( "Functional tests will be run against %s:%s" % ( galaxy_test_host, galaxy_test_port ) ) - rval = False + success = False try: @@ -186,7 +186,7 @@ result = test_runner.run( tests ) - rval = result.wasSuccessful() + success = result.wasSuccessful() except: log.exception( "Failure running tests" ) @@ -206,7 +206,10 @@ app = None log.info( "Embedded Universe application stopped" ) - return rval + if success: + return 0 + else: + return 1 if __name__ == "__main__": - main() + sys.exit( main() )
1
0
0
0
[hg] galaxy 2947: Improved search functionality for history grid...
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/f776fa6045ba
changeset: 2947:f776fa6045ba user: jeremy goecks <jeremy.goecks(a)emory.edu> date: Tue Nov 03 12:58:13 2009 -0500 description: Improved search functionality for history grid: (a) generic free text search box and (b) advanced search using name, tag, deleted status, and shared status. diffstat: lib/galaxy/tags/tag_handler.py | 8 +- lib/galaxy/web/controllers/history.py | 145 +++++++++++++++++--- lib/galaxy/web/framework/helpers/grids.py | 1 + static/scripts/autocomplete_tagging.js | 2 +- templates/history/grid.mako | 250 +++++++++++++++++++--------------- test/base/twilltestcase.py | 18 +- test/functional/test_history_functions.py | 2 +- 7 files changed, 278 insertions(+), 148 deletions(-) diffs (649 lines): diff -r 80915982fdb2 -r f776fa6045ba lib/galaxy/tags/tag_handler.py --- a/lib/galaxy/tags/tag_handler.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/tags/tag_handler.py Tue Nov 03 12:58:13 2009 -0500 @@ -3,6 +3,12 @@ class TagHandler( object ): + # Minimum tag length. + min_tag_len = 2 + + # Maximum tag length. + max_tag_len = 255 + # Tag separator. tag_separators = ',;' @@ -215,7 +221,7 @@ scrubbed_name = scrubbed_name[1:] # If name is too short or too long, return None. - if len(scrubbed_name) < 3 or len(scrubbed_name) > 255: + if len(scrubbed_name) < self.min_tag_len or len(scrubbed_name) > self.max_tag_len: return None return scrubbed_name diff -r 80915982fdb2 -r f776fa6045ba lib/galaxy/web/controllers/history.py --- a/lib/galaxy/web/controllers/history.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/web/controllers/history.py Tue Nov 03 12:58:13 2009 -0500 @@ -5,6 +5,7 @@ from galaxy.model import History from galaxy.model.orm import * from galaxy.util.json import * +from galaxy.util.odict import odict from galaxy.tags.tag_handler import TagHandler from sqlalchemy.sql.expression import ClauseElement import webhelpers, logging, operator @@ -19,12 +20,29 @@ class HistoryListGrid( grids.Grid ): # Custom column types class NameColumn( grids.GridColumn ): - def __init( self, key, link, attach_popup ): + def __init( self, key, link, attach_popup, filterable ): grids.GridColumn.__init__(self, key, link, attach_popup) def get_value( self, trans, grid, history ): return history.get_display_name() + def filter( self, db_session, query, column_filter ): + """ Modify query to filter histories by name. """ + if column_filter == "All": + pass + elif column_filter: + query = query.filter( func.lower( History.name ).like( "%" + column_filter.lower() + "%" ) ) + return query + def get_accepted_filters( self ): + """ Returns a list of accepted filters for this column. """ + accepted_filter_labels_and_vals = odict() + accepted_filter_labels_and_vals["FREETEXT"] = "FREETEXT" + accepted_filters = [] + for label, val in accepted_filter_labels_and_vals.iteritems(): + args = { self.key: val } + accepted_filters.append( grids.GridColumnFilter( label, args) ) + return accepted_filters + class DatasetsByStateColumn( grids.GridColumn ): def get_value( self, trans, grid, history ): rval = [] @@ -48,6 +66,7 @@ if item.users_shared_with or item.importable: return dict( operation="sharing" ) return None + class TagsColumn( grids.GridColumn ): def __init__( self, col_name, key, filterable ): grids.GridColumn.__init__(self, col_name, key=key, filterable=filterable) @@ -61,7 +80,7 @@ return div_elt + trans.fill_template( "/tagging_common.mako", trans=trans, tagged_item=history, elt_id = elt_id, in_form="true", input_size="20", tag_click_fn="add_tag_to_grid_filter" ) def filter( self, db_session, query, column_filter ): - """ Modify query to include only histories with tags in column_filter. """ + """ Modify query to filter histories by tag. """ if column_filter == "All": pass elif column_filter: @@ -69,52 +88,115 @@ tag_handler = TagHandler() raw_tags = tag_handler.parse_tags( column_filter.encode("utf-8") ) for name, value in raw_tags.items(): - tag = tag_handler.get_tag_by_name( db_session, name ) - if tag: - query = query.filter( History.tags.any( tag_id=tag.id ) ) + if name: + # Search for tag names. + query = query.filter( History.tags.any( func.lower( model.HistoryTagAssociation.user_tname ).like( "%" + name.lower() + "%" ) ) ) if value: - query = query.filter( History.tags.any( value=value.lower() ) ) - else: - # Tag doesn't exist; unclear what to do here, but the literal thing to do is add the criterion, which - # will then yield a query that returns no results. - query = query.filter( History.tags.any( user_tname=name ) ) + # Search for tag values. + query = query.filter( History.tags.any( func.lower( model.HistoryTagAssociation.user_value ).like( "%" + value.lower() + "%" ) ) ) return query def get_accepted_filters( self ): - """ Returns a list of accepted filters for this column. """ - accepted_filter_labels_and_vals = { "All": "All" } - accepted_filters = [] - for label, val in accepted_filter_labels_and_vals.items(): - args = { self.key: val } - accepted_filters.append( grids.GridColumnFilter( label, args) ) - return accepted_filters - - + """ Returns a list of accepted filters for this column. """ + accepted_filter_labels_and_vals = odict() + accepted_filter_labels_and_vals["FREETEXT"] = "FREETEXT" + accepted_filters = [] + for label, val in accepted_filter_labels_and_vals.iteritems(): + args = { self.key: val } + accepted_filters.append( grids.GridColumnFilter( label, args) ) + return accepted_filters + class DeletedColumn( grids.GridColumn ): def get_accepted_filters( self ): """ Returns a list of accepted filters for this column. """ - accepted_filter_labels_and_vals = { "Active" : "False", "Deleted" : "True", "All": "All" } + accepted_filter_labels_and_vals = { "active" : "False", "deleted" : "True", "all": "All" } accepted_filters = [] for label, val in accepted_filter_labels_and_vals.items(): args = { self.key: val } accepted_filters.append( grids.GridColumnFilter( label, args) ) return accepted_filters + + class SharingColumn( grids.GridColumn ): + def filter( self, db_session, query, column_filter ): + """ Modify query to filter histories by sharing status. """ + if column_filter == "All": + pass + elif column_filter: + if column_filter == "private": + query = query.filter( History.users_shared_with == None ) + query = query.filter( History.importable == False ) + elif column_filter == "shared": + query = query.filter( History.users_shared_with != None ) + elif column_filter == "importable": + query = query.filter( History.importable == True ) + return query + def get_accepted_filters( self ): + """ Returns a list of accepted filters for this column. """ + accepted_filter_labels_and_vals = odict() + accepted_filter_labels_and_vals["private"] = "private" + accepted_filter_labels_and_vals["shared"] = "shared" + accepted_filter_labels_and_vals["importable"] = "importable" + accepted_filter_labels_and_vals["all"] = "All" + accepted_filters = [] + for label, val in accepted_filter_labels_and_vals.items(): + args = { self.key: val } + accepted_filters.append( grids.GridColumnFilter( label, args) ) + return accepted_filters + + class FreeTextSearchColumn( grids.GridColumn ): + def filter( self, db_session, query, column_filter ): + """ Modify query to search tags and history names. """ + if column_filter == "All": + pass + elif column_filter: + # Build tags filter. + tag_handler = TagHandler() + raw_tags = tag_handler.parse_tags( column_filter.encode("utf-8") ) + tags_filter = None + for name, value in raw_tags.items(): + if name: + # Search for tag names. + tags_filter = History.tags.any( func.lower( model.HistoryTagAssociation.user_tname ).like( "%" + name.lower() + "%" ) ) + if value: + # Search for tag values. + tags_filter = and_( tags_filter, func.lower( History.tags.any( model.HistoryTagAssociation.user_value ).like( "%" + value.lower() + "%" ) ) ) + + # Build history name filter. + history_name_filter = func.lower( History.name ).like( "%" + column_filter.lower() + "%" ) + + # Apply filters to query. + if tags_filter: + query = query.filter( or_( tags_filter, history_name_filter ) ) + else: + query = query.filter( history_name_filter ) + return query + def get_accepted_filters( self ): + """ Returns a list of accepted filters for this column. """ + accepted_filter_labels_and_vals = odict() + accepted_filter_labels_and_vals["FREETEXT"] = "FREETEXT" + accepted_filters = [] + for label, val in accepted_filter_labels_and_vals.iteritems(): + args = { self.key: val } + accepted_filters.append( grids.GridColumnFilter( label, args) ) + return accepted_filters # Grid definition - title = "Stored histories" + title = "Saved Histories" model_class = model.History template='/history/grid.mako' default_sort_key = "-create_time" columns = [ NameColumn( "Name", key="name", link=( lambda history: iff( history.deleted, None, dict( operation="switch", id=history.id ) ) ), - attach_popup=True ), + attach_popup=True, filterable=True ), DatasetsByStateColumn( "Datasets (by state)", ncells=4 ), TagsColumn( "Tags", key="tags", filterable=True), StatusColumn( "Status", attach_popup=False ), grids.GridColumn( "Created", key="create_time", format=time_ago ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), - # Valid for filtering but invisible - DeletedColumn( "Status", key="deleted", visible=False, filterable=True ) + # Columns that are valid for filtering but are not visible. + DeletedColumn( "Deleted", key="deleted", visible=False, filterable=True ), + SharingColumn( "Shared", key="shared", visible=False, filterable=True ), + FreeTextSearchColumn( "Search", key="free-text-search", visible=False ) # Not filterable because it's the default search. ] operations = [ grids.GridOperation( "Switch", allow_multiple=False, condition=( lambda item: not item.deleted ) ), @@ -131,7 +213,7 @@ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), grids.GridColumnFilter( "All", args=dict( deleted='All' ) ), ] - default_filter = dict( deleted="False", tags="All" ) + default_filter = dict( name="All", deleted="False", tags="All", shared="All" ) num_rows_per_page = 50 preserve_state = False use_paging = True @@ -160,6 +242,7 @@ template='/history/grid.mako' model_class = model.History default_sort_key = "-update_time" + default_filter = {} columns = [ grids.GridColumn( "Name", key="name" ), DatasetsByStateColumn( "Datasets (by state)", ncells=4 ), @@ -374,6 +457,18 @@ trans.sa_session.flush() @web.expose + def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ): + """Return autocomplete data for history names""" + user = trans.get_user() + if not user: + return + + ac_data = "" + for history in trans.sa_session.query( History ).filter_by( user=user ).filter( func.lower( History.name ) .like(q.lower() + "%") ): + ac_data = ac_data + history.name + "\n" + return ac_data + + @web.expose def imp( self, trans, id=None, confirm=False, **kwd ): """Import another user's history via a shared URL""" msg = "" diff -r 80915982fdb2 -r f776fa6045ba lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py Tue Nov 03 11:28:34 2009 -0500 +++ b/lib/galaxy/web/framework/helpers/grids.py Tue Nov 03 12:58:13 2009 -0500 @@ -183,6 +183,7 @@ query=query, cur_page_num = page_num, num_pages = num_pages, + default_filter_dict=self.default_filter, cur_filter_dict=cur_filter_dict, sort_key=sort_key, encoded_sort_key=encoded_sort_key, diff -r 80915982fdb2 -r f776fa6045ba static/scripts/autocomplete_tagging.js --- a/static/scripts/autocomplete_tagging.js Tue Nov 03 11:28:34 2009 -0500 +++ b/static/scripts/autocomplete_tagging.js Tue Nov 03 12:58:13 2009 -0500 @@ -309,7 +309,7 @@ new_value = new_value.replace(/^\s+|\s+$/g,""); // Too short? - if (new_value.length < 3) + if (new_value.length < 2) return false; // diff -r 80915982fdb2 -r f776fa6045ba templates/history/grid.mako --- a/templates/history/grid.mako Tue Nov 03 11:28:34 2009 -0500 +++ b/templates/history/grid.mako Tue Nov 03 12:58:13 2009 -0500 @@ -29,71 +29,20 @@ }); // Set up autocomplete for tag filter input. - var t = $("#input-tag-filter"); - t.keyup( function( e ) - { - if ( e.keyCode == 27 ) - { - // Escape key - $(this).trigger( "blur" ); - } else if ( - ( e.keyCode == 13 ) || // Return Key - ( e.keyCode == 188 ) || // Comma - ( e.keyCode == 32 ) // Space - ) - { - // - // Check input. - // - - new_value = this.value; - - // Do nothing if return key was used to autocomplete. - if (return_key_pressed_for_autocomplete == true) - { - return_key_pressed_for_autocomplete = false; - return false; - } - - // Suppress space after a ":" - if ( new_value.indexOf(": ", new_value.length - 2) != -1) - { - this.value = new_value.substring(0, new_value.length-1); - return false; - } - - // Remove trigger keys from input. - if ( (e.keyCode == 188) || (e.keyCode == 32) ) - new_value = new_value.substring( 0 , new_value.length - 1 ); - - // Trim whitespace. - new_value = new_value.replace(/^\s+|\s+$/g,""); - - // Too short? - if (new_value.length < 3) - return false; - - // - // New tag OK. - // - } - }); + var t = $("#input-tags-filter"); - // Add autocomplete to input. - var format_item_func = function(key, row_position, num_rows, value, search_term) - { - tag_name_and_value = value.split(":"); - return (tag_name_and_value.length == 1 ? tag_name_and_value[0] :tag_name_and_value[1]); - //var array = new Array(key, value, row_position, num_rows, - //search_term ); return "\"" + array.join("*") + "\""; - } var autocomplete_options = - { selectFirst: false, formatItem : format_item_func, autoFill: false, highlight: false, mustMatch: true }; + { selectFirst: false, autoFill: false, highlight: false, mustMatch: false }; t.autocomplete("${h.url_for( controller='tag', action='tag_autocomplete_data', item_class='History' )}", autocomplete_options); - - $("#page-select").change(navigate_to_page); + // Set up autocomplete for name filter input. + var t2 = $("#input-name-filter"); + + var autocomplete_options = + { selectFirst: false, autoFill: false, highlight: false, mustMatch: false }; + + t2.autocomplete("${h.url_for( controller='history', action='name_autocomplete_data' )}", autocomplete_options); }); ## Can this be moved into base.mako? %if refresh_frames: @@ -125,21 +74,52 @@ %endif %endif + // Filter and sort args for grid. + var filter_args = ${h.to_json_string(cur_filter_dict)}; + var sort_key = "${sort_key}"; + // - // Add a tag to the current grid filter; this adds the tag to the filter and then issues a request to refresh the grid. + // Add tag to grid filter. // function add_tag_to_grid_filter(tag_name, tag_value) { - // Use tag as a filter: replace TAGNAME with tag_name and issue query. - <% - url_args = {} - if "tags" in cur_filter_dict and cur_filter_dict["tags"] != "All": - url_args["f-tags"] = cur_filter_dict["tags"].encode("utf-8") + ", TAGNAME" - else: - url_args["f-tags"] = "TAGNAME" - %> - var url_base = "${url( url_args )}"; - var url = url_base.replace("TAGNAME", tag_name); + // Put tag name and value together. + var tag = tag_name + (tag_value != null && tag_value != "" ? ":" + tag_value : ""); + add_condition_to_grid_filter("tags", tag, true); + } + + // + // Add a filter to the current grid filter; this adds the filter and then issues a request to refresh the grid. + // + function add_condition_to_grid_filter(name, value, append) + { + // Update filter arg with new condition. + if (append) + { + // Append value. + var cur_val = filter_args[name]; + if (cur_val != "All") + cur_val = cur_val + ", " + value; + else + cur_val = value; + filter_args[name] = cur_val; + } + else + { + // Replace value. + filter_args[name] = value; + } + + // Build URL with filter args, sort key. + var filter_arg_value_strs = new Array(); + var i = 0; + for (arg in filter_args) + { + filter_arg_value_strs[i++] = "f-" + arg + "=" + filter_args[arg]; + } + var filter_str = filter_arg_value_strs.join("&"); + var url_base = "${h.url_for( controller='history', action='list')}"; + var url = url_base + "?" + filter_str + "&sort=" + sort_key; self.location = url; } @@ -154,7 +134,7 @@ var url = url_base.replace("PAGE", page_num); self.location = url; } - + </script> </%def> @@ -175,47 +155,95 @@ <div class="grid-header"> <h2>${grid.title}</h2> - - ## Print grid filter. - <form name="history_actions" action="javascript:add_tag_to_grid_filter($('#input-tag-filter').attr('value'))" method="get" > - <strong>Filter: </strong> - %for column in grid.columns: - %if column.filterable: - <span> by ${column.label.lower()}:</span> - ## For now, include special case to handle tags. - %if column.key == "tags": - %if cur_filter_dict[column.key] != "All": - <span class="filter" "style='font-style: italic'"> - ${cur_filter_dict[column.key]} - </span> - <span>|</span> + + ## Search box and more options filter at top of grid. + <div> + ## Grid search. TODO: use more elegant way to get free text search column. + <% column = grid.columns[-1] %> + <% use_form = False %> + %for i, filter in enumerate( column.get_accepted_filters() ): + %if i > 0: + <span>|</span> + %endif + %if column.key in cur_filter_dict and cur_filter_dict[column.key] == filter.args[column.key]: + <span class="filter" "style='font-style: italic'">${filter.label}</span> + %elif filter.label == "FREETEXT": + <form name="history_actions" + action="javascript:add_condition_to_grid_filter($('#input-${column.key}-filter').attr('name'),$('#input-${column.key}-filter').attr('value'),false)" + method="get" > + ${column.label}: + %if column.key in cur_filter_dict and cur_filter_dict[column.key] != "All": + <span style="font-style: italic">${cur_filter_dict[column.key]}</span> + <% filter_all = GridColumnFilter( "", { column.key : "All" } ) %> + <a href="${url( filter_all.get_url_args() )}"><img src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a> + | %endif - <input id="input-tag-filter" name="f-tags" type="text" value="" size="15"/> - <span>|</span> - %endif - - ## Handle other columns. - %for i, filter in enumerate( column.get_accepted_filters() ): - %if i > 0: - <span>|</span> - %endif - %if cur_filter_dict[column.key] == filter.args[column.key]: - <span class="filter" "style='font-style: italic'">${filter.label}</span> - %else: - <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span> - %endif - %endfor - <span> </span> + <span><input id="input-${column.key}-filter" name="${column.key}" type="text" value="" size="15"/></span> + <% use_form = True %> + %else: + <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span> %endif %endfor - - ## Link to clear all filters. TODO: this should be the default filter or an empty filter. - <% - args = { "deleted" : "False", "tags" : "All" } - no_filter = GridColumnFilter("Clear Filter", args) - %> - <span><a href="${url( no_filter.get_url_args() )}">${no_filter.label}</a></span> - </form> + | <a href="" onclick="javascript:$('#more-search-options').slideToggle('fast');return false;">Advanced Search</a> + %if use_form: + </form> + %endif + </div> + + ## Advanced Search + <div id="more-search-options" style="display: none; padding-top: 5px"> + <table style="border: 1px solid gray;"> + <tr><td style="text-align: left" colspan="100"> + Advanced Search | + <a href=""# onclick="javascript:$('#more-search-options').slideToggle('fast');return false;">Close</a> | + ## Link to clear all filters. + <% + no_filter = GridColumnFilter("Clear All", default_filter_dict) + %> + <a href="${url( no_filter.get_url_args() )}">${no_filter.label}</a> + </td></tr> + %for column in grid.columns: + %if column.filterable: + <tr> + ## Show div if current filter has value that is different from the default filter. + %if cur_filter_dict[column.key] != default_filter_dict[column.key]: + <script type="text/javascript"> + $('#more-search-options').css("display", "block"); + </script> + %endif + <td style="padding-left: 10px">${column.label.lower()}:</td> + <td> + <% use_form = False %> + %for i, filter in enumerate( column.get_accepted_filters() ): + %if i > 0: + <span>|</span> + %endif + %if cur_filter_dict[column.key] == filter.args[column.key]: + <span class="filter" style="font-style: italic">${filter.label}</span> + %elif filter.label == "FREETEXT": + <form name="history_actions" action="javascript:add_condition_to_grid_filter($('#input-${column.key}-filter').attr('name'),$('#input-${column.key}-filter').attr('value'),true)" + method="get" > + %if column.key in cur_filter_dict and cur_filter_dict[column.key] != "All": + <span style="font-style: italic">${cur_filter_dict[column.key]}</span> + <% filter_all = GridColumnFilter( "", { column.key : "All" } ) %> + <a href="${url( filter_all.get_url_args() )}"><img src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a> + | + %endif + <span><input id="input-${column.key}-filter" name="${column.key}" type="text" value="" size="15"/></span> + <% use_form = True %> + %else: + <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span> + %endif + %endfor + %if use_form: + </form> + %endif + </td> + </tr> + %endif + %endfor + </table> + </div> </div> <form name="history_actions" action="${url()}" method="post" > <input type="hidden" name="page" value="${cur_page_num}"> @@ -291,7 +319,7 @@ extra = "" %> %if href: - <td><div class="menubutton split" style="float: left;"><a class="label" href="${href}">${v}${extra}</a> </td> + <td><div class="menubutton split" style="float: left;"><a class="label" href="${href}">${v}</a>${extra}</td> %else: <td >${v}${extra}</td> %endif diff -r 80915982fdb2 -r f776fa6045ba test/base/twilltestcase.py --- a/test/base/twilltestcase.py Tue Nov 03 11:28:34 2009 -0500 +++ b/test/base/twilltestcase.py Tue Nov 03 12:58:13 2009 -0500 @@ -1,7 +1,7 @@ import pkg_resources pkg_resources.require( "twill==0.9" ) -import StringIO, os, sys, random, filecmp, time, unittest, urllib, logging, difflib, zipfile, tempfile +import StringIO, os, sys, random, filecmp, time, unittest, urllib, logging, difflib, zipfile, tempfile, re from itertools import * import twill @@ -311,20 +311,20 @@ def view_stored_active_histories( self, check_str='' ): self.home() self.visit_page( "history/list" ) - self.check_page_for_string( 'Stored histories' ) + self.check_page_for_string( 'Saved Histories' ) self.check_page_for_string( '<input type="checkbox" name="id" value=' ) - self.check_page_for_string( 'operation=Rename&id' ) - self.check_page_for_string( 'operation=Switch&id' ) - self.check_page_for_string( 'operation=Delete&id' ) + self.check_page_for_string( 'operation=Rename' ) + self.check_page_for_string( 'operation=Switch' ) + self.check_page_for_string( 'operation=Delete' ) if check_str: self.check_page_for_string( check_str ) self.home() def view_stored_deleted_histories( self, check_str='' ): self.home() self.visit_page( "history/list?f-deleted=True" ) - self.check_page_for_string( 'Stored histories' ) + self.check_page_for_string( 'Saved Histories' ) self.check_page_for_string( '<input type="checkbox" name="id" value=' ) - self.check_page_for_string( 'operation=Undelete&id' ) + self.check_page_for_string( 'operation=Undelete' ) if check_str: self.check_page_for_string( check_str ) self.home() @@ -723,14 +723,14 @@ # Functions associated with browsers, cookies, HTML forms and page visits def check_page_for_string( self, patt ): - """Looks for 'patt' in the current browser page""" + """Looks for 'patt' in the current browser page""" page = self.last_page() for subpatt in patt.split(): if page.find( patt ) == -1: fname = self.write_temp_file( page ) errmsg = "no match to '%s'\npage content written to '%s'" % ( patt, fname ) raise AssertionError( errmsg ) - + def write_temp_file( self, content ): fd, fname = tempfile.mkstemp( suffix='.html', prefix='twilltestcase-' ) f = os.fdopen( fd, "w" ) diff -r 80915982fdb2 -r f776fa6045ba test/functional/test_history_functions.py --- a/test/functional/test_history_functions.py Tue Nov 03 11:28:34 2009 -0500 +++ b/test/functional/test_history_functions.py Tue Nov 03 12:58:13 2009 -0500 @@ -179,7 +179,7 @@ self.share_current_history( regular_user1.email, check_str=history3.name ) # Check out list of histories to make sure history3 was shared - self.view_stored_active_histories( check_str='operation=sharing">shared' ) + self.view_stored_active_histories( check_str='operation=sharing' ) # Enable importing history3 via a URL self.enable_import_via_link( self.security.encode_id( history3.id ), check_str='Unshare',
1
0
0
0
[hg] galaxy 2944: Changed the test user emails in the user_info ...
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/7594fc81bdfc
changeset: 2944:7594fc81bdfc user: rc date: Tue Nov 03 07:58:24 2009 -0500 description: Changed the test user emails in the user_info functional tests diffstat: test/functional/test_user_info.py | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-) diffs (60 lines): diff -r 6e657b4aa837 -r 7594fc81bdfc test/functional/test_user_info.py --- a/test/functional/test_user_info.py Mon Nov 02 16:52:32 2009 -0500 +++ b/test/functional/test_user_info.py Tue Nov 03 07:58:24 2009 -0500 @@ -75,7 +75,7 @@ # user a new user with 'Student' user info form form_one = get_latest_form(form_one_name) user_info_values=['Educational', 'Penn State'] - self.create_user_with_info( 'test1(a)bx.psu.edu', 'testuser', 'test1', + self.create_user_with_info( 'test11(a)bx.psu.edu', 'testuser', 'test11', user_info_forms='multiple', user_info_form_id=form_one.id, user_info_values=user_info_values ) @@ -98,7 +98,7 @@ # user a new user with 'Student' user info form form_one = get_latest_form(form_one_name) user_info_values=['Educational', 'Penn State'] - self.create_user_with_info( 'test2(a)bx.psu.edu', 'testuser', 'test2', + self.create_user_with_info( 'test12(a)bx.psu.edu', 'testuser', 'test12', user_info_forms='single', user_info_form_id=form_one.id, user_info_values=user_info_values ) @@ -110,31 +110,31 @@ def test_015_edit_user_info( self ): """Testing editing user info as a regular user""" self.logout() - self.login( 'test1(a)bx.psu.edu' ) + self.login( 'test11(a)bx.psu.edu' ) user = sa_session.query( galaxy.model.User ) \ - .filter( and_( galaxy.model.User.table.c.email=='test1(a)bx.psu.edu' ) ).first() - self.edit_login_info( new_email='test1_new(a)bx.psu.edu', new_username='test1_new' ) + .filter( and_( galaxy.model.User.table.c.email=='test11(a)bx.psu.edu' ) ).first() + self.edit_login_info( new_email='test11_new(a)bx.psu.edu', new_username='test11_new' ) self.change_password('testuser', 'new_testuser') self.edit_user_info( ['Research', 'PSU'] ) def test_020_create_user_as_admin( self ): ''' Testing creating users as an admin ''' self.logout() - self.login( 'test2(a)bx.psu.edu' ) + self.login( 'test(a)bx.psu.edu' ) form_one = get_latest_form(form_one_name) user_info_values=['Educational', 'Penn State'] - self.create_user_with_info( 'test3(a)bx.psu.edu', 'testuser', 'test3', + self.create_user_with_info( 'test13(a)bx.psu.edu', 'testuser', 'test13', user_info_forms='single', user_info_form_id=form_one.id, user_info_values=user_info_values ) self.logout() self.login( 'test(a)bx.psu.edu' ) user = sa_session.query( galaxy.model.User ) \ - .filter( and_( galaxy.model.User.table.c.email=='test3(a)bx.psu.edu' ) ).first() + .filter( and_( galaxy.model.User.table.c.email=='test13(a)bx.psu.edu' ) ).first() self.home() page = "admin/users?id=%s&operation=information&f-deleted=False" % self.security.encode_id( user.id ) self.visit_page( page ) self.check_page_for_string( 'Manage User Information' ) - self.check_page_for_string( 'test3(a)bx.psu.edu' ) + self.check_page_for_string( 'test13(a)bx.psu.edu' ) for value in user_info_values: self.check_page_for_string( value ) # lets delete the 'Student' user info form
1
0
0
0
[hg] galaxy 2945: Fixed a bug in the user selectbox in new reque...
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/c96e886f883f
changeset: 2945:c96e886f883f user: rc date: Tue Nov 03 10:26:42 2009 -0500 description: Fixed a bug in the user selectbox in new requests page diffstat: lib/galaxy/web/controllers/requests_admin.py | 9 +++------ 1 files changed, 3 insertions(+), 6 deletions(-) diffs (34 lines): diff -r 7594fc81bdfc -r c96e886f883f lib/galaxy/web/controllers/requests_admin.py --- a/lib/galaxy/web/controllers/requests_admin.py Tue Nov 03 07:58:24 2009 -0500 +++ b/lib/galaxy/web/controllers/requests_admin.py Tue Nov 03 10:26:42 2009 -0500 @@ -377,8 +377,10 @@ msg=msg, messagetype=messagetype) def __select_user(self, trans, userid): + user_list = trans.sa_session.query( trans.app.model.User )\ + .order_by( trans.app.model.User.email.asc() ) user_ids = ['none'] - for user in trans.sa_session.query( trans.app.model.User ): + for user in user_list: if not user.deleted: user_ids.append(str(user.id)) select_user = SelectField('select_user', @@ -388,10 +390,6 @@ select_user.add_option('Select one', 'none', selected=True) else: select_user.add_option('Select one', 'none') - def __get_email(user): - return user.email - user_list = trans.sa_session.query( trans.app.model.User ) - #user_list.sort(key=__get_email) for user in user_list: if not user.deleted: if userid == str(user.id): @@ -399,7 +397,6 @@ else: select_user.add_option(user.email, user.id) return select_user - def __library_ui(self, trans, user, request=None, **kwd): ''' This method creates the data library & folder selectbox for new &
1
0
0
0
[hg] galaxy 2938: trackster: fix window resizing, wrong tiles be...
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/3aa07fc9512a
changeset: 2938:3aa07fc9512a user: Kanwei Li <kanwei(a)gmail.com> date: Fri Oct 30 21:55:17 2009 -0400 description: trackster: fix window resizing, wrong tiles being displayed diffstat: static/scripts/trackster.js | 27 ++++++++++++++------------- 1 files changed, 14 insertions(+), 13 deletions(-) diffs (92 lines): diff -r dac542cca894 -r 3aa07fc9512a static/scripts/trackster.js --- a/static/scripts/trackster.js Fri Oct 30 14:27:48 2009 -0400 +++ b/static/scripts/trackster.js Fri Oct 30 21:55:17 2009 -0400 @@ -7,7 +7,7 @@ DATA_NONE = "No data for this chrom/contig.", DATA_PENDING = "Currently indexing... please wait", DATA_LOADING = "Loading data...", - CACHED_TILES = 5, + CACHED_TILES = 10, CACHED_DATA = 20, CONTEXT = $("<canvas></canvas>").get(0).getContext("2d"), RIGHT_STRAND, LEFT_STRAND; @@ -22,7 +22,6 @@ left_img.onload = function() { LEFT_STRAND = CONTEXT.createPattern(left_img, "repeat"); } - var right_img_inv = new Image(); right_img_inv.src = "../images/visualization/strand_right_inv.png"; right_img_inv.onload = function() { @@ -182,10 +181,10 @@ var tile_index = Math.floor( low / resolution / DENSITY ); while ( ( tile_index * DENSITY * resolution ) < high ) { // Check in cache - var key = this.view.zoom_level + "_" + tile_index; + var key = this.content_div.width() + '_' + this.view.zoom_level + '_' + tile_index; var cached = this.tile_cache.get(key); if ( cached ) { - // console.log("cached tile"); + // console.log("cached tile " + tile_index); var tile_low = tile_index * DENSITY * resolution; cached.css( { left: ( tile_low - this.view.low ) * w_scale @@ -194,9 +193,9 @@ parent_element.append( cached ); } else { tile_element = this.draw_tile( resolution, tile_index, parent_element, w_scale ); - } - if ( tile_element ) { - this.tile_cache.set(key, tile_element); + if ( tile_element ) { + this.tile_cache.set(key, tile_element); + } } tile_index += 1; } @@ -384,7 +383,7 @@ calc_slots: function( include_labels ) { // console.log("num vals: " + this.values.length); var end_ary = [], - scale = this.container_div.width() / (this.view.high - this.view.low), + scale = this.content_div.width() / (this.view.high - this.view.low), labels_scale = this.show_labels_scale, max_high = this.view.max_high, max_low = this.view.max_low; @@ -396,14 +395,15 @@ for (var i = 0, len = this.values.length; i < len; i++) { var f_start, f_end, feature = this.values[i]; if (include_labels) { - f_start = Math.floor( Math.max(max_low, (feature.start - max_low) * labels_scale) ); + f_start = Math.floor( (feature.start - max_low) * labels_scale ); f_start -= dummy_canvas.measureText(feature.name).width; - f_end = Math.ceil( Math.min(max_high, (feature.end - max_low) * labels_scale) ); + f_end = Math.ceil( (feature.end - max_low) * labels_scale ); } else { - f_start = Math.floor( Math.max(max_low, (feature.start - max_low) * scale) ); - f_end = Math.ceil( Math.min(max_high, (feature.end - max_low) * scale) ); + f_start = Math.floor( (feature.start - max_low) * scale ); + f_end = Math.ceil( (feature.end - max_low) * scale ); } // if (include_labels) { console.log(f_start, f_end); } + var j = 0; while (true) { if (end_ary[j] === undefined || end_ary[j] < f_start) { @@ -425,7 +425,7 @@ if (!this.values) { // Still loading return null; } - // console.log("drawing"); + // console.log("drawing " + tile_index); // Once we zoom in enough, show name labels if (w_scale > this.show_labels_scale && !this.showing_labels) { this.showing_labels = true; @@ -480,6 +480,7 @@ // Showing labels, blocks, details if (ctx.fillText) { ctx.fillText(feature.name, f_start - 1, y_center + 8); + // ctx.fillText(commatize(feature.start), f_start - 1, y_center + 8); } var blocks = feature.blocks; if (blocks) {
1
0
0
0
[hg] galaxy 2939: Bug fix for cleanup_datasets.py - use app.sa_s...
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/6e8263ea83fa
changeset: 2939:6e8263ea83fa user: Greg Von Kuster <greg(a)bx.psu.edu> date: Mon Nov 02 09:54:18 2009 -0500 description: Bug fix for cleanup_datasets.py - use app.sa_session instead of self.sa_session. diffstat: scripts/cleanup_datasets/cleanup_datasets.py | 90 +++++++++++++++--------------- 1 files changed, 45 insertions(+), 45 deletions(-) diffs (156 lines): diff -r 3aa07fc9512a -r 6e8263ea83fa scripts/cleanup_datasets/cleanup_datasets.py --- a/scripts/cleanup_datasets/cleanup_datasets.py Fri Oct 30 21:55:17 2009 -0400 +++ b/scripts/cleanup_datasets/cleanup_datasets.py Mon Nov 02 09:54:18 2009 -0500 @@ -93,14 +93,14 @@ history_count = 0 start = time.time() if force_retry: - histories = self.sa_session.query( app.model.History ) \ - .filter( and_( app.model.History.table.c.user_id==None, - app.model.History.table.c.update_time < cutoff_time ) ) + histories = app.sa_session.query( app.model.History ) \ + .filter( and_( app.model.History.table.c.user_id==None, + app.model.History.table.c.update_time < cutoff_time ) ) else: - histories = self.sa_session.query( app.model.History ) \ - .filter( and_( app.model.History.table.c.user_id==None, - app.model.History.table.c.deleted==False, - app.model.History.table.c.update_time < cutoff_time ) ) + histories = app.sa_session.query( app.model.History ) \ + .filter( and_( app.model.History.table.c.user_id==None, + app.model.History.table.c.deleted==False, + app.model.History.table.c.update_time < cutoff_time ) ) for history in histories: if not info_only: print "Deleting history id ", history.id @@ -121,16 +121,16 @@ history_count = 0 start = time.time() if force_retry: - histories = self.sa_session.query( app.model.History ) \ - .filter( and_( app.model.History.table.c.deleted==True, - app.model.History.table.c.update_time < cutoff_time ) ) \ - .options( eagerload( 'datasets' ) ) + histories = app.sa_session.query( app.model.History ) \ + .filter( and_( app.model.History.table.c.deleted==True, + app.model.History.table.c.update_time < cutoff_time ) ) \ + .options( eagerload( 'datasets' ) ) else: - histories = self.sa_session.query( app.model.History ) \ - .filter( and_( app.model.History.table.c.deleted==True, - app.model.History.table.c.purged==False, - app.model.History.table.c.update_time < cutoff_time ) ) \ - .options( eagerload( 'datasets' ) ) + histories = app.sa_session.query( app.model.History ) \ + .filter( and_( app.model.History.table.c.deleted==True, + app.model.History.table.c.purged==False, + app.model.History.table.c.update_time < cutoff_time ) ) \ + .options( eagerload( 'datasets' ) ) for history in histories: for dataset_assoc in history.datasets: _purge_dataset_instance( dataset_assoc, app, remove_from_disk, info_only = info_only ) #mark a DatasetInstance as deleted, clear associated files, and mark the Dataset as deleted if it is deletable @@ -158,14 +158,14 @@ library_count = 0 start = time.time() if force_retry: - libraries = self.sa_session.query( app.model.Library ) \ - .filter( and_( app.model.Library.table.c.deleted==True, - app.model.Library.table.c.update_time < cutoff_time ) ) + libraries = app.sa_session.query( app.model.Library ) \ + .filter( and_( app.model.Library.table.c.deleted==True, + app.model.Library.table.c.update_time < cutoff_time ) ) else: - libraries = self.sa_session.query( app.model.Library ) \ - .filter( and_( app.model.Library.table.c.deleted==True, - app.model.Library.table.c.purged==False, - app.model.Library.table.c.update_time < cutoff_time ) ) + libraries = app.sa_session.query( app.model.Library ) \ + .filter( and_( app.model.Library.table.c.deleted==True, + app.model.Library.table.c.purged==False, + app.model.Library.table.c.update_time < cutoff_time ) ) for library in libraries: _purge_folder( library.root_folder, app, remove_from_disk, info_only = info_only ) if not info_only: @@ -187,14 +187,14 @@ folder_count = 0 start = time.time() if force_retry: - folders = self.sa_session.query( app.model.LibraryFolder ) \ - .filter( and_( app.model.LibraryFolder.table.c.deleted==True, - app.model.LibraryFolder.table.c.update_time < cutoff_time ) ) + folders = app.sa_session.query( app.model.LibraryFolder ) \ + .filter( and_( app.model.LibraryFolder.table.c.deleted==True, + app.model.LibraryFolder.table.c.update_time < cutoff_time ) ) else: - folders = self.sa_session.query( app.model.LibraryFolder ) \ - .filter( and_( app.model.LibraryFolder.table.c.deleted==True, - app.model.LibraryFolder.table.c.purged==False, - app.model.LibraryFolder.table.c.update_time < cutoff_time ) ) + folders = app.sa_session.query( app.model.LibraryFolder ) \ + .filter( and_( app.model.LibraryFolder.table.c.deleted==True, + app.model.LibraryFolder.table.c.purged==False, + app.model.LibraryFolder.table.c.update_time < cutoff_time ) ) for folder in folders: _purge_folder( folder, app, remove_from_disk, info_only = info_only ) folder_count += 1 @@ -241,7 +241,7 @@ deleted_instance_count = 0 for dataset_id in dataset_ids: print "######### Processing dataset id:", dataset_id - dataset = self.sa_session.query( app.model.Dataset ).get( dataset_id ) + dataset = app.sa_session.query( app.model.Dataset ).get( dataset_id ) if dataset.id not in skip and _dataset_is_deletable( dataset ): deleted_dataset_count += 1 for dataset_instance in dataset.history_associations + dataset.library_associations: @@ -261,16 +261,16 @@ disk_space = 0 start = time.time() if force_retry: - datasets = self.sa_session.query( app.model.Dataset ) \ - .filter( and_( app.model.Dataset.table.c.deleted==True, - app.model.Dataset.table.c.purgable==True, - app.model.Dataset.table.c.update_time < cutoff_time ) ) + datasets = app.sa_session.query( app.model.Dataset ) \ + .filter( and_( app.model.Dataset.table.c.deleted==True, + app.model.Dataset.table.c.purgable==True, + app.model.Dataset.table.c.update_time < cutoff_time ) ) else: - datasets = self.sa_session.query( app.model.Dataset ) \ - .filter( and_( app.model.Dataset.table.c.deleted==True, - app.model.Dataset.table.c.purgable==True, - app.model.Dataset.table.c.purged==False, - app.model.Dataset.table.c.update_time < cutoff_time ) ) + datasets = app.sa_session.query( app.model.Dataset ) \ + .filter( and_( app.model.Dataset.table.c.deleted==True, + app.model.Dataset.table.c.purgable==True, + app.model.Dataset.table.c.purged==False, + app.model.Dataset.table.c.update_time < cutoff_time ) ) for dataset in datasets: file_size = dataset.file_size _purge_dataset( dataset, remove_from_disk, info_only = info_only ) @@ -294,7 +294,7 @@ dataset_instance.mark_deleted( include_children = include_children ) dataset_instance.clear_associated_files() dataset_instance.flush() - self.sa_session.refresh( dataset_instance.dataset ) + app.sa_session.refresh( dataset_instance.dataset ) if is_deletable or _dataset_is_deletable( dataset_instance.dataset ): # Calling methods may have already checked _dataset_is_deletable, if so, is_deletable should be True _delete_dataset( dataset_instance.dataset, app, remove_from_disk, info_only=info_only, is_deletable=is_deletable ) @@ -317,12 +317,12 @@ metadata_files = [] #lets create a list of metadata files, then perform actions on them for hda in dataset.history_associations: - for metadata_file in self.sa_session.query( app.model.MetadataFile ) \ - .filter( app.model.MetadataFile.table.c.hda_id==hda.id ): + for metadata_file in app.sa_session.query( app.model.MetadataFile ) \ + .filter( app.model.MetadataFile.table.c.hda_id==hda.id ): metadata_files.append( metadata_file ) for lda in dataset.library_associations: - for metadata_file in self.sa_session.query( app.model.MetadataFile ) \ - .filter( app.model.MetadataFile.table.c.lda_id==lda.id ): + for metadata_file in app.sa_session.query( app.model.MetadataFile ) \ + .filter( app.model.MetadataFile.table.c.lda_id==lda.id ): metadata_files.append( metadata_file ) for metadata_file in metadata_files: print "The following metadata files attached to associations of Dataset '%s' have been purged:" % dataset.id
1
0
0
0
[hg] galaxy 2940: User information forms first pass.
by Greg Von Kuster
07 Nov '09
07 Nov '09
details:
http://www.bx.psu.edu/hg/galaxy/rev/cd0596754ff1
changeset: 2940:cd0596754ff1 user: rc date: Mon Nov 02 13:30:17 2009 -0500 description: User information forms first pass. - added a foreign key in the galaxy_user table to the form_values table - separate user info page for viewing/editing user info - user addresses added to user info page diffstat: lib/galaxy/model/__init__.py | 18 +- lib/galaxy/model/mapping.py | 3 + lib/galaxy/model/migrate/versions/0025_user_info.py | 62 ++ lib/galaxy/web/controllers/admin.py | 42 +- lib/galaxy/web/controllers/library_common.py | 3 +- lib/galaxy/web/controllers/user.py | 604 ++++++++++++++++++++----- lib/galaxy/web/form_builder.py | 19 + lib/galaxy/web/framework/helpers/grids.py | 3 +- templates/admin/user/grid.mako | 2 +- templates/user/edit_address.mako | 33 + templates/user/index.mako | 5 +- templates/user/info.mako | 161 ++++++ templates/user/permissions.mako | 8 +- templates/user/register.mako | 87 +++ test/base/twilltestcase.py | 105 +++- test/functional/test_forms_and_requests.py | 39 +- test/functional/test_user_info.py | 149 ++++++ 17 files changed, 1134 insertions(+), 209 deletions(-) diffs (1702 lines): diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/model/__init__.py Mon Nov 02 13:30:17 2009 -0500 @@ -1098,7 +1098,8 @@ class FormDefinition( object ): types = Bunch( REQUEST = 'Sequencing Request Form', SAMPLE = 'Sequencing Sample Form', - LIBRARY_INFO_TEMPLATE = 'Library information template' ) + LIBRARY_INFO_TEMPLATE = 'Library information template', + USER_INFO = 'User Information' ) def __init__(self, name=None, desc=None, fields=[], form_definition_current=None, form_type=None, layout=None): self.name = name @@ -1203,21 +1204,6 @@ self.folder = folder self.state = state self.samples_list = [] - def add_sample(self, sample_name=None, sample_desc=None, sample_values=None): - # create a form_values row - values = trans.app.model.FormValues(self.type.sample_form, sample_values) - values.flush() - sample = Sample(sample_name, sample_desc, self, values) - sample.flush() - # set the initial state - state = self.type.states[0] - event = SampleEvent(sample, state) - event.flush() - # add this sample to the member array - self.samples_list.append(sample) - return sample - def delete_sample(self, sample_name): - pass def has_sample(self, sample_name): for s in self.samples: if s.name == sample_name: diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/model/mapping.py Mon Nov 02 13:30:17 2009 -0500 @@ -47,6 +47,7 @@ Column( "username", TrimmedString( 255 ), index=True, unique=True ), Column( "password", TrimmedString( 40 ), nullable=False ), Column( "external", Boolean, default=False ), + Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), Column( "deleted", Boolean, index=True, default=False ), Column( "purged", Boolean, index=True, default=False ) ) @@ -781,6 +782,8 @@ _preferences=relation( UserPreference, backref="user", collection_class=attribute_mapped_collection('name')), # addresses=relation( UserAddress, # primaryjoin=( User.table.c.id == UserAddress.table.c.user_id ) ) + values=relation( FormValues, + primaryjoin=( User.table.c.form_values_id == FormValues.table.c.id ) ), ) ) # Set up proxy so that this syntax is possible: diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/model/migrate/versions/0025_user_info.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/model/migrate/versions/0025_user_info.py Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,62 @@ +""" +This script adds a foreign key to the form_values table in the galaxy_user table +""" +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.exceptions import * +from migrate import * +from migrate.changeset import * +import datetime +now = datetime.datetime.utcnow +import sys, logging +# Need our custom types, but don't import anything else from model +from galaxy.model.custom_types import * + +log = logging.getLogger( __name__ ) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler( sys.stdout ) +format = "%(name)s %(levelname)s %(asctime)s %(message)s" +formatter = logging.Formatter( format ) +handler.setFormatter( formatter ) +log.addHandler( handler ) + +metadata = MetaData( migrate_engine ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) + +def display_migration_details(): + print "========================================" + print "This script adds a foreign key to the form_values table in the galaxy_user table" + print "========================================" +def upgrade(): + display_migration_details() + # Load existing tables + metadata.reflect() + try: + User_table = Table( "galaxy_user", metadata, autoload=True ) + except NoSuchTableError: + User_table = None + log.debug( "Failed loading table galaxy_user" ) + if User_table: + try: + col = Column( "form_values_id", Integer, index=True ) + col.create( User_table ) + assert col is User_table.c.form_values_id + except Exception, e: + log.debug( "Adding column 'form_values_id' to galaxy_user table failed: %s" % ( str( e ) ) ) + try: + FormValues_table = Table( "form_values", metadata, autoload=True ) + except NoSuchTableError: + FormValues_table = None + log.debug( "Failed loading table form_values" ) + # Add 1 foreign key constraint to the form_values table + if User_table and FormValues_table: + try: + cons = ForeignKeyConstraint( [User_table.c.form_values_id], + [FormValues_table.c.id], + name='user_form_values_id_fk' ) + # Create the constraint + cons.create() + except Exception, e: + log.debug( "Adding foreign key constraint 'user_form_values_id_fk' to table 'galaxy_user' failed: %s" % ( str( e ) ) ) +def downgrade(): + pass \ No newline at end of file diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/controllers/admin.py --- a/lib/galaxy/web/controllers/admin.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/controllers/admin.py Mon Nov 02 13:30:17 2009 -0500 @@ -41,7 +41,7 @@ model_class = model.User template='/admin/user/grid.mako' columns = [ - EmailColumn( "Email", link=( lambda item: dict( operation="user", id=item.id ) ), attach_popup=True ), + EmailColumn( "Email", link=( lambda item: dict( operation="information", id=item.id ) ), attach_popup=True ), UserNameColumn( "User Name", attach_popup=False ), GroupsColumn( "Groups", attach_popup=False ), RolesColumn( "Roles", attach_popup=False ), @@ -51,13 +51,15 @@ grids.GridColumn( "Deleted", key="deleted", visible=False ) ] operations = [ - grids.GridOperation( "reset_password", condition=( lambda item: not item.deleted ), allow_multiple=True ) + grids.GridOperation( "Manage Roles & Groups", condition=( lambda item: not item.deleted ), allow_multiple=False ) + ] #TODO: enhance to account for trans.app.config.allow_user_deletion here so that we can eliminate these operations if # the setting is False - operations.append( grids.GridOperation( "delete", condition=( lambda item: not item.deleted ), allow_multiple=True ) ) - operations.append( grids.GridOperation( "undelete", condition=( lambda item: item.deleted ), allow_multiple=True ) ) - operations.append( grids.GridOperation( "purge", condition=( lambda item: item.deleted ), allow_multiple=True ) ) + operations.append( grids.GridOperation( "Reset Password", condition=( lambda item: not item.deleted ), allow_multiple=True, allow_popup=False ) ) + operations.append( grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), allow_multiple=True ) ) + operations.append( grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ), allow_multiple=True ) ) + operations.append( grids.GridOperation( "Purge", condition=( lambda item: item.deleted ), allow_multiple=True ) ) standard_filters = [ grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), @@ -530,6 +532,9 @@ @web.expose @web.require_admin def create_new_user( self, trans, **kwargs ): + return trans.response.send_redirect( web.url_for( controller='user', + action='create', + admin_view='True' ) ) email = '' password = '' confirm = '' @@ -731,9 +736,9 @@ def users( self, trans, **kwargs ): if 'operation' in kwargs: operation = kwargs['operation'].lower() - if operation == "user": + if operation == "roles": return self.user( trans, **kwargs ) - if operation == "reset_password": + if operation == "reset password": return self.reset_user_password( trans, **kwargs ) if operation == "delete": return self.mark_user_deleted( trans, **kwargs ) @@ -743,10 +748,33 @@ return self.purge_user( trans, **kwargs ) if operation == "create": return self.create_new_user( trans, **kwargs ) + if operation == "information": + return self.user_info( trans, **kwargs ) # Render the list view return self.user_list_grid( trans, **kwargs ) @web.expose @web.require_admin + def user_info( self, trans, **kwd ): + ''' + This method displays the user information page which consists of login + information, public username, reset password & other user information + obtained during registration + ''' + print 'KWD', kwd + user_id = kwd.get( 'id', None ) + if not user_id: + message += "Invalid user id (%s) received" % str( user_id ) + trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message=util.sanitize_text( message ), + status='error' ) ) + user = get_user( trans, user_id ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True ) ) + @web.expose + @web.require_admin def user( self, trans, **kwd ): user_id = kwd.get( 'id', None ) message = '' diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/controllers/library_common.py --- a/lib/galaxy/web/controllers/library_common.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/controllers/library_common.py Mon Nov 02 13:30:17 2009 -0500 @@ -242,7 +242,8 @@ trans.response.send_redirect( web.url_for( controller='forms', action='new', msg=msg, - messagetype='done' ) ) + messagetype='done', + form_type=trans.app.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE ) ) if params.get( 'add_info_template_button', False ): form = trans.sa_session.query( trans.app.model.FormDefinition ).get( int( kwd[ 'form_id' ] ) ) #fields = list( copy.deepcopy( form.fields ) ) diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/controllers/user.py --- a/lib/galaxy/web/controllers/user.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/controllers/user.py Mon Nov 02 13:30:17 2009 -0500 @@ -6,6 +6,9 @@ from galaxy import util import logging, os, string, re from random import choice +from galaxy.web.controllers.forms import get_all_forms +from galaxy.web.form_builder import * +from galaxy.web.controllers import admin log = logging.getLogger( __name__ ) @@ -23,10 +26,11 @@ VALID_USERNAME_RE = re.compile( "^[a-z0-9\-]+$" ) class User( BaseController ): - edit_address_id = None + @web.expose def index( self, trans, **kwd ): return trans.fill_template( '/user/index.mako', user=trans.get_user() ) + @web.expose def change_password(self, trans, old_pass='', new_pass='', conf_pass='', **kwd): old_pass_err = new_pass_err = conf_pass_err = '' @@ -158,50 +162,392 @@ msg += ' <a href="%s">Click here</a> to return to the login page.' % web.url_for( controller='user', action='login' ) return trans.show_ok_message( msg, refresh_frames=refresh_frames ) @web.expose - def create( self, trans, email='', password='', confirm='', subscribe=False ): + def create( self, trans, **kwd ): + params = util.Params( kwd ) + email = util.restore_text( params.get('email', '') ) + username = util.restore_text( params.get('username', '') ) + password = util.restore_text( params.get('password', '') ) + confirm = util.restore_text( params.get('confirm', '') ) + subscribe = params.get('subscribe', False) + admin_view = params.get('admin_view', 'False') + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) if trans.app.config.require_login: refresh_frames = [ 'masthead', 'history', 'tools' ] else: refresh_frames = [ 'masthead', 'history' ] if not trans.app.config.allow_user_creation and not trans.user_is_admin(): return trans.show_error_message( 'User registration is disabled. Please contact your Galaxy administrator for an account.' ) - email_error = password_error = confirm_error = None - if email: - if len( email ) == 0 or "@" not in email or "." not in email: - email_error = "Please enter a real email address" - elif len( email ) > 255: - email_error = "Email address exceeds maximum allowable length" - elif trans.sa_session.query( trans.app.model.User ).filter_by( email=email ).all(): - email_error = "User with that email already exists" - elif len( password ) < 6: - password_error = "Please use a password of at least 6 characters" - elif password != confirm: - confirm_error = "Passwords do not match" - else: - user = trans.app.model.User( email=email ) - user.set_password_cleartext( password ) - user.flush() - trans.app.security_agent.create_private_user_role( user ) - # We set default user permissions, before we log in and set the default history permissions - trans.app.security_agent.user_set_default_permissions( user, default_access_private = trans.app.config.new_user_dataset_access_role_default_private ) + # + # Create the user, save all the user info and login to Galaxy + # + if params.get('create_user_button', None) == "Submit": + # check email and password validity + error = self.__validate(trans, params, email, password, confirm) + if error: + kwd[ 'msg' ] = error + kwd[ 'messagetype' ] = 'error' + kwd[ 'create_user_button' ] = None + return trans.response.send_redirect( web.url_for( controller='user', + action='create', + **kwd ) ) + # all the values are valid + user = trans.app.model.User( email=email ) + user.set_password_cleartext( password ) + user.username = username + user.flush() + trans.app.security_agent.create_private_user_role( user ) + # We set default user permissions, before we log in and set the default history permissions + trans.app.security_agent.user_set_default_permissions( user, default_access_private = trans.app.config.new_user_dataset_access_role_default_private ) + # save user info + self.__save_user_info(trans, user, action='create', new_user=True, **kwd) + if subscribe: + mail = os.popen("%s -t" % trans.app.config.sendmail_path, 'w') + mail.write("To: %s\nFrom: %s\nSubject: Join Mailing List\n\nJoin Mailing list." % (trans.app.config.mailing_join_addr,email) ) + if mail.close(): + return trans.show_warn_message( "Now logged in as " + user.email+". However, subscribing to the mailing list has failed.", refresh_frames=refresh_frames ) + if admin_view == 'False': # The handle_user_login() method has a call to the history_set_default_permissions() method # (needed when logging in with a history), user needs to have default permissions set before logging in trans.handle_user_login( user ) trans.log_event( "User created a new account" ) trans.log_event( "User logged in" ) - #subscribe user to email list - if subscribe: - mail = os.popen("%s -t" % trans.app.config.sendmail_path, 'w') - mail.write("To: %s\nFrom: %s\nSubject: Join Mailing List\n\nJoin Mailing list." % (trans.app.config.mailing_join_addr,email) ) - if mail.close(): - return trans.show_warn_message( "Now logged in as " + user.email+". However, subscribing to the mailing list has failed.", refresh_frames=refresh_frames ) + # subscribe user to email list return trans.show_ok_message( "Now logged in as " + user.email, refresh_frames=refresh_frames ) - return trans.show_form( - web.FormBuilder( web.url_for(), "Create account", submit_text="Create" ) - .add_text( "email", "Email address", value=email, error=email_error ) - .add_password( "password", "Password", value='', error=password_error ) - .add_password( "confirm", "Confirm password", value='', error=confirm_error ) - .add_input( "checkbox","Subscribe To Mailing List","subscribe", value='subscribe' ) ) + else: + trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message='Created new user account (%s)' % user.email, + status='done' ) ) + else: + # + # Show the user registration form + # + user_info_select, user_info_form, login_info, widgets = self.__user_info_ui(trans, **kwd) + return trans.fill_template( '/user/register.mako', + user_info_select=user_info_select, + user_info_form=user_info_form, widgets=widgets, + login_info=login_info, admin_view=admin_view, + msg=msg, messagetype=messagetype) + + def __save_user_info(self, trans, user, action, new_user=True, **kwd): + ''' + This method saves the user information for new users as well as editing user + info for existing users. For new users, the user info form is retrieved from + the one that user has selected. And for existing users, the user info form is + retrieved from the db. + ''' + params = util.Params( kwd ) + # get all the user information forms + user_info_forms = get_all_forms( trans, filter=dict(deleted=False), + form_type=trans.app.model.FormDefinition.types.USER_INFO ) + # if there are no user forms available then there is nothing to save + if not len( user_info_forms ): + return + if new_user: + user_info_type = params.get( 'user_info_select', 'none' ) + try: + user_info_form = trans.sa_session.query( trans.app.model.FormDefinition ).get(int(user_info_type)) + except: + return trans.response.send_redirect( web.url_for( controller='user', + action=action, + msg='Invalid user information form id', + messagetype='error') ) + else: + user_info_form = user.values.form_definition + values = [] + for index, field in enumerate(user_info_form.fields): + if field['type'] == 'AddressField': + value = util.restore_text(params.get('field_%i' % index, '')) + if value == 'new': + # save this new address in the list of this user's addresses + user_address = trans.app.model.UserAddress( user=user ) + user_address.desc = util.restore_text(params.get('field_%i_short_desc' % index, '')) + user_address.name = util.restore_text(params.get('field_%i_name' % index, '')) + user_address.institution = util.restore_text(params.get('field_%i_institution' % index, '')) + user_address.address = util.restore_text(params.get('field_%i_address1' % index, ''))+' '+util.restore_text(params.get('field_%i_address2' % index, '')) + user_address.city = util.restore_text(params.get('field_%i_city' % index, '')) + user_address.state = util.restore_text(params.get('field_%i_state' % index, '')) + user_address.postal_code = util.restore_text(params.get('field_%i_postal_code' % index, '')) + user_address.country = util.restore_text(params.get('field_%i_country' % index, '')) + user_address.phone = util.restore_text(params.get('field_%i_phone' % index, '')) + user_address.flush() + trans.user.refresh() + values.append(int(user_address.id)) + elif value == unicode('none'): + values.append('') + else: + values.append(int(value)) + else: + values.append(util.restore_text(params.get('field_%i' % index, ''))) + if new_user: + form_values = trans.app.model.FormValues(user_info_form, values) + form_values.flush() + user.values = form_values + else: # editing the user info of an existing user + user.values.content = values + user.values.flush() + user.flush() + def __validate_email(self, trans, params, email, user=None): + error = None + if user: + if user.email == email: + return None + if len(email) == 0 or "@" not in email or "." not in email: + error = "Please enter a real email address" + elif len(email) > 255: + error = "Email address exceeds maximum allowable length" + elif trans.sa_session.query( trans.app.model.User ).filter_by(email=email).all(): + error = "User with that email already exists" + return error + def __validate_password(self, trans, params, password, confirm): + error = None + if len(password) < 6: + error = "Please use a password of at least 6 characters" + elif password != confirm: + error = "Passwords do not match" + return error + + def __validate(self, trans, params, email, password, confirm): + error = self.__validate_email(trans, params, email) + if error: + return error + error = self.__validate_password(trans, params, password, confirm) + if error: + return error + if len(get_all_forms( trans, + filter=dict(deleted=False), + form_type=trans.app.model.FormDefinition.types.USER_INFO )): + if params.get('user_info_select', 'none') == 'none': + return 'Select the user type and the user information' + return None + + def __user_info_ui(self, trans, user=None, **kwd): + ''' + This method creates the user type select box & user information form widgets + and is called during user registration and editing user information. + If there exists only one user information form then show it after main + login info. However, if there are more than one user info forms then + show a selectbox containing all the forms, then the user can select + the one that fits the user's description the most + ''' + params = util.Params( kwd ) + # get all the user information forms + user_info_forms = get_all_forms( trans, filter=dict(deleted=False), + form_type=trans.app.model.FormDefinition.types.USER_INFO ) + user_info_select = None + if user: + if user.values: + selected_user_form_id = user.values.form_definition.id + else: + selected_user_form_id = 'none' + else: + selected_user_form_id = params.get( 'user_info_select', 'none' ) + # when there are more than one user information forms then show a select box + # list all these forms + if len(user_info_forms) > 1: + # create the select box + user_info_select = SelectField('user_info_select', refresh_on_change=True, + refresh_on_change_values=[str(u.id) for u in user_info_forms]) + if selected_user_form_id == 'none': + user_info_select.add_option('Select one', 'none', selected=True) + else: + user_info_select.add_option('Select one', 'none') + for u in user_info_forms: + if selected_user_form_id == str(u.id): + user_info_select.add_option(u.name, u.id, selected=True) + else: + user_info_select.add_option(u.name, u.id) + # when there is just one user information form the just render that form + elif len(user_info_forms) == 1: + selected_user_form_id = user_info_forms[0].id + # now, create the selected user form widgets starting with the basic + # login information + if user: + login_info = { 'Email': TextField( 'email', 40, user.email ), + 'Public Username': TextField( 'username', 40, user.username ), + 'Current Password': PasswordField( 'current', 40, '' ), + 'New Password': PasswordField( 'password', 40, '' ), + 'Confirm': PasswordField( 'confirm', 40, '' ) } + else: + login_info = { 'Email': TextField( 'email', 40, + util.restore_text( params.get('email', '') ) ), + 'Public Username': TextField( 'username', 40, + util.restore_text( params.get('username', '') ) ), + 'Password': PasswordField( 'password', 40, + util.restore_text( params.get('password', '') ) ), + 'Confirm': PasswordField( 'confirm', 40, + util.restore_text( params.get('confirm', '') ) ), + 'Subscribe To Mailing List': CheckboxField( 'subscribe', + util.restore_text( params.get('subscribe', '') ) ) } + # user information + try: + user_info_form = trans.sa_session.query( trans.app.model.FormDefinition ).get(int(selected_user_form_id)) + except: + return user_info_select, None, login_info, None + if user: + if user.values: + widgets = user_info_form.get_widgets(user=user, + contents=user.values.content, + **kwd) + else: + widgets = user_info_form.get_widgets(None, contents=[], **kwd) + else: + widgets = user_info_form.get_widgets(None, contents=[], **kwd) + return user_info_select, user_info_form, login_info, widgets + + @web.expose + def show_info( self, trans, **kwd ): + ''' + This method displays the user information page which consists of login + information, public username, reset password & other user information + obtained during registration + ''' + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + # check if this method is called from the admin perspective, + if params.get('admin_view', 'False') == 'True': + try: + user = trans.sa_session.query( trans.app.model.User ).get( int( params.get( 'user_id', None ) ) ) + except: + return trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message='Invalid user', + status='error' ) ) + admin_view = True + else: + user = trans.user + admin_view = False + user_info_select, user_info_form, login_info, widgets = self.__user_info_ui(trans, user, **kwd) + # user's addresses + show_filter = util.restore_text( params.get( 'show_filter', 'Active' ) ) + if show_filter == 'All': + addresses = [address for address in user.addresses] + elif show_filter == 'Deleted': + addresses = [address for address in user.addresses if address.deleted] + else: + addresses = [address for address in user.addresses if not address.deleted] + return trans.fill_template( '/user/info.mako', user=user, admin_view=admin_view, + user_info_select=user_info_select, + user_info_form=user_info_form, widgets=widgets, + login_info=login_info, + addresses=addresses, show_filter=show_filter, + msg=msg, messagetype=messagetype) + @web.expose + def edit_info( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + if params.get('admin_view', 'False') == 'True': + try: + user = trans.sa_session.query( trans.app.model.User ).get( int( params.get( 'user_id', None ) ) ) + except: + return trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message='Invalid user', + status='error' ) ) + else: + user = trans.user + # + # Editing login info (email & username) + # + if params.get('login_info_button', None) == 'Save': + email = util.restore_text( params.get('email', '') ) + username = util.restore_text( params.get('username', '') ) + # validate the new values + error = self.__validate_email(trans, params, email, user) + if error: + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=error, + messagetype='error') ) + # the new email & username + user.email = email + user.username = username + user.flush() + msg = 'The login information has been updated with the changes' + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=msg, + messagetype='done' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=msg, + messagetype='done') ) + # + # Change password + # + elif params.get('change_password_button', None) == 'Save': + password = util.restore_text( params.get('password', '') ) + confirm = util.restore_text( params.get('confirm', '') ) + # when from the user perspective, validate the current password + if params.get('admin_view', 'False') == 'False': + current = util.restore_text( params.get('current', '') ) + if not trans.user.check_password( current ): + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg='Invalid current password', + messagetype='error') ) + # validate the new values + error = self.__validate_password(trans, params, password, confirm) + if error: + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=error, + messagetype='error' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=error, + messagetype='error') ) + # save new password + user.set_password_cleartext( password ) + user.flush() + trans.log_event( "User change password" ) + msg = 'The password has been changed.' + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=msg, + messagetype='done' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=msg, + messagetype='done') ) + # + # Edit user information + # + elif params.get('edit_user_info_button', None) == 'Save': + self.__save_user_info(trans, user, "show_info", new_user=False, **kwd) + msg = "The user information has been updated with the changes." + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=msg, + messagetype='done' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=msg, + messagetype='done') ) + else: + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info' ) ) + @web.expose def reset_password( self, trans, email=None, **kwd ): error = '' @@ -305,7 +651,7 @@ country=country, phone=phone) user_address.flush() return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', + action='show_info', msg='Address <b>%s</b> has been added' % user_address.desc, messagetype='done') ) @@ -322,109 +668,123 @@ .add_text( "country", "Country", value=country, error=country_error ) .add_text( "phone", "Phone", value=phone, error=phone_error ) ) @web.expose - def edit_address( self, trans, address_id=None, short_desc='', name='', institution='', address1='', - address2='', city='', state='', postal_code='', country='', phone='' ): - import sys - - if trans.app.config.require_login: - refresh_frames = [ 'masthead', 'history', 'tools' ] + def edit_address( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + admin_view = params.get( 'admin_view', 'False' ) + error = '' + user = trans.sa_session.query( trans.app.model.User ).get( int( params.get( 'user_id', None ) ) ) + try: + user_address = trans.sa_session.query( trans.app.model.UserAddress ).get(int(params.get( 'address_id', None ))) + except: + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=admin_view, + msg='Invalid address ID', + messagetype='error' ) ) + if params.get( 'edit_address_button', None ) == 'Save changes': + if not len( util.restore_text( params.get( 'short_desc', '' ) ) ): + error = 'Enter a short description for this address' + elif not len( util.restore_text( params.get( 'name', '' ) ) ): + error = 'Enter the full name' + elif not len( util.restore_text( params.get( 'institution', '' ) ) ): + error = 'Enter the institution associated with the user' + elif not len ( util.restore_text( params.get( 'address1', '' ) ) ): + error = 'Enter the address' + elif not len( util.restore_text( params.get( 'city', '' ) ) ): + error = 'Enter the city' + elif not len( util.restore_text( params.get( 'state', '' ) ) ): + error = 'Enter the state/province/region' + elif not len( util.restore_text( params.get( 'postal_code', '' ) ) ): + error = 'Enter the postal code' + elif not len( util.restore_text( params.get( 'country', '' ) ) ): + error = 'Enter the country' + else: + user_address.desc = util.restore_text( params.get( 'short_desc', '' ) ) + user_address.name = util.restore_text( params.get( 'name', '' ) ) + user_address.institution = util.restore_text( params.get( 'institution', '' ) ) + user_address.address = util.restore_text( params.get( 'address1', '' ) )+' '+util.restore_text( params.get( 'address2', '' ) ) + user_address.city = util.restore_text( params.get( 'city', '' ) ) + user_address.state = util.restore_text( params.get( 'state', '' ) ) + user_address.postal_code = util.restore_text( params.get( 'postal_code', '' ) ) + user_address.country = util.restore_text( params.get( 'country', '' ) ) + user_address.phone = util.restore_text( params.get( 'phone', '' ) ) + user_address.flush() + msg = 'Changes made to address <b>%s</b> are saved.' % user_address.desc + if admin_view == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=msg, + messagetype='done' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=msg, + messagetype='done') ) else: - refresh_frames = [ 'masthead', 'history' ] - if not trans.app.config.allow_user_creation and not trans.user_is_admin(): - return trans.show_error_message( 'User registration is disabled. Please contact your Galaxy administrator for an account.' ) - short_desc_error = name_error = institution_error = address1_error = city_error = None - address2_error = state_error = postal_code_error = country_error = phone_error = None - if short_desc: - if not len( short_desc ): - short_desc_error = 'Enter a short description for this address' - elif not len( name ): - name_error = 'Enter the full name' - elif not len( institution ): - institution_error = 'Enter the institution associated with the user' - elif not len ( address1 ): - address1_error = 'Enter the address' - elif not len( city ): - city_error = 'Enter the city' - elif not len( state ): - state_error = 'Enter the state/province/region' - elif not len( postal_code ): - postal_code_error = 'Enter the postal code' - elif not len( country ): - country_error = 'Enter the country' - else: - if self.edit_address_id: - try: - user_address = trans.sa_session.query( trans.app.model.UserAddress ).get( int( self.edit_address_id ) ) - except: - return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', - msg='Invalid address ID', - messagetype='error') ) - user_address.desc = short_desc - user_address.name = name - user_address.institution = institution - user_address.address = address1+' '+address2 - user_address.city = city - user_address.state = state - user_address.postal_code = postal_code - user_address.country = country - user_address.phone = phone - user_address.flush() - self.edit_address_id = None - return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', - msg='Changes made to address <b>%s</b> are saved.' % user_address.desc, - messagetype='done') ) - self.edit_address_id = address_id - return trans.show_form( - web.FormBuilder( web.url_for(), "Edit address", submit_text="Save changes" ) - .add_text( "short_desc", "Short address description", value=short_desc, error=short_desc_error ) - .add_text( "name", "Name", value=name, error=name_error ) - .add_text( "institution", "Institution", value=institution, error=institution_error ) - .add_text( "address1", "Address Line 1", value=address1, error=address1_error ) - .add_text( "address2", "Address Line 2", value=address2, error=address2_error ) - .add_text( "city", "City", value=city, error=city_error ) - .add_text( "state", "State/Province/Region", value=state, error=state_error ) - .add_text( "postal_code", "Postal Code", value=postal_code, error=postal_code_error ) - .add_text( "country", "Country", value=country, error=country_error ) - .add_text( "phone", "Phone", value=phone, error=phone_error ) ) + # show the address form with the current values filled in + # create the widgets for each address field + widgets = [] + widgets.append(dict(label='Short description', + widget=TextField( 'short_desc', 40, user_address.desc ) ) ) + widgets.append(dict(label='Name', + widget=TextField( 'name', 40, user_address.name ) ) ) + widgets.append(dict(label='Institution', + widget=TextField( 'institution', 40, user_address.institution ) ) ) + widgets.append(dict(label='Address Line 1', + widget=TextField( 'address1', 40, user_address.address ) ) ) + widgets.append(dict(label='City', + widget=TextField( 'city', 40, user_address.city ) ) ) + widgets.append(dict(label='State', + widget=TextField( 'state', 40, user_address.state ) ) ) + widgets.append(dict(label='Postal Code', + widget=TextField( 'postal_code', 40, user_address.postal_code ) ) ) + widgets.append(dict(label='Country', + widget=TextField( 'country', 40, user_address.country ) ) ) + widgets.append(dict(label='Phone', + widget=TextField( 'phone', 40, user_address.phone ) ) ) + return trans.fill_template( 'user/edit_address.mako', user=user, + address=user_address, admin_view=admin_view, + widgets=widgets, msg=msg, messagetype=messagetype) @web.expose - def delete_address( self, trans, address_id=None): - if trans.app.config.require_login: - refresh_frames = [ 'masthead', 'history', 'tools' ] - else: - refresh_frames = [ 'masthead', 'history' ] - if not trans.app.config.allow_user_creation and not trans.user_is_admin(): - return trans.show_error_message( 'User registration is disabled. Please contact your Galaxy administrator for an account.' ) + def delete_address( self, trans, address_id=None, user_id=None, admin_view='False'): try: user_address = trans.sa_session.query( trans.app.model.UserAddress ).get( int( address_id ) ) except: - return trans.fill_template( 'user/address.mako', - msg='Invalid address ID', - messagetype='error' ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user_id, + admin_view=admin_view, + msg='Invalid address ID', + messagetype='error' ) ) user_address.deleted = True user_address.flush() return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', + action='show_info', + admin_view=admin_view, + user_id=user_id, msg='Address <b>%s</b> deleted' % user_address.desc, messagetype='done') ) @web.expose - def undelete_address( self, trans, address_id=None): - if trans.app.config.require_login: - refresh_frames = [ 'masthead', 'history', 'tools' ] - else: - refresh_frames = [ 'masthead', 'history' ] - if not trans.app.config.allow_user_creation and not trans.user_is_admin(): - return trans.show_error_message( 'User registration is disabled. Please contact your Galaxy administrator for an account.' ) + def undelete_address( self, trans, address_id=None, user_id=None, admin_view='False'): try: user_address = trans.sa_session.query( trans.app.model.UserAddress ).get( int( address_id ) ) except: - return trans.fill_template( 'user/address.mako', - msg='Invalid address ID', - messagetype='error' ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user_id, + admin_view=admin_view, + msg='Invalid address ID', + messagetype='error' ) ) user_address.deleted = False user_address.flush() return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', - msg='Address <b>%s</b> is restored' % user_address.desc, + action='show_info', + admin_view=admin_view, + user_id=user_id, + msg='Address <b>%s</b> undeleted' % user_address.desc, messagetype='done') ) + diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/form_builder.py --- a/lib/galaxy/web/form_builder.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/form_builder.py Mon Nov 02 13:30:17 2009 -0500 @@ -33,6 +33,25 @@ def set_size(self, size): self.size = int( size ) +class PasswordField(BaseField): + """ + A password input box. text appears as "******" + + >>> print PasswordField( "foo" ).get_html() + <input type="password" name="foo" size="10" value=""> + >>> print PasswordField( "bins", size=4, value="default" ).get_html() + <input type="password" name="bins" size="4" value="default"> + """ + def __init__( self, name, size=None, value=None ): + self.name = name + self.size = int( size or 10 ) + self.value = value or "" + def get_html( self, prefix="" ): + return '<input type="password" name="%s%s" size="%d" value="%s">' \ + % ( prefix, self.name, self.size, escape(str(self.value), quote=True) ) + def set_size(self, size): + self.size = int( size ) + class NumberField(BaseField): """ A number input box. diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/framework/helpers/grids.py Mon Nov 02 13:30:17 2009 -0500 @@ -266,10 +266,11 @@ return accepted_filters class GridOperation( object ): - def __init__( self, label, key=None, condition=None, allow_multiple=True, target=None, url_args=None ): + def __init__( self, label, key=None, condition=None, allow_multiple=True, allow_popup=True, target=None, url_args=None ): self.label = label self.key = key self.allow_multiple = allow_multiple + self.allow_popup = allow_popup self.condition = condition self.target = target self.url_args = url_args diff -r 6e8263ea83fa -r cd0596754ff1 templates/admin/user/grid.mako --- a/templates/admin/user/grid.mako Mon Nov 02 09:54:18 2009 -0500 +++ b/templates/admin/user/grid.mako Mon Nov 02 13:30:17 2009 -0500 @@ -195,7 +195,7 @@ <td> <div popupmenu="grid-${i}-popup"> %for operation in grid.operations: - %if operation.allowed( item ): + %if operation.allowed( item ) and operation.allow_popup: <a class="action-button" href="${url( operation=operation.label, id=item.id )}">${operation.label}</a> %endif %endfor diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/edit_address.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/user/edit_address.mako Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,33 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif +</br> +</br> +<h3>Edit address</h3> + +<ul class="manage-table-actions"> + <li> + <a class="action-button" href="${h.url_for( controller='user', action='show_info')}"> + <span>Manage User Information</span></a> + </li> +</ul> +<div class="toolForm"> +<form name="login_info" id="login_info" action="${h.url_for( controller='user', action='edit_address', admin_view=admin_view, address_id=address.id, user_id=user.id )}" method="post" > + <div class="toolFormTitle">Edit address</div> + <div class="toolFormBody"> + %for field in widgets: + <div class="form-row"> + <label>${field[ 'label' ]}</label> + ${field[ 'widget' ].get_html()} + </div> + %endfor + <div class="form-row"> + <input type="submit" name="edit_address_button" value="Save changes"> + </div> + </div> +</form> +</div> \ No newline at end of file diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/index.mako --- a/templates/user/index.mako Mon Nov 02 09:54:18 2009 -0500 +++ b/templates/user/index.mako Mon Nov 02 13:30:17 2009 -0500 @@ -7,11 +7,8 @@ %if user: <p>You are currently logged in as ${user.email}.</p> <ul> - <li><a href="${h.url_for( action='change_password' )}">${_('Change your password')}</a></li> - <li><a href="${h.url_for( action='change_email' )}">${_('Update your email address')}</a></li> - <li><a href="${h.url_for( action='change_username' )}">${_('Change your public username')}</a></li> + <li><a href="${h.url_for( action='show_info' )}">${_('Manage your information')}</a></li> <li><a href="${h.url_for( action='set_default_permissions' )}">${_('Change default permissions')}</a> for new histories</li> - <li><a href="${h.url_for( action='manage_addresses' )}">${_('Manage your addresses')}</a></li> <li><a href="${h.url_for( action='logout' )}">${_('Logout')}</a></li> </ul> %else: diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/info.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/user/info.mako Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,161 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +<h2>Manage User Information</h2> +%if not admin_view: + <ul class="manage-table-actions"> + <li> + <a class="action-button" href="${h.url_for( controller='user', action='index')}"> + <span>User preferences</span></a> + </li> + </ul> +%endif + +<script type="text/javascript"> +$( function() { + $( "select[refresh_on_change='true']").change( function() { + var refresh = false; + var refresh_on_change_values = $( this )[0].attributes.getNamedItem( 'refresh_on_change_values' ) + if ( refresh_on_change_values ) { + refresh_on_change_values = refresh_on_change_values.value.split( ',' ); + var last_selected_value = $( this )[0].attributes.getNamedItem( 'last_selected_value' ); + for( i= 0; i < refresh_on_change_values.length; i++ ) { + if ( $( this )[0].value == refresh_on_change_values[i] || ( last_selected_value && last_selected_value.value == refresh_on_change_values[i] ) ){ + refresh = true; + break; + } + } + } + else { + refresh = true; + } + if ( refresh ){ + $( "#user_info" ).submit(); + } + }); +}); +</script> + +<div class="toolForm"> + <form name="login_info" id="login_info" action="${h.url_for( controller='user', action='edit_info', user_id=user.id, admin_view=admin_view )}" method="post" > + <div class="toolFormTitle">Login Information</div> + <div class="form-row"> + <label>Email</label> + ${login_info[ 'Email' ].get_html()} + </div> + <div class="form-row"> + <label>Public Username</label> + ${login_info[ 'Public Username' ].get_html()} + </div> + <div class="form-row"> + <input type="submit" name="login_info_button" value="Save"> + </div> + </form> + <form name="change_password" id="change_password" action="${h.url_for( controller='user', action='edit_info', user_id=user.id, admin_view=admin_view )}" method="post" > + <div class="toolFormTitle">Change Password</div> + %if not admin_view: + <div class="form-row"> + <label>Current Password</label> + ${login_info[ 'Current Password' ].get_html()} + </div> + %endif + <div class="form-row"> + <label>New Password</label> + ${login_info[ 'New Password' ].get_html()} + </div> + <div class="form-row"> + <label>Confirm</label> + ${login_info[ 'Confirm' ].get_html()} + </div> + <div class="form-row"> + <input type="submit" name="change_password_button" value="Save"> + </div> + </form> + %if user.values: + <form name="user_info" id="user_info" action="${h.url_for( controller='user', action='edit_info', user_id=user.id, admin_view=admin_view )}" method="post" > + <div class="toolFormTitle">User information</div> + %if user_info_form: + %for field in widgets: + <div class="form-row"> + <label>${field['label']}</label> + ${field['widget'].get_html()} + <div class="toolParamHelp" style="clear: both;"> + ${field['helptext']} + </div> + <div style="clear: both"></div> + </div> + %endfor + %if not user_info_select: + <input type="hidden" name="user_info_select" value="${user_info_form.id}"/> + %endif + %endif + <div class="form-row"> + <input type="submit" name="edit_user_info_button" value="Save"> + </div> + </form> + %endif + <form name="user_info" id="user_info" action="${h.url_for( controller='user', action='new_address' )}" method="post" > + <div class="toolFormTitle">User Addresses</div> + <div class="toolFormBody"> + %if user.addresses: + <div class="form-row"> + <div class="grid-header"> + ##<span class="title">Filter:</span> + %for i, filter in enumerate( ['Active', 'Deleted', 'All'] ): + %if i > 0: + <span>|</span> + %endif + %if show_filter == filter: + <span class="filter"><a href="${h.url_for( controller='user', action='show_info', show_filter=filter, user_id=user.id, admin_view=admin_view )}"><b>${filter}</b></a></span> + %else: + <span class="filter"><a href="${h.url_for( controller='user', action='show_info', show_filter=filter, user_id=user.id, admin_view=admin_view )}">${filter}</a></span> + %endif + %endfor + </div> + </div> + <table class="grid"> + <tbody> + %for index, address in enumerate(addresses): + <tr class="libraryRow libraryOrFolderRow" id="libraryRow"> + <td> + <div class="form-row"> + <label>${address.desc}</label> + ${address.get_html()} + </div> + <div class="form-row"> + <ul class="manage-table-actions"> + <li> + %if not address.deleted: + <a class="action-button" href="${h.url_for( controller='user', action='edit_address', admin_view=admin_view, address_id=address.id, user_id=user.id )}"> + <span>Edit</span></a> + <a class="action-button" href="${h.url_for( controller='user', action='delete_address', admin_view=admin_view, address_id=address.id, user_id=user.id)}"> + <span>Delete</span></a> + %else: + <a class="action-button" href="${h.url_for( controller='user', action='undelete_address', admin_view=admin_view, address_id=address.id, user_id=user.id)}"> + <span>Undelete</span></a> + %endif + + </li> + </ul> + </div> + </td> + </tr> + %endfor + </tbody> + </table> + %endif + <div class="form-row"> + <input type="submit" value="Add a new address"> + </div> + </div> + </form> + + + + +</div> \ No newline at end of file diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/permissions.mako --- a/templates/user/permissions.mako Mon Nov 02 09:54:18 2009 -0500 +++ b/templates/user/permissions.mako Mon Nov 02 13:30:17 2009 -0500 @@ -1,7 +1,13 @@ <%inherit file="/base.mako"/> <%def name="title()">Change Default Permissions on New Histories</%def> <%namespace file="/dataset/security_common.mako" import="render_permission_form" /> - +<br/><br/> +<ul class="manage-table-actions"> + <li> + <a class="action-button" href="${h.url_for( controller='user', action='index')}"> + <span>User preferences</span></a> + </li> +</ul> %if trans.user: ${render_permission_form( trans.user, trans.user.email, h.url_for(), trans.user.all_roles() )} %endif diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/register.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/user/register.mako Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,87 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + + + +<script type="text/javascript"> +$( function() { + $( "select[refresh_on_change='true']").change( function() { + var refresh = false; + var refresh_on_change_values = $( this )[0].attributes.getNamedItem( 'refresh_on_change_values' ) + if ( refresh_on_change_values ) { + refresh_on_change_values = refresh_on_change_values.value.split( ',' ); + var last_selected_value = $( this )[0].attributes.getNamedItem( 'last_selected_value' ); + for( i= 0; i < refresh_on_change_values.length; i++ ) { + if ( $( this )[0].value == refresh_on_change_values[i] || ( last_selected_value && last_selected_value.value == refresh_on_change_values[i] ) ){ + refresh = true; + break; + } + } + } + else { + refresh = true; + } + if ( refresh ){ + $( "#registration" ).submit(); + } + }); +}); +</script> + +<div class="toolForm"> + <form name="registration" id="registration" action="${h.url_for( controller='user', action='create', admin_view=admin_view )}" method="post" > + <div class="toolFormTitle">Create account</div> + <div class="form-row"> + <label>Email</label> + ${login_info[ 'Email' ].get_html()} + </div> + <div class="form-row"> + <label>Password</label> + ${login_info[ 'Password' ].get_html()} + </div> + <div class="form-row"> + <label>Confirm</label> + ${login_info[ 'Confirm' ].get_html()} + </div> + <div class="form-row"> + <label>Public Username</label> + ${login_info[ 'Public Username' ].get_html()} + <div class="toolParamHelp" style="clear: both;"> + Optional + </div> + </div> + <div class="form-row"> + <label>Subscribe To Mailing List</label> + ${login_info[ 'Subscribe To Mailing List' ].get_html()} + </div> + %if user_info_select: + <div class="form-row"> + <label>User type</label> + ${user_info_select.get_html()} + </div> + %endif + %if user_info_form: + %for field in widgets: + <div class="form-row"> + <label>${field['label']}</label> + ${field['widget'].get_html()} + <div class="toolParamHelp" style="clear: both;"> + ${field['helptext']} + </div> + <div style="clear: both"></div> + </div> + %endfor + %if not user_info_select: + <input type="hidden" name="user_info_select" value="${user_info_form.id}"/> + %endif + %endif + <div class="form-row"> + <input type="submit" name="create_user_button" value="Submit"> + </div> + </form> +</div> \ No newline at end of file diff -r 6e8263ea83fa -r cd0596754ff1 test/base/twilltestcase.py --- a/test/base/twilltestcase.py Mon Nov 02 09:54:18 2009 -0500 +++ b/test/base/twilltestcase.py Mon Nov 02 13:30:17 2009 -0500 @@ -596,13 +596,82 @@ # Functions associated with user accounts def create( self, email='test(a)bx.psu.edu', password='testuser' ): self.home() - self.visit_page( "user/create?email=%s&password=%s&confirm=%s" % ( email, password, password ) ) + self.visit_page( "user/create?email=%s&password=%s&confirm=%s&create_user_button=Submit" % ( email, password, password ) ) self.check_page_for_string( "Now logged in as %s" %email ) self.home() # Make sure a new private role was created for the user self.visit_page( "user/set_default_permissions" ) self.check_page_for_string( email ) self.home() + def create_user_with_info( self, email, password, username, user_info_forms, user_info_form_id, user_info_values ): + ''' + This method registers a new user and also provides use info + ''' + self.home() + if user_info_forms == 'multiple': + self.visit_page( "user/create?user_info_select=%i&admin_view=False" % user_info_form_id ) + else: + self.visit_page( "user/create?admin_view=False" ) + self.check_page_for_string( "Create account" ) + tc.fv( "1", "email", email ) + tc.fv( "1", "password", password ) + tc.fv( "1", "confirm", password ) + tc.fv( "1", "username", username ) + if user_info_forms == 'multiple': + self.check_page_for_string( "User type" ) + for index, info_value in enumerate(user_info_values): + tc.fv( "1", "field_%i" % index, info_value ) + tc.submit( "create_user_button" ) + self.check_page_for_string( "Now logged in as %s" % email ) + def create_user_with_info_as_admin( self, email, password, username, user_info_forms, user_info_form_id, user_info_values ): + ''' + This method registers a new user and also provides use info as an admin + ''' + self.home() + if user_info_forms == 'multiple': + self.visit_page( "admin/users?operation=create?user_info_select=%i&admin_view=False" % user_info_form_id ) + else: + self.visit_page( "admin/users?operation=create" ) + self.check_page_for_string( "Create account" ) + tc.fv( "1", "email", email ) + tc.fv( "1", "password", password ) + tc.fv( "1", "confirm", password ) + tc.fv( "1", "username", username ) + if user_info_forms == 'multiple': + self.check_page_for_string( "User type" ) + for index, info_value in enumerate(user_info_values): + tc.fv( "1", "field_%i" % index, info_value ) + tc.submit( "create_user_button" ) + self.check_page_for_string( "Created new user account (%s)" % email ) + def edit_login_info( self, new_email, new_username ): + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + tc.fv( "1", "email", new_email ) + tc.fv( "1", "username", new_username ) + tc.submit( "login_info_button" ) + self.check_page_for_string( 'The login information has been updated with the changes' ) + self.check_page_for_string( new_email ) + self.check_page_for_string( new_username ) + def change_password( self, password, new_password ): + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + tc.fv( "2", "current", password ) + tc.fv( "2", "password", new_password ) + tc.fv( "2", "confirm", new_password ) + tc.submit( "change_password_button" ) + self.check_page_for_string( 'The password has been changed.' ) + def edit_user_info( self, info_values ): + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + for index, info_value in enumerate(info_values): + tc.fv( "3", "field_%i" % index, info_value ) + tc.submit( "edit_user_info_button" ) + self.check_page_for_string( "The user information has been updated with the changes." ) + for value in info_values: + self.check_page_for_string( value ) def user_set_default_permissions( self, permissions_out=[], permissions_in=[], role_id=2 ): # role.id = 2 is Private Role for test2(a)bx.psu.edu # NOTE: Twill has a bug that requires the ~/user/permissions page to contain at least 1 option value # in each select list or twill throws an exception, which is: ParseError: OPTION outside of SELECT @@ -1161,19 +1230,8 @@ for index, field_value in enumerate(fields): tc.fv( "1", "field_%i" % index, field_value ) tc.submit( "create_request_button" ) - def create_request_admin( self, request_type_id, user_id, name, desc, library_id, fields ): - self.home() - self.visit_url( "%s/requests_admin/new?create=True&select_request_type=%i&library_id=%i" % ( self.url, - request_type_id, - library_id ) ) - self.check_page_for_string( 'Add a new request' ) - tc.fv( "1", "select_user", str(user_id) ) - tc.fv( "1", "name", name ) - tc.fv( "1", "desc", desc ) - tc.fv( "1", "library_id", str(library_id) ) - for index, field_value in enumerate(fields): - tc.fv( "1", "field_%i" % index, field_value ) - tc.submit( "create_request_button" ) + self.check_page_for_string( name ) + self.check_page_for_string( desc ) def edit_request( self, request_id, name, new_name, new_desc, new_library_id, new_folder_id, new_fields): self.home() self.visit_url( "%s/requests/edit?request_id=%i&show=True" % (self.url, request_id) ) @@ -1185,6 +1243,8 @@ for index, field_value in enumerate(new_fields): tc.fv( "1", "field_%i" % index, field_value ) tc.submit( "save_changes_request_button" ) + self.check_page_for_string( new_name ) + self.check_page_for_string( new_desc ) def add_samples( self, request_id, request_name, samples ): self.home() self.visit_url( "%s/requests/list?sort=-create_time&operation=show_request&id=%s" % ( self.url, self.security.encode_id( request_id ) )) @@ -1196,6 +1256,11 @@ for field_index, field_value in enumerate(fields): tc.fv( "1", "sample_%i_field_%i" % ( sample_index, field_index ), field_value ) tc.submit( "save_samples_button" ) + for sample_name, fields in samples: + self.check_page_for_string( sample_name ) + self.check_page_for_string( 'Unsubmitted' ) + for field_value in fields: + self.check_page_for_string( field_value ) def submit_request( self, request_id, request_name ): self.home() self.visit_url( "%s/requests/submit_request?id=%i" % ( self.url, request_id )) @@ -1207,21 +1272,15 @@ for index, bar_code in enumerate(bar_codes): tc.fv( "1", "sample_%i_bar_code" % index, bar_code ) tc.submit( "save_bar_codes" ) - def change_sample_state( self, sample_name, sample_id, new_state_id, comment='' ): + self.check_page_for_string( 'Bar codes has been saved for this request' ) + def change_sample_state( self, sample_name, sample_id, new_state_id, new_state_name, comment='' ): self.home() self.visit_url( "%s/requests_admin/show_events?sample_id=%i" % (self.url, sample_id) ) self.check_page_for_string( 'Events for Sample "%s"' % sample_name ) tc.fv( "1", "select_state", str(new_state_id) ) tc.fv( "1", "comment", comment ) tc.submit( "add_event_button" ) - # Address stuff - def create_address( self, address ): - self.home() - self.visit_url( "%s/user/new_address" % self.url ) - self.check_page_for_string( 'New address' ) - for name, value in address.iteritems(): - tc.fv( "1", name, value ) - tc.submit( "Save_button" ) + self.check_page_for_string( new_state_name ) # Library stuff def create_library( self, name='Library One', description='This is Library One' ): """Create a new library""" diff -r 6e8263ea83fa -r cd0596754ff1 test/functional/test_forms_and_requests.py --- a/test/functional/test_forms_and_requests.py Mon Nov 02 09:54:18 2009 -0500 +++ b/test/functional/test_forms_and_requests.py Mon Nov 02 13:30:17 2009 -0500 @@ -41,16 +41,11 @@ self.login( email='test(a)bx.psu.edu' ) # create a form global form_one_name - name = form_one_name desc = "This is Form One's description" formtype = galaxy.model.FormDefinition.types.REQUEST - self.create_form( name=name, desc=desc, formtype=formtype, num_fields=0 ) + self.create_form( name=form_one_name, desc=desc, formtype=formtype, num_fields=0 ) # Get the form_definition object for later tests - form_one = sa_session.query( galaxy.model.FormDefinition ) \ - .filter( and_( galaxy.model.FormDefinition.table.c.name==name, - galaxy.model.FormDefinition.table.c.desc==desc, - galaxy.model.FormDefinition.table.c.type==formtype ) ) \ - .all()[-1] + form_one = get_latest_form(form_one_name) assert form_one is not None, 'Problem retrieving form named "%s" from the database' % name # edit form & add few more fields new_name = "Request Form (Renamed)" @@ -83,20 +78,18 @@ def test_015_create_sample_form( self ): """Testing creating another form (for samples)""" global form_two_name - name = form_two_name desc = "This is Form One's description" formtype = 'Sequencing Sample Form' - self.create_form( name=name, desc=desc, formtype=formtype ) + self.create_form( name=form_two_name, desc=desc, formtype=formtype ) self.home() self.visit_page( 'forms/manage' ) - self.check_page_for_string( name ) + self.check_page_for_string( form_two_name ) self.check_page_for_string( desc ) self.check_page_for_string( formtype ) def test_020_create_request_type( self ): """Testing creating a new requestype""" request_form = get_latest_form(form_one_name) sample_form = get_latest_form(form_two_name) - print request_form.id, sample_form.id self.create_request_type(request_type_name, "test request type", str(request_form.id), str(sample_form.id), sample_states ) global request_type @@ -179,7 +172,6 @@ % ( self.url, address1[ 'short_desc' ], address1[ 'name' ], address1[ 'institution' ], address1[ 'address1' ], address1[ 'address2' ], address1[ 'city' ], address1[ 'state' ], address1[ 'postal_code' ], address1[ 'country' ], address1[ 'phone' ] ) - print url_str self.visit_url( url_str ) self.check_page_for_string( 'Address <b>%s</b> has been added' % address1[ 'short_desc' ] ) global regular_user @@ -201,8 +193,6 @@ # create the request request_name, request_desc = 'Request One', 'Request One Description' self.create_request(request_type.id, request_name, request_desc, library_one.id, 'none', fields) - self.check_page_for_string( request_name ) - self.check_page_for_string( request_desc ) global request_one request_one = sa_session.query( galaxy.model.Request ) \ .filter( and_( galaxy.model.Request.table.c.name==request_name, @@ -216,18 +206,11 @@ ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ] # add samples to this request self.add_samples( request_one.id, request_one.name, samples ) - for sample_name, fields in samples: - self.check_page_for_string( sample_name ) - self.check_page_for_string( 'Unsubmitted' ) - for field_value in fields: - self.check_page_for_string( field_value ) # edit this request fields = ['option2', str(user_address.id), 'field three value (edited)'] self.edit_request(request_one.id, request_one.name, request_one.name+' (Renamed)', request_one.desc+' (Re-described)', library_one.id, folder_one.id, fields) sa_session.refresh( request_one ) - self.check_page_for_string( request_name+' (Renamed)' ) - self.check_page_for_string( request_desc+' (Re-described)' ) # check if the request is showing in the 'unsubmitted' filter self.home() self.visit_url( '%s/requests/list?show_filter=Unsubmitted' % self.url ) @@ -256,15 +239,10 @@ # set bar codes for the samples bar_codes = [ '1234567890', '0987654321' ] self.add_bar_codes( request_one.id, request_one.name, bar_codes ) - self.check_page_for_string( 'Bar codes has been saved for this request' ) # change the states of all the samples of this request for sample in request_one.samples: - self.change_sample_state( sample.name, sample.id, request_type.states[1].id ) - self.check_page_for_string( request_type.states[1].name ) - self.check_page_for_string( request_type.states[1].desc ) - self.change_sample_state( sample.name, sample.id, request_type.states[2].id ) - self.check_page_for_string( request_type.states[2].name ) - self.check_page_for_string( request_type.states[2].desc ) + self.change_sample_state( sample.name, sample.id, request_type.states[1].id, request_type.states[1].name ) + self.change_sample_state( sample.name, sample.id, request_type.states[2].id, request_type.states[2].name ) self.home() sa_session.refresh( request_one ) # check if the request's state is now set to 'complete' @@ -300,11 +278,6 @@ ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ] # add samples to this request self.add_samples( request_two.id, request_two.name, samples ) - for sample_name, fields in samples: - self.check_page_for_string( sample_name ) - self.check_page_for_string( 'Unsubmitted' ) - for field_value in fields: - self.check_page_for_string( field_value ) # submit the request self.submit_request( request_two.id, request_two.name ) sa_session.refresh( request_two ) diff -r 6e8263ea83fa -r cd0596754ff1 test/functional/test_user_info.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/functional/test_user_info.py Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,149 @@ +import galaxy.model +from galaxy.model.orm import * +from galaxy.model.mapping import context as sa_session +from base.twilltestcase import * + +not_logged_in_as_admin_security_msg = 'You must be logged in as an administrator to access this feature.' +logged_in_as_admin_security_msg = 'You must be an administrator to access this feature.' +not_logged_in_security_msg = 'You must be logged in to create/submit sequencing requests' +form_one_name = "Student" +form_two_name = "Researcher" + +def get_latest_form(form_name): + fdc_list = sa_session.query( galaxy.model.FormDefinitionCurrent ) \ + .filter( galaxy.model.FormDefinitionCurrent.table.c.deleted==False ) \ + .order_by( galaxy.model.FormDefinitionCurrent.table.c.create_time.desc() ) + for fdc in fdc_list: + if form_name == fdc.latest_form.name: + return fdc.latest_form + return None + + +class TestUserInfo( TwillTestCase ): + def test_000_create_user_info_forms( self ): + """Testing creating a new user info form and editing it""" + self.logout() + self.login( email='test(a)bx.psu.edu' ) + # create a the first form + global form_one_name + name = form_one_name + desc = "This is Student user info form's description" + formtype = galaxy.model.FormDefinition.types.USER_INFO + self.create_form( name=name, desc=desc, formtype=formtype, num_fields=0 ) + # Get the form_definition object for later tests + form_one = get_latest_form(form_one_name) + assert form_one is not None, 'Problem retrieving form named "%s" from the database' % name + # edit form & add few more fields + fields = [dict(name='Affiliation', + desc='The type of organization you are affiliated with', + type='SelectField', + required='optional', + selectlist=['Educational', 'Research', 'Commercial']), + dict(name='Name of Organization', + desc='', + type='TextField', + required='optional')] + form_one = get_latest_form(form_one_name) + self.form_add_field(form_one.id, form_one.name, form_one.desc, form_one.type, field_index=len(form_one.fields), fields=fields) + form_one_latest = get_latest_form(form_one_name) + assert len(form_one_latest.fields) == len(form_one.fields)+len(fields) + # create the second form + global form_two_name + name = form_two_name + desc = "This is Researcher user info form's description" + self.create_form( name=name, desc=desc, formtype=formtype, num_fields=0 ) + # Get the form_definition object for later tests + form_two = get_latest_form(form_two_name) + assert form_two is not None, 'Problem retrieving form named "%s" from the database' % name + # edit form & add few more fields + fields = [dict(name='Affiliation', + desc='The type of organization you are affiliated with', + type='SelectField', + required='optional', + selectlist=['Educational', 'Research', 'Commercial']), + dict(name='Name of Organization', + desc='', + type='TextField', + required='optional')] + form_two = get_latest_form(form_two_name) + self.form_add_field(form_two.id, form_two.name, form_two.desc, form_two.type, field_index=len(form_one.fields), fields=fields) + form_two_latest = get_latest_form(form_two_name) + assert len(form_two_latest.fields) == len(form_two.fields)+len(fields) + def test_005_user_reqistration_multiple_user_info_forms( self ): + ''' Testing user registration with multiple user info forms ''' + self.logout() + # user a new user with 'Student' user info form + form_one = get_latest_form(form_one_name) + user_info_values=['Educational', 'Penn State'] + self.create_user_with_info( 'test1(a)bx.psu.edu', 'testuser', 'test1', + user_info_forms='multiple', + user_info_form_id=form_one.id, + user_info_values=user_info_values ) + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + for value in user_info_values: + self.check_page_for_string( value ) + def test_010_user_reqistration_single_user_info_forms( self ): + ''' Testing user registration with a single user info form ''' + # lets delete the 'Researcher' user info form + self.login( 'test(a)bx.psu.edu' ) + form_two_latest = get_latest_form(form_two_name) + form_two_latest.current.deleted = True + form_two_latest.current.flush() + self.home() + self.visit_page('forms/manage?show_filter=Deleted') + self.check_page_for_string(form_two_latest.name) + self.logout() + # user a new user with 'Student' user info form + form_one = get_latest_form(form_one_name) + user_info_values=['Educational', 'Penn State'] + self.create_user_with_info( 'test2(a)bx.psu.edu', 'testuser', 'test2', + user_info_forms='single', + user_info_form_id=form_one.id, + user_info_values=user_info_values ) + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + for value in user_info_values: + self.check_page_for_string( value ) + def test_015_edit_user_info( self ): + """Testing editing user info as a regular user""" + self.logout() + self.login( 'test1(a)bx.psu.edu' ) + user = sa_session.query( galaxy.model.User ) \ + .filter( and_( galaxy.model.User.table.c.email=='test1(a)bx.psu.edu' ) ).first() + self.edit_login_info( new_email='test1_new(a)bx.psu.edu', new_username='test1_new' ) + self.change_password('testuser', 'new_testuser') + self.edit_user_info( ['Research', 'PSU'] ) + def test_020_create_user_as_admin( self ): + ''' Testing creating users as an admin ''' + self.logout() + self.login( 'test2(a)bx.psu.edu' ) + form_one = get_latest_form(form_one_name) + user_info_values=['Educational', 'Penn State'] + self.create_user_with_info( 'test3(a)bx.psu.edu', 'testuser', 'test3', + user_info_forms='single', + user_info_form_id=form_one.id, + user_info_values=user_info_values ) + self.logout() + self.login( 'test(a)bx.psu.edu' ) + user = sa_session.query( galaxy.model.User ) \ + .filter( and_( galaxy.model.User.table.c.email=='test3(a)bx.psu.edu' ) ).first() + self.home() + page = "admin/users?id=%s&operation=information&f-deleted=False" % self.security.encode_id( user.id ) + self.visit_page( page ) + self.check_page_for_string( 'Manage User Information' ) + self.check_page_for_string( 'test3(a)bx.psu.edu' ) + for value in user_info_values: + self.check_page_for_string( value ) + # lets delete the 'Student' user info form + self.login( 'test(a)bx.psu.edu' ) + form_one_latest = get_latest_form(form_one_name) + form_one_latest.current.deleted = True + form_one_latest.current.flush() + self.home() + self.visit_page('forms/manage?show_filter=Deleted') + self.check_page_for_string(form_one_latest.name) + self.logout() + \ No newline at end of file
1
0
0
0
← Newer
1
...
18
19
20
21
22
23
24
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Results per page:
10
25
50
100
200