Class: Alexandria::BookProviders::AmazonProvider

Inherits:
GenericProvider show all
Includes:
Logging, GetText
Defined in:
lib/alexandria/book_providers/amazon_aws.rb

Constant Summary collapse

LOCALES =

CACHE_DIR = File.join(Alexandria::Library::DIR, ‘.amazon_cache’)

['ca', 'de', 'fr', 'jp', 'uk', 'us'].freeze

Instance Attribute Summary

Attributes inherited from AbstractProvider

#fullname, #name, #prefs

Instance Method Summary collapse

Methods included from Logging

included, #log

Methods inherited from AbstractProvider

#<=>, abstract?, #abstract?, #action_name, #enabled, #reinitialize, #remove, #toggle_enabled, #transport, unabstract, #variable_name

Constructor Details

#initializeAmazonProvider

Returns a new instance of AmazonProvider.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/alexandria/book_providers/amazon_aws.rb', line 38

def initialize
  super('Amazon', 'Amazon')
  # prefs.add("enabled", _("Enabled"), true, [true,false])
  prefs.add('locale', _('Locale'), 'us', AmazonProvider::LOCALES)
  prefs.add('dev_token', _('Access key ID'), '')
  prefs.add('secret_key', _('Secret access key'), '')
  prefs.add('associate_tag', _('Associate Tag'), '')

  prefs.read
  token = prefs.variable_named('dev_token')
  # kill old (shorter) tokens, or previously distributed Access Key Id (see #26250)

  if token
    token.new_value = token.value.strip if token.value != token.value.strip
  end
  token.new_value = '' if token && ((token.value.size != 20) || (token.value == '0J356Z09CN88KB743582'))

  secret = prefs.variable_named('secret_key')
  if secret
    secret.new_value = secret.value.strip if secret.value != secret.value.strip
  end

  associate = prefs.variable_named('associate_tag')
  if associate
    associate.new_value = 'rubyalexa-20' if associate.value.strip.empty?
    associate.new_value = associate.value.strip if associate.value != associate.value.strip
  end
end

Instance Method Details

#normalize(str) ⇒ Object



237
238
239
240
# File 'lib/alexandria/book_providers/amazon_aws.rb', line 237

def normalize(str)
  str = str.squeeze(' ').strip unless str.nil?
  str
end

#search(criterion, type) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/alexandria/book_providers/amazon_aws.rb', line 67

