quorum 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -5
- data/.travis.yml +0 -2
- data/Gemfile.lock +2 -2
- data/HISTORY.md +12 -0
- data/Rakefile +13 -48
- data/app/models/quorum/blastn_job.rb +15 -22
- data/app/models/quorum/blastp_job.rb +15 -22
- data/app/models/quorum/blastx_job.rb +15 -22
- data/app/models/quorum/tblastn_job.rb +15 -22
- data/app/views/quorum/jobs/form/_blastn_form.html.erb +4 -4
- data/app/views/quorum/jobs/form/_blastp_form.html.erb +4 -4
- data/app/views/quorum/jobs/form/_blastx_form.html.erb +4 -4
- data/app/views/quorum/jobs/form/_tblastn_form.html.erb +4 -4
- data/app/views/quorum/jobs/templates/_blast_detailed_report_template.html.erb +6 -17
- data/db/migrate/20120918205556_rename_max_score_column.rb +15 -0
- data/lib/generators/templates/blast.rb +37 -31
- data/lib/quorum/version.rb +1 -1
- data/spec/config/quorum_settings.yml +55 -0
- data/spec/javascripts/fixtures/quorum_search_form.html +8 -8
- data/spec/javascripts/jobs_spec.js +8 -8
- data/spec/lib/tasks/travis.rake +120 -0
- data/spec/models/blastn_job_spec.rb +41 -22
- data/spec/models/blastp_job_spec.rb +41 -22
- data/spec/models/blastx_job_spec.rb +41 -22
- data/spec/models/job_spec.rb +1 -1
- data/spec/models/tblastn_job_spec.rb +41 -22
- data/spec/requests/jobs_spec.rb +8 -8
- data/spec/templates/blast_db_spec.rb +1 -1
- data/spec/templates/blast_spec.rb +1 -1
- metadata +7 -32
- data/spec/dummy/config/initializers/quorum_initializer.rb +0 -36
- data/spec/dummy/config/locales/quorum.en.yml +0 -8
- data/spec/dummy/config/quorum_settings.yml +0 -141
- data/spec/dummy/db/schema.rb +0 -213
- data/spec/dummy/quorum/bin/fetch +0 -115
- data/spec/dummy/quorum/bin/search +0 -141
- data/spec/dummy/quorum/blastdb/test/contigs.fa +0 -15
- data/spec/dummy/quorum/blastdb/test/peptides.fa +0 -4
- data/spec/dummy/quorum/blastdb/test.nhd +0 -2
- data/spec/dummy/quorum/blastdb/test.nhi +0 -0
- data/spec/dummy/quorum/blastdb/test.nhr +0 -0
- data/spec/dummy/quorum/blastdb/test.nin +0 -0
- data/spec/dummy/quorum/blastdb/test.nog +0 -0
- data/spec/dummy/quorum/blastdb/test.nsd +0 -2
- data/spec/dummy/quorum/blastdb/test.nsi +0 -0
- data/spec/dummy/quorum/blastdb/test.nsq +0 -0
- data/spec/dummy/quorum/blastdb/test.phd +0 -2
- data/spec/dummy/quorum/blastdb/test.phi +0 -0
- data/spec/dummy/quorum/blastdb/test.phr +0 -0
- data/spec/dummy/quorum/blastdb/test.pin +0 -0
- data/spec/dummy/quorum/blastdb/test.pog +0 -0
- data/spec/dummy/quorum/blastdb/test.psd +0 -2
- data/spec/dummy/quorum/blastdb/test.psi +0 -0
- data/spec/dummy/quorum/blastdb/test.psq +0 -0
- data/spec/dummy/quorum/lib/fetch_tools/blast_db.rb +0 -106
- data/spec/dummy/quorum/lib/logger.rb +0 -43
- data/spec/dummy/quorum/lib/search_tools/blast.rb +0 -452
- data/spec/dummy/quorum/lib/trollop.rb +0 -781
@@ -1,452 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.expand_path("../../", __FILE__))
|
2
|
-
|
3
|
-
require 'bio-blastxmlparser'
|
4
|
-
require 'logger'
|
5
|
-
|
6
|
-
module Quorum
|
7
|
-
module SearchTools
|
8
|
-
#
|
9
|
-
# Blast Search Tool
|
10
|
-
#
|
11
|
-
class Blast
|
12
|
-
|
13
|
-
class QuorumJob < ActiveRecord::Base
|
14
|
-
self.table_name = "quorum_jobs"
|
15
|
-
|
16
|
-
has_one :quorum_blastn_job,
|
17
|
-
:foreign_key => "job_id"
|
18
|
-
has_many :quorum_blastn_job_reports,
|
19
|
-
:foreign_key => "blastn_job_id"
|
20
|
-
|
21
|
-
has_one :quorum_blastx_job,
|
22
|
-
:foreign_key => "job_id"
|
23
|
-
has_many :quorum_blastx_job_reports,
|
24
|
-
:foreign_key => "blastx_job_id"
|
25
|
-
|
26
|
-
has_one :quorum_tblastn_job,
|
27
|
-
:foreign_key => "job_id"
|
28
|
-
has_many :quorum_tblastn_job_reports,
|
29
|
-
:foreign_key => "tblastn_job_id"
|
30
|
-
|
31
|
-
has_one :quorum_blastp_job,
|
32
|
-
:foreign_key => "job_id"
|
33
|
-
has_many :quorum_blastp_job_reports,
|
34
|
-
:foreign_key => "blastp_job_id"
|
35
|
-
end
|
36
|
-
|
37
|
-
class QuorumBlastnJob < ActiveRecord::Base
|
38
|
-
self.table_name = "quorum_blastn_jobs"
|
39
|
-
belongs_to :quorum_job
|
40
|
-
has_many :quorum_blastn_job_reports
|
41
|
-
end
|
42
|
-
|
43
|
-
class QuorumBlastnJobReport < ActiveRecord::Base
|
44
|
-
self.table_name = "quorum_blastn_job_reports"
|
45
|
-
belongs_to :quorum_blastn_job
|
46
|
-
end
|
47
|
-
|
48
|
-
class QuorumBlastxJob < ActiveRecord::Base
|
49
|
-
self.table_name = "quorum_blastx_jobs"
|
50
|
-
belongs_to :quorum_job
|
51
|
-
has_many :quorum_blastx_job_reports
|
52
|
-
end
|
53
|
-
|
54
|
-
class QuorumBlastxJobReport < ActiveRecord::Base
|
55
|
-
self.table_name = "quorum_blastx_job_reports"
|
56
|
-
belongs_to :quorum_blastx_job
|
57
|
-
end
|
58
|
-
|
59
|
-
class QuorumTblastnJob < ActiveRecord::Base
|
60
|
-
self.table_name = "quorum_tblastn_jobs"
|
61
|
-
belongs_to :quorum_job
|
62
|
-
has_many :quorum_tblastn_job_reports
|
63
|
-
end
|
64
|
-
|
65
|
-
class QuorumTblastnJobReport < ActiveRecord::Base
|
66
|
-
self.table_name = "quorum_tblastn_job_reports"
|
67
|
-
belongs_to :quorum_tblastn_job
|
68
|
-
end
|
69
|
-
|
70
|
-
class QuorumBlastpJob < ActiveRecord::Base
|
71
|
-
self.table_name = "quorum_blastp_jobs"
|
72
|
-
belongs_to :quorum_job
|
73
|
-
has_many :quorum_blastp_job_reports
|
74
|
-
end
|
75
|
-
|
76
|
-
class QuorumBlastpJobReport < ActiveRecord::Base
|
77
|
-
self.table_name = "quorum_blastp_job_reports"
|
78
|
-
belongs_to :quorum_blastp_job
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def initialize(args)
|
84
|
-
@algorithm = args[:search_tool]
|
85
|
-
@id = args[:id]
|
86
|
-
@log_directory = args[:log_directory]
|
87
|
-
@tmp = args[:tmp_directory]
|
88
|
-
@search_database = args[:search_database]
|
89
|
-
@threads = args[:threads]
|
90
|
-
|
91
|
-
@logger = Quorum::Logger.new(@log_directory)
|
92
|
-
|
93
|
-
begin
|
94
|
-
@job = QuorumJob.find(@id)
|
95
|
-
rescue Exception => e
|
96
|
-
@logger.log("ActiveRecord", e.message, 1)
|
97
|
-
end
|
98
|
-
|
99
|
-
@na_sequence = @job.na_sequence
|
100
|
-
@aa_sequence = @job.aa_sequence
|
101
|
-
|
102
|
-
## Create for method invocation ##
|
103
|
-
@job_association = "quorum_#{@algorithm}_job".to_sym
|
104
|
-
@job_report_association = "quorum_#{@algorithm}_job_reports".to_sym
|
105
|
-
|
106
|
-
@filter = @job.method(@job_association).call.filter
|
107
|
-
@expectation = @job.method(@job_association).call.expectation
|
108
|
-
@max_score = @job.method(@job_association).call.max_score
|
109
|
-
@min_score = @job.method(@job_association).call.min_bit_score
|
110
|
-
@gapped_alignments = @job.method(@job_association).call.gapped_alignments
|
111
|
-
@gap_opening_penalty = @job.method(@job_association).call.gap_opening_penalty
|
112
|
-
@gap_extension_penalty = @job.method(@job_association).call.gap_extension_penalty
|
113
|
-
|
114
|
-
@db = @job.method(@job_association).call.blast_dbs.split(';')
|
115
|
-
@db.map! { |d| File.join(@search_database, d) }
|
116
|
-
@db = @db.join(' ')
|
117
|
-
|
118
|
-
@hash = create_hash(@job.sequence)
|
119
|
-
@tmp_files = File.join(@tmp, @hash) << "*"
|
120
|
-
end
|
121
|
-
|
122
|
-
#
|
123
|
-
# Create a unique hash plus timestamp.
|
124
|
-
#
|
125
|
-
def create_hash(sequence)
|
126
|
-
Digest::MD5.hexdigest(sequence).to_s + "-" + Time.now.to_f.to_s
|
127
|
-
end
|
128
|
-
|
129
|
-
#
|
130
|
-
# Blastn command
|
131
|
-
#
|
132
|
-
def generate_blastn_cmd
|
133
|
-
blastn = "blastn " <<
|
134
|
-
"-db \"#{@db}\" " <<
|
135
|
-
"-query #{@na_fasta} " <<
|
136
|
-
"-outfmt 5 " <<
|
137
|
-
"-num_threads #{@threads} " <<
|
138
|
-
"-evalue #{@expectation} " <<
|
139
|
-
"-max_target_seqs #{@max_score} " <<
|
140
|
-
"-out #{@out} "
|
141
|
-
if @gapped_alignments
|
142
|
-
blastn << "-gapopen #{@gap_opening_penalty} "
|
143
|
-
blastn << "-gapextend #{@gap_extension_penalty} "
|
144
|
-
else
|
145
|
-
blastn << "-ungapped "
|
146
|
-
end
|
147
|
-
blastn << "-dust yes " if @filter
|
148
|
-
blastn
|
149
|
-
end
|
150
|
-
|
151
|
-
#
|
152
|
-
# Blastx command
|
153
|
-
#
|
154
|
-
def generate_blastx_cmd
|
155
|
-
blastx = "blastx " <<
|
156
|
-
"-db \"#{@db}\" " <<
|
157
|
-
"-query #{@na_fasta} " <<
|
158
|
-
"-outfmt 5 " <<
|
159
|
-
"-num_threads #{@threads} " <<
|
160
|
-
"-evalue #{@expectation} " <<
|
161
|
-
"-max_target_seqs #{@max_score} " <<
|
162
|
-
"-out #{@out} "
|
163
|
-
if @gapped_alignments
|
164
|
-
blastx << "-gapopen #{@gap_opening_penalty} "
|
165
|
-
blastx << "-gapextend #{@gap_extension_penalty} "
|
166
|
-
else
|
167
|
-
blastx << "-ungapped "
|
168
|
-
end
|
169
|
-
blastx << "-seg yes " if @filter
|
170
|
-
blastx
|
171
|
-
end
|
172
|
-
|
173
|
-
#
|
174
|
-
# Tblastn command
|
175
|
-
#
|
176
|
-
def generate_tblastn_cmd
|
177
|
-
tblastn = "tblastn " <<
|
178
|
-
"-db \"#{@db}\" " <<
|
179
|
-
"-query #{@aa_fasta} " <<
|
180
|
-
"-outfmt 5 " <<
|
181
|
-
"-num_threads #{@threads} " <<
|
182
|
-
"-evalue #{@expectation} " <<
|
183
|
-
"-max_target_seqs #{@max_score} " <<
|
184
|
-
"-out #{@out} "
|
185
|
-
if @gapped_alignments
|
186
|
-
tblastn << "-gapopen #{@gap_opening_penalty} "
|
187
|
-
tblastn << "-gapextend #{@gap_extension_penalty} "
|
188
|
-
tblastn << "-comp_based_stats D "
|
189
|
-
else
|
190
|
-
tblastn << "-ungapped "
|
191
|
-
tblastn << "-comp_based_stats F "
|
192
|
-
end
|
193
|
-
tblastn << "-seg yes " if @filter
|
194
|
-
tblastn
|
195
|
-
end
|
196
|
-
|
197
|
-
#
|
198
|
-
# Blastp command
|
199
|
-
#
|
200
|
-
def generate_blastp_cmd
|
201
|
-
blastp = "blastp " <<
|
202
|
-
"-db \"#{@db}\" " <<
|
203
|
-
"-query #{@aa_fasta} " <<
|
204
|
-
"-outfmt 5 " <<
|
205
|
-
"-num_threads #{@threads} " <<
|
206
|
-
"-evalue #{@expectation} " <<
|
207
|
-
"-max_target_seqs #{@max_score} " <<
|
208
|
-
"-out #{@out} "
|
209
|
-
if @gapped_alignments
|
210
|
-
blastp << "-gapopen #{@gap_opening_penalty} "
|
211
|
-
blastp << "-gapextend #{@gap_extension_penalty} "
|
212
|
-
blastp << "-comp_based_stats D "
|
213
|
-
else
|
214
|
-
blastp << "-ungapped "
|
215
|
-
blastp << "-comp_based_stats F "
|
216
|
-
end
|
217
|
-
blastp << "-seg yes " if @filter
|
218
|
-
blastp
|
219
|
-
end
|
220
|
-
|
221
|
-
#
|
222
|
-
# Generate Blast Command
|
223
|
-
#
|
224
|
-
def generate_blast_cmd
|
225
|
-
@cmd = ""
|
226
|
-
|
227
|
-
@na_fasta = File.join(@tmp, @hash + ".na.fa")
|
228
|
-
@aa_fasta = File.join(@tmp, @hash + ".aa.fa")
|
229
|
-
File.open(@na_fasta, "w") { |f| f << @na_sequence }
|
230
|
-
File.open(@aa_fasta, "w") { |f| f << @aa_sequence }
|
231
|
-
|
232
|
-
@out = File.join(@tmp, @hash + ".out.xml")
|
233
|
-
File.new(@out, "w")
|
234
|
-
|
235
|
-
case @algorithm
|
236
|
-
when "blastn"
|
237
|
-
@cmd << generate_blastn_cmd
|
238
|
-
when "blastx"
|
239
|
-
@cmd << generate_blastx_cmd
|
240
|
-
when "tblastn"
|
241
|
-
@cmd << generate_tblastn_cmd
|
242
|
-
when "blastp"
|
243
|
-
@cmd << generate_blastp_cmd
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
#
|
248
|
-
# Format Blast report hit_display_id and hit_def.
|
249
|
-
#
|
250
|
-
# For added flexibility, Quorum doesn't parse seqids or deflines.
|
251
|
-
# Instead, format_hit splits the hit_def on whitespace and
|
252
|
-
# reports hit_display_id as the first element and hit_def as the second.
|
253
|
-
#
|
254
|
-
def format_hit(str)
|
255
|
-
hit_display_id = ""
|
256
|
-
hit_def = ""
|
257
|
-
|
258
|
-
hit = str.split(" ", 2)
|
259
|
-
if hit.length < 2
|
260
|
-
hit_display_id = hit.first
|
261
|
-
hit_def = "None"
|
262
|
-
else
|
263
|
-
hit_display_id = hit.first
|
264
|
-
hit_def = hit.last
|
265
|
-
end
|
266
|
-
|
267
|
-
return hit_display_id, hit_def
|
268
|
-
end
|
269
|
-
|
270
|
-
#
|
271
|
-
# Save Blast Job Report
|
272
|
-
#
|
273
|
-
# Hsps are only reported if a query hit against the Blast db.
|
274
|
-
# Only save the @data if bit_score exists and it's > the user
|
275
|
-
# defined minimum score.
|
276
|
-
#
|
277
|
-
# Set the attribute results to true for downstream processes.
|
278
|
-
#
|
279
|
-
def save_hsp_results
|
280
|
-
if @data[:bit_score] && (@data[:bit_score].to_i > @min_score.to_i)
|
281
|
-
@data[:results] = true
|
282
|
-
@data["#{@algorithm}_job_id".to_sym] = @job.method(@job_association).call.job_id
|
283
|
-
|
284
|
-
job_report = @job.method(@job_report_association).call.build(@data)
|
285
|
-
|
286
|
-
unless job_report.save!
|
287
|
-
@logger.log(
|
288
|
-
"ActiveRecord",
|
289
|
-
"Unable to save #{@algorithm} results to database.",
|
290
|
-
1,
|
291
|
-
@tmp_files
|
292
|
-
)
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
#
|
298
|
-
# Save empty Blast Job Report
|
299
|
-
#
|
300
|
-
# Set the attribute results to false for downstream processes.
|
301
|
-
#
|
302
|
-
def save_empty_results
|
303
|
-
job_report = @job.method(@job_report_association).call.build(
|
304
|
-
"#{@algorithm}_job_id" => @job.method(@job_association).call.job_id,
|
305
|
-
"results" => false
|
306
|
-
)
|
307
|
-
unless job_report.save!
|
308
|
-
@logger.log(
|
309
|
-
"ActiveRecord",
|
310
|
-
"Unable to save #{@algorithm} results to database.",
|
311
|
-
1,
|
312
|
-
@tmp_files
|
313
|
-
)
|
314
|
-
end
|
315
|
-
@logger.log(
|
316
|
-
"NCBI Blast",
|
317
|
-
"#{@algorithm} report empty.",
|
318
|
-
0,
|
319
|
-
@tmp_files
|
320
|
-
)
|
321
|
-
end
|
322
|
-
|
323
|
-
#
|
324
|
-
# Parse and save Blast results.
|
325
|
-
#
|
326
|
-
# Parse the Blast XML output and save results.
|
327
|
-
#
|
328
|
-
def parse_and_save_results
|
329
|
-
@results = false # Did the xml contain results?
|
330
|
-
|
331
|
-
if File.size(@out) > 0
|
332
|
-
report = Bio::Blast::XmlIterator.new(@out)
|
333
|
-
report.to_enum.each do |iteration|
|
334
|
-
|
335
|
-
@data = {}
|
336
|
-
|
337
|
-
@data[:query] = iteration.query_def.split(" ").first
|
338
|
-
@data[:query_len] = iteration.query_len
|
339
|
-
|
340
|
-
iteration.each do |hit|
|
341
|
-
@data[:hit_display_id], @data[:hit_def] = format_hit(hit.hit_def)
|
342
|
-
|
343
|
-
@data[:hit_id] = hit.hit_id
|
344
|
-
@data[:hit_accession] = hit.accession
|
345
|
-
@data[:hit_len] = hit.len
|
346
|
-
|
347
|
-
hit.each do |hsp|
|
348
|
-
@data[:hsp_num] = hsp.hsp_num
|
349
|
-
@data[:bit_score] = hsp.bit_score
|
350
|
-
@data[:score] = hsp.score
|
351
|
-
@data[:evalue] = hsp.evalue
|
352
|
-
@data[:query_from] = hsp.query_from
|
353
|
-
@data[:query_to] = hsp.query_to
|
354
|
-
@data[:hit_from] = hsp.hit_from
|
355
|
-
@data[:hit_to] = hsp.hit_to
|
356
|
-
@data[:query_frame] = hsp.query_frame
|
357
|
-
@data[:hit_frame] = hsp.hit_frame
|
358
|
-
@data[:identity] = hsp.identity
|
359
|
-
@data[:positive] = hsp.positive
|
360
|
-
@data[:gaps] = hsp.gaps
|
361
|
-
@data[:align_len] = hsp.align_len
|
362
|
-
@data[:qseq] = hsp.qseq
|
363
|
-
@data[:hseq] = hsp.hseq
|
364
|
-
@data[:midline] = hsp.midline
|
365
|
-
|
366
|
-
if @data[:bit_score] &&
|
367
|
-
(@data[:bit_score].to_i > @min_score.to_i)
|
368
|
-
@results = true
|
369
|
-
save_hsp_results
|
370
|
-
end
|
371
|
-
|
372
|
-
end
|
373
|
-
end
|
374
|
-
end
|
375
|
-
end
|
376
|
-
|
377
|
-
save_empty_results unless @results
|
378
|
-
end
|
379
|
-
|
380
|
-
#
|
381
|
-
# Group record ids of hsps belonging to the same query and hit_accession.
|
382
|
-
#
|
383
|
-
def add_hps_groups_to_reports
|
384
|
-
groups = {}
|
385
|
-
|
386
|
-
query_hit_acc = @job.method(
|
387
|
-
@job_report_association
|
388
|
-
).call.select("DISTINCT query, hit_accession")
|
389
|
-
|
390
|
-
# Populate the hash of hashes with correct query and hit_accession.
|
391
|
-
query_hit_acc.each do |q|
|
392
|
-
groups[q.query] = {}
|
393
|
-
end
|
394
|
-
query_hit_acc.each do |q|
|
395
|
-
groups[q.query][q.hit_accession] = []
|
396
|
-
end
|
397
|
-
|
398
|
-
# Group record ids.
|
399
|
-
reports = @job.method(@job_report_association).call.order("id ASC")
|
400
|
-
reports.each do |r|
|
401
|
-
if groups[r.query][r.hit_accession]
|
402
|
-
groups[r.query][r.hit_accession] << r.id
|
403
|
-
end
|
404
|
-
end
|
405
|
-
|
406
|
-
# Save hsp_group as a comma delimited string.
|
407
|
-
reports.each do |r|
|
408
|
-
if groups[r.query][r.hit_accession]
|
409
|
-
if groups[r.query][r.hit_accession].length > 1
|
410
|
-
r.hsp_group = groups[r.query][r.hit_accession].join(",")
|
411
|
-
unless r.save!
|
412
|
-
@logger.log(
|
413
|
-
"ActiveRecord",
|
414
|
-
"Unable to save #{@algorithm} results to database.",
|
415
|
-
1,
|
416
|
-
@tmp_files
|
417
|
-
)
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
#
|
425
|
-
# Remove tmp files.
|
426
|
-
#
|
427
|
-
def remove_tmp_files
|
428
|
-
`rm #{@tmp_files}` if @tmp_files
|
429
|
-
end
|
430
|
-
|
431
|
-
public
|
432
|
-
|
433
|
-
#
|
434
|
-
# Execute Blast on a given dataset.
|
435
|
-
#
|
436
|
-
def execute_blast
|
437
|
-
generate_blast_cmd
|
438
|
-
@logger.log("NCBI Blast", @cmd)
|
439
|
-
system(@cmd)
|
440
|
-
|
441
|
-
# Wrap these methods in a transaction to prevent premature return.
|
442
|
-
@job.method(@job_report_association).call.transaction do
|
443
|
-
parse_and_save_results
|
444
|
-
add_hps_groups_to_reports
|
445
|
-
end
|
446
|
-
|
447
|
-
remove_tmp_files
|
448
|
-
end
|
449
|
-
|
450
|
-
end
|
451
|
-
end
|
452
|
-
end
|