galaxy-dev
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
November 2009
- 26 participants
- 233 discussions
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
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
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
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
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
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
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
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
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
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