[2] | 1 | """ |
---|
| 2 | Support for generating the options for a SelectToolParameter dynamically (based |
---|
| 3 | on the values of other parameters or other aspects of the current state) |
---|
| 4 | """ |
---|
| 5 | |
---|
| 6 | import operator, sys, os, logging |
---|
| 7 | import basic, validation |
---|
| 8 | from galaxy.util import string_as_bool |
---|
| 9 | |
---|
| 10 | log = logging.getLogger(__name__) |
---|
| 11 | |
---|
| 12 | class Filter( object ): |
---|
| 13 | """ |
---|
| 14 | A filter takes the current options list and modifies it. |
---|
| 15 | """ |
---|
| 16 | @classmethod |
---|
| 17 | def from_element( cls, d_option, elem ): |
---|
| 18 | """Loads the proper filter by the type attribute of elem""" |
---|
| 19 | type = elem.get( 'type', None ) |
---|
| 20 | assert type is not None, "Required 'type' attribute missing from filter" |
---|
| 21 | return filter_types[type.strip()]( d_option, elem ) |
---|
| 22 | def __init__( self, d_option, elem ): |
---|
| 23 | self.dynamic_option = d_option |
---|
| 24 | self.elem = elem |
---|
| 25 | def get_dependency_name( self ): |
---|
| 26 | """Returns the name of any depedencies, otherwise None""" |
---|
| 27 | return None |
---|
| 28 | def filter_options( self, options, trans, other_values ): |
---|
| 29 | """Returns a list of options after the filter is applied""" |
---|
| 30 | raise TypeError( "Abstract Method" ) |
---|
| 31 | |
---|
| 32 | class StaticValueFilter( Filter ): |
---|
| 33 | """ |
---|
| 34 | Filters a list of options on a column by a static value. |
---|
| 35 | |
---|
| 36 | Type: static_value |
---|
| 37 | |
---|
| 38 | Required Attributes: |
---|
| 39 | value: static value to compare to |
---|
| 40 | column: column in options to compare with |
---|
| 41 | Optional Attributes: |
---|
| 42 | keep: Keep columns matching value (True) |
---|
| 43 | Discard columns matching value (False) |
---|
| 44 | """ |
---|
| 45 | def __init__( self, d_option, elem ): |
---|
| 46 | Filter.__init__( self, d_option, elem ) |
---|
| 47 | self.value = elem.get( "value", None ) |
---|
| 48 | assert self.value is not None, "Required 'value' attribute missing from filter" |
---|
| 49 | column = elem.get( "column", None ) |
---|
| 50 | assert column is not None, "Required 'column' attribute missing from filter, when loading from file" |
---|
| 51 | self.column = d_option.column_spec_to_index( column ) |
---|
| 52 | self.keep = string_as_bool( elem.get( "keep", 'True' ) ) |
---|
| 53 | def filter_options( self, options, trans, other_values ): |
---|
| 54 | rval = [] |
---|
| 55 | for fields in options: |
---|
| 56 | if ( self.keep and fields[self.column] == self.value ) or ( not self.keep and fields[self.column] != self.value ): |
---|
| 57 | rval.append( fields ) |
---|
| 58 | return rval |
---|
| 59 | |
---|
| 60 | class DataMetaFilter( Filter ): |
---|
| 61 | """ |
---|
| 62 | Filters a list of options on a column by a dataset metadata value. |
---|
| 63 | |
---|
| 64 | Type: data_meta |
---|
| 65 | |
---|
| 66 | When no 'from_' source has been specified in the <options> tag, this will populate the options list with (meta_value, meta_value, False). |
---|
| 67 | Otherwise, options which do not match the metadata value in the column are discarded. |
---|
| 68 | |
---|
| 69 | Required Attributes: |
---|
| 70 | ref: Name of input dataset |
---|
| 71 | key: Metadata key to use for comparison |
---|
| 72 | column: column in options to compare with (not required when not associated with input options) |
---|
| 73 | Optional Attributes: |
---|
| 74 | multiple: Option values are multiple, split column by separator (True) |
---|
| 75 | separator: When multiple split by this (,) |
---|
| 76 | """ |
---|
| 77 | def __init__( self, d_option, elem ): |
---|
| 78 | Filter.__init__( self, d_option, elem ) |
---|
| 79 | self.ref_name = elem.get( "ref", None ) |
---|
| 80 | assert self.ref_name is not None, "Required 'ref' attribute missing from filter" |
---|
| 81 | d_option.has_dataset_dependencies = True |
---|
| 82 | self.key = elem.get( "key", None ) |
---|
| 83 | assert self.key is not None, "Required 'key' attribute missing from filter" |
---|
| 84 | self.column = elem.get( "column", None ) |
---|
| 85 | if self.column is None: |
---|
| 86 | assert self.dynamic_option.file_fields is None and self.dynamic_option.dataset_ref_name is None, "Required 'column' attribute missing from filter, when loading from file" |
---|
| 87 | else: |
---|
| 88 | self.column = d_option.column_spec_to_index( self.column ) |
---|
| 89 | self.multiple = string_as_bool( elem.get( "multiple", "False" ) ) |
---|
| 90 | self.separator = elem.get( "separator", "," ) |
---|
| 91 | def get_dependency_name( self ): |
---|
| 92 | return self.ref_name |
---|
| 93 | def filter_options( self, options, trans, other_values ): |
---|
| 94 | def compare_meta_value( file_value, dataset_value ): |
---|
| 95 | if isinstance( dataset_value, list ): |
---|
| 96 | if self.multiple: |
---|
| 97 | file_value = file_value.split( self.separator ) |
---|
| 98 | for value in dataset_value: |
---|
| 99 | if value not in file_value: |
---|
| 100 | return False |
---|
| 101 | return True |
---|
| 102 | return file_value in dataset_value |
---|
| 103 | if self.multiple: |
---|
| 104 | return dataset_value in file_value.split( self.separator ) |
---|
| 105 | return file_value == dataset_value |
---|
| 106 | assert self.ref_name in other_values or ( trans is not None and trans.workflow_building_mode), "Required dependency '%s' not found in incoming values" % self.ref_name |
---|
| 107 | ref = other_values.get( self.ref_name, None ) |
---|
| 108 | if not isinstance( ref, self.dynamic_option.tool_param.tool.app.model.HistoryDatasetAssociation ): |
---|
| 109 | return [] #not a valid dataset |
---|
| 110 | meta_value = ref.metadata.get( self.key, None ) |
---|
| 111 | if meta_value is None: #assert meta_value is not None, "Required metadata value '%s' not found in referenced dataset" % self.key |
---|
| 112 | return [ ( disp_name, basic.UnvalidatedValue( optval ), selected ) for disp_name, optval, selected in options ] |
---|
| 113 | |
---|
| 114 | if self.column is not None: |
---|
| 115 | rval = [] |
---|
| 116 | for fields in options: |
---|
| 117 | if compare_meta_value( fields[self.column], meta_value ): |
---|
| 118 | rval.append( fields ) |
---|
| 119 | return rval |
---|
| 120 | else: |
---|
| 121 | if not isinstance( meta_value, list ): |
---|
| 122 | meta_value = [meta_value] |
---|
| 123 | for value in meta_value: |
---|
| 124 | options.append( ( value, value, False ) ) |
---|
| 125 | return options |
---|
| 126 | |
---|
| 127 | class ParamValueFilter( Filter ): |
---|
| 128 | """ |
---|
| 129 | Filters a list of options on a column by the value of another input. |
---|
| 130 | |
---|
| 131 | Type: param_value |
---|
| 132 | |
---|
| 133 | Required Attributes: |
---|
| 134 | ref: Name of input value |
---|
| 135 | column: column in options to compare with |
---|
| 136 | Optional Attributes: |
---|
| 137 | keep: Keep columns matching value (True) |
---|
| 138 | Discard columns matching value (False) |
---|
| 139 | ref_attribute: Period (.) separated attribute chain of input (ref) to use as value for filter |
---|
| 140 | """ |
---|
| 141 | def __init__( self, d_option, elem ): |
---|
| 142 | Filter.__init__( self, d_option, elem ) |
---|
| 143 | self.ref_name = elem.get( "ref", None ) |
---|
| 144 | assert self.ref_name is not None, "Required 'ref' attribute missing from filter" |
---|
| 145 | column = elem.get( "column", None ) |
---|
| 146 | assert column is not None, "Required 'column' attribute missing from filter" |
---|
| 147 | self.column = d_option.column_spec_to_index( column ) |
---|
| 148 | self.keep = string_as_bool( elem.get( "keep", 'True' ) ) |
---|
| 149 | self.ref_attribute = elem.get( "ref_attribute", None ) |
---|
| 150 | if self.ref_attribute: |
---|
| 151 | self.ref_attribute = self.ref_attribute.split( '.' ) |
---|
| 152 | else: |
---|
| 153 | self.ref_attribute = [] |
---|
| 154 | def get_dependency_name( self ): |
---|
| 155 | return self.ref_name |
---|
| 156 | def filter_options( self, options, trans, other_values ): |
---|
| 157 | if trans is not None and trans.workflow_building_mode: return [] |
---|
| 158 | assert self.ref_name in other_values, "Required dependency '%s' not found in incoming values" % self.ref_name |
---|
| 159 | ref = other_values.get( self.ref_name, None ) |
---|
| 160 | for ref_attribute in self.ref_attribute: |
---|
| 161 | if not hasattr( ref, ref_attribute ): |
---|
| 162 | return [] #ref does not have attribute, so we cannot filter, return empty list |
---|
| 163 | ref = getattr( ref, ref_attribute ) |
---|
| 164 | ref = str( ref ) |
---|
| 165 | rval = [] |
---|
| 166 | for fields in options: |
---|
| 167 | if ( self.keep and fields[self.column] == ref ) or ( not self.keep and fields[self.column] != ref ): |
---|
| 168 | rval.append( fields ) |
---|
| 169 | return rval |
---|
| 170 | |
---|
| 171 | class UniqueValueFilter( Filter ): |
---|
| 172 | """ |
---|
| 173 | Filters a list of options to be unique by a column value. |
---|
| 174 | |
---|
| 175 | Type: unique_value |
---|
| 176 | |
---|
| 177 | Required Attributes: |
---|
| 178 | column: column in options to compare with |
---|
| 179 | """ |
---|
| 180 | def __init__( self, d_option, elem ): |
---|
| 181 | Filter.__init__( self, d_option, elem ) |
---|
| 182 | column = elem.get( "column", None ) |
---|
| 183 | assert column is not None, "Required 'column' attribute missing from filter" |
---|
| 184 | self.column = d_option.column_spec_to_index( column ) |
---|
| 185 | def get_dependency_name( self ): |
---|
| 186 | return self.dynamic_option.dataset_ref_name |
---|
| 187 | def filter_options( self, options, trans, other_values ): |
---|
| 188 | rval = [] |
---|
| 189 | skip_list = [] |
---|
| 190 | for fields in options: |
---|
| 191 | if fields[self.column] not in skip_list: |
---|
| 192 | rval.append( fields ) |
---|
| 193 | skip_list.append( fields[self.column] ) |
---|
| 194 | return rval |
---|
| 195 | |
---|
| 196 | class MultipleSplitterFilter( Filter ): |
---|
| 197 | """ |
---|
| 198 | Turns a single line of options into multiple lines, by splitting a column and creating a line for each item. |
---|
| 199 | |
---|
| 200 | Type: multiple_splitter |
---|
| 201 | |
---|
| 202 | Required Attributes: |
---|
| 203 | column: column in options to compare with |
---|
| 204 | Optional Attributes: |
---|
| 205 | separator: Split column by this (,) |
---|
| 206 | """ |
---|
| 207 | def __init__( self, d_option, elem ): |
---|
| 208 | Filter.__init__( self, d_option, elem ) |
---|
| 209 | self.separator = elem.get( "separator", "," ) |
---|
| 210 | columns = elem.get( "column", None ) |
---|
| 211 | assert columns is not None, "Required 'columns' attribute missing from filter" |
---|
| 212 | self.columns = [ d_option.column_spec_to_index( column ) for column in columns.split( "," ) ] |
---|
| 213 | def filter_options( self, options, trans, other_values ): |
---|
| 214 | rval = [] |
---|
| 215 | for fields in options: |
---|
| 216 | for column in self.columns: |
---|
| 217 | for field in fields[column].split( self.separator ): |
---|
| 218 | rval.append( fields[0:column] + [field] + fields[column+1:] ) |
---|
| 219 | return rval |
---|
| 220 | |
---|
| 221 | class AttributeValueSplitterFilter( Filter ): |
---|
| 222 | """ |
---|
| 223 | Filters a list of attribute-value pairs to be unique attribute names. |
---|
| 224 | |
---|
| 225 | Type: attribute_value_splitter |
---|
| 226 | |
---|
| 227 | Required Attributes: |
---|
| 228 | column: column in options to compare with |
---|
| 229 | Optional Attributes: |
---|
| 230 | pair_separator: Split column by this (,) |
---|
| 231 | name_val_separator: Split name-value pair by this ( whitespace ) |
---|
| 232 | """ |
---|
| 233 | def __init__( self, d_option, elem ): |
---|
| 234 | Filter.__init__( self, d_option, elem ) |
---|
| 235 | self.pair_separator = elem.get( "pair_separator", "," ) |
---|
| 236 | self.name_val_separator = elem.get( "name_val_separator", None ) |
---|
| 237 | self.columns = elem.get( "column", None ) |
---|
| 238 | assert self.columns is not None, "Required 'columns' attribute missing from filter" |
---|
| 239 | self.columns = [ int ( column ) for column in self.columns.split( "," ) ] |
---|
| 240 | def filter_options( self, options, trans, other_values ): |
---|
| 241 | attr_names = [] |
---|
| 242 | rval = [] |
---|
| 243 | for fields in options: |
---|
| 244 | for column in self.columns: |
---|
| 245 | for pair in fields[column].split( self.pair_separator ): |
---|
| 246 | ary = pair.split( self.name_val_separator ) |
---|
| 247 | if len( ary ) == 2: |
---|
| 248 | name, value = ary |
---|
| 249 | if name not in attr_names: |
---|
| 250 | rval.append( fields[0:column] + [name] + fields[column:] ) |
---|
| 251 | attr_names.append( name ) |
---|
| 252 | return rval |
---|
| 253 | |
---|
| 254 | |
---|
| 255 | class AdditionalValueFilter( Filter ): |
---|
| 256 | """ |
---|
| 257 | Adds a single static value to an options list. |
---|
| 258 | |
---|
| 259 | Type: add_value |
---|
| 260 | |
---|
| 261 | Required Attributes: |
---|
| 262 | value: value to appear in select list |
---|
| 263 | Optional Attributes: |
---|
| 264 | name: Display name to appear in select list (value) |
---|
| 265 | index: Index of option list to add value (APPEND) |
---|
| 266 | """ |
---|
| 267 | def __init__( self, d_option, elem ): |
---|
| 268 | Filter.__init__( self, d_option, elem ) |
---|
| 269 | self.value = elem.get( "value", None ) |
---|
| 270 | assert self.value is not None, "Required 'value' attribute missing from filter" |
---|
| 271 | self.name = elem.get( "name", None ) |
---|
| 272 | if self.name is None: |
---|
| 273 | self.name = self.value |
---|
| 274 | self.index = elem.get( "index", None ) |
---|
| 275 | if self.index is not None: |
---|
| 276 | self.index = int( self.index ) |
---|
| 277 | def filter_options( self, options, trans, other_values ): |
---|
| 278 | rval = list( options ) |
---|
| 279 | add_value = [] |
---|
| 280 | for i in range( self.dynamic_option.largest_index + 1 ): |
---|
| 281 | add_value.append( "" ) |
---|
| 282 | add_value[self.dynamic_option.columns['value']] = self.value |
---|
| 283 | add_value[self.dynamic_option.columns['name']] = self.name |
---|
| 284 | if self.index is not None: |
---|
| 285 | rval.insert( self.index, add_value ) |
---|
| 286 | else: |
---|
| 287 | rval.append( add_value ) |
---|
| 288 | return rval |
---|
| 289 | |
---|
| 290 | class RemoveValueFilter( Filter ): |
---|
| 291 | """ |
---|
| 292 | Removes a value from an options list. |
---|
| 293 | |
---|
| 294 | Type: remove_value |
---|
| 295 | |
---|
| 296 | Required Attributes: |
---|
| 297 | value: value to remove from select list |
---|
| 298 | or |
---|
| 299 | ref: param to refer to |
---|
| 300 | or |
---|
| 301 | meta_ref: dataset to refer to |
---|
| 302 | key: metadata key to compare to |
---|
| 303 | """ |
---|
| 304 | def __init__( self, d_option, elem ): |
---|
| 305 | Filter.__init__( self, d_option, elem ) |
---|
| 306 | self.value = elem.get( "value", None ) |
---|
| 307 | self.ref_name = elem.get( "ref", None ) |
---|
| 308 | self.meta_ref = elem.get( "meta_ref", None ) |
---|
| 309 | self.metadata_key = elem.get( "key", None ) |
---|
| 310 | assert self.value is not None or ( ( self.ref_name is not None or self.meta_ref is not None )and self.metadata_key is not None ), ValueError( "Required 'value' or 'ref' and 'key' attributes missing from filter" ) |
---|
| 311 | self.multiple = string_as_bool( elem.get( "multiple", "False" ) ) |
---|
| 312 | self.separator = elem.get( "separator", "," ) |
---|
| 313 | def filter_options( self, options, trans, other_values ): |
---|
| 314 | if trans is not None and trans.workflow_building_mode: return options |
---|
| 315 | assert self.value is not None or ( self.ref_name is not None and self.ref_name in other_values ) or (self.meta_ref is not None and self.meta_ref in other_values ) or ( trans is not None and trans.workflow_building_mode), Exception( "Required dependency '%s' or '%s' not found in incoming values" % ( self.ref_name, self.meta_ref ) ) |
---|
| 316 | def compare_value( option_value, filter_value ): |
---|
| 317 | if isinstance( filter_value, list ): |
---|
| 318 | if self.multiple: |
---|
| 319 | option_value = option_value.split( self.separator ) |
---|
| 320 | for value in filter_value: |
---|
| 321 | if value not in filter_value: |
---|
| 322 | return False |
---|
| 323 | return True |
---|
| 324 | return option_value in filter_value |
---|
| 325 | if self.multiple: |
---|
| 326 | return filter_value in option_value.split( self.separator ) |
---|
| 327 | return option_value == filter_value |
---|
| 328 | value = self.value |
---|
| 329 | if value is None: |
---|
| 330 | if self.ref_name is not None: |
---|
| 331 | value = other_values.get( self.ref_name ) |
---|
| 332 | else: |
---|
| 333 | data_ref = other_values.get( self.meta_ref ) |
---|
| 334 | if not isinstance( data_ref, self.dynamic_option.tool_param.tool.app.model.HistoryDatasetAssociation ): |
---|
| 335 | return options #cannot modify options |
---|
| 336 | value = data_ref.metadata.get( self.metadata_key, None ) |
---|
| 337 | return [ ( disp_name, optval, selected ) for disp_name, optval, selected in options if not compare_value( optval, value ) ] |
---|
| 338 | |
---|
| 339 | class SortByColumnFilter( Filter ): |
---|
| 340 | """ |
---|
| 341 | Sorts an options list by a column |
---|
| 342 | |
---|
| 343 | Type: sort_by |
---|
| 344 | |
---|
| 345 | Required Attributes: |
---|
| 346 | column: column to sort by |
---|
| 347 | """ |
---|
| 348 | def __init__( self, d_option, elem ): |
---|
| 349 | Filter.__init__( self, d_option, elem ) |
---|
| 350 | column = elem.get( "column", None ) |
---|
| 351 | assert column is not None, "Required 'column' attribute missing from filter" |
---|
| 352 | self.column = d_option.column_spec_to_index( column ) |
---|
| 353 | def filter_options( self, options, trans, other_values ): |
---|
| 354 | rval = [] |
---|
| 355 | for i, fields in enumerate( options ): |
---|
| 356 | for j in range( 0, len( rval ) ): |
---|
| 357 | if fields[self.column] < rval[j][self.column]: |
---|
| 358 | rval.insert( j, fields ) |
---|
| 359 | break |
---|
| 360 | else: |
---|
| 361 | rval.append( fields ) |
---|
| 362 | return rval |
---|
| 363 | |
---|
| 364 | |
---|
| 365 | filter_types = dict( data_meta = DataMetaFilter, |
---|
| 366 | param_value = ParamValueFilter, |
---|
| 367 | static_value = StaticValueFilter, |
---|
| 368 | unique_value = UniqueValueFilter, |
---|
| 369 | multiple_splitter = MultipleSplitterFilter, |
---|
| 370 | attribute_value_splitter = AttributeValueSplitterFilter, |
---|
| 371 | add_value = AdditionalValueFilter, |
---|
| 372 | remove_value = RemoveValueFilter, |
---|
| 373 | sort_by = SortByColumnFilter ) |
---|
| 374 | |
---|
| 375 | class DynamicOptions( object ): |
---|
| 376 | """Handles dynamically generated SelectToolParameter options""" |
---|
| 377 | def __init__( self, elem, tool_param ): |
---|
| 378 | def load_from_parameter( from_parameter, transform_lines = None ): |
---|
| 379 | obj = self.tool_param |
---|
| 380 | for field in from_parameter.split( '.' ): |
---|
| 381 | obj = getattr( obj, field ) |
---|
| 382 | if transform_lines: |
---|
| 383 | obj = eval( transform_lines ) |
---|
| 384 | return self.parse_file_fields( obj ) |
---|
| 385 | self.tool_param = tool_param |
---|
| 386 | self.columns = {} |
---|
| 387 | self.filters = [] |
---|
| 388 | self.file_fields = None |
---|
| 389 | self.largest_index = 0 |
---|
| 390 | self.dataset_ref_name = None |
---|
| 391 | # True if the options generation depends on one or more other parameters |
---|
| 392 | # that are dataset inputs |
---|
| 393 | self.has_dataset_dependencies = False |
---|
| 394 | self.validators = [] |
---|
| 395 | self.converter_safe = True |
---|
| 396 | |
---|
| 397 | # Parse the <options> tag |
---|
| 398 | self.separator = elem.get( 'separator', '\t' ) |
---|
| 399 | self.line_startswith = elem.get( 'startswith', None ) |
---|
| 400 | data_file = elem.get( 'from_file', None ) |
---|
| 401 | dataset_file = elem.get( 'from_dataset', None ) |
---|
| 402 | from_parameter = elem.get( 'from_parameter', None ) |
---|
| 403 | tool_data_table_name = elem.get( 'from_data_table', None ) |
---|
| 404 | |
---|
| 405 | # Options are defined from a data table loaded by the app |
---|
| 406 | self.tool_data_table = None |
---|
| 407 | if tool_data_table_name: |
---|
| 408 | app = tool_param.tool.app |
---|
| 409 | assert tool_data_table_name in app.tool_data_tables, \ |
---|
| 410 | "Data table named '%s' is required by tool but not configured" % tool_data_table_name |
---|
| 411 | self.tool_data_table = app.tool_data_tables[ tool_data_table_name ] |
---|
| 412 | # Column definitions are optional, but if provided override those from the table |
---|
| 413 | if elem.find( "column" ) is not None: |
---|
| 414 | self.parse_column_definitions( elem ) |
---|
| 415 | else: |
---|
| 416 | self.columns = self.tool_data_table.columns |
---|
| 417 | |
---|
| 418 | # Options are defined by parsing tabular text data from an data file |
---|
| 419 | # on disk, a dataset, or the value of another parameter |
---|
| 420 | elif data_file is not None or dataset_file is not None or from_parameter is not None: |
---|
| 421 | self.parse_column_definitions( elem ) |
---|
| 422 | if data_file is not None: |
---|
| 423 | data_file = data_file.strip() |
---|
| 424 | if not os.path.isabs( data_file ): |
---|
| 425 | data_file = os.path.join( self.tool_param.tool.app.config.tool_data_path, data_file ) |
---|
| 426 | self.file_fields = self.parse_file_fields( open( data_file ) ) |
---|
| 427 | elif dataset_file is not None: |
---|
| 428 | self.dataset_ref_name = dataset_file |
---|
| 429 | self.has_dataset_dependencies = True |
---|
| 430 | self.converter_safe = False |
---|
| 431 | elif from_parameter is not None: |
---|
| 432 | transform_lines = elem.get( 'transform_lines', None ) |
---|
| 433 | self.file_fields = list( load_from_parameter( from_parameter, transform_lines ) ) |
---|
| 434 | |
---|
| 435 | # Load filters |
---|
| 436 | for filter_elem in elem.findall( 'filter' ): |
---|
| 437 | self.filters.append( Filter.from_element( self, filter_elem ) ) |
---|
| 438 | |
---|
| 439 | # Load Validators |
---|
| 440 | for validator in elem.findall( 'validator' ): |
---|
| 441 | self.validators.append( validation.Validator.from_element( self.tool_param, validator ) ) |
---|
| 442 | |
---|
| 443 | def parse_column_definitions( self, elem ): |
---|
| 444 | for column_elem in elem.findall( 'column' ): |
---|
| 445 | name = column_elem.get( 'name', None ) |
---|
| 446 | assert name is not None, "Required 'name' attribute missing from column def" |
---|
| 447 | index = column_elem.get( 'index', None ) |
---|
| 448 | assert index is not None, "Required 'index' attribute missing from column def" |
---|
| 449 | index = int( index ) |
---|
| 450 | self.columns[name] = index |
---|
| 451 | if index > self.largest_index: |
---|
| 452 | self.largest_index = index |
---|
| 453 | assert 'value' in self.columns, "Required 'value' column missing from column def" |
---|
| 454 | if 'name' not in self.columns: |
---|
| 455 | self.columns['name'] = self.columns['value'] |
---|
| 456 | |
---|
| 457 | def parse_file_fields( self, reader ): |
---|
| 458 | rval = [] |
---|
| 459 | for line in reader: |
---|
| 460 | if line.startswith( '#' ) or ( self.line_startswith and not line.startswith( self.line_startswith ) ): |
---|
| 461 | continue |
---|
| 462 | line = line.rstrip( "\n\r" ) |
---|
| 463 | if line: |
---|
| 464 | fields = line.split( self.separator ) |
---|
| 465 | if self.largest_index < len( fields ): |
---|
| 466 | rval.append( fields ) |
---|
| 467 | return rval |
---|
| 468 | |
---|
| 469 | def get_dependency_names( self ): |
---|
| 470 | """ |
---|
| 471 | Return the names of parameters these options depend on -- both data |
---|
| 472 | and other param types. |
---|
| 473 | """ |
---|
| 474 | rval = [] |
---|
| 475 | if self.dataset_ref_name: |
---|
| 476 | rval.append( self.dataset_ref_name ) |
---|
| 477 | for filter in self.filters: |
---|
| 478 | depend = filter.get_dependency_name() |
---|
| 479 | if depend: |
---|
| 480 | rval.append( depend ) |
---|
| 481 | return rval |
---|
| 482 | |
---|
| 483 | def get_fields( self, trans, other_values ): |
---|
| 484 | if self.dataset_ref_name: |
---|
| 485 | dataset = other_values.get( self.dataset_ref_name, None ) |
---|
| 486 | assert dataset is not None, "Required dataset '%s' missing from input" % self.dataset_ref_name |
---|
| 487 | if not dataset: return [] #no valid dataset in history |
---|
| 488 | options = self.parse_file_fields( open( dataset.file_name ) ) |
---|
| 489 | elif self.tool_data_table: |
---|
| 490 | options = self.tool_data_table.get_fields() |
---|
| 491 | else: |
---|
| 492 | options = list( self.file_fields ) |
---|
| 493 | for filter in self.filters: |
---|
| 494 | options = filter.filter_options( options, trans, other_values ) |
---|
| 495 | return options |
---|
| 496 | |
---|
| 497 | def get_options( self, trans, other_values ): |
---|
| 498 | rval = [] |
---|
| 499 | if self.file_fields is not None or self.tool_data_table is not None or self.dataset_ref_name is not None: |
---|
| 500 | options = self.get_fields( trans, other_values ) |
---|
| 501 | for fields in options: |
---|
| 502 | rval.append( ( fields[self.columns['name']], fields[self.columns['value']], False ) ) |
---|
| 503 | else: |
---|
| 504 | for filter in self.filters: |
---|
| 505 | rval = filter.filter_options( rval, trans, other_values ) |
---|
| 506 | return rval |
---|
| 507 | |
---|
| 508 | def column_spec_to_index( self, column_spec ): |
---|
| 509 | """ |
---|
| 510 | Convert a column specification (as read from the config file), to an |
---|
| 511 | index. A column specification can just be a number, a column name, or |
---|
| 512 | a column alias. |
---|
| 513 | """ |
---|
| 514 | # Name? |
---|
| 515 | if column_spec in self.columns: |
---|
| 516 | return self.columns[column_spec] |
---|
| 517 | # Int? |
---|
| 518 | return int( column_spec ) |
---|