Mercurial > repos > jankanis > blast2html
comparison blast2html.py @ 53:4217bb9cf1d3
depend on python 3; fix internal links with multiple iterations
author | Jan Kanis <jan.code@jankanis.nl> |
---|---|
date | Mon, 26 May 2014 13:07:13 +0200 |
parents | b15a20c2372a |
children | 19c48f2ec775 |
comparison
equal
deleted
inserted
replaced
52:d6c7b5de2833 | 53:4217bb9cf1d3 |
---|---|
1 #!/usr/bin/env python3 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 # | |
4 # Actually runs under either python 2 or 3 | |
5 | 3 |
6 # Copyright The Hyve B.V. 2014 | 4 # Copyright The Hyve B.V. 2014 |
7 # License: GPL version 3 or higher | 5 # License: GPL version 3 or higher |
8 | 6 |
9 from __future__ import unicode_literals | 7 from __future__ import unicode_literals |
11 import sys | 9 import sys |
12 import math | 10 import math |
13 import warnings | 11 import warnings |
14 from os import path | 12 from os import path |
15 from itertools import repeat | 13 from itertools import repeat |
16 import six | |
17 import argparse | 14 import argparse |
18 from lxml import objectify | 15 from lxml import objectify |
19 import jinja2 | 16 import jinja2 |
20 | 17 |
21 | 18 |
22 | 19 |
23 _filters = {} | 20 _filters = {} |
24 def filter(func_or_name): | 21 def filter(func_or_name): |
25 "Decorator to register a function as filter in the current jinja environment" | 22 "Decorator to register a function as filter in the current jinja environment" |
26 if isinstance(func_or_name, six.string_types): | 23 if isinstance(func_or_name, str): |
27 def inner(func): | 24 def inner(func): |
28 _filters[func_or_name] = func.__name__ | 25 _filters[func_or_name] = func.__name__ |
29 return func | 26 return func |
30 return inner | 27 return inner |
31 else: | 28 else: |
97 if node.tag == 'Hsp': | 94 if node.tag == 'Hsp': |
98 return int(node['Hsp_align-len']) | 95 return int(node['Hsp_align-len']) |
99 elif node.tag == 'Iteration': | 96 elif node.tag == 'Iteration': |
100 return int(node['Iteration_query-len']) | 97 return int(node['Iteration_query-len']) |
101 raise Exception("Unknown XML node type: "+node.tag) | 98 raise Exception("Unknown XML node type: "+node.tag) |
102 | 99 |
103 | 100 @filter |
101 def nodeid(node): | |
102 id = [] | |
103 if node.tag == 'Hsp': | |
104 id.insert(0, node.Hsp_num.text) | |
105 node = node.getparent().getparent() | |
106 assert node.tag == 'Hit' | |
107 if node.tag == 'Hit': | |
108 id.insert(0, node.Hit_num.text) | |
109 node = node.getparent().getparent() | |
110 assert node.tag == 'Iteration' | |
111 if node.tag == 'Iteration': | |
112 id.insert(0, node['Iteration_iter-num'].text) | |
113 return '-'.join(id) | |
114 raise ValueError("The nodeid filter can only be applied to Hsp, Hit or Iteration nodes in a BlastXML document") | |
115 | |
116 | |
104 @filter | 117 @filter |
105 def asframe(frame): | 118 def asframe(frame): |
106 if frame == 1: | 119 if frame == 1: |
107 return 'Plus' | 120 return 'Plus' |
108 elif frame == -1: | 121 elif frame == -1: |
172 def __init__(self, input, templatedir, templatename): | 185 def __init__(self, input, templatedir, templatename): |
173 self.input = input | 186 self.input = input |
174 self.templatename = templatename | 187 self.templatename = templatename |
175 | 188 |
176 self.blast = objectify.parse(self.input).getroot() | 189 self.blast = objectify.parse(self.input).getroot() |
177 self.loader = jinja2.FileSystemLoader(searchpath=templatedir, encoding='utf-8') | 190 self.loader = jinja2.FileSystemLoader(searchpath=templatedir) |
178 self.environment = jinja2.Environment(loader=self.loader, | 191 self.environment = jinja2.Environment(loader=self.loader, |
179 lstrip_blocks=True, trim_blocks=True, autoescape=True) | 192 lstrip_blocks=True, trim_blocks=True, autoescape=True) |
180 | 193 |
181 self._addfilters(self.environment) | 194 self._addfilters(self.environment) |
182 | 195 |
236 matches.append((count * percent_multiplier, self.colors[last] if last != 255 else 'transparent')) | 249 matches.append((count * percent_multiplier, self.colors[last] if last != 255 else 'transparent')) |
237 last = table[i] | 250 last = table[i] |
238 count = 1 | 251 count = 1 |
239 matches.append((count * percent_multiplier, self.colors[last] if last != 255 else 'transparent')) | 252 matches.append((count * percent_multiplier, self.colors[last] if last != 255 else 'transparent')) |
240 | 253 |
241 yield dict(colors=matches, link="#hit"+hit.Hit_num.text, defline=firsttitle(hit)) | 254 yield dict(colors=matches, hit=hit, defline=firsttitle(hit)) |
242 | 255 |
243 @filter | 256 @filter |
244 def queryscale(self, result): | 257 def queryscale(self, result): |
245 query_length = blastxml_len(result) | 258 query_length = blastxml_len(result) |
246 skip = math.ceil(query_length / self.max_scale_labels) | 259 skip = math.ceil(query_length / self.max_scale_labels) |
267 | 280 |
268 def hsp_val(path): | 281 def hsp_val(path): |
269 return (float(hsp[path]) for hsp in hsps) | 282 return (float(hsp[path]) for hsp in hsps) |
270 | 283 |
271 yield dict(hit = hit, | 284 yield dict(hit = hit, |
272 title = firsttitle(hit), | 285 title = firsttitle(hit), |
273 link_id = hit.Hit_num, | |
274 maxscore = "{:.1f}".format(max(hsp_val('Hsp_bit-score'))), | 286 maxscore = "{:.1f}".format(max(hsp_val('Hsp_bit-score'))), |
275 totalscore = "{:.1f}".format(sum(hsp_val('Hsp_bit-score'))), | 287 totalscore = "{:.1f}".format(sum(hsp_val('Hsp_bit-score'))), |
276 cover = "{:.0%}".format(cover_count / query_length), | 288 cover = "{:.0%}".format(cover_count / query_length), |
277 e_value = "{:.4g}".format(min(hsp_val('Hsp_evalue'))), | 289 e_value = "{:.4g}".format(min(hsp_val('Hsp_evalue'))), |
278 # FIXME: is this the correct formula vv? | 290 # FIXME: is this the correct formula vv? |
286 parser = argparse.ArgumentParser(description="Convert a BLAST XML result into a nicely readable html page", | 298 parser = argparse.ArgumentParser(description="Convert a BLAST XML result into a nicely readable html page", |
287 usage="{} [-i] INPUT [-o OUTPUT]".format(sys.argv[0])) | 299 usage="{} [-i] INPUT [-o OUTPUT]".format(sys.argv[0])) |
288 input_group = parser.add_mutually_exclusive_group(required=True) | 300 input_group = parser.add_mutually_exclusive_group(required=True) |
289 input_group.add_argument('positional_arg', metavar='INPUT', nargs='?', type=argparse.FileType(mode='r'), | 301 input_group.add_argument('positional_arg', metavar='INPUT', nargs='?', type=argparse.FileType(mode='r'), |
290 help='The input Blast XML file, same as -i/--input') | 302 help='The input Blast XML file, same as -i/--input') |
291 input_group.add_argument('-i', '--input', type=argparse.FileType(mode='r', encoding='utf-8'), | 303 input_group.add_argument('-i', '--input', type=argparse.FileType(mode='r'), |
292 help='The input Blast XML file') | 304 help='The input Blast XML file') |
293 parser.add_argument('-o', '--output', type=argparse.FileType(mode='w', encoding='utf-8'), default=sys.stdout, | 305 parser.add_argument('-o', '--output', type=argparse.FileType(mode='w'), default=sys.stdout, |
294 help='The output html file') | 306 help='The output html file') |
295 # We just want the file name here, so jinja can open the file | 307 # We just want the file name here, so jinja can open the file |
296 # itself. But it is easier to just use a FileType so argparse can | 308 # itself. But it is easier to just use a FileType so argparse can |
297 # handle the errors. This introduces a small race condition when | 309 # handle the errors. This introduces a small race condition when |
298 # jinja later tries to re-open the template file, but we don't | 310 # jinja later tries to re-open the template file, but we don't |
299 # care too much. | 311 # care too much. |
300 parser.add_argument('--template', type=argparse.FileType(mode='r', encoding='utf-8'), default=default_template, | 312 parser.add_argument('--template', type=argparse.FileType(mode='r'), default=default_template, |
301 help='The template file to use. Defaults to blast_html.html.jinja') | 313 help='The template file to use. Defaults to blast_html.html.jinja') |
302 | 314 |
303 args = parser.parse_args() | 315 args = parser.parse_args() |
304 if args.input == None: | 316 if args.input == None: |
305 args.input = args.positional_arg | 317 args.input = args.positional_arg |