alexandria-book-collection-manager 0.7.1 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +9 -0
  3. data/.gitignore +5 -2
  4. data/.hound.yml +2 -0
  5. data/.rubocop.yml +113 -45
  6. data/.rubocop_todo.yml +82 -170
  7. data/.simplecov +5 -1
  8. data/.travis.yml +45 -0
  9. data/.yardopts +1 -1
  10. data/CHANGELOG.md +60 -0
  11. data/ChangeLog.0 +33 -35
  12. data/Gemfile +6 -5
  13. data/INSTALL.md +164 -0
  14. data/README.md +52 -42
  15. data/Rakefile +95 -109
  16. data/TODO.md +9 -1
  17. data/alexandria-book-collection-manager.gemspec +52 -45
  18. data/bin/alexandria +31 -53
  19. data/doc/AUTHORS +61 -0
  20. data/doc/BUGS +31 -0
  21. data/doc/FAQ +365 -0
  22. data/doc/HACKING +19 -0
  23. data/doc/NEWS +341 -0
  24. data/doc/alexandria.1 +120 -0
  25. data/doc/cuecat_support.rdoc +67 -0
  26. data/doc/dependency_decisions.yml +80 -0
  27. data/lib/alexandria.rb +29 -37
  28. data/lib/alexandria/about.rb +52 -51
  29. data/lib/alexandria/book_providers.rb +94 -101
  30. data/lib/alexandria/book_providers/adlibris.rb +45 -85
  31. data/lib/alexandria/book_providers/amazon_aws.rb +105 -113
  32. data/lib/alexandria/book_providers/amazon_ecs_util.rb +293 -324
  33. data/lib/alexandria/book_providers/barnes_and_noble.rb +54 -53
  34. data/lib/alexandria/book_providers/douban.rb +29 -51
  35. data/lib/alexandria/book_providers/proxis.rb +42 -59
  36. data/lib/alexandria/book_providers/pseudomarc.rb +79 -99
  37. data/lib/alexandria/book_providers/siciliano.rb +68 -70
  38. data/lib/alexandria/book_providers/thalia.rb +46 -45
  39. data/lib/alexandria/book_providers/web.rb +17 -33
  40. data/lib/alexandria/book_providers/worldcat.rb +74 -102
  41. data/lib/alexandria/book_providers/z3950.rb +170 -174
  42. data/lib/alexandria/config.rb +5 -3
  43. data/lib/alexandria/console.rb +10 -21
  44. data/lib/alexandria/default_preferences.rb +37 -0
  45. data/lib/alexandria/execution_queue.rb +17 -15
  46. data/lib/alexandria/export_format.rb +47 -0
  47. data/lib/alexandria/export_library.rb +188 -302
  48. data/lib/alexandria/import_library.rb +114 -155
  49. data/lib/alexandria/import_library_csv.rb +46 -96
  50. data/lib/alexandria/library_collection.rb +79 -0
  51. data/lib/alexandria/library_sort_order.rb +45 -0
  52. data/lib/alexandria/library_store.rb +233 -0
  53. data/lib/alexandria/logging.rb +15 -19
  54. data/lib/alexandria/models/book.rb +15 -20
  55. data/lib/alexandria/models/library.rb +81 -363
  56. data/lib/alexandria/net.rb +7 -6
  57. data/lib/alexandria/preferences.rb +73 -91
  58. data/lib/alexandria/scanners.rb +4 -2
  59. data/lib/alexandria/scanners/{cuecat.rb → cue_cat.rb} +24 -20
  60. data/lib/alexandria/scanners/keyboard.rb +10 -8
  61. data/lib/alexandria/smart_library.rb +135 -171
  62. data/lib/alexandria/ui.rb +17 -15
  63. data/lib/alexandria/ui/about_dialog.rb +49 -0
  64. data/lib/alexandria/ui/{dialogs/acquire_dialog.rb → acquire_dialog.rb} +129 -152
  65. data/lib/alexandria/ui/alert_dialog.rb +64 -0
  66. data/lib/alexandria/ui/bad_isbns_dialog.rb +41 -0
  67. data/lib/alexandria/ui/{dialogs/barcode_animation.rb → barcode_animation.rb} +18 -15
  68. data/lib/alexandria/ui/{dialogs/book_properties_dialog.rb → book_properties_dialog.rb} +44 -61
  69. data/lib/alexandria/ui/{dialogs/book_properties_dialog_base.rb → book_properties_dialog_base.rb} +84 -89
  70. data/lib/alexandria/ui/builder_base.rb +9 -27
  71. data/lib/alexandria/ui/callbacks.rb +188 -186
  72. data/lib/alexandria/ui/columns.rb +2 -0
  73. data/lib/alexandria/ui/completion_models.rb +12 -23
  74. data/lib/alexandria/ui/confirm_erase_dialog.rb +33 -0
  75. data/lib/alexandria/ui/conflict_while_copying_dialog.rb +34 -0
  76. data/lib/alexandria/ui/dndable.rb +10 -8
  77. data/lib/alexandria/ui/error_dialog.rb +25 -0
  78. data/lib/alexandria/ui/export_dialog.rb +139 -0
  79. data/lib/alexandria/ui/icons.rb +49 -65
  80. data/lib/alexandria/ui/iconview.rb +15 -13
  81. data/lib/alexandria/ui/iconview_tooltips.rb +43 -58
  82. data/lib/alexandria/ui/import_dialog.rb +157 -0
  83. data/lib/alexandria/ui/init.rb +23 -33
  84. data/lib/alexandria/ui/keep_bad_isbn_dialog.rb +36 -0
  85. data/lib/alexandria/ui/libraries_combo.rb +18 -14
  86. data/lib/alexandria/ui/listview.rb +77 -88
  87. data/lib/alexandria/ui/main_app.rb +26 -26
  88. data/lib/alexandria/ui/misc_dialogs.rb +10 -0
  89. data/lib/alexandria/ui/multi_drag_treeview.rb +30 -41
  90. data/lib/alexandria/ui/{dialogs/new_book_dialog.rb → new_book_dialog.rb} +168 -215
  91. data/lib/alexandria/ui/new_book_dialog_manual.rb +139 -0
  92. data/lib/alexandria/ui/new_provider_dialog.rb +100 -0
  93. data/lib/alexandria/ui/new_smart_library_dialog.rb +74 -0
  94. data/lib/alexandria/ui/preferences_dialog.rb +313 -0
  95. data/lib/alexandria/ui/provider_preferences_base_dialog.rb +95 -0
  96. data/lib/alexandria/ui/provider_preferences_dialog.rb +35 -0
  97. data/lib/alexandria/ui/really_delete_dialog.rb +53 -0
  98. data/lib/alexandria/ui/{sidepane.rb → sidepane_manager.rb} +62 -72
  99. data/lib/alexandria/ui/skip_entry_dialog.rb +33 -0
  100. data/lib/alexandria/ui/smart_library_properties_dialog.rb +60 -0
  101. data/lib/alexandria/ui/{dialogs/smart_library_properties_dialog_base.rb → smart_library_properties_dialog_base.rb} +96 -172
  102. data/lib/alexandria/ui/smart_library_rule_box.rb +119 -0
  103. data/lib/alexandria/ui/sound.rb +13 -13
  104. data/lib/alexandria/ui/ui_manager.rb +262 -283
  105. data/lib/alexandria/undo_manager.rb +3 -0
  106. data/lib/alexandria/version.rb +6 -19
  107. data/lib/alexandria/web_themes.rb +24 -21
  108. data/po/Makefile +2 -2
  109. data/po/cs.po +993 -880
  110. data/po/cy.po +957 -874
  111. data/po/de.po +990 -869
  112. data/po/el.po +989 -869
  113. data/po/es.po +985 -865
  114. data/po/fr.po +986 -870
  115. data/po/ga.po +907 -823
  116. data/po/gl.po +981 -865
  117. data/po/it.po +986 -868
  118. data/po/ja.po +969 -853
  119. data/po/mk.po +983 -863
  120. data/po/nb.po +979 -863
  121. data/po/nl.po +983 -864
  122. data/po/pl.po +1020 -969
  123. data/po/pt.po +988 -861
  124. data/po/pt_BR.po +984 -868
  125. data/po/ru.po +992 -873
  126. data/po/sk.po +987 -869
  127. data/po/sv.po +977 -861
  128. data/po/uk.po +975 -865
  129. data/po/zh_TW.po +976 -860
  130. data/schemas/alexandria.schemas +25 -3
  131. data/share/alexandria/glade/acquire_dialog__builder.glade +15 -12
  132. data/share/alexandria/glade/book_properties_dialog__builder.glade +171 -299
  133. data/share/alexandria/glade/main_app__builder.glade +24 -33
  134. data/share/alexandria/glade/new_book_dialog__builder.glade +27 -59
  135. data/share/alexandria/glade/preferences_dialog__builder.glade +250 -290
  136. data/share/gnome/help/alexandria/C/introduction.xml +0 -8
  137. data/share/gnome/help/alexandria/C/searching.xml +1 -1
  138. data/share/gnome/help/alexandria/C/smart-libraries.xml +2 -2
  139. data/share/gnome/help/alexandria/C/working-with-libraries.xml +1 -1
  140. data/share/gnome/help/alexandria/fr/alexandria.xml +1 -1
  141. data/share/gnome/help/alexandria/ja/introduction.xml +0 -8
  142. data/share/gnome/help/alexandria/ja/smart-libraries.xml +1 -1
  143. data/spec/alexandria/book_providers/world_cat_provider_spec.rb +160 -0
  144. data/spec/alexandria/book_providers_spec.rb +77 -210
  145. data/spec/alexandria/book_spec.rb +16 -12
  146. data/spec/alexandria/console_spec.rb +27 -0
  147. data/spec/alexandria/export_library_spec.rb +130 -0
  148. data/spec/alexandria/library_spec.rb +130 -172
  149. data/spec/alexandria/library_store_spec.rb +37 -0
  150. data/spec/alexandria/preferences_spec.rb +46 -17
  151. data/spec/alexandria/scanners/cue_cat_spec.rb +52 -0
  152. data/spec/alexandria/smart_library_spec.rb +32 -25
  153. data/spec/alexandria/ui/about_dialog_spec.rb +14 -0
  154. data/spec/alexandria/ui/acquire_dialog_spec.rb +14 -0
  155. data/spec/alexandria/ui/alert_dialog_spec.rb +16 -0
  156. data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +14 -0
  157. data/spec/alexandria/ui/book_properties_dialog_spec.rb +17 -0
  158. data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +14 -0
  159. data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +16 -0
  160. data/spec/alexandria/ui/error_dialog_spec.rb +14 -0
  161. data/spec/alexandria/ui/export_dialog_spec.rb +15 -0
  162. data/spec/alexandria/ui/icons_spec.rb +26 -0
  163. data/spec/alexandria/ui/iconview_spec.rb +9 -21
  164. data/spec/alexandria/ui/import_dialog_spec.rb +41 -0
  165. data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +17 -0
  166. data/spec/alexandria/ui/main_app_spec.rb +8 -33
  167. data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +15 -0
  168. data/spec/alexandria/ui/new_book_dialog_spec.rb +22 -0
  169. data/spec/alexandria/ui/new_provider_dialog_spec.rb +30 -0
  170. data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +39 -0
  171. data/spec/alexandria/ui/preferences_dialog_spec.rb +14 -0
  172. data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +34 -0
  173. data/spec/alexandria/ui/really_delete_dialog_spec.rb +16 -0
  174. data/spec/alexandria/ui/sidepane_manager_spec.rb +15 -0
  175. data/spec/alexandria/ui/skip_entry_dialog_spec.rb +14 -0
  176. data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +32 -0
  177. data/spec/alexandria/ui/sound_spec.rb +4 -2
  178. data/spec/alexandria/ui/ui_manager_spec.rb +45 -20
  179. data/spec/end_to_end/basic_run_spec.rb +57 -0
  180. data/spec/spec_helper.rb +66 -33
  181. data/tasks/setup.rb +5 -3
  182. data/tasks/spec.rake +18 -3
  183. data/util/rake/fileinstall.rb +38 -40
  184. data/util/rake/gettextgenerate.rb +15 -70
  185. data/util/rake/omfgenerate.rb +10 -10
  186. metadata +176 -60
  187. data/INSTALL.rdoc +0 -148
  188. data/dogtail/basic_run_test.py +0 -9
  189. data/lib/alexandria/book_providers/bol_it.rb +0 -160
  190. data/lib/alexandria/book_providers/deastore.rb +0 -273
  191. data/lib/alexandria/book_providers/ibs_it.rb +0 -147
  192. data/lib/alexandria/book_providers/mcu.rb +0 -169
  193. data/lib/alexandria/book_providers/renaud.rb +0 -140
  194. data/lib/alexandria/book_providers/webster_it.rb +0 -167
  195. data/lib/alexandria/ui/dialogs/about_dialog.rb +0 -59
  196. data/lib/alexandria/ui/dialogs/alert_dialog.rb +0 -70
  197. data/lib/alexandria/ui/dialogs/bad_isbns_dialog.rb +0 -43
  198. data/lib/alexandria/ui/dialogs/export_dialog.rb +0 -171
  199. data/lib/alexandria/ui/dialogs/import_dialog.rb +0 -196
  200. data/lib/alexandria/ui/dialogs/misc_dialogs.rb +0 -85
  201. data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +0 -154
  202. data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +0 -74
  203. data/lib/alexandria/ui/dialogs/preferences_dialog.rb +0 -578
  204. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +0 -57
  205. data/spec/alexandria/scanners/cuecat_spec.rb +0 -65
  206. data/spec/alexandria/ui/dialogs_spec.rb +0 -94
  207. data/spec/alexandria/ui/sidepane_spec.rb +0 -27
  208. data/spec/alexandria/ui/ui_utilities_spec.rb +0 -60
  209. data/spec/alexandria/utilities_spec.rb +0 -50
  210. data/tasks/dogtail.rake +0 -4
