alexandria-book-collection-manager 0.7.8 → 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +45 -50
  3. data/.rubocop.yml +18 -5
  4. data/.rubocop_todo.yml +29 -22
  5. data/CHANGELOG.md +29 -0
  6. data/ChangeLog.0 +19 -19
  7. data/INSTALL.md +3 -5
  8. data/README.md +0 -5
  9. data/Rakefile +11 -11
  10. data/alexandria-book-collection-manager.gemspec +35 -34
  11. data/doc/FAQ +2 -2
  12. data/lib/alexandria/about.rb +1 -1
  13. data/lib/alexandria/book_providers/bl_provider.rb +88 -0
  14. data/lib/alexandria/book_providers/loc_provider.rb +38 -0
  15. data/lib/alexandria/book_providers/pseudomarc.rb +1 -1
  16. data/lib/alexandria/book_providers/sbn_provider.rb +108 -0
  17. data/lib/alexandria/book_providers/thalia_provider.rb +1 -1
  18. data/lib/alexandria/book_providers/web.rb +2 -2
  19. data/lib/alexandria/book_providers/worldcat.rb +9 -7
  20. data/lib/alexandria/book_providers/z3950_provider.rb +199 -0
  21. data/lib/alexandria/book_providers.rb +10 -25
  22. data/lib/alexandria/default_preferences.rb +1 -1
  23. data/lib/alexandria/export_library.rb +10 -10
  24. data/lib/alexandria/image_fetcher.rb +25 -0
  25. data/lib/alexandria/import_library.rb +9 -9
  26. data/lib/alexandria/library_store.rb +3 -4
  27. data/lib/alexandria/models/book.rb +13 -0
  28. data/lib/alexandria/models/library.rb +13 -21
  29. data/lib/alexandria/preferences.rb +4 -6
  30. data/lib/alexandria/scanners/cue_cat.rb +1 -1
  31. data/lib/alexandria/ui/about_dialog.rb +1 -1
  32. data/lib/alexandria/ui/acquire_dialog.rb +6 -9
  33. data/lib/alexandria/ui/barcode_animation.rb +1 -1
  34. data/lib/alexandria/ui/book_properties_dialog_base.rb +2 -6
  35. data/lib/alexandria/ui/completion_models.rb +1 -5
  36. data/lib/alexandria/ui/conflict_while_copying_dialog.rb +1 -1
  37. data/lib/alexandria/ui/listview.rb +1 -1
  38. data/lib/alexandria/ui/multi_drag_treeview.rb +1 -1
  39. data/lib/alexandria/ui/new_book_dialog.rb +11 -13
  40. data/lib/alexandria/ui/new_book_dialog_manual.rb +1 -1
  41. data/lib/alexandria/ui/preferences_dialog.rb +2 -2
  42. data/lib/alexandria/ui/provider_preferences_base_dialog.rb +1 -1
  43. data/lib/alexandria/ui/really_delete_dialog.rb +1 -1
  44. data/lib/alexandria/ui/ui_manager.rb +14 -22
  45. data/lib/alexandria/version.rb +1 -1
  46. data/po/cs.po +90 -125
  47. data/po/cy.po +87 -125
  48. data/po/de.po +96 -125
  49. data/po/el.po +96 -125
  50. data/po/es.po +96 -125
  51. data/po/fr.po +90 -125
  52. data/po/ga.po +83 -124
  53. data/po/gl.po +90 -125
  54. data/po/it.po +90 -125
  55. data/po/ja.po +90 -125
  56. data/po/mk.po +96 -125
  57. data/po/nb.po +90 -125
  58. data/po/nl.po +107 -124
  59. data/po/pl.po +113 -124
  60. data/po/pt.po +90 -125
  61. data/po/pt_BR.po +90 -125
  62. data/po/ru.po +92 -124
  63. data/po/sk.po +90 -125
  64. data/po/sv.po +90 -125
  65. data/po/uk.po +90 -125
  66. data/po/zh_TW.po +90 -125
  67. data/schemas/alexandria.schemas +1 -1
  68. data/share/gnome/help/alexandria/C/adding-books.xml +3 -4
  69. data/share/gnome/help/alexandria/C/introduction.xml +0 -16
  70. data/share/gnome/help/alexandria/C/searching.xml +1 -4
  71. data/share/gnome/help/alexandria/C/settings.xml +0 -30
  72. data/share/gnome/help/alexandria/fr/alexandria.xml +4 -159
  73. data/share/gnome/help/alexandria/ja/adding-books.xml +1 -1
  74. data/share/gnome/help/alexandria/ja/introduction.xml +0 -15
  75. data/share/gnome/help/alexandria/ja/searching.xml +3 -7
  76. data/share/gnome/help/alexandria/ja/settings.xml +0 -27
  77. data/spec/alexandria/book_providers/bl_provider_spec.rb +13 -0
  78. data/spec/alexandria/book_providers/loc_provider_spec.rb +17 -0
  79. data/spec/alexandria/book_providers/sbn_provider_spec.rb +13 -0
  80. data/spec/alexandria/book_providers_spec.rb +0 -81
  81. data/spec/alexandria/library_spec.rb +20 -2
  82. data/spec/alexandria/ui/import_dialog_spec.rb +1 -1
  83. data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +1 -1
  84. data/spec/alexandria/ui/preferences_dialog_spec.rb +1 -1
  85. data/spec/alexandria/ui/ui_manager_spec.rb +78 -2
  86. data/spec/data/libraries/0.6.2/My Library/9780571147168.yaml +2 -0
  87. data/util/rake/fileinstall.rb +4 -4
  88. data/util/rake/omfgenerate.rb +1 -1
  89. metadata +69 -55
  90. data/lib/alexandria/book_providers/adlibris.rb +0 -191
  91. data/lib/alexandria/book_providers/amazon_aws.rb +0 -239
  92. data/lib/alexandria/book_providers/amazon_ecs_util.rb +0 -373
  93. data/lib/alexandria/book_providers/barnes_and_noble.rb +0 -209
  94. data/lib/alexandria/book_providers/proxis.rb +0 -176
  95. data/lib/alexandria/book_providers/siciliano.rb +0 -256
  96. data/lib/alexandria/book_providers/z3950.rb +0 -408
