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
@@ -0,0 +1,88 @@
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
+ require "alexandria/book_providers/z3950_provider"
8
+
9
+ module Alexandria
10
+ class BookProviders
11
+ class BLProvider < Z3950Provider
12
+ # http://en.wikipedia.org/wiki/Copac
13
+ # http://en.wikipedia.org/wiki/British_Library
14
+ # http://www.bl.uk/catalogues/z3950fullaccess.html
15
+ # http://www.bl.uk/catalogues/z3950copacaccess.html
16
+ #
17
+ # FIXME: switch from BL to Copac, which incudes the BL itself and many more
18
+ # libraries: http://copac.ac.uk/libraries/
19
+ #
20
+ # Details: http://copac.ac.uk/interfaces/z39.50/
21
+ # The SUTRS format used by Copac is different from the one used by BL
22
+ unabstract
23
+
24
+ include GetText
25
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
26
+
27
+ def initialize
28
+ super("BL", _("British Library"))
29
+ prefs.variable_named("hostname").default_value = "z3950cat.bl.uk"
30
+ prefs.variable_named("port").default_value = 9909
31
+ prefs.variable_named("database").default_value = "BLAC"
32
+ prefs.variable_named("record_syntax").default_value = "SUTRS"
33
+ prefs.variable_named("charset").default_value = "ISO-8859-1"
34
+ prefs.read
35
+ end
36
+
37
+ def url(book)
38
+ "http://copac.ac.uk/openurl?isbn=" + Library.canonicalise_isbn(book.isbn)
39
+ rescue StandardError => ex
40
+ log.warn { "Cannot create url for book #{book}; #{ex.message}" }
41
+ nil
42
+ end
43
+
44
+ private
45
+
46
+ def books_from_sutrs(resultset)
47
+ results = []
48
+ resultset[0..9].each do |record|
49
+ text = record.render(prefs["charset"], "UTF-8")
50
+ # File.open(',bl.marc', 'wb') {|f| f.write(text) }
51
+ log.debug { text }
52
+
53
+ title = isbn = publisher = publish_year = edition = nil
54
+ authors = []
55
+
56
+ text.split("\n").each do |line|
57
+ if (md = /^Title:\s+(.*)$/.match(line))
58
+ title = md[1].sub(/\.$/, "").squeeze(" ")
59
+ elsif (md = /^(?:Added Person|ME-Personal) Name:\s+(.*),[^,]+$/.match(line))
60
+ authors << md[1]
61
+ elsif (md = /^ISBN:\s+([\dXx]+)/.match(line))
62
+ isbn = Library.canonicalise_ean(md[1])
63
+ elsif (md = /^Imprint:.+:\s*(.+),/.match(line))
64
+ publisher = md[1]
65
+ end
66
+ end
67
+
68
+ log.debug do
69
+ msg = "Parsing SUTRS"
70
+ msg += "\n title: #{title}"
71
+ msg += "\n authors: #{authors.join(' and ')}"
72
+ msg += "\n isbn: #{isbn}"
73
+ msg += "\n publisher: #{publisher}"
74
+ msg += "\n edition: #{edition}"
75
+ msg
76
+ end
77
+
78
+ if title # and !authors.empty?
79
+ book = Book.new(title, authors, isbn, (publisher || nil),
80
+ (publish_year || nil), (edition || nil))
81
+ results << [book]
82
+ end
83
+ end
84
+ results
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,38 @@
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
+ require "alexandria/book_providers/z3950_provider"
8
+
9
+ module Alexandria
10
+ class BookProviders
11
+ class LOCProvider < Z3950Provider
12
+ # http://en.wikipedia.org/wiki/Library_of_Congress
13
+ unabstract
14
+
15
+ include GetText
16
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
17
+
18
+ def initialize
19
+ super("LOC", _("Library of Congress (Usa)"))
20
+ prefs.variable_named("hostname").default_value = "z3950.loc.gov"
21
+ prefs.variable_named("port").default_value = 7090
22
+ prefs.variable_named("database").default_value = "Voyager"
23
+ prefs.variable_named("record_syntax").default_value = "USMARC"
24
+ prefs.variable_named("charset").default_value = "ISO-8859-1"
25
+ prefs.read
26
+ end
27
+
28
+ def url(book)
29
+ isbn = Library.canonicalise_isbn(book.isbn)
30
+ "http://catalog.loc.gov/cgi-bin/Pwebrecon.cgi?" \
31
+ "DB=local&CNT=25+records+per+page&CMD=isbn+#{isbn}"
32
+ rescue StandardError => ex
33
+ log.warn { "Cannot create url for book #{book}; #{ex.message}" }
34
+ nil
35
+ end
36
+ end
37
+ end
38
+ end
@@ -133,7 +133,7 @@ module Alexandria
133
133
 
