#!/usr/bin/env ruby require 'readline' require 'English' require 'slop' ENV['RACK_ENV'] ||= 'production' # display name for tools like `ps` $PROGRAM_NAME = 'sequenceserver' # Given a url, downloads the archive into a temporary file # and and extracts it into ~/.sequenceserver. def download_from_url(url) archive = File.join('/tmp', File.basename(url)) # -ip4 is required to avoid this curl bug http://sourceforge.net/p/curl/bugs/1424/?limit=25 # see also https://github.com/yannickwurm/sequenceserver/issues/139 cmd = "curl -ip4 -C - #{url} -o #{archive} && " \ 'mkdir -p ~/.sequenceserver && ' \ "tar xvf #{archive} -C ~/.sequenceserver" system(cmd) end begin Slop.parse!(strict: true, help: true) do banner <<~BANNER SUMMARY custom, local, BLAST server USAGE sequenceserver [options] EXAMPLE # Launch SequenceServer. This will read configuration from # ~/.sequenceserver.conf, if present. $ sequenceserver # Use a different config file. $ sequenceserver -c ~/.sequenceserver.ants.conf # Set number of threads to use. This will save the number # of threads to use in config file. $ sequenceserver -s -n 16 # See if you have FASTA files in database dir that haven't # been converted into BLAST database. $ sequenceserver -u # Search for FASTA files in database dir that haven't been # converted into BLAST database yet, and convert them. $ sequenceserver -m DESCRIPTION SequenceServer lets you rapidly set up a BLAST+ server with an intuitive user interface for use locally or over the web. If BLAST+ is not installed on your system, SequenceServer will offer to install BLAST+ for you. You should only ever have to point it to a directory of FASTA files / BLAST+ databases. In a given directory, SequenceServer is able to tell FASTA files that are yet to be formatted for use with BLAST+ and format them, and FASTA files that are already formatted for use with BLAST+, heuristically skipping all other files in the directory. Directories are scanned recursively. Type of sequences in a FASTA file is detected automagically. `parse_seqids` and `hash_index` options of `makeblastdb` are used to create BLAST+ databases. BANNER on 'c', 'config_file=', 'Use the given configuration file', argument: true on 'config=', 'Same as --config_file (deprecated)', argument: true on 'b', 'bin=', 'Load BLAST+ binaries from this directory', argument: true on 'd', 'database_dir=', 'Read FASTA and BLAST database from this directory', argument: true on 'n', 'num_threads=', 'Number of threads to use to run a BLAST search', argument: true on 'r', 'require=', 'Load extension from this file', argument: true on 'H', 'host=', 'Host to run SequenceServer on', argument: true on 'p', 'port=', 'Port to run SequenceServer on', argument: true on 's', 'set', 'Set configuration value in default or given config file' on 'm', 'make-blast-databases', 'Create BLAST databases' on 'download-taxdb', 'Download the taxdb files' on 'l', 'list_databases', 'List BLAST databases' on 'u', 'list-unformatted-fastas', 'List unformatted FASTA files' on 'i', 'interactive', 'Run SequenceServer in interactive mode' # on 'doctor', # 'Run SequenceServer doctor' on 'D', 'devel', 'Start SequenceServer in development mode' on '-v', '--version', 'Print version number of SequenceServer that will be loaded' on '-h', '--help', 'Display this help message' clean_opts = lambda do |hash| hash.delete_if { |k, v| k == :set || v.nil? } hash[:config_file] ||= hash.delete(:config) hash end run do if version? require 'sequenceserver/version' puts SequenceServer::VERSION exit end ENV['RACK_ENV'] = 'development' if devel? # Exit gracefully on SIGINT. stty = `stty -g`.chomp trap('INT') do puts '' puts 'Aborted.' system('stty', stty) exit end require 'sequenceserver' begin SequenceServer.init clean_opts[to_h] # The aim of following error recovery scenarios is to guide user to a # working SequenceServer installation. We expect to land following # error scenarios either when creating a new SequenceServer (first # time or later), or updating config values using -s CLI option. rescue SequenceServer::CONFIG_FILE_ERROR, SequenceServer::BLAST_DATABASE_ERROR => e puts e exit! rescue SequenceServer::ENOENT => e puts e puts 'Please consult sequenceserver --help for instructions on how to set the correct values.' exit! rescue SequenceServer::NUM_THREADS_INCORRECT => e puts e unless num_threads? puts 'You can set the correct value by running:' puts puts ' sequenceserver -s -n ' puts end exit! rescue SequenceServer::BLAST_NOT_INSTALLED_OR_NOT_EXECUTABLE, SequenceServer::BLAST_NOT_COMPATIBLE => e # Show original error message first. puts puts e # Set a flag so that if we recovered from error resulting config can be # saved. Config will be saved unless invoked with -b option. fetch_option(:set).value = !bin? # Ask user if she already has BLAST+ downloaded or offer to download # BLAST+ for her. puts puts <<~MSG SequenceServer can use NCBI BLAST+ that you may have on your system already, or download the correct package for itself. A recent system update may have broken your NCBI BLAST+ installation. Please enter the path to NCBI BLAST+ or press Enter to download. Press Ctrl+C to quit. MSG puts response = Readline.readline('>> ').to_s.strip if response.empty? puts puts 'Installing NCBI BLAST+.' puts "RUBY_PLATFORM #{RUBY_PLATFORM}" puts 'Archive will be extracted in ~/.sequenceserver directory.' puts version = SequenceServer::BLAST_VERSION url = case RUBY_PLATFORM when /x86_64-linux/ # 64 bit Linux 'ftp://ftp.ncbi.nlm.nih.gov/blast/executables/blast+/' \ "#{version.chop}/ncbi-blast-#{version}-x64-linux.tar.gz" when /darwin/ # Mac 'ftp://ftp.ncbi.nlm.nih.gov/blast/executables/blast+/' \ "#{version.chop}/" \ "ncbi-blast-#{version}-x64-macosx.tar.gz" else puts <<~ERR ------------------------------------------------------------------------ FAILED!! to install NCBI BLAST+. We currently support Linux and Mac only (64 bit). If you believe you are running a supported platform, please open a support ticket titled "#{RUBY_PLATFORM}" at: https://github.com/yannickwurm/sequenceserver/issues ------------------------------------------------------------------------ ERR end download_from_url(url) unless $CHILD_STATUS.success? puts 'Failed to install BLAST+.' puts ' You may need to download BLAST+ from - ' puts ' http://www.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Web&PAGE_TYPE=BlastDocs&DOC_TYPE=Download' puts " Please ensure that you download BLAST+ version #{SequenceServer::BLAST_VERSION}." exit! end fetch_option(:bin).value = "~/.sequenceserver/ncbi-blast-#{version}/bin/" else unless File.basename(response) == 'bin' response = File.join(response, 'bin') end fetch_option(:bin).value = File.join(response) puts end redo rescue SequenceServer::DATABASE_DIR_NOT_SET => e # Show original error message. puts puts e # Set a flag so that if we recovered from error resulting config can be # saved. Config will be saved unless invoked with -d option. fetch_option(:set).value = !database_dir? # Ask user for the directory containing sequences or BLAST+ # databases. puts puts <<~MSG SequenceServer needs to know where your FASTA files or BLAST+ databases are. Please enter the path to the relevant directory (default: current directory). Press Ctrl+C to quit. MSG puts response = Readline.readline('>> ').to_s.strip fetch_option(:database_dir).value = response redo rescue SequenceServer::NO_BLAST_DATABASE_FOUND => e unless list_databases? || list_unformatted_fastas? || make_blast_databases? # Print error raised. puts puts e # Offer user to format the FASTA files. database_dir = SequenceServer.config[:database_dir] puts puts <<~MSG Search for FASTA files (.fa, .fasta, .fna) in '#{database_dir}' and try creating BLAST+ databases? [y/n] (Default: y). MSG puts print '>> ' response = STDIN.gets.to_s.strip unless response =~ /^[n]$/i puts puts 'Searching ...' if SequenceServer::Database.unformatted_fastas.empty? puts "Couldn't find any FASTA files." exit! else formatted = SequenceServer::Database.make_blast_databases exit! if formatted.empty? && !set? redo unless set? end else exit! unless set? end end rescue => e # This will catch any unhandled error and some very special errors. # Ideally we will never hit this block. If we do, there's a bug in # SequenceServer or something really weird going on. If we hit this # error block we show the stacktrace to the user requesting them to # post the same to our Google Group. puts <<~MSG Something went wonky Looks like you have encountered a bug in SequenceServer. Please could you report this incident to our Google Group - https://groups.google.com/forum/?fromgroups#!forum/sequenceserver Error: #{e.backtrace.unshift(e.message).join("\n")} MSG exit end if doctor? puts '*** Running SequenceServer Doctor.' puts ' Initializing an index of databases. This may take a while..' SequenceServer::Doctor.new.diagnose exit end if interactive? SequenceServer.irb exit end if list_databases? puts SequenceServer::Database.all exit end if list_unformatted_fastas? || make_blast_databases? unformatted_fastas = SequenceServer::Database.unformatted_fastas if unformatted_fastas.empty? puts "All FASTA files in #{SequenceServer.config[:database_dir]} " \ 'are formatted.' exit end end if list_unformatted_fastas? puts unformatted_fastas exit end if make_blast_databases? SequenceServer::Database.make_blast_databases exit end if download_taxdb? download_from_url('ftp://ftp.ncbi.nlm.nih.gov/blast/db/'\ 'taxdb.tar.gz') puts puts 'taxdb downloaded!' exit end if set? SequenceServer.config.write_config_file exit end SequenceServer.config.write_config_file if fetch_option(:set).value SequenceServer.run end end rescue Slop::Error => e puts e puts "Run '#{$PROGRAM_NAME} -h' for help with command line options." exit end