@@ -1,22 +1,8 @@
1
- # -*- ruby -*-
2
- #
3
- # Copyright (C) 2009 Cathal Mc Ginley
4
- # Copyright (C) 2014 Matijs van Zuijlen
5
- #
6
- # Alexandria is free software; you can redistribute it and/or
7
- # modify it under the terms of the GNU General Public License as
8
- # published by the Free Software Foundation; either version 2 of the
9
- # License, or (at your option) any later version.
10
- #
11
- # Alexandria is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
- # General Public License for more details.
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Alexandria.
15
4
  #
16
- # You should have received a copy of the GNU General Public
17
- # License along with Alexandria; see the file COPYING. If not,
18
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
19
- # Fifth Floor, Boston, MA 02110-1301 USA.
5
+ # See the file README.md for authorship and licensing information.
20
6
 
21
7
  # AdLibris Bokhandel AB http://www.adlibris.com/se/
22
8
  # Swedish online book store
@@ -25,23 +11,23 @@
25
11
  # modified to fit the structure of Alexandria book providers.
26
12
  # (26 Feb 2009)
27
13
 
28
- require 'cgi'
29
- require 'alexandria/book_providers/web'
14
+ require "cgi"
15
+ require "alexandria/book_providers/web"
30
16
 
31
17
  module Alexandria