134
134
  d_idx = 0
135
135
  while d_idx < data.size
136
- d_str = data[d_idx..-1]
136
+ d_str = data[d_idx..]
137
137
  idx = d_str =~ /\$([a-z]) ([^$]+)/
138
138
  break unless idx
139
139
 
@@ -0,0 +1,108 @@
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
+ require "alexandria/book_providers/z3950_provider"
8
+
9
+ module Alexandria
10
+ class BookProviders
11
+ class SBNProvider < Z3950Provider
12
+ # http://sbnonline.sbn.it/
13
+ # http://it.wikipedia.org/wiki/ICCU
14
+ unabstract
15
+
16
+ include GetText
17
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
18
+
19
+ def initialize
20
+ super("SBN", "Servizio Bibliotecario Nazionale (Italy)")
21
+ prefs.variable_named("hostname").default_value = "opac.sbn.it"
22
+ prefs.variable_named("port").default_value = 3950
23
+ prefs.variable_named("database").default_value = "nopac"
24
+ # supported 'USMARC', 'UNIMARC' , 'SUTRS'
25
+ prefs.variable_named("record_syntax").default_value = "USMARC"
26
+ prefs.variable_named("charset").default_value = "ISO-8859-1"
27
+ prefs.read
28
+ end
29
+
30
+ def url(book)
31
+ "http://sbnonline.sbn.it/cgi-bin/zgw/BRIEF.pl?displayquery=" \
32
+ "%253CB%253E%253Cfont%2520color%253D%2523000064%253E" \
33
+ "Codice%2520ISBN%253C%2FB%253E%253C%2Ffont%253E%2520" \
34
+ "contiene%2520%2522%2520%253CFONT%2520COLOR%253Dred%253E" +
35
+ canonicalise_isbn_with_dashes(book.isbn) +
36
+ "%253C%2FFONT%253E%2522&session=&zurl=opac" \
37
+ "&zquery=%281%3D7+4%3D2+2%3D3+5%3D100+6%3D1+3%3D3+%22" +
38
+ canonicalise_isbn_with_dashes(book.isbn) +
39
+ "%22%29&language=it&maxentries=10&target=0&position=1"
40
+ rescue StandardError => ex
41
+ log.warn { "Cannot create url for book #{book}; #{ex.message}" }
42
+ nil
43
+ end
44
+
45
+ private
46
+
47
+ def canonicalise_criterion(criterion, _type)
48
+ canonicalise_isbn_with_dashes(criterion)
49
+ end
50
+
51
+ def request_count(_type)
52
+ 0
53
+ end
54
+
55
+ def canonicalise_isbn_with_dashes(isbn)
56
+ # The reference for the position of the dashes is
57
+ # http://www.isbn-international.org/converter/ranges.htm
58
+
59
+ isbn = Alexandria::Library.canonicalise_isbn(isbn)
60
+
61
+ if isbn[0..1] == "88"
62
+ # Italian speaking area
63
+ if isbn > "8895000" && (isbn <= "8899999996")
64
+ isbn[0..1] + "-" + isbn[2..6] + "-" + isbn[7..8] + "-" + isbn[9..9]
65
+ elsif isbn > "88900000"
66
+ isbn[0..1] + "-" + isbn[2..7] + "-" + isbn[8..8] + "-" + isbn[9..9]
67
+ elsif isbn > "8885000"
68
+ isbn[0..1] + "-" + isbn[2..6] + "-" + isbn[7..8] + "-" + isbn[9..9]
69
+ elsif isbn > "886000"
70
+ isbn[0..1] + "-" + isbn[2..5] + "-" + isbn[6..8] + "-" + isbn[9..9]
71
+ elsif isbn > "88200"
72
+ isbn[0..1] + "-" + isbn[2..4] + "-" + isbn[5..8] + "-" + isbn[9..9]
73
+ elsif isbn > "8800"
74
+ isbn[0..1] + "-" + isbn[2..3] + "-" + isbn[4..8] + "-" + isbn[9..9]
75
+ else
76
+ raise _("Invalid ISBN")
77
+ end
78
+
79
+ else
80
+ isbn
81
+ end
82
+ end
83
+ #
84
+ # Remarks about SBN
85
+ #
86
+ # This provider requires that value of conn.count is 0.
87
+ # It's a Yaz option "Number of records to be retrieved".
88
+ # This provider requires to specify the value of conn.element_set_name = 'F'.
89
+ # It's a Yaz option "Element-Set name of records".
90
+ # See http://www.indexdata.dk/yaz/doc/zoom.resultsets.tkl
91
+ #
92
+ # Dashes:
93
+ # this database requires that Italian books are searched with dashes :(
94
+ # However, they have also books with dashes in wrong positions, for
95
+ # instance 88-061-4934-2
96
+ #
97
+ # References:
98
+ # http://opac.internetculturale.it/cgi-bin/main.cgi?type=field
99
+ # http://www.internetculturale.it/
100
+ # http://sbnonline.sbn.it/zgw/homeit.html
101
+ # http://www.iccu.sbn.it/genera.jsp?id=124
102
+ # with link at http://www.iccu.sbn.it/upload/documenti/cartecsbn.pdf
103
+ # http://www.loc.gov/cgi-bin/zgstart?ACTION=INIT&FORM_HOST_PORT=/prod/www/data/z3950/iccu.html,opac.sbn.it,2100
104
+ # http://gwz.cilea.it/cgi-bin/reportOpac.cgi
105
+ #
106
+ end
107
+ end
108
+ end
@@ -151,7 +151,7 @@ module Alexandria
151
151
  trace = ex.backtrace.join("\n> ")
