class Quandl::Command::Tasks::Schedule < Quandl::Command::Task before_execute :require_dependencies autoload_quandl_client authenticated_users_only! description "Schedule a script to be run in the quandl cloud." syntax %{quandl schedule command [file] COMMANDS: schedule list schedule add file schedule show file schedule delete file schedule replace file EXAMPLES: $ quandl schedule add scraper.rb You have successfully scheduled scraper.rb. $ quandl schedule add scraper.rb --name weekly_scraper.rb --at "7pm Monday" You have successfully scheduled weekly-scraper.rb. $ quandl schedule replace scraper.rb --at "13:00" You have successfully replaced scraper.rb} options({ String => { name: "The name used to reference your scraper in the quandl cloud.", 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) def list scrapers.each { |x| script_info x} end def show script_info scraper end def add result=Quandl::Client::Scraper.create( name: name, scraper: args.first, schedule_at: cron_at) if result.valid? info("You have successfully scheduled '#{scraper.name}'.") info("#{schedule_message}") else raise "#{Quandl::Command::Presenter.pretty_errors(result.errors.messages).to_s.gsub("\n", ' ')}" end end def download begin require 'open-uri' open( scraper.name, 'wb') do |file| $stdout << open(scraper.scraper_url, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}).read end 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 raise "#{Quandl::Command::Presenter.pretty_errors(result.errors.messages).to_s.gsub("\n", ' ')}" end end def replace (info("'#{name}' does not exist "); return;) if scraper.blank? cron_at #validate if cron format is valid before deleting scraper info("You are about to replace '#{scraper.name}'") return unless confirmed? scraper.scraper = args.first scraper.schedule_at = cron_at scraper.save if scraper.valid? info("You have successfully replaced '#{scraper.name}'.") info("#{schedule_message}") else raise "#{Quandl::Command::Presenter.pretty_errors(scraper.errors.messages).to_s.gsub("\n", ' ')}" end end private def require_dependencies require 'chronic' require 'whedon' end def script_info(script) unless script.schedule_at.empty? info("#{(script.name+'.').ljust(20)} Scheduled: #{script_run_time(script.schedule_at)}") else info("#{(script.name+'.').ljust(20)} Scheduled: 3 times a day") end end def script_run_time(cron_time) "every #{cron_time.ends_with?('*') ? 'day' : DAYS_OF_THE_WEEK[cron_time[-1].to_i]} at #{script_next_run(cron_time).strftime("%H:%M (UTC)")}" end def script_next_run(cron_time) Whedon::Schedule.new(cron_time).next end def schedule_message if options.at "Your script first run will happen #{script_next_run(cron_at).strftime("on %d %B at %H:%M (UTC)")}. It will continue to run #{script_run_time(cron_at)} Check your scrapers run status at #{quandl_url.gsub(/\/api\/?/,'')}/scrapers" else "It will run 3 times a day starting immediately.\n Check your scrapers run status at #{quandl_url.gsub(/\/api\/?/,'')}/scrapers" 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_value=Chronic.parse(options.at) raise "#{options.at} is invalid time" if time_value.nil? 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 options.name if options.name.present? return File.basename(args.first) if args.first.present? end end