Mercurial > repos > iuc > jbrowse
comparison jbrowse.py @ 25:1cfc579079a6 draft
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/jbrowse commit b6f9a87b6091cc881a49e0b6acfadc5e7786967f
| author | iuc |
|---|---|
| date | Tue, 29 Jan 2019 05:34:16 -0500 |
| parents | fa30df9b79c2 |
| children | 61ce21e36cb5 |
comparison
equal
deleted
inserted
replaced
| 24:fa30df9b79c2 | 25:1cfc579079a6 |
|---|---|
| 261 trackConfig['style']['color'] = color_function.replace('\n', '') | 261 trackConfig['style']['color'] = color_function.replace('\n', '') |
| 262 return trackConfig | 262 return trackConfig |
| 263 | 263 |
| 264 | 264 |
| 265 def etree_to_dict(t): | 265 def etree_to_dict(t): |
| 266 if t is None: | |
| 267 return {} | |
| 268 | |
| 266 d = {t.tag: {} if t.attrib else None} | 269 d = {t.tag: {} if t.attrib else None} |
| 267 children = list(t) | 270 children = list(t) |
| 268 if children: | 271 if children: |
| 269 dd = defaultdict(list) | 272 dd = defaultdict(list) |
| 270 for dc in map(etree_to_dict, children): | 273 for dc in map(etree_to_dict, children): |
| 379 | 382 |
| 380 def subprocess_check_call(self, command): | 383 def subprocess_check_call(self, command): |
| 381 log.debug('cd %s && %s', self.outdir, ' '.join(command)) | 384 log.debug('cd %s && %s', self.outdir, ' '.join(command)) |
| 382 subprocess.check_call(command, cwd=self.outdir) | 385 subprocess.check_call(command, cwd=self.outdir) |
| 383 | 386 |
| 387 def subprocess_popen(self, command): | |
| 388 log.debug('cd %s && %s', self.outdir, command) | |
| 389 p = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| 390 output, err = p.communicate() | |
| 391 retcode = p.returncode | |
| 392 if retcode != 0: | |
| 393 log.error('cd %s && %s', self.outdir, command) | |
| 394 log.error(output) | |
| 395 log.error(err) | |
| 396 raise RuntimeError("Command failed with exit code %s" % (retcode)) | |
| 397 | |
| 384 def _jbrowse_bin(self, command): | 398 def _jbrowse_bin(self, command): |
| 385 return os.path.realpath(os.path.join(self.jbrowse, 'bin', command)) | 399 return os.path.realpath(os.path.join(self.jbrowse, 'bin', command)) |
| 386 | 400 |
| 387 def process_genomes(self): | 401 def process_genomes(self): |
| 388 for genome_node in self.genome_paths: | 402 for genome_node in self.genome_paths: |
| 389 # TODO: Waiting on https://github.com/GMOD/jbrowse/pull/884 | 403 # We only expect one input genome per run. This for loop is just |
| 404 # easier to write than the alternative / catches any possible | |
| 405 # issues. | |
| 406 | |
| 407 # Copy the file in workdir, prepare-refseqs.pl will copy it to jbrowse's data dir | |
| 408 local_genome = os.path.realpath('./genome.fasta') | |
| 409 shutil.copy(genome_node['path'], local_genome) | |
| 410 | |
| 411 cmd = ['samtools', 'faidx', local_genome] | |
| 412 self.subprocess_check_call(cmd) | |
| 413 | |
| 390 self.subprocess_check_call([ | 414 self.subprocess_check_call([ |
| 391 'perl', self._jbrowse_bin('prepare-refseqs.pl'), | 415 'perl', self._jbrowse_bin('prepare-refseqs.pl'), |
| 392 '--fasta', genome_node['path']]) | 416 '--trackConfig', json.dumps({'metadata': genome_node['meta']}), |
| 417 '--indexed_fasta', os.path.realpath(local_genome)]) | |
| 418 | |
| 419 os.unlink(local_genome) | |
| 420 os.unlink(local_genome + '.fai') | |
| 393 | 421 |
| 394 def generate_names(self): | 422 def generate_names(self): |
| 395 # Generate names | 423 # Generate names |
| 396 args = [ | 424 args = [ |
| 397 'perl', self._jbrowse_bin('generate-names.pl'), | 425 'perl', self._jbrowse_bin('generate-names.pl'), |
| 418 def _add_track_json(self, json_data): | 446 def _add_track_json(self, json_data): |
| 419 if len(json_data) == 0: | 447 if len(json_data) == 0: |
| 420 return | 448 return |
| 421 | 449 |
| 422 tmp = tempfile.NamedTemporaryFile(delete=False) | 450 tmp = tempfile.NamedTemporaryFile(delete=False) |
| 423 tmp.write(json.dumps(json_data)) | 451 json.dump(json_data, tmp) |
| 424 tmp.close() | 452 tmp.close() |
| 425 cmd = ['perl', self._jbrowse_bin('add-track-json.pl'), tmp.name, | 453 cmd = ['perl', self._jbrowse_bin('add-track-json.pl'), tmp.name, |
| 426 os.path.join('data', 'trackList.json')] | 454 os.path.join('data', 'trackList.json')] |
| 427 self.subprocess_check_call(cmd) | 455 self.subprocess_check_call(cmd) |
| 428 os.unlink(tmp.name) | 456 os.unlink(tmp.name) |
| 429 | 457 |
| 430 def _blastxml_to_gff3(self, xml, min_gap=10): | 458 def _blastxml_to_gff3(self, xml, min_gap=10): |
| 431 gff3_unrebased = tempfile.NamedTemporaryFile(delete=False) | 459 gff3_unrebased = tempfile.NamedTemporaryFile(delete=False) |
| 432 cmd = ['python', os.path.join(INSTALLED_TO, 'blastxml_to_gapped_gff3.py'), | 460 cmd = ['python', os.path.join(INSTALLED_TO, 'blastxml_to_gapped_gff3.py'), |
| 433 '--trim', '--trim_end', '--min_gap', str(min_gap), xml] | 461 '--trim', '--trim_end', '--include_seq', '--min_gap', str(min_gap), xml] |
| 434 log.debug('cd %s && %s > %s', self.outdir, ' '.join(cmd), gff3_unrebased.name) | 462 log.debug('cd %s && %s > %s', self.outdir, ' '.join(cmd), gff3_unrebased.name) |
| 435 subprocess.check_call(cmd, cwd=self.outdir, stdout=gff3_unrebased) | 463 subprocess.check_call(cmd, cwd=self.outdir, stdout=gff3_unrebased) |
| 436 gff3_unrebased.close() | 464 gff3_unrebased.close() |
| 437 return gff3_unrebased.name | 465 return gff3_unrebased.name |
| 438 | 466 |
| 451 | 479 |
| 452 # Replace original gff3 file | 480 # Replace original gff3 file |
| 453 shutil.copy(gff3_rebased.name, gff3) | 481 shutil.copy(gff3_rebased.name, gff3) |
| 454 os.unlink(gff3_rebased.name) | 482 os.unlink(gff3_rebased.name) |
| 455 | 483 |
| 456 config = { | 484 dest = os.path.join(self.outdir, 'data', 'raw', trackData['label'] + '.gff') |
| 457 'glyph': 'JBrowse/View/FeatureGlyph/Segments', | 485 |
| 458 "category": trackData['category'], | 486 self._sort_gff(gff3, dest) |
| 459 } | 487 |
| 460 | 488 url = os.path.join('raw', trackData['label'] + '.gff.gz') |
| 461 clientConfig = trackData['style'] | 489 trackData.update({ |
| 462 | 490 "urlTemplate": url, |
| 463 cmd = ['perl', self._jbrowse_bin('flatfile-to-json.pl'), | 491 "storeClass": "JBrowse/Store/SeqFeature/GFF3Tabix", |
| 464 '--gff', gff3, | 492 }) |
| 465 '--trackLabel', trackData['label'], | 493 |
| 466 '--key', trackData['key'], | 494 trackData['glyph'] = 'JBrowse/View/FeatureGlyph/Segments' |
| 467 '--clientConfig', json.dumps(clientConfig), | 495 |
| 468 '--config', json.dumps(config), | 496 trackData['trackType'] = 'BlastView/View/Track/CanvasFeatures' |
| 469 '--trackType', 'BlastView/View/Track/CanvasFeatures' | 497 trackData['type'] = 'BlastView/View/Track/CanvasFeatures' |
| 470 ] | 498 |
| 471 | 499 self._add_track_json(trackData) |
| 472 # className in --clientConfig is ignored, it needs to be set with --className | 500 |
| 473 if 'className' in trackData['style']: | |
| 474 cmd += ['--className', trackData['style']['className']] | |
| 475 | |
| 476 self.subprocess_check_call(cmd) | |
| 477 os.unlink(gff3) | 501 os.unlink(gff3) |
| 478 | 502 |
| 479 if blastOpts.get('index', 'false') == 'true': | 503 if blastOpts.get('index', 'false') == 'true': |
| 480 self.tracksToIndex.append("%s" % trackData['label']) | 504 self.tracksToIndex.append("%s" % trackData['label']) |
| 481 | 505 |
| 502 | 526 |
| 503 trackData['scale'] = wiggleOpts['scale'] | 527 trackData['scale'] = wiggleOpts['scale'] |
| 504 | 528 |
| 505 self._add_track_json(trackData) | 529 self._add_track_json(trackData) |
| 506 | 530 |
| 531 def add_bigwig_multiple(self, data, trackData, wiggleOpts, **kwargs): | |
| 532 | |
| 533 urls = [] | |
| 534 for idx, bw in enumerate(data): | |
| 535 dest = os.path.join('data', 'raw', trackData['label'] + '_' + str(idx) + '.bw') | |
| 536 cmd = ['ln', '-s', bw[1], dest] | |
| 537 self.subprocess_check_call(cmd) | |
| 538 | |
| 539 urls.append({"url": os.path.join('raw', trackData['label'] + '_' + str(idx) + '.bw'), "name": str(idx + 1) + ' - ' + bw[0]}) | |
| 540 | |
| 541 trackData.update({ | |
| 542 "urlTemplates": urls, | |
| 543 "showTooltips": "true", | |
| 544 "storeClass": "MultiBigWig/Store/SeqFeature/MultiBigWig", | |
| 545 "type": "MultiBigWig/View/Track/MultiWiggle/MultiDensity", | |
| 546 }) | |
| 547 if 'XYPlot' in wiggleOpts['type']: | |
| 548 trackData['type'] = "MultiBigWig/View/Track/MultiWiggle/MultiXYPlot" | |
| 549 | |
| 550 trackData['variance_band'] = True if wiggleOpts['variance_band'] == 'true' else False | |
| 551 | |
| 552 if 'min' in wiggleOpts and 'max' in wiggleOpts: | |
| 553 trackData['min_score'] = wiggleOpts['min'] | |
| 554 trackData['max_score'] = wiggleOpts['max'] | |
| 555 else: | |
| 556 trackData['autoscale'] = wiggleOpts.get('autoscale', 'local') | |
| 557 | |
| 558 trackData['scale'] = wiggleOpts['scale'] | |
| 559 | |
| 560 self._add_track_json(trackData) | |
| 561 | |
| 507 def add_bam(self, data, trackData, bamOpts, bam_index=None, **kwargs): | 562 def add_bam(self, data, trackData, bamOpts, bam_index=None, **kwargs): |
| 508 dest = os.path.join('data', 'raw', trackData['label'] + '.bam') | 563 dest = os.path.join('data', 'raw', trackData['label'] + '.bam') |
| 509 cmd = ['ln', '-s', os.path.realpath(data), dest] | 564 cmd = ['ln', '-s', os.path.realpath(data), dest] |
| 510 self.subprocess_check_call(cmd) | 565 self.subprocess_check_call(cmd) |
| 511 | 566 |
| 515 url = os.path.join('raw', trackData['label'] + '.bam') | 570 url = os.path.join('raw', trackData['label'] + '.bam') |
| 516 trackData.update({ | 571 trackData.update({ |
| 517 "urlTemplate": url, | 572 "urlTemplate": url, |
| 518 "type": "JBrowse/View/Track/Alignments2", | 573 "type": "JBrowse/View/Track/Alignments2", |
| 519 "storeClass": "JBrowse/Store/SeqFeature/BAM", | 574 "storeClass": "JBrowse/Store/SeqFeature/BAM", |
| 575 "chunkSizeLimit": bamOpts.get('chunkSizeLimit', '5000000') | |
| 520 }) | 576 }) |
| 521 | 577 |
| 522 # Apollo will only switch to the (prettier) 'bam-read' className if it's not set explicitly in the track config | 578 # Apollo will only switch to the (prettier) 'bam-read' className if it's not set explicitly in the track config |
| 523 # So remove the default 'feature' value for these bam tracks | 579 # So remove the default 'feature' value for these bam tracks |
| 524 if 'className' in trackData['style'] and trackData['style']['className'] == 'feature': | 580 if 'className' in trackData['style'] and trackData['style']['className'] == 'feature': |
| 530 trackData2 = copy.copy(trackData) | 586 trackData2 = copy.copy(trackData) |
| 531 trackData2.update({ | 587 trackData2.update({ |
| 532 "type": "JBrowse/View/Track/SNPCoverage", | 588 "type": "JBrowse/View/Track/SNPCoverage", |
| 533 "key": trackData['key'] + " - SNPs/Coverage", | 589 "key": trackData['key'] + " - SNPs/Coverage", |
| 534 "label": trackData['label'] + "_autosnp", | 590 "label": trackData['label'] + "_autosnp", |
| 591 "chunkSizeLimit": bamOpts.get('chunkSizeLimit', '5000000') | |
| 535 }) | 592 }) |
| 536 self._add_track_json(trackData2) | 593 self._add_track_json(trackData2) |
| 537 | 594 |
| 538 def add_vcf(self, data, trackData, vcfOpts={}, **kwargs): | 595 def add_vcf(self, data, trackData, vcfOpts={}, **kwargs): |
| 539 dest = os.path.join('data', 'raw', trackData['label'] + '.vcf') | 596 dest = os.path.join('data', 'raw', trackData['label'] + '.vcf') |
| 543 cmd = ['bgzip', dest] | 600 cmd = ['bgzip', dest] |
| 544 self.subprocess_check_call(cmd) | 601 self.subprocess_check_call(cmd) |
| 545 cmd = ['tabix', '-p', 'vcf', dest + '.gz'] | 602 cmd = ['tabix', '-p', 'vcf', dest + '.gz'] |
| 546 self.subprocess_check_call(cmd) | 603 self.subprocess_check_call(cmd) |
| 547 | 604 |
| 548 url = os.path.join('raw', trackData['label'] + '.vcf') | 605 url = os.path.join('raw', trackData['label'] + '.vcf.gz') |
| 549 trackData.update({ | 606 trackData.update({ |
| 550 "urlTemplate": url, | 607 "urlTemplate": url, |
| 551 "type": "JBrowse/View/Track/HTMLVariants", | 608 "type": "JBrowse/View/Track/HTMLVariants", |
| 552 "storeClass": "JBrowse/Store/SeqFeature/VCFTabix", | 609 "storeClass": "JBrowse/Store/SeqFeature/VCFTabix", |
| 553 }) | 610 }) |
| 554 self._add_track_json(trackData) | 611 self._add_track_json(trackData) |
| 555 | 612 |
| 556 def add_features(self, data, format, trackData, gffOpts, metadata=None, **kwargs): | 613 def _sort_gff(self, data, dest): |
| 557 cmd = [ | 614 |
| 558 'perl', self._jbrowse_bin('flatfile-to-json.pl'), | 615 if not os.path.exists(dest): |
| 559 self.TN_TABLE.get(format, 'gff'), | 616 # Only index if not already done |
| 560 data, | 617 cmd = "grep ^\"#\" '%s' > '%s'" % (data, dest) |
| 561 '--trackLabel', trackData['label'], | 618 self.subprocess_popen(cmd) |
| 562 '--key', trackData['key'] | 619 |
| 563 ] | 620 cmd = "grep -v ^\"#\" '%s' | grep -v \"^$\" | grep \"\t\" | sort -k1,1 -k4,4n >> '%s'" % (data, dest) |
| 564 | 621 self.subprocess_popen(cmd) |
| 565 # className in --clientConfig is ignored, it needs to be set with --className | 622 |
| 566 if 'className' in trackData['style']: | 623 cmd = ['bgzip', '-f', dest] |
| 567 cmd += ['--className', trackData['style']['className']] | 624 self.subprocess_popen(' '.join(cmd)) |
| 568 | 625 cmd = ['tabix', '-f', '-p', 'gff', dest + '.gz'] |
| 569 config = copy.copy(trackData) | 626 self.subprocess_popen(' '.join(cmd)) |
| 570 clientConfig = trackData['style'] | 627 |
| 571 del config['style'] | 628 def add_features(self, data, format, trackData, gffOpts, **kwargs): |
| 629 | |
| 630 dest = os.path.join(self.outdir, 'data', 'raw', trackData['label'] + '.gff') | |
| 631 | |
| 632 self._sort_gff(data, dest) | |
| 633 | |
| 634 url = os.path.join('raw', trackData['label'] + '.gff.gz') | |
| 635 trackData.update({ | |
| 636 "urlTemplate": url, | |
| 637 "storeClass": "JBrowse/Store/SeqFeature/GFF3Tabix", | |
| 638 }) | |
| 572 | 639 |
| 573 if 'match' in gffOpts: | 640 if 'match' in gffOpts: |
| 574 config['glyph'] = 'JBrowse/View/FeatureGlyph/Segments' | 641 trackData['glyph'] = 'JBrowse/View/FeatureGlyph/Segments' |
| 575 if bool(gffOpts['match']): | |
| 576 # Can be empty for CanvasFeatures = will take all by default | |
| 577 cmd += ['--type', gffOpts['match']] | |
| 578 | |
| 579 cmd += ['--clientConfig', json.dumps(clientConfig), | |
| 580 ] | |
| 581 | 642 |
| 582 trackType = 'JBrowse/View/Track/CanvasFeatures' | 643 trackType = 'JBrowse/View/Track/CanvasFeatures' |
| 583 if 'trackType' in gffOpts: | 644 if 'trackType' in gffOpts: |
| 584 trackType = gffOpts['trackType'] | 645 trackType = gffOpts['trackType'] |
| 646 trackData['trackType'] = trackType | |
| 585 | 647 |
| 586 if trackType == 'JBrowse/View/Track/CanvasFeatures': | 648 if trackType == 'JBrowse/View/Track/CanvasFeatures': |
| 587 if 'transcriptType' in gffOpts and gffOpts['transcriptType']: | 649 if 'transcriptType' in gffOpts and gffOpts['transcriptType']: |
| 588 config['transcriptType'] = gffOpts['transcriptType'] | 650 trackData['transcriptType'] = gffOpts['transcriptType'] |
| 589 if 'subParts' in gffOpts and gffOpts['subParts']: | 651 if 'subParts' in gffOpts and gffOpts['subParts']: |
| 590 config['subParts'] = gffOpts['subParts'] | 652 trackData['subParts'] = gffOpts['subParts'] |
| 591 if 'impliedUTRs' in gffOpts and gffOpts['impliedUTRs']: | 653 if 'impliedUTRs' in gffOpts and gffOpts['impliedUTRs']: |
| 592 config['impliedUTRs'] = gffOpts['impliedUTRs'] | 654 trackData['impliedUTRs'] = gffOpts['impliedUTRs'] |
| 593 elif trackType == 'JBrowse/View/Track/HTMLFeatures': | 655 |
| 594 if 'transcriptType' in gffOpts and gffOpts['transcriptType']: | 656 self._add_track_json(trackData) |
| 595 cmd += ['--type', gffOpts['transcriptType']] | |
| 596 | |
| 597 cmd += [ | |
| 598 '--trackType', gffOpts['trackType'] | |
| 599 ] | |
| 600 | |
| 601 if metadata: | |
| 602 config.update({'metadata': metadata}) | |
| 603 cmd.extend(['--config', json.dumps(config)]) | |
| 604 | |
| 605 self.subprocess_check_call(cmd) | |
| 606 | 657 |
| 607 if gffOpts.get('index', 'false') == 'true': | 658 if gffOpts.get('index', 'false') == 'true': |
| 608 self.tracksToIndex.append("%s" % trackData['label']) | 659 self.tracksToIndex.append("%s" % trackData['label']) |
| 609 | 660 |
| 610 def add_rest(self, url, trackData): | 661 def add_rest(self, url, trackData): |
| 612 "label": trackData['label'], | 663 "label": trackData['label'], |
| 613 "key": trackData['key'], | 664 "key": trackData['key'], |
| 614 "category": trackData['category'], | 665 "category": trackData['category'], |
| 615 "type": "JBrowse/View/Track/HTMLFeatures", | 666 "type": "JBrowse/View/Track/HTMLFeatures", |
| 616 "storeClass": "JBrowse/Store/SeqFeature/REST", | 667 "storeClass": "JBrowse/Store/SeqFeature/REST", |
| 617 "baseUrl": url, | 668 "baseUrl": url |
| 618 "query": { | 669 } |
| 619 "organism": "tyrannosaurus" | 670 self._add_track_json(data) |
| 620 } | 671 |
| 672 def add_sparql(self, url, query, trackData): | |
| 673 data = { | |
| 674 "label": trackData['label'], | |
| 675 "key": trackData['key'], | |
| 676 "category": trackData['category'], | |
| 677 "type": "JBrowse/View/Track/CanvasFeatures", | |
| 678 "storeClass": "JBrowse/Store/SeqFeature/SPARQL", | |
| 679 "urlTemplate": url, | |
| 680 "queryTemplate": query | |
| 621 } | 681 } |
| 622 self._add_track_json(data) | 682 self._add_track_json(data) |
| 623 | 683 |
| 624 def process_annotations(self, track): | 684 def process_annotations(self, track): |
| 625 category = track['category'].replace('__pd__date__pd__', TODAY) | 685 category = track['category'].replace('__pd__date__pd__', TODAY) |
| 643 '[': '__ob__', | 703 '[': '__ob__', |
| 644 ']': '__cb__', | 704 ']': '__cb__', |
| 645 '{': '__oc__', | 705 '{': '__oc__', |
| 646 '}': '__cc__', | 706 '}': '__cc__', |
| 647 '@': '__at__', | 707 '@': '__at__', |
| 648 '#': '__pd__' | 708 '#': '__pd__', |
| 709 "": '__cn__' | |
| 649 } | 710 } |
| 650 | 711 |
| 651 for i, (dataset_path, dataset_ext, track_human_label, extra_metadata) in enumerate(track['trackfiles']): | 712 for i, (dataset_path, dataset_ext, track_human_label, extra_metadata) in enumerate(track['trackfiles']): |
| 652 # Unsanitize labels (element_identifiers are always sanitized by Galaxy) | 713 # Unsanitize labels (element_identifiers are always sanitized by Galaxy) |
| 653 for key, value in mapped_chars.items(): | 714 for key, value in mapped_chars.items(): |
| 654 track_human_label = track_human_label.replace(value, key) | 715 track_human_label = track_human_label.replace(value, key) |
| 655 | 716 |
| 656 log.info('Processing %s / %s', category, track_human_label) | 717 log.info('Processing %s / %s', category, track_human_label) |
| 657 outputTrackConfig['key'] = track_human_label | 718 outputTrackConfig['key'] = track_human_label |
| 658 # We add extra data to hash for the case of REST + SPARQL. | 719 # We add extra data to hash for the case of REST + SPARQL. |
| 659 try: | 720 if 'conf' in track and 'options' in track['conf'] and 'url' in track['conf']['options']: |
| 660 rest_url = track['conf']['options']['url'] | 721 rest_url = track['conf']['options']['url'] |
| 661 except KeyError: | 722 else: |
| 662 rest_url = '' | 723 rest_url = '' |
| 663 | 724 |
| 664 # I chose to use track['category'] instead of 'category' here. This | 725 # I chose to use track['category'] instead of 'category' here. This |
| 665 # is intentional. This way re-running the tool on a different date | 726 # is intentional. This way re-running the tool on a different date |
| 666 # will not generate different hashes and make comparison of outputs | 727 # will not generate different hashes and make comparison of outputs |
| 667 # much simpler. | 728 # much simpler. |
| 668 hashData = [dataset_path, track_human_label, track['category'], rest_url] | 729 hashData = [str(dataset_path), track_human_label, track['category'], rest_url] |
| 669 hashData = '|'.join(hashData).encode('utf-8') | 730 hashData = '|'.join(hashData).encode('utf-8') |
| 670 outputTrackConfig['label'] = hashlib.md5(hashData).hexdigest() + '_%s' % i | 731 outputTrackConfig['label'] = hashlib.md5(hashData).hexdigest() + '_%s' % i |
| 732 outputTrackConfig['metadata'] = extra_metadata | |
| 671 | 733 |
| 672 # Colour parsing is complex due to different track types having | 734 # Colour parsing is complex due to different track types having |
| 673 # different colour options. | 735 # different colour options. |
| 674 colourOptions = self.cs.parse_colours(track['conf']['options'], track['format'], gff3=dataset_path) | 736 colourOptions = self.cs.parse_colours(track['conf']['options'], track['format'], gff3=dataset_path) |
| 675 # This used to be done with a dict.update() call, however that wiped out any previous style settings... | 737 # This used to be done with a dict.update() call, however that wiped out any previous style settings... |
| 686 | 748 |
| 687 # import pprint; pprint.pprint(track) | 749 # import pprint; pprint.pprint(track) |
| 688 # import sys; sys.exit() | 750 # import sys; sys.exit() |
| 689 if dataset_ext in ('gff', 'gff3', 'bed'): | 751 if dataset_ext in ('gff', 'gff3', 'bed'): |
| 690 self.add_features(dataset_path, dataset_ext, outputTrackConfig, | 752 self.add_features(dataset_path, dataset_ext, outputTrackConfig, |
| 691 track['conf']['options']['gff'], metadata=extra_metadata) | 753 track['conf']['options']['gff']) |
| 692 elif dataset_ext == 'bigwig': | 754 elif dataset_ext == 'bigwig': |
| 693 self.add_bigwig(dataset_path, outputTrackConfig, | 755 self.add_bigwig(dataset_path, outputTrackConfig, |
| 694 track['conf']['options']['wiggle'], metadata=extra_metadata) | 756 track['conf']['options']['wiggle']) |
| 757 elif dataset_ext == 'bigwig_multiple': | |
| 758 self.add_bigwig_multiple(dataset_path, outputTrackConfig, | |
| 759 track['conf']['options']['wiggle']) | |
| 695 elif dataset_ext == 'bam': | 760 elif dataset_ext == 'bam': |
| 696 real_indexes = track['conf']['options']['pileup']['bam_indices']['bam_index'] | 761 real_indexes = track['conf']['options']['pileup']['bam_indices']['bam_index'] |
| 697 if not isinstance(real_indexes, list): | 762 if not isinstance(real_indexes, list): |
| 698 # <bam_indices> | 763 # <bam_indices> |
| 699 # <bam_index>/path/to/a.bam.bai</bam_index> | 764 # <bam_index>/path/to/a.bam.bai</bam_index> |
| 704 # becomes a list. Fun! | 769 # becomes a list. Fun! |
| 705 real_indexes = [real_indexes] | 770 real_indexes = [real_indexes] |
| 706 | 771 |
| 707 self.add_bam(dataset_path, outputTrackConfig, | 772 self.add_bam(dataset_path, outputTrackConfig, |
| 708 track['conf']['options']['pileup'], | 773 track['conf']['options']['pileup'], |
| 709 bam_index=real_indexes[i], metadata=extra_metadata) | 774 bam_index=real_indexes[i]) |
| 710 elif dataset_ext == 'blastxml': | 775 elif dataset_ext == 'blastxml': |
| 711 self.add_blastxml(dataset_path, outputTrackConfig, track['conf']['options']['blast'], metadata=extra_metadata) | 776 self.add_blastxml(dataset_path, outputTrackConfig, track['conf']['options']['blast']) |
| 712 elif dataset_ext == 'vcf': | 777 elif dataset_ext == 'vcf': |
| 713 self.add_vcf(dataset_path, outputTrackConfig, metadata=extra_metadata) | 778 self.add_vcf(dataset_path, outputTrackConfig) |
| 714 elif dataset_ext == 'rest': | 779 elif dataset_ext == 'rest': |
| 715 self.add_rest(track['conf']['options']['url'], outputTrackConfig, metadata=extra_metadata) | 780 self.add_rest(track['conf']['options']['rest']['url'], outputTrackConfig) |
| 781 elif dataset_ext == 'sparql': | |
| 782 sparql_query = track['conf']['options']['sparql']['query'] | |
| 783 for key, value in mapped_chars.items(): | |
| 784 sparql_query = sparql_query.replace(value, key) | |
| 785 self.add_sparql(track['conf']['options']['sparql']['url'], sparql_query, outputTrackConfig) | |
| 716 else: | 786 else: |
| 717 log.warn('Do not know how to handle %s', dataset_ext) | 787 log.warn('Do not know how to handle %s', dataset_ext) |
| 718 | 788 |
| 719 # Return non-human label for use in other fields | 789 # Return non-human label for use in other fields |
| 720 yield outputTrackConfig['label'] | 790 yield outputTrackConfig['label'] |
| 748 viz_data.update(generalData) | 818 viz_data.update(generalData) |
| 749 self._add_json(viz_data) | 819 self._add_json(viz_data) |
| 750 | 820 |
| 751 if 'GCContent' in data['plugins_python']: | 821 if 'GCContent' in data['plugins_python']: |
| 752 self._add_track_json({ | 822 self._add_track_json({ |
| 753 "storeClass": "JBrowse/Store/SeqFeature/SequenceChunks", | 823 "storeClass": "JBrowse/Store/SeqFeature/IndexedFasta", |
| 754 "type": "GCContent/View/Track/GCContentXY", | 824 "type": "GCContent/View/Track/GCContentXY", |
| 755 "label": "GCContentXY", | 825 "label": "GC Content", |
| 756 "urlTemplate": "seq/{refseq_dirpath}/{refseq}-", | 826 "key": "GCContentXY", |
| 757 "bicolor_pivot": 0.5 | 827 "urlTemplate": "seq/genome.fasta", |
| 828 "bicolor_pivot": 0.5, | |
| 829 "category": "GC Content", | |
| 830 "metadata": { | |
| 831 "tool_tool": '<a target="_blank" href="https://github.com/elsiklab/gccontent/commit/030180e75a19fad79478d43a67c566ec6">elsiklab/gccontent</a>', | |
| 832 "tool_tool_version": "5c8b0582ecebf9edf684c76af8075fb3d30ec3fa", | |
| 833 "dataset_edam_format": "", | |
| 834 "dataset_size": "", | |
| 835 "history_display_name": "", | |
| 836 "history_user_email": "", | |
| 837 "metadata_dbkey": "", | |
| 838 } | |
| 839 # TODO: Expose params for everyone. | |
| 840 }) | |
| 841 self._add_track_json({ | |
| 842 "storeClass": "JBrowse/Store/SeqFeature/IndexedFasta", | |
| 843 "type": "GCContent/View/Track/GCContentXY", | |
| 844 "label": "GC skew", | |
| 845 "key": "GCSkew", | |
| 846 "urlTemplate": "seq/genome.fasta", | |
| 847 "gcMode": "skew", | |
| 848 "min_score": -1, | |
| 849 "bicolor_pivot": 0, | |
| 850 "category": "GC Content", | |
| 851 "metadata": { | |
| 852 "tool_tool": '<a target="_blank" href="https://github.com/elsiklab/gccontent/commit/030180e75a19fad79478d43a67c566ec6">elsiklab/gccontent</a>', | |
| 853 "tool_tool_version": "5c8b0582ecebf9edf684c76af8075fb3d30ec3fa", | |
| 854 "dataset_edam_format": "", | |
| 855 "dataset_size": "", | |
| 856 "history_display_name": "", | |
| 857 "history_user_email": "", | |
| 858 "metadata_dbkey": "", | |
| 859 } | |
| 758 # TODO: Expose params for everyone. | 860 # TODO: Expose params for everyone. |
| 759 }) | 861 }) |
| 760 | 862 |
| 761 if 'ComboTrackSelector' in data['plugins_python']: | 863 if 'ComboTrackSelector' in data['plugins_python']: |
| 762 with open(os.path.join(self.outdir, 'data', 'trackList.json'), 'r') as handle: | 864 with open(os.path.join(self.outdir, 'data', 'trackList.json'), 'r') as handle: |
| 783 "history_user_email", | 885 "history_user_email", |
| 784 "metadata_dbkey", | 886 "metadata_dbkey", |
| 785 ], | 887 ], |
| 786 "type": "Faceted", | 888 "type": "Faceted", |
| 787 "title": ["Galaxy Metadata"], | 889 "title": ["Galaxy Metadata"], |
| 890 "icon": "https://galaxyproject.org/images/logos/galaxy-icon-square.png", | |
| 788 "escapeHTMLInData": False | 891 "escapeHTMLInData": False |
| 789 }, | 892 }, |
| 790 "trackMetadata": { | 893 "trackMetadata": { |
| 791 "indexFacets": [ | 894 "indexFacets": [ |
| 792 "category", | 895 "category", |
| 831 parser.add_argument('xml', type=argparse.FileType('r'), help='Track Configuration') | 934 parser.add_argument('xml', type=argparse.FileType('r'), help='Track Configuration') |
| 832 | 935 |
| 833 parser.add_argument('--jbrowse', help='Folder containing a jbrowse release') | 936 parser.add_argument('--jbrowse', help='Folder containing a jbrowse release') |
| 834 parser.add_argument('--outdir', help='Output directory', default='out') | 937 parser.add_argument('--outdir', help='Output directory', default='out') |
| 835 parser.add_argument('--standalone', help='Standalone mode includes a copy of JBrowse', action='store_true') | 938 parser.add_argument('--standalone', help='Standalone mode includes a copy of JBrowse', action='store_true') |
| 836 parser.add_argument('--version', '-V', action='version', version="%(prog)s 0.7.0") | 939 parser.add_argument('--version', '-V', action='version', version="%(prog)s 0.8.0") |
| 837 args = parser.parse_args() | 940 args = parser.parse_args() |
| 838 | 941 |
| 839 tree = ET.parse(args.xml.name) | 942 tree = ET.parse(args.xml.name) |
| 840 root = tree.getroot() | 943 root = tree.getroot() |
| 944 | |
| 945 # This should be done ASAP | |
| 946 GALAXY_INFRASTRUCTURE_URL = root.find('metadata/galaxyUrl').text | |
| 947 # Sometimes this comes as `localhost` without a protocol | |
| 948 if not GALAXY_INFRASTRUCTURE_URL.startswith('http'): | |
| 949 # so we'll prepend `http://` and hope for the best. Requests *should* | |
| 950 # be GET and not POST so it should redirect OK | |
| 951 GALAXY_INFRASTRUCTURE_URL = 'http://' + GALAXY_INFRASTRUCTURE_URL | |
| 841 | 952 |
| 842 jc = JbrowseConnector( | 953 jc = JbrowseConnector( |
| 843 jbrowse=args.jbrowse, | 954 jbrowse=args.jbrowse, |
| 844 outdir=args.outdir, | 955 outdir=args.outdir, |
| 845 genomes=[ | 956 genomes=[ |
| 869 'show_nav': root.find('metadata/general/show_nav').text, | 980 'show_nav': root.find('metadata/general/show_nav').text, |
| 870 'show_overview': root.find('metadata/general/show_overview').text, | 981 'show_overview': root.find('metadata/general/show_overview').text, |
| 871 'show_menu': root.find('metadata/general/show_menu').text, | 982 'show_menu': root.find('metadata/general/show_menu').text, |
| 872 'hideGenomeOptions': root.find('metadata/general/hideGenomeOptions').text, | 983 'hideGenomeOptions': root.find('metadata/general/hideGenomeOptions').text, |
| 873 }, | 984 }, |
| 874 'plugins': [{ | 985 'plugins': [], |
| 875 'location': 'https://cdn.jsdelivr.net/gh/TAMU-CPT/blastview@97572a21b7f011c2b4d9a0b5af40e292d694cbef/', | 986 'plugins_python': [], |
| 876 'name': 'BlastView' | |
| 877 }], | |
| 878 'plugins_python': ['BlastView'], | |
| 879 } | 987 } |
| 880 | 988 |
| 881 plugins = root.find('plugins').attrib | 989 plugins = root.find('plugins').attrib |
| 882 if plugins['GCContent'] == 'True': | 990 if plugins['GCContent'] == 'True': |
| 883 extra_data['plugins_python'].append('GCContent') | 991 extra_data['plugins_python'].append('GCContent') |
| 884 extra_data['plugins'].append({ | 992 extra_data['plugins'].append({ |
| 885 'location': 'https://cdn.jsdelivr.net/gh/elsiklab/gccontent@5c8b0582ecebf9edf684c76af8075fb3d30ec3fa/', | 993 'location': 'https://cdn.jsdelivr.net/gh/elsiklab/gccontent@5c8b0582ecebf9edf684c76af8075fb3d30ec3fa/', |
| 886 'name': 'GCContent' | 994 'name': 'GCContent' |
| 887 }) | 995 }) |
| 888 | 996 |
| 889 if plugins['Bookmarks'] == 'True': | 997 # Not needed in 1.16.1: it's built in the conda package now, and this plugin doesn't need to be enabled anywhere |
| 890 extra_data['plugins'].append({ | 998 # if plugins['Bookmarks'] == 'True': |
| 891 'location': 'https://cdn.jsdelivr.net/gh/TAMU-CPT/bookmarks-jbrowse@5242694120274c86e1ccd5cb0e5e943e78f82393/', | 999 # extra_data['plugins'].append({ |
| 892 'name': 'Bookmarks' | 1000 # 'location': 'https://cdn.jsdelivr.net/gh/TAMU-CPT/bookmarks-jbrowse@5242694120274c86e1ccd5cb0e5e943e78f82393/', |
| 893 }) | 1001 # 'name': 'Bookmarks' |
| 894 | 1002 # }) |
| 1003 | |
| 1004 # Not needed in 1.16.1: it's built in the conda package now, and this plugin doesn't need to be enabled anywhere | |
| 895 if plugins['ComboTrackSelector'] == 'True': | 1005 if plugins['ComboTrackSelector'] == 'True': |
| 896 extra_data['plugins_python'].append('ComboTrackSelector') | 1006 extra_data['plugins_python'].append('ComboTrackSelector') |
| 897 extra_data['plugins'].append({ | 1007 # Not needed in 1.16.1: it's built in the conda package now, and this plugin doesn't need to be enabled anywhere |
| 898 'location': 'https://cdn.jsdelivr.net/gh/Arabidopsis-Information-Portal/ComboTrackSelector@52403928d5ccbe2e3a86b0fa5eb8e61c0f2e2f57', | 1008 # extra_data['plugins'].append({ |
| 899 'icon': 'https://galaxyproject.org/images/logos/galaxy-icon-square.png', | 1009 # 'location': 'https://cdn.jsdelivr.net/gh/Arabidopsis-Information-Portal/ComboTrackSelector@52403928d5ccbe2e3a86b0fa5eb8e61c0f2e2f57/', |
| 900 'name': 'ComboTrackSelector' | 1010 # 'icon': 'https://galaxyproject.org/images/logos/galaxy-icon-square.png', |
| 901 }) | 1011 # 'name': 'ComboTrackSelector' |
| 1012 # }) | |
| 902 | 1013 |
| 903 if plugins['theme'] == 'Minimalist': | 1014 if plugins['theme'] == 'Minimalist': |
| 904 extra_data['plugins'].append({ | 1015 extra_data['plugins'].append({ |
| 905 'location': 'https://cdn.jsdelivr.net/gh/erasche/jbrowse-minimalist-theme@d698718442da306cf87f033c72ddb745f3077775/', | 1016 'location': 'https://cdn.jsdelivr.net/gh/erasche/jbrowse-minimalist-theme@d698718442da306cf87f033c72ddb745f3077775/', |
| 906 'name': 'MinimalistTheme' | 1017 'name': 'MinimalistTheme' |
| 909 extra_data['plugins'].append({ | 1020 extra_data['plugins'].append({ |
| 910 'location': 'https://cdn.jsdelivr.net/gh/erasche/jbrowse-dark-theme@689eceb7e33bbc1b9b15518d45a5a79b2e5d0a26/', | 1021 'location': 'https://cdn.jsdelivr.net/gh/erasche/jbrowse-dark-theme@689eceb7e33bbc1b9b15518d45a5a79b2e5d0a26/', |
| 911 'name': 'DarkTheme' | 1022 'name': 'DarkTheme' |
| 912 }) | 1023 }) |
| 913 | 1024 |
| 914 GALAXY_INFRASTRUCTURE_URL = root.find('metadata/galaxyUrl').text | 1025 if plugins['BlastView'] == 'True': |
| 915 # Sometimes this comes as `localhost` without a protocol | 1026 extra_data['plugins_python'].append('BlastView') |
| 916 if not GALAXY_INFRASTRUCTURE_URL.startswith('http'): | 1027 extra_data['plugins'].append({ |
| 917 # so we'll prepend `http://` and hope for the best. Requests *should* | 1028 'location': 'https://cdn.jsdelivr.net/gh/TAMU-CPT/blastview@97572a21b7f011c2b4d9a0b5af40e292d694cbef/', |
| 918 # be GET and not POST so it should redirect OK | 1029 'name': 'BlastView' |
| 919 GALAXY_INFRASTRUCTURE_URL = 'http://' + GALAXY_INFRASTRUCTURE_URL | 1030 }) |
| 920 | 1031 |
| 921 for track in root.findall('tracks/track'): | 1032 for track in root.findall('tracks/track'): |
| 922 track_conf = {} | 1033 track_conf = {} |
| 923 track_conf['trackfiles'] = [] | 1034 track_conf['trackfiles'] = [] |
| 924 | 1035 |
| 925 for x in track.findall('files/trackFile'): | 1036 is_multi_bigwig = False |
| 1037 try: | |
| 1038 if track.find('options/wiggle/multibigwig') and (track.find('options/wiggle/multibigwig').text == 'True'): | |
| 1039 is_multi_bigwig = True | |
| 1040 multi_bigwig_paths = [] | |
| 1041 except KeyError: | |
| 1042 pass | |
| 1043 | |
| 1044 trackfiles = track.findall('files/trackFile') | |
| 1045 if trackfiles: | |
| 1046 for x in track.findall('files/trackFile'): | |
| 1047 if is_multi_bigwig: | |
| 1048 multi_bigwig_paths.append((x.attrib['label'], os.path.realpath(x.attrib['path']))) | |
| 1049 else: | |
| 1050 if trackfiles: | |
| 1051 metadata = metadata_from_node(x.find('metadata')) | |
| 1052 | |
| 1053 track_conf['trackfiles'].append(( | |
| 1054 os.path.realpath(x.attrib['path']), | |
| 1055 x.attrib['ext'], | |
| 1056 x.attrib['label'], | |
| 1057 metadata | |
| 1058 )) | |
| 1059 else: | |
| 1060 # For tracks without files (rest, sparql) | |
| 1061 track_conf['trackfiles'].append(( | |
| 1062 '', # N/A, no path for rest or sparql | |
| 1063 track.attrib['format'], | |
| 1064 track.find('options/label').text, | |
| 1065 {} | |
| 1066 )) | |
| 1067 | |
| 1068 if is_multi_bigwig: | |
| 926 metadata = metadata_from_node(x.find('metadata')) | 1069 metadata = metadata_from_node(x.find('metadata')) |
| 927 | 1070 |
| 928 track_conf['trackfiles'].append(( | 1071 track_conf['trackfiles'].append(( |
| 929 os.path.realpath(x.attrib['path']), | 1072 multi_bigwig_paths, # Passing an array of paths to represent as one track |
| 930 x.attrib['ext'], | 1073 'bigwig_multiple', |
| 931 x.attrib['label'], | 1074 'MultiBigWig', # Giving an hardcoded name for now |
| 932 metadata | 1075 {} # No metadata for multiple bigwig |
| 933 )) | 1076 )) |
| 934 | 1077 |
| 935 track_conf['category'] = track.attrib['cat'] | 1078 track_conf['category'] = track.attrib['cat'] |
| 936 track_conf['format'] = track.attrib['format'] | 1079 track_conf['format'] = track.attrib['format'] |
| 937 try: | 1080 try: |
| 938 # Only pertains to gff3 + blastxml. TODO? | 1081 # Only pertains to gff3 + blastxml. TODO? |
| 939 track_conf['style'] = {t.tag: t.text for t in track.find('options/style')} | 1082 track_conf['style'] = {t.tag: t.text for t in track.find('options/style')} |
| 940 except TypeError as te: | 1083 except TypeError: |
| 941 track_conf['style'] = {} | 1084 track_conf['style'] = {} |
| 942 pass | 1085 pass |
| 943 track_conf['conf'] = etree_to_dict(track.find('options')) | 1086 track_conf['conf'] = etree_to_dict(track.find('options')) |
| 944 keys = jc.process_annotations(track_conf) | 1087 keys = jc.process_annotations(track_conf) |
| 945 | 1088 |
