#!/usr/bin/env ruby require 'open-uri' require 'json' require 'cgi' require 'optparse' # ruby standard option parser #--------------------------------class definitiom class ScopusSearch attr_accessor :options, :version, :using_alfred, :scopusBaseURL VER = '1.0.10'.freeze OPT = Struct.new(:key, :doi, :uuid, :source, :openURLs, :append) #--------------------- constructor def initialize # set up class @options = OPT.new('7f59af901d2d86f78a1fd60c1bf9426a', '', '', 'scopus', false, false) @version = VER @using_alfred = false @surl = '' @scite = '' @ncite = '' @uuid = '' @title = '' @subtitle = '' @cited = '' @success = false @scopusBaseURL = '' end # ###############################parse inputs def parseInputs(_arg) optparse = OptionParser.new do |opts| opts.banner = 'ScopusSearch V' + @version + "\n" opts.banner += "===================\n" opts.banner += " Either Pass the DOI with -d or select a ref in Bookends\n" opts.banner += " (containing a DOI), and get the Scopus URLs back…\n" opts.on('-k', '--key KEY', 'API KEY?') do |v| @options[:key] = v end opts.on('-d', '--doi DOI', 'DOI to search for?') do |v| @options[:doi] = v end opts.on('-s', '--source database', String, 'scopus (default) or scidir?') do |v| @options[:source] = v end opts.on('-a', '--append [true]|false', TrueClass, 'append URLS to Bookends note') do |v| @options[:append] = v end opts.on('-o', '--open true|[false] ', FalseClass, 'open URLs in browser?') do |v| @options[:openURLs] = v end opts.on('-h', '--help', 'Prints this help!') do puts optparse exit(0) end end # end OptionParser optparse.parse! end # end parseInputs def run returnNullResults('Incomplete input options...') if options.doi.nil? || options.key.nil? url = 'https://api.elsevier.com/content/search/' + options.source url += '?query=DOI%28%22' + CGI.escape(@options.doi) + '%22%29' url += '&apiKey=' + @options.key url += '&httpAccept=application%2Fjson' response = '' begin uri = URI.parse(url) response = uri.open(:proxy => nil).read rescue => exception if @using_alfred subt = "Fetching => #{@options.doi}" subt = "Try to disable system proxy. " + subt if exception.to_s =~ /127.0.0.1/ jsono = { items: [{ uid: 'error', arg: '', title: 'URI error:'+exception.to_s, subtitle: subt }] } puts JSON.pretty_generate(jsono) else puts 'Error fetching DOI: ' + exception.to_s end exit(false) end response, n, json = parseResponse(response) parseJSON(json) if n > 0 if @success == true changeBaseURL returnResults else returnNullResults(response) end end def parseResponse(response) if response.nil? || response.empty? n = 0 response = 'Response empty' json = '' elsif response =~ /service-error/ n = 0 response = 'Service Error' json = '' elsif response =~ /Result set was empty/ n = 0 response = 'Result set was empty' json = '' else json = JSON.parse(response) n = json['search-results']['opensearch:totalResults'].to_i end return response, n, json end def parseJSON(json) r = json['search-results']['entry'] r.each do |re| @uuid = re['dc:identifier'] @uuid = 'none' if @uuid.nil? @title = re['dc:title'] @creator = re['dc:creator'] @cited = re['citedby-count'] @cited = '' if @cited.nil? links = re['link'] links.each do |lnk| if lnk['@ref'] == options.source @surl = lnk['@href'] elsif lnk['@ref'] == 'scopus-citedby' @scite = lnk['@href'] elsif lnk['@ref'] == 'first' @scite = lnk['@href'] end end end @success = true end def returnResults if @using_alfred jsonc = { uid: @uuid + "A", arg: @scite, title: "#{@cited} CITING: " + @title, subtitle: 'Open URL to papers that cite this paper…' } if @surl.empty? jsono = { items: [jsonc]} else jsons = { uid: @uuid + "B", arg: @surl, title: @title, subtitle: "Open URL to the reference..." } jsono = { items: [jsonc, jsons]} end puts JSON.pretty_generate(jsono) else puts 'SCOPUS ' + @cited + ' CITED: ' + @scite + "\nSCOPUS ENTRY: " + @surl end end def returnNoDOI() if @using_alfred jsono = { items: [{ uid: 'error', arg: '-1', title: 'No DOI; please select ref with DOI!', subtitle: 'Unique ID => ' + @options.uuid }] } puts JSON.pretty_generate(jsono) else puts 'No DOI was specified or found: ' + @options.uuid + ' - ' + response end exit(0) end def returnNullResults(response) if @using_alfred jsono = { items: [{ uid: 'error', arg: '-1', title: 'Sorry, no entry in Scopus was found', subtitle: @options.doi + " => " + response }] } puts JSON.pretty_generate(jsono) else puts 'No results found for: ' + @options.doi + ' => ' + response end exit(0) end def writeNotes return unless @success == true return unless @options.append == true return if @options.uuid.empty? puts "Will try to write to ID=#{options.uuid}…" unless @using_alfred newnote = "#SCOPUS #{@cited} CITES: #{@scite}\r\r#SCOPUS URL: #{@surl}" osascript <<-EOT tell application "Bookends" tell front library window set tnote to notes of publication item id #{options.uuid} if tnote is not equal to "" then set tnote to tnote & return & return end if set out to tnote & "#{newnote}" & return & return set notes of publication item id #{options.uuid} to out end tell end tell EOT puts "… finished appending to: ID=#{options.uuid}" unless using_alfred end def getDOI results = osascript <<-EOT tell application "Bookends" set mynull to ASCII character 30 set myRefs to selected publication items of front library window set thisID to id of first item of myRefs set thisDOI to doi of first item of myRefs return thisID & mynull & thisDOI end tell EOT if results.nil? || results.empty? @options[:uuid] = '' @options[:doi] = '' else results = results.split("\u001E") @options[:uuid] = results[0].chomp.strip doi = results[1].chomp.strip doi = '' if doi.nil? @options[:doi] = doi end end def changeBaseURL return unless @success == true return if @scopusBaseURL.empty? @scite.gsub!(/https:\/\/[^\/]+\//,@scopusBaseURL) @surl.gsub!(/https:\/\/[^\/]+\//,@scopusBaseURL) end def open_links return unless @options.openURLs == true return unless @success == true puts 'Will open Scopus Links in browser...' unless using_alfred cmd = "open \"#{@surl}\"" IO.popen(cmd) cmd = "open \"#{@scite}\"" IO.popen(cmd) end # this converts to -e line format so osascript can run, pass in a heredoc # beware this splits on \n so can't include them in the applescript itself def osascript(script) cmd = ['osascript'] + script.split(/\n/).map { |line| ['-e', line] }.flatten IO.popen(cmd) { |f| return f.gets } end end #--------------- end ScopusSearch class #------------------------------------------------- ss = ScopusSearch.new ss.parseInputs(ARGV) # check if running under alfred ss.using_alfred = true unless ENV['alfred_version'].nil? # check ENV for scopusKey, overrides default/ARGV key = ENV['scopusKey'] ss.options.key = key unless key.nil? || key.empty? # check ENV for append option, overrides ARGV append = ENV['appendScopusNotes'] unless append.nil? || append.empty? || append.to_s =~ /(false|0)/ ss.options.append = true end # check ENV for baseURL option ss.scopusBaseURL = ENV['scopusBaseURL'] ss.scopusBaseURL = '' if ss.scopusBaseURL.nil? # if no DOI try to get our DOI directly from the selected ref in Bookends ss.getDOI if ss.options.doi.empty? if ss.options.doi.empty? ss.returnNoDOI else ss.run ss.writeNotes ss.open_links ENV['scopusSearchReturn'] = '0' end