1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/009088d5e76f/
Changeset: 009088d5e76f
User: fubar
Date: 2013-08-10 03:28:28
Summary: Patch for missing options in FromDataTableOutputActionOption when these are used in an output filter
Affected #: 1 file
diff -r 31d2d58ebf102f87c21f170b16452d06fd510a14 -r 009088d5e76fb00794da78dc0ee3cdaa8524b7d8 lib/galaxy/tools/parameters/output.py
--- a/lib/galaxy/tools/parameters/output.py
+++ b/lib/galaxy/tools/parameters/output.py
@@ -217,7 +217,10 @@
else:
self.missing_tool_data_table_name = self.name
def get_value( self, other_values ):
- options = self.options
+ try:
+ options = self.options
+ except:
+ options = []
for filter in self.filters:
options = filter.filter_options( options, other_values )
try:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e7e82003f235/
Changeset: e7e82003f235
Branch: next-stable
User: natefoo
Date: 2013-08-08 21:40:00
Summary: Define the ActionInputError exception that was being caught in the quotas API controller. Thanks Jim Johnson.
Affected #: 3 files
diff -r 8d8429484ac3ffef21fedd1077acf050c8aed459 -r e7e82003f235c4cca84939e04aa432d128093866 lib/galaxy/actions/admin.py
--- a/lib/galaxy/actions/admin.py
+++ b/lib/galaxy/actions/admin.py
@@ -4,7 +4,7 @@
import logging
from galaxy import util
-from galaxy.exceptions import MessageException
+from galaxy.exceptions import ActionInputError
log = logging.getLogger( __name__ )
@@ -21,21 +21,21 @@
except AssertionError:
create_amount = False
if not params.name or not params.description:
- raise MessageException( "Enter a valid name and a description.", type='error' )
+ raise ActionInputError( "Enter a valid name and a description." )
elif self.sa_session.query( self.app.model.Quota ).filter( self.app.model.Quota.table.c.name==params.name ).first():
- raise MessageException( "Quota names must be unique and a quota with that name already exists, so choose another name.", type='error' )
+ raise ActionInputError( "Quota names must be unique and a quota with that name already exists, so choose another name." )
elif not params.get( 'amount', None ):
- raise MessageException( "Enter a valid quota amount.", type='error' )
+ raise ActionInputError( "Enter a valid quota amount." )
elif create_amount is False:
- raise MessageException( "Unable to parse the provided amount.", type='error' )
+ raise ActionInputError( "Unable to parse the provided amount." )
elif params.operation not in self.app.model.Quota.valid_operations:
- raise MessageException( "Enter a valid operation.", type='error' )
+ raise ActionInputError( "Enter a valid operation." )
elif params.default != 'no' and params.default not in self.app.model.DefaultQuotaAssociation.types.__dict__.values():
- raise MessageException( "Enter a valid default type.", type='error' )
+ raise ActionInputError( "Enter a valid default type." )
elif params.default != 'no' and params.operation != '=':
- raise MessageException( "Operation for a default quota must be '='.", type='error' )
+ raise ActionInputError( "Operation for a default quota must be '='." )
elif create_amount is None and params.operation != '=':
- raise MessageException( "Operation for an unlimited quota must be '='.", type='error' )
+ raise ActionInputError( "Operation for an unlimited quota must be '='." )
else:
# Create the quota
quota = self.app.model.Quota( name=params.name, description=params.description, amount=create_amount, operation=params.operation )
@@ -59,9 +59,9 @@
def _rename_quota( self, quota, params ):
if not params.name:
- raise MessageException( 'Enter a valid name', type='error' )
+ raise ActionInputError( 'Enter a valid name' )
elif params.name != quota.name and self.sa_session.query( self.app.model.Quota ).filter( self.app.model.Quota.table.c.name==params.name ).first():
- raise MessageException( 'A quota with that name already exists', type='error' )
+ raise ActionInputError( 'A quota with that name already exists' )
else:
old_name = quota.name
quota.name = params.name
@@ -73,7 +73,7 @@
def _manage_users_and_groups_for_quota( self, quota, params ):
if quota.default:
- raise MessageException( 'Default quotas cannot be associated with specific users and groups', type='error' )
+ raise ActionInputError( 'Default quotas cannot be associated with specific users and groups' )
else:
in_users = [ self.sa_session.query( self.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
in_groups = [ self.sa_session.query( self.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
@@ -91,11 +91,11 @@
except AssertionError:
new_amount = False
if not params.amount:
- raise MessageException( 'Enter a valid amount', type='error' )
+ raise ActionInputError( 'Enter a valid amount' )
elif new_amount is False:
- raise MessageException( 'Unable to parse the provided amount', type='error' )
+ raise ActionInputError( 'Unable to parse the provided amount' )
elif params.operation not in self.app.model.Quota.valid_operations:
- raise MessageException( 'Enter a valid operation', type='error' )
+ raise ActionInputError( 'Enter a valid operation' )
else:
quota.amount = new_amount
quota.operation = params.operation
@@ -106,7 +106,7 @@
def _set_quota_default( self, quota, params ):
if params.default != 'no' and params.default not in self.app.model.DefaultQuotaAssociation.types.__dict__.values():
- raise MessageException( 'Enter a valid default type.', type='error' )
+ raise ActionInputError( 'Enter a valid default type.' )
else:
if params.default != 'no':
self.app.quota_agent.set_default_quota( params.default, quota )
@@ -123,7 +123,7 @@
def _unset_quota_default( self, quota, params ):
if not quota.default:
- raise MessageException( "Quota '%s' is not a default." % quota.name, type='error' )
+ raise ActionInputError( "Quota '%s' is not a default." % quota.name )
else:
message = "Quota '%s' is no longer the default for %s users." % ( quota.name, quota.default[0].type )
for dqa in quota.default:
@@ -138,9 +138,9 @@
if q.default:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' is a default, please unset it as a default before deleting it" % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' is a default, please unset it as a default before deleting it" % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas are defaults, please unset them as defaults before deleting them: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas are defaults, please unset them as defaults before deleting them: " + ', '.join( names ) )
message = "Deleted %d quotas: " % len( quotas )
for q in quotas:
q.deleted = True
@@ -157,9 +157,9 @@
if not q.deleted:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' has not been deleted, so it cannot be undeleted." % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' has not been deleted, so it cannot be undeleted." % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ) )
message = "Undeleted %d quotas: " % len( quotas )
for q in quotas:
q.deleted = False
@@ -182,9 +182,9 @@
if not q.deleted:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' has not been deleted, so it cannot be purged." % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' has not been deleted, so it cannot be purged." % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ) )
message = "Purged %d quotas: " % len( quotas )
for q in quotas:
# Delete UserQuotaAssociations
diff -r 8d8429484ac3ffef21fedd1077acf050c8aed459 -r e7e82003f235c4cca84939e04aa432d128093866 lib/galaxy/exceptions/__init__.py
--- a/lib/galaxy/exceptions/__init__.py
+++ b/lib/galaxy/exceptions/__init__.py
@@ -21,6 +21,10 @@
class ItemOwnershipException( MessageException ):
pass
+class ActionInputError( MessageException ):
+ def __init__( self, err_msg, type="error" ):
+ super( ActionInputError, self ).__init__( err_msg, type )
+
class ObjectNotFound( Exception ):
""" Accessed object was not found """
pass
diff -r 8d8429484ac3ffef21fedd1077acf050c8aed459 -r e7e82003f235c4cca84939e04aa432d128093866 lib/galaxy/webapps/galaxy/api/quotas.py
--- a/lib/galaxy/webapps/galaxy/api/quotas.py
+++ b/lib/galaxy/webapps/galaxy/api/quotas.py
@@ -11,7 +11,7 @@
from galaxy.actions.admin import AdminActions
from paste.httpexceptions import HTTPBadRequest
-from galaxy.exceptions import *
+from galaxy.exceptions import ActionInputError
log = logging.getLogger( __name__ )
https://bitbucket.org/galaxy/galaxy-central/commits/61ddd0d07950/
Changeset: 61ddd0d07950
User: natefoo
Date: 2013-08-08 21:50:05
Summary: Merge next-stable.
Affected #: 3 files
diff -r 3778811f053a5f71c68408a9e8beb627f6ccdede -r 61ddd0d07950fafc18fed95354459e7715e6b1d7 lib/galaxy/actions/admin.py
--- a/lib/galaxy/actions/admin.py
+++ b/lib/galaxy/actions/admin.py
@@ -4,7 +4,7 @@
import logging
from galaxy import util
-from galaxy.exceptions import MessageException
+from galaxy.exceptions import ActionInputError
log = logging.getLogger( __name__ )
@@ -21,21 +21,21 @@
except AssertionError:
create_amount = False
if not params.name or not params.description:
- raise MessageException( "Enter a valid name and a description.", type='error' )
+ raise ActionInputError( "Enter a valid name and a description." )
elif self.sa_session.query( self.app.model.Quota ).filter( self.app.model.Quota.table.c.name==params.name ).first():
- raise MessageException( "Quota names must be unique and a quota with that name already exists, so choose another name.", type='error' )
+ raise ActionInputError( "Quota names must be unique and a quota with that name already exists, so choose another name." )
elif not params.get( 'amount', None ):
- raise MessageException( "Enter a valid quota amount.", type='error' )
+ raise ActionInputError( "Enter a valid quota amount." )
elif create_amount is False:
- raise MessageException( "Unable to parse the provided amount.", type='error' )
+ raise ActionInputError( "Unable to parse the provided amount." )
elif params.operation not in self.app.model.Quota.valid_operations:
- raise MessageException( "Enter a valid operation.", type='error' )
+ raise ActionInputError( "Enter a valid operation." )
elif params.default != 'no' and params.default not in self.app.model.DefaultQuotaAssociation.types.__dict__.values():
- raise MessageException( "Enter a valid default type.", type='error' )
+ raise ActionInputError( "Enter a valid default type." )
elif params.default != 'no' and params.operation != '=':
- raise MessageException( "Operation for a default quota must be '='.", type='error' )
+ raise ActionInputError( "Operation for a default quota must be '='." )
elif create_amount is None and params.operation != '=':
- raise MessageException( "Operation for an unlimited quota must be '='.", type='error' )
+ raise ActionInputError( "Operation for an unlimited quota must be '='." )
else:
# Create the quota
quota = self.app.model.Quota( name=params.name, description=params.description, amount=create_amount, operation=params.operation )
@@ -59,9 +59,9 @@
def _rename_quota( self, quota, params ):
if not params.name:
- raise MessageException( 'Enter a valid name', type='error' )
+ raise ActionInputError( 'Enter a valid name' )
elif params.name != quota.name and self.sa_session.query( self.app.model.Quota ).filter( self.app.model.Quota.table.c.name==params.name ).first():
- raise MessageException( 'A quota with that name already exists', type='error' )
+ raise ActionInputError( 'A quota with that name already exists' )
else:
old_name = quota.name
quota.name = params.name
@@ -73,7 +73,7 @@
def _manage_users_and_groups_for_quota( self, quota, params ):
if quota.default:
- raise MessageException( 'Default quotas cannot be associated with specific users and groups', type='error' )
+ raise ActionInputError( 'Default quotas cannot be associated with specific users and groups' )
else:
in_users = [ self.sa_session.query( self.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
in_groups = [ self.sa_session.query( self.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
@@ -91,11 +91,11 @@
except AssertionError:
new_amount = False
if not params.amount:
- raise MessageException( 'Enter a valid amount', type='error' )
+ raise ActionInputError( 'Enter a valid amount' )
elif new_amount is False:
- raise MessageException( 'Unable to parse the provided amount', type='error' )
+ raise ActionInputError( 'Unable to parse the provided amount' )
elif params.operation not in self.app.model.Quota.valid_operations:
- raise MessageException( 'Enter a valid operation', type='error' )
+ raise ActionInputError( 'Enter a valid operation' )
else:
quota.amount = new_amount
quota.operation = params.operation
@@ -106,7 +106,7 @@
def _set_quota_default( self, quota, params ):
if params.default != 'no' and params.default not in self.app.model.DefaultQuotaAssociation.types.__dict__.values():
- raise MessageException( 'Enter a valid default type.', type='error' )
+ raise ActionInputError( 'Enter a valid default type.' )
else:
if params.default != 'no':
self.app.quota_agent.set_default_quota( params.default, quota )
@@ -123,7 +123,7 @@
def _unset_quota_default( self, quota, params ):
if not quota.default:
- raise MessageException( "Quota '%s' is not a default." % quota.name, type='error' )
+ raise ActionInputError( "Quota '%s' is not a default." % quota.name )
else:
message = "Quota '%s' is no longer the default for %s users." % ( quota.name, quota.default[0].type )
for dqa in quota.default:
@@ -138,9 +138,9 @@
if q.default:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' is a default, please unset it as a default before deleting it" % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' is a default, please unset it as a default before deleting it" % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas are defaults, please unset them as defaults before deleting them: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas are defaults, please unset them as defaults before deleting them: " + ', '.join( names ) )
message = "Deleted %d quotas: " % len( quotas )
for q in quotas:
q.deleted = True
@@ -157,9 +157,9 @@
if not q.deleted:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' has not been deleted, so it cannot be undeleted." % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' has not been deleted, so it cannot be undeleted." % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ) )
message = "Undeleted %d quotas: " % len( quotas )
for q in quotas:
q.deleted = False
@@ -182,9 +182,9 @@
if not q.deleted:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' has not been deleted, so it cannot be purged." % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' has not been deleted, so it cannot be purged." % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ) )
message = "Purged %d quotas: " % len( quotas )
for q in quotas:
# Delete UserQuotaAssociations
diff -r 3778811f053a5f71c68408a9e8beb627f6ccdede -r 61ddd0d07950fafc18fed95354459e7715e6b1d7 lib/galaxy/exceptions/__init__.py
--- a/lib/galaxy/exceptions/__init__.py
+++ b/lib/galaxy/exceptions/__init__.py
@@ -21,6 +21,10 @@
class ItemOwnershipException( MessageException ):
pass
+class ActionInputError( MessageException ):
+ def __init__( self, err_msg, type="error" ):
+ super( ActionInputError, self ).__init__( err_msg, type )
+
class ObjectNotFound( Exception ):
""" Accessed object was not found """
pass
diff -r 3778811f053a5f71c68408a9e8beb627f6ccdede -r 61ddd0d07950fafc18fed95354459e7715e6b1d7 lib/galaxy/webapps/galaxy/api/quotas.py
--- a/lib/galaxy/webapps/galaxy/api/quotas.py
+++ b/lib/galaxy/webapps/galaxy/api/quotas.py
@@ -11,7 +11,7 @@
from galaxy.actions.admin import AdminActions
from paste.httpexceptions import HTTPBadRequest
-from galaxy.exceptions import *
+from galaxy.exceptions import ActionInputError
log = logging.getLogger( __name__ )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3778811f053a/
Changeset: 3778811f053a
User: carlfeberhard
Date: 2013-08-08 21:04:53
Summary: Graph datatype: add data providers for SIF & XGMML; simple graph data structure to util
Affected #: 2 files
diff -r 2e0abb7f9b04540e616458a90a87191ed98a3ba4 -r 3778811f053a5f71c68408a9e8beb627f6ccdede lib/galaxy/datatypes/graph.py
--- a/lib/galaxy/datatypes/graph.py
+++ b/lib/galaxy/datatypes/graph.py
@@ -2,12 +2,18 @@
Graph content classes.
"""
-import data, tabular, xml
+import data
+import tabular
+import xml
+
+import dataproviders
+from galaxy.util import simplegraph
import logging
log = logging.getLogger( __name__ )
+(a)dataproviders.decorators.has_dataproviders
class Xgmml( xml.GenericXml ):
"""
XGMML graph format
@@ -48,7 +54,13 @@
#For one file only, use base class method (move/copy)
data.Text.merge( split_files, output_file )
+ @dataproviders.decorators.dataprovider_factory( 'node-edge', dataproviders.hierarchy.XMLDataProvider.settings )
+ def node_edge_dataprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return XGMMLGraphDataProvider( dataset_source, **settings )
+
+(a)dataproviders.decorators.has_dataproviders
class Sif( tabular.Tabular ):
"""
SIF graph format
@@ -75,7 +87,6 @@
"""
Determines whether the file is SIF
"""
- print '---------------------------------------- sniffing Siffing'
line = ''
with open( filename ) as infile:
correct = True
@@ -92,6 +103,11 @@
def merge( split_files, output_file ):
data.Text.merge( split_files, output_file )
+ @dataproviders.decorators.dataprovider_factory( 'node-edge', dataproviders.column.ColumnarDataProvider.settings )
+ def node_edge_dataprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return SIFGraphDataProvider( dataset_source, **settings )
+
#TODO: we might want to look at rdflib or a similar, larger lib/egg
class Rdf( xml.GenericXml ):
@@ -108,3 +124,75 @@
else:
dataset.peek = 'file does not exist'
dataset.blurb = 'file purged from disk'
+
+ #TODO: won't be as simple
+ #(a)dataproviders.decorators.dataprovider_factory( 'node-edge', dataproviders.column.ColumnarDataProvider.settings )
+ #def node_edge_dataprovider( self, dataset, **settings ):
+ # dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ # return None
+
+
+# ----------------------------------------------------------------------------- graph specific data providers
+class XGMMLGraphDataProvider( dataproviders.hierarchy.XMLDataProvider ):
+ """
+ Provide two lists: nodes, edges::
+
+ 'nodes': contains objects of the form:
+ { 'id' : <some string id>, 'data': <any extra data> }
+ 'edges': contains objects of the form:
+ { 'source' : <an index into nodes>, 'target': <an index into nodes>, 'data': <any extra data> }
+ """
+ def __iter__( self ):
+ # use simple graph to store nodes and links, later providing them as a dict
+ # essentially this is a form of aggregation
+ graph = simplegraph.SimpleGraph()
+
+ parent_gen = super( XGMMLGraphDataProvider, self ).__iter__()
+ for graph_elem in parent_gen:
+ if 'children' not in graph_elem:
+ continue
+ for elem in graph_elem[ 'children' ]:
+ # use endswith to work around Elementtree namespaces
+ if elem[ 'tag' ].endswith( 'node' ):
+ node_id = elem[ 'attrib' ][ 'id' ]
+ # pass the entire, parsed xml element as the data
+ graph.add_node( node_id, **elem )
+
+ elif elem[ 'tag' ].endswith( 'edge' ):
+ source_id = elem[ 'attrib' ][ 'source' ]
+ target_id = elem[ 'attrib' ][ 'target' ]
+ graph.add_edge( source_id, target_id, **elem )
+
+ yield graph.as_dict()
+
+
+class SIFGraphDataProvider( dataproviders.column.ColumnarDataProvider ):
+ """
+ Provide two lists: nodes, edges::
+
+ 'nodes': contains objects of the form:
+ { 'id' : <some string id>, 'data': <any extra data> }
+ 'edges': contains objects of the form:
+ { 'source' : <an index into nodes>, 'target': <an index into nodes>, 'data': <any extra data> }
+ """
+ def __iter__( self ):
+ # use simple graph to store nodes and links, later providing them as a dict
+ # essentially this is a form of aggregation
+ graph = simplegraph.SimpleGraph()
+ # SIF is tabular with the source, link-type, and all targets in the columns
+ parent_gen = super( SIFGraphDataProvider, self ).__iter__()
+ for columns in parent_gen:
+ if columns:
+ source_id = columns[0]
+ # there's no extra data for nodes (or links) in the examples I've seen
+ graph.add_node( source_id )
+
+ # targets are the (variadic) remaining columns
+ if len( columns ) >= 3:
+ relation = columns[1]
+ targets = columns[2:]
+ for target_id in targets:
+ graph.add_node( target_id )
+ graph.add_edge( source_id, target_id, type=relation )
+
+ yield graph.as_dict()
diff -r 2e0abb7f9b04540e616458a90a87191ed98a3ba4 -r 3778811f053a5f71c68408a9e8beb627f6ccdede lib/galaxy/util/simplegraph.py
--- /dev/null
+++ b/lib/galaxy/util/simplegraph.py
@@ -0,0 +1,127 @@
+"""
+Fencepost-simple graph structure implementation.
+"""
+# Currently (2013.7.12) only used in easing the parsing of graph datatype data.
+
+from galaxy.util.odict import odict
+
+
+class SimpleGraphNode( object ):
+ """
+ Node representation.
+ """
+ def __init__( self, index, **data ):
+ """
+ :param index: index of this node in some parent list
+ :type index: int
+ :param data: any extra data that needs to be saved
+ :type data: (variadic dictionary)
+ """
+ # a bit application specific (could be 'id')
+ self.index = index
+ self.data = data
+
+
+class SimpleGraphEdge( object ):
+ """
+ Edge representation.
+ """
+ def __init__( self, source_index, target_index, **data ):
+ """
+ :param source_index: index of the edge's source node in some parent list
+ :type source_index: int
+ :param target_index: index of the edge's target node in some parent list
+ :type target_index: int
+ :param data: any extra data that needs to be saved
+ :type data: (variadic dictionary)
+ """
+ self.source_index = source_index
+ self.target_index = target_index
+ self.data = data
+
+
+class SimpleGraph( object ):
+ """
+ Each node is unique (by id) and stores it's own index in the node list/odict.
+ Each edge is represented as two indeces into the node list/odict.
+ Both nodes and edges allow storing extra information if needed.
+
+ Allows:
+ multiple edges between two nodes
+ self referential edges (an edge from a node to itself)
+
+ These graphs are not specifically directed but since source and targets on the
+ edges are listed - it could easily be used that way.
+ """
+ def __init__( self, nodes=None, edges=None ):
+ # use an odict so that edge indeces actually match the final node list indeces
+ self.nodes = nodes or odict()
+ self.edges = edges or []
+
+ def add_node( self, node_id, **data ):
+ """
+ Adds a new node only if it doesn't already exist.
+ :param node_id: some unique identifier
+ :type node_id: (hashable)
+ :param data: any extra data that needs to be saved
+ :type data: (variadic dictionary)
+ :returns: the new node
+ """
+ if node_id in self.nodes:
+ return self.nodes[ node_id ]
+ node_index = len( self.nodes )
+ new_node = SimpleGraphNode( node_index, **data )
+ self.nodes[ node_id ] = new_node
+ return new_node
+
+ def add_edge( self, source_id, target_id, **data ):
+ """
+ Adds a new node only if it doesn't already exist.
+ :param source_id: the id of the source node
+ :type source_id: (hashable)
+ :param target_id: the id of the target node
+ :type target_id: (hashable)
+ :param data: any extra data that needs to be saved for the edge
+ :type data: (variadic dictionary)
+ :returns: the new node
+
+ ..note: that, although this will create new nodes if necessary, there's
+ no way to pass `data` to them - so if you need to assoc. more data with
+ the nodes, use `add_node` first.
+ """
+ # adds target_id to source_id's edge list
+ # adding source_id and/or target_id to nodes if not there already
+ if source_id not in self.nodes:
+ self.add_node( source_id )
+ if target_id not in self.nodes:
+ self.add_node( target_id )
+ new_edge = SimpleGraphEdge( self.nodes[ source_id ].index, self.nodes[ target_id ].index, **data )
+ self.edges.append( new_edge )
+ return new_edge
+
+ def gen_node_dicts( self ):
+ """
+ Returns a generator that yields node dictionaries in the form:
+ { 'id': <the nodes unique id>, 'data': <any additional node data> }
+ """
+ for node_id, node in self.nodes.items():
+ yield { 'id': node_id, 'data': node.data }
+
+ def gen_edge_dicts( self ):
+ """
+ Returns a generator that yields node dictionaries in the form:
+ {
+ 'source': <the index of the source node in the graph's node list>,
+ 'target': <the index of the target node in the graph's node list>,
+ 'data' : <any additional edge data>
+ }
+ """
+ for edge in self.edges:
+ yield { 'source': edge.source_index, 'target': edge.target_index, 'data': edge.data }
+
+ def as_dict( self ):
+ """
+ Returns a dictionary of the form
+ { 'nodes': <a list of node dictionaries>, 'edges': <a list of node dictionaries> }
+ """
+ return { 'nodes': list( self.gen_node_dicts() ), 'edges': list( self.gen_edge_dicts() ) }
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3f8f52fa01f8/
Changeset: 3f8f52fa01f8
User: guerler
Date: 2013-08-08 10:10:36
Summary: Fix precision formatting in Circster
Affected #: 1 file
diff -r ba8330a9fdefe0431ee1869e3a60c55caebc937c -r 3f8f52fa01f807b624ce05ef2186284b947c2e34 static/scripts/viz/circster.js
--- a/static/scripts/viz/circster.js
+++ b/static/scripts/viz/circster.js
@@ -85,10 +85,14 @@
*/
formatNum: function(num, sigDigits) {
// Use default of 2 sig. digits.
- if (sigDigits === undefined) {
+ if (sigDigits === undefined)
sigDigits = 2;
- }
-
+
+ // Verify input number
+ if (num === null)
+ return null;
+
+ // Calculate return value
var rval = null;
if (num < 1) {
rval = num.toPrecision(sigDigits);
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.