32
18
  class BookProviders
33
19
  class AdLibrisProvider < WebsiteBasedProvider
34
- include Alexandria::Logging
20
+ include Logging
35
21
 
36
- SITE = 'http://www.adlibris.com/se/'.freeze
22
+ SITE = "http://www.adlibris.com/se/"
37
23
 
38
24
  BASE_SEARCH_URL = "#{SITE}searchresult.aspx?search=advanced&%s=%s" \
39
- '&fromproduct=False'.freeze # type/term
25
+ "&fromproduct=False" # type/term
40
26
 
41
- PRODUCT_URL = "#{SITE}product.aspx?isbn=%s".freeze
27
+ PRODUCT_URL = "#{SITE}product.aspx?isbn=%s"
42
28
 
43
29
  def initialize
44
- super('AdLibris', 'AdLibris (Sweden)')
30
+ super("AdLibris", "AdLibris (Sweden)")
45
31
  prefs.read
46
32
  # @ent = HTMLEntities.new
47
33
  end
@@ -65,7 +51,7 @@ module Alexandria
65
51
  ## url
66
52
  def url(book)
67
53
  create_search_uri(SEARCH_BY_ISBN, book.isbn)
68
- rescue => ex
54
+ rescue StandardError => ex
69
55
  log.warn { "Cannot create url for book #{book}; #{ex.message}" }