152
152
  log.warn do
153
153
  "Failed parsing search results for Thalia " \
154
- "#{ex.message} #{trace}"
154
+ "#{ex.message} #{trace}"
155
155
  end
156
156
  raise NoResultsError
157
157
  end
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # See the file README.md for authorship and licensing information.
6
6
 
7
- require "hpricot"
7
+ require "nokogiri"
8
8
  require "htmlentities"
9
9
 
10
10
  module Alexandria
@@ -19,7 +19,7 @@ module Alexandria
19
19
  html.force_encoding source_data_charset
20
20
  utf8_html = html.encode("utf-8")
21
21
  normalized_html = @htmlentities.decode(utf8_html)
22
- Hpricot(normalized_html)
22
+ Nokogiri.parse(normalized_html)
23
23
  end
24
24
 
25
25
  ## from Palatina
@@ -76,9 +76,10 @@ module Alexandria
76
76
  doc = html_to_doc(html, "UTF-8")
77
77
  book_search_results = []
78
78
  begin
79
- result_cells = doc / "td.result/div.name/.."
80
- result_cells.each do |td|
81
- type_icon = (td % "div.type/img.icn")
79
+ result_divs = doc / "td.result/div.name"
80
+ result_divs.each do |div|
81
+ td = div.parent
82
+ type_icon = td % "div.type/img.icn"
82
83
  next unless type_icon && type_icon["src"].include?("icon-bks")
