quorum 0.1.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 +10 -0
- data/.rspec +1 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +170 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +318 -0
- data/Rakefile +31 -0
- data/app/assets/images/quorum/.gitkeep +0 -0
- data/app/assets/images/quorum/bg.png +0 -0
- data/app/assets/images/quorum/knight_rider.gif +0 -0
- data/app/assets/images/quorum/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/app/assets/images/quorum/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/app/assets/images/quorum/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/app/assets/images/quorum/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/app/assets/images/quorum/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/app/assets/images/quorum/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/app/assets/images/quorum/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/app/assets/images/quorum/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
- data/app/assets/images/quorum/ui-icons_222222_256x240.png +0 -0
- data/app/assets/images/quorum/ui-icons_2e83ff_256x240.png +0 -0
- data/app/assets/images/quorum/ui-icons_454545_256x240.png +0 -0
- data/app/assets/images/quorum/ui-icons_888888_256x240.png +0 -0
- data/app/assets/images/quorum/ui-icons_cd0a0a_256x240.png +0 -0
- data/app/assets/javascripts/quorum/application.js +13 -0
- data/app/assets/javascripts/quorum/jobs.js +386 -0
- data/app/assets/stylesheets/quorum/application.css +224 -0
- data/app/assets/stylesheets/quorum/jobs.css +72 -0
- data/app/assets/stylesheets/quorum/jquery-ui-1.8.16.custom.css +568 -0
- data/app/assets/stylesheets/quorum/tipsy.css +25 -0
- data/app/controllers/quorum/application_controller.rb +5 -0
- data/app/controllers/quorum/jobs_controller.rb +102 -0
- data/app/helpers/quorum/application_helper.rb +4 -0
- data/app/models/quorum/blastn_job.rb +111 -0
- data/app/models/quorum/blastn_job_report.rb +7 -0
- data/app/models/quorum/blastp_job.rb +111 -0
- data/app/models/quorum/blastp_job_report.rb +7 -0
- data/app/models/quorum/blastx_job.rb +111 -0
- data/app/models/quorum/blastx_job_report.rb +7 -0
- data/app/models/quorum/job.rb +164 -0
- data/app/models/quorum/tblastn_job.rb +111 -0
- data/app/models/quorum/tblastn_job_report.rb +7 -0
- data/app/views/layouts/quorum/application.html.erb +15 -0
- data/app/views/quorum/jobs/_blastn_form.html.erb +71 -0
- data/app/views/quorum/jobs/_blastp_form.html.erb +71 -0
- data/app/views/quorum/jobs/_blastx_form.html.erb +71 -0
- data/app/views/quorum/jobs/_tblastn_form.html.erb +71 -0
- data/app/views/quorum/jobs/new.html.erb +45 -0
- data/app/views/quorum/jobs/show.html.erb +183 -0
- data/app/views/shared/_error_messages.html.erb +10 -0
- data/config/locales/en.yml +8 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20111031204518_create_jobs.rb +12 -0
- data/db/migrate/20111031204701_create_blastn_jobs.rb +19 -0
- data/db/migrate/20111031204719_create_blastx_jobs.rb +19 -0
- data/db/migrate/20111031204733_create_blastp_jobs.rb +19 -0
- data/db/migrate/20111031204754_create_tblastn_jobs.rb +19 -0
- data/db/migrate/20111031204846_create_blastn_job_reports.rb +34 -0
- data/db/migrate/20111031204903_create_blastx_job_reports.rb +34 -0
- data/db/migrate/20111031204922_create_blastp_job_reports.rb +34 -0
- data/db/migrate/20111031204941_create_tblastn_job_reports.rb +34 -0
- data/lib/generators/quorum/install_generator.rb +68 -0
- data/lib/generators/quorum/styles_generator.rb +18 -0
- data/lib/generators/quorum/views_generator.rb +18 -0
- data/lib/generators/templates/README +25 -0
- data/lib/generators/templates/blast.rb +412 -0
- data/lib/generators/templates/logger.rb +43 -0
- data/lib/generators/templates/quorum_initializer.rb +36 -0
- data/lib/generators/templates/quorum_settings.yml +157 -0
- data/lib/generators/templates/search +141 -0
- data/lib/generators/templates/trollop.rb +781 -0
- data/lib/quorum/engine.rb +5 -0
- data/lib/quorum/helpers.rb +17 -0
- data/lib/quorum/sequence.rb +89 -0
- data/lib/quorum/version.rb +3 -0
- data/lib/quorum.rb +89 -0
- data/lib/tasks/blastdb/README +17 -0
- data/lib/tasks/blastdb/build_blast_db.rb +222 -0
- data/lib/tasks/quorum_resque.rake +3 -0
- data/lib/tasks/quorum_tasks.rake +50 -0
- data/lib/workers/quorum.rb +45 -0
- data/quorum.gemspec +29 -0
- data/script/rails +6 -0
- data/spec/data/nucl_prot_seqs.txt +36 -0
- data/spec/data/nucl_seqs.txt +32 -0
- data/spec/data/prot_seqs.txt +4 -0
- data/spec/data/seqs.docx +0 -0
- data/spec/data/seqs_not_fa.txt +16 -0
- data/spec/data/tmp/test.tgz +0 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/blast.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +46 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/environment.rb +20 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/quorum_initializer.rb +36 -0
- data/spec/dummy/config/initializers/resque.rb +1 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/locales/quorum.en.yml +8 -0
- data/spec/dummy/config/quorum_settings.yml +145 -0
- data/spec/dummy/config/routes.rb +9 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/schema.rb +214 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/quorum/bin/search +141 -0
- data/spec/dummy/quorum/blastdb/test/contigs.fa +2 -0
- data/spec/dummy/quorum/blastdb/test/peptides.fa +2 -0
- data/spec/dummy/quorum/blastdb/test.nhd +1 -0
- 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 +1 -0
- 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 +1 -0
- 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 +1 -0
- data/spec/dummy/quorum/blastdb/test.psi +0 -0
- data/spec/dummy/quorum/blastdb/test.psq +0 -0
- data/spec/dummy/quorum/lib/logger.rb +43 -0
- data/spec/dummy/quorum/lib/search_tools/blast.rb +412 -0
- data/spec/dummy/quorum/lib/trollop.rb +781 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/models/blastn_job_report_spec.rb +13 -0
- data/spec/models/blastn_job_spec.rb +103 -0
- data/spec/models/blastp_job_report_spec.rb +13 -0
- data/spec/models/blastp_job_spec.rb +103 -0
- data/spec/models/blastx_job_report_spec.rb +13 -0
- data/spec/models/blastx_job_spec.rb +103 -0
- data/spec/models/job_spec.rb +40 -0
- data/spec/models/tblastn_job_report_spec.rb +13 -0
- data/spec/models/tblastn_job_spec.rb +103 -0
- data/spec/quorum/quorum_sequence_spec.rb +64 -0
- data/spec/quorum_installed_spec.rb +64 -0
- data/spec/requests/jobs_spec.rb +138 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/tasks/blastdb_rake_spec.rb +119 -0
- data/spec/templates/blast_spec.rb +8 -0
- data/spec/templates/logger_spec.rb +35 -0
- data/vendor/assets/javascripts/jquery.tipsy.js +241 -0
- data/vendor/assets/javascripts/underscore-min.js +30 -0
- metadata +325 -0
|
@@ -0,0 +1,412 @@
|
|
|
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
|
+
has_one :quorum_blastn_job,
|
|
15
|
+
:foreign_key => "job_id"
|
|
16
|
+
has_many :quorum_blastn_job_reports,
|
|
17
|
+
:foreign_key => "blastn_job_id"
|
|
18
|
+
|
|
19
|
+
has_one :quorum_blastx_job,
|
|
20
|
+
:foreign_key => "job_id"
|
|
21
|
+
has_many :quorum_blastx_job_reports,
|
|
22
|
+
:foreign_key => "blastx_job_id"
|
|
23
|
+
|
|
24
|
+
has_one :quorum_tblastn_job,
|
|
25
|
+
:foreign_key => "job_id"
|
|
26
|
+
has_many :quorum_tblastn_job_reports,
|
|
27
|
+
:foreign_key => "tblastn_job_id"
|
|
28
|
+
|
|
29
|
+
has_one :quorum_blastp_job,
|
|
30
|
+
:foreign_key => "job_id"
|
|
31
|
+
has_many :quorum_blastp_job_reports,
|
|
32
|
+
:foreign_key => "blastp_job_id"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class QuorumBlastnJob < ActiveRecord::Base
|
|
36
|
+
belongs_to :quorum_job
|
|
37
|
+
has_many :quorum_blastn_job_reports
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class QuorumBlastnJobReport < ActiveRecord::Base
|
|
41
|
+
belongs_to :quorum_blastn_job
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class QuorumBlastxJob < ActiveRecord::Base
|
|
45
|
+
belongs_to :quorum_job
|
|
46
|
+
has_many :quorum_blastx_job_reports
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class QuorumBlastxJobReport < ActiveRecord::Base
|
|
50
|
+
belongs_to :quorum_blastx_job
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class QuorumTblastnJob < ActiveRecord::Base
|
|
54
|
+
belongs_to :quorum_job
|
|
55
|
+
has_many :quorum_tblastn_job_reports
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class QuorumTblastnJobReport < ActiveRecord::Base
|
|
59
|
+
belongs_to :quorum_tblastn_job
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class QuorumBlastpJob < ActiveRecord::Base
|
|
63
|
+
belongs_to :quorum_job
|
|
64
|
+
has_many :quorum_blastp_job_reports
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class QuorumBlastpJobReport < ActiveRecord::Base
|
|
68
|
+
belongs_to :quorum_blastp_job
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def initialize(args)
|
|
74
|
+
@algorithm = args[:search_tool]
|
|
75
|
+
@id = args[:id]
|
|
76
|
+
@log_directory = args[:log_directory]
|
|
77
|
+
@tmp = args[:tmp_directory]
|
|
78
|
+
@search_database = args[:search_database]
|
|
79
|
+
@threads = args[:threads]
|
|
80
|
+
|
|
81
|
+
@logger = Quorum::Logger.new(@log_directory)
|
|
82
|
+
|
|
83
|
+
begin
|
|
84
|
+
@job = QuorumJob.find(@id)
|
|
85
|
+
rescue Exception => e
|
|
86
|
+
@logger.log("ActiveRecord", e.message, 1)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
@na_sequence = @job.na_sequence
|
|
90
|
+
@aa_sequence = @job.aa_sequence
|
|
91
|
+
|
|
92
|
+
## Create for method invocation ##
|
|
93
|
+
@job_association = "quorum_#{@algorithm}_job".to_sym
|
|
94
|
+
@job_report_association = "quorum_#{@algorithm}_job_reports".to_sym
|
|
95
|
+
|
|
96
|
+
@filter = @job.method(@job_association).call.filter
|
|
97
|
+
@expectation = @job.method(@job_association).call.expectation
|
|
98
|
+
@max_score = @job.method(@job_association).call.max_score
|
|
99
|
+
@min_score = @job.method(@job_association).call.min_bit_score
|
|
100
|
+
@gapped_alignments = @job.method(@job_association).call.gapped_alignments
|
|
101
|
+
@gap_opening_penalty = @job.method(@job_association).call.gap_opening_penalty
|
|
102
|
+
@gap_extension_penalty = @job.method(@job_association).call.gap_extension_penalty
|
|
103
|
+
|
|
104
|
+
@db = @job.method(@job_association).call.blast_dbs.split(';')
|
|
105
|
+
@db.map! { |d| File.join(@search_database, d) }
|
|
106
|
+
@db = @db.join(' ')
|
|
107
|
+
|
|
108
|
+
@hash = create_hash(@job.sequence)
|
|
109
|
+
@tmp_files = File.join(@tmp, @hash) << "*"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
#
|
|
113
|
+
# Create a unique hash plus timestamp.
|
|
114
|
+
#
|
|
115
|
+
def create_hash(sequence)
|
|
116
|
+
Digest::MD5.hexdigest(sequence).to_s + "-" + Time.now.to_f.to_s
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def generate_blastn_cmd
|
|
120
|
+
blastn = "blastn " <<
|
|
121
|
+
"-db \"#{@db}\" " <<
|
|
122
|
+
"-query #{@na_fasta} " <<
|
|
123
|
+
"-outfmt 5 " <<
|
|
124
|
+
"-num_threads #{@threads} " <<
|
|
125
|
+
"-evalue #{@expectation} " <<
|
|
126
|
+
"-max_target_seqs #{@max_score} " <<
|
|
127
|
+
"-out #{@out} "
|
|
128
|
+
if @gapped_alignments
|
|
129
|
+
blastn << "-gapopen #{@gap_opening_penalty} "
|
|
130
|
+
blastn << "-gapextend #{@gap_extension_penalty} "
|
|
131
|
+
else
|
|
132
|
+
blastn << "-ungapped "
|
|
133
|
+
end
|
|
134
|
+
blastn << "-dust yes " if @filter
|
|
135
|
+
blastn
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def generate_blastx_cmd
|
|
139
|
+
blastx = "blastx " <<
|
|
140
|
+
"-db \"#{@db}\" " <<
|
|
141
|
+
"-query #{@na_fasta} " <<
|
|
142
|
+
"-outfmt 5 " <<
|
|
143
|
+
"-num_threads #{@threads} " <<
|
|
144
|
+
"-evalue #{@expectation} " <<
|
|
145
|
+
"-max_target_seqs #{@max_score} " <<
|
|
146
|
+
"-out #{@out} "
|
|
147
|
+
if @gapped_alignments
|
|
148
|
+
blastx << "-gapopen #{@gap_opening_penalty} "
|
|
149
|
+
blastx << "-gapextend #{@gap_extension_penalty} "
|
|
150
|
+
else
|
|
151
|
+
blastx << "-ungapped "
|
|
152
|
+
end
|
|
153
|
+
blastx << "-seg yes " if @filter
|
|
154
|
+
blastx
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def generate_tblastn_cmd
|
|
158
|
+
tblastn = "tblastn " <<
|
|
159
|
+
"-db \"#{@db}\" " <<
|
|
160
|
+
"-query #{@aa_fasta} " <<
|
|
161
|
+
"-outfmt 5 " <<
|
|
162
|
+
"-num_threads #{@threads} " <<
|
|
163
|
+
"-evalue #{@expectation} " <<
|
|
164
|
+
"-max_target_seqs #{@max_score} " <<
|
|
165
|
+
"-out #{@out} "
|
|
166
|
+
if @gapped_alignments
|
|
167
|
+
tblastn << "-gapopen #{@gap_opening_penalty} "
|
|
168
|
+
tblastn << "-gapextend #{@gap_extension_penalty} "
|
|
169
|
+
tblastn << "-comp_based_stats D "
|
|
170
|
+
else
|
|
171
|
+
tblastn << "-ungapped "
|
|
172
|
+
tblastn << "-comp_based_stats F "
|
|
173
|
+
end
|
|
174
|
+
tblastn << "-seg yes " if @filter
|
|
175
|
+
tblastn
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def generate_blastp_cmd
|
|
179
|
+
blastp = "blastp " <<
|
|
180
|
+
"-db \"#{@db}\" " <<
|
|
181
|
+
"-query #{@aa_fasta} " <<
|
|
182
|
+
"-outfmt 5 " <<
|
|
183
|
+
"-num_threads #{@threads} " <<
|
|
184
|
+
"-evalue #{@expectation} " <<
|
|
185
|
+
"-max_target_seqs #{@max_score} " <<
|
|
186
|
+
"-out #{@out} "
|
|
187
|
+
if @gapped_alignments
|
|
188
|
+
blastp << "-gapopen #{@gap_opening_penalty} "
|
|
189
|
+
blastp << "-gapextend #{@gap_extension_penalty} "
|
|
190
|
+
blastp << "-comp_based_stats D "
|
|
191
|
+
else
|
|
192
|
+
blastp << "-ungapped "
|
|
193
|
+
blastp << "-comp_based_stats F "
|
|
194
|
+
end
|
|
195
|
+
blastp << "-seg yes " if @filter
|
|
196
|
+
blastp
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
#
|
|
200
|
+
# Generate Blast Command
|
|
201
|
+
#
|
|
202
|
+
def generate_blast_cmd
|
|
203
|
+
@cmd = ""
|
|
204
|
+
|
|
205
|
+
@na_fasta = File.join(@tmp, @hash + ".na.fa")
|
|
206
|
+
@aa_fasta = File.join(@tmp, @hash + ".aa.fa")
|
|
207
|
+
File.open(@na_fasta, "w") { |f| f << @na_sequence }
|
|
208
|
+
File.open(@aa_fasta, "w") { |f| f << @aa_sequence }
|
|
209
|
+
|
|
210
|
+
@out = File.join(@tmp, @hash + ".out.xml")
|
|
211
|
+
File.new(@out, "w")
|
|
212
|
+
|
|
213
|
+
case @algorithm
|
|
214
|
+
when "blastn"
|
|
215
|
+
@cmd << generate_blastn_cmd
|
|
216
|
+
when "blastx"
|
|
217
|
+
@cmd << generate_blastx_cmd
|
|
218
|
+
when "tblastn"
|
|
219
|
+
@cmd << generate_tblastn_cmd
|
|
220
|
+
when "blastp"
|
|
221
|
+
@cmd << generate_blastp_cmd
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
#
|
|
226
|
+
# Make the E-value look pretty.
|
|
227
|
+
#
|
|
228
|
+
def format_evalue(evalue)
|
|
229
|
+
evalue = evalue.to_s
|
|
230
|
+
e = evalue.slice!(/e.*/)
|
|
231
|
+
unless e.nil?
|
|
232
|
+
e = " x 10<sup>" << e.sub(/e/, '') << "</sup>"
|
|
233
|
+
end
|
|
234
|
+
evalue.to_f.round(1).to_s << e.to_s
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
#
|
|
238
|
+
# Format Blast report hit_id and hit_def.
|
|
239
|
+
#
|
|
240
|
+
# For added flexibility, Quorum doesn't parse seqids or deflines.
|
|
241
|
+
# Instead, format_hit splits the hit_def on whitespace and
|
|
242
|
+
# reports hit_id as the first element and hit_def as the second.
|
|
243
|
+
#
|
|
244
|
+
def format_hit(str)
|
|
245
|
+
hit_id = ""
|
|
246
|
+
hit_def = ""
|
|
247
|
+
|
|
248
|
+
hit = str.split(" ", 2)
|
|
249
|
+
if hit.length < 2
|
|
250
|
+
hit_id = hit.first
|
|
251
|
+
hit_def = "None"
|
|
252
|
+
else
|
|
253
|
+
hit_id = hit.first
|
|
254
|
+
hit_def = hit.last
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
return hit_id, hit_def
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
#
|
|
261
|
+
# Parse and save Blast results using bio-blastxmlparser.
|
|
262
|
+
# Only save Blast results if results.bit_score > @min_score.
|
|
263
|
+
#
|
|
264
|
+
def parse_and_save_results
|
|
265
|
+
# Helper to avoid having to perform a query.
|
|
266
|
+
saved = false
|
|
267
|
+
|
|
268
|
+
if File.size(@out) > 0
|
|
269
|
+
report = Bio::Blast::XmlIterator.new(@out)
|
|
270
|
+
report.to_enum.each do |iteration|
|
|
271
|
+
|
|
272
|
+
@data = {}
|
|
273
|
+
|
|
274
|
+
@data[:query] = iteration.query_def.split(" ").first
|
|
275
|
+
@data[:query_len] = iteration.query_len
|
|
276
|
+
|
|
277
|
+
iteration.each do |hit|
|
|
278
|
+
@data[:hit_id], @data[:hit_def] = format_hit(hit.hit_def)
|
|
279
|
+
|
|
280
|
+
@data[:hit_accession] = hit.accession
|
|
281
|
+
@data[:hit_len] = hit.len
|
|
282
|
+
|
|
283
|
+
hit.each do |hsp|
|
|
284
|
+
@data[:hsp_num] = hsp.hsp_num
|
|
285
|
+
@data[:bit_score] = hsp.bit_score
|
|
286
|
+
@data[:score] = hsp.score
|
|
287
|
+
@data[:evalue] = format_evalue(hsp.evalue)
|
|
288
|
+
@data[:query_from] = hsp.query_from
|
|
289
|
+
@data[:query_to] = hsp.query_to
|
|
290
|
+
@data[:hit_from] = hsp.hit_from
|
|
291
|
+
@data[:hit_to] = hsp.hit_to
|
|
292
|
+
@data[:query_frame] = hsp.query_frame
|
|
293
|
+
@data[:hit_frame] = hsp.hit_frame
|
|
294
|
+
@data[:identity] = hsp.identity
|
|
295
|
+
@data[:positive] = hsp.positive
|
|
296
|
+
@data[:align_len] = hsp.align_len
|
|
297
|
+
@data[:qseq] = hsp.qseq
|
|
298
|
+
@data[:hseq] = hsp.hseq
|
|
299
|
+
@data[:midline] = hsp.midline
|
|
300
|
+
|
|
301
|
+
# Hsps are only reported if a query hit against the Blast db.
|
|
302
|
+
# Only save the @data if bit_score exists.
|
|
303
|
+
if @data[:bit_score] &&
|
|
304
|
+
(@data[:bit_score].to_i > @min_score.to_i)
|
|
305
|
+
@data[:results] = true
|
|
306
|
+
@data["#{@algorithm}_job_id".to_sym] = @job.method(@job_association).call.job_id
|
|
307
|
+
saved = true
|
|
308
|
+
# Build a new report for each Hsp.
|
|
309
|
+
job_report = @job.method(@job_report_association).call.build(@data)
|
|
310
|
+
unless job_report.save!
|
|
311
|
+
@logger.log(
|
|
312
|
+
"ActiveRecord",
|
|
313
|
+
"Unable to save #{@algorithm} results to database.",
|
|
314
|
+
1,
|
|
315
|
+
@tmp_files
|
|
316
|
+
)
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Save the record and set results to false.
|
|
326
|
+
unless saved
|
|
327
|
+
job_report = @job.method(@job_report_association).call.build(
|
|
328
|
+
"#{@algorithm}_job_id" => @job.method(@job_association).call.job_id,
|
|
329
|
+
"results" => false
|
|
330
|
+
)
|
|
331
|
+
unless job_report.save!
|
|
332
|
+
@logger.log(
|
|
333
|
+
"ActiveRecord",
|
|
334
|
+
"Unable to save #{@algorithm} results to database.",
|
|
335
|
+
1,
|
|
336
|
+
@tmp_files
|
|
337
|
+
)
|
|
338
|
+
end
|
|
339
|
+
@logger.log(
|
|
340
|
+
"NCBI Blast",
|
|
341
|
+
"#{@algorithm} report empty.",
|
|
342
|
+
0,
|
|
343
|
+
@tmp_files
|
|
344
|
+
)
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
#
|
|
349
|
+
# Group record ids of hsps belonging to the same query and hit_accession.
|
|
350
|
+
#
|
|
351
|
+
def add_hps_groups_to_reports
|
|
352
|
+
groups = {}
|
|
353
|
+
|
|
354
|
+
query_hit_acc = @job.method(
|
|
355
|
+
@job_report_association
|
|
356
|
+
).call.select("DISTINCT query, hit_accession")
|
|
357
|
+
|
|
358
|
+
# Populate the hash of hashes with correct query and hit_accession.
|
|
359
|
+
query_hit_acc.each do |q|
|
|
360
|
+
groups[q.query] = {}
|
|
361
|
+
end
|
|
362
|
+
query_hit_acc.each do |q|
|
|
363
|
+
groups[q.query][q.hit_accession] = []
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Group record ids.
|
|
367
|
+
reports = @job.method(@job_report_association).call.order("id ASC")
|
|
368
|
+
reports.each do |r|
|
|
369
|
+
if groups[r.query][r.hit_accession]
|
|
370
|
+
groups[r.query][r.hit_accession] << r.id
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Save hsp_group as a comma delimited string.
|
|
375
|
+
reports.each do |r|
|
|
376
|
+
if groups[r.query][r.hit_accession]
|
|
377
|
+
if groups[r.query][r.hit_accession].length > 1
|
|
378
|
+
r.hsp_group = groups[r.query][r.hit_accession].join(",")
|
|
379
|
+
unless r.save!
|
|
380
|
+
@logger.log(
|
|
381
|
+
"ActiveRecord",
|
|
382
|
+
"Unable to save #{@algorithm} results to database.",
|
|
383
|
+
1,
|
|
384
|
+
@tmp_files
|
|
385
|
+
)
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def remove_tmp_files
|
|
393
|
+
`rm #{@tmp_files}` if @tmp_files
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
public
|
|
397
|
+
|
|
398
|
+
#
|
|
399
|
+
# Execute Blast on a given dataset.
|
|
400
|
+
#
|
|
401
|
+
def execute_blast
|
|
402
|
+
generate_blast_cmd
|
|
403
|
+
@logger.log("NCBI Blast", @cmd)
|
|
404
|
+
system(@cmd)
|
|
405
|
+
parse_and_save_results
|
|
406
|
+
add_hps_groups_to_reports
|
|
407
|
+
remove_tmp_files
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Quorum
|
|
2
|
+
class Logger
|
|
3
|
+
|
|
4
|
+
def initialize(dir)
|
|
5
|
+
@log_directory = dir
|
|
6
|
+
@log_file = "quorum.log"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Write to log file and exit if exit_status is present.
|
|
11
|
+
#
|
|
12
|
+
def log(program, message, exit_status = nil, files = nil)
|
|
13
|
+
File.open(File.join(@log_directory, @log_file), "a") do |log|
|
|
14
|
+
log.puts ""
|
|
15
|
+
log.puts Time.now.to_s + " " + program
|
|
16
|
+
log.puts message
|
|
17
|
+
log.puts ""
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if exit_status
|
|
21
|
+
remove_files(files) unless files.nil?
|
|
22
|
+
exit exit_status.to_i
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Removes instance files.
|
|
30
|
+
#
|
|
31
|
+
def remove_files(files)
|
|
32
|
+
unless Dir.glob(files).empty?
|
|
33
|
+
`rm #{files}`
|
|
34
|
+
else
|
|
35
|
+
log(
|
|
36
|
+
"remove_files",
|
|
37
|
+
"Unable to remove #{files}"
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
## Quorum Settings ##
|
|
2
|
+
#
|
|
3
|
+
# DO NOT edit this file directly. To configure Quorum edit
|
|
4
|
+
# config/quorum_settings.yml.
|
|
5
|
+
|
|
6
|
+
## Load Quorum Setttings ##
|
|
7
|
+
settings = YAML.load_file(
|
|
8
|
+
"#{::Rails.root.to_s}/config/quorum_settings.yml"
|
|
9
|
+
)[::Rails.env.to_s]
|
|
10
|
+
|
|
11
|
+
## Sub %{RAILS_ROOT} ##
|
|
12
|
+
settings.keys.each do |k|
|
|
13
|
+
settings[k.to_s].each_value do |v|
|
|
14
|
+
if v.kind_of?(String)
|
|
15
|
+
v.to_s.gsub!('%{RAILS_ROOT}', ::Rails.root.to_s)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
blast = settings['blast']
|
|
21
|
+
|
|
22
|
+
## Quorum Blast Settings ##
|
|
23
|
+
Quorum.blast_remote = blast['remote']
|
|
24
|
+
Quorum.blast_ssh_host = blast['ssh_host']
|
|
25
|
+
Quorum.blast_ssh_user = blast['ssh_user']
|
|
26
|
+
Quorum.blast_ssh_options = blast['ssh_options']
|
|
27
|
+
Quorum.blast_script = blast['script']
|
|
28
|
+
Quorum.blast_log_dir = blast['log_dir']
|
|
29
|
+
Quorum.blast_tmp_dir = blast['tmp_dir']
|
|
30
|
+
Quorum.blast_db = blast['blast_db']
|
|
31
|
+
Quorum.tblastn = blast['tblastn']
|
|
32
|
+
Quorum.blastp = blast['blastp']
|
|
33
|
+
Quorum.blastn = blast['blastn']
|
|
34
|
+
Quorum.blastx = blast['blastx']
|
|
35
|
+
Quorum.blast_threads = blast['blast_threads']
|
|
36
|
+
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Quorum Settings
|
|
2
|
+
#
|
|
3
|
+
# ==> General
|
|
4
|
+
# Quorum initializer safely substitutes %{RAILS_ROOT} with ::Rails.root.to_s.
|
|
5
|
+
#
|
|
6
|
+
# ==> NCBI BLAST+ Settings
|
|
7
|
+
# blast:
|
|
8
|
+
# remote: true to execute script on remote machine via
|
|
9
|
+
# Net::SSH. false to execute locally via Kernel#system.
|
|
10
|
+
# See Net::SSH for more information.
|
|
11
|
+
# http://net-ssh.github.com/ssh/v2/api/index.html
|
|
12
|
+
#
|
|
13
|
+
# ssh_host: Net::SSH.start() host.
|
|
14
|
+
#
|
|
15
|
+
# ssh_user: Net::SSH.start() user.
|
|
16
|
+
#
|
|
17
|
+
# ssh_options: Net::SSH.start() optional params.
|
|
18
|
+
# See http://net-ssh.github.com/ssh/v2/api/classes/Net/SSH.html#M000002
|
|
19
|
+
# for a complete list of options.
|
|
20
|
+
# Example Usage:
|
|
21
|
+
# ssh_options:
|
|
22
|
+
# password: "secret password"
|
|
23
|
+
# port: 8888
|
|
24
|
+
#
|
|
25
|
+
# script: Absolute path to search script.
|
|
26
|
+
#
|
|
27
|
+
# log_dir: Absolute path to log file directory.
|
|
28
|
+
#
|
|
29
|
+
# tmp_dir: Absolute path to tmp file directory.
|
|
30
|
+
#
|
|
31
|
+
# blast_db: Absolute path to NCBI Blast database directory.
|
|
32
|
+
# Execute the rake task below to build your Blast DB.
|
|
33
|
+
# rake quorum:blastdb:build
|
|
34
|
+
#
|
|
35
|
+
# blastn: Collection of Blast DBs under the parent blast_db
|
|
36
|
+
# to use with blastn.
|
|
37
|
+
# Example Usage:
|
|
38
|
+
# blastn:
|
|
39
|
+
# "Display name": blastn_database_name
|
|
40
|
+
# "A really cool genome": really_cool_genome
|
|
41
|
+
#
|
|
42
|
+
# blastx: Collection of Blast DBs under the parent blast_db
|
|
43
|
+
# to use with blastx.
|
|
44
|
+
# Example Usage:
|
|
45
|
+
# blastx:
|
|
46
|
+
# "Display name": blastx_database_name
|
|
47
|
+
# "A really cool genome": really_cool_genome
|
|
48
|
+
#
|
|
49
|
+
# tblastn: Collection of Blast DBs under the parent blast_db
|
|
50
|
+
# to use with tblastn.
|
|
51
|
+
# Example Usage:
|
|
52
|
+
# tblastn:
|
|
53
|
+
# "Display name": tblastn_database_name
|
|
54
|
+
# "A really cool genome": really_cool_genome
|
|
55
|
+
#
|
|
56
|
+
# blastp: Collection of Blast DBs under the parent blast_db
|
|
57
|
+
# to use with blastp.
|
|
58
|
+
# Example Usage:
|
|
59
|
+
# blastp:
|
|
60
|
+
# "Display name": blastp_database_name
|
|
61
|
+
# "A really cool genome": really_cool_genome
|
|
62
|
+
#
|
|
63
|
+
# blast_threads: Number of Blast threads. >= 1
|
|
64
|
+
# Defaults to 1.
|
|
65
|
+
|
|
66
|
+
development:
|
|
67
|
+
blast:
|
|
68
|
+
remote: false
|
|
69
|
+
ssh_host:
|
|
70
|
+
ssh_user:
|
|
71
|
+
ssh_options:
|
|
72
|
+
# Example:
|
|
73
|
+
# password: "secret password"
|
|
74
|
+
# port: 8888
|
|
75
|
+
script: "%{RAILS_ROOT}/quorum/bin/search"
|
|
76
|
+
log_dir: "%{RAILS_ROOT}/quorum/log"
|
|
77
|
+
tmp_dir: "%{RAILS_ROOT}/quorum/tmp"
|
|
78
|
+
blast_db: "%{RAILS_ROOT}/quorum/blastdb"
|
|
79
|
+
blastn:
|
|
80
|
+
# Example:
|
|
81
|
+
# "Display name": blastn_database_name
|
|
82
|
+
# "A really cool genome": really_cool_genome
|
|
83
|
+
blastx:
|
|
84
|
+
# Example:
|
|
85
|
+
# "Display name": blastx_database_name
|
|
86
|
+
# "A really cool genome": really_cool_genome
|
|
87
|
+
tblastn:
|
|
88
|
+
# Example:
|
|
89
|
+
# "Display name": tblastn_database_name
|
|
90
|
+
# "A really cool genome": really_cool_genome
|
|
91
|
+
blastp:
|
|
92
|
+
# Example:
|
|
93
|
+
# "Display name": blastp_database_name
|
|
94
|
+
# "A really cool genome": really_cool_genome
|
|
95
|
+
blast_threads:
|
|
96
|
+
|
|
97
|
+
test:
|
|
98
|
+
blast:
|
|
99
|
+
remote: false
|
|
100
|
+
ssh_host:
|
|
101
|
+
ssh_user:
|
|
102
|
+
ssh_options:
|
|
103
|
+
# Example:
|
|
104
|
+
# password: "secret password"
|
|
105
|
+
# port: 8888
|
|
106
|
+
script: "%{RAILS_ROOT}/quorum/bin/search"
|
|
107
|
+
log_dir: "%{RAILS_ROOT}/quorum/log"
|
|
108
|
+
tmp_dir: "%{RAILS_ROOT}/quorum/tmp"
|
|
109
|
+
blast_db: "%{RAILS_ROOT}/quorum/blastdb"
|
|
110
|
+
blastn:
|
|
111
|
+
# Example:
|
|
112
|
+
# "Display name": blastn_database_name
|
|
113
|
+
# "A really cool genome": really_cool_genome
|
|
114
|
+
blastx:
|
|
115
|
+
# Example:
|
|
116
|
+
# "Display name": blastx_database_name
|
|
117
|
+
# "A really cool genome": really_cool_genome
|
|
118
|
+
tblastn:
|
|
119
|
+
# Example:
|
|
120
|
+
# "Display name": tblastn_database_name
|
|
121
|
+
# "A really cool genome": really_cool_genome
|
|
122
|
+
blastp:
|
|
123
|
+
# Example:
|
|
124
|
+
# "Display name": blastp_database_name
|
|
125
|
+
# "A really cool genome": really_cool_genome
|
|
126
|
+
blast_threads:
|
|
127
|
+
|
|
128
|
+
production:
|
|
129
|
+
blast:
|
|
130
|
+
remote: true
|
|
131
|
+
ssh_host:
|
|
132
|
+
ssh_user:
|
|
133
|
+
ssh_options:
|
|
134
|
+
# Example:
|
|
135
|
+
# password: "secret password"
|
|
136
|
+
# port: 8888
|
|
137
|
+
script:
|
|
138
|
+
log_dir:
|
|
139
|
+
tmp_dir:
|
|
140
|
+
blast_db:
|
|
141
|
+
blastn:
|
|
142
|
+
# Example:
|
|
143
|
+
# "Display name": blastn_database_name
|
|
144
|
+
# "A really cool genome": really_cool_genome
|
|
145
|
+
blastx:
|
|
146
|
+
# Example:
|
|
147
|
+
# "Display name": blastx_database_name
|
|
148
|
+
# "A really cool genome": really_cool_genome
|
|
149
|
+
tblastn:
|
|
150
|
+
# Example:
|
|
151
|
+
# "Display name": tblastn_database_name
|
|
152
|
+
# "A really cool genome": really_cool_genome
|
|
153
|
+
blastp:
|
|
154
|
+
# Example:
|
|
155
|
+
# "Display name": blastp_database_name
|
|
156
|
+
# "A really cool genome": really_cool_genome
|
|
157
|
+
blast_threads:
|