70
56
  nil
71
57
  end
@@ -77,12 +63,12 @@ module Alexandria
77
63
  PRODUCT_URL % Library.canonicalise_isbn(search_term)
78
64
  else
79
65
  (search_type_code = {
80
- SEARCH_BY_AUTHORS => 'author',
81
- SEARCH_BY_TITLE => 'title',
82
- SEARCH_BY_KEYWORD => 'keyword'
83
- }[search_type]) || 'keyword'
66
+ SEARCH_BY_AUTHORS => "author",
67
+ SEARCH_BY_TITLE => "title",
68
+ SEARCH_BY_KEYWORD => "keyword"
69
+ }[search_type]) || "keyword"
84
70
  search_term_encoded = CGI.escape(search_term)
85
- BASE_SEARCH_URL % [search_type_code, search_term_encoded]
71
+ format(BASE_SEARCH_URL, search_type_code, search_term_encoded)
86
72
  end
87
73
  end
88
74
 
@@ -95,20 +81,17 @@ module Alexandria
95
81
  end
96
82
 
97
83
  def parse_search_result_data(html)
98
- # adlibris site presents data in ISO-8859-1, so change it to UTF-8
99
- # html = Iconv.conv("UTF-8", "ISO-8859-1", html)
100
- # doc = Hpricot(html)
101
84
  doc = html_to_doc(html)
102
85
  book_search_results = []
103
86
 
104
87
  search_hit = doc.search("div'searchResult")[0]
105
88
  return [] unless search_hit
106
89
 
107
- (search_hit / 'ul.ulSearch table').each do |t|
90
+ (search_hit / "ul.ulSearch table").each do |t|
108
91
  result = {}
109
- if (title_data = (t % 'div.divTitle'))
92
+ if (title_data = (t % "div.divTitle"))
110
93
  result[:title] = (title_data % :a).inner_text
111
- lookup_url = (title_data % :a)['href']
94
+ lookup_url = (title_data % :a)["href"]
112
95
  end
113
96
  result[:lookup_url] = "#{SITE}#{lookup_url}"
114
97
 
@@ -117,46 +100,25 @@ module Alexandria
117
100
  book_search_results
118
101
  end
119
102
 
120
- # def binding_type(binding) # swedish string
121
- # # hrm, this is a HACK and not currently working
122
- # # perhaps use regexes instead...
123
- # {"inbunden" => :hardback,
124
- # "pocket" => :paperback,
125
- # "storpocket" => :paperback,
126
- # "kartonnage" => :hardback,
127
- # "kassettbok" => :audiobook}[binding.downcase] or :paperback
128
- # # H&#228;ftad == Paperback
129
- # end
130
-
131
103
  def normalize(text)
