sequenceserver 1.1.0.beta12 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.travis.yml +5 -5
- data/README.md +86 -43
- data/bin/sequenceserver +29 -32
- data/lib/sequenceserver.rb +86 -123
- data/lib/sequenceserver/api_errors.rb +3 -1
- data/lib/sequenceserver/blast/hit.rb +48 -28
- data/lib/sequenceserver/blast/job.rb +28 -8
- data/lib/sequenceserver/blast/report.rb +52 -15
- data/lib/sequenceserver/database.rb +71 -0
- data/lib/sequenceserver/exceptions.rb +5 -10
- data/lib/sequenceserver/job.rb +8 -1
- data/lib/sequenceserver/links.rb +49 -17
- data/lib/sequenceserver/logger.rb +2 -2
- data/lib/sequenceserver/routes.rb +20 -14
- data/lib/sequenceserver/sys.rb +86 -0
- data/lib/sequenceserver/version.rb +1 -1
- data/public/css/sequenceserver.min.css +3 -3
- data/public/js/alignment_exporter.js +1 -1
- data/public/js/hsp.js +1 -1
- data/public/js/report.js +76 -63
- data/public/js/search.js +29 -6
- data/public/sequenceserver-report.min.js +4 -4
- data/public/sequenceserver-search.min.js +2 -2
- data/sequenceserver.gemspec +3 -2
- data/spec/blast_versions/blast_2.2.30/blast_2.2.30_spec.rb +228 -0
- data/spec/blast_versions/blast_2.2.30/import_spec_capybara_local_2.2.30.rb +53 -0
- data/spec/blast_versions/blast_2.2.31/blast_2.2.31_spec.rb +228 -0
- data/spec/blast_versions/blast_2.2.31/import_spec_capybara_local_2.2.31.rb +53 -0
- data/spec/{blast_spec.rb → blast_versions/blast_2.3.0/blast_2.3.0_spec.rb} +8 -37
- data/spec/blast_versions/blast_2.3.0/import_spec_capybara_local_2.3.0.rb +52 -0
- data/spec/blast_versions/blast_2.4.0/blast_2.4.0_spec.rb +228 -0
- data/spec/blast_versions/blast_2.4.0/import_spec_capybara_local_2.4.0.rb +52 -0
- data/spec/blast_versions/blast_2.5.0/blast_2.5.0_spec.rb +228 -0
- data/spec/blast_versions/blast_2.5.0/import_spec_capybara_local_2.5.0.rb +53 -0
- data/spec/blast_versions/blast_2.6.0/blast_2.6.0_spec.rb +228 -0
- data/spec/blast_versions/blast_2.6.0/import_spec_capybara_local_2.6.0.rb +52 -0
- data/spec/blast_versions/blast_2.7.1/blast_2.7.1_spec.rb +228 -0
- data/spec/blast_versions/blast_2.7.1/import_spec_capybara_local_2.7.1.rb +56 -0
- data/spec/blast_versions/blast_2.8.1/blast_2.8.1_spec.rb +228 -0
- data/spec/blast_versions/blast_2.8.1/import_spec_capybara_local_2.8.1.rb +55 -0
- data/spec/blast_versions/blast_2.9.0/blast_2.9.0_spec.rb +228 -0
- data/spec/blast_versions/blast_2.9.0/import_spec_capybara_local_2.9.0.rb +53 -0
- data/spec/blast_versions/diamond_0.9.24/diamond_0.9.24_spec.rb +176 -0
- data/spec/blast_versions/diamond_0.9.24/import_spec_capybara_local_0.9.24.rb +39 -0
- data/spec/capybara_spec.local.rb +190 -0
- data/spec/capybara_spec.rb +15 -13
- data/spec/config_spec.rb +1 -1
- data/spec/database/funky_ids/funky_ids.fa +2 -0
- data/spec/database/funky_ids/funky_ids.fa.nhd +8 -0
- data/spec/database/funky_ids/funky_ids.fa.nhi +0 -0
- data/spec/database/funky_ids/funky_ids.fa.nhr +0 -0
- data/spec/database/funky_ids/funky_ids.fa.nin +0 -0
- data/spec/database/funky_ids/funky_ids.fa.nog +0 -0
- data/spec/database/funky_ids/funky_ids.fa.nsd +15 -13
- data/spec/database/funky_ids/funky_ids.fa.nsi +0 -0
- data/spec/database/funky_ids/funky_ids.fa.nsq +0 -0
- data/spec/database_spec.rb +2 -2
- data/spec/dotdir/blast_2.2.30/blastn/BLASTN_XML_2.2.30.xml +1201 -0
- data/spec/dotdir/blast_2.2.30/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.30/blastn_nohits/BLASTN_NO_HITS_XML_2.2.30.xml +866 -0
- data/spec/dotdir/blast_2.2.30/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.30/blastp/BLASTP_XML_2.2.30.xml +1181 -0
- data/spec/dotdir/blast_2.2.30/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.30/blastx/BLASTX_XML_2.2.30.xml +1181 -0
- data/spec/dotdir/blast_2.2.30/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.30/tblastn/TBLASTN_XML_2.2.30.xml +1181 -0
- data/spec/dotdir/blast_2.2.30/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.30/tblastx/TBLASTX_XML_2.2.30.xml +8857 -0
- data/spec/dotdir/blast_2.2.30/tblastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.31/blastn/BLASTN_XML_2.2.31.xml +1201 -0
- data/spec/dotdir/blast_2.2.31/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.31/blastn_nohits/BLASTN_NO_HITS_XML_2.2.31.xml +866 -0
- data/spec/dotdir/blast_2.2.31/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.31/blastp/BLASTP_XML_2.2.31.xml +1181 -0
- data/spec/dotdir/blast_2.2.31/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.31/blastx/BLASTX_XML_2.2.31.xml +1181 -0
- data/spec/dotdir/blast_2.2.31/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.31/tblastn/TBLASTN_XML_2.2.31.xml +1181 -0
- data/spec/dotdir/blast_2.2.31/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.2.31/tblastx/TBLASTX_XML_2.2.31.xml +8857 -0
- data/spec/dotdir/blast_2.2.31/tblastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.3.0/blastn/BLASTN_XML_2.3.0.xml +1201 -0
- data/spec/dotdir/blast_2.3.0/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.3.0/blastn_nohits/BLASTN_NO_HITS_XML_2.3.0.xml +866 -0
- data/spec/dotdir/blast_2.3.0/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.3.0/blastp/BLASTP_XML_2.3.0.xml +1181 -0
- data/spec/dotdir/blast_2.3.0/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.3.0/blastx/BLASTX_XML_2.3.0.xml +1181 -0
- data/spec/dotdir/blast_2.3.0/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.3.0/tblastn/TBLASTN_XML_2.3.0.xml +1181 -0
- data/spec/dotdir/blast_2.3.0/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.3.0/tblastx/TBLASTX_XML_2.3.0.xml +8857 -0
- data/spec/dotdir/blast_2.3.0/tblastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.4.0/blastn/BLASTN_XML_2.4.0.xml +1201 -0
- data/spec/dotdir/blast_2.4.0/blastn/TBLASTN_XML_2.4.0.xml +1181 -0
- data/spec/dotdir/blast_2.4.0/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.4.0/blastn_nohits/BLASTN_NO_HITS_XML_2.4.0.xml +866 -0
- data/spec/dotdir/blast_2.4.0/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.4.0/blastp/BLASTP_XML_2.4.0.xml +1181 -0
- data/spec/dotdir/blast_2.4.0/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.4.0/blastx/BLASTX_XML_2.4.0.xml +1181 -0
- data/spec/dotdir/blast_2.4.0/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.4.0/tblastn/TBLASTN_XML_2.4.0.xml +1181 -0
- data/spec/dotdir/blast_2.4.0/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.4.0/tblastx/TBLASTX_XML_2.4.0.xml +8857 -0
- data/spec/dotdir/blast_2.4.0/tblastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.5.0/blastn/BLASTN_LONG_XML_2.5.0.xml +18813 -0
- data/spec/dotdir/blast_2.5.0/blastn/BLASTN_XML_2.5.0.xml +1201 -0
- data/spec/dotdir/blast_2.5.0/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.5.0/blastn_nohits/BLASTN_NO_HITS_XML_2.5.0.xml +866 -0
- data/spec/dotdir/blast_2.5.0/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.5.0/blastp/BLASTP +2161 -0
- data/spec/dotdir/blast_2.5.0/blastp/BLASTP_XML_2.5.0.xml +1181 -0
- data/spec/dotdir/blast_2.5.0/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.5.0/blastx/BLASTX +28080 -0
- data/spec/dotdir/blast_2.5.0/blastx/BLASTX_XML_2.5.0.xml +1181 -0
- data/spec/dotdir/blast_2.5.0/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.5.0/tblastn/TBLASTN +29486 -0
- data/spec/dotdir/blast_2.5.0/tblastn/TBLASTN_XML_2.5.0.xml +1181 -0
- data/spec/dotdir/blast_2.5.0/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.5.0/tblastx/TBLASTX +180859 -0
- data/spec/dotdir/blast_2.5.0/tblastx/TBLASTX_XML_2.5.0.xml +8857 -0
- data/spec/dotdir/blast_2.5.0/tblastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.6.0/blastn/BLASTN_XML_2.6.0.xml +1201 -0
- data/spec/dotdir/blast_2.6.0/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.6.0/blastn_nohits/BLASTN_NO_HITS_XML_2.6.0.xml +866 -0
- data/spec/dotdir/blast_2.6.0/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.6.0/blastp/BLASTP_XML_2.6.0.xml +1181 -0
- data/spec/dotdir/blast_2.6.0/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.6.0/blastx/BLASTX_XML_2.6.0.xml +1181 -0
- data/spec/dotdir/blast_2.6.0/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.6.0/tblastn/TBLASTN_XML_2.6.0.xml +1181 -0
- data/spec/dotdir/blast_2.6.0/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.6.0/tblastx/TBLASTX_XML_2.6.0.xml +8857 -0
- data/spec/dotdir/blast_2.6.0/tblastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.7.1/blastn/BLASTN_XML_2.7.1.xml +1201 -0
- data/spec/dotdir/blast_2.7.1/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.7.1/blastn_nohits/BLASTN_NO_HITS_XML_2.7.1.xml +866 -0
- data/spec/dotdir/blast_2.7.1/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.7.1/blastp/BLASTP_XML_2.7.1.xml +1181 -0
- data/spec/dotdir/blast_2.7.1/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.7.1/blastx/BLASTX_XML_2.7.1.xml +1181 -0
- data/spec/dotdir/blast_2.7.1/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.7.1/tblastn/TBLASTN_XML_2.7.1.xml +1181 -0
- data/spec/dotdir/blast_2.7.1/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.7.1/tblastx/TBLASTX_XML_2.7.1.xml +8857 -0
- data/spec/dotdir/blast_2.7.1/tblastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.8.1/blastn/BLASTN_XML_2.8.1.xml +1201 -0
- data/spec/dotdir/blast_2.8.1/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.8.1/blastn_nohits/BLASTN_NO_HITS_XML_2.8.1.xml +866 -0
- data/spec/dotdir/blast_2.8.1/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.8.1/blastp/BLASTP_XML_2.8.1.xml +1181 -0
- data/spec/dotdir/blast_2.8.1/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.8.1/blastx/BLASTX_XML_2.8.1.xml +1181 -0
- data/spec/dotdir/blast_2.8.1/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.8.1/tblastn/TBLASTN_XML_2.8.1.xml +1181 -0
- data/spec/dotdir/blast_2.8.1/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.8.1/tblastx/TBLASTX_XML_2.8.1.xml +8857 -0
- data/spec/dotdir/blast_2.8.1/tblastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.9.0/blastn/BLASTN_XML_2.9.0.xml +1201 -0
- data/spec/dotdir/blast_2.9.0/blastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.9.0/blastn_nohits/BLASTN_NO_HITS_XML_2.9.0.xml +866 -0
- data/spec/dotdir/blast_2.9.0/blastn_nohits/job.yaml +8 -0
- data/spec/dotdir/blast_2.9.0/blastp/BLASTP_XML_2.9.0.xml +1181 -0
- data/spec/dotdir/blast_2.9.0/blastp/job.yaml +8 -0
- data/spec/dotdir/blast_2.9.0/blastx/BLASTX_XML_2.9.0.xml +1181 -0
- data/spec/dotdir/blast_2.9.0/blastx/job.yaml +8 -0
- data/spec/dotdir/blast_2.9.0/tblastn/TBLASTN_XML_2.9.0.xml +1181 -0
- data/spec/dotdir/blast_2.9.0/tblastn/job.yaml +8 -0
- data/spec/dotdir/blast_2.9.0/tblastx/TBLASTX_XML_2.9.0.xml +8857 -0
- data/spec/dotdir/blast_2.9.0/tblastx/job.yaml +8 -0
- data/spec/dotdir/diamond_0.9.24/blastp/DIAMOND_BLASTP_0.9.24.xml +1040 -0
- data/spec/dotdir/diamond_0.9.24/blastp/job.yaml +8 -0
- data/spec/dotdir/diamond_0.9.24/blastx/DIAMOND_BLASTX_0.9.24.xml +1040 -0
- data/spec/dotdir/diamond_0.9.24/blastx/job.yaml +8 -0
- data/spec/dotdir/diamond_0.9.24/blastx_nohits/DIAMOND_BLASTX_NOHITS_0.9.24.xml +41 -0
- data/spec/dotdir/diamond_0.9.24/blastx_nohits/job.yaml +8 -0
- data/spec/download_helper.rb +32 -0
- data/spec/import_spec_capybara_local.rb +61 -0
- data/spec/sequence_spec.rb +1 -2
- data/spec/sequences/Nucleotide_TP53_COX41.fasta +15 -0
- data/spec/sequences/Protein_TP53_COX41.fasta +12 -0
- data/spec/sequences/Query_1_SI2_2_0_06267.txt +6 -0
- data/spec/{nucleotide_query.fa → sequences/nucleotide_query.fa} +0 -0
- data/spec/sequences/problematic_query.fa +5 -0
- data/spec/sequences/protein_query.fa +9 -0
- data/spec/sequences/sample_query_fire_ant_obps.fa +44 -0
- data/spec/sequences/sequenceserver-SI2.2.0_06267.fa +5 -0
- data/spec/sequenceserver_spec.rb +3 -3
- data/spec/spec_helper.rb +59 -0
- metadata +185 -28
- data/spec/protein_query.fa +0 -21
- data/spec/sample_reports/blastn_sample/job.yaml +0 -10
- data/spec/sample_reports/blastn_sample/stdout +0 -144
- data/spec/sample_reports/blastp_sample/job.yaml +0 -10
- data/spec/sample_reports/blastp_sample/stdout +0 -1187
- data/spec/sample_reports/blastx_sample/job.yaml +0 -10
- data/spec/sample_reports/blastx_sample/stdout +0 -1191
- data/spec/sample_reports/no_hits_sample/job.yaml +0 -10
- data/spec/sample_reports/no_hits_sample/stdout +0 -130
- data/spec/sample_reports/tblastn_sample/job.yaml +0 -10
- data/spec/sample_reports/tblastn_sample/stdout +0 -1107
- data/spec/sample_reports/tblastx_sample/job.yaml +0 -10
- data/spec/sample_reports/tblastx_sample/stdout +0 -2422
- data/spec/sample_reports/with_hits_sample/job.yaml +0 -10
- data/spec/sample_reports/with_hits_sample/stdout +0 -1489
@@ -1,12 +1,9 @@
|
|
1
1
|
module SequenceServer
|
2
2
|
# Define BLAST::Hit.
|
3
3
|
module BLAST
|
4
|
-
# Hit
|
5
|
-
# in ascending order of evalue.
|
4
|
+
# Hit object to store all the hits per Query.
|
6
5
|
Hit = Struct.new(:query, :number, :id, :accession, :title,
|
7
6
|
:length, :sciname, :qcovs, :hsps) do
|
8
|
-
include Links
|
9
|
-
|
10
7
|
def initialize(*args)
|
11
8
|
args[1] = args[1].to_i
|
12
9
|
args[4] = '' if args[4] == 'No definition line'
|
@@ -16,29 +13,45 @@ module SequenceServer
|
|
16
13
|
super
|
17
14
|
end
|
18
15
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
16
|
+
# This gets called when #to_json is called on report object in routes. We
|
17
|
+
# cannot use the to_json method provided by Struct class because what we
|
18
|
+
# want to send to the browser differs from the attributes declared with
|
19
|
+
# Struct class. Some of these are derived data such as score, identity,
|
20
|
+
# custom links. While some attributes are necessary for internal
|
21
|
+
# representation.
|
22
|
+
def to_json(*args)
|
23
|
+
# List all attributes that we want to send to the browser.
|
24
|
+
properties = %i[number id accession title length score identity
|
25
|
+
qcovs sciname evalue hsps links]
|
26
|
+
properties.inject({}) { |h, k| h[k] = send(k); h }.to_json(*args)
|
22
27
|
end
|
23
28
|
|
24
|
-
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
###
|
30
|
+
# Link generator functionality.
|
31
|
+
###
|
32
|
+
|
33
|
+
# Include the Links module.
|
34
|
+
include Links
|
30
35
|
|
36
|
+
# Links returns a list of Hashes that can be easily turned into an href
|
37
|
+
# in the client. These are derived by calling link generators, that is,
|
38
|
+
# instance methods of the Links module.
|
31
39
|
def links
|
32
40
|
links = Links.instance_methods.map { |m| send m }
|
33
41
|
links.compact!
|
34
42
|
links.sort_by { |link| [link[:order], link[:title]] }
|
35
43
|
end
|
36
44
|
|
45
|
+
# Returns the database type (nucleotide or protein).
|
46
|
+
def dbtype
|
47
|
+
report.dbtype
|
48
|
+
end
|
49
|
+
|
37
50
|
# Returns a list of databases that contain this hit.
|
38
51
|
#
|
39
52
|
# e.g., whichdb('SI_2.2.23') => [<Database: ...>, ...]
|
40
53
|
def whichdb
|
41
|
-
querydb.select { |db| db.include? id }
|
54
|
+
report.querydb.select { |db| db.include? id }
|
42
55
|
end
|
43
56
|
|
44
57
|
# Returns tuple of tuple indicating start and end coordinates of matched
|
@@ -52,26 +65,33 @@ module SequenceServer
|
|
52
65
|
[[qstart_min, qend_max], [sstart_min, send_max]]
|
53
66
|
end
|
54
67
|
|
55
|
-
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
68
|
+
###
|
69
|
+
# Score, identity, and evalue attributes below are used in tabular summary
|
70
|
+
# of hits in the HTML report. At some point we should move these to the
|
71
|
+
# client.
|
72
|
+
###
|
60
73
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
h[k] = send(k)
|
65
|
-
h
|
66
|
-
}.to_json(*args)
|
74
|
+
# Returns the sum of scores of all HSPs.
|
75
|
+
def score
|
76
|
+
hsps.map(&:score).reduce(:+)
|
67
77
|
end
|
68
78
|
|
69
|
-
|
79
|
+
# Returns the sum of identity of all HSPs divided by sum of length of all
|
80
|
+
# HSPs (expressed as percentage).
|
81
|
+
def identity
|
82
|
+
hsps.map(&:identity).reduce(:+) * 100 / hsps.map(&:length).reduce(:+)
|
83
|
+
end
|
70
84
|
|
71
|
-
|
72
|
-
|
85
|
+
# Returns the minimum evalue of all HSPs of the Hit. This is shown in the
|
86
|
+
# tabular overview of hits in the HTML report.
|
87
|
+
def evalue
|
88
|
+
hsps.first.evalue
|
73
89
|
end
|
74
90
|
|
91
|
+
private
|
92
|
+
|
93
|
+
# Returns the report object that this hit is a part of. This is used to
|
94
|
+
# access list of databases etc.
|
75
95
|
def report
|
76
96
|
query.report
|
77
97
|
end
|
@@ -6,13 +6,26 @@ module SequenceServer
|
|
6
6
|
# Extends SequenceServer::Job to describe a BLAST job.
|
7
7
|
class Job < Job
|
8
8
|
def initialize(params)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
if params.key?(:xml)
|
10
|
+
super do
|
11
|
+
@imported_xml_file = File.basename params[:xml]
|
12
|
+
# Copy over the XML file to job directory so that a job dir in
|
13
|
+
# itself is self-contained. This will help with tests among
|
14
|
+
# other things.
|
15
|
+
FileUtils.cp(params[:xml], dir)
|
16
|
+
@advanced_params = {}
|
17
|
+
@databases = []
|
18
|
+
done!
|
19
|
+
end
|
20
|
+
else
|
21
|
+
validate params
|
22
|
+
super do
|
23
|
+
@method = params[:method]
|
24
|
+
@qfile = store('query.fa', params[:sequence])
|
25
|
+
@databases = Database[params[:databases]]
|
26
|
+
@options = params[:advanced].to_s.strip + defaults
|
27
|
+
@advanced_params = parse_advanced params[:advanced]
|
28
|
+
end
|
16
29
|
end
|
17
30
|
end
|
18
31
|
|
@@ -22,6 +35,13 @@ module SequenceServer
|
|
22
35
|
# Attributes used by us - should be considered private.
|
23
36
|
attr_reader :method, :qfile, :databases, :options
|
24
37
|
|
38
|
+
# :nodoc:
|
39
|
+
# Returns path to the imported xml file if the job was created using the
|
40
|
+
# --import switch. Returns nil otherwise.
|
41
|
+
def imported_xml_file
|
42
|
+
File.join(dir, @imported_xml_file) if @imported_xml_file
|
43
|
+
end
|
44
|
+
|
25
45
|
# Returns the command that will be executed. Job super class takes care
|
26
46
|
# of actual execution.
|
27
47
|
def command
|
@@ -146,7 +166,7 @@ module SequenceServer
|
|
146
166
|
end
|
147
167
|
|
148
168
|
def disallowed_options
|
149
|
-
/-out|-html|-outfmt|-db|-query|-num_threads/i
|
169
|
+
/-out|-html|-outfmt|-db |-query|-num_threads/i
|
150
170
|
end
|
151
171
|
end
|
152
172
|
end
|
@@ -29,18 +29,38 @@ module SequenceServer
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
# :nodoc:
|
33
32
|
# Attributes parsed out from XML output.
|
34
|
-
attr_reader :program, :program_version
|
35
|
-
|
36
|
-
|
33
|
+
attr_reader :program, :program_version, :params, :stats, :queries
|
34
|
+
|
35
|
+
# This is obtained from the job object.
|
36
|
+
attr_reader :querydb
|
37
|
+
|
38
|
+
# Returns database type (nucleotide or protein) used for running BLAST
|
39
|
+
# search. If we ran the BLAST search, this information is available
|
40
|
+
# from Job#databases. For imported XML, this is inferred from
|
41
|
+
# Report#program (i.e., the BLAST algorithm)
|
42
|
+
def dbtype
|
43
|
+
return @dbtype if @dbtype
|
44
|
+
@dbtype = if @querydb.empty?
|
45
|
+
case program
|
46
|
+
when /blastn|tblastn|tblastx/
|
47
|
+
'nucleotide'
|
48
|
+
when /blastp|blastx/
|
49
|
+
'protein'
|
50
|
+
end
|
51
|
+
else
|
52
|
+
@querydb.first.type
|
53
|
+
end
|
54
|
+
end
|
37
55
|
|
38
56
|
def to_json
|
39
57
|
[:querydb, :program, :program_version, :params, :stats,
|
40
58
|
:queries].inject({}) { |h, k|
|
41
59
|
h[k] = send(k)
|
42
60
|
h
|
43
|
-
}.update(search_id: job.id,
|
61
|
+
}.update(search_id: job.id,
|
62
|
+
submitted_at: job.submitted_at.utc,
|
63
|
+
imported_xml: !!job.imported_xml_file).to_json
|
44
64
|
end
|
45
65
|
|
46
66
|
private
|
@@ -48,12 +68,25 @@ module SequenceServer
|
|
48
68
|
# Generate report.
|
49
69
|
def generate
|
50
70
|
job.raise!
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
71
|
+
if job.imported_xml_file
|
72
|
+
xml_ir = parse_xml File.read(job.imported_xml_file)
|
73
|
+
tsv_ir = Hash.new do |h1,k1|
|
74
|
+
h1[k1] = Hash.new do |h2,k2|
|
75
|
+
h2[k2]=['','',[]]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
extract_program_info xml_ir
|
79
|
+
extract_params xml_ir
|
80
|
+
extract_stats xml_ir
|
81
|
+
extract_queries xml_ir, tsv_ir
|
82
|
+
else
|
83
|
+
xml_ir = parse_xml File.read(Formatter.run(job, 'xml').file)
|
84
|
+
tsv_ir = parse_tsv File.read(Formatter.run(job, 'custom_tsv').file )
|
85
|
+
extract_program_info xml_ir
|
86
|
+
extract_params xml_ir
|
87
|
+
extract_stats xml_ir
|
88
|
+
extract_queries xml_ir, tsv_ir
|
89
|
+
end
|
57
90
|
end
|
58
91
|
|
59
92
|
# Make program name and program name + version available via `program`
|
@@ -107,10 +140,14 @@ module SequenceServer
|
|
107
140
|
def extract_hits(xml_ir, tsv_ir, query)
|
108
141
|
return if xml_ir == ["\n"] # => No hits.
|
109
142
|
xml_ir.each do |n|
|
110
|
-
# If hit comes from a non -parse_seqids database, then
|
111
|
-
#
|
112
|
-
#
|
113
|
-
|
143
|
+
# If hit comes from a non -parse_seqids database, then id (n[1]) is a
|
144
|
+
# BLAST assigned internal id of the format 'gnl|BL_ORD_ID|serial'. We
|
145
|
+
# assign the id to accession (because we use accession for sequence
|
146
|
+
# retrieval and this id is what blastdbcmd expects for non
|
147
|
+
# -parse_seqids databases) and parse the hit defline to
|
148
|
+
# obtain id and title ourselves (we use id and title
|
149
|
+
# for display purposes).
|
150
|
+
if n[1] =~ /^gnl\|BL_ORD_ID\|\d+/
|
114
151
|
n[3] = n[1]
|
115
152
|
defline = n[2].split
|
116
153
|
n[1] = defline.shift
|
@@ -36,6 +36,19 @@ module SequenceServer
|
|
36
36
|
|
37
37
|
attr_reader :id
|
38
38
|
|
39
|
+
def retrieve(accession, coords = nil)
|
40
|
+
cmd = "blastdbcmd -db #{name} -entry '#{accession}'"
|
41
|
+
if coords
|
42
|
+
cmd << " -range #{coords}"
|
43
|
+
end
|
44
|
+
out, = sys(cmd, path: config[:bin])
|
45
|
+
out.chomp
|
46
|
+
rescue CommandFailed
|
47
|
+
# Command failed beacuse stdout was empty, meaning accession not
|
48
|
+
# present in this database.
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
39
52
|
def include?(accession)
|
40
53
|
cmd = "blastdbcmd -entry '#{accession}' -db #{name}"
|
41
54
|
out, = sys(cmd, path: config[:bin])
|
@@ -103,6 +116,64 @@ module SequenceServer
|
|
103
116
|
collection.values.to_json
|
104
117
|
end
|
105
118
|
|
119
|
+
# Retrieve given loci from the databases we have.
|
120
|
+
#
|
121
|
+
# loci to retrieve are specified as a String:
|
122
|
+
#
|
123
|
+
# "accession_1,accession_2:start-stop,accession_3"
|
124
|
+
#
|
125
|
+
# Return value is a FASTA format String containing sequences in the same
|
126
|
+
# order in which they were requested. If an accession could not be found,
|
127
|
+
# a commented out error message is included in place of the sequence.
|
128
|
+
# Sequences are retrieved from the first database in which the accession
|
129
|
+
# is found. The returned sequences can, thus, be incorrect if accessions
|
130
|
+
# are not unique across all database (admins should make sure of that).
|
131
|
+
def retrieve(loci)
|
132
|
+
# Exit early if loci is nil.
|
133
|
+
return unless loci
|
134
|
+
|
135
|
+
# String -> Array
|
136
|
+
# We may have empty string if loci contains a double comma as a result
|
137
|
+
# of typo (remember - loci is external input). These are eliminated.
|
138
|
+
loci = loci.split(',').delete_if(&:empty?)
|
139
|
+
|
140
|
+
# Each database is searched for each locus. For each locus, search is
|
141
|
+
# terminated on the first database match.
|
142
|
+
# NOTE: This can return incorrect sequence if the sequence ids are
|
143
|
+
# not unique across all databases.
|
144
|
+
seqs = loci.map do |locus|
|
145
|
+
# Get sequence id and coords. coords may be nil. accession can't
|
146
|
+
# be.
|
147
|
+
accession, coords = locus.split(':')
|
148
|
+
|
149
|
+
# Initialise a variable to store retrieved sequence.
|
150
|
+
seq = nil
|
151
|
+
|
152
|
+
# Go over each database looking for this accession.
|
153
|
+
each do |database|
|
154
|
+
# Database lookup will return a string if given accession is
|
155
|
+
# present in the database, nil otherwise.
|
156
|
+
seq = database.retrieve(accession, coords)
|
157
|
+
# Found a match! Terminate iteration returning the retrieved
|
158
|
+
# sequence.
|
159
|
+
break if seq
|
160
|
+
end
|
161
|
+
|
162
|
+
# If accession was not present in any database, insert an error
|
163
|
+
# message in place of the sequence. The line starts with '#'
|
164
|
+
# and should be ignored by BLAST (not tested).
|
165
|
+
unless seq
|
166
|
+
seq = "# ERROR: #{locus} not found in any database"
|
167
|
+
end
|
168
|
+
|
169
|
+
# Return seq.
|
170
|
+
seq
|
171
|
+
end
|
172
|
+
|
173
|
+
# Array -> String
|
174
|
+
seqs.join("\n")
|
175
|
+
end
|
176
|
+
|
106
177
|
# Intended to be used only for testing.
|
107
178
|
def first
|
108
179
|
all.first
|
@@ -61,21 +61,16 @@ module SequenceServer
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
## BLAST NOT INSTALLED OR NOT COMPATIBLE ##
|
64
|
+
## BLAST NOT INSTALLED, NOT EXECUTABLE, OR NOT COMPATIBLE ##
|
65
65
|
|
66
66
|
# Raised if SequenceServer could not locate NCBI BLAST+ installation on
|
67
67
|
# user's system.
|
68
68
|
class BLAST_NOT_INSTALLED_OR_NOT_EXECUTABLE < StandardError
|
69
69
|
def to_s
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
# Raised if SequenceServer could not successfully execute 'blastp -version'
|
75
|
-
# on user's system (see #141).
|
76
|
-
class BLAST_NOT_EXECUTABLE < StandardError
|
77
|
-
def to_s
|
78
|
-
'Error executing BLAST+ binaries.'
|
70
|
+
<<~MSG
|
71
|
+
BLAST+ is either not installed, or there is a problem with the
|
72
|
+
installed version.
|
73
|
+
MSG
|
79
74
|
end
|
80
75
|
end
|
81
76
|
|
data/lib/sequenceserver/job.rb
CHANGED
@@ -25,7 +25,7 @@ module SequenceServer
|
|
25
25
|
# Creates and queues a job. Returns created job object.
|
26
26
|
def create(params)
|
27
27
|
job = BLAST::Job.new(params) # TODO: Dynamic dispatch.
|
28
|
-
|
28
|
+
pool.queue { job.run }
|
29
29
|
job
|
30
30
|
end
|
31
31
|
|
@@ -46,6 +46,13 @@ module SequenceServer
|
|
46
46
|
Dir["#{DOTDIR}/**/job.yaml"]
|
47
47
|
.map { |f| fetch File.basename File.dirname f }
|
48
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Thread pool used for running BLAST searches.
|
53
|
+
def pool
|
54
|
+
@pool ||= Pool.new(SequenceServer.config[:num_threads])
|
55
|
+
end
|
49
56
|
end
|
50
57
|
|
51
58
|
include FileUtils
|
data/lib/sequenceserver/links.rb
CHANGED
@@ -9,8 +9,10 @@ module SequenceServer
|
|
9
9
|
|
10
10
|
NCBI_ID_PATTERN = /gi\|(\d+)\|/
|
11
11
|
UNIPROT_ID_PATTERN = /sp\|(\w+)\|/
|
12
|
+
PFAM_ID_PATTERN = /(PF\d{5}\.?\d*)/
|
13
|
+
RFAM_ID_PATTERN = /(RF\d{5})/
|
12
14
|
|
13
|
-
# Link generators return a Hash
|
15
|
+
# Link generators are methods that return a Hash as defined below.
|
14
16
|
#
|
15
17
|
# {
|
16
18
|
# # Required. Display title.
|
@@ -45,25 +47,29 @@ module SequenceServer
|
|
45
47
|
# sequence_id = encode sequence_id
|
46
48
|
# url = "http://www.ncbi.nlm.nih.gov/nucleotide/#{sequence_id}"
|
47
49
|
#
|
48
|
-
#
|
49
|
-
# Returns
|
50
|
+
# dbtype:
|
51
|
+
# Returns the database type (nucleotide or protein) that was used for
|
52
|
+
# BLAST search.
|
50
53
|
#
|
51
54
|
# whichdb:
|
52
|
-
# Returns the
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
55
|
+
# Returns the databases from which the hit could have originated. To
|
56
|
+
# ensure that one and the correct database is returned, ensure that
|
57
|
+
# your sequence ids are unique across different FASTA files.
|
58
|
+
# NOTE: This method is slow.
|
59
|
+
#
|
60
|
+
# coordinates:
|
61
|
+
# Returns min alignment start and max alignment end coordinates for
|
62
|
+
# query and hit sequences.
|
63
|
+
#
|
64
|
+
# e.g.,
|
65
|
+
# query_coords = coordinates[0]
|
66
|
+
# hit_coords = coordinates[1]
|
61
67
|
|
62
68
|
def ncbi
|
63
|
-
return nil unless id.match(NCBI_ID_PATTERN)
|
69
|
+
return nil unless id.match(NCBI_ID_PATTERN) or title.match(NCBI_ID_PATTERN)
|
64
70
|
ncbi_id = Regexp.last_match[1]
|
65
71
|
ncbi_id = encode ncbi_id
|
66
|
-
url = "
|
72
|
+
url = "https://www.ncbi.nlm.nih.gov/#{dbtype}/#{ncbi_id}"
|
67
73
|
{
|
68
74
|
order: 2,
|
69
75
|
title: 'NCBI',
|
@@ -73,13 +79,39 @@ module SequenceServer
|
|
73
79
|
end
|
74
80
|
|
75
81
|
def uniprot
|
76
|
-
return nil unless id.match(UNIPROT_ID_PATTERN)
|
82
|
+
return nil unless id.match(UNIPROT_ID_PATTERN) or title.match(UNIPROT_ID_PATTERN)
|
77
83
|
uniprot_id = Regexp.last_match[1]
|
78
84
|
uniprot_id = encode uniprot_id
|
79
|
-
url = "
|
85
|
+
url = "https://www.uniprot.org/uniprot/#{uniprot_id}"
|
86
|
+
{
|
87
|
+
order: 2,
|
88
|
+
title: 'UniProt',
|
89
|
+
url: url,
|
90
|
+
icon: 'fa-external-link'
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def pfam
|
95
|
+
return nil unless id.match(PFAM_ID_PATTERN) or title.match(PFAM_ID_PATTERN)
|
96
|
+
pfam_id = Regexp.last_match[1]
|
97
|
+
pfam_id = encode pfam_id
|
98
|
+
url = "https://pfam.xfam.org/family/#{pfam_id}"
|
99
|
+
{
|
100
|
+
order: 2,
|
101
|
+
title: 'Pfam',
|
102
|
+
url: url,
|
103
|
+
icon: 'fa-external-link'
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def rfam
|
108
|
+
return nil unless id.match(RFAM_ID_PATTERN) or title.match(RFAM_ID_PATTERN)
|
109
|
+
rfam_id = Regexp.last_match[1]
|
110
|
+
rfam_id = encode rfam_id
|
111
|
+
url = "https://rfam.xfam.org/family/#{rfam_id}"
|
80
112
|
{
|
81
113
|
order: 2,
|
82
|
-
title: '
|
114
|
+
title: 'Rfam',
|
83
115
|
url: url,
|
84
116
|
icon: 'fa-external-link'
|
85
117
|
}
|