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