def search(criterion, type)
  prefs.read

  if prefs['secret_key'].empty?
    raise(Amazon::RequestError,
          'Secret Access Key required for Authentication:' \
          ' you must sign up for your own Amazon AWS account')
  end

  if (config = Alexandria::Preferences.instance.http_proxy_config)
    host, port, user, pass = config
    url = 'http://'
    url += user + ':' + pass + '@' if user && pass
    url += host + ':' + port.to_s
    ENV['http_proxy'] = url
  end

  access_key_id = prefs['dev_token']

  Amazon::Ecs.options = { aWS_access_key_id: access_key_id,
                          associateTag: prefs['associate_tag'] }
  Amazon::Ecs.secret_access_key = prefs['secret_key']
  # #req.cache = Amazon::Search::Cache.new(CACHE_DIR)
  locales = AmazonProvider::LOCALES.dup
  locales.delete prefs['locale']
  locales.unshift prefs['locale']
  locales.reverse!

  begin
    request_locale = locales.pop.intern
    products = []
    case type
    when SEARCH_BY_ISBN
      criterion = Library.canonicalise_isbn(criterion)
      # This isn't ideal : I'd like to do an ISBN/EAN-specific search
      res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images',
                                               country: request_locale)
      res.items.each do |item|
        products << item
      end
      # #req.asin_search(criterion) do |product|

      # Shouldn't happen.
      # raise TooManyResultsError if products.length > 1

      # I had assumed that publishers were bogusly publishing
      # multiple editions of a book with the same ISBN, and
      # Amazon was distinguishing between them.  So we'll log
      # this case, and arbitrarily return the FIRST item

      # Actually, this may be due to Amazon recommending a
      # preferred later edition of a book, in spite of our
      # searching on a single ISBN it can return more than one
      # result with different ISBNs

      if products.length > 1
        log.warn {
          "ISBN search at Amazon[#{request_locale}] got #{products.length} results;" \
          ' returning the first result only'
        }
      end

    when SEARCH_BY_TITLE
      res = Amazon::Ecs.item_search(criterion,
                                    response_group: 'ItemAttributes,Images',
                                    country: request_locale)

      res.items.each do |item|
        products << item if item.get('itemattributes/title') =~ /#{criterion}/i
      end
      # #req.keyword_search(criterion) do |product|

    when SEARCH_BY_AUTHORS
      criterion = "author:#{criterion}"
      res = Amazon::Ecs.item_search(criterion,
                                    response_group: 'ItemAttributes,Images',
                                    country: request_locale, type: 'Power')
      res.items.each do |item|
        products << item
      end
      # #req.author_search(criterion) do |product|

    when SEARCH_BY_KEYWORD
      res = Amazon::Ecs.item_search(criterion,
                                    response_group: 'ItemAttributes,Images',
                                    country: request_locale)

      res.items.each do |item|
        products << item
      end

    else
      raise InvalidSearchTypeError
    end
    raise Amazon::RequestError, 'No products' if products.empty?
    # raise NoResultsError if products.empty?
  rescue Amazon::RequestError => re
    log.debug { "Got Amazon::RequestError at #{request_locale}: #{re}" }
    retry unless locales.empty?
    raise NoResultsError
  end

  results = []
  products.each do |item|
    next unless item.get('itemattributes/productgroup') == 'Book'
    atts = item.search_and_convert('itemattributes')
    title = normalize(atts.get('title'))

    media = normalize(atts.get('binding'))
    media = nil if media == 'Unknown Binding'

    isbn = normalize(atts.get('isbn'))
    isbn = (Library.canonicalise_ean(isbn) if isbn && Library.valid_isbn?(isbn))
    # hack, extract year by regexp (not Y10K compatible :-)
    /([1-9][0-9]{3})/ =~ atts.get('publicationdate')
    publishing_year = Regexp.last_match[1] ? Regexp.last_match[1].to_i : nil
    book = Book.new(title,
                    atts.get_array('author').map { |x| normalize(x) },
                    isbn,
                    normalize(atts.get('manufacturer')),
                    publishing_year,
                    media)

    image_url = item.get('mediumimage/url')
    log.info { "Found at Amazon[#{request_locale}]: #{book.title}" }
    results << [book, image_url]
  end
  if type == SEARCH_BY_ISBN
    if results.size == 1
      return results.first
    else
      log.info { 'Found multiple results for lookup: checking each' }
      query_isbn_canon = Library.canonicalise_ean(criterion)
      results.each do |rslt|
        book = rslt[0]
        book_isbn_canon = Library.canonicalise_ean(book.isbn)
        return rslt if query_isbn_canon == book_isbn_canon
        log.debug { "rejected possible result #{book}" }
      end
      # gone through all and no ISBN match, so just return first result
      log.info { 'no more results to check. Returning first result, just an approximation' }
      return results.first
    end
  else
    return results
  end
end

#url(book) ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/alexandria/book_providers/amazon_aws.rb', line 215

def url(book)
  isbn = Library.canonicalise_isbn(book.isbn)
  url = case prefs['locale']
        when 'fr'
          'http://www.amazon.fr/exec/obidos/ASIN/%s'
        when 'uk'
          'http://www.amazon.co.uk/exec/obidos/ASIN/%s'
        when 'de'
          'http://www.amazon.de/exec/obidos/ASIN/%s'
        when 'ca'
          'http://www.amazon.ca/exec/obidos/ASIN/%s'
        when 'jp'
          'http://www.amazon.jp/exec/obidos/ASIN/%s'
        when 'us'
          'http://www.amazon.com/exec/obidos/ASIN/%s'
        end
  url % isbn
rescue => ex
  log.warn { "Cannot create url for book #{book}; #{ex.message}" }
  nil
end