132
- # unless text.nil?
133
- # text = @ent.decode(text).strip
134
- # end
135
104
  text
136
105
  end
137
106
 
138
107
  def parse_result_data(html)
139
- # adlibris site presents data in ISO-8859-1, so change it to UTF-8
140
- # html = Iconv.conv("UTF-8", "ISO-8859-1", html)
141
- ## File.open(',log.html', 'wb') {|f| f.write('<?xml encoding="utf-8"?>'); f.write(html) } # DEBUG
142
- # doc = Hpricot(html)
143
108
  doc = html_to_doc(html)
144
109
  begin
110
+ h1 = doc.at("div.productTitleFormat h1")
111
+ raise NoResultsError, _("title not found on page") unless h1
145
112
 
146
- title = nil
147
- if (h1 = doc.at('div.productTitleFormat h1'))
148
- title = text_of(h1)
149
- else
150
- raise NoResultsError, 'title not found on page'
151
- end
113
+ title = text_of(h1)
152
114
 
153
- product = doc.at('div.product')
154
- ul_info = doc.at('ul.info') # NOTE, two of these
115
+ product = doc.at("div.product")
116
+ ul_info = doc.at("ul.info") # NOTE, two of these
155
117
 
156
- author_cells = ul_info.search('li.liAuthor') # css-like search
118
+ author_cells = ul_info.search("li.liAuthor") # css-like search
157
119
  authors = []
158
120
  author_cells.each do |li|
159
- author_name = text_of(li.search('h2 > a')[0])
121
+ author_name = text_of(li.search("h2 > a")[0])
160
122
 
161
123
  authors << author_name
162
124
  end
@@ -167,19 +129,15 @@ module Alexandria
167
129
  end
168
130
 
169
131
  binding = nil
170
- if (format = doc.search('div.productTitleFormat span').first)
132
+ if (format = doc.search("div.productTitleFormat span").first)
171
133
  binding = text_of(format)
172
- if binding =~ /\(([^\)]+)\)/
173
- binding = Regexp.last_match[1]
174
- end
134
+ binding = Regexp.last_match[1] if binding =~ /\(([^)]+)\)/
175
135
  end
176
136
 
177
137
  year = nil
178
138
  if (published = product.search('span[@id$="Published"]').first)
179
139
  publication = published.inner_text
180
- if publication =~ /([12][0-9]{3})/
181
- year = Regexp.last_match[1].to_i
182
- end
140
+ year = Regexp.last_match[1].to_i if publication =~ /([12][0-9]{3})/
183
141
  end
184
142
 
185
143
  isbns = []
@@ -188,10 +146,9 @@ module Alexandria
188
146
  isbn_tds.each do |isbn_td|
189
147
  isbn = isbn_td.inner_text
190
148
  next unless isbn =~ /[0-9x]{10,13}/i
191
- isbn.gsub(/(\n|\r)/, ' ')
192
- if isbn =~ /:[\s]*([0-9x]+)/i
193
- isbn = Regexp.last_match[1]
194
- end
149
+
150
+ isbn.gsub(/(\n|\r)/, " ")
151
+ isbn = Regexp.last_match[1] if isbn =~ /:\s*([0-9x]+)/i
195
152
  isbns << isbn
196
153
  end
197
154
  isbn = isbns.first
@@ -199,13 +156,15 @@ module Alexandria
199
156
 
200
157
  # cover
201
158
  image_url = nil
202
- if (cover_img = doc.search('span.imageWithShadow img[@id$="ProductImageNotLinked"]').first)
203
- image_url = if cover_img['src'] =~ /^http\:\/\//
204
- cover_img['src']
159
+ cover_img =
160
+ doc.search('span.imageWithShadow img[@id$="ProductImageNotLinked"]').first
161
+ if cover_img
162
+ image_url = if cover_img["src"].start_with?("http://")
163
+ cover_img["src"]
205
164
  else
206
165
  "#{SITE}/#{cover_img['src']}" # HACK: use html base
207
166
  end
208
- if image_url =~ /noimage.gif$/
167
+ if /noimage.gif$/.match?(image_url)
209
168
  # no point downloading a "no image" graphic
210
169
  # Alexandria has its own generic book icon...
211
170
  image_url = nil
@@ -215,14 +174,15 @@ module Alexandria
215
174
 
216
175
  book = Book.new(title, authors, isbn, publisher, year, binding)
217
176
 
218
- return [book, image_url]
219
- rescue => ex
177
+ [book, image_url]
178
+ rescue StandardError => ex
220
179
  raise ex if ex.instance_of? NoResultsError
180
+
221
181
  trace = ex.backtrace.join("\n> ")
