neurohmmerapp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +81 -0
  5. data/LICENSE.txt +661 -0
  6. data/README.md +96 -0
  7. data/Rakefile +21 -0
  8. data/bin/neurohmmerapp +166 -0
  9. data/config.ru +3 -0
  10. data/lib/neurohmmerapp/config.rb +87 -0
  11. data/lib/neurohmmerapp/exceptions.rb +77 -0
  12. data/lib/neurohmmerapp/logger.rb +24 -0
  13. data/lib/neurohmmerapp/neurohmmer.rb +158 -0
  14. data/lib/neurohmmerapp/routes.rb +79 -0
  15. data/lib/neurohmmerapp/server.rb +63 -0
  16. data/lib/neurohmmerapp/version.rb +3 -0
  17. data/lib/neurohmmerapp.rb +207 -0
  18. data/neurohmmerapp.gemspec +50 -0
  19. data/public/NeuroHmmer/loading.html +12 -0
  20. data/public/src/css/bootstrap1.min.css +7 -0
  21. data/public/src/css/custom.css +189 -0
  22. data/public/src/css/custom.min.css +1 -0
  23. data/public/src/css/font-awesome.min.css +4 -0
  24. data/public/src/js/bionode-seq.min.js +1 -0
  25. data/public/src/js/bootstrap.min.js +6 -0
  26. data/public/src/js/jquery.cookie.min.js +1 -0
  27. data/public/src/js/jquery.min.js +4 -0
  28. data/public/src/js/jquery.validate.min.js +4 -0
  29. data/public/src/js/neurohmmer.js +228 -0
  30. data/public/src/js/neurohmmer.min.js +1 -0
  31. data/public/web_files/css/nh_compiled_css.min.css +14 -0
  32. data/public/web_files/fonts/FontAwesome.otf +0 -0
  33. data/public/web_files/fonts/fontawesome-webfont.eot +0 -0
  34. data/public/web_files/fonts/fontawesome-webfont.svg +504 -0
  35. data/public/web_files/fonts/fontawesome-webfont.ttf +0 -0
  36. data/public/web_files/fonts/fontawesome-webfont.woff +0 -0
  37. data/public/web_files/js/nh_compiled_js.min.js +20 -0
  38. data/spec/empty_config.yml +0 -0
  39. data/spec/route_spec.rb +72 -0
  40. data/views/500.slim +5 -0
  41. data/views/index.slim +38 -0
  42. data/views/layout.slim +74 -0
  43. data/views/results.slim +9 -0
  44. metadata +281 -0
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # NeuroHmmerApp
2
+ [![Build Status](https://travis-ci.org/wurmlab/neurohmmerapp.svg?branch=master)](https://travis-ci.org/wurmlab/neurohmmerapp)
3
+ [![Gem Version](https://badge.fury.io/rb/neurohmmerapp.svg)](http://badge.fury.io/rb/neurohmmerapp)
4
+ [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/wurmlab/neurohmmerapp/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/wurmlab/neurohmmerapp/?branch=master)
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+ ## Introduction
13
+
14
+ This is a online web application for [Neurohmmer](https://github.com/wurmlab/neurohmmer). This app is currently hosted at: ...
15
+
16
+
17
+ If you use Neurohmmer in your work, please cite us as follows:
18
+ > "Moghul MI, Elphick M & Wurm Y (<em>in prep.</em>) NeuroHmmer: identify Neuropeptide Precursors"
19
+
20
+
21
+
22
+
23
+
24
+
25
+ -
26
+ ## Installation
27
+ ### Installation Requirements
28
+ * Ruby (>= 2.0.0)
29
+ * HMMer (>=3.0)
30
+
31
+
32
+ ### Installation
33
+ Simply run the following command in the terminal.
34
+
35
+ ```bash
36
+ gem install neurohmmerapp
37
+ ```
38
+
39
+ If that doesn't work, try `sudo gem install neurohmmerapp` instead.
40
+
41
+ ##### Running From Source (Not Recommended)
42
+ It is also possible to run from source. However, this is not recommended.
43
+
44
+ ```bash
45
+ # Clone the repository.
46
+ git clone https://github.com/wurmlab/neurohmmerapp.git
47
+
48
+ # Move into GeneValidatorApp source directory.
49
+ cd neurohmmerapp
50
+
51
+ # Install bundler
52
+ gem install bundler
53
+
54
+ # Use bundler to install dependencies
55
+ bundle install
56
+
57
+ # Optional: run tests and build the gem from source
58
+ bundle exec rake
59
+
60
+ # Run NeuroHmmer.
61
+ bundle exec neurohmmerapp -h
62
+ # note that `bundle exec` executes NeuroHmmerApp in the context of the bundle
63
+
64
+ # Alternativaly, install NeuroHmmerApp as a gem
65
+ bundle exec rake install
66
+ neurohmmerapp -h
67
+ ```
68
+
69
+
70
+
71
+
72
+ ## Launch NeuroHmmer
73
+
74
+ To configure and launch NeuroHmmerApp, run the following from a command line.
75
+
76
+ ```bash
77
+ neurohmmerapp
78
+ ```
79
+
80
+ NeuroHmmerApp will automatically guide you through an interactive setup process to help locate BLAST+ binaries and ask for the location of BLAST+ databases.
81
+
82
+ That's it! Open http://localhost:4567/ and start using NeuroHmmer!
83
+
84
+
85
+
86
+
87
+
88
+
89
+ ## Advanced Usage
90
+
91
+ See `$ neurohmmerapp -h` for more information on all the options available when running NeuroHmmerApp.
92
+
93
+
94
+ <hr>
95
+
96
+ This program was developed at [Wurm Lab](https://wurmlab.github.io), [QMUL](http://sbcs.qmul.ac.uk).
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core'
3
+ require 'rspec/core/rake_task'
4
+
5
+ task default: [:build]
6
+
7
+ desc 'Builds and installs'
8
+ task install: [:build] do
9
+ require_relative 'lib/genevalidatorapp/version'
10
+ sh "gem install ./genevalidatorapp-#{GeneValidatorApp::VERSION}.gem"
11
+ end
12
+
13
+ desc 'Runs tests and builds gem (default)'
14
+ task build: [:test] do
15
+ sh 'gem build genevalidatorapp.gemspec'
16
+ end
17
+
18
+ task test: :spec
19
+ RSpec::Core::RakeTask.new(:spec) do |spec|
20
+ spec.pattern = FileList['spec/**/*_spec.rb']
21
+ end
data/bin/neurohmmerapp ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env ruby
2
+ require 'readline'
3
+ require 'English'
4
+ require 'slop'
5
+
6
+ ENV['RACK_ENV'] ||= 'production'
7
+
8
+ # display name for tools like `ps`
9
+ $PROGRAM_NAME = 'neurohmmerapp'
10
+
11
+ begin
12
+ Slop.parse!(strict: true, help: true) do
13
+ banner <<BANNER
14
+ SUMMARY:
15
+ NeuroHmmer - Identify Neuropeptide Precursors
16
+
17
+ USAGE:
18
+ $ neurohmmerapp [options]
19
+
20
+ Examples:
21
+ # Launch NeuroHmmerApp with the given config file
22
+ $ neurohmmerapp --config ~/.neurohmmerapp.conf
23
+
24
+ # Launch NeuroHmmerApp with 8 threads at port 8888
25
+ $ neurohmmerapp --num_threads 8 --port 8888
26
+
27
+ # Create a config file with the other arguments
28
+ $ neurohmmerapp -s -d ~/database_dir
29
+
30
+ BANNER
31
+ on 'c', 'config_file=',
32
+ 'Use the given configuration file',
33
+ argument: true
34
+
35
+ on 'g', 'public_dir=',
36
+ 'dhe public directory that is served to the web application.',
37
+ argument: true
38
+
39
+ on 'b', 'bin=',
40
+ 'Load HMMER 3.0 binaries from this directory',
41
+ argument: true,
42
+ as: Array
43
+
44
+ on 'n', 'num_threads=',
45
+ 'Number of threads to use to run a BLAST search',
46
+ argument: true
47
+
48
+ on 'H', 'host=',
49
+ 'Host to run NeuroHmmerApp on',
50
+ argument: true
51
+
52
+ on 'p', 'port=',
53
+ 'Port to run NeuroHmmerApp on',
54
+ argument: true
55
+
56
+ on 's', 'set',
57
+ 'Set configuration value in default or given config file'
58
+
59
+ on 'D', 'devel',
60
+ 'Start NeuroHmmerApp in development mode'
61
+
62
+ on '-v', '--version',
63
+ 'Print version number of NeuroHmmerApp that will be loaded'
64
+
65
+ on '-h', '--help',
66
+ 'Display this help message'
67
+
68
+ clean_opts = lambda do |hash|
69
+ hash.delete_if { |k, v| k == :set || v.nil? }
70
+ hash
71
+ end
72
+
73
+ run do
74
+ if version?
75
+ require 'neurohmmerapp/version'
76
+ puts NeuroHmmerApp::VERSION
77
+ exit
78
+ end
79
+
80
+ ENV['RACK_ENV'] = 'development' if devel?
81
+
82
+ # Exit gracefully on SIGINT.
83
+ stty = `stty -g`.chomp
84
+ trap('INT') do
85
+ puts ''
86
+ puts 'Aborted.'
87
+ system('stty', stty)
88
+ exit
89
+ end
90
+
91
+ require 'neurohmmerapp'
92
+
93
+ begin
94
+ NeuroHmmerApp.init clean_opts[to_h]
95
+
96
+ # The aim of following error recovery scenarios is to guide user to a
97
+ # working NeuroHmmerApp installation. We expect to land following
98
+ # error scenarios either when creating a new NeuroHmmerApp (first
99
+ # time or later), or updating config values using -s CLI option.
100
+
101
+ rescue NeuroHmmerApp::CONFIG_FILE_ERROR => e
102
+
103
+ puts e
104
+ exit!
105
+
106
+ rescue NeuroHmmerApp::BIN_DIR_NOT_FOUND => e
107
+
108
+ puts e
109
+
110
+ unless bin?
111
+ puts 'You can set the correct value by running:'
112
+ puts
113
+ puts ' neurohmmerapp -s -b <value>'
114
+ puts
115
+ end
116
+
117
+ exit!
118
+
119
+
120
+ rescue NeuroHmmerApp::NUM_THREADS_INCORRECT => e
121
+
122
+ puts e
123
+
124
+ unless num_threads?
125
+ puts 'You can set the correct value by running:'
126
+ puts
127
+ puts ' neurohmmerapp -s -n <value>'
128
+ puts
129
+ end
130
+
131
+ exit!
132
+
133
+ rescue => e
134
+ # This will catch any unhandled error and some very special errors.
135
+ # Ideally we will never hit this block. If we do, there's a bug in
136
+ # NeuroHmmerApp or something really weird going on. If we hit this
137
+ # error block we show the stacktrace to the user requesting them to
138
+ # post the same to our Google Group.
139
+ puts <<MSG
140
+ Something went wonky
141
+
142
+ Looks like you have encountered a bug in NeuroHmmerApp. Please could you
143
+ report this incident here -
144
+ https://github.com/wurmlab/neurohmmerapp/issues
145
+
146
+ Error:
147
+ #{e.backtrace.unshift(e.message).join("\n")}
148
+ MSG
149
+ exit
150
+ end
151
+
152
+ if set?
153
+ NeuroHmmerApp.config.write_config_file
154
+ exit
155
+ end
156
+
157
+ NeuroHmmerApp.config.write_config_file if fetch_option(:set).value
158
+
159
+ NeuroHmmerApp.run
160
+ end
161
+ end
162
+ rescue Slop::Error => e
163
+ puts e
164
+ puts "Run '#{$PROGRAM_NAME} -h' for help with command line options."
165
+ exit
166
+ end
data/config.ru ADDED
@@ -0,0 +1,3 @@
1
+ require 'genevalidatorapp'
2
+ GeneValidatorApp.init
3
+ run GeneValidatorApp
@@ -0,0 +1,87 @@
1
+ require 'forwardable'
2
+
3
+ # Define Config class.
4
+ module NeuroHmmerApp
5
+ # Capture our configuration system.
6
+ class Config
7
+ extend Forwardable
8
+
9
+ def_delegators NeuroHmmerApp, :logger
10
+
11
+ def initialize(data = {})
12
+ @data = symbolise data
13
+ @config_file = @data.delete(:config_file) || default_config_file
14
+ @config_file = File.expand_path(@config_file)
15
+ @data = parse_config_file.update @data
16
+ @data = defaults.update @data
17
+ end
18
+
19
+ attr_reader :data, :config_file
20
+
21
+ # Get.
22
+ def [](key)
23
+ data[key]
24
+ end
25
+
26
+ # Set.
27
+ def []=(key, value)
28
+ data[key] = value
29
+ end
30
+
31
+ # Exists?
32
+ def include?(key)
33
+ data.include? key
34
+ end
35
+
36
+ # Write config data to config file.
37
+ def write_config_file
38
+ return unless config_file
39
+
40
+ File.open(config_file, 'w') do |f|
41
+ f.puts(data.delete_if { |_, v| v.nil? }.to_yaml)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ # Symbolizes keys. Changes `database` key to `database_dir`.
48
+ def symbolise(data)
49
+ return {} unless data
50
+ # Symbolize keys.
51
+ Hash[data.map { |k, v| [k.to_sym, v] }]
52
+ end
53
+
54
+ # Parses and returns data from config_file if it exists. Returns {}
55
+ # otherwise.
56
+ def parse_config_file
57
+ unless file? config_file
58
+ logger.debug "Configuration file not found: #{config_file}"
59
+ return {}
60
+ end
61
+
62
+ logger.debug "Reading configuration file: #{config_file}."
63
+ symbolise YAML.load_file(config_file)
64
+ rescue => error
65
+ raise CONFIG_FILE_ERROR.new(config_file, error)
66
+ end
67
+
68
+ def file?(file)
69
+ file && File.exist?(file) && File.file?(file)
70
+ end
71
+
72
+ # Default configuration data.
73
+ def defaults
74
+ {
75
+ :num_threads => 1,
76
+ :port => 4567,
77
+ :host => '0.0.0.0',
78
+ :public_dir => File.join(Dir.home, '.neurohmmerapp/'),
79
+ :max_characters => 'undefined',
80
+ }
81
+ end
82
+
83
+ def default_config_file
84
+ '~/.neurohmmerapp.conf'
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,77 @@
1
+ # This file defines all possible exceptions that can be thrown by
2
+ # NeuroHmmerApp on startup.
3
+ #
4
+ # Exceptions only ever inform another entity (downstream code or users) of an
5
+ # issue. Exceptions may or may not be recoverable.
6
+ #
7
+ # Error classes should be seen as: the error code (class name), human readable
8
+ # message (to_s method), and necessary attributes to act on the error.
9
+ #
10
+ # We define as many error classes as needed to be precise about the issue, thus
11
+ # making it easy for downstream code (bin/genevalidatorapp or config.ru) to act
12
+ # on them.
13
+
14
+ module NeuroHmmerApp
15
+ # Error in config file.
16
+ class CONFIG_FILE_ERROR < StandardError
17
+ def initialize(ent, err)
18
+ @ent = ent
19
+ @err = err
20
+ end
21
+
22
+ attr_reader :ent, :err
23
+
24
+ def to_s
25
+ <<MSG
26
+ Error reading config file: #{ent}.
27
+ #{err}
28
+ MSG
29
+ end
30
+ end
31
+
32
+ ## ENOENT ##
33
+
34
+ # Name borrowed from standard Errno::ENOENT, this class serves as a template
35
+ # for defining errors that mean "expected to find <entity> at <path>, but
36
+ # didn't".
37
+ #
38
+ # ENOENT is raised if and only if an entity was set, either using CLI or
39
+ # config file. For instance, it's compulsory to set database_dir. But ENOENT
40
+ # is not raised if database_dir is not set. ENOENT is raised if database_dir
41
+ # was set, but does not exist.
42
+ class ENOENT < StandardError
43
+ def initialize(des, ent)
44
+ @des = des
45
+ @ent = ent
46
+ end
47
+
48
+ attr_reader :des, :ent
49
+
50
+ def to_s
51
+ "Could not find #{des}: #{ent}"
52
+ end
53
+ end
54
+
55
+ # Raised if bin dir set, but does not exist.
56
+ class BIN_DIR_NOT_FOUND < ENOENT
57
+ def initialize(ent)
58
+ super 'bin dir', ent
59
+ end
60
+ end
61
+
62
+ # Raised if extension file set, but does not exist.
63
+ class EXTENSION_FILE_NOT_FOUND < ENOENT
64
+ def initialize(ent)
65
+ super 'extension file', ent
66
+ end
67
+ end
68
+
69
+ ## NUM THREADS ##
70
+
71
+ # Raised if num_threads set by the user is incorrect.
72
+ class NUM_THREADS_INCORRECT < StandardError
73
+ def to_s
74
+ 'Number of threads should be a number greater than or equal to 1.'
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,24 @@
1
+ require 'logger'
2
+
3
+ module NeuroHmmerApp
4
+ class Logger < Logger
5
+ def initialize(dev, verbose = false)
6
+ super dev
7
+ self.level = verbose ? DEBUG : INFO
8
+ self.formatter = Formatter.new
9
+ end
10
+
11
+ # We change Logging format so that it is consistent with Sinatra's
12
+ class Formatter < Formatter
13
+ Format = "[%s] %s %s\n"
14
+
15
+ def initialize
16
+ self.datetime_format = '%Y-%m-%d %H:%M:%S'
17
+ end
18
+
19
+ def call(severity, time, _progname, msg)
20
+ Format % [format_datetime(time), severity, msg2str(msg)]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,158 @@
1
+ require 'forwardable'
2
+ require 'neurohmmer'
3
+
4
+ module NeuroHmmerApp
5
+ # Module that runs NeuroHmmer
6
+ module RunNeuroHmmer
7
+ # To signal error in query sequence or options.
8
+ #
9
+ # ArgumentError is raised when BLAST+'s exit status is 1; see [1].
10
+ class ArgumentError < ArgumentError
11
+ end
12
+
13
+ # To signal internal errors.
14
+ #
15
+ # RuntimeError is raised when there is a problem in writing the input file,
16
+ # in running BLAST, get_raw_sequence or genevalidator. These are rare,
17
+ # infrastructure errors, used internally, and of concern only to the
18
+ # admins/developers.
19
+ class RuntimeError < RuntimeError
20
+ end
21
+
22
+ class << self
23
+ extend Forwardable
24
+
25
+ def_delegators NeuroHmmerApp, :config, :logger, :public_dir
26
+
27
+ attr_reader :gv_dir, :input_file, :xml_file, :raw_seq, :unique_id, :params
28
+
29
+ # Setting the scene
30
+ def init(base_url, params)
31
+ create_unique_id
32
+ create_run_dir
33
+ @params = params
34
+ validate_params
35
+ # @url = produce_result_url_link(base_url)
36
+ end
37
+
38
+ # Runs genevalidator & Returns parsed JSON, or link to JSON/results file
39
+ def run
40
+ write_seqs_to_file
41
+ run_neurohmmer
42
+ end
43
+
44
+ private
45
+
46
+ # Creates a unique run ID (based on time),
47
+ def create_unique_id
48
+ @unique_id = Time.new.strftime('%Y-%m-%d_%H-%M-%S_%L-%N')
49
+ @run_dir = File.join(NeuroHmmerApp.public_dir, 'NeuroHmmer',
50
+ @unique_id)
51
+ ensure_unique_id
52
+ end
53
+
54
+ # Ensures that the Unique id is unique (if a sub dir is present in the
55
+ # temp dir with the unique id, it simply creates a new one)
56
+ def ensure_unique_id
57
+ while File.exist?(@run_dir)
58
+ @unique_id = create_unique_id
59
+ @run_dir = File.join(NeuroHmmerApp.public_dir, 'NeuroHmmer',
60
+ @unique_id)
61
+ end
62
+ logger.debug("Unique ID = #{@unique_id}")
63
+ end
64
+
65
+ # Create a sub_dir in the Tempdir (name is based on unique id)
66
+ def create_run_dir
67
+ logger.debug("NeuroHmmerApp Tempdir = #{@run_dir}")
68
+ FileUtils.mkdir_p(@run_dir)
69
+ end
70
+
71
+ # Validates the paramaters provided via the app.
72
+ # Only important if POST request is sent via API - Web APP also validates
73
+ # all params via Javascript.
74
+ def validate_params
75
+ logger.debug("Input Paramaters: #{@params}")
76
+ check_seq_param_present
77
+ check_seq_length
78
+ check_nps_param_present
79
+ end
80
+
81
+ # Simply asserts whether that the seq param is present
82
+ def check_seq_param_present
83
+ return if @params[:seq]
84
+ fail ArgumentError, 'No input sequence provided.'
85
+ end
86
+
87
+ def check_seq_length
88
+ return unless config[:max_characters] != 'undefined'
89
+ return if @params[:seq].length < config[:max_characters]
90
+ fail ArgumentError, 'The input sequence is too long.'
91
+ end
92
+
93
+ def check_nps_param_present
94
+ return if @params[:neuropeptides]
95
+ fail ArgumentError, 'No neuropeptides groups specified'
96
+ end
97
+
98
+ # Writes the input sequences to a file with the sub_dir in the temp_dir
99
+ def write_seqs_to_file
100
+ @input_file = File.join(@run_dir, 'input_file.fa')
101
+ logger.debug("Writing input seqs to: '#{@input_file}'")
102
+ ensure_unix_line_ending
103
+ ensure_fasta_valid
104
+ File.open(@input_file, 'w+') { |f| f.write(@params[:seq]) }
105
+ assert_input_file_present
106
+ end
107
+
108
+ def ensure_unix_line_ending
109
+ @params[:seq].gsub!(/\r\n?/, "\n")
110
+ end
111
+
112
+ # Adds a ID (based on the time when submitted) to sequences that are not
113
+ # in fasta format.
114
+ def ensure_fasta_valid
115
+ logger.debug('Adding an ID to sequences that are not in fasta format.')
116
+ unique_queries = {}
117
+ sequence = @params[:seq].lstrip
118
+ if sequence[0] != '>'
119
+ sequence.insert(0, '>Submitted:'\
120
+ "#{Time.now.strftime('%H:%M-%B_%d_%Y')}\n")
121
+ end
122
+ sequence.gsub!(/^\>(\S+)/) do |s|
123
+ if unique_queries.key?(s)
124
+ unique_queries[s] += 1
125
+ s + '_' + (unique_queries[s] - 1).to_s
126
+ else
127
+ unique_queries[s] = 1
128
+ s
129
+ end
130
+ end
131
+ @params[:seq] = sequence
132
+ end
133
+
134
+ # Asserts that the input file has been generated and is not empty
135
+ def assert_input_file_present
136
+ return if File.exist?(@input_file) && !File.zero?(@input_file)
137
+ fail 'NeuroHmmerApp was unable to create the input file.'
138
+ end
139
+
140
+ def run_neurohmmer
141
+ opt = {
142
+ temp_dir: File.join(@run_dir, 'tmp'),
143
+ input_file: @input_file,
144
+ num_threads: config[:num_threads]
145
+ }
146
+ Neurohmmer.init(opt)
147
+ Neurohmmer::Hmmer.search
148
+ hmm_results = Neurohmmer::Hmmer.analyse_output
149
+ Neurohmmer::Output.format_seqs_for_html(hmm_results)
150
+ end
151
+
152
+ def create_log_file
153
+ @log_file = File.join(@run_dir, 'log_file.txt')
154
+ logger.debug("Log file: #{@log_file}")
155
+ end
156
+ end
157
+ end
158
+ end