Mercurial > repos > iuc > jbrowse
comparison jbrowse.py @ 17:ff11d442feed draft
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/jbrowse commit 908f16ea4eb082227437dc93e06e8cb742f5a257
| author | iuc |
|---|---|
| date | Wed, 15 Nov 2017 15:15:27 -0500 |
| parents | b5c5470d7c09 |
| children | 558d652cd681 |
comparison
equal
deleted
inserted
replaced
| 16:b5c5470d7c09 | 17:ff11d442feed |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 import argparse | 2 import argparse |
| 3 import codecs | 3 import binascii |
| 4 import copy | 4 import copy |
| 5 import datetime | |
| 5 import hashlib | 6 import hashlib |
| 6 import json | 7 import json |
| 7 import logging | 8 import logging |
| 8 import os | 9 import os |
| 9 import shutil | 10 import shutil |
| 12 import tempfile | 13 import tempfile |
| 13 import xml.etree.ElementTree as ET | 14 import xml.etree.ElementTree as ET |
| 14 from collections import defaultdict | 15 from collections import defaultdict |
| 15 | 16 |
| 16 from Bio.Data import CodonTable | 17 from Bio.Data import CodonTable |
| 17 | |
| 18 logging.basicConfig(level=logging.INFO) | 18 logging.basicConfig(level=logging.INFO) |
| 19 log = logging.getLogger('jbrowse') | 19 log = logging.getLogger('jbrowse') |
| 20 TODAY = datetime.datetime.now().strftime("%Y-%m-%d") | |
| 21 GALAXY_INFRASTRUCTURE_URL = None | |
| 20 | 22 |
| 21 | 23 |
| 22 class ColorScaling(object): | 24 class ColorScaling(object): |
| 23 | 25 |
| 24 COLOR_FUNCTION_TEMPLATE = """ | 26 COLOR_FUNCTION_TEMPLATE = """ |
| 61 }}; | 63 }}; |
| 62 | 64 |
| 63 var color = ({user_spec_color} || search_up(feature, 'color') || search_down(feature, 'color') || {auto_gen_color}); | 65 var color = ({user_spec_color} || search_up(feature, 'color') || search_down(feature, 'color') || {auto_gen_color}); |
| 64 var score = (search_up(feature, 'score') || search_down(feature, 'score')); | 66 var score = (search_up(feature, 'score') || search_down(feature, 'score')); |
| 65 {opacity} | 67 {opacity} |
| 68 if(score === undefined){{ opacity = 1; }} | |
| 66 var result = /^#?([a-f\d]{{2}})([a-f\d]{{2}})([a-f\d]{{2}})$/i.exec(color); | 69 var result = /^#?([a-f\d]{{2}})([a-f\d]{{2}})([a-f\d]{{2}})$/i.exec(color); |
| 67 var red = parseInt(result[1], 16); | 70 var red = parseInt(result[1], 16); |
| 68 var green = parseInt(result[2], 16); | 71 var green = parseInt(result[2], 16); |
| 69 var blue = parseInt(result[3], 16); | 72 var blue = parseInt(result[3], 16); |
| 70 if(isNaN(opacity) || opacity < 0){{ opacity = 0; }} | 73 if(isNaN(opacity) || opacity < 0){{ opacity = 0; }} |
| 80 var opacity = (score - ({min})) / (({max}) - ({min})); | 83 var opacity = (score - ({min})) / (({max}) - ({min})); |
| 81 opacity = Math.log10(opacity) + Math.log10({max}); | 84 opacity = Math.log10(opacity) + Math.log10({max}); |
| 82 """, | 85 """, |
| 83 'blast': """ | 86 'blast': """ |
| 84 var opacity = 0; | 87 var opacity = 0; |
| 85 if(score == 0.0) { | 88 if(score == 0.0) {{ |
| 86 opacity = 1; | 89 opacity = 1; |
| 87 } else{ | 90 }} else {{ |
| 88 opacity = (20 - Math.log10(score)) / 180; | 91 opacity = (20 - Math.log10(score)) / 180; |
| 89 } | 92 }} |
| 90 """ | 93 """ |
| 91 } | 94 } |
| 92 | 95 |
| 93 BREWER_COLOUR_IDX = 0 | 96 BREWER_COLOUR_IDX = 0 |
| 94 BREWER_COLOUR_SCHEMES = [ | 97 BREWER_COLOUR_SCHEMES = [ |
| 126 def __init__(self): | 129 def __init__(self): |
| 127 self.brewer_colour_idx = 0 | 130 self.brewer_colour_idx = 0 |
| 128 | 131 |
| 129 def rgb_from_hex(self, hexstr): | 132 def rgb_from_hex(self, hexstr): |
| 130 # http://stackoverflow.com/questions/4296249/how-do-i-convert-a-hex-triplet-to-an-rgb-tuple-and-back | 133 # http://stackoverflow.com/questions/4296249/how-do-i-convert-a-hex-triplet-to-an-rgb-tuple-and-back |
| 131 return struct.unpack('BBB', codecs.decode(hexstr, 'hex')) | 134 return struct.unpack('BBB', binascii.unhexlify(hexstr)) |
| 132 | 135 |
| 133 def min_max_gff(self, gff_file): | 136 def min_max_gff(self, gff_file): |
| 134 min_val = None | 137 min_val = None |
| 135 max_val = None | 138 max_val = None |
| 136 with open(gff_file, 'r') as handle: | 139 with open(gff_file, 'r') as handle: |
| 283 # score comes from feature._parent.get('score') or feature.get('score') | 286 # score comes from feature._parent.get('score') or feature.get('score') |
| 284 | 287 |
| 285 INSTALLED_TO = os.path.dirname(os.path.realpath(__file__)) | 288 INSTALLED_TO = os.path.dirname(os.path.realpath(__file__)) |
| 286 | 289 |
| 287 | 290 |
| 291 def metadata_from_node(node): | |
| 292 metadata = {} | |
| 293 try: | |
| 294 if len(node.findall('dataset')) != 1: | |
| 295 # exit early | |
| 296 return metadata | |
| 297 except Exception: | |
| 298 return {} | |
| 299 | |
| 300 for (key, value) in node.findall('dataset')[0].attrib.items(): | |
| 301 metadata['dataset_%s' % key] = value | |
| 302 | |
| 303 for (key, value) in node.findall('history')[0].attrib.items(): | |
| 304 metadata['history_%s' % key] = value | |
| 305 | |
| 306 for (key, value) in node.findall('metadata')[0].attrib.items(): | |
| 307 metadata['metadata_%s' % key] = value | |
| 308 | |
| 309 for (key, value) in node.findall('tool')[0].attrib.items(): | |
| 310 metadata['tool_%s' % key] = value | |
| 311 | |
| 312 # Additional Mappings applied: | |
| 313 metadata['dataset_edam_format'] = '<a target="_blank" href="http://edamontology.org/{0}">{1}</a>'.format(metadata['dataset_edam_format'], metadata['dataset_file_ext']) | |
| 314 metadata['history_user_email'] = '<a href="mailto:{0}">{0}</a>'.format(metadata['history_user_email']) | |
| 315 metadata['history_display_name'] = '<a target="_blank" href="{galaxy}/history/view/{encoded_hist_id}">{hist_name}</a>'.format( | |
| 316 galaxy=GALAXY_INFRASTRUCTURE_URL, | |
| 317 encoded_hist_id=metadata['history_id'], | |
| 318 hist_name=metadata['history_display_name'] | |
| 319 ) | |
| 320 metadata['tool_tool'] = '<a target="_blank" href="{galaxy}/datasets/{encoded_id}/show_params">{tool_id}</a>'.format( | |
| 321 galaxy=GALAXY_INFRASTRUCTURE_URL, | |
| 322 encoded_id=metadata['dataset_id'], | |
| 323 tool_id=metadata['tool_tool_id'], | |
| 324 tool_version=metadata['tool_tool_version'], | |
| 325 ) | |
| 326 return metadata | |
| 327 | |
| 328 | |
| 288 class JbrowseConnector(object): | 329 class JbrowseConnector(object): |
| 289 | 330 |
| 290 def __init__(self, jbrowse, outdir, genomes, standalone=False, gencode=1): | 331 def __init__(self, jbrowse, outdir, genomes, standalone=False, gencode=1): |
| 291 self.TN_TABLE = { | 332 self.TN_TABLE = { |
| 292 'gff3': '--gff', | 333 'gff3': '--gff', |
| 310 os.makedirs(self.outdir) | 351 os.makedirs(self.outdir) |
| 311 except OSError: | 352 except OSError: |
| 312 # Ignore if the folder exists | 353 # Ignore if the folder exists |
| 313 pass | 354 pass |
| 314 | 355 |
| 356 try: | |
| 357 os.makedirs(os.path.join(self.outdir, 'data', 'raw')) | |
| 358 except OSError: | |
| 359 # Ignore if the folder exists | |
| 360 pass | |
| 361 | |
| 315 self.process_genomes() | 362 self.process_genomes() |
| 316 self.update_gencode() | 363 self.update_gencode() |
| 317 | 364 |
| 318 def update_gencode(self): | 365 def update_gencode(self): |
| 319 table = CodonTable.unambiguous_dna_by_id[int(self.gencode)] | 366 table = CodonTable.unambiguous_dna_by_id[int(self.gencode)] |
| 336 | 383 |
| 337 def _jbrowse_bin(self, command): | 384 def _jbrowse_bin(self, command): |
| 338 return os.path.realpath(os.path.join(self.jbrowse, 'bin', command)) | 385 return os.path.realpath(os.path.join(self.jbrowse, 'bin', command)) |
| 339 | 386 |
| 340 def process_genomes(self): | 387 def process_genomes(self): |
| 341 for genome_path in self.genome_paths: | 388 for genome_node in self.genome_paths: |
| 389 # TODO: Waiting on https://github.com/GMOD/jbrowse/pull/884 | |
| 342 self.subprocess_check_call([ | 390 self.subprocess_check_call([ |
| 343 'perl', self._jbrowse_bin('prepare-refseqs.pl'), | 391 'perl', self._jbrowse_bin('prepare-refseqs.pl'), |
| 344 '--fasta', genome_path]) | 392 '--fasta', genome_node['path']]) |
| 345 | 393 |
| 346 def generate_names(self): | 394 def generate_names(self): |
| 347 # Generate names | 395 # Generate names |
| 348 | |
| 349 args = [ | 396 args = [ |
| 350 'perl', self._jbrowse_bin('generate-names.pl'), | 397 'perl', self._jbrowse_bin('generate-names.pl'), |
| 351 '--hashBits', '16' | 398 '--hashBits', '16' |
| 352 ] | 399 ] |
| 353 | 400 |
| 354 tracks = ','.join(self.tracksToIndex) | 401 tracks = ','.join(self.tracksToIndex) |
| 355 | |
| 356 if tracks: | 402 if tracks: |
| 357 args += ['--tracks', tracks] | 403 args += ['--tracks', tracks] |
| 358 else: | 404 else: |
| 359 # No tracks to index, index only the refseq | 405 # No tracks to index, index only the refseq |
| 360 args += ['--tracks', 'DNA'] | 406 args += ['--tracks', 'DNA'] |
| 361 | 407 |
| 362 self.subprocess_check_call(args) | 408 self.subprocess_check_call(args) |
| 363 | 409 |
| 364 def _add_json(self, json_data): | 410 def _add_json(self, json_data): |
| 365 | |
| 366 cmd = [ | 411 cmd = [ |
| 367 'perl', self._jbrowse_bin('add-json.pl'), | 412 'perl', self._jbrowse_bin('add-json.pl'), |
| 368 json.dumps(json_data), | 413 json.dumps(json_data), |
| 369 os.path.join('data', 'trackList.json') | 414 os.path.join('data', 'trackList.json') |
| 370 ] | 415 ] |
| 419 '--gff', gff3, | 464 '--gff', gff3, |
| 420 '--trackLabel', trackData['label'], | 465 '--trackLabel', trackData['label'], |
| 421 '--key', trackData['key'], | 466 '--key', trackData['key'], |
| 422 '--clientConfig', json.dumps(clientConfig), | 467 '--clientConfig', json.dumps(clientConfig), |
| 423 '--config', json.dumps(config), | 468 '--config', json.dumps(config), |
| 424 '--trackType', 'JBrowse/View/Track/CanvasFeatures' | 469 '--trackType', 'BlastView/View/Track/CanvasFeatures' |
| 425 ] | 470 ] |
| 426 | 471 |
| 427 # className in --clientConfig is ignored, it needs to be set with --className | 472 # className in --clientConfig is ignored, it needs to be set with --className |
| 428 if 'className' in trackData['style']: | 473 if 'className' in trackData['style']: |
| 429 cmd += ['--className', trackData['style']['className']] | 474 cmd += ['--className', trackData['style']['className']] |
| 452 if 'min' in wiggleOpts and 'max' in wiggleOpts: | 497 if 'min' in wiggleOpts and 'max' in wiggleOpts: |
| 453 trackData['min_score'] = wiggleOpts['min'] | 498 trackData['min_score'] = wiggleOpts['min'] |
| 454 trackData['max_score'] = wiggleOpts['max'] | 499 trackData['max_score'] = wiggleOpts['max'] |
| 455 else: | 500 else: |
| 456 trackData['autoscale'] = wiggleOpts.get('autoscale', 'local') | 501 trackData['autoscale'] = wiggleOpts.get('autoscale', 'local') |
| 502 | |
| 503 trackData['scale'] = wiggleOpts['scale'] | |
| 457 | 504 |
| 458 self._add_track_json(trackData) | 505 self._add_track_json(trackData) |
| 459 | 506 |
| 460 def add_bam(self, data, trackData, bamOpts, bam_index=None, **kwargs): | 507 def add_bam(self, data, trackData, bamOpts, bam_index=None, **kwargs): |
| 461 dest = os.path.join('data', 'raw', trackData['label'] + '.bam') | 508 dest = os.path.join('data', 'raw', trackData['label'] + '.bam') |
| 504 "type": "JBrowse/View/Track/HTMLVariants", | 551 "type": "JBrowse/View/Track/HTMLVariants", |
| 505 "storeClass": "JBrowse/Store/SeqFeature/VCFTabix", | 552 "storeClass": "JBrowse/Store/SeqFeature/VCFTabix", |
| 506 }) | 553 }) |
| 507 self._add_track_json(trackData) | 554 self._add_track_json(trackData) |
| 508 | 555 |
| 509 def add_features(self, data, format, trackData, gffOpts, **kwargs): | 556 def add_features(self, data, format, trackData, gffOpts, metadata=None, **kwargs): |
| 510 cmd = [ | 557 cmd = [ |
| 511 'perl', self._jbrowse_bin('flatfile-to-json.pl'), | 558 'perl', self._jbrowse_bin('flatfile-to-json.pl'), |
| 512 self.TN_TABLE.get(format, 'gff'), | 559 self.TN_TABLE.get(format, 'gff'), |
| 513 data, | 560 data, |
| 514 '--trackLabel', trackData['label'], | 561 '--trackLabel', trackData['label'], |
| 547 | 594 |
| 548 cmd += [ | 595 cmd += [ |
| 549 '--trackType', gffOpts['trackType'] | 596 '--trackType', gffOpts['trackType'] |
| 550 ] | 597 ] |
| 551 | 598 |
| 599 if metadata: | |
| 600 config.update({'metadata': metadata}) | |
| 552 cmd.extend(['--config', json.dumps(config)]) | 601 cmd.extend(['--config', json.dumps(config)]) |
| 553 | 602 |
| 554 self.subprocess_check_call(cmd) | 603 self.subprocess_check_call(cmd) |
| 555 | 604 |
| 556 if gffOpts.get('index', 'false') == 'true': | 605 if gffOpts.get('index', 'false') == 'true': |
| 557 self.tracksToIndex.append("%s" % trackData['label']) | 606 self.tracksToIndex.append("%s" % trackData['label']) |
| 558 | 607 |
| 608 def add_rest(self, url, trackData): | |
| 609 data = { | |
| 610 "label": trackData['label'], | |
| 611 "key": trackData['key'], | |
| 612 "category": trackData['category'], | |
| 613 "type": "JBrowse/View/Track/HTMLFeatures", | |
| 614 "storeClass": "JBrowse/Store/SeqFeature/REST", | |
| 615 "baseUrl": url, | |
| 616 "query": { | |
| 617 "organism": "tyrannosaurus" | |
| 618 } | |
| 619 } | |
| 620 self._add_track_json(data) | |
| 621 | |
| 559 def process_annotations(self, track): | 622 def process_annotations(self, track): |
| 623 category = track['category'].replace('__pd__date__pd__', TODAY) | |
| 560 outputTrackConfig = { | 624 outputTrackConfig = { |
| 561 'style': { | 625 'style': { |
| 562 'label': track['style'].get('label', 'description'), | 626 'label': track['style'].get('label', 'description'), |
| 563 'className': track['style'].get('className', 'feature'), | 627 'className': track['style'].get('className', 'feature'), |
| 564 'description': track['style'].get('description', ''), | 628 'description': track['style'].get('description', ''), |
| 565 }, | 629 }, |
| 566 'category': track['category'], | 630 'overridePlugins': track['style'].get('overridePlugins', False) == 'True', |
| 631 'overrideDraggable': track['style'].get('overrideDraggable', False) == 'True', | |
| 632 'maxHeight': track['style'].get('maxHeight', '600'), | |
| 633 'category': category, | |
| 567 } | 634 } |
| 568 | 635 |
| 569 mapped_chars = { | 636 mapped_chars = { |
| 570 '>': '__gt__', | 637 '>': '__gt__', |
| 571 '<': '__lt__', | 638 '<': '__lt__', |
| 577 '}': '__cc__', | 644 '}': '__cc__', |
| 578 '@': '__at__', | 645 '@': '__at__', |
| 579 '#': '__pd__' | 646 '#': '__pd__' |
| 580 } | 647 } |
| 581 | 648 |
| 582 for i, (dataset_path, dataset_ext, track_human_label) in enumerate(track['trackfiles']): | 649 for i, (dataset_path, dataset_ext, track_human_label, extra_metadata) in enumerate(track['trackfiles']): |
| 583 # Unsanitize labels (element_identifiers are always sanitized by Galaxy) | 650 # Unsanitize labels (element_identifiers are always sanitized by Galaxy) |
| 584 for key, value in mapped_chars.items(): | 651 for key, value in mapped_chars.items(): |
| 585 track_human_label = track_human_label.replace(value, key) | 652 track_human_label = track_human_label.replace(value, key) |
| 586 | 653 |
| 587 log.info('Processing %s / %s', track['category'], track_human_label) | 654 log.info('Processing %s / %s', category, track_human_label) |
| 588 outputTrackConfig['key'] = track_human_label | 655 outputTrackConfig['key'] = track_human_label |
| 589 hashData = [dataset_path, track_human_label, track['category']] | 656 # We add extra data to hash for the case of REST + SPARQL. |
| 590 outputTrackConfig['label'] = hashlib.md5('|'.join(hashData).encode('utf-8')).hexdigest() + '_%s' % i | 657 try: |
| 658 rest_url = track['conf']['options']['url'] | |
| 659 except KeyError: | |
| 660 rest_url = '' | |
| 661 | |
| 662 # I chose to use track['category'] instead of 'category' here. This | |
| 663 # is intentional. This way re-running the tool on a different date | |
| 664 # will not generate different hashes and make comparison of outputs | |
| 665 # much simpler. | |
| 666 hashData = [dataset_path, track_human_label, track['category'], rest_url] | |
| 667 hashData = '|'.join(hashData).encode('utf-8') | |
| 668 outputTrackConfig['label'] = hashlib.md5(hashData).hexdigest() + '_%s' % i | |
| 591 | 669 |
| 592 # Colour parsing is complex due to different track types having | 670 # Colour parsing is complex due to different track types having |
| 593 # different colour options. | 671 # different colour options. |
| 594 colourOptions = self.cs.parse_colours(track['conf']['options'], track['format'], gff3=dataset_path) | 672 colourOptions = self.cs.parse_colours(track['conf']['options'], track['format'], gff3=dataset_path) |
| 595 # This used to be done with a dict.update() call, however that wiped out any previous style settings... | 673 # This used to be done with a dict.update() call, however that wiped out any previous style settings... |
| 606 | 684 |
| 607 # import pprint; pprint.pprint(track) | 685 # import pprint; pprint.pprint(track) |
| 608 # import sys; sys.exit() | 686 # import sys; sys.exit() |
| 609 if dataset_ext in ('gff', 'gff3', 'bed'): | 687 if dataset_ext in ('gff', 'gff3', 'bed'): |
| 610 self.add_features(dataset_path, dataset_ext, outputTrackConfig, | 688 self.add_features(dataset_path, dataset_ext, outputTrackConfig, |
| 611 track['conf']['options']['gff']) | 689 track['conf']['options']['gff'], metadata=extra_metadata) |
| 612 elif dataset_ext == 'bigwig': | 690 elif dataset_ext == 'bigwig': |
| 613 self.add_bigwig(dataset_path, outputTrackConfig, | 691 self.add_bigwig(dataset_path, outputTrackConfig, |
| 614 track['conf']['options']['wiggle']) | 692 track['conf']['options']['wiggle'], metadata=extra_metadata) |
| 615 elif dataset_ext == 'bam': | 693 elif dataset_ext == 'bam': |
| 616 real_indexes = track['conf']['options']['pileup']['bam_indices']['bam_index'] | 694 real_indexes = track['conf']['options']['pileup']['bam_indices']['bam_index'] |
| 617 if not isinstance(real_indexes, list): | 695 if not isinstance(real_indexes, list): |
| 618 # <bam_indices> | 696 # <bam_indices> |
| 619 # <bam_index>/path/to/a.bam.bai</bam_index> | 697 # <bam_index>/path/to/a.bam.bai</bam_index> |
| 624 # becomes a list. Fun! | 702 # becomes a list. Fun! |
| 625 real_indexes = [real_indexes] | 703 real_indexes = [real_indexes] |
| 626 | 704 |
| 627 self.add_bam(dataset_path, outputTrackConfig, | 705 self.add_bam(dataset_path, outputTrackConfig, |
| 628 track['conf']['options']['pileup'], | 706 track['conf']['options']['pileup'], |
| 629 bam_index=real_indexes[i]) | 707 bam_index=real_indexes[i], metadata=extra_metadata) |
| 630 elif dataset_ext == 'blastxml': | 708 elif dataset_ext == 'blastxml': |
| 631 self.add_blastxml(dataset_path, outputTrackConfig, track['conf']['options']['blast']) | 709 self.add_blastxml(dataset_path, outputTrackConfig, track['conf']['options']['blast'], metadata=extra_metadata) |
| 632 elif dataset_ext == 'vcf': | 710 elif dataset_ext == 'vcf': |
| 633 self.add_vcf(dataset_path, outputTrackConfig) | 711 self.add_vcf(dataset_path, outputTrackConfig, metadata=extra_metadata) |
| 712 elif dataset_ext == 'rest': | |
| 713 self.add_rest(track['conf']['options']['url'], outputTrackConfig, metadata=extra_metadata) | |
| 714 else: | |
| 715 log.warn('Do not know how to handle %s', dataset_ext) | |
| 634 | 716 |
| 635 # Return non-human label for use in other fields | 717 # Return non-human label for use in other fields |
| 636 yield outputTrackConfig['label'] | 718 yield outputTrackConfig['label'] |
| 637 | 719 |
| 638 def add_final_data(self, data): | 720 def add_final_data(self, data): |
| 657 generalData['show_tracklist'] = (data['general']['show_tracklist'] == 'true') | 739 generalData['show_tracklist'] = (data['general']['show_tracklist'] == 'true') |
| 658 generalData['show_nav'] = (data['general']['show_nav'] == 'true') | 740 generalData['show_nav'] = (data['general']['show_nav'] == 'true') |
| 659 generalData['show_overview'] = (data['general']['show_overview'] == 'true') | 741 generalData['show_overview'] = (data['general']['show_overview'] == 'true') |
| 660 generalData['show_menu'] = (data['general']['show_menu'] == 'true') | 742 generalData['show_menu'] = (data['general']['show_menu'] == 'true') |
| 661 generalData['hideGenomeOptions'] = (data['general']['hideGenomeOptions'] == 'true') | 743 generalData['hideGenomeOptions'] = (data['general']['hideGenomeOptions'] == 'true') |
| 744 generalData['plugins'] = data['plugins'] | |
| 662 | 745 |
| 663 viz_data.update(generalData) | 746 viz_data.update(generalData) |
| 664 self._add_json(viz_data) | 747 self._add_json(viz_data) |
| 748 | |
| 749 if 'GCContent' in data['plugins_python']: | |
| 750 self._add_track_json({ | |
| 751 "storeClass": "JBrowse/Store/SeqFeature/SequenceChunks", | |
| 752 "type": "GCContent/View/Track/GCContentXY", | |
| 753 "label": "GCContentXY", | |
| 754 "urlTemplate": "seq/{refseq_dirpath}/{refseq}-", | |
| 755 "bicolor_pivot": 0.5 | |
| 756 # TODO: Expose params for everyone. | |
| 757 }) | |
| 758 | |
| 759 if 'ComboTrackSelector' in data['plugins_python']: | |
| 760 with open(os.path.join(self.outdir, 'data', 'trackList.json'), 'r') as handle: | |
| 761 trackListJson = json.load(handle) | |
| 762 trackListJson.update({ | |
| 763 "trackSelector": { | |
| 764 "renameFacets": { | |
| 765 "tool_tool": "Tool ID", | |
| 766 "tool_tool_id": "Tool ID", | |
| 767 "tool_tool_version": "Tool Version", | |
| 768 "dataset_edam_format": "EDAM", | |
| 769 "dataset_size": "Size", | |
| 770 "history_display_name": "History Name", | |
| 771 "history_user_email": "Owner", | |
| 772 "metadata_dbkey": "Dbkey", | |
| 773 }, | |
| 774 "displayColumns": [ | |
| 775 "key", | |
| 776 "tool_tool", | |
| 777 "tool_tool_version", | |
| 778 "dataset_edam_format", | |
| 779 "dataset_size", | |
| 780 "history_display_name", | |
| 781 "history_user_email", | |
| 782 "metadata_dbkey", | |
| 783 ], | |
| 784 "type": "Faceted", | |
| 785 "title": ["Galaxy Metadata"], | |
| 786 "escapeHTMLInData": False | |
| 787 }, | |
| 788 "trackMetadata": { | |
| 789 "indexFacets": [ | |
| 790 "category", | |
| 791 "key", | |
| 792 "tool_tool_id", | |
| 793 "tool_tool_version", | |
| 794 "dataset_edam_format", | |
| 795 "history_user_email", | |
| 796 "history_display_name" | |
| 797 ] | |
| 798 } | |
| 799 }) | |
| 800 with open(os.path.join(self.outdir, 'data', 'trackList2.json'), 'w') as handle: | |
| 801 json.dump(trackListJson, handle) | |
| 665 | 802 |
| 666 def clone_jbrowse(self, jbrowse_dir, destination): | 803 def clone_jbrowse(self, jbrowse_dir, destination): |
| 667 """Clone a JBrowse directory into a destination directory. | 804 """Clone a JBrowse directory into a destination directory. |
| 668 """ | 805 """ |
| 669 # JBrowse seems to have included some bad symlinks, cp ignores bad symlinks | 806 # JBrowse seems to have included some bad symlinks, cp ignores bad symlinks |
| 675 log.debug(' '.join(cmd)) | 812 log.debug(' '.join(cmd)) |
| 676 subprocess.check_call(cmd) | 813 subprocess.check_call(cmd) |
| 677 | 814 |
| 678 # http://unix.stackexchange.com/a/38691/22785 | 815 # http://unix.stackexchange.com/a/38691/22785 |
| 679 # JBrowse releases come with some broken symlinks | 816 # JBrowse releases come with some broken symlinks |
| 680 cmd = ['find', destination, '-type', 'l', '-xtype', 'l', '-exec', 'rm', "'{}'", '+'] | 817 cmd = ['find', destination, '-type', 'l', '-xtype', 'l'] |
| 681 log.debug(' '.join(cmd)) | 818 log.debug(' '.join(cmd)) |
| 682 subprocess.check_call(cmd) | 819 symlinks = subprocess.check_output(cmd) |
| 820 for i in symlinks: | |
| 821 try: | |
| 822 os.unlink(i) | |
| 823 except OSError: | |
| 824 pass | |
| 683 | 825 |
| 684 | 826 |
| 685 if __name__ == '__main__': | 827 if __name__ == '__main__': |
| 686 parser = argparse.ArgumentParser(description="", epilog="") | 828 parser = argparse.ArgumentParser(description="", epilog="") |
| 687 parser.add_argument('xml', type=argparse.FileType('r'), help='Track Configuration') | 829 parser.add_argument('xml', type=argparse.FileType('r'), help='Track Configuration') |
| 688 | 830 |
| 689 parser.add_argument('--jbrowse', help='Folder containing a jbrowse release') | 831 parser.add_argument('--jbrowse', help='Folder containing a jbrowse release') |
| 690 parser.add_argument('--outdir', help='Output directory', default='out') | 832 parser.add_argument('--outdir', help='Output directory', default='out') |
| 691 parser.add_argument('--standalone', help='Standalone mode includes a copy of JBrowse', action='store_true') | 833 parser.add_argument('--standalone', help='Standalone mode includes a copy of JBrowse', action='store_true') |
| 834 parser.add_argument('--version', '-V', action='version', version="%(prog)s 0.7.0") | |
| 692 args = parser.parse_args() | 835 args = parser.parse_args() |
| 693 | 836 |
| 694 tree = ET.parse(args.xml.name) | 837 tree = ET.parse(args.xml.name) |
| 695 root = tree.getroot() | 838 root = tree.getroot() |
| 696 | 839 |
| 697 jc = JbrowseConnector( | 840 jc = JbrowseConnector( |
| 698 jbrowse=args.jbrowse, | 841 jbrowse=args.jbrowse, |
| 699 outdir=args.outdir, | 842 outdir=args.outdir, |
| 700 genomes=[os.path.realpath(x.text) for x in root.findall('metadata/genomes/genome')], | 843 genomes=[ |
| 844 { | |
| 845 'path': os.path.realpath(x.attrib['path']), | |
| 846 'meta': metadata_from_node(x.find('metadata')) | |
| 847 } | |
| 848 for x in root.findall('metadata/genomes/genome') | |
| 849 ], | |
| 701 standalone=args.standalone, | 850 standalone=args.standalone, |
| 702 gencode=root.find('metadata/gencode').text | 851 gencode=root.find('metadata/gencode').text |
| 703 ) | 852 ) |
| 704 | 853 |
| 705 extra_data = { | 854 extra_data = { |
| 717 'show_tracklist': root.find('metadata/general/show_tracklist').text, | 866 'show_tracklist': root.find('metadata/general/show_tracklist').text, |
| 718 'show_nav': root.find('metadata/general/show_nav').text, | 867 'show_nav': root.find('metadata/general/show_nav').text, |
| 719 'show_overview': root.find('metadata/general/show_overview').text, | 868 'show_overview': root.find('metadata/general/show_overview').text, |
| 720 'show_menu': root.find('metadata/general/show_menu').text, | 869 'show_menu': root.find('metadata/general/show_menu').text, |
| 721 'hideGenomeOptions': root.find('metadata/general/hideGenomeOptions').text, | 870 'hideGenomeOptions': root.find('metadata/general/hideGenomeOptions').text, |
| 722 } | 871 }, |
| 872 'plugins': [{ | |
| 873 'location': 'https://cdn.rawgit.com/TAMU-CPT/blastview/97572a21b7f011c2b4d9a0b5af40e292d694cbef/', | |
| 874 'name': 'BlastView' | |
| 875 }], | |
| 876 'plugins_python': ['BlastView'], | |
| 723 } | 877 } |
| 878 | |
| 879 plugins = root.find('plugins').attrib | |
| 880 if plugins['GCContent'] == 'True': | |
| 881 extra_data['plugins_python'].append('GCContent') | |
| 882 extra_data['plugins'].append({ | |
| 883 'location': 'https://cdn.rawgit.com/elsiklab/gccontent/5c8b0582ecebf9edf684c76af8075fb3d30ec3fa/', | |
| 884 'name': 'GCContent' | |
| 885 }) | |
| 886 | |
| 887 if plugins['Bookmarks'] == 'True': | |
| 888 extra_data['plugins'].append({ | |
| 889 'location': 'https://cdn.rawgit.com/TAMU-CPT/bookmarks-jbrowse/5242694120274c86e1ccd5cb0e5e943e78f82393/', | |
| 890 'name': 'Bookmarks' | |
| 891 }) | |
| 892 | |
| 893 if plugins['ComboTrackSelector'] == 'True': | |
| 894 extra_data['plugins_python'].append('ComboTrackSelector') | |
| 895 extra_data['plugins'].append({ | |
| 896 'location': 'https://cdn.rawgit.com/Arabidopsis-Information-Portal/ComboTrackSelector/52403928d5ccbe2e3a86b0fa5eb8e61c0f2e2f57', | |
| 897 'icon': 'https://galaxyproject.org/images/logos/galaxy-icon-square.png', | |
| 898 'name': 'ComboTrackSelector' | |
| 899 }) | |
| 900 | |
| 901 if plugins['theme'] == 'Minimalist': | |
| 902 extra_data['plugins'].append({ | |
| 903 'location': 'https://cdn.rawgit.com/erasche/jbrowse-minimalist-theme/d698718442da306cf87f033c72ddb745f3077775/', | |
| 904 'name': 'MinimalistTheme' | |
| 905 }) | |
| 906 elif plugins['theme'] == 'Dark': | |
| 907 extra_data['plugins'].append({ | |
| 908 'location': 'https://cdn.rawgit.com/erasche/jbrowse-dark-theme/689eceb7e33bbc1b9b15518d45a5a79b2e5d0a26/', | |
| 909 'name': 'DarkTheme' | |
| 910 }) | |
| 911 | |
| 912 GALAXY_INFRASTRUCTURE_URL = root.find('metadata/galaxyUrl').text | |
| 913 # Sometimes this comes as `localhost` without a protocol | |
| 914 if not GALAXY_INFRASTRUCTURE_URL.startswith('http'): | |
| 915 # so we'll prepend `http://` and hope for the best. Requests *should* | |
| 916 # be GET and not POST so it should redirect OK | |
| 917 GALAXY_INFRASTRUCTURE_URL = 'http://' + GALAXY_INFRASTRUCTURE_URL | |
| 918 | |
| 724 for track in root.findall('tracks/track'): | 919 for track in root.findall('tracks/track'): |
| 725 track_conf = {} | 920 track_conf = {} |
| 726 track_conf['trackfiles'] = [ | 921 track_conf['trackfiles'] = [] |
| 727 (os.path.realpath(x.attrib['path']), x.attrib['ext'], x.attrib['label']) | 922 |
| 728 for x in track.findall('files/trackFile') | 923 for x in track.findall('files/trackFile'): |
| 729 ] | 924 metadata = metadata_from_node(x.find('metadata')) |
| 925 | |
| 926 track_conf['trackfiles'].append(( | |
| 927 os.path.realpath(x.attrib['path']), | |
| 928 x.attrib['ext'], | |
| 929 x.attrib['label'], | |
| 930 metadata | |
| 931 )) | |
| 730 | 932 |
| 731 track_conf['category'] = track.attrib['cat'] | 933 track_conf['category'] = track.attrib['cat'] |
| 732 track_conf['format'] = track.attrib['format'] | 934 track_conf['format'] = track.attrib['format'] |
| 733 try: | 935 try: |
| 734 # Only pertains to gff3 + blastxml. TODO? | 936 # Only pertains to gff3 + blastxml. TODO? |
| 735 track_conf['style'] = {t.tag: t.text for t in track.find('options/style')} | 937 track_conf['style'] = {t.tag: t.text for t in track.find('options/style')} |
| 736 except TypeError: | 938 except TypeError as te: |
| 737 track_conf['style'] = {} | 939 track_conf['style'] = {} |
| 738 pass | 940 pass |
| 739 track_conf['conf'] = etree_to_dict(track.find('options')) | 941 track_conf['conf'] = etree_to_dict(track.find('options')) |
| 740 keys = jc.process_annotations(track_conf) | 942 keys = jc.process_annotations(track_conf) |
| 741 | 943 |
| 742 for key in keys: | 944 for key in keys: |
| 743 extra_data['visibility'][track.attrib.get('visibility', 'default_off')].append(key) | 945 extra_data['visibility'][track.attrib.get('visibility', 'default_off')].append(key) |
| 744 | 946 |
| 745 jc.add_final_data(extra_data) | 947 jc.add_final_data(extra_data) |
| 746 jc.generate_names() |