222
- log.warn {
223
- 'Failed parsing search results for AdLibris ' \
182
+ log.warn do
183
+ "Failed parsing search results for AdLibris " \
224
184
  "#{ex.message} #{trace}"
225
- }
185
+ end
226
186
  raise NoResultsError
227
187
  end
228
188
  end
@@ -1,101 +1,81 @@
1
- # Copyright (C) 2004-2006 Laurent Sansonetti
2
- # Copyright (C) 2008 Cathal Mc Ginley
3
- # Copyright (C) 2014, 2016 Matijs van Zuijlen
4
- #
5
- # Alexandria is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU General Public License as
7
- # published by the Free Software Foundation; either version 2 of the
8
- # License, or (at your option) any later version.
9
- #
10
- # Alexandria is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
- # General Public License for more details.
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Alexandria.
14
4
  #
15
- # You should have received a copy of the GNU General Public
16
- # License along with Alexandria; see the file COPYING. If not,
17
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
18
- # Fifth Floor, Boston, MA 02110-1301 USA.
5
+ # See the file README.md for authorship and licensing information.
19
6
 
20
7
  # http://en.wikipedia.org/wiki/Amazon
21
8
 
22
- require 'hpricot'
23
- require 'alexandria/book_providers/amazon_ecs_util'
9
+ require "hpricot"
10
+ require "alexandria/book_providers/amazon_ecs_util"
24
11
 
25
12
  module Alexandria
26
13
  class BookProviders
27
14
  class AmazonProvider < GenericProvider
28
15
  include Logging
29
16
  include GetText
30
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
17
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
31
18
 
32
19
  # CACHE_DIR = File.join(Alexandria::Library::DIR, '.amazon_cache')
33
20
 
34
- LOCALES = ['ca', 'de', 'fr', 'jp', 'uk', 'us'].freeze
21
+ LOCALES = ["ca", "de", "fr", "jp", "uk", "us"].freeze
35
22
 
36
23
  def initialize
37
- super('Amazon', 'Amazon')
24
+ super("Amazon", "Amazon")
38
25
  # prefs.add("enabled", _("Enabled"), true, [true,false])
39
- prefs.add('locale', _('Locale'), 'us', AmazonProvider::LOCALES)
40
- prefs.add('dev_token', _('Access key ID'), '')
41
- prefs.add('secret_key', _('Secret access key'), '')
42
- prefs.add('associate_tag', _('Associate Tag'), '')
26
+ prefs.add("locale", _("Locale"), "us", AmazonProvider::LOCALES)
27
+ prefs.add("dev_token", _("Access key ID"), "")
28
+ prefs.add("secret_key", _("Secret access key"), "")
29
+ prefs.add("associate_tag", _("Associate Tag"), "")
43
30
 
44
31
  prefs.read
45
- token = prefs.variable_named('dev_token')
32
+ token = prefs.variable_named("dev_token")
46
33
  # kill old (shorter) tokens, or previously distributed Access Key Id (see #26250)
47
34
 
48
35
  if token
49
- if token.value != token.value.strip
50
- token.new_value = token.value.strip
36
+ token.new_value = token.value.strip if token.value != token.value.strip
37
+ if (token.value.size != 20) || (token.value == "0J356Z09CN88KB743582")
38
+ token.new_value = ""
51
39
  end
52
40
  end
53
- if token && ((token.value.size != 20) || (token.value == '0J356Z09CN88KB743582'))
54
- token.new_value = ''
55
- end
56
41
 
57
- secret = prefs.variable_named('secret_key')
58
- if secret
59
- if secret.value != secret.value.strip
60
- secret.new_value = secret.value.strip
61
- end
42
+ secret = prefs.variable_named("secret_key")
43
+ if secret && (secret.value != secret.value.strip)
44
+ secret.new_value = secret.value.strip
62
45
  end
63
46
 
64
- associate = prefs.variable_named('associate_tag')
65
- if associate
66
- if associate.value.strip.empty?
67
- associate.new_value = 'rubyalexa-20'
68
- end
69
- if associate.value != associate.value.strip
70
- associate.new_value = associate.value.strip
71
- end
47
+ associate = prefs.variable_named("associate_tag") or return
48
+
49
+ associate.new_value = "rubyalexa-20" if associate.value.strip.empty?
50
+ if associate.value != associate.value.strip
51
+ associate.new_value = associate.value.strip
72
52
  end
73
53
  end
74
54
 
75
55
  def search(criterion, type)
76
56
  prefs.read
77
57
 
78
- if prefs['secret_key'].empty?
79
- raise Amazon::RequestError, 'Secret Access Key required for Authentication: you must sign up for your own Amazon AWS account'
58
+ if prefs["secret_key"].empty?
59
+ raise(Amazon::RequestError, _("Provide secret key for your Amazon AWS account."))
80
60
  end
81
61
 
82
62
  if (config = Alexandria::Preferences.instance.http_proxy_config)
83
63
  host, port, user, pass = config
