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.

Potentially problematic release.


This version of sequenceserver might be problematic. Click here for more details.

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/