class Quandl::Command::Tasks::Schedule < Quandl::Command::Task depends 'chronic', 'whedon', 'open-uri', 'zlib', 'archive/tar/minitar' autoload_quandl_client authenticated_users_only! description "Schedule a script to be run in the quandl cloud." syntax %{quandl schedule command [file(s)] COMMANDS: schedule list [FILE] schedule add RUN_FILE [DEPENDENT_FILE1] [DEPENDENT_FILE2] [--at TIME] schedule delete SCRIPT schedule run SCRIPT EXAMPLES: $ quandl schedule add scraper.rb You have successfully scheduled scraper.rb. $ quandl schedule add scraper.rb helper.txt helper2.txt --at "7pm Monday" You have successfully scheduled scraper.rb to run alongside helper.txt, helper2.txt. [weekly scraper] $ quandl schedule deleted scraper.rb You have successfully deleted scraper.rb. $ quandl schedule run scraper.rb Your scraper.rb will run immediately. } options({ String => { at: "Time to run your script in UTC timezone. e.g. '14:30','7pm Monday', 'friday 13:00'" } }) DAYS_OF_THE_WEEK = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday) validate :cron_at_should_be_valid! def execute # fire a subcommand if specified return self.send(args.shift) if args.first.present? && self.respond_to?(args.first) # otherwise fire add return add if args.first.present? && args.first.downcase != "help" # otherwise display syntax info(syntax) end def list if args.first (info("'#{name}' does not exist "); return;) if scraper.blank? script_info scraper else # create new blank scope and iterate over all pages Quandl::Client::Scraper.scope.new.each_in_page{|x| script_info(x) } end end def add upload_file = Tempfile.new('temp') begin result=Quandl::Client::Scraper.create( name: name, scraper: file_to_send(args, upload_file), schedule_at: cron_at_utc) if result.valid? info("You have successfully scheduled '#{scraper.name}'.") info("#{schedule_message}") else error "#{Quandl::Command::Presenter.pretty_errors(result.errors.messages).to_s.gsub("\n", ' ')}" end ensure upload_file.close! end end def download begin $stdout.binmode $stdout << open(scraper.scraper_url, "rb", {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}).read rescue => err present err end end def delete (info("'#{name}' does not exist "); return;) if scraper.blank? info("You are about to delete '#{scraper.name}'") return unless confirmed? result=scraper.destroy if scraper.respond_to?(:destroy) if result.valid? info("You have successfully deleted '#{scraper.name}'") else error "#{Quandl::Command::Presenter.pretty_errors(result.errors.messages).to_s.gsub("\n", ' ')}" end end def run (info("'#{name}' does not exist "); return;) if scraper.blank? info("You are about to run '#{scraper.name}'") return unless confirmed? result=scraper.run_now if scraper.respond_to?(:run_now) if result.valid? info("Your '#{scraper.name}' will run immediately.") else if result.status == 429 info("You have exceeded your daily run limit.") else error "#{Quandl::Command::Presenter.pretty_errors(result.errors.messages).to_s.gsub("\n", ' ')}" end end end #not supported for now =begin def replace upload_file = Tempfile.new('temp') begin (info("'#{name}' does not exist "); return;) if scraper.blank? info("You are about to replace '#{scraper.name}'") return unless confirmed? scraper.scraper = file_to_send(args, upload_file) scraper.schedule_at = cron_at_utc scraper.save if scraper.valid? info("You have successfully replaced '#{scraper.name}'.") info("#{schedule_message}") if cron_at else error "#{Quandl::Command::Presenter.pretty_errors(scraper.errors.messages).to_s.gsub("\n", ' ')}" end ensure upload_file.close! end end =end private def script_info(script) message = script.name.to_s.ljust(20) message += " Scheduled: #{script.schedule_run_time.to_s.ljust(30)}" message += " Next: in #{script.schedule_next}" if script.schedule_next.present? info message end def schedule_message if options.at "Your script first run will happen in #{scraper.schedule_next}. It will continue to run #{scraper.schedule_run_time} Check your script run status at #{quandl_url.gsub(/\/api\/?/,'')}/schedule/#{scraper.id}" else "It will run 3 times a day starting immediately.\nCheck your script run status at #{quandl_url.gsub(/\/api\/?/,'')}/schedule/#{scraper.id}" end end def scrapers @scrapers ||= Quandl::Client::Scraper.where( page: page ).fetch end def scraper defined?(@scraper) ? @scraper : ( @scraper = find_scraper ) end def find_scraper return nil unless args.first.present? # if the arg is numeric lookup the id. otherwise search for a scraper by name and grab the id id = args.first.numeric? ? args.first : Quandl::Client::Scraper.where( name: name ).try(:first).try(:id) # find the scraper with the id Quandl::Client::Scraper.find(id) unless id.nil? end def cron_at return nil unless options.at time_to_cron Chronic.parse(options.at) end def cron_at_utc return nil unless options.at time_to_cron Chronic.parse(options.at).utc end def time_to_cron(time_value) day_of_the_week = options.at =~ (/Mon|Tue|Wed|Thu|Fri|Sat|Sun/i) ? time_value.wday : '*' "* #{time_value.min} #{time_value.hour} * * #{day_of_the_week}" end def name return File.basename(args.first) if args.first.present? end def cron_at_should_be_valid! if options.at.present? && !Chronic.parse(options.at) self.errors.add(:base,"#{options.at} is invalid time format" ) return false end true end def file_to_send(values, upload_file) if [*values].count > 1 file = upload_file Archive::Tar::Minitar.pack( values, Zlib::GzipWriter.new( File.new( file, 'wb' ) ) ) else file = [*values].first end file end end