quorum 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|