Mercurial > repos > iuc > jbrowse
comparison jbrowse.py @ 8:ad4b9d7eae6a draft
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/jbrowse commit 9a243c616a4a3156347e38fdb5f35863ae5133f9
author | iuc |
---|---|
date | Tue, 29 Nov 2016 10:55:30 -0500 |
parents | ae9382cfb6ac |
children | 1a6d882d340d |
comparison
equal
deleted
inserted
replaced
7:1e74f16adaa1 | 8:ad4b9d7eae6a |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 import argparse | |
3 import copy | |
4 import hashlib | |
5 import json | |
6 import logging | |
2 import os | 7 import os |
3 import copy | 8 import shutil |
4 import argparse | 9 import struct |
5 import subprocess | 10 import subprocess |
6 import hashlib | |
7 import struct | |
8 import tempfile | 11 import tempfile |
9 import shutil | 12 import xml.etree.ElementTree as ET |
10 import json | 13 from collections import defaultdict |
14 | |
11 from Bio.Data import CodonTable | 15 from Bio.Data import CodonTable |
12 import xml.etree.ElementTree as ET | 16 |
13 import logging | |
14 from collections import defaultdict | |
15 logging.basicConfig(level=logging.INFO) | 17 logging.basicConfig(level=logging.INFO) |
16 log = logging.getLogger('jbrowse') | 18 log = logging.getLogger('jbrowse') |
17 | 19 |
18 | 20 |
19 class ColorScaling(object): | 21 class ColorScaling(object): |
77 var opacity = (score - ({min})) / (({max}) - ({min})); | 79 var opacity = (score - ({min})) / (({max}) - ({min})); |
78 opacity = Math.log10(opacity) + Math.log10({max}); | 80 opacity = Math.log10(opacity) + Math.log10({max}); |
79 """, | 81 """, |
80 'blast': """ | 82 'blast': """ |
81 var opacity = 0; | 83 var opacity = 0; |
82 if(score == 0.0) {{ | 84 if(score == 0.0) { |
83 opacity = 1; | 85 opacity = 1; |
84 }} else {{ | 86 } else{ |
85 opacity = (20 - Math.log10(score)) / 180; | 87 opacity = (20 - Math.log10(score)) / 180; |
86 }} | 88 } |
87 """ | 89 """ |
88 } | 90 } |
89 | 91 |
90 BREWER_COLOUR_IDX = 0 | 92 BREWER_COLOUR_IDX = 0 |
91 BREWER_COLOUR_SCHEMES = [ | 93 BREWER_COLOUR_SCHEMES = [ |
123 def __init__(self): | 125 def __init__(self): |
124 self.brewer_colour_idx = 0 | 126 self.brewer_colour_idx = 0 |
125 | 127 |
126 def rgb_from_hex(self, hexstr): | 128 def rgb_from_hex(self, hexstr): |
127 # http://stackoverflow.com/questions/4296249/how-do-i-convert-a-hex-triplet-to-an-rgb-tuple-and-back | 129 # http://stackoverflow.com/questions/4296249/how-do-i-convert-a-hex-triplet-to-an-rgb-tuple-and-back |
128 return struct.unpack('BBB',hexstr.decode('hex')) | 130 return struct.unpack('BBB', hexstr.decode('hex')) |
129 | 131 |
130 def min_max_gff(self, gff_file): | 132 def min_max_gff(self, gff_file): |
131 min_val = None | 133 min_val = None |
132 max_val = None | 134 max_val = None |
133 with open(gff_file, 'r') as handle: | 135 with open(gff_file, 'r') as handle: |
152 def _get_colours(self): | 154 def _get_colours(self): |
153 r, g, b = self.BREWER_COLOUR_SCHEMES[self.brewer_colour_idx % len(self.BREWER_COLOUR_SCHEMES)] | 155 r, g, b = self.BREWER_COLOUR_SCHEMES[self.brewer_colour_idx % len(self.BREWER_COLOUR_SCHEMES)] |
154 self.brewer_colour_idx += 1 | 156 self.brewer_colour_idx += 1 |
155 return r, g, b | 157 return r, g, b |
156 | 158 |
159 def parse_menus(self, track): | |
160 trackConfig = {'menuTemplate': [{}, {}, {}]} | |
161 | |
162 if 'menu' in track['menus']: | |
163 menu_list = [track['menus']['menu']] | |
164 if isinstance(track['menus']['menu'], list): | |
165 menu_list = track['menus']['menu'] | |
166 | |
167 for m in menu_list: | |
168 tpl = { | |
169 'action': m['action'], | |
170 'label': m.get('label', '{name}'), | |
171 'iconClass': m.get('iconClass', 'dijitIconBookmark'), | |
172 } | |
173 if 'url' in m: | |
174 tpl['url'] = m['url'] | |
175 if 'content' in m: | |
176 tpl['content'] = m['content'] | |
177 if 'title' in m: | |
178 tpl['title'] = m['title'] | |
179 | |
180 trackConfig['menuTemplate'].append(tpl) | |
181 | |
182 return trackConfig | |
183 | |
157 def parse_colours(self, track, trackFormat, gff3=None): | 184 def parse_colours(self, track, trackFormat, gff3=None): |
158 # Wiggle tracks have a bicolor pallete | 185 # Wiggle tracks have a bicolor pallete |
159 trackConfig = {'style': {}} | 186 trackConfig = {'style': {}} |
160 if trackFormat == 'wiggle': | 187 if trackFormat == 'wiggle': |
161 | 188 |
163 trackConfig['style']['neg_color'] = track['wiggle']['color_neg'] | 190 trackConfig['style']['neg_color'] = track['wiggle']['color_neg'] |
164 | 191 |
165 if trackConfig['style']['pos_color'] == '__auto__': | 192 if trackConfig['style']['pos_color'] == '__auto__': |
166 trackConfig['style']['neg_color'] = self.hex_from_rgb(*self._get_colours()) | 193 trackConfig['style']['neg_color'] = self.hex_from_rgb(*self._get_colours()) |
167 trackConfig['style']['pos_color'] = self.hex_from_rgb(*self._get_colours()) | 194 trackConfig['style']['pos_color'] = self.hex_from_rgb(*self._get_colours()) |
168 | |
169 | 195 |
170 # Wiggle tracks can change colour at a specified place | 196 # Wiggle tracks can change colour at a specified place |
171 bc_pivot = track['wiggle']['bicolor_pivot'] | 197 bc_pivot = track['wiggle']['bicolor_pivot'] |
172 if bc_pivot not in ('mean', 'zero'): | 198 if bc_pivot not in ('mean', 'zero'): |
173 # The values are either one of those two strings | 199 # The values are either one of those two strings |
221 else: | 247 else: |
222 user_color = 'undefined' | 248 user_color = 'undefined' |
223 auto_color = "'%s'" % self.hex_from_rgb(*self._get_colours()) | 249 auto_color = "'%s'" % self.hex_from_rgb(*self._get_colours()) |
224 | 250 |
225 color_function = self.COLOR_FUNCTION_TEMPLATE_QUAL.format(**{ | 251 color_function = self.COLOR_FUNCTION_TEMPLATE_QUAL.format(**{ |
226 'opacity': self.OPACITY_MATH[algo].format(**{'max': max_val,'min': min_val}), | 252 'opacity': self.OPACITY_MATH[algo].format(**{'max': max_val, 'min': min_val}), |
227 'user_spec_color': user_color, | 253 'user_spec_color': user_color, |
228 'auto_gen_color': auto_color, | 254 'auto_gen_color': auto_color, |
229 }) | 255 }) |
230 | 256 |
231 trackConfig['style']['color'] = color_function.replace('\n', '') | 257 trackConfig['style']['color'] = color_function.replace('\n', '') |
238 if children: | 264 if children: |
239 dd = defaultdict(list) | 265 dd = defaultdict(list) |
240 for dc in map(etree_to_dict, children): | 266 for dc in map(etree_to_dict, children): |
241 for k, v in dc.iteritems(): | 267 for k, v in dc.iteritems(): |
242 dd[k].append(v) | 268 dd[k].append(v) |
243 d = {t.tag: {k:v[0] if len(v) == 1 else v for k, v in dd.iteritems()}} | 269 d = {t.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.iteritems()}} |
244 if t.attrib: | 270 if t.attrib: |
245 d[t.tag].update(('@' + k, v) for k, v in t.attrib.iteritems()) | 271 d[t.tag].update(('@' + k, v) for k, v in t.attrib.iteritems()) |
246 if t.text: | 272 if t.text: |
247 text = t.text.strip() | 273 text = t.text.strip() |
248 if children or t.attrib: | 274 if children or t.attrib: |
249 if text: | 275 if text: |
250 d[t.tag]['#text'] = text | 276 d[t.tag]['#text'] = text |
251 else: | 277 else: |
252 d[t.tag] = text | 278 d[t.tag] = text |
253 return d | 279 return d |
254 | 280 |
255 | 281 |
272 self.jbrowse = jbrowse | 298 self.jbrowse = jbrowse |
273 self.outdir = outdir | 299 self.outdir = outdir |
274 self.genome_paths = genomes | 300 self.genome_paths = genomes |
275 self.standalone = standalone | 301 self.standalone = standalone |
276 self.gencode = gencode | 302 self.gencode = gencode |
303 self.tracksToIndex = [] | |
277 | 304 |
278 if standalone: | 305 if standalone: |
279 self.clone_jbrowse(self.jbrowse, self.outdir) | 306 self.clone_jbrowse(self.jbrowse, self.outdir) |
280 else: | 307 else: |
281 try: | 308 try: |
300 }) | 327 }) |
301 | 328 |
302 with open(trackList, 'w') as handle: | 329 with open(trackList, 'w') as handle: |
303 json.dump(trackListData, handle, indent=2) | 330 json.dump(trackListData, handle, indent=2) |
304 | 331 |
305 | |
306 def subprocess_check_call(self, command): | 332 def subprocess_check_call(self, command): |
307 log.debug('cd %s && %s', self.outdir, ' '.join(command)) | 333 log.debug('cd %s && %s', self.outdir, ' '.join(command)) |
308 subprocess.check_call(command, cwd=self.outdir) | 334 subprocess.check_call(command, cwd=self.outdir) |
309 | 335 |
310 def _jbrowse_bin(self, command): | 336 def _jbrowse_bin(self, command): |
314 for genome_path in self.genome_paths: | 340 for genome_path in self.genome_paths: |
315 self.subprocess_check_call([ | 341 self.subprocess_check_call([ |
316 'perl', self._jbrowse_bin('prepare-refseqs.pl'), | 342 'perl', self._jbrowse_bin('prepare-refseqs.pl'), |
317 '--fasta', genome_path]) | 343 '--fasta', genome_path]) |
318 | 344 |
319 # Generate name | 345 def generate_names(self): |
320 # self.subprocess_check_call([ | 346 # Generate names |
321 # 'perl', self._jbrowse_bin('generate-names.pl'), | 347 |
322 # '--hashBits', '16' | 348 args = [ |
323 # ]) | 349 'perl', self._jbrowse_bin('generate-names.pl'), |
350 '--hashBits', '16' | |
351 ] | |
352 | |
353 tracks = ','.join(self.tracksToIndex) | |
354 | |
355 if tracks: | |
356 args += ['--tracks', tracks] | |
357 else: | |
358 # No tracks to index, index only the refseq | |
359 args += ['--tracks', 'DNA'] | |
360 | |
361 self.subprocess_check_call(args) | |
324 | 362 |
325 def _add_json(self, json_data): | 363 def _add_json(self, json_data): |
326 | 364 |
327 cmd = [ | 365 cmd = [ |
328 'perl', self._jbrowse_bin('add-json.pl'), | 366 'perl', self._jbrowse_bin('add-json.pl'), |
340 tmp.close() | 378 tmp.close() |
341 cmd = ['perl', self._jbrowse_bin('add-track-json.pl'), tmp.name, | 379 cmd = ['perl', self._jbrowse_bin('add-track-json.pl'), tmp.name, |
342 os.path.join('data', 'trackList.json')] | 380 os.path.join('data', 'trackList.json')] |
343 self.subprocess_check_call(cmd) | 381 self.subprocess_check_call(cmd) |
344 os.unlink(tmp.name) | 382 os.unlink(tmp.name) |
345 | |
346 | 383 |
347 def _blastxml_to_gff3(self, xml, min_gap=10): | 384 def _blastxml_to_gff3(self, xml, min_gap=10): |
348 gff3_unrebased = tempfile.NamedTemporaryFile(delete=False) | 385 gff3_unrebased = tempfile.NamedTemporaryFile(delete=False) |
349 cmd = ['python', os.path.join(INSTALLED_TO, 'blastxml_to_gapped_gff3.py'), | 386 cmd = ['python', os.path.join(INSTALLED_TO, 'blastxml_to_gapped_gff3.py'), |
350 '--trim', '--trim_end', '--min_gap', str(min_gap), xml] | 387 '--trim', '--trim_end', '--min_gap', str(min_gap), xml] |
387 ] | 424 ] |
388 | 425 |
389 self.subprocess_check_call(cmd) | 426 self.subprocess_check_call(cmd) |
390 os.unlink(gff3) | 427 os.unlink(gff3) |
391 | 428 |
429 if blastOpts.get('index', 'false') == 'true': | |
430 self.tracksToIndex.append("%s" % trackData['label']) | |
431 | |
392 def add_bigwig(self, data, trackData, wiggleOpts, **kwargs): | 432 def add_bigwig(self, data, trackData, wiggleOpts, **kwargs): |
393 dest = os.path.join('data', 'raw', trackData['label'] + '.bw') | 433 dest = os.path.join('data', 'raw', trackData['label'] + '.bw') |
394 cmd = ['ln', data, dest] | 434 cmd = ['ln', data, dest] |
395 self.subprocess_check_call(cmd) | 435 self.subprocess_check_call(cmd) |
396 | 436 |
423 "urlTemplate": os.path.join('..', dest), | 463 "urlTemplate": os.path.join('..', dest), |
424 "type": "JBrowse/View/Track/Alignments2", | 464 "type": "JBrowse/View/Track/Alignments2", |
425 "storeClass": "JBrowse/Store/SeqFeature/BAM", | 465 "storeClass": "JBrowse/Store/SeqFeature/BAM", |
426 }) | 466 }) |
427 | 467 |
428 | |
429 self._add_track_json(trackData) | 468 self._add_track_json(trackData) |
430 | 469 |
431 if bamOpts.get('auto_snp', 'false') == 'true': | 470 if bamOpts.get('auto_snp', 'false') == 'true': |
432 trackData2 = copy.copy(trackData) | 471 trackData2 = copy.copy(trackData) |
433 trackData2.update({ | 472 trackData2.update({ |
434 "type": "JBrowse/View/Track/SNPCoverage", | 473 "type": "JBrowse/View/Track/SNPCoverage", |
435 "key": trackData['key'] + " - SNPs/Coverage", | 474 "key": trackData['key'] + " - SNPs/Coverage", |
436 "label": trackData['label'] + "_autosnp", | 475 "label": trackData['label'] + "_autosnp", |
437 }) | 476 }) |
438 self._add_track_json(trackData2) | 477 self._add_track_json(trackData2) |
439 | 478 |
440 def add_vcf(self, data, trackData, vcfOpts={}, **kwargs): | 479 def add_vcf(self, data, trackData, vcfOpts={}, **kwargs): |
441 dest = os.path.join('data', 'raw', trackData['label'] + '.vcf') | 480 dest = os.path.join('data', 'raw', trackData['label'] + '.vcf') |
466 | 505 |
467 config = copy.copy(trackData) | 506 config = copy.copy(trackData) |
468 clientConfig = trackData['style'] | 507 clientConfig = trackData['style'] |
469 del config['style'] | 508 del config['style'] |
470 | 509 |
471 if 'match' in gffOpts: | 510 if 'match' in gffOpts: |
472 config['glyph'] = 'JBrowse/View/FeatureGlyph/Segments' | 511 config['glyph'] = 'JBrowse/View/FeatureGlyph/Segments' |
473 cmd += ['--type', gffOpts['match']] | 512 cmd += ['--type', gffOpts['match']] |
474 | 513 |
475 cmd += ['--clientConfig', json.dumps(clientConfig), | 514 cmd += ['--clientConfig', json.dumps(clientConfig), |
476 ] | 515 ] |
486 | 525 |
487 cmd.extend(['--config', json.dumps(config)]) | 526 cmd.extend(['--config', json.dumps(config)]) |
488 | 527 |
489 self.subprocess_check_call(cmd) | 528 self.subprocess_check_call(cmd) |
490 | 529 |
530 if gffOpts.get('index', 'false') == 'true': | |
531 self.tracksToIndex.append("%s" % trackData['label']) | |
491 | 532 |
492 def process_annotations(self, track): | 533 def process_annotations(self, track): |
493 outputTrackConfig = { | 534 outputTrackConfig = { |
494 'style': { | 535 'style': { |
495 'label': track['style'].get('label', 'description'), | 536 'label': track['style'].get('label', 'description'), |
496 'className': track['style'].get('className', 'feature'), | 537 'className': track['style'].get('className', 'feature'), |
497 'description': track['style'].get('description', ''), | 538 'description': track['style'].get('description', ''), |
498 }, | 539 }, |
499 'category': track['category'], | 540 'category': track['category'], |
500 } | 541 } |
501 | 542 |
514 for subkey in colourOptions['style']: | 555 for subkey in colourOptions['style']: |
515 outputTrackConfig['style'][subkey] = colourOptions['style'][subkey] | 556 outputTrackConfig['style'][subkey] = colourOptions['style'][subkey] |
516 else: | 557 else: |
517 outputTrackConfig[key] = colourOptions[key] | 558 outputTrackConfig[key] = colourOptions[key] |
518 | 559 |
560 menus = self.cs.parse_menus(track['conf']['options']) | |
561 outputTrackConfig.update(menus) | |
562 | |
519 # import pprint; pprint.pprint(track) | 563 # import pprint; pprint.pprint(track) |
520 # import sys; sys.exit() | 564 # import sys; sys.exit() |
521 if dataset_ext in ('gff', 'gff3', 'bed'): | 565 if dataset_ext in ('gff', 'gff3', 'bed'): |
522 self.add_features(dataset_path, dataset_ext, outputTrackConfig, | 566 self.add_features(dataset_path, dataset_ext, outputTrackConfig, |
523 track['conf']['options']['gff']) | 567 track['conf']['options']['gff']) |
524 elif dataset_ext == 'bigwig': | 568 elif dataset_ext == 'bigwig': |
525 self.add_bigwig(dataset_path, outputTrackConfig, | 569 self.add_bigwig(dataset_path, outputTrackConfig, |
526 track['conf']['options']['wiggle']) | 570 track['conf']['options']['wiggle']) |
527 elif dataset_ext == 'bam': | 571 elif dataset_ext == 'bam': |
528 real_indexes = track['conf']['options']['pileup']['bam_indices']['bam_index'] | 572 real_indexes = track['conf']['options']['pileup']['bam_indices']['bam_index'] |
643 track_conf['category'] = track.attrib['cat'] | 687 track_conf['category'] = track.attrib['cat'] |
644 track_conf['format'] = track.attrib['format'] | 688 track_conf['format'] = track.attrib['format'] |
645 try: | 689 try: |
646 # Only pertains to gff3 + blastxml. TODO? | 690 # Only pertains to gff3 + blastxml. TODO? |
647 track_conf['style'] = {t.tag: t.text for t in track.find('options/style')} | 691 track_conf['style'] = {t.tag: t.text for t in track.find('options/style')} |
648 except TypeError, te: | 692 except TypeError: |
649 track_conf['style'] = {} | 693 track_conf['style'] = {} |
650 pass | 694 pass |
651 track_conf['conf'] = etree_to_dict(track.find('options')) | 695 track_conf['conf'] = etree_to_dict(track.find('options')) |
652 keys = jc.process_annotations(track_conf) | 696 keys = jc.process_annotations(track_conf) |
653 | 697 |
654 | |
655 for key in keys: | 698 for key in keys: |
656 extra_data['visibility'][track.attrib.get('visibility', 'default_off')].append(key) | 699 extra_data['visibility'][track.attrib.get('visibility', 'default_off')].append(key) |
657 | 700 |
658 jc.add_final_data(extra_data) | 701 jc.add_final_data(extra_data) |
702 jc.generate_names() |