quorum 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +82 -74
- data/HISTORY.md +9 -0
- data/README.rdoc +101 -23
- data/app/assets/javascripts/quorum/jobs.js +45 -3
- data/app/controllers/quorum/jobs_controller.rb +47 -2
- data/app/models/quorum/job.rb +52 -9
- data/app/views/quorum/jobs/new.html.erb +7 -6
- data/app/views/quorum/jobs/show.html.erb +7 -118
- data/app/views/quorum/jobs/templates/_blast_detailed_report_template.html.erb +53 -0
- data/app/views/quorum/jobs/templates/_blast_template.html.erb +68 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20120109232446_add_hit_display_id_to_blast_reports.rb +8 -0
- data/lib/generators/quorum/images_generator.rb +18 -0
- data/lib/generators/quorum/install_generator.rb +41 -13
- data/lib/generators/templates/README +1 -1
- data/lib/generators/templates/blast.rb +18 -7
- data/lib/generators/templates/blast_db.rb +106 -0
- data/lib/generators/templates/fetch +115 -0
- data/lib/generators/templates/quorum_initializer.rb +1 -1
- data/lib/generators/templates/quorum_settings.yml +4 -4
- data/lib/quorum/helpers.rb +71 -1
- data/lib/quorum/version.rb +1 -1
- data/lib/quorum.rb +15 -5
- data/lib/workers/quorum.rb +12 -2
- data/quorum.gemspec +4 -2
- data/spec/data/seqs_not_fa.txt +16 -16
- data/spec/dummy/config/initializers/quorum_initializer.rb +1 -1
- data/spec/dummy/config/initializers/resque.rb +4 -1
- data/spec/dummy/config/quorum_settings.yml +4 -4
- data/spec/dummy/db/schema.rb +8 -1
- data/spec/dummy/quorum/bin/fetch +115 -0
- data/spec/dummy/quorum/blastdb/test.nin +0 -0
- data/spec/dummy/quorum/blastdb/test.pin +0 -0
- data/spec/dummy/quorum/lib/fetch_tools/blast_db.rb +106 -0
- data/spec/dummy/quorum/lib/search_tools/blast.rb +18 -7
- data/spec/models/job_spec.rb +3 -4
- data/spec/quorum_installed_spec.rb +5 -0
- data/spec/requests/jobs_spec.rb +14 -1
- data/spec/spec_helper.rb +32 -1
- data/spec/support/streams.rb +16 -0
- data/spec/templates/blast_db_spec.rb +63 -0
- data/spec/templates/blast_spec.rb +108 -2
- metadata +64 -37
- /data/app/assets/images/quorum/{knight_rider.gif → loading.gif} +0 -0
- /data/app/views/quorum/jobs/{_blastn_form.html.erb → form/_blastn_form.html.erb} +0 -0
- /data/app/views/quorum/jobs/{_blastp_form.html.erb → form/_blastp_form.html.erb} +0 -0
- /data/app/views/quorum/jobs/{_blastx_form.html.erb → form/_blastx_form.html.erb} +0 -0
- /data/app/views/quorum/jobs/{_tblastn_form.html.erb → form/_tblastn_form.html.erb} +0 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
|
5
|
+
require 'trollop'
|
6
|
+
require 'fetch_tools/blast_db'
|
7
|
+
|
8
|
+
FETCH_TOOLS = {
|
9
|
+
"blastdbcmd" => ["blastdbcmd"]
|
10
|
+
}
|
11
|
+
|
12
|
+
module Quorum
|
13
|
+
#
|
14
|
+
# Parse Options for Quorum Fetch
|
15
|
+
#
|
16
|
+
class Fetch
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
opts = Trollop::options do
|
20
|
+
text <<-HEAD
|
21
|
+
|
22
|
+
Quorum
|
23
|
+
|
24
|
+
A flexible bioinformatics search tool.
|
25
|
+
|
26
|
+
Options:
|
27
|
+
|
28
|
+
HEAD
|
29
|
+
|
30
|
+
# Fetch tool
|
31
|
+
opt :fetch_tool, "Fetch tool to execute. Available tools: " <<
|
32
|
+
"#{FETCH_TOOLS.keys.join(', ')}", :type => :string,
|
33
|
+
:required => true, :short => "-f"
|
34
|
+
|
35
|
+
# General settings
|
36
|
+
opt :log_directory, "Path to log directory", :type => :string,
|
37
|
+
:required => true, :short => "-l"
|
38
|
+
opt :tmp_directory, "Path to tmp directory", :type => :string,
|
39
|
+
:required => true, :short => "-m"
|
40
|
+
|
41
|
+
# Global settings
|
42
|
+
opt :fetch_database, "Path to fetch tool database", :type => :string,
|
43
|
+
:short => "-d"
|
44
|
+
opt :fetch_database_names, "Semicolon delimited database names",
|
45
|
+
:type => :string, :short => "-n"
|
46
|
+
|
47
|
+
# Algorithm specific opts
|
48
|
+
opt :blast_hit_id, "Blast hit id", :type => :string, :short => "-b"
|
49
|
+
opt :blast_hit_display_id, "Blast hit display id", :type => :string,
|
50
|
+
:short => "-s"
|
51
|
+
opt :blast_algo, "Blast algorithm", :type => :string, :short => "-a"
|
52
|
+
end
|
53
|
+
|
54
|
+
@args = {} # Contains valid opts.
|
55
|
+
|
56
|
+
## Additional opt validation. ##
|
57
|
+
|
58
|
+
unless FETCH_TOOLS.keys.include?(opts[:fetch_tool].downcase)
|
59
|
+
Trollop::die :fetch_tool, "fetch_tool not found\n" <<
|
60
|
+
"Available tools: " + FETCH_TOOLS.keys.join(", ")
|
61
|
+
end
|
62
|
+
|
63
|
+
@args[:fetch_tool] = opts[:fetch_tool].downcase
|
64
|
+
|
65
|
+
unless opts[:fetch_database] && Dir.exists?(opts[:fetch_database])
|
66
|
+
Trollop::die :fetch_database, "fetch tool database not found\n" <<
|
67
|
+
"Supplied directory: " + opts[:fetch_database]
|
68
|
+
end
|
69
|
+
|
70
|
+
@args[:fetch_database] = opts[:fetch_database]
|
71
|
+
@args[:fetch_database_names] = opts[:fetch_database_names]
|
72
|
+
|
73
|
+
unless Dir.exists?(opts[:log_directory])
|
74
|
+
Trollop::die :log_directory, "log directory not found\n" <<
|
75
|
+
"Supplied directory: " + opts[:log_directory]
|
76
|
+
end
|
77
|
+
|
78
|
+
@args[:log_directory] = opts[:log_directory]
|
79
|
+
@args[:tmp_directory] = opts[:tmp_directory]
|
80
|
+
@args[:blast_hit_id] = opts[:blast_hit_id]
|
81
|
+
@args[:blast_hit_display_id] = opts[:blast_hit_display_id]
|
82
|
+
@args[:blast_algo] = opts[:blast_algo]
|
83
|
+
|
84
|
+
## Check System Dependancies ##
|
85
|
+
|
86
|
+
check_dependencies
|
87
|
+
|
88
|
+
## Execute fetch tool ##
|
89
|
+
|
90
|
+
if @args[:fetch_tool].include? "blastdbcmd"
|
91
|
+
blast = Quorum::FetchTools::BlastDb.new(@args)
|
92
|
+
blast.execute_blast_db_cmd
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Check system dependencies.
|
98
|
+
#
|
99
|
+
def check_dependencies
|
100
|
+
FETCH_TOOLS[@args[:fetch_tool]].each do |f|
|
101
|
+
system("which #{f} > /dev/null 2>&1")
|
102
|
+
if $?.exitstatus > 0
|
103
|
+
Trollop::die "Quorum dependency not found for tool " <<
|
104
|
+
"#{@args[:fetch_tool].to_s}. Please add `#{f}` to your PATH."
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if __FILE__ == $0
|
113
|
+
Quorum::Fetch.new
|
114
|
+
end
|
115
|
+
|
Binary file
|
Binary file
|
@@ -0,0 +1,106 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path("../../", __FILE__))
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Quorum
|
6
|
+
module FetchTools
|
7
|
+
#
|
8
|
+
# Blast DB Fetch Tool
|
9
|
+
#
|
10
|
+
class BlastDb
|
11
|
+
|
12
|
+
# blastdbcmd -dbtype
|
13
|
+
DB_TYPE = {
|
14
|
+
"blastn" => "nucl",
|
15
|
+
"blastx" => "prot",
|
16
|
+
"tblastn" => "nucl",
|
17
|
+
"blastp" => "prot"
|
18
|
+
}
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def initialize(args)
|
23
|
+
@tool = args[:fetch_tool]
|
24
|
+
@entry = args[:blast_hit_id]
|
25
|
+
@hit_display_id = args[:blast_hit_display_id]
|
26
|
+
@blast_algo = args[:blast_algo]
|
27
|
+
@log_directory = args[:log_directory]
|
28
|
+
@tmp_directory = args[:tmp_directory]
|
29
|
+
@fetch_database = args[:fetch_database]
|
30
|
+
@databases = args[:fetch_database_names]
|
31
|
+
|
32
|
+
@logger = Quorum::Logger.new(@log_directory)
|
33
|
+
|
34
|
+
@db = @databases.split(';')
|
35
|
+
@db.map! { |d| File.join(@fetch_database, d) }
|
36
|
+
|
37
|
+
@db_type = DB_TYPE[@blast_algo]
|
38
|
+
@db_type ||= 'guess'
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Generate the blastdbcmd(s).
|
43
|
+
#
|
44
|
+
def generate_blast_db_cmds
|
45
|
+
@cmds = []
|
46
|
+
@db.each do |d|
|
47
|
+
blastdbcmd = "blastdbcmd " <<
|
48
|
+
"-db '#{d}' " <<
|
49
|
+
"-dbtype '#{@db_type}' " <<
|
50
|
+
"-entry '#{@entry}' "
|
51
|
+
@cmds << blastdbcmd
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Parse the blastdbcmd returned sequence(s) and delete if @hit_display_id
|
57
|
+
# is not present.
|
58
|
+
#
|
59
|
+
def parse_and_send_results
|
60
|
+
seqs = @seqs.split('>')
|
61
|
+
seqs.delete_if { |s| s.empty? || !s.include?(@hit_display_id) }
|
62
|
+
if seqs.length != 1
|
63
|
+
@logger.log(
|
64
|
+
"Quorum::FetchTools::BlastDb#parse_and_send_results",
|
65
|
+
"blastdbcmd returned #{seqs.length} sequence(s). Please ensure " <<
|
66
|
+
"your Blast database source FASTA headers are unique across ALL " <<
|
67
|
+
"databases. See the entry above for details."
|
68
|
+
)
|
69
|
+
"An error occurred while processing your request."
|
70
|
+
else
|
71
|
+
">" << seqs.first
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
public
|
76
|
+
|
77
|
+
#
|
78
|
+
# Execute the blastdbcmd(s) and return the matching sequence.
|
79
|
+
#
|
80
|
+
# To make Blast execute as quickly as possible, each selected
|
81
|
+
# algorithm blasts against all supplied databases at once.
|
82
|
+
#
|
83
|
+
# See quorum/lib/search_tools/blast.rb for more information.
|
84
|
+
#
|
85
|
+
# One consequence of this is not knowing which Blast database to use
|
86
|
+
# when retrieving a hit sequence via blastdbcmd.
|
87
|
+
#
|
88
|
+
# See blastdbcmd -help for more information.
|
89
|
+
#
|
90
|
+
# To work around this issue, simply execute blastdbcmd against all
|
91
|
+
# Blast databases and filter on hit_display_id.
|
92
|
+
#
|
93
|
+
def execute_blast_db_cmd
|
94
|
+
generate_blast_db_cmds
|
95
|
+
@logger.log("NCBI Blast", @cmds.join('; '))
|
96
|
+
|
97
|
+
@seqs = ""
|
98
|
+
@cmds.each { |c| @seqs << `#{c} 2> /dev/null` }
|
99
|
+
|
100
|
+
seq = parse_and_send_results
|
101
|
+
$stdout.print seq
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -11,6 +11,8 @@ module Quorum
|
|
11
11
|
class Blast
|
12
12
|
|
13
13
|
class QuorumJob < ActiveRecord::Base
|
14
|
+
set_table_name "quorum_jobs"
|
15
|
+
|
14
16
|
has_one :quorum_blastn_job,
|
15
17
|
:foreign_key => "job_id"
|
16
18
|
has_many :quorum_blastn_job_reports,
|
@@ -33,38 +35,46 @@ module Quorum
|
|
33
35
|
end
|
34
36
|
|
35
37
|
class QuorumBlastnJob < ActiveRecord::Base
|
38
|
+
set_table_name "quorum_blastn_jobs"
|
36
39
|
belongs_to :quorum_job
|
37
40
|
has_many :quorum_blastn_job_reports
|
38
41
|
end
|
39
42
|
|
40
43
|
class QuorumBlastnJobReport < ActiveRecord::Base
|
44
|
+
set_table_name "quorum_blastn_job_reports"
|
41
45
|
belongs_to :quorum_blastn_job
|
42
46
|
end
|
43
47
|
|
44
48
|
class QuorumBlastxJob < ActiveRecord::Base
|
49
|
+
set_table_name "quorum_blastx_jobs"
|
45
50
|
belongs_to :quorum_job
|
46
51
|
has_many :quorum_blastx_job_reports
|
47
52
|
end
|
48
53
|
|
49
54
|
class QuorumBlastxJobReport < ActiveRecord::Base
|
55
|
+
set_table_name "quorum_blastx_job_reports"
|
50
56
|
belongs_to :quorum_blastx_job
|
51
57
|
end
|
52
58
|
|
53
59
|
class QuorumTblastnJob < ActiveRecord::Base
|
60
|
+
set_table_name "quorum_tblastn_jobs"
|
54
61
|
belongs_to :quorum_job
|
55
62
|
has_many :quorum_tblastn_job_reports
|
56
63
|
end
|
57
64
|
|
58
65
|
class QuorumTblastnJobReport < ActiveRecord::Base
|
66
|
+
set_table_name "quorum_tblastn_job_reports"
|
59
67
|
belongs_to :quorum_tblastn_job
|
60
68
|
end
|
61
69
|
|
62
70
|
class QuorumBlastpJob < ActiveRecord::Base
|
71
|
+
set_table_name "quorum_blastp_jobs"
|
63
72
|
belongs_to :quorum_job
|
64
73
|
has_many :quorum_blastp_job_reports
|
65
74
|
end
|
66
75
|
|
67
76
|
class QuorumBlastpJobReport < ActiveRecord::Base
|
77
|
+
set_table_name "quorum_blastp_job_reports"
|
68
78
|
belongs_to :quorum_blastp_job
|
69
79
|
end
|
70
80
|
|
@@ -235,26 +245,26 @@ module Quorum
|
|
235
245
|
end
|
236
246
|
|
237
247
|
#
|
238
|
-
# Format Blast report
|
248
|
+
# Format Blast report hit_display_id and hit_def.
|
239
249
|
#
|
240
250
|
# For added flexibility, Quorum doesn't parse seqids or deflines.
|
241
251
|
# Instead, format_hit splits the hit_def on whitespace and
|
242
|
-
# reports
|
252
|
+
# reports hit_display_id as the first element and hit_def as the second.
|
243
253
|
#
|
244
254
|
def format_hit(str)
|
245
|
-
|
255
|
+
hit_display_id = ""
|
246
256
|
hit_def = ""
|
247
257
|
|
248
258
|
hit = str.split(" ", 2)
|
249
259
|
if hit.length < 2
|
250
|
-
|
260
|
+
hit_display_id = hit.first
|
251
261
|
hit_def = "None"
|
252
262
|
else
|
253
|
-
|
263
|
+
hit_display_id = hit.first
|
254
264
|
hit_def = hit.last
|
255
265
|
end
|
256
266
|
|
257
|
-
return
|
267
|
+
return hit_display_id, hit_def
|
258
268
|
end
|
259
269
|
|
260
270
|
#
|
@@ -275,8 +285,9 @@ module Quorum
|
|
275
285
|
@data[:query_len] = iteration.query_len
|
276
286
|
|
277
287
|
iteration.each do |hit|
|
278
|
-
@data[:
|
288
|
+
@data[:hit_display_id], @data[:hit_def] = format_hit(hit.hit_def)
|
279
289
|
|
290
|
+
@data[:hit_id] = hit.hit_id
|
280
291
|
@data[:hit_accession] = hit.accession
|
281
292
|
@data[:hit_len] = hit.len
|
282
293
|
|
data/spec/models/job_spec.rb
CHANGED
@@ -4,6 +4,7 @@ describe Quorum::Job do
|
|
4
4
|
|
5
5
|
before(:each) do
|
6
6
|
@job = Quorum::Job.new()
|
7
|
+
ResqueSpec.reset!
|
7
8
|
end
|
8
9
|
|
9
10
|
it "fails validation without params (using error_on)" do
|
@@ -22,9 +23,6 @@ describe Quorum::Job do
|
|
22
23
|
end
|
23
24
|
|
24
25
|
it "queues workers after save" do
|
25
|
-
resque = double("Resque")
|
26
|
-
resque.stub(:enqueue)
|
27
|
-
|
28
26
|
@job.sequence = File.open(
|
29
27
|
File.expand_path("../../data/nucl_prot_seqs.txt", __FILE__)
|
30
28
|
).read
|
@@ -33,8 +31,9 @@ describe Quorum::Job do
|
|
33
31
|
@job.blastn_job.queue = true
|
34
32
|
@job.blastn_job.blast_dbs = ["tmp"]
|
35
33
|
|
36
|
-
@job.should_receive(:queue_workers)
|
37
34
|
@job.save!
|
35
|
+
|
36
|
+
Workers::System.should have_queue_size_of(1)
|
38
37
|
end
|
39
38
|
|
40
39
|
end
|
@@ -61,4 +61,9 @@ describe "Quorum" do
|
|
61
61
|
f = File.open(File.join(@dummy_path, "config", "routes.rb"), "r")
|
62
62
|
f.read.include?("mount Quorum::Engine => \"/quorum\"").should be_true
|
63
63
|
end
|
64
|
+
|
65
|
+
it "ensures Resque::Server is mounted in dummy/config/routes.rb" do
|
66
|
+
f = File.open(File.join(@dummy_path, "config", "routes.rb"), "r")
|
67
|
+
f.read.include?("mount Resque::Server.new, :at => \"/quorum/resque\"").should be_true
|
68
|
+
end
|
64
69
|
end
|
data/spec/requests/jobs_spec.rb
CHANGED
@@ -51,8 +51,12 @@ describe "Jobs" do
|
|
51
51
|
before(:all) do
|
52
52
|
Capybara.current_driver = :selenium
|
53
53
|
end
|
54
|
+
before(:each) do
|
55
|
+
ResqueSpec.reset!
|
56
|
+
ResqueSpec.inline = true
|
57
|
+
end
|
54
58
|
describe "submit sequences in attached file" do
|
55
|
-
it "check
|
59
|
+
it "check algorithms, fill in values, view results and download hit sequence" do
|
56
60
|
visit new_job_path
|
57
61
|
current_path.should eq(new_job_path)
|
58
62
|
|
@@ -109,13 +113,22 @@ describe "Jobs" do
|
|
109
113
|
click_link "Tblastn"
|
110
114
|
page.should have_content("Your search returned 0 hits.")
|
111
115
|
|
116
|
+
## Interact with the Blast results. ##
|
112
117
|
click_link "Blastn"
|
118
|
+
|
119
|
+
# Render modal box.
|
113
120
|
find("#blastn-results").find("td a").click
|
114
121
|
page.should have_content("Quorum Report Details")
|
115
122
|
page.should have_content("qseq")
|
116
123
|
page.should have_content("hseq")
|
124
|
+
# Download sequence
|
125
|
+
find("p.small a#download_sequence_1").click
|
126
|
+
page.should have_content("Fetching sequence...")
|
127
|
+
page.should have_content("Sequence Downloaded Successfully")
|
117
128
|
|
118
129
|
click_link "Blastp"
|
130
|
+
|
131
|
+
# Render modal box.
|
119
132
|
find("#blastp-results").find("td a").click
|
120
133
|
page.should have_content("Quorum Report Details")
|
121
134
|
page.should have_content("qseq")
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,7 @@ require 'rspec/rails'
|
|
5
5
|
require 'capybara/rspec'
|
6
6
|
require 'factory_girl'
|
7
7
|
require 'database_cleaner'
|
8
|
+
require 'resque_spec'
|
8
9
|
|
9
10
|
FactoryGirl.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
|
10
11
|
FactoryGirl.find_definitions
|
@@ -13,7 +14,7 @@ ENGINE_RAILS_ROOT = File.expand_path("../../", __FILE__)
|
|
13
14
|
|
14
15
|
# Requires supporting ruby files with custom matchers and macros, etc,
|
15
16
|
# in spec/support/ and its subdirectories.
|
16
|
-
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
17
|
+
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
|
17
18
|
|
18
19
|
RSpec.configure do |config|
|
19
20
|
config.mock_with :rspec
|
@@ -42,4 +43,34 @@ RSpec.configure do |config|
|
|
42
43
|
config.after(:each) do
|
43
44
|
DatabaseCleaner.clean
|
44
45
|
end
|
46
|
+
|
47
|
+
## Redis ##
|
48
|
+
REDIS_PID = File.expand_path("../dummy/tmp/pids/redis-test.pid", __FILE__)
|
49
|
+
REDIS_CACHE_PATH = File.expand_path("../dummy/tmp/cache/", __FILE__)
|
50
|
+
|
51
|
+
config.before(:suite) do
|
52
|
+
redis_options = {
|
53
|
+
"daemonize" => "yes",
|
54
|
+
"pidfile" => REDIS_PID,
|
55
|
+
"port" => 9736,
|
56
|
+
"timeout" => 300,
|
57
|
+
"save 900" => 1,
|
58
|
+
"save 300" => 1,
|
59
|
+
"save 60" => 10000,
|
60
|
+
"dbfilename" => "dump.rdb",
|
61
|
+
"dir" => REDIS_CACHE_PATH,
|
62
|
+
"loglevel" => "debug",
|
63
|
+
"logfile" => "stdout",
|
64
|
+
"databases" => 16
|
65
|
+
}.map { |k, v| "#{k} #{v}" }.join('
|
66
|
+
')
|
67
|
+
`echo '#{redis_options}' | redis-server -`
|
68
|
+
end
|
69
|
+
|
70
|
+
config.after(:suite) do
|
71
|
+
%x{
|
72
|
+
cat #{REDIS_PID} | xargs kill -QUIT
|
73
|
+
rm -f #{REDIS_CACHE_PATH}dump.rdb
|
74
|
+
}
|
75
|
+
end
|
45
76
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Capture STDOUT and STDERR
|
5
|
+
#
|
6
|
+
def capture(*streams)
|
7
|
+
streams.map! { |stream| stream.to_s }
|
8
|
+
begin
|
9
|
+
result = StringIO.new
|
10
|
+
streams.each { |stream| eval "$#{stream} = result" }
|
11
|
+
yield
|
12
|
+
ensure
|
13
|
+
streams.each { |stream| eval("$#{stream} = #{stream.upcase}") }
|
14
|
+
end
|
15
|
+
result.string
|
16
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require File.expand_path("../../dummy/quorum/lib/fetch_tools/blast_db", __FILE__)
|
3
|
+
|
4
|
+
describe "Quorum::FetchTools::BlastDb" do
|
5
|
+
describe "#execute_blast_db_cmd" do
|
6
|
+
before(:each) do
|
7
|
+
# Set the args as though we executed bin/fetch
|
8
|
+
@args = {
|
9
|
+
:fetch_tool => "blastdbcmd",
|
10
|
+
:blast_hit_id => nil,
|
11
|
+
:blast_hit_display_id => nil,
|
12
|
+
:blast_algo => nil,
|
13
|
+
:fetch_database_names => "test",
|
14
|
+
|
15
|
+
:log_directory => File.expand_path(
|
16
|
+
"../../dummy/quorum/log", __FILE__
|
17
|
+
),
|
18
|
+
:tmp_directory => File.expand_path(
|
19
|
+
"../../dummy/quorum/tmp", __FILE__
|
20
|
+
),
|
21
|
+
:fetch_database => File.expand_path(
|
22
|
+
"../../dummy/quorum/blastdb", __FILE__
|
23
|
+
)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "executes blastdbcmd for blastn and returns correct sequence" do
|
28
|
+
@args[:blast_hit_id] = "gnl|BL_ORD_ID|0"
|
29
|
+
@args[:blast_hit_display_id] = "TOG900080"
|
30
|
+
@args[:blast_algo] = "blastn"
|
31
|
+
|
32
|
+
seqs = File.readlines(
|
33
|
+
File.expand_path("../../data/nucl_seqs.txt", __FILE__)
|
34
|
+
)
|
35
|
+
seqs[0].gsub!(">", "")
|
36
|
+
seqs.insert(0, ">" + @args[:blast_hit_id] + " ")
|
37
|
+
|
38
|
+
# Sequence #execute_blast_db_cmd should return.
|
39
|
+
seq = seqs.slice(0,3).join("").gsub("\n", "")
|
40
|
+
|
41
|
+
fetch = Quorum::FetchTools::BlastDb.new(@args)
|
42
|
+
lambda {
|
43
|
+
output = capture(:stdout) {
|
44
|
+
fetch.execute_blast_db_cmd
|
45
|
+
}
|
46
|
+
output.gsub("\n", "").should eq(seq)
|
47
|
+
}.should_not raise_error
|
48
|
+
end
|
49
|
+
|
50
|
+
it "returns error message if filtered sequence.length != 1" do
|
51
|
+
@args[:blast_hit_id] = "null"
|
52
|
+
@args[:blast_hit_display_id] = "null"
|
53
|
+
@args[:blast_algo] = "blastp"
|
54
|
+
fetch = Quorum::FetchTools::BlastDb.new(@args)
|
55
|
+
lambda {
|
56
|
+
output = capture(:stdout) {
|
57
|
+
fetch.execute_blast_db_cmd
|
58
|
+
}
|
59
|
+
output.should eq("An error occurred while processing your request.")
|
60
|
+
}.should_not raise_error
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -1,8 +1,114 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require
|
2
|
+
require File.expand_path("../../dummy/quorum/lib/search_tools/blast", __FILE__)
|
3
3
|
|
4
|
-
# Let the request specs handle Quorum::SearchTools
|
5
4
|
describe "Quorum::SearchTools::Blast" do
|
6
5
|
describe "#execute_blast" do
|
6
|
+
before(:each) do
|
7
|
+
# Set args as though we executed bin/search.
|
8
|
+
@args = {
|
9
|
+
:search_tool => "blastn",
|
10
|
+
:id => nil,
|
11
|
+
:log_directory => File.expand_path(
|
12
|
+
"../../dummy/quorum/log", __FILE__
|
13
|
+
),
|
14
|
+
:tmp_directory => File.expand_path(
|
15
|
+
"../../dummy/quorum/tmp", __FILE__
|
16
|
+
),
|
17
|
+
:search_database => File.expand_path(
|
18
|
+
"../../dummy/quorum/blastdb", __FILE__
|
19
|
+
),
|
20
|
+
:threads => 1
|
21
|
+
}
|
22
|
+
|
23
|
+
@job = Quorum::Job.new()
|
24
|
+
|
25
|
+
@job.sequence = File.open(
|
26
|
+
File.expand_path("../../data/nucl_prot_seqs.txt", __FILE__)
|
27
|
+
).read
|
28
|
+
|
29
|
+
@job.build_blastn_job
|
30
|
+
@job.blastn_job.queue = true
|
31
|
+
@job.blastn_job.blast_dbs = ["test"]
|
32
|
+
|
33
|
+
@job.build_blastx_job
|
34
|
+
@job.blastx_job.queue = true
|
35
|
+
@job.blastx_job.blast_dbs = ["test"]
|
36
|
+
|
37
|
+
@job.build_tblastn_job
|
38
|
+
@job.tblastn_job.queue = true
|
39
|
+
@job.tblastn_job.blast_dbs = ["test"]
|
40
|
+
|
41
|
+
@job.build_blastp_job
|
42
|
+
@job.blastp_job.queue = true
|
43
|
+
@job.blastp_job.blast_dbs = ["test"]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "executes blastn on a given dataset" do
|
47
|
+
@job.stub(:queue_workers)
|
48
|
+
@job.save!
|
49
|
+
|
50
|
+
@args[:id] = @job.id
|
51
|
+
|
52
|
+
blast = Quorum::SearchTools::Blast.new(@args)
|
53
|
+
expect {
|
54
|
+
blast.execute_blast
|
55
|
+
}.to_not raise_error
|
56
|
+
|
57
|
+
Dir.glob(
|
58
|
+
File.join(@args[:tmp_directory], "*")
|
59
|
+
).length.should be == 0
|
60
|
+
end
|
61
|
+
|
62
|
+
it "executes blastx on a given dataset" do
|
63
|
+
@job.stub(:queue_workers)
|
64
|
+
@job.save!
|
65
|
+
|
66
|
+
@args[:search_tool] = "blastx"
|
67
|
+
@args[:id] = @job.id
|
68
|
+
|
69
|
+
blast = Quorum::SearchTools::Blast.new(@args)
|
70
|
+
expect {
|
71
|
+
blast.execute_blast
|
72
|
+
}.to raise_error(SystemExit)
|
73
|
+
|
74
|
+
Dir.glob(
|
75
|
+
File.join(@args[:tmp_directory], "*")
|
76
|
+
).length.should be == 0
|
77
|
+
end
|
78
|
+
|
79
|
+
it "executes tblastn on a given dataset" do
|
80
|
+
@job.stub(:queue_workers)
|
81
|
+
@job.save!
|
82
|
+
|
83
|
+
@args[:search_tool] = "tblastn"
|
84
|
+
@args[:id] = @job.id
|
85
|
+
|
86
|
+
blast = Quorum::SearchTools::Blast.new(@args)
|
87
|
+
expect {
|
88
|
+
blast.execute_blast
|
89
|
+
}.to raise_error(SystemExit)
|
90
|
+
|
91
|
+
Dir.glob(
|
92
|
+
File.join(@args[:tmp_directory], "*")
|
93
|
+
).length.should be == 0
|
94
|
+
end
|
95
|
+
|
96
|
+
it "executes blastp on a given dataset" do
|
97
|
+
@job.stub(:queue_workers)
|
98
|
+
@job.save!
|
99
|
+
|
100
|
+
@args[:search_tool] = "blastp"
|
101
|
+
@args[:id] = @job.id
|
102
|
+
|
103
|
+
blast = Quorum::SearchTools::Blast.new(@args)
|
104
|
+
expect {
|
105
|
+
blast.execute_blast
|
106
|
+
}.to_not raise_error
|
107
|
+
|
108
|
+
Dir.glob(
|
109
|
+
File.join(@args[:tmp_directory], "*")
|
110
|
+
).length.should be == 0
|
111
|
+
end
|
112
|
+
|
7
113
|
end
|
8
114
|
end
|