alexandria-book-collection-manager 0.7.5 → 0.7.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +9 -0
- data/.gitignore +4 -1
- data/.rubocop.yml +51 -29
- data/.rubocop_todo.yml +33 -155
- data/.simplecov +5 -2
- data/.travis.yml +10 -4
- data/CHANGELOG.md +19 -0
- data/INSTALL.md +23 -11
- data/README.md +36 -35
- data/Rakefile +7 -5
- data/alexandria-book-collection-manager.gemspec +8 -3
- data/doc/dependency_decisions.yml +22 -3
- data/lib/alexandria.rb +2 -2
- data/lib/alexandria/book_providers.rb +47 -49
- data/lib/alexandria/book_providers/adlibris.rb +8 -13
- data/lib/alexandria/book_providers/amazon_aws.rb +47 -60
- data/lib/alexandria/book_providers/amazon_ecs_util.rb +283 -298
- data/lib/alexandria/book_providers/barnes_and_noble.rb +8 -8
- data/lib/alexandria/book_providers/douban.rb +2 -2
- data/lib/alexandria/book_providers/proxis.rb +12 -11
- data/lib/alexandria/book_providers/pseudomarc.rb +60 -70
- data/lib/alexandria/book_providers/siciliano.rb +5 -6
- data/lib/alexandria/book_providers/thalia.rb +8 -9
- data/lib/alexandria/book_providers/worldcat.rb +25 -31
- data/lib/alexandria/book_providers/z3950.rb +62 -69
- data/lib/alexandria/default_preferences.rb +37 -0
- data/lib/alexandria/execution_queue.rb +13 -12
- data/lib/alexandria/export_library.rb +4 -8
- data/lib/alexandria/import_library.rb +38 -62
- data/lib/alexandria/import_library_csv.rb +16 -16
- data/lib/alexandria/library_sort_order.rb +3 -1
- data/lib/alexandria/library_store.rb +16 -16
- data/lib/alexandria/logging.rb +4 -8
- data/lib/alexandria/models/book.rb +2 -2
- data/lib/alexandria/models/library.rb +18 -14
- data/lib/alexandria/net.rb +1 -2
- data/lib/alexandria/preferences.rb +27 -31
- data/lib/alexandria/scanners.rb +2 -2
- data/lib/alexandria/scanners/cue_cat.rb +5 -5
- data/lib/alexandria/scanners/keyboard.rb +1 -1
- data/lib/alexandria/smart_library.rb +22 -26
- data/lib/alexandria/ui.rb +7 -7
- data/lib/alexandria/ui/about_dialog.rb +1 -1
- data/lib/alexandria/ui/acquire_dialog.rb +9 -10
- data/lib/alexandria/ui/alert_dialog.rb +34 -19
- data/lib/alexandria/ui/bad_isbns_dialog.rb +13 -9
- data/lib/alexandria/ui/barcode_animation.rb +5 -5
- data/lib/alexandria/ui/book_properties_dialog.rb +2 -2
- data/lib/alexandria/ui/book_properties_dialog_base.rb +23 -17
- data/lib/alexandria/ui/callbacks.rb +141 -120
- data/lib/alexandria/ui/completion_models.rb +1 -1
- data/lib/alexandria/ui/confirm_erase_dialog.rb +1 -1
- data/lib/alexandria/ui/conflict_while_copying_dialog.rb +1 -1
- data/lib/alexandria/ui/error_dialog.rb +1 -1
- data/lib/alexandria/ui/export_dialog.rb +13 -15
- data/lib/alexandria/ui/icons.rb +34 -40
- data/lib/alexandria/ui/iconview_tooltips.rb +40 -53
- data/lib/alexandria/ui/import_dialog.rb +48 -47
- data/lib/alexandria/ui/init.rb +3 -2
- data/lib/alexandria/ui/keep_bad_isbn_dialog.rb +2 -2
- data/lib/alexandria/ui/libraries_combo.rb +10 -9
- data/lib/alexandria/ui/listview.rb +5 -6
- data/lib/alexandria/ui/main_app.rb +2 -2
- data/lib/alexandria/ui/multi_drag_treeview.rb +4 -6
- data/lib/alexandria/ui/new_book_dialog.rb +52 -52
- data/lib/alexandria/ui/new_provider_dialog.rb +12 -11
- data/lib/alexandria/ui/new_smart_library_dialog.rb +39 -27
- data/lib/alexandria/ui/preferences_dialog.rb +23 -82
- data/lib/alexandria/ui/provider_preferences_base_dialog.rb +9 -5
- data/lib/alexandria/ui/provider_preferences_dialog.rb +5 -5
- data/lib/alexandria/ui/really_delete_dialog.rb +1 -1
- data/lib/alexandria/ui/sidepane_manager.rb +35 -37
- data/lib/alexandria/ui/skip_entry_dialog.rb +3 -2
- data/lib/alexandria/ui/smart_library_properties_dialog.rb +35 -36
- data/lib/alexandria/ui/smart_library_properties_dialog_base.rb +59 -138
- data/lib/alexandria/ui/smart_library_rule_box.rb +119 -0
- data/lib/alexandria/ui/sound.rb +4 -6
- data/lib/alexandria/ui/ui_manager.rb +62 -64
- data/lib/alexandria/version.rb +2 -2
- data/lib/alexandria/web_themes.rb +15 -15
- data/po/cs.po +991 -874
- data/po/cy.po +957 -870
- data/po/de.po +991 -866
- data/po/el.po +987 -863
- data/po/es.po +986 -862
- data/po/fr.po +988 -868
- data/po/ga.po +910 -822
- data/po/gl.po +983 -863
- data/po/it.po +984 -862
- data/po/ja.po +969 -849
- data/po/mk.po +983 -859
- data/po/nb.po +982 -862
- data/po/nl.po +992 -869
- data/po/pl.po +1018 -963
- data/po/pt.po +983 -852
- data/po/pt_BR.po +983 -863
- data/po/ru.po +992 -869
- data/po/sk.po +986 -864
- data/po/sv.po +980 -860
- data/po/uk.po +975 -861
- data/po/zh_TW.po +974 -854
- data/share/alexandria/glade/main_app__builder.glade +6 -21
- data/share/gnome/help/alexandria/C/smart-libraries.xml +2 -2
- data/share/gnome/help/alexandria/C/working-with-libraries.xml +1 -1
- data/share/gnome/help/alexandria/fr/alexandria.xml +1 -1
- data/share/gnome/help/alexandria/ja/smart-libraries.xml +1 -1
- data/spec/alexandria/book_providers/world_cat_provider_spec.rb +160 -0
- data/spec/alexandria/book_providers_spec.rb +73 -129
- data/spec/alexandria/console_spec.rb +0 -5
- data/spec/alexandria/export_library_spec.rb +27 -38
- data/spec/alexandria/library_spec.rb +56 -44
- data/spec/alexandria/preferences_spec.rb +29 -3
- data/spec/alexandria/scanners/cue_cat_spec.rb +1 -1
- data/spec/alexandria/ui/about_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/acquire_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/alert_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/book_properties_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/error_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/export_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/icons_spec.rb +26 -0
- data/spec/alexandria/ui/iconview_spec.rb +1 -1
- data/spec/alexandria/ui/import_dialog_spec.rb +30 -3
- data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/main_app_spec.rb +1 -1
- data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +1 -1
- data/spec/alexandria/ui/new_provider_dialog_spec.rb +19 -3
- data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +28 -3
- data/spec/alexandria/ui/preferences_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +23 -8
- data/spec/alexandria/ui/really_delete_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/sidepane_manager_spec.rb +2 -2
- data/spec/alexandria/ui/skip_entry_dialog_spec.rb +1 -1
- data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +21 -7
- data/spec/alexandria/ui/ui_manager_spec.rb +39 -2
- data/spec/spec_helper.rb +46 -3
- data/tasks/spec.rake +3 -5
- data/util/rake/fileinstall.rb +12 -11
- metadata +82 -10
- data/spec/alexandria/ui/ui_utilities_spec.rb +0 -62
- data/spec/alexandria/utilities_spec.rb +0 -52
@@ -37,19 +37,15 @@ module Alexandria
|
|
37
37
|
prefs.read
|
38
38
|
criterion = criterion.encode(prefs["charset"])
|
39
39
|
|
40
|
-
# We only decode MARC at the moment.
|
41
|
-
# SUTRS needs to be decoded separately, because each Z39.50 server has a
|
42
|
-
# different one.
|
43
|
-
raise NoResultsError unless marc?
|
44
|
-
|
45
40
|
isbn = type == SEARCH_BY_ISBN ? criterion : nil
|
46
|
-
criterion =
|
47
|
-
conn_count = type
|
41
|
+
criterion = canonicalise_criterion(criterion, type)
|
42
|
+
conn_count = request_count(type)
|
48
43
|
resultset = search_records(criterion, type, conn_count)
|
49
44
|
log.debug { "total #{resultset.length}" }
|
50
|
-
raise NoResultsError if resultset.length == 0
|
51
45
|
|
52
|
-
results =
|
46
|
+
results = books_from_resultset(resultset, isbn)
|
47
|
+
raise NoResultsError if results.empty?
|
48
|
+
|
53
49
|
type == SEARCH_BY_ISBN ? results.first : results
|
54
50
|
end
|
55
51
|
|
@@ -57,9 +53,35 @@ module Alexandria
|
|
57
53
|
nil
|
58
54
|
end
|
59
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
|
+
|
60
67
|
private
|
61
68
|
|
62
|
-
def
|
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)
|
63
85
|
begin
|
64
86
|
marc = MARC::Record.new_from_marc(marc_txt, forgiving: true)
|
65
87
|
rescue StandardError => ex
|
@@ -87,18 +109,17 @@ module Alexandria
|
|
87
109
|
|
88
110
|
return if marc.title.nil? # or marc.authors.empty?
|
89
111
|
|
90
|
-
|
112
|
+
isbn ||= marc.isbn
|
91
113
|
isbn = Library.canonicalise_ean(isbn)
|
92
114
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
book
|
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 || ""))
|
99
120
|
end
|
100
121
|
|
101
|
-
def books_from_marc(resultset,
|
122
|
+
def books_from_marc(resultset, isbn)
|
102
123
|
results = []
|
103
124
|
resultset[0..9].each do |record|
|
104
125
|
marc_txt = record.render(prefs["charset"], "UTF-8")
|
@@ -119,7 +140,7 @@ module Alexandria
|
|
119
140
|
mappings)
|
120
141
|
if book.nil?
|
121
142
|
# failing that, try the genuine MARC parser
|
122
|
-
book = marc_to_book(marc_txt)
|
143
|
+
book = marc_to_book(marc_txt, isbn)
|
123
144
|
end
|
124
145
|
rescue StandardError => ex
|
125
146
|
log.warn { ex }
|
@@ -132,7 +153,7 @@ module Alexandria
|
|
132
153
|
end
|
133
154
|
|
134
155
|
def marc?
|
135
|
-
|
156
|
+
prefs["record_syntax"].end_with?("MARC")
|
136
157
|
end
|
137
158
|
|
138
159
|
def search_records(criterion, type, conn_count)
|
@@ -147,9 +168,6 @@ module Alexandria
|
|
147
168
|
conn = ZOOM::Connection.new(options).connect(hostname, port)
|
148
169
|
conn.database_name = prefs["database"]
|
149
170
|
|
150
|
-
# HACK: turn off piggybacking, just to see CMcG
|
151
|
-
# #conn.piggyback = false
|
152
|
-
|
153
171
|
conn.preferred_record_syntax = prefs["record_syntax"]
|
154
172
|
conn.element_set_name = "F"
|
155
173
|
conn.count = conn_count
|
@@ -165,27 +183,21 @@ module Alexandria
|
|
165
183
|
log.debug { "pqf is #{pqf}, syntax #{prefs['record_syntax']}" }
|
166
184
|
|
167
185
|
begin
|
168
|
-
if prefs.variable_named("piggyback")
|
169
|
-
|
170
|
-
|
171
|
-
conn.piggyback = false
|
172
|
-
end
|
186
|
+
if prefs.variable_named("piggyback") && !prefs["piggyback"]
|
187
|
+
log.debug { "setting conn.piggyback to false" }
|
188
|
+
conn.piggyback = false
|
173
189
|
end
|
174
190
|
conn.search(pqf)
|
175
191
|
rescue StandardError => ex
|
176
|
-
if /1005/.match?(ex.message)
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
# hopefully these precautions will prevent infinite loops here
|
183
|
-
else
|
184
|
-
raise ex
|
185
|
-
end
|
186
|
-
else
|
187
|
-
raise 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
|
188
198
|
end
|
199
|
+
|
200
|
+
raise ex
|
189
201
|
end
|
190
202
|
end
|
191
203
|
end
|
@@ -243,20 +255,6 @@ module Alexandria
|
|
243
255
|
prefs.read
|
244
256
|
end
|
245
257
|
|
246
|
-
def search(criterion, type)
|
247
|
-
return super unless prefs["record_syntax"] == "SUTRS"
|
248
|
-
|
249
|
-
prefs.read
|
250
|
-
criterion = Library.canonicalise_isbn(criterion) if type == SEARCH_BY_ISBN
|
251
|
-
conn_count = type == SEARCH_BY_ISBN ? 1 : 10 # results to retrieve
|
252
|
-
resultset = search_records(criterion, type, conn_count)
|
253
|
-
log.debug { "total #{resultset.length}" }
|
254
|
-
raise NoResultsError if resultset.length == 0
|
255
|
-
|
256
|
-
results = books_from_sutrs(resultset)
|
257
|
-
type == SEARCH_BY_ISBN ? results.first : results
|
258
|
-
end
|
259
|
-
|
260
258
|
def url(book)
|
261
259
|
"http://copac.ac.uk/openurl?isbn=" + Library.canonicalise_isbn(book.isbn)
|
262
260
|
rescue StandardError => ex
|
@@ -285,7 +283,7 @@ module Alexandria
|
|
285
283
|
authors << md[1]
|
286
284
|
elsif (md = /^ISBN:\s+([\dXx]+)/.match(line))
|
287
285
|
isbn = Library.canonicalise_ean(md[1])
|
288
|
-
elsif (md = /^Imprint
|
286
|
+
elsif (md = /^Imprint:.+:\s*(.+),/.match(line))
|
289
287
|
publisher = md[1]
|
290
288
|
end
|
291
289
|
end
|
@@ -329,19 +327,6 @@ module Alexandria
|
|
329
327
|
prefs.read
|
330
328
|
end
|
331
329
|
|
332
|
-
def search(criterion, type)
|
333
|
-
prefs.read
|
334
|
-
|
335
|
-
isbn = type == SEARCH_BY_ISBN ? criterion : nil
|
336
|
-
criterion = canonicalise_isbn_with_dashes(criterion)
|
337
|
-
resultset = search_records(criterion, type, 0)
|
338
|
-
log.debug { "total #{resultset.length}" }
|
339
|
-
raise NoResultsError if resultset.length == 0
|
340
|
-
|
341
|
-
results = books_from_marc(resultset, isbn)
|
342
|
-
type == SEARCH_BY_ISBN ? results.first : results
|
343
|
-
end
|
344
|
-
|
345
330
|
def url(book)
|
346
331
|
"http://sbnonline.sbn.it/cgi-bin/zgw/BRIEF.pl?displayquery=" \
|
347
332
|
"%253CB%253E%253Cfont%2520color%253D%2523000064%253E" \
|
@@ -359,6 +344,14 @@ module Alexandria
|
|
359
344
|
|
360
345
|
private
|
361
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
|
+
|
362
355
|
def canonicalise_isbn_with_dashes(isbn)
|
363
356
|
# The reference for the position of the dashes is
|
364
357
|
# http://www.isbn-international.org/converter/ranges.htm
|
@@ -380,7 +373,7 @@ module Alexandria
|
|
380
373
|
elsif isbn > "8800"
|
381
374
|
isbn[0..1] + "-" + isbn[2..3] + "-" + isbn[4..8] + "-" + isbn[9..9]
|
382
375
|
else
|
383
|
-
raise "Invalid ISBN"
|
376
|
+
raise _("Invalid ISBN")
|
384
377
|
end
|
385
378
|
|
386
379
|
else
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# This file is automatically generated by the alexandria installer.
|
2
|
+
# Do not edit it directly.
|
3
|
+
|
4
|
+
module Alexandria
|
5
|
+
class Preferences
|
6
|
+
DEFAULT_VALUES = {
|
7
|
+
"position" => [0, 0],
|
8
|
+
"size" => [640, 480],
|
9
|
+
"maximized" => false,
|
10
|
+
"sidepane_position" => 140,
|
11
|
+
"view_as" => 0,
|
12
|
+
"arrange_icons_mode" => 0,
|
13
|
+
"reverse_icons" => false,
|
14
|
+
"selected_library" => "",
|
15
|
+
"barcode_scanner" => "CueCat",
|
16
|
+
"play_scanning_sound" => true,
|
17
|
+
"play_scan_sound" => true,
|
18
|
+
"toolbar_visible" => true,
|
19
|
+
"sidepane_visible" => true,
|
20
|
+
"statusbar_visible" => true,
|
21
|
+
"col_authors_visible" => true,
|
22
|
+
"col_edition_visible" => true,
|
23
|
+
"col_isbn_visible" => true,
|
24
|
+
"col_publisher_visible" => true,
|
25
|
+
"col_publish_date_visible" => true,
|
26
|
+
"col_loaned_to_visible" => true,
|
27
|
+
"col_rating_visible" => true,
|
28
|
+
"col_redd_visible" => true,
|
29
|
+
"col_own_visible" => true,
|
30
|
+
"col_want_visible" => true,
|
31
|
+
"col_tags_visible" => true,
|
32
|
+
"cols_width" => "{}",
|
33
|
+
"providers_priority" => ["Amazon", "BarnesAndNoble", "AdLibris", "Proxis", "Thalia", "Siciliano", "WorldCat", "LOC", "BL", "SBN"],
|
34
|
+
"view_advanced_settings" => false
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
@@ -63,10 +63,11 @@ module Alexandria
|
|
63
63
|
|
64
64
|
id, procedure, args, need_retval = ary
|
65
65
|
retval = procedure.call(*args)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
|
67
|
+
return unless need_retval
|
68
|
+
|
69
|
+
@protect_pending_retvals.synchronize do
|
70
|
+
@pending_retvals << [id, retval]
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
@@ -81,14 +82,14 @@ module Alexandria
|
|
81
82
|
@id += 1
|
82
83
|
@pending_calls << [@id, procedure, args, need_retval]
|
83
84
|
end
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
85
|
+
return unless need_retval
|
86
|
+
|
87
|
+
loop do
|
88
|
+
@protect_pending_retvals.synchronize do
|
89
|
+
ary = @pending_retvals.find { |id, _retval| id == @id }
|
90
|
+
if ary
|
91
|
+
@pending_retvals.delete(ary)
|
92
|
+
return ary[1]
|
92
93
|
end
|
93
94
|
end
|
94
95
|
end
|
@@ -51,10 +51,6 @@ module Alexandria
|
|
51
51
|
def export_as_tellico_xml_archive(filename)
|
52
52
|
File.open(File.join(Dir.tmpdir, "tellico.xml"), "w") do |io|
|
53
53
|
to_tellico_document.write(io, 0)
|
54
|
-
rescue StandardError => ex
|
55
|
-
puts ex.message
|
56
|
-
puts ex.backtrace
|
57
|
-
raise ex
|
58
54
|
end
|
59
55
|
copy_covers(File.join(Dir.tmpdir, "images"))
|
60
56
|
Dir.chdir(Dir.tmpdir) do
|
@@ -190,7 +186,7 @@ module Alexandria
|
|
190
186
|
File.join("images", final_cover(book))
|
191
187
|
end
|
192
188
|
if book.isbn
|
193
|
-
BookProviders.each do |provider|
|
189
|
+
BookProviders.list.each do |provider|
|
194
190
|
elem = prod.add_element("ProductWebsite")
|
195
191
|
elem.add_element("ProductWebsiteDescription").text =
|
196
192
|
provider.fullname
|
@@ -267,7 +263,7 @@ module Alexandria
|
|
267
263
|
escaped.gsub!(/&/, "&")
|
268
264
|
escaped.gsub!(/</, "<")
|
269
265
|
escaped.gsub!(/>/, ">")
|
270
|
-
escaped.gsub!(
|
266
|
+
escaped.gsub!(/"/, """)
|
271
267
|
escaped
|
272
268
|
end
|
273
269
|
|
@@ -398,13 +394,13 @@ module Alexandria
|
|
398
394
|
my_str = str.dup
|
399
395
|
my_str.gsub!(/%/, '\\%')
|
400
396
|
my_str.gsub!(/~/, '\\textasciitilde')
|
401
|
-
my_str.gsub!(
|
397
|
+
my_str.gsub!(/&/, '\\\\&')
|
402
398
|
my_str.gsub!(/\#/, '\\\\#')
|
403
399
|
my_str.gsub!(/\{/, '\\{')
|
404
400
|
my_str.gsub!(/\}/, '\\}')
|
405
401
|
my_str.gsub!(/_/, '\\_')
|
406
402
|
my_str.gsub!(/\$/, "\\\$")
|
407
|
-
my_str.gsub!(
|
403
|
+
my_str.gsub!(/"(.+)"/, "``\1''")
|
408
404
|
my_str
|
409
405
|
end
|
410
406
|
end
|
@@ -11,6 +11,7 @@ module Alexandria
|
|
11
11
|
class ImportFilter
|
12
12
|
attr_reader :name, :patterns, :message
|
13
13
|
|
14
|
+
include Logging
|
14
15
|
include GetText
|
15
16
|
extend GetText
|
16
17
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
@@ -34,7 +35,7 @@ module Alexandria
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def invoke(library_name, filename)
|
37
|
-
|
38
|
+
log.debug { "Selected: #{@message} -- #{library_name} -- #{filename}" }
|
38
39
|
Library.send(@message, library_name, filename,
|
39
40
|
@on_iterate_cb, @on_error_cb)
|
40
41
|
end
|
@@ -50,30 +51,24 @@ module Alexandria
|
|
50
51
|
|
51
52
|
class Library
|
52
53
|
def self.import_autodetect(*args)
|
53
|
-
|
54
|
+
log.debug { args.inspect }
|
54
55
|
filename = args[1]
|
55
|
-
|
56
|
-
|
56
|
+
log.debug { "Filename is #{filename} and ext is #{filename[-4..-1]}" }
|
57
|
+
log.debug { "Beginning import: #{args[0]}, #{args[1]}" }
|
57
58
|
if filename[-4..-1] == ".txt"
|
58
59
|
import_as_isbn_list(*args)
|
59
60
|
elsif [".tc", ".bc"].include? filename[-3..-1]
|
60
|
-
|
61
|
-
import_as_tellico_xml_archive(*args)
|
62
|
-
rescue StandardError => ex
|
63
|
-
puts ex.message
|
64
|
-
puts ex.backtrace.join("\n>> ")
|
65
|
-
end
|
61
|
+
import_as_tellico_xml_archive(*args)
|
66
62
|
elsif [".csv"].include? filename[-4..-1]
|
67
63
|
import_as_csv_file(*args)
|
68
64
|
else
|
69
|
-
|
70
|
-
raise "Not supported type"
|
65
|
+
raise _("Unsupported type")
|
71
66
|
end
|
72
67
|
end
|
73
68
|
|
74
69
|
def self.import_as_tellico_xml_archive(name, filename,
|
75
70
|
on_iterate_cb, _on_error_cb)
|
76
|
-
|
71
|
+
log.debug { "Starting import_as_tellico_xml_archive... " }
|
77
72
|
return nil unless system("unzip -qqt \"#{filename}\"")
|
78
73
|
|
79
74
|
tmpdir = File.join(Dir.tmpdir, "tellico_export")
|
@@ -111,25 +106,12 @@ module Alexandria
|
|
111
106
|
book_elements += keys.map do |key|
|
112
107
|
neaten(elements[key].text) if elements[key]
|
113
108
|
end
|
114
|
-
|
115
|
-
if book_elements[2].nil? || book_elements[2].strip.empty?
|
116
|
-
book_elements[2] = nil
|
117
|
-
else
|
118
|
-
begin
|
119
|
-
book_elements[2] = book_elements[2].strip
|
120
|
-
book_elements[2] = Library.canonicalise_ean(book_elements[2])
|
121
|
-
rescue StandardError => ex
|
122
|
-
puts book_elements[2]
|
123
|
-
puts ex.message
|
124
|
-
puts ex.backtrace.join("\n> ")
|
125
|
-
raise ex
|
126
|
-
end
|
127
|
-
end
|
109
|
+
book_elements[2] = Library.canonicalise_ean(book_elements[2])
|
128
110
|
# publishing_year
|
129
111
|
book_elements[4] = book_elements[4].to_i unless book_elements[4].nil?
|
130
|
-
|
112
|
+
log.debug { book_elements.inspect }
|
131
113
|
cover = (neaten(elements["cover"].text) if elements["cover"])
|
132
|
-
|
114
|
+
log.debug { cover }
|
133
115
|
book = Book.new(*book_elements)
|
134
116
|
if elements["rating"]
|
135
117
|
rating = elements["rating"].text.to_i
|
@@ -140,19 +122,16 @@ module Alexandria
|
|
140
122
|
on_iterate_cb&.call(n + 1, total)
|
141
123
|
end
|
142
124
|
|
143
|
-
library
|
125
|
+
# TODO: Pass in library store as an argument
|
126
|
+
library = LibraryCollection.instance.library_store.load_library name
|
144
127
|
content.each do |book, cover|
|
145
|
-
unless cover.nil?
|
146
|
-
library.save_cover(book,
|
147
|
-
File.join(Dir.pwd, "images",
|
148
|
-
cover))
|
149
|
-
end
|
128
|
+
library.save_cover(book, File.join(Dir.pwd, "images", cover)) unless cover.nil?
|
150
129
|
library << book
|
151
130
|
library.save(book)
|
152
131
|
end
|
153
132
|
return [library, []]
|
154
133
|
rescue StandardError => ex
|
155
|
-
|
134
|
+
log.info { ex.message }
|
156
135
|
return nil
|
157
136
|
end
|
158
137
|
end
|
@@ -177,7 +156,7 @@ module Alexandria
|
|
177
156
|
if book.isbn
|
178
157
|
# if we can search by ISBN, try to grab the cover
|
179
158
|
begin
|
180
|
-
dl_book, dl_cover =
|
159
|
+
dl_book, dl_cover = BookProviders.isbn_search(book.isbn)
|
181
160
|
if dl_book.authors.size > book.authors.size
|
182
161
|
# LibraryThing only supports a single author, so
|
183
162
|
# attempt to include more author information if it's
|
@@ -187,8 +166,7 @@ module Alexandria
|
|
187
166
|
book.edition = dl_book.edition unless book.edition
|
188
167
|
cover = dl_cover
|
189
168
|
rescue StandardError
|
190
|
-
|
191
|
-
# note failure
|
169
|
+
log.debug { "Failed to get cover for #{book.title} #{book.isbn}" }
|
192
170
|
end
|
193
171
|
end
|
194
172
|
|
@@ -203,7 +181,7 @@ module Alexandria
|
|
203
181
|
# probably Goodreads' wonky ISBN fields ,,="043432432X",
|
204
182
|
# this is a hack to fix up such files
|
205
183
|
data = File.read(filename)
|
206
|
-
data.gsub!(
|
184
|
+
data.gsub!(/,="/, ',"')
|
207
185
|
csv_fixed = Tempfile.new("alexandria_import_csv_fixed_")
|
208
186
|
csv_fixed.write(data)
|
209
187
|
csv_fixed.close
|
@@ -216,12 +194,13 @@ module Alexandria
|
|
216
194
|
end
|
217
195
|
end
|
218
196
|
|
219
|
-
library
|
197
|
+
# TODO: Pass in library store as an argument
|
198
|
+
library = LibraryCollection.instance.library_store.load_library name
|
220
199
|
|
221
200
|
books_and_covers.each do |book, cover_uri|
|
222
|
-
|
201
|
+
log.debug { "Saving #{book.isbn} cover" }
|
223
202
|
library.save_cover(book, cover_uri) unless cover_uri.nil?
|
224
|
-
|
203
|
+
log.debug { "Saving #{book.isbn}" }
|
225
204
|
library << book
|
226
205
|
library.save(book)
|
227
206
|
end
|
@@ -230,18 +209,12 @@ module Alexandria
|
|
230
209
|
|
231
210
|
def self.import_as_isbn_list(name, filename, on_iterate_cb,
|
232
211
|
on_error_cb)
|
233
|
-
|
212
|
+
log.debug { "Starting import_as_isbn_list... " }
|
234
213
|
isbn_list = IO.readlines(filename).map do |line|
|
235
|
-
|
236
|
-
|
237
|
-
begin
|
238
|
-
[line.chomp, canonicalise_isbn(line.chomp)] unless line == "\n"
|
239
|
-
rescue StandardError => ex
|
240
|
-
puts ex.message
|
241
|
-
[line.chomp, nil]
|
242
|
-
end
|
214
|
+
log.debug { "Trying line #{line}" }
|
215
|
+
[line.chomp, canonicalise_isbn(line.chomp)] unless line == "\n"
|
243
216
|
end
|
244
|
-
|
217
|
+
log.debug { "Isbn list: #{isbn_list.inspect}" }
|
245
218
|
isbn_list.compact!
|
246
219
|
return nil if isbn_list.empty?
|
247
220
|
|
@@ -253,27 +226,30 @@ module Alexandria
|
|
253
226
|
isbn_list.each do |isbn|
|
254
227
|
begin
|
255
228
|
if isbn[1]
|
256
|
-
books <<
|
229
|
+
books << BookProviders.isbn_search(isbn[1])
|
257
230
|
else
|
258
231
|
bad_isbns << isbn[0]
|
259
232
|
end
|
260
|
-
rescue
|
261
|
-
|
233
|
+
rescue BookProviders::SearchEmptyError => ex
|
234
|
+
log.debug { ex.message }
|
262
235
|
failed_lookup_isbns << isbn[1]
|
263
|
-
|
236
|
+
log.debug { "NOTE : ignoring on_error_cb #{on_error_cb}" }
|
264
237
|
# return nil unless
|
265
238
|
# (on_error_cb and on_error_cb.call(e.message))
|
266
239
|
end
|
267
240
|
|
268
241
|
on_iterate_cb&.call(current_iteration += 1, max_iterations)
|
269
242
|
end
|
270
|
-
|
271
|
-
|
272
|
-
|
243
|
+
log.debug { "Bad Isbn list: #{bad_isbns.inspect}" } if bad_isbns
|
244
|
+
|
245
|
+
# TODO: Pass in library store as an argument
|
246
|
+
library = LibraryCollection.instance.library_store.load_library name
|
247
|
+
|
248
|
+
log.debug { "Going with these #{books.length} books: #{books.inspect}" }
|
273
249
|
books.each do |book, cover_uri|
|
274
|
-
|
250
|
+
log.debug { "Saving #{book.isbn} cover..." }
|
275
251
|
library.save_cover(book, cover_uri) unless cover_uri.nil?
|
276
|
-
|
252
|
+
log.debug { "Saving #{book.isbn}..." }
|
277
253
|
library << book
|
278
254
|
library.save(book)
|
279
255
|
on_iterate_cb&.call(current_iteration += 1, max_iterations)
|