0
|
1 #!/usr/bin/env python
|
|
2 import argparse
|
|
3 import os
|
|
4
|
9
|
5 import extract_genomic_dna_utils as egdu
|
0
|
6 import bx.seq.nib
|
|
7 import bx.seq.twobit
|
|
8 from bx.intervals.io import Header, Comment
|
|
9
|
|
10
|
|
11 parser = argparse.ArgumentParser()
|
3
|
12 parser.add_argument('--input_format', dest='input_format', help="Input dataset format")
|
|
13 parser.add_argument('--input', dest='input', help="Input dataset")
|
|
14 parser.add_argument('--genome', dest='genome', help="Input dataset genome build")
|
|
15 parser.add_argument('--interpret_features', dest='interpret_features', default=None, help="Interpret features if input format is gff")
|
|
16 parser.add_argument('--columns', dest='columns', help="Columns to use in input file")
|
|
17 parser.add_argument('--reference_genome_source', dest='reference_genome_source', help="Source of reference genome file")
|
|
18 parser.add_argument('--reference_genome', dest='reference_genome', help="Reference genome file")
|
|
19 parser.add_argument('--output_format', dest='output_format', help="Output format")
|
14
|
20 parser.add_argument('--description_field_delimiter', dest='description_field_delimiter', default=None, help="Fasta description field delimiter")
|
3
|
21 parser.add_argument('--output', dest='output', help="Output dataset")
|
0
|
22 args = parser.parse_args()
|
|
23
|
|
24 input_is_gff = args.input_format == 'gff'
|
1
|
25 interpret_features = input_is_gff and args.interpret_features == "yes"
|
4
|
26 if len(args.columns.split(',')) == 5:
|
0
|
27 # Bed file.
|
9
|
28 chrom_col, start_col, end_col, strand_col, name_col = egdu.parse_cols_arg(args.columns)
|
0
|
29 else:
|
|
30 # Gff file.
|
9
|
31 chrom_col, start_col, end_col, strand_col = egdu.parse_cols_arg(args.columns)
|
0
|
32 name_col = False
|
|
33
|
|
34 if args.reference_genome_source == "history":
|
9
|
35 seq_path = egdu.convert_to_twobit(args.reference_genome)
|
0
|
36 else:
|
|
37 seq_path = args.reference_genome
|
|
38 seq_dir = os.path.split(seq_path)[0]
|
|
39
|
|
40 includes_strand_col = strand_col >= 0
|
|
41 strand = None
|
|
42 nibs = {}
|
|
43 skipped_lines = 0
|
|
44 first_invalid_line = 0
|
|
45 invalid_lines = []
|
|
46 warnings = []
|
|
47 warning = ''
|
|
48 twobitfile = None
|
|
49 line_count = 1
|
|
50 file_iterator = open(args.input)
|
|
51 if interpret_features:
|
9
|
52 file_iterator = egdu.GFFReaderWrapper(file_iterator, fix_strand=False)
|
0
|
53 out = open(args.output, 'wt')
|
|
54
|
|
55 for feature in file_iterator:
|
|
56 # Ignore comments, headers.
|
|
57 if isinstance(feature, (Header, Comment)):
|
|
58 line_count += 1
|
|
59 continue
|
|
60 name = ""
|
|
61 if interpret_features:
|
|
62 # Processing features.
|
9
|
63 egdu.convert_gff_coords_to_bed(feature)
|
0
|
64 chrom = feature.chrom
|
|
65 start = feature.start
|
|
66 end = feature.end
|
|
67 strand = feature.strand
|
|
68 else:
|
|
69 # Processing lines, either interval or GFF format.
|
|
70 line = feature.rstrip('\r\n')
|
|
71 if line and not line.startswith("#"):
|
|
72 fields = line.split('\t')
|
|
73 try:
|
|
74 chrom = fields[chrom_col]
|
|
75 start = int(fields[start_col])
|
|
76 end = int(fields[end_col])
|
|
77 if name_col:
|
|
78 name = fields[name_col]
|
|
79 if input_is_gff:
|
9
|
80 start, end = egdu.convert_gff_coords_to_bed([start, end])
|
0
|
81 if includes_strand_col:
|
|
82 strand = fields[strand_col]
|
|
83 except:
|
|
84 warning = "Invalid chrom, start or end column values. "
|
|
85 warnings.append(warning)
|
|
86 if not invalid_lines:
|
9
|
87 invalid_lines = egdu.get_lines(feature)
|
0
|
88 first_invalid_line = line_count
|
|
89 skipped_lines += len(invalid_lines)
|
|
90 continue
|
|
91 if start > end:
|
|
92 warning = "Invalid interval, start '%d' > end '%d'. " % (start, end)
|
|
93 warnings.append(warning)
|
|
94 if not invalid_lines:
|
9
|
95 invalid_lines = egdu.get_lines(feature)
|
0
|
96 first_invalid_line = line_count
|
|
97 skipped_lines += len(invalid_lines)
|
|
98 continue
|
|
99 if strand not in ['+', '-']:
|
|
100 strand = '+'
|
|
101 sequence = ''
|
|
102 else:
|
|
103 continue
|
|
104 # Open sequence file and get sequence for feature/interval.
|
|
105 if os.path.exists("%s/%s.nib" % (seq_dir, chrom)):
|
|
106 if chrom in nibs:
|
|
107 nib = nibs[chrom]
|
|
108 else:
|
9
|
109 nibs[chrom] = nib = bx.seq.nib.NibFile(open("%s/%s.nib" % (seq_path, chrom)))
|
0
|
110 try:
|
|
111 sequence = nib.get(start, end - start)
|
|
112 except Exception, e:
|
1
|
113 warning = "Unable to fetch the sequence from '%d' to '%d' for build '%s'. " % (start, end - start, args.genome)
|
0
|
114 warnings.append(warning)
|
|
115 if not invalid_lines:
|
9
|
116 invalid_lines = egdu.get_lines(feature)
|
0
|
117 first_invalid_line = line_count
|
|
118 skipped_lines += len(invalid_lines)
|
|
119 continue
|
5
|
120 elif os.path.isfile(seq_path):
|
0
|
121 if not(twobitfile):
|
9
|
122 twobitfile = bx.seq.twobit.TwoBitFile(open(seq_path))
|
0
|
123 try:
|
|
124 if interpret_features:
|
|
125 # Create sequence from intervals within a feature.
|
|
126 sequence = ''
|
|
127 for interval in feature.intervals:
|
|
128 sequence += twobitfile[interval.chrom][interval.start:interval.end]
|
|
129 else:
|
|
130 sequence = twobitfile[chrom][start:end]
|
|
131 except:
|
|
132 warning = "Unable to fetch the sequence from '%d' to '%d' for chrom '%s'. " % (start, end - start, chrom)
|
|
133 warnings.append(warning)
|
|
134 if not invalid_lines:
|
9
|
135 invalid_lines = egdu.get_lines(feature)
|
0
|
136 first_invalid_line = line_count
|
|
137 skipped_lines += len(invalid_lines)
|
|
138 continue
|
|
139 else:
|
1
|
140 warning = "Chromosome by name '%s' was not found for build '%s'. " % (chrom, args.genome)
|
0
|
141 warnings.append(warning)
|
|
142 if not invalid_lines:
|
9
|
143 invalid_lines = egdu.get_lines(feature)
|
0
|
144 first_invalid_line = line_count
|
|
145 skipped_lines += len(invalid_lines)
|
|
146 continue
|
|
147 if sequence == '':
|
1
|
148 warning = "Chrom: '%s', start: '%d', end: '%d' is either invalid or not present in build '%s'. " % (chrom, start, end, args.genome)
|
0
|
149 warnings.append(warning)
|
|
150 if not invalid_lines:
|
9
|
151 invalid_lines = egdu.get_lines(feature)
|
0
|
152 first_invalid_line = line_count
|
|
153 skipped_lines += len(invalid_lines)
|
|
154 continue
|
|
155 if includes_strand_col and strand == "-":
|
9
|
156 sequence = egdu.reverse_complement(sequence)
|
0
|
157 if args.output_format == "fasta":
|
|
158 l = len(sequence)
|
|
159 c = 0
|
|
160 if input_is_gff:
|
9
|
161 start, end = egdu.convert_bed_coords_to_gff([start, end])
|
1
|
162 fields = [args.genome, str(chrom), str(start), str(end), strand]
|
14
|
163 field_delimiter = extract_genomic_dna_utils.get_description_field_delimiter(args.description_field_delimiter)
|
|
164 meta_data = field_delimiter.join(fields)
|
0
|
165 if name.strip():
|
|
166 out.write(">%s %s\n" % (meta_data, name))
|
|
167 else:
|
|
168 out.write(">%s\n" % meta_data)
|
|
169 while c < l:
|
|
170 b = min(c + 50, l)
|
|
171 out.write("%s\n" % str(sequence[c:b]))
|
|
172 c = b
|
|
173 else:
|
|
174 # output_format == "interval".
|
|
175 if interpret_features:
|
|
176 meta_data = "\t".join([feature.chrom,
|
|
177 "galaxy_extract_genomic_dna",
|
|
178 "interval",
|
|
179 str(feature.start),
|
|
180 str(feature.end),
|
|
181 feature.score,
|
|
182 feature.strand,
|
|
183 ".",
|
9
|
184 egdu.gff_attributes_to_str(feature.attributes, "GTF")])
|
0
|
185 else:
|
|
186 # Where is fields being set here?
|
|
187 meta_data = "\t".join(fields)
|
|
188 if input_is_gff:
|
|
189 format_str = "%s seq \"%s\";\n"
|
|
190 else:
|
|
191 format_str = "%s\t%s\n"
|
|
192 out.write(format_str % (meta_data, str(sequence)))
|
|
193 # Update line count.
|
9
|
194 if isinstance(feature, egdu.GFFFeature):
|
0
|
195 line_count += len(feature.intervals)
|
|
196 else:
|
|
197 line_count += 1
|
|
198 out.close()
|
|
199
|
|
200 if warnings:
|
|
201 warn_msg = "%d warnings, 1st is: " % len(warnings)
|
|
202 warn_msg += warnings[0]
|
|
203 print warn_msg
|
|
204 if skipped_lines:
|
|
205 # Error message includes up to the first 10 skipped lines.
|
|
206 print 'Skipped %d invalid lines, 1st is #%d, "%s"' % (skipped_lines, first_invalid_line, '\n'.join(invalid_lines[:10]))
|
|
207
|
|
208 if args.reference_genome_source == "history":
|
|
209 os.remove(seq_path)
|