@@ -1,408 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This file is part of Alexandria.
4
- #
5
- # See the file README.md for authorship and licensing information.
6
-
7
- ## $Z3950_DEBUG = $DEBUG
8
-
9
- require "zoom"
10
- require "alexandria/book_providers/pseudomarc"
11
- require "marc"
12
-
13
- module Alexandria
14
- class BookProviders
15
- class Z3950Provider < AbstractProvider
16
- include Logging
17
- include GetText
18
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
19
-
20
- def initialize(name = "Z3950", fullname = "Z39.50")
21
- super
22
- prefs.add("hostname", _("Hostname"), "")
23
- prefs.add("port", _("Port"), 7090)
24
- prefs.add("database", _("Database"), "")
25
- prefs.add("record_syntax", _("Record syntax"), "USMARC",
26
- ["USMARC", "UNIMARC", "SUTRS"])
27
- prefs.add("username", _("Username"), "", nil, false)
28
- prefs.add("password", _("Password"), "", nil, false)
29
- prefs.add("charset", _("Charset encoding"), "ISO-8859-1")
30
-
31
- # HACK : piggybacking support
32
- prefs.add("piggyback", "Piggyback", true, [true, false])
33
- prefs.read
34
- end
35
-
36
- def search(criterion, type)
37
- prefs.read
38
- criterion = criterion.encode(prefs["charset"])
39
-
40
- isbn = type == SEARCH_BY_ISBN ? criterion : nil
41
- criterion = canonicalise_criterion(criterion, type)
42
- conn_count = request_count(type)
43
- resultset = search_records(criterion, type, conn_count)
44
- log.debug { "total #{resultset.length}" }
45
-
46
- results = books_from_resultset(resultset, isbn)
47
- raise NoResultsError if results.empty?
48
-
49
- type == SEARCH_BY_ISBN ? results.first : results
50
- end
51
-
52
- def url(_book)
53
- nil
54
- end
55
-
56
- def books_from_resultset(resultset, isbn)
57
- case prefs["record_syntax"]
58
- when /MARC$/
59
- books_from_marc(resultset, isbn)
60
- when "SUTRS"
61
- books_from_sutrs(resultset)
62
- else
63
- raise NoResultsError
64
- end
65
- end
66
-
67
- private
68
-
69
- def request_count(type)
70
- type == SEARCH_BY_ISBN ? 1 : 10 # results to retrieve
71
- end
72
-
73
- def canonicalise_criterion(criterion, type)
74
- criterion = Library.canonicalise_isbn(criterion) if type == SEARCH_BY_ISBN
75
- criterion
76
- end
77
-
78
- def books_from_sutrs(_resultset)
79
- # SUTRS needs to be decoded separately, because each Z39.50 server has a
80
- # different one.
81
- raise NoResultsError
82
- end
83
-
84
- def marc_to_book(marc_txt, isbn)
85
- begin
86
- marc = MARC::Record.new_from_marc(marc_txt, forgiving: true)
87
- rescue StandardError => ex
88
- log.error { ex.message }
89
- log.error { ex.backtrace.join("> \n") }
90
- begin
91
- marc = MARC::Record.new(marc_txt)
92
- rescue StandardError => ex
93
- log.error { ex.message }
94
- log.error { ex.backtrace.join("> \n") }
95
- raise ex
96
- end
97
- end
98
-
99
- log.debug do
100
- msg = "Parsing MARC"
101
- msg += "\n title: #{marc.title}"
102
- msg += "\n authors: #{marc.authors.join(', ')}"
103
- msg += "\n isbn: #{marc.isbn}, #{isbn}"
104
- msg += "\n publisher: #{marc.publisher}"
105
- msg += "\n publish year: #{marc.publish_year}" if marc.respond_to?(:publish_year)
106
- msg += "\n edition: #{marc.edition}"
107
- msg
108
- end
109
-
110
- return if marc.title.nil? # or marc.authors.empty?
111
-
112
- isbn ||= marc.isbn
113
- isbn = Library.canonicalise_ean(isbn)
114
-
115
- Book.new(marc.title, marc.authors,
116
- isbn,
117
- (marc.publisher || ""),
118
- marc.respond_to?(:publish_year) ? marc.publish_year.to_i : nil,
119
- (marc.edition || ""))
120
- end
121
-
122
- def books_from_marc(resultset, isbn)
123
- results = []
124
- resultset[0..9].each do |record|
125
- marc_txt = record.render(prefs["charset"], "UTF-8")
126
- log.debug { marc_txt }
127
- if $DEBUG
128
- File.open(",marc.txt", "wb") do |f| # DEBUG
129
- f.write(marc_txt)
130
- end
131
- end
132
- book = nil
133
- begin
134
- mappings = Alexandria::PseudoMarcParser::USMARC_MAPPINGS
135
- if prefs["hostname"] == "z3950.bnf.fr"
136
- mappings = Alexandria::PseudoMarcParser::BNF_FR_MAPPINGS
137
- end
138
- # try pseudo-marc parser first (it seems to have more luck)
139
- book = Alexandria::PseudoMarcParser.marc_text_to_book(marc_txt,
140
- mappings)
141
- if book.nil?
142
- # failing that, try the genuine MARC parser
143
- book = marc_to_book(marc_txt, isbn)
144
- end
145
- rescue StandardError => ex
146
- log.warn { ex }
147
- log.warn { ex.backtrace }
148
- end
149
-
150
- results << [book] unless book.nil?
151
- end
152
- results
153
- end
154
-
155
- def marc?
156
- prefs["record_syntax"].end_with?("MARC")
157
- end
158
-
159
- def search_records(criterion, type, conn_count)
160
- options = {}
161
- unless prefs["username"].empty? || prefs["password"].empty?
162
- options["user"] = prefs["username"]
163
- options["password"] = prefs["password"]
164
- end
165
- hostname = prefs["hostname"]
166
- port = prefs["port"].to_i
167
- log.debug { "hostname #{hostname} port #{port} options #{options}" }
168
- conn = ZOOM::Connection.new(options).connect(hostname, port)
169
- conn.database_name = prefs["database"]
170
-
171
- conn.preferred_record_syntax = prefs["record_syntax"]
172
- conn.element_set_name = "F"
173
- conn.count = conn_count
174
- attr = case type
175
- when SEARCH_BY_ISBN then [7]
176
- when SEARCH_BY_TITLE then [4]
177
- when SEARCH_BY_AUTHORS then [1, 1003]
178
- when SEARCH_BY_KEYWORD then [1016]
179
- end
180
- pqf = ""
181
- attr.each { |att| pqf += "@attr 1=#{att} " }
182
- pqf += '"' + criterion.upcase + '"'
183
- log.debug { "pqf is #{pqf}, syntax #{prefs['record_syntax']}" }
184
-
185
- begin
186
- if prefs.variable_named("piggyback") && !prefs["piggyback"]
187
- log.debug { "setting conn.piggyback to false" }
188
- conn.piggyback = false
189
- end
190
- conn.search(pqf)
191
- rescue StandardError => ex
192
- if /1005/.match?(ex.message) &&
193
- prefs.variable_named("piggyback") && prefs["piggyback"]
194
- log.error { "Z39.50 search failed:: #{ex.message}" }
195
- log.info { "Turning off piggybacking for this provider" }
196
- prefs.variable_named("piggyback").new_value = false
197
- retry
198
- end
199
-
200
- raise ex
201
- end
202
- end
203
- end
204
-
205
- class LOCProvider < Z3950Provider
206
- # http://en.wikipedia.org/wiki/Library_of_Congress
207
- unabstract
208
-
209
- include GetText
210
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
211
-
212
- def initialize
213
- super("LOC", _("Library of Congress (Usa)"))
214
- prefs.variable_named("hostname").default_value = "z3950.loc.gov"
215
- prefs.variable_named("port").default_value = 7090
216
- prefs.variable_named("database").default_value = "Voyager"
217
- prefs.variable_named("record_syntax").default_value = "USMARC"
218
- prefs.variable_named("charset").default_value = "ISO-8859-1"
219
- prefs.read
220
- end
221
-
222
- def url(book)
223
- isbn = Library.canonicalise_isbn(book.isbn)
224
- "http://catalog.loc.gov/cgi-bin/Pwebrecon.cgi?" \
225
- "DB=local&CNT=25+records+per+page&CMD=isbn+#{isbn}"
226
- rescue StandardError => ex
227
- log.warn { "Cannot create url for book #{book}; #{ex.message}" }
228
- nil
229
- end
230
- end
231
-
232
- class BLProvider < Z3950Provider
233
- # http://en.wikipedia.org/wiki/Copac
234
- # http://en.wikipedia.org/wiki/British_Library
235
- # http://www.bl.uk/catalogues/z3950fullaccess.html
236
- # http://www.bl.uk/catalogues/z3950copacaccess.html
237
- #
238
- # FIXME: switch from BL to Copac, which incudes the BL itself and many more
239
- # libraries: http://copac.ac.uk/libraries/
240
- #
241
- # Details: http://copac.ac.uk/interfaces/z39.50/
242
- # The SUTRS format used by Copac is different from the one used by BL
243
- unabstract
244
-
245
- include GetText
246
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
247
-
248
- def initialize
249
- super("BL", _("British Library"))
250
- prefs.variable_named("hostname").default_value = "z3950cat.bl.uk"
251
- prefs.variable_named("port").default_value = 9909
252
- prefs.variable_named("database").default_value = "BLAC"
253
- prefs.variable_named("record_syntax").default_value = "SUTRS"
254
- prefs.variable_named("charset").default_value = "ISO-8859-1"
255
- prefs.read
256
- end
257
-
258
- def url(book)
259
- "http://copac.ac.uk/openurl?isbn=" + Library.canonicalise_isbn(book.isbn)
260
- rescue StandardError => ex
261
- log.warn { "Cannot create url for book #{book}; #{ex.message}" }
262
- nil
263
- end
264
-
265
- private
266
-
267
- def books_from_sutrs(resultset)
268
- results = []
269
- resultset[0..9].each do |record|
270
- text = record.render(prefs["charset"], "UTF-8")
271
- # File.open(',bl.marc', 'wb') {|f| f.write(text) }
272
- log.debug { text }
273
-
274
- title = isbn = publisher = publish_year = edition = nil
275
- authors = []
276
-
277
- text.split(/\n/).each do |line|
278
- if (md = /^Title:\s+(.*)$/.match(line))
279
- title = md[1].sub(/\.$/, "").squeeze(" ")
280
- elsif (md = /^Added Person Name:\s+(.*),[^,]+$/.match(line))
281
- authors << md[1]
282
- elsif (md = /^ME-Personal Name:\s+(.*),[^,]+$/.match(line))
283
- authors << md[1]
284
- elsif (md = /^ISBN:\s+([\dXx]+)/.match(line))
285
- isbn = Library.canonicalise_ean(md[1])
286
- elsif (md = /^Imprint:.+:\s*(.+),/.match(line))
287
- publisher = md[1]
288
- end
289
- end
290
-
291
- log.debug do
292
- msg = "Parsing SUTRS"
293
- msg += "\n title: #{title}"
294
- msg += "\n authors: #{authors.join(' and ')}"
295
- msg += "\n isbn: #{isbn}"
296
- msg += "\n publisher: #{publisher}"
297
- msg += "\n edition: #{edition}"
298
- msg
299
- end
300
-
301
- if title # and !authors.empty?
302
- book = Book.new(title, authors, isbn, (publisher || nil),
303
- (publish_year || nil), (edition || nil))
304
- results << [book]
305
- end
306
- end
307
- results
308
- end
309
- end
310
-
311
- class SBNProvider < Z3950Provider
312
- # http://sbnonline.sbn.it/
313
- # http://it.wikipedia.org/wiki/ICCU
314
- unabstract
315
-
316
- include GetText
317
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
318
-
319
- def initialize
320
- super("SBN", "Servizio Bibliotecario Nazionale (Italy)")
321
- prefs.variable_named("hostname").default_value = "opac.sbn.it"
322
- prefs.variable_named("port").default_value = 3950
323
- prefs.variable_named("database").default_value = "nopac"
324
- # supported 'USMARC', 'UNIMARC' , 'SUTRS'
325
- prefs.variable_named("record_syntax").default_value = "USMARC"
326
- prefs.variable_named("charset").default_value = "ISO-8859-1"
327
- prefs.read
328
- end
329
-
330
- def url(book)
331
- "http://sbnonline.sbn.it/cgi-bin/zgw/BRIEF.pl?displayquery=" \
332
- "%253CB%253E%253Cfont%2520color%253D%2523000064%253E" \
333
- "Codice%2520ISBN%253C%2FB%253E%253C%2Ffont%253E%2520" \
334
- "contiene%2520%2522%2520%253CFONT%2520COLOR%253Dred%253E" +
335
- canonicalise_isbn_with_dashes(book.isbn) +
336
- "%253C%2FFONT%253E%2522&session=&zurl=opac" \
337
- "&zquery=%281%3D7+4%3D2+2%3D3+5%3D100+6%3D1+3%3D3+%22" +
338
- canonicalise_isbn_with_dashes(book.isbn) +
339
- "%22%29&language=it&maxentries=10&target=0&position=1"
340
- rescue StandardError => ex
341
- log.warn { "Cannot create url for book #{book}; #{ex.message}" }
342
- nil
343
- end
344
-
345
- private
346
-
347
- def canonicalise_criterion(criterion, _type)
348
- canonicalise_isbn_with_dashes(criterion)
349
- end
350
-
351
- def request_count(_type)
352
- 0
353
- end
354
-
355
- def canonicalise_isbn_with_dashes(isbn)
356
- # The reference for the position of the dashes is
357
- # http://www.isbn-international.org/converter/ranges.htm
358
-
359
- isbn = Alexandria::Library.canonicalise_isbn(isbn)
360
-
361
- if isbn[0..1] == "88"
362
- # Italian speaking area
363
- if isbn > "8895000" && (isbn <= "8899999996")
364
- isbn[0..1] + "-" + isbn[2..6] + "-" + isbn[7..8] + "-" + isbn[9..9]
365
- elsif isbn > "88900000"
366
- isbn[0..1] + "-" + isbn[2..7] + "-" + isbn[8..8] + "-" + isbn[9..9]
367
- elsif isbn > "8885000"
368
- isbn[0..1] + "-" + isbn[2..6] + "-" + isbn[7..8] + "-" + isbn[9..9]
369
- elsif isbn > "886000"
370
- isbn[0..1] + "-" + isbn[2..5] + "-" + isbn[6..8] + "-" + isbn[9..9]
371
- elsif isbn > "88200"
372
- isbn[0..1] + "-" + isbn[2..4] + "-" + isbn[5..8] + "-" + isbn[9..9]
373
- elsif isbn > "8800"
374
- isbn[0..1] + "-" + isbn[2..3] + "-" + isbn[4..8] + "-" + isbn[9..9]
375
- else
376
- raise _("Invalid ISBN")
377
- end
378
-
379
- else
380
- isbn
381
- end
382
- end
383
- #
384
- # Remarks about SBN
385
- #
386
- # This provider requires that value of conn.count is 0.
387
- # It's a Yaz option "Number of records to be retrieved".
388
- # This provider requires to specify the value of conn.element_set_name = 'F'.
389
- # It's a Yaz option "Element-Set name of records".
390
- # See http://www.indexdata.dk/yaz/doc/zoom.resultsets.tkl
391
- #
392
- # Dashes:
393
- # this database requires that Italian books are searched with dashes :(
394
- # However, they have also books with dashes in wrong positions, for
395
- # instance 88-061-4934-2
396
- #
397
- # References:
398
- # http://opac.internetculturale.it/cgi-bin/main.cgi?type=field
399
- # http://www.internetculturale.it/
400
- # http://sbnonline.sbn.it/zgw/homeit.html
401
- # http://www.iccu.sbn.it/genera.jsp?id=124
402
- # with link at http://www.iccu.sbn.it/upload/documenti/cartecsbn.pdf
403
- # http://www.loc.gov/cgi-bin/zgstart?ACTION=INIT&FORM_HOST_PORT=/prod/www/data/z3950/iccu.html,opac.sbn.it,2100
404
- # http://gwz.cilea.it/cgi-bin/reportOpac.cgi
405
- #
406
- end
407
- end
408
- end