84
- url = 'http://'
85
- url += user + ':' + pass + '@' if user && pass
86
- url += host + ':' + port.to_s
87
- ENV['http_proxy'] = url
64
+ url = "http://"
65
+ url += user + ":" + pass + "@" if user && pass
66
+ url += host + ":" + port.to_s
67
+ ENV["http_proxy"] = url
88
68
  end
89
69
 
90
- access_key_id = prefs['dev_token']
70
+ access_key_id = prefs["dev_token"]
91
71
 
92
72
  Amazon::Ecs.options = { aWS_access_key_id: access_key_id,
93
- associateTag: prefs['associate_tag'] }
94
- Amazon::Ecs.secret_access_key = prefs['secret_key']
73
+ associateTag: prefs["associate_tag"] }
74
+ Amazon::Ecs.secret_access_key = prefs["secret_key"]
95
75
  # #req.cache = Amazon::Search::Cache.new(CACHE_DIR)
96
76
  locales = AmazonProvider::LOCALES.dup
97
- locales.delete prefs['locale']
98
- locales.unshift prefs['locale']
77
+ locales.delete prefs["locale"]
78
+ locales.unshift prefs["locale"]
99
79
  locales.reverse!
100
80
 
101
81
  begin
@@ -105,7 +85,10 @@ module Alexandria
105
85
  when SEARCH_BY_ISBN
106
86
  criterion = Library.canonicalise_isbn(criterion)
107
87
  # This isn't ideal : I'd like to do an ISBN/EAN-specific search
108
- res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
88
+ res = Amazon::Ecs.item_search(criterion,
89
+ response_group: "ItemAttributes,Images",
90
+ country: request_locale)
91
+
109
92
  res.items.each do |item|
110
93
  products << item
111
94
  end
@@ -125,29 +108,36 @@ module Alexandria
125
108
  # result with different ISBNs
126
109
 
127
110
  if products.length > 1
128
- log.warn { "ISBN search at Amazon[#{request_locale}] got #{products.length} results; returning the first result only" }
111
+ log.warn do
112
+ "ISBN search at Amazon[#{request_locale}] got #{products.length} results;" \
113
+ " returning the first result only"
114
+ end
129
115
  end
130
116
 
131
117
  when SEARCH_BY_TITLE
132
- res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
118
+ res = Amazon::Ecs.item_search(criterion,
119
+ response_group: "ItemAttributes,Images",
120
+ country: request_locale)
133
121
 
134
122
  res.items.each do |item|
135
- if item.get('itemattributes/title') =~ /#{criterion}/i
136
- products << item
137
- end
123
+ products << item if /#{criterion}/i.match?(item.get("itemattributes/title"))
138
124
  end
139
125
  # #req.keyword_search(criterion) do |product|
140
126
 
141
127
  when SEARCH_BY_AUTHORS
142
128
  criterion = "author:#{criterion}"
143
- res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale, type: 'Power')
129
+ res = Amazon::Ecs.item_search(criterion,
130
+ response_group: "ItemAttributes,Images",
131
+ country: request_locale, type: "Power")
144
132
  res.items.each do |item|
145
133
  products << item
146
134
  end
147
135
  # #req.author_search(criterion) do |product|
148
136
 
149
137
  when SEARCH_BY_KEYWORD
150
- res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
138
+ res = Amazon::Ecs.item_search(criterion,
139
+ response_group: "ItemAttributes,Images",
140
+ country: request_locale)
151
141
 
152
142
  res.items.each do |item|
153
143
  products << item
@@ -156,92 +146,94 @@ module Alexandria
156
146
  else
157
147
  raise InvalidSearchTypeError
158
148
  end
159
- if products.empty?
160
- raise Amazon::RequestError, 'No products'
161
- end
149
+ raise Amazon::RequestError, _("No products") if products.empty?
162
150
  # raise NoResultsError if products.empty?
163
- rescue Amazon::RequestError => re
164
- log.debug { "Got Amazon::RequestError at #{request_locale}: #{re}" }
151
+ rescue Amazon::RequestError => ex
152
+ log.debug { "Got Amazon::RequestError at #{request_locale}: #{ex}" }
165
153
  retry unless locales.empty?
166
154
  raise NoResultsError
167
155
  end
168
156
 
169
157
  results = []
170
158
  products.each do |item|
171
- next unless item.get('itemattributes/productgroup') == 'Book'
172
- atts = item.search_and_convert('itemattributes')
173
- title = normalize(atts.get('title'))
159
+ next unless item.get("itemattributes/productgroup") == "Book"
160
+
161
+ atts = item.search_and_convert("itemattributes")
162
+ title = normalize(atts.get("title"))
174
163
 
175
- media = normalize(atts.get('binding'))
176
- media = nil if media == 'Unknown Binding'
164
+ media = normalize(atts.get("binding"))
165
+ media = nil if media == "Unknown Binding"
177
166
 