83
84
 
84
85
  name_div = td % "div.name"
@@ -96,7 +97,7 @@ module Alexandria
96
97
  trace = ex.backtrace.join("\n> ")
97
98
  log.warn do
98
99
  "Failed parsing search results for WorldCat " \
99
- "#{ex.message} #{trace}"
100
+ "#{ex.message} #{trace}"
100
101
  end
101
102
  end
102
103
  book_search_results
@@ -170,9 +171,10 @@ module Alexandria
170
171
  # can we do better? get the City name?? or multiple publishers?
171
172
  bibdata = doc % "div#bibdata"
172
173
  bibdata_table = bibdata % :table
173
- publisher_row = bibdata_table % "th[text()*=Publisher]/.."
174
+ publisher_header = bibdata_table % "th[text()*=Publisher]"
174
175
 
175
- if publisher_row
176
+ if publisher_header
177
+ publisher_row = publisher_header.parent
176
178
  publication_info = (publisher_row / "td").last.inner_text
177
179
 
178
180
  publication_info =~ if publication_info.index(";")
@@ -213,7 +215,7 @@ module Alexandria
213
215
  trace = ex.backtrace.join("\n> ")
214
216
  log.warn do
215
217
  "Failed parsing search results for WorldCat " \
216
- "#{ex.message} #{trace}"
218
+ "#{ex.message} #{trace}"
217
219
  end
218
220
  raise NoResultsError
219
221
  end
