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()