178
- isbn = normalize(atts.get('isbn'))
179
- isbn = if isbn && Library.valid_isbn?(isbn)
180
- Library.canonicalise_ean(isbn)
181
- end
167
+ isbn = normalize(atts.get("isbn"))
168
+ isbn = (Library.canonicalise_ean(isbn) if isbn && Library.valid_isbn?(isbn))
182
169
  # hack, extract year by regexp (not Y10K compatible :-)
183
- /([1-9][0-9]{3})/ =~ atts.get('publicationdate')
170
+ /([1-9][0-9]{3})/ =~ atts.get("publicationdate")
184
171
  publishing_year = Regexp.last_match[1] ? Regexp.last_match[1].to_i : nil
185
172
  book = Book.new(title,
186
- atts.get_array('author').map { |x| normalize(x) },
173
+ atts.get_array("author").map { |x| normalize(x) },
187
174
  isbn,
188
- normalize(atts.get('manufacturer')),
175
+ normalize(atts.get("manufacturer")),
189
176
  publishing_year,
190
177
  media)
191
178
 
192
- image_url = item.get('mediumimage/url')
179
+ image_url = item.get("mediumimage/url")
193
180
  log.info { "Found at Amazon[#{request_locale}]: #{book.title}" }
194
181
  results << [book, image_url]
195
182
  end
196
183
  if type == SEARCH_BY_ISBN
197
184
  if results.size == 1
198
- return results.first
185
+ results.first
199
186
  else
200
- log.info { 'Found multiple results for lookup: checking each' }
201
- query_isbn_canon = Library.canonicalise_ean(criterion)
202
- results.each do |rslt|
203
- book = rslt[0]
204
- book_isbn_canon = Library.canonicalise_ean(book.isbn)
205
- if query_isbn_canon == book_isbn_canon
206
- return rslt
207
- end
208
- log.debug { "rejected possible result #{book}" }
209
- end
210
- # gone through all and no ISBN match, so just return first result
211
- log.info { 'no more results to check. Returning first result, just an approximation' }
212
- return results.first
187
+ exact_match_or_first(criterion, results)
213
188
  end
214
189
  else
215
- return results
190
+ results
216
191
  end
217
192
  end
218
193
 
194
+ LOCALE_URLS = {
195
+ "fr" => "http://www.amazon.fr/exec/obidos/ASIN/%s",
196
+ "uk" => "http://www.amazon.co.uk/exec/obidos/ASIN/%s",
197
+ "de" => "http://www.amazon.de/exec/obidos/ASIN/%s",
198
+ "ca" => "http://www.amazon.ca/exec/obidos/ASIN/%s",
199
+ "jp" => "http://www.amazon.jp/exec/obidos/ASIN/%s",
200
+ "us" => "http://www.amazon.com/exec/obidos/ASIN/%s"
201
+ }.freeze
202
+
219
203
  def url(book)
220
204
  isbn = Library.canonicalise_isbn(book.isbn)
221
- url = case prefs['locale']
222
- when 'fr'
223
- 'http://www.amazon.fr/exec/obidos/ASIN/%s'
224
- when 'uk'
225
- 'http://www.amazon.co.uk/exec/obidos/ASIN/%s'
226
- when 'de'
227
- 'http://www.amazon.de/exec/obidos/ASIN/%s'
228
- when 'ca'
229
- 'http://www.amazon.ca/exec/obidos/ASIN/%s'
230
- when 'jp'
231
- 'http://www.amazon.jp/exec/obidos/ASIN/%s'
232
- when 'us'
233
- 'http://www.amazon.com/exec/obidos/ASIN/%s'
234
- end
205
+ url = LOCALE_URLS.fetch(prefs["locale"])
235
206
  url % isbn
236
- rescue => ex
207
+ rescue StandardError => ex
237
208
  log.warn { "Cannot create url for book #{book}; #{ex.message}" }
238
209
  nil
239
210
  end
240
211
 
241
212
  def normalize(str)
242
- str = str.squeeze(' ').strip unless str.nil?
213
+ str = str.squeeze(" ").strip unless str.nil?
243
214
  str
244
215
  end
216
+
217
+ private
218
+
219
+ def exact_match_or_first(criterion, results)
220
+ log.info { "Found multiple results for lookup: checking for exact isbn match" }
221
+ query_isbn_canon = Library.canonicalise_ean(criterion)
222
+ exact_match = results.find do |book, _|
223
+ book_isbn_canon = Library.canonicalise_ean(book.isbn)
224
+ query_isbn_canon == book_isbn_canon
225
+ end
226
+
227
+ if exact_match
228
+ # gone through all and no ISBN match, so just return first result
229
+ log.info do
230
+ "no more results to check. Returning first result, just an approximation"
231
+ end
232
+ exact_match
233
+ else
234
+ results.first
235
+ end
236
+ end
245
237
  end
246
238
  end
247
239
  end