@@ -0,0 +1,199 @@
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
+ require "zoom"
8
+ require "alexandria/book_providers/pseudomarc"
9
+ require "marc"
10
+
11
+ module Alexandria
12
+ class BookProviders
13
+ class Z3950Provider < AbstractProvider
14
+ include Logging
15
+ include GetText
16
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
17
+
18
+ def initialize(name = "Z3950", fullname = "Z39.50")
19
+ super
20
+ prefs.add("hostname", _("Hostname"), "")
21
+ prefs.add("port", _("Port"), 7090)
22
+ prefs.add("database", _("Database"), "")
23
+ prefs.add("record_syntax", _("Record syntax"), "USMARC",
24
+ ["USMARC", "UNIMARC", "SUTRS"])
25
+ prefs.add("username", _("Username"), "", nil, false)
26
+ prefs.add("password", _("Password"), "", nil, false)
27
+ prefs.add("charset", _("Charset encoding"), "ISO-8859-1")
28
+
29
+ # HACK : piggybacking support
30
+ prefs.add("piggyback", "Piggyback", true, [true, false])
31
+ prefs.read
32
+ end
33
+
34
+ def search(criterion, type)
35
+ prefs.read
36
+ criterion = criterion.encode(prefs["charset"])
37
+
38
+ isbn = type == SEARCH_BY_ISBN ? criterion : nil
39
+ criterion = canonicalise_criterion(criterion, type)
40
+ conn_count = request_count(type)
41
+ resultset = search_records(criterion, type, conn_count)
42
+ log.debug { "total #{resultset.length}" }
43
+
44
+ results = books_from_resultset(resultset, isbn)
45
+ raise NoResultsError if results.empty?
46
+
47
+ type == SEARCH_BY_ISBN ? results.first : results
48
+ end
49
+
50
+ def url(_book)
51
+ nil
52
+ end
53
+
54
+ def books_from_resultset(resultset, isbn)
55
+ case prefs["record_syntax"]
56
+ when /MARC$/
57
+ books_from_marc(resultset, isbn)
58
+ when "SUTRS"
59
+ books_from_sutrs(resultset)
60
+ else
61
+ raise NoResultsError
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def request_count(type)
68
+ type == SEARCH_BY_ISBN ? 1 : 10 # results to retrieve
69
+ end
70
+
71
+ def canonicalise_criterion(criterion, type)
72
+ criterion = Library.canonicalise_isbn(criterion) if type == SEARCH_BY_ISBN
73
+ criterion
74
+ end
75
+
76
+ def books_from_sutrs(_resultset)
77
+ # SUTRS needs to be decoded separately, because each Z39.50 server has a
78
+ # different one.
79
+ raise NoResultsError
80
+ end
81
+
82
+ def marc_to_book(marc_txt, isbn)
83
+ begin
84
+ marc = MARC::Record.new_from_marc(marc_txt, forgiving: true)
85
+ rescue StandardError => ex
86
+ log.error { ex.message }
87
+ log.error { ex.backtrace.join("> \n") }
88
+ begin
89
+ marc = MARC::Record.new(marc_txt)
90
+ rescue StandardError => ex
91
+ log.error { ex.message }
92
+ log.error { ex.backtrace.join("> \n") }
93
+ raise ex
94
+ end
95
+ end
96
+
97
+ log.debug do
98
+ msg = "Parsing MARC"
99
+ msg += "\n title: #{marc.title}"
100
+ msg += "\n authors: #{marc.authors.join(', ')}"
101
+ msg += "\n isbn: #{marc.isbn}, #{isbn}"
102
+ msg += "\n publisher: #{marc.publisher}"
103
+ msg += "\n publish year: #{marc.publish_year}" if marc.respond_to?(:publish_year)
104
+ msg += "\n edition: #{marc.edition}"
105
+ msg
106
+ end
107
+
108
+ return if marc.title.nil? # or marc.authors.empty?
109
+
110
+ isbn ||= marc.isbn
111
+ isbn = Library.canonicalise_ean(isbn)
112
+
113
+ Book.new(marc.title, marc.authors,
114
+ isbn,
115
+ (marc.publisher || ""),
116
+ marc.respond_to?(:publish_year) ? marc.publish_year.to_i : nil,
117
+ (marc.edition || ""))
118
+ end
119
+
120
+ def books_from_marc(resultset, isbn)
121
+ results = []
122
+ resultset[0..9].each do |record|
123
+ marc_txt = record.render(prefs["charset"], "UTF-8")
124
+ log.debug { marc_txt }
125
+ File.binwrite(",marc.txt", marc_txt) if $DEBUG
126
+ book = nil
127
+ begin
128
+ mappings = Alexandria::PseudoMarcParser::USMARC_MAPPINGS
129
+ if prefs["hostname"] == "z3950.bnf.fr"
130
+ mappings = Alexandria::PseudoMarcParser::BNF_FR_MAPPINGS
131
+ end
132
+ # try pseudo-marc parser first (it seems to have more luck)
133
+ book = Alexandria::PseudoMarcParser.marc_text_to_book(marc_txt,
134
+ mappings)
135
+ if book.nil?
136
+ # failing that, try the genuine MARC parser
137
+ book = marc_to_book(marc_txt, isbn)
138
+ end
139
+ rescue StandardError => ex
140
+ log.warn { ex }
141
+ log.warn { ex.backtrace }
142
+ end
143
+
144
+ results << [book] unless book.nil?
145
+ end
146
+ results
147
+ end
148
+
149
+ def marc?
150
+ prefs["record_syntax"].end_with?("MARC")
151
+ end
152
+
153
+ def search_records(criterion, type, conn_count)
154
+ options = {}
155
+ unless prefs["username"].empty? || prefs["password"].empty?
156
+ options["user"] = prefs["username"]
157
+ options["password"] = prefs["password"]
158
+ end
159
+ hostname = prefs["hostname"]
160
+ port = prefs["port"].to_i
161
+ log.debug { "hostname #{hostname} port #{port} options #{options}" }
162
+ conn = ZOOM::Connection.new(options).connect(hostname, port)
163
+ conn.database_name = prefs["database"]
164
+
165
+ conn.preferred_record_syntax = prefs["record_syntax"]
166
+ conn.element_set_name = "F"
167
+ conn.count = conn_count
168
+ attr = case type
169
+ when SEARCH_BY_ISBN then [7]
170
+ when SEARCH_BY_TITLE then [4]
171
+ when SEARCH_BY_AUTHORS then [1, 1003]
172
+ when SEARCH_BY_KEYWORD then [1016]
173
+ end
174
+ pqf = ""
175
+ attr.each { |att| pqf += "@attr 1=#{att} " }
176
+ pqf += '"' + criterion.upcase + '"'
177
+ log.debug { "pqf is #{pqf}, syntax #{prefs['record_syntax']}" }
178
+
179
+ begin
180
+ if prefs.variable_named("piggyback") && !prefs["piggyback"]
181
+ log.debug { "setting conn.piggyback to false" }
182
+ conn.piggyback = false
183
+ end
184
+ conn.search(pqf)
185
+ rescue StandardError => ex
186
+ if /1005/.match?(ex.message) &&
187
+ prefs.variable_named("piggyback") && prefs["piggyback"]
188
+ log.error { "Z39.50 search failed:: #{ex.message}" }
189
+ log.info { "Turning off piggybacking for this provider" }
190
+ prefs.variable_named("piggyback").new_value = false
191
+ retry
192
+ end
193
+
194
+ raise ex
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -35,12 +35,16 @@ module Alexandria
35
35
  SEARCH_BY_KEYWORD = (0..3).to_a
