sequenceserver 1.0.0.pre.3 → 1.0.0.pre.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ddd56505296b6813bedb5e400c5d2fd44bb9fcf3
4
- data.tar.gz: 126f3e127d5d3a1eadfac6bf7940d0ec08875997
3
+ metadata.gz: 9ed1609668685537118f3ff44bb18bda69cbdf8b
4
+ data.tar.gz: d2b5aa23928abb0ed230747e789b45440b9927fa
5
5
  SHA512:
6
- metadata.gz: 9207f4705a670d0b4a652d4b0a95c17910ebe9e296209d033914e56bd3c2119fad1406ca84c4f3d7515a49c3a2ebead11b9482fd4a0c2743632ea7d196d08f91
7
- data.tar.gz: 372f45058b21fdff4d7b922efc1c8e51eb358eef9cd2d9cb10e8ecbed943966d1c82d366e0acc9d55e795b2129bc602a2686900269c337373b195224f0c7b960
6
+ metadata.gz: a2b7d392d183377a918b4841685adc34fe26997b73eda7f5129273064bf8bae30929448ea8fd4818508728957460cc20c299b375ba644405060ccbed432ac5fa
7
+ data.tar.gz: bdca19b354e1b70113d2c77fc99699c4150e30e61e8fe5a82195d8e8033ad826a82ea7fdd9fde69a0528197d4aa739648f2bfd0d31f81d2da25dd7b32e520bfc
data/README.md CHANGED
@@ -1,7 +1,10 @@
1
1
  [![build status](https://secure.travis-ci.org/yannickwurm/sequenceserver.png?branch=master)](https://travis-ci.org/yannickwurm/sequenceserver)
2
- [![Code Climate](https://codeclimate.com/github/yannickwurm/sequenceserver/badges/gpa.svg)](https://codeclimate.com/github/yannickwurm/sequenceserver)
3
- [![Test Coverage](https://codeclimate.com/github/yannickwurm/sequenceserver/badges/coverage.svg)](https://codeclimate.com/github/yannickwurm/sequenceserver)
4
- [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/yannickwurm/sequenceserver)
2
+ [![code climate](https://codeclimate.com/github/yannickwurm/sequenceserver/badges/gpa.svg)](https://codeclimate.com/github/yannickwurm/sequenceserver)
3
+ [![coverage](https://codeclimate.com/github/yannickwurm/sequenceserver/badges/coverage.svg)](https://codeclimate.com/github/yannickwurm/sequenceserver)
4
+ [![gem version](https://badge.fury.io/rb/sequenceserver.svg)](http://rubygems.org/gems/sequenceserver)
5
+ [![total downloads](http://ruby-gem-downloads-badge.herokuapp.com/sequenceserver?type=total&color=brightgreen)](http://rubygems.org/gems/sequenceserver)
6
+
7
+ [![gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/yannickwurm/sequenceserver)
5
8
 
6
9
  # SequenceServer - BLAST searching made easy!
7
10
 
@@ -10,7 +13,47 @@ interface for use locally or over the web.
10
13
 
11
14
  ## Installation
12
15
 
13
- Please see http://www.sequenceserver.com
16
+ Please see http://www.sequenceserver.com.
17
+
18
+ ## Contribute
19
+
20
+ You will need Ruby and NodeJS and respective package managers (RubyGems and
21
+ npm) for development.
22
+
23
+ ##### Get source code.
24
+ ```
25
+ git clone https://github.com/yannickwurm/sequenceserver
26
+ cd sequenceserver
27
+ ```
28
+
29
+ ##### Install dependencies.
30
+ ###### Ruby
31
+ ```
32
+ gem install bundler && bundle
33
+ ```
34
+
35
+ ###### Node
36
+ ```
37
+ npm install
38
+ ```
39
+
40
+ #### Run, test, lint
41
+ ```
42
+ # Launch SequenceServer in development mode.
43
+ bundle exec bin/sequenceserver -D
44
+
45
+ # Run RSpec, Capybara, and RuboCop.
46
+ rake
47
+
48
+ # Run bootlint, csslint, jshint
49
+ npm run-script cop
50
+
51
+ # Minify JS and CSS
52
+ npm run-script build
53
+ ```
54
+
55
+ SequenceServer runs in production mode by default. Minified JS and CSS are
56
+ picked in production mode only.
14
57
 
15
58
  ## Contributors
16
59
 
@@ -1,16 +1,14 @@
1
- require 'yaml'
2
1
  require 'English'
3
- require 'fileutils'
4
- require 'sinatra/base'
5
2
  require 'thin'
6
- require 'json'
7
3
 
8
4
  require 'sequenceserver/exceptions'
9
5
  require 'sequenceserver/config'
10
6
  require 'sequenceserver/logger'
7
+ require 'sequenceserver/search'
11
8
  require 'sequenceserver/sequence'
12
9
  require 'sequenceserver/database'
13
10
  require 'sequenceserver/blast'
11
+ require 'sequenceserver/routes'
14
12
 
15
13
  # Top level module / namespace.
16
14
  module SequenceServer
@@ -90,7 +88,7 @@ module SequenceServer
90
88
  # controller.
91
89
  def call(env)
92
90
  env['rack.logger'] = logger
93
- App.call(env)
91
+ Routes.call(env)
94
92
  end
95
93
 
96
94
  # Run SequenceServer interactively.
@@ -201,176 +199,4 @@ module SequenceServer
201
199
  system("which #{command} > /dev/null 2>&1")
202
200
  end
203
201
  end
204
-
205
- # Controller.
206
- class App < Sinatra::Base
207
- # See
208
- # http://www.sinatrarb.com/configuration.html
209
- configure do
210
- # We don't need Rack::MethodOverride. Let's avoid the overhead.
211
- disable :method_override
212
-
213
- # Ensure exceptions never leak out of the app. Exceptions raised within
214
- # the app must be handled by the app. We do this by attaching error
215
- # blocks to exceptions we know how to handle and attaching to Exception
216
- # as fallback.
217
- disable :show_exceptions, :raise_errors
218
-
219
- # Make it a policy to dump to 'rack.errors' any exception raised by the
220
- # app so that error handlers don't have to do it themselves. But for it
221
- # to always work, Exceptions defined by us should not respond to `code`
222
- # or http_status` methods. Error blocks errors must explicitly set http
223
- # status, if needed, by calling `status` method.
224
- # method.
225
- enable :dump_errors
226
-
227
- # We don't want Sinatra do setup any loggers for us. We will use our own.
228
- set :logging, nil
229
-
230
- # Public, and views directory will be found here.
231
- set :root, lambda { SequenceServer.root }
232
- end
233
-
234
- # See
235
- # http://www.sinatrarb.com/intro.html#Mime%20Types
236
- configure do
237
- mime_type :fasta, 'text/fasta'
238
- mime_type :xml, 'text/xml'
239
- mime_type :tsv, 'text/tsv'
240
- end
241
-
242
- configure :production do
243
- set :public_folder,
244
- lambda { File.join SequenceServer.root, 'public', 'dist' }
245
- end
246
-
247
- helpers do
248
- # Render an anchor element from the given Hash.
249
- #
250
- # See links.rb for example of a Hash object that will be rendered.
251
- def a(link)
252
- return unless link[:title] && link[:url]
253
- a = ['<a']
254
- a << "href=#{link[:url]}"
255
- a << "class=\"#{link[:class]}\"" if link[:class]
256
- a << "target=\"_blank\"" if absolute? link[:url]
257
- a << '>'
258
- a << "<i class=\"fa #{link[:icon]}\"></i>" if link[:icon]
259
- a << link[:title]
260
- a << '</a>'
261
- a.join("\n")
262
- end
263
-
264
- # Is the given URI absolute? (or relative?)
265
- def absolute?(uri)
266
- URI.parse(uri).absolute?
267
- end
268
-
269
- # Prettify given data.
270
- def prettify(data)
271
- return prettify_tuple(data) if tuple? data
272
- return prettify_float(data) if data.is_a? Float
273
- data
274
- end
275
-
276
- # Formats float as "a.bcd" or "a x b^c". The latter if float is
277
- # scientific notation. Former otherwise.
278
- def prettify_float(float)
279
- float.to_s.sub(/(\d*\.\d*)e?([+-]\d*)?/) do
280
- base = Regexp.last_match[1]
281
- power = Regexp.last_match[2]
282
- s = format '%.2f', base
283
- s << " &times; 10<sup>#{power}</sup>" if power
284
- s
285
- end
286
- end
287
-
288
- # Formats an array of two elements as "first (last)".
289
- def prettify_tuple(tuple)
290
- "#{tuple.first} (#{tuple.last})"
291
- end
292
-
293
- # Is the given value a tuple? (array of length two).
294
- def tuple?(data)
295
- return true if data.is_a?(Array) && data.length == 2
296
- end
297
- end
298
-
299
- # For any request that hits the app in development mode, log incoming
300
- # params.
301
- before do
302
- logger.debug params
303
- end
304
-
305
- # Render the search form.
306
- get '/' do
307
- erb :search, :locals => { :databases => Database.group_by(&:type) }
308
- end
309
-
310
- # BLAST search!
311
- post '/' do
312
- erb :result, :locals => { :report => BLAST.run(params) }
313
- end
314
-
315
- # @params sequence_ids: whitespace separated list of sequence ids to
316
- # retrieve
317
- # @params database_ids: whitespace separated list of database ids to
318
- # retrieve the sequence from.
319
- # @params download: whether to return raw response or initiate file
320
- # download
321
- #
322
- # Use whitespace to separate entries in sequence_ids (all other chars exist
323
- # in identifiers) and retreival_databases (we don't allow whitespace in a
324
- # database's name, so it's safe).
325
- get '/get_sequence/' do
326
- sequence_ids = params[:sequence_ids].split(/\s/)
327
- database_ids = params[:database_ids].split(/\s/)
328
-
329
- sequences = Sequence.from_blastdb(sequence_ids, database_ids)
330
-
331
- if params[:download]
332
- file_name = "sequenceserver_#{sequence_ids.first}.fasta"
333
- file = Tempfile.new file_name
334
- sequences.each do |sequence|
335
- file.puts sequence.fasta
336
- end
337
- file.close
338
- send_file file.path, :type => :fasta, :filename => file_name
339
- else
340
- {
341
- :sequence_ids => sequence_ids,
342
- :databases => Database[database_ids].map(&:title),
343
- :sequences => sequences.map(&:info)
344
- }.to_json
345
- end
346
- end
347
-
348
- get '/get_report/' do
349
- ofile = BLAST.format(params)
350
-
351
- send_file ofile[:filepath],
352
- :filename => ofile[:filename],
353
- :type => ofile[:type].to_sym
354
- end
355
-
356
- # This error block will only ever be hit if the user gives us a funny
357
- # sequence or incorrect advanced parameter. Well, we could hit this block
358
- # if someone is playing around with our HTTP API too.
359
- error BLAST::ArgumentError do
360
- status 400
361
- error = env['sinatra.error']
362
- erb :'400', :locals => { :error => error }
363
- end
364
-
365
- # This will catch any unhandled error and some very special errors. Ideally
366
- # we will never hit this block. If we do, there's a bug in SequenceServer
367
- # or something really weird going on. If we hit this error block we show
368
- # the stacktrace to the user requesting them to post the same to our Google
369
- # Group.
370
- error Exception, BLAST::RuntimeError do
371
- status 500
372
- error = env['sinatra.error']
373
- erb :'500', :locals => { :error => error }
374
- end
375
- end
376
202
  end
@@ -5,6 +5,8 @@ require 'ox'
5
5
 
6
6
  require 'sequenceserver/links'
7
7
  require 'sequenceserver/blast/exceptions'
8
+ require 'sequenceserver/blast/constants'
9
+ require 'sequenceserver/blast/formatter'
8
10
  require 'sequenceserver/blast/report'
9
11
  require 'sequenceserver/blast/query'
10
12
  require 'sequenceserver/blast/hit'
@@ -16,25 +18,6 @@ module SequenceServer
16
18
  # `BLAST::ArgumentError` and `BLAST::RuntimeError` signal errors encountered
17
19
  # when attempting a BLAST search.
18
20
  module BLAST
19
- ERROR_LINE = /\(CArgException.*\)\s(.*)/
20
-
21
- ALGORITHMS = %w(blastn blastp blastx tblastn tblastx)
22
-
23
- OUTFMT = {
24
- 'pairwise' => [0, 'txt'],
25
- 'qa_identity' => [1, 'txt'],
26
- 'qa_no_identity' => [2, 'txt'],
27
- 'fqa_identity' => [3, 'txt'],
28
- 'fqa_no_identity' => [4, 'txt'],
29
- 'xml' => [5, 'xml'],
30
- 'tsv' => [6, 'tsv'],
31
- 'tsv_commented' => [7, 'tsv'],
32
- 'asn_text' => [8, 'asn'],
33
- 'asn_binary' => [9, 'asn'],
34
- 'csv' => [10, 'csv'],
35
- 'archive' => [11, 'txt']
36
- } # See [1]
37
-
38
21
  class << self
39
22
  extend Forwardable
40
23
 
@@ -109,30 +92,8 @@ module SequenceServer
109
92
  fail RuntimeError.new(status, error)
110
93
  end
111
94
 
112
- Report.new(File.basename(rfile), databases)
113
- end
114
-
115
- def format(params)
116
- validate_format_params params
117
-
118
- rfile = create_file_path params['report']
119
- ofmt = OUTFMT[params['format']]
120
- type = params['type']
121
-
122
- oname = "seqserv_#{type}_#{Time.now.strftime('%H%M')}.#{ofmt[1]}"
123
- ofile = Tempfile.new oname
124
-
125
- command = "blast_formatter -archive '#{rfile}' -out #{ofile.path}" \
126
- " -outfmt '#{ofmt[0]} #{params['specifiers']}' 2> /dev/null"
127
-
128
- logger.debug("Executing: #{command}")
129
- system command
130
-
131
- {
132
- :filepath => ofile.path,
133
- :filename => oname,
134
- :type => ofmt[1]
135
- }
95
+ Search << rfile
96
+ Report.new(File.basename(rfile.path), databases)
136
97
  end
137
98
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
138
99
  # rubocop:enable Metrics/MethodLength
@@ -141,16 +102,6 @@ module SequenceServer
141
102
  params[:sequence].strip! unless params[:sequence].nil?
142
103
  end
143
104
 
144
- def validate_format_params(params)
145
- return true if params.include?('report') &&
146
- params.include?('format') &&
147
- File.exist?(create_file_path params['report'])
148
- fail ArgumentError, <<MSG
149
- Incorrect request parameters. Please ensure that requested file name is
150
- correct and the file type is either xml or tsv.
151
- MSG
152
- end
153
-
154
105
  def validate_blast_params(params)
155
106
  validate_blast_method params[:method]
156
107
  validate_blast_sequences params[:sequence]
@@ -201,12 +152,6 @@ MSG
201
152
  true
202
153
  end
203
154
 
204
- # Returns filename if path exists otherwise returns a path to tmp dir.
205
- def create_file_path(filename)
206
- return File.join(Dir.tmpdir, filename) unless File.exist? filename
207
- filename
208
- end
209
-
210
155
  def allowed_chars
211
156
  /\A[a-z0-9\-_\. ']*\Z/i
212
157
  end
@@ -0,0 +1,34 @@
1
+ module SequenceServer
2
+ # Define constanst used by BLAST module.
3
+ module BLAST
4
+ ERROR_LINE = /\(CArgException.*\)\s(.*)/
5
+
6
+ ALGORITHMS = %w(blastn blastp blastx tblastn tblastx)
7
+
8
+ OUTFMT_SPECIFIERS = %w(qseqid qgi qacc sseqid sallseqid sgi sallgi sacc
9
+ sallacc qstart qend sstart send qseq sseq evalue
10
+ bitscore score length length pident nident
11
+ mismatch positive gapopen gaps ppos frames
12
+ qframe hframe btop staxids sscinames scomnames
13
+ sblastnames sskingdoms stitle salltitles sstrand
14
+ qcovs qcovhsp)
15
+ OUTFMT = {
16
+ 'pairwise' => [0, :txt],
17
+ 'qa' => [1, :txt],
18
+ 'qa_no_identity' => [2, :txt],
19
+ 'fqa' => [3, :txt],
20
+ 'fqa_no_identity' => [4, :txt],
21
+ 'xml' => [5, :xml],
22
+ 'std_tsv' => [7, :tsv],
23
+ 'full_tsv' => [7, :tsv, OUTFMT_SPECIFIERS],
24
+ 'asn_text' => [8, :asn],
25
+ 'asn_binary' => [9, :asn],
26
+ 'csv' => [10, :csv],
27
+ 'archive' => [11, :txt]
28
+ }
29
+ end
30
+ end
31
+
32
+ # References
33
+ # ----------
34
+ # [1]: http://www.ncbi.nlm.nih.gov/books/NBK1763/
@@ -0,0 +1,69 @@
1
+ require 'forwardable'
2
+
3
+ module SequenceServer
4
+ module BLAST
5
+ # Formats BLAST+ archive file format into other file formats.
6
+ class Formatter
7
+ class << self
8
+ alias_method :run, :new
9
+ end
10
+
11
+ extend Forwardable
12
+
13
+ def_delegators SequenceServer, :logger
14
+
15
+ def initialize(search_id, type)
16
+ @archive_file = get_archive_file search_id
17
+ @format, @mime, @specifiers = OUTFMT[type]
18
+ @type = type
19
+
20
+ validate && run
21
+ end
22
+
23
+ attr_reader :archive_file, :type
24
+
25
+ attr_reader :format, :mime, :specifiers
26
+
27
+ def file
28
+ @file ||= Tempfile.new filename
29
+ end
30
+
31
+ def filename
32
+ @filename ||=
33
+ "sequenceserver-#{type}_report.#{mime}"
34
+ end
35
+
36
+ private
37
+
38
+ def run
39
+ command =
40
+ "blast_formatter -archive '#{archive_file}'" \
41
+ " -outfmt '#{format} #{specifiers}'" \
42
+ " -out '#{file.path}' 2> /dev/null"
43
+ logger.debug("Executing: #{command}")
44
+ system command
45
+ end
46
+
47
+ def validate
48
+ return true if archive_file && format &&
49
+ File.exist?(archive_file)
50
+ fail ArgumentError, <<MSG
51
+ Incorrect request parameters. Please ensure that requested file name is
52
+ correct and the file type is either xml or tsv.
53
+ MSG
54
+ end
55
+
56
+ # Returns filename if path exists otherwise returns a path to tmp dir.
57
+ def get_archive_file(file)
58
+ return unless file
59
+ return file.path if file.respond_to? :path
60
+ return file if File.exist? file
61
+ File.join Dir.tmpdir, file
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ # References
68
+ # ----------
69
+ # [1]: http://www.ncbi.nlm.nih.gov/books/NBK1763/