36
36
 
37
37
  class SearchError < StandardError; end
38
+
38
39
  class NoResultsError < SearchError; end
40
+
39
41
  class TooManyResultsError < SearchError; end
42
+
40
43
  class InvalidSearchTypeError < SearchError; end
41
44
 
42
45
  # These errors are not really errors
43
46
  class ProviderSkippedError < NoResultsError; end
47
+
44
48
  class SearchEmptyError < SearchError; end
45
49
 
46
50
  def self.search(criterion, type)
@@ -93,14 +97,9 @@ module Alexandria
93
97
 
94
98
  when SocketError
95
99
  format(_("Couldn't reach the provider '%s': socket " \
96
- "error (%s)."), factory.name, ex.message)
100
+ "error (%s)."), factory.name, ex.message)
97
101
 
98
- when NoResultsError
99
- _("No results were found. Make sure your " \
100
- "search criterion is spelled correctly, and " \
101
- "try again.")
102
-
103
- when ProviderSkippedError
102
+ when NoResultsError, ProviderSkippedError
104
103
  _("No results were found. Make sure your " \
105
104
  "search criterion is spelled correctly, and " \
106
105
  "try again.")
@@ -115,7 +114,7 @@ module Alexandria
115
114
  ex.message
116
115
  end
117
116
  log.debug { "raising empty error #{message}" }
118
- raise SearchEmptyError, message # rubocop:disable GetText/DecorateFunctionMessage
117
+ raise SearchEmptyError, message # rubocop:disable I18n/GetText/DecorateFunctionMessage
119
118
  else
120
119
  factory_n += 1
121
120
  retry
@@ -291,19 +290,14 @@ module Alexandria
291
290
 
292
291
  require "alexandria/book_providers/douban" # only requires YAML
293
292
 
294
- # Amazon AWS (Amazon Associates Web Services) provider, needs hpricot
295
- require "alexandria/book_providers/amazon_aws"
296
-
297
293
  # Website based providers
298
- require "alexandria/book_providers/adlibris"
299
- require "alexandria/book_providers/barnes_and_noble"
300
- require "alexandria/book_providers/proxis"
301
- require "alexandria/book_providers/siciliano"
302
294
  require "alexandria/book_providers/thalia_provider"
303
295
  require "alexandria/book_providers/worldcat"
304
296
 
305
297
  # Z39.50 based providers
306
- require "alexandria/book_providers/z3950"
298
+ require "alexandria/book_providers/loc_provider"
299
+ require "alexandria/book_providers/bl_provider"
300
+ require "alexandria/book_providers/sbn_provider"
307
301
 
308
302
  attr_reader :abstract_classes
309
303
 
@@ -381,19 +375,10 @@ module Alexandria
381
375
 
382
376
  changed = false
383
377
 
384
- if (ecs_index = priority.index("AmazonECS"))
385
- priority[ecs_index] = "Amazon" # replace legacy "AmazonECS" name
386
- priority.uniq! # remove any other "Amazon" from the list
387
- changed = true
388
- end
389
378
  if (worldcat_index = priority.index("Worldcat"))
390
379
  priority[worldcat_index] = "WorldCat"
391
380
  changed = true
392
381
  end
393
- if (adlibris_index = priority.index("Adlibris"))
394
- priority[adlibris_index] = "AdLibris"
395
- changed = true
396
- end
397
382
  @prefs.providers_priority = priority if changed
398
383
  end
399
384
  end
@@ -30,7 +30,7 @@ module Alexandria
30
30
  "col_want_visible" => true,
31
31
  "col_tags_visible" => true,
32
32
  "cols_width" => "{}",
33
- "providers_priority" => ["Amazon", "BarnesAndNoble", "AdLibris", "Proxis", "Thalia", "Siciliano", "WorldCat", "LOC", "BL", "SBN"],
33
+ "providers_priority" => ["Thalia", "WorldCat", "LOC", "BL", "SBN"],
34
34
  "view_advanced_settings" => false
35
35
  }
36
36
  end
@@ -251,7 +251,7 @@ module Alexandria
251
251
  entry.add_element("cover").text = final_cover(book)
252
252
  image = images.add_element("image")
253
253
  image.add_attribute("id", final_cover(book))
254
- image_s = ImageSize.new(IO.read(cover(book)))
254
+ image_s = ImageSize.new(File.read(cover(book)))
255
255
  image.add_attribute("height", image_s.height.to_s)
256
256
  image.add_attribute("width", image_s.width.to_s)
257
257
  image.add_attribute("format", image_s.format)
@@ -298,7 +298,7 @@ module Alexandria
298
298
  EOS
299
299
 
300
300
  if File.exist?(cover(book))
301
- image_s = ImageSize.new(IO.read(cover(book)))
301
+ image_s = ImageSize.new(File.read(cover(book)))
302
302
  xhtml << <<~EOS
303
303
  <img class="book_cover"
304
304
  src="#{File.join('pixmaps', final_cover(book))}"
@@ -374,7 +374,7 @@ module Alexandria
374
374
  bibtex << 'author = "'
375
375
  if book.authors != []
376
376
  bibtex << book.authors[0]
377
- book.authors[1..-1].each do |author|
377
+ book.authors[1..].each do |author|
378
378
  bibtex << " and #{latex_escape(author)}"
379
379
  end
380
380
  end
@@ -395,13 +395,13 @@ module Alexandria
395
395
  return "" if str.nil?
396
396
 
397
397
  my_str = str.dup
398
- my_str.gsub!(/%/, '\\%')
399
- my_str.gsub!(/~/, '\\textasciitilde')
400
- my_str.gsub!(/&/, '\\\\&')
401
- my_str.gsub!(/\#/, '\\\\#')
402
- my_str.gsub!(/\{/, '\\{')
403
- my_str.gsub!(/\}/, '\\}')
404
- my_str.gsub!(/_/, '\\_')
398
+ my_str.gsub!(/%/, "\\%")
399
+ my_str.gsub!(/~/, "\\textasciitilde")
400
+ my_str.gsub!(/&/, "\\\\&")
401
+ my_str.gsub!(/\#/, "\\\\#")
402
+ my_str.gsub!(/\{/, "\\{")
403
+ my_str.gsub!(/\}/, "\\}")
404
+ my_str.gsub!(/_/, "\\_")
405
405
  my_str.gsub!(/\$/, "\\\$")
406
406
  my_str.gsub!(/"(.+)"/, "``\1''")
407
407
  my_str