alexandria-book-collection-manager 0.7.9 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +5 -1
- data/.github/workflows/ruby.yml +36 -22
- data/.rubocop.yml +12 -3
- data/.rubocop_todo.yml +35 -47
- data/.simplecov +2 -2
- data/CHANGELOG.md +71 -24
- data/Gemfile +0 -6
- data/Rakefile +5 -5
- data/alexandria-book-collection-manager.gemspec +24 -21
- data/bin/rake +28 -0
- data/bin/rspec +28 -0
- data/doc/dependency_decisions.yml +32 -26
- data/{bin → exe}/alexandria +1 -3
- data/lib/alexandria/about.rb +1 -0
- data/lib/alexandria/book_providers/bl_provider.rb +4 -6
- data/lib/alexandria/book_providers/{douban.rb → douban_provider.rb} +1 -1
- data/lib/alexandria/book_providers/loc_provider.rb +2 -6
- data/lib/alexandria/book_providers/sbn_provider.rb +2 -12
- data/lib/alexandria/book_providers/thalia_provider.rb +5 -6
- data/lib/alexandria/book_providers/{web.rb → website_based_provider.rb} +20 -1
- data/lib/alexandria/book_providers/{worldcat.rb → world_cat_provider.rb} +3 -4
- data/lib/alexandria/book_providers/z3950_provider.rb +25 -27
- data/lib/alexandria/book_providers.rb +14 -10
- data/lib/alexandria/config.rb +2 -2
- data/lib/alexandria/console.rb +12 -10
- data/lib/alexandria/export_format.rb +3 -2
- data/lib/alexandria/export_library.rb +35 -40
- data/lib/alexandria/import_library.rb +3 -4
- data/lib/alexandria/import_library_csv.rb +2 -2
- data/lib/alexandria/library_collection.rb +1 -1
- data/lib/alexandria/library_store.rb +20 -15
- data/lib/alexandria/logging.rb +22 -21
- data/lib/alexandria/models/book.rb +1 -2
- data/lib/alexandria/models/library.rb +7 -8
- data/lib/alexandria/preferences.rb +7 -19
- data/lib/alexandria/{book_providers/pseudomarc.rb → pseudo_marc_parser.rb} +2 -2
- data/lib/alexandria/scanners/cue_cat.rb +5 -9
- data/lib/alexandria/scanners/{keyboard.rb → keyboard_wedge.rb} +3 -3
- data/lib/alexandria/scanners.rb +2 -2
- data/lib/alexandria/smart_library.rb +9 -5
- data/lib/alexandria/ui/acquire_dialog.rb +42 -45
- data/lib/alexandria/ui/alert_dialog.rb +3 -3
- data/lib/alexandria/ui/barcode_animation.rb +3 -3
- data/lib/alexandria/ui/book_properties_dialog.rb +9 -9
- data/lib/alexandria/ui/book_properties_dialog_base.rb +13 -14
- data/lib/alexandria/ui/builder_base.rb +1 -1
- data/lib/alexandria/ui/callbacks.rb +8 -7
- data/lib/alexandria/ui/confirm_erase_dialog.rb +1 -0
- data/lib/alexandria/ui/conflict_while_copying_dialog.rb +1 -0
- data/lib/alexandria/ui/export_dialog.rb +1 -0
- data/lib/alexandria/ui/{iconview.rb → icon_view_manager.rb} +1 -0
- data/lib/alexandria/ui/icons.rb +2 -2
- data/lib/alexandria/ui/iconview_tooltips.rb +1 -1
- data/lib/alexandria/ui/init.rb +10 -4
- data/lib/alexandria/ui/keep_bad_isbn_dialog.rb +1 -0
- data/lib/alexandria/ui/libraries_combo.rb +1 -0
- data/lib/alexandria/ui/listview.rb +2 -0
- data/lib/alexandria/ui/main_app.rb +3 -1
- data/lib/alexandria/ui/multi_drag_treeview.rb +0 -2
- data/lib/alexandria/ui/new_book_dialog.rb +15 -20
- data/lib/alexandria/ui/new_book_dialog_manual.rb +7 -7
- data/lib/alexandria/ui/new_provider_dialog.rb +1 -0
- data/lib/alexandria/ui/new_smart_library_dialog.rb +2 -1
- data/lib/alexandria/ui/preferences_dialog.rb +4 -4
- data/lib/alexandria/ui/provider_preferences_dialog.rb +1 -0
- data/lib/alexandria/ui/really_delete_dialog.rb +1 -0
- data/lib/alexandria/ui/sidepane_manager.rb +49 -48
- data/lib/alexandria/ui/skip_entry_dialog.rb +1 -0
- data/lib/alexandria/ui/smart_library_properties_dialog.rb +1 -0
- data/lib/alexandria/ui/smart_library_properties_dialog_base.rb +2 -1
- data/lib/alexandria/ui/{sound.rb → sound_effects_player.rb} +3 -0
- data/lib/alexandria/ui/ui_manager.rb +194 -143
- data/lib/alexandria/ui.rb +1 -0
- data/lib/alexandria/version.rb +1 -1
- data/lib/alexandria/web_themes.rb +1 -1
- data/lib/alexandria.rb +6 -5
- data/po/Makefile +1 -1
- data/po/it.po +64 -82
- data/spec/alexandria/book_providers/bl_provider_spec.rb +11 -2
- data/spec/alexandria/book_providers/douban_provider_spec.rb +17 -0
- data/spec/alexandria/book_providers/loc_provider_spec.rb +10 -2
- data/spec/alexandria/book_providers/sbn_provider_spec.rb +10 -2
- data/spec/alexandria/book_providers/thalia_provider_spec.rb +9 -1
- data/spec/alexandria/book_providers/world_cat_provider_spec.rb +30 -10
- data/spec/alexandria/book_providers/z3950_provider_spec.rb +22 -0
- data/spec/alexandria/book_spec.rb +5 -3
- data/spec/alexandria/console_spec.rb +1 -1
- data/spec/alexandria/export_library_spec.rb +65 -19
- data/spec/alexandria/library_collection_spec.rb +24 -0
- data/spec/alexandria/library_spec.rb +68 -53
- data/spec/alexandria/library_store_spec.rb +33 -1
- data/spec/alexandria/preferences_spec.rb +7 -7
- data/spec/alexandria/pseudo_marc_parser_spec.rb +71 -0
- data/spec/alexandria/scanners/cue_cat_spec.rb +11 -4
- data/spec/alexandria/scanners/keyboard_wedge_spec.rb +47 -0
- data/spec/alexandria/smart_library_spec.rb +7 -5
- data/spec/alexandria/ui/about_dialog_spec.rb +2 -2
- data/spec/alexandria/ui/acquire_dialog_spec.rb +8 -3
- data/spec/alexandria/ui/alert_dialog_spec.rb +6 -4
- data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +2 -2
- data/spec/alexandria/ui/book_properties_dialog_spec.rb +5 -5
- data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +19 -3
- data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +2 -2
- data/spec/alexandria/ui/error_dialog_spec.rb +14 -3
- data/spec/alexandria/ui/export_dialog_spec.rb +6 -6
- data/spec/alexandria/ui/{iconview_spec.rb → icon_view_manager_spec.rb} +2 -2
- data/spec/alexandria/ui/import_dialog_spec.rb +3 -3
- data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +2 -2
- data/spec/alexandria/ui/main_app_spec.rb +0 -2
- data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +5 -5
- data/spec/alexandria/ui/new_book_dialog_spec.rb +7 -4
- data/spec/alexandria/ui/new_provider_dialog_spec.rb +3 -3
- data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +9 -7
- data/spec/alexandria/ui/preferences_dialog_spec.rb +2 -2
- data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +22 -7
- data/spec/alexandria/ui/really_delete_dialog_spec.rb +2 -2
- data/spec/alexandria/ui/sidepane_manager_spec.rb +2 -2
- data/spec/alexandria/ui/skip_entry_dialog_spec.rb +19 -3
- data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +2 -2
- data/spec/alexandria/ui/ui_manager_spec.rb +7 -5
- data/spec/end_to_end/basic_run_spec.rb +2 -1
- data/spec/spec_helper.rb +26 -33
- data/tasks/setup.rb +1 -1
- data/util/rake/fileinstall.rb +12 -13
- data/util/rake/gettextgenerate.rb +1 -1
- data/util/rake/omfgenerate.rb +1 -1
- metadata +97 -64
- /data/spec/alexandria/ui/{sound_spec.rb → sound_effects_player_spec.rb} +0 -0
|
@@ -31,8 +31,8 @@ module Alexandria
|
|
|
31
31
|
@library.name
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def each(&
|
|
35
|
-
@sorted.each(&
|
|
34
|
+
def each(&)
|
|
35
|
+
@sorted.each(&)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def export_as_onix_xml_archive(filename)
|
|
@@ -52,28 +52,30 @@ module Alexandria
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def export_as_tellico_xml_archive(filename)
|
|
55
|
-
|
|
55
|
+
tmpdir = Dir.mktmpdir "tellico_export"
|
|
56
|
+
|
|
57
|
+
File.open(File.join(tmpdir, "tellico.xml"), "w") do |io|
|
|
56
58
|
to_tellico_document.write(io, 0)
|
|
57
59
|
end
|
|
58
|
-
copy_covers(File.join(
|
|
59
|
-
Dir.chdir(
|
|
60
|
+
copy_covers(File.join(tmpdir, "images"))
|
|
61
|
+
Dir.chdir(tmpdir) do
|
|
60
62
|
output = `zip -q -r \"#{filename}\" tellico.xml images 2>&1`
|
|
61
63
|
raise output unless $CHILD_STATUS.success?
|
|
62
64
|
end
|
|
63
|
-
FileUtils.rm_rf(File.join(
|
|
64
|
-
FileUtils.rm(File.join(
|
|
65
|
+
FileUtils.rm_rf(File.join(tmpdir, "images"))
|
|
66
|
+
FileUtils.rm(File.join(tmpdir, "tellico.xml"))
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
def export_as_isbn_list(filename)
|
|
68
70
|
File.open(filename, "w") do |io|
|
|
69
71
|
each do |book|
|
|
70
|
-
io.puts(
|
|
72
|
+
io.puts(book.isbn || "")
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
|
|
75
77
|
def export_as_html(filename, theme)
|
|
76
|
-
FileUtils.
|
|
78
|
+
FileUtils.mkdir_p(filename)
|
|
77
79
|
Dir.chdir(filename) do
|
|
78
80
|
copy_covers("pixmaps")
|
|
79
81
|
FileUtils.cp_r(theme.pixmaps_directory, "pixmaps") if theme.has_pixmaps?
|
|
@@ -91,7 +93,7 @@ module Alexandria
|
|
|
91
93
|
end
|
|
92
94
|
|
|
93
95
|
def export_as_ipod_notes(filename, _theme)
|
|
94
|
-
FileUtils.
|
|
96
|
+
FileUtils.mkdir_p(filename)
|
|
95
97
|
tempdir = Dir.getwd
|
|
96
98
|
Dir.chdir(filename)
|
|
97
99
|
copy_covers("pixmaps")
|
|
@@ -113,7 +115,7 @@ module Alexandria
|
|
|
113
115
|
end
|
|
114
116
|
io.puts book.authors.join(", ")
|
|
115
117
|
io.puts book.edition
|
|
116
|
-
io.puts(
|
|
118
|
+
io.puts(book.isbn || "")
|
|
117
119
|
# we need to close the files so the iPod can be ejected/unmounted
|
|
118
120
|
# without us closing Alexandria
|
|
119
121
|
io.close
|
|
@@ -140,6 +142,8 @@ module Alexandria
|
|
|
140
142
|
private
|
|
141
143
|
|
|
142
144
|
ONIX_DTD_URL = "http://www.editeur.org/onix/2.1/reference/onix-international.dtd"
|
|
145
|
+
private_constant :ONIX_DTD_URL
|
|
146
|
+
|
|
143
147
|
def to_onix_document
|
|
144
148
|
doc = REXML::Document.new
|
|
145
149
|
doc << REXML::XMLDecl.new
|
|
@@ -211,8 +215,8 @@ module Alexandria
|
|
|
211
215
|
doc = REXML::Document.new
|
|
212
216
|
doc << REXML::XMLDecl.new
|
|
213
217
|
doc << REXML::DocType.new("tellico",
|
|
214
|
-
'PUBLIC "-//Robby Stephenson/DTD Tellico V7.0//EN"' \
|
|
215
|
-
'
|
|
218
|
+
'PUBLIC "-//Robby Stephenson/DTD Tellico V7.0//EN" ' \
|
|
219
|
+
'"http://periapsis.org/tellico/dtd/v7/tellico.dtd"')
|
|
216
220
|
tellico = doc.add_element("tellico")
|
|
217
221
|
tellico.add_attribute("syntaxVersion", "7")
|
|
218
222
|
tellico.add_namespace("http://periapsis.org/tellico/")
|
|
@@ -263,10 +267,10 @@ module Alexandria
|
|
|
263
267
|
def xhtml_escape(str)
|
|
264
268
|
escaped = str.dup
|
|
265
269
|
# used to occasionally use CGI.escapeHTML
|
|
266
|
-
escaped.gsub!(
|
|
267
|
-
escaped.gsub!(
|
|
268
|
-
escaped.gsub!(
|
|
269
|
-
escaped.gsub!(
|
|
270
|
+
escaped.gsub!("&", "&")
|
|
271
|
+
escaped.gsub!("<", "<")
|
|
272
|
+
escaped.gsub!(">", ">")
|
|
273
|
+
escaped.gsub!('"', """)
|
|
270
274
|
escaped
|
|
271
275
|
end
|
|
272
276
|
|
|
@@ -357,27 +361,18 @@ module Alexandria
|
|
|
357
361
|
def to_bibtex
|
|
358
362
|
generator = "Alexandria " + Alexandria::DISPLAY_VERSION
|
|
359
363
|
bibtex = +""
|
|
360
|
-
bibtex << "
|
|
361
|
-
bibtex << "
|
|
364
|
+
bibtex << "%Generated on #{Date.today} by: #{generator}\n"
|
|
365
|
+
bibtex << "%\n"
|
|
362
366
|
bibtex << "\n"
|
|
363
367
|
|
|
364
368
|
auths = Hash.new(0)
|
|
365
369
|
each do |book|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
else
|
|
370
|
-
auths[k] = 1
|
|
371
|
-
end
|
|
372
|
-
cite_key = k + auths[k].to_s
|
|
370
|
+
auth_key = (book.authors[0] || "Anonymous").split[0]
|
|
371
|
+
auths[auth_key] += 1
|
|
372
|
+
cite_key = "#{auth_key}#{auths[auth_key]}"
|
|
373
373
|
bibtex << "@BOOK{#{cite_key},\n"
|
|
374
374
|
bibtex << 'author = "'
|
|
375
|
-
|
|
376
|
-
bibtex << book.authors[0]
|
|
377
|
-
book.authors[1..].each do |author|
|
|
378
|
-
bibtex << " and #{latex_escape(author)}"
|
|
379
|
-
end
|
|
380
|
-
end
|
|
375
|
+
bibtex << book.authors.map { latex_escape(_1) }.join(" and ")
|
|
381
376
|
bibtex << "\",\n"
|
|
382
377
|
bibtex << "title = \"#{latex_escape(book.title)}\",\n"
|
|
383
378
|
bibtex << "publisher = \"#{latex_escape(book.publisher)}\",\n"
|
|
@@ -395,14 +390,14 @@ module Alexandria
|
|
|
395
390
|
return "" if str.nil?
|
|
396
391
|
|
|
397
392
|
my_str = str.dup
|
|
398
|
-
my_str.gsub!(
|
|
399
|
-
my_str.gsub!(
|
|
400
|
-
my_str.gsub!(
|
|
401
|
-
my_str.gsub!(
|
|
402
|
-
my_str.gsub!(
|
|
403
|
-
my_str.gsub!(
|
|
404
|
-
my_str.gsub!(
|
|
405
|
-
my_str.gsub!(
|
|
393
|
+
my_str.gsub!("%", "\\%")
|
|
394
|
+
my_str.gsub!("~", "\\textasciitilde")
|
|
395
|
+
my_str.gsub!("&", "\\\\&")
|
|
396
|
+
my_str.gsub!("#", "\\\\#")
|
|
397
|
+
my_str.gsub!("{", "\\{")
|
|
398
|
+
my_str.gsub!("}", "\\}")
|
|
399
|
+
my_str.gsub!("_", "\\_")
|
|
400
|
+
my_str.gsub!("$", "\\$")
|
|
406
401
|
my_str.gsub!(/"(.+)"/, "``\1''")
|
|
407
402
|
my_str
|
|
408
403
|
end
|
|
@@ -14,6 +14,7 @@ module Alexandria
|
|
|
14
14
|
include Logging
|
|
15
15
|
include GetText
|
|
16
16
|
extend GetText
|
|
17
|
+
|
|
17
18
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
|
18
19
|
|
|
19
20
|
def self.all
|
|
@@ -71,9 +72,7 @@ module Alexandria
|
|
|
71
72
|
log.debug { "Starting import_as_tellico_xml_archive... " }
|
|
72
73
|
return nil unless system("unzip -qqt \"#{filename}\"")
|
|
73
74
|
|
|
74
|
-
tmpdir =
|
|
75
|
-
FileUtils.rm_rf(tmpdir) if File.exist?(tmpdir)
|
|
76
|
-
Dir.mkdir(tmpdir)
|
|
75
|
+
tmpdir = Dir.mktmpdir "tellico_import"
|
|
77
76
|
Dir.chdir(tmpdir) do
|
|
78
77
|
system("unzip -qq \"#{filename}\"")
|
|
79
78
|
file = File.exist?("bookcase.xml") ? "bookcase.xml" : "tellico.xml"
|
|
@@ -181,7 +180,7 @@ module Alexandria
|
|
|
181
180
|
# probably Goodreads' wonky ISBN fields ,,="043432432X",
|
|
182
181
|
# this is a hack to fix up such files
|
|
183
182
|
data = File.read(filename)
|
|
184
|
-
data.gsub!(
|
|
183
|
+
data.gsub!(',="', ',"')
|
|
185
184
|
csv_fixed = Tempfile.new("alexandria_import_csv_fixed_")
|
|
186
185
|
csv_fixed.write(data)
|
|
187
186
|
csv_fixed.close
|
|
@@ -46,7 +46,7 @@ module Alexandria
|
|
|
46
46
|
|
|
47
47
|
class GoodreadsCSVImport < CSVImport
|
|
48
48
|
def initialize(header)
|
|
49
|
-
super
|
|
49
|
+
super
|
|
50
50
|
@title = index_of("Title")
|
|
51
51
|
@author = index_of("Author")
|
|
52
52
|
@additional_authors = index_of("Additional Authors")
|
|
@@ -119,7 +119,7 @@ module Alexandria
|
|
|
119
119
|
|
|
120
120
|
class LibraryThingCSVImport < CSVImport
|
|
121
121
|
def initialize(header)
|
|
122
|
-
super
|
|
122
|
+
super
|
|
123
123
|
@title = index_of("'TITLE'")
|
|
124
124
|
@author = index_of("'AUTHOR (first, last)'")
|
|
125
125
|
@isbn = index_of("'ISBN'")
|
|
@@ -23,7 +23,7 @@ module Alexandria
|
|
|
23
23
|
ruined = []
|
|
24
24
|
deleted = []
|
|
25
25
|
all_regular_libraries.each do |library|
|
|
26
|
-
ruined
|
|
26
|
+
library.ruined_books.each { |isbn| ruined << [nil, isbn, library] }
|
|
27
27
|
# make deleted books from each library accessible so we don't crash on
|
|
28
28
|
# smart libraries
|
|
29
29
|
deleted += library.deleted_books
|
|
@@ -9,9 +9,10 @@ module Alexandria
|
|
|
9
9
|
include Logging
|
|
10
10
|
|
|
11
11
|
FIX_BIGNUM_REGEX =
|
|
12
|
-
%r{loaned_since:\s*(!ruby/object:Bignum\s*)?(\d+)\n}
|
|
12
|
+
%r{loaned_since:\s*(!ruby/object:Bignum\s*)?(\d+)\n}
|
|
13
13
|
|
|
14
14
|
include GetText
|
|
15
|
+
|
|
15
16
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
|
16
17
|
|
|
17
18
|
def initialize(dir)
|
|
@@ -32,8 +33,8 @@ module Alexandria
|
|
|
32
33
|
rescue Errno::ENOENT
|
|
33
34
|
FileUtils.mkdir_p(library_dir)
|
|
34
35
|
end
|
|
35
|
-
# Create the default library if there is no library yet.
|
|
36
36
|
|
|
37
|
+
# Create the default library if there is no library yet.
|
|
37
38
|
a << load_library(_("My Library")) if a.empty?
|
|
38
39
|
|
|
39
40
|
a
|
|
@@ -43,21 +44,13 @@ module Alexandria
|
|
|
43
44
|
test = [0, nil]
|
|
44
45
|
ruined_books = []
|
|
45
46
|
library = Library.new(name, self)
|
|
46
|
-
FileUtils.mkdir_p(library.path)
|
|
47
|
+
FileUtils.mkdir_p(library.path)
|
|
47
48
|
Dir.chdir(library.path) do
|
|
48
|
-
Dir["*" + Library::EXT[:book]].
|
|
49
|
-
test[1] = filename if
|
|
49
|
+
Dir["*" + Library::EXT[:book]].each do |filename|
|
|
50
|
+
test[1] = filename if test[0].zero?
|
|
50
51
|
|
|
51
52
|
unless File.size? test[1]
|
|
52
|
-
|
|
53
|
-
md = /([\dxX]{10,13})#{Library::EXT[:book]}/.match(filename)
|
|
54
|
-
if md
|
|
55
|
-
file_isbn = md[1]
|
|
56
|
-
ruined_books << [nil, file_isbn, library]
|
|
57
|
-
else
|
|
58
|
-
log.warn { "Filename #{filename} does not contain an ISBN" }
|
|
59
|
-
# TODO: delete this file...
|
|
60
|
-
end
|
|
53
|
+
handle_empty_book_file(test[1], ruined_books)
|
|
61
54
|
next
|
|
62
55
|
end
|
|
63
56
|
book = regularize_book_from_yaml(test[1])
|
|
@@ -180,6 +173,18 @@ module Alexandria
|
|
|
180
173
|
|
|
181
174
|
private
|
|
182
175
|
|
|
176
|
+
def handle_empty_book_file(filename, ruined_books)
|
|
177
|
+
log.warn { "Book file #{filename} was empty" }
|
|
178
|
+
md = /([\dxX]{10,13})#{Library::EXT[:book]}/.match(filename)
|
|
179
|
+
if md
|
|
180
|
+
file_isbn = md[1]
|
|
181
|
+
ruined_books << file_isbn
|
|
182
|
+
else
|
|
183
|
+
log.warn { "Filename #{filename} does not contain an ISBN" }
|
|
184
|
+
# TODO: delete this file...
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
183
188
|
def regularize_book_from_yaml(name)
|
|
184
189
|
text = File.read(name)
|
|
185
190
|
|
|
@@ -188,7 +193,7 @@ module Alexandria
|
|
|
188
193
|
|
|
189
194
|
# The string is removed on load, but can't make it stick, maybe has to do with cache
|
|
190
195
|
|
|
191
|
-
if
|
|
196
|
+
if text.include?("!str:Amazon::Search::Response")
|
|
192
197
|
log.debug { "Removing Ruby/Amazon strings from #{name}" }
|
|
193
198
|
text.gsub!("!str:Amazon::Search::Response", "")
|
|
194
199
|
end
|
data/lib/alexandria/logging.rb
CHANGED
|
@@ -37,24 +37,24 @@ module Alexandria
|
|
|
37
37
|
super(severity, message, category, &block)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def debug(source = nil, progname = nil, &
|
|
41
|
-
add(DEBUG, nil, source, progname, &
|
|
40
|
+
def debug(source = nil, progname = nil, &)
|
|
41
|
+
add(DEBUG, nil, source, progname, &)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
def info(source = nil, progname = nil, &
|
|
45
|
-
add(INFO, nil, source, progname, &
|
|
44
|
+
def info(source = nil, progname = nil, &)
|
|
45
|
+
add(INFO, nil, source, progname, &)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
def warn(source = nil, progname = nil, &
|
|
49
|
-
add(WARN, nil, source, progname, &
|
|
48
|
+
def warn(source = nil, progname = nil, &)
|
|
49
|
+
add(WARN, nil, source, progname, &)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
def error(source = nil, progname = nil, &
|
|
53
|
-
add(ERROR, nil, source, progname, &
|
|
52
|
+
def error(source = nil, progname = nil, &)
|
|
53
|
+
add(ERROR, nil, source, progname, &)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def fatal(source = nil, progname = nil, &
|
|
57
|
-
add(FATAL, nil, source, progname, &
|
|
56
|
+
def fatal(source = nil, progname = nil, &)
|
|
57
|
+
add(FATAL, nil, source, progname, &)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def self.category(source)
|
|
@@ -73,6 +73,7 @@ module Alexandria
|
|
|
73
73
|
# whithout needing to specify the source each time.
|
|
74
74
|
class LogWrapper
|
|
75
75
|
extend Forwardable
|
|
76
|
+
|
|
76
77
|
def initialize(logger, source)
|
|
77
78
|
@logger = logger
|
|
78
79
|
@source = source
|
|
@@ -90,24 +91,24 @@ module Alexandria
|
|
|
90
91
|
end
|
|
91
92
|
end
|
|
92
93
|
|
|
93
|
-
def debug(progname = nil, &
|
|
94
|
-
@logger.debug(@source, progname, &
|
|
94
|
+
def debug(progname = nil, &)
|
|
95
|
+
@logger.debug(@source, progname, &)
|
|
95
96
|
end
|
|
96
97
|
|
|
97
|
-
def info(progname = nil, &
|
|
98
|
-
@logger.info(@source, progname, &
|
|
98
|
+
def info(progname = nil, &)
|
|
99
|
+
@logger.info(@source, progname, &)
|
|
99
100
|
end
|
|
100
101
|
|
|
101
|
-
def warn(progname = nil, &
|
|
102
|
-
@logger.warn(@source, progname, &
|
|
102
|
+
def warn(progname = nil, &)
|
|
103
|
+
@logger.warn(@source, progname, &)
|
|
103
104
|
end
|
|
104
105
|
|
|
105
|
-
def error(progname = nil, &
|
|
106
|
-
@logger.error(@source, progname, &
|
|
106
|
+
def error(progname = nil, &)
|
|
107
|
+
@logger.error(@source, progname, &)
|
|
107
108
|
end
|
|
108
109
|
|
|
109
|
-
def fatal(progname = nil, &
|
|
110
|
-
@logger.fatal(@source, progname, &
|
|
110
|
+
def fatal(progname = nil, &)
|
|
111
|
+
@logger.fatal(@source, progname, &)
|
|
111
112
|
end
|
|
112
113
|
end
|
|
113
114
|
|
|
@@ -135,7 +136,7 @@ module Alexandria
|
|
|
135
136
|
def self.create_logger
|
|
136
137
|
logger = Alexandria::Logger.new($stderr)
|
|
137
138
|
|
|
138
|
-
level = ENV["LOGLEVEL"]
|
|
139
|
+
level = ENV["LOGLEVEL"]&.intern
|
|
139
140
|
if [:FATAL, :ERROR, :WARN, :INFO, :DEBUG].include? level
|
|
140
141
|
logger.level = Logger.const_get(level)
|
|
141
142
|
else
|
|
@@ -15,11 +15,10 @@ module Alexandria
|
|
|
15
15
|
|
|
16
16
|
DEFAULT_RATING = 0
|
|
17
17
|
MAX_RATING_STARS = 5
|
|
18
|
-
VALID_RATINGS = (DEFAULT_RATING..MAX_RATING_STARS)
|
|
18
|
+
VALID_RATINGS = (DEFAULT_RATING..MAX_RATING_STARS)
|
|
19
19
|
|
|
20
20
|
def initialize(title, authors, isbn, publisher, publishing_year,
|
|
21
21
|
edition)
|
|
22
|
-
|
|
23
22
|
@title = title
|
|
24
23
|
@authors = authors
|
|
25
24
|
@isbn = isbn
|
|
@@ -20,11 +20,12 @@ module Alexandria
|
|
|
20
20
|
attr_reader :name
|
|
21
21
|
attr_accessor :ruined_books, :updating, :deleted_books
|
|
22
22
|
|
|
23
|
-
DEFAULT_DIR = File.join(
|
|
23
|
+
DEFAULT_DIR = File.join(Dir.home, ".alexandria")
|
|
24
24
|
EXT = { book: ".yaml", cover: ".cover" }.freeze
|
|
25
25
|
|
|
26
26
|
include GetText
|
|
27
27
|
extend GetText
|
|
28
|
+
|
|
28
29
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
|
29
30
|
|
|
30
31
|
BOOK_ADDED, BOOK_UPDATED, BOOK_REMOVED = (0..3).to_a
|
|
@@ -117,8 +118,7 @@ module Alexandria
|
|
|
117
118
|
|
|
118
119
|
def self.valid_upc?(upc)
|
|
119
120
|
numbers = extract_numbers(upc)
|
|
120
|
-
|
|
121
|
-
(upc_checksum(numbers[0..10]) == numbers[11]))
|
|
121
|
+
numbers.length == 17 && upc_checksum(numbers[0..10]) == numbers[11]
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
AMERICAN_UPC_LOOKUP = {
|
|
@@ -167,7 +167,7 @@ module Alexandria
|
|
|
167
167
|
elsif valid_upc?(isbn)
|
|
168
168
|
# Seems to be a valid UPC number.
|
|
169
169
|
prefix = upc_convert(numbers[0..5])
|
|
170
|
-
isbn_sans_chcksm = prefix + numbers[(
|
|
170
|
+
isbn_sans_chcksm = prefix + numbers[(prefix.length + 8)..17]
|
|
171
171
|
isbn_sans_chcksm + [isbn_checksum(isbn_sans_chcksm)]
|
|
172
172
|
elsif valid_isbn?(isbn)
|
|
173
173
|
# Seems to be a valid ISBN number.
|
|
@@ -216,8 +216,7 @@ module Alexandria
|
|
|
216
216
|
book.saved_ident = book.ident
|
|
217
217
|
end
|
|
218
218
|
# #was File.exist? but that returns true for empty files... CathalMagus
|
|
219
|
-
already_there =
|
|
220
|
-
!@deleted_books.include?(book))
|
|
219
|
+
already_there = File.size?(yaml(book)) && !@deleted_books.include?(book)
|
|
221
220
|
|
|
222
221
|
temp_book = book.dup
|
|
223
222
|
temp_book.library = nil
|
|
@@ -379,7 +378,7 @@ module Alexandria
|
|
|
379
378
|
end
|
|
380
379
|
|
|
381
380
|
def copy_covers(somewhere)
|
|
382
|
-
FileUtils.rm_rf(somewhere)
|
|
381
|
+
FileUtils.rm_rf(somewhere)
|
|
383
382
|
FileUtils.mkdir(somewhere)
|
|
384
383
|
each do |book|
|
|
385
384
|
next unless File.exist?(cover(book))
|
|
@@ -398,7 +397,7 @@ module Alexandria
|
|
|
398
397
|
book.ident + (Library.jpeg?(cover(book)) ? ".jpg" : ".gif")
|
|
399
398
|
end
|
|
400
399
|
|
|
401
|
-
|
|
400
|
+
private
|
|
402
401
|
|
|
403
402
|
def initialize(name, store = nil)
|
|
404
403
|
@name = name
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
# See the file README.md for authorship and licensing information.
|
|
6
6
|
|
|
7
7
|
require "singleton"
|
|
8
|
-
require "set"
|
|
9
8
|
require "alexandria/default_preferences"
|
|
10
9
|
|
|
11
10
|
module Alexandria
|
|
@@ -64,7 +63,7 @@ module Alexandria
|
|
|
64
63
|
|
|
65
64
|
DEFAULT_VALUES.each_key do |var|
|
|
66
65
|
define_method(var) { generic_getter var }
|
|
67
|
-
define_method("#{var}=") { |val| generic_setter var, val }
|
|
66
|
+
define_method(:"#{var}=") { |val| generic_setter var, val }
|
|
68
67
|
end
|
|
69
68
|
|
|
70
69
|
def get_variable(variable_name)
|
|
@@ -120,13 +119,6 @@ module Alexandria
|
|
|
120
119
|
# set non-list value
|
|
121
120
|
exec_gconf_set(var_path, new_value)
|
|
122
121
|
end
|
|
123
|
-
rescue StandardError => ex
|
|
124
|
-
log.debug { new_value.inspect }
|
|
125
|
-
log.error do
|
|
126
|
-
"Could not set GConf setting #{variable_name} to value: #{new_value.inspect}"
|
|
127
|
-
end
|
|
128
|
-
log << ex.message
|
|
129
|
-
log << ex
|
|
130
122
|
end
|
|
131
123
|
|
|
132
124
|
##
|
|
@@ -161,13 +153,13 @@ module Alexandria
|
|
|
161
153
|
end
|
|
162
154
|
|
|
163
155
|
def make_list_string(list)
|
|
164
|
-
list.map! { |x| x.gsub(
|
|
156
|
+
list.map! { |x| x.gsub('"', '\\"') } if get_gconf_type(list.first) == "string"
|
|
165
157
|
contents = list.join(",")
|
|
166
158
|
"[" + contents + "]"
|
|
167
159
|
end
|
|
168
160
|
|
|
169
161
|
def exec_gconf_set(var_path, new_value)
|
|
170
|
-
if
|
|
162
|
+
if var_path.include?("cols_width")
|
|
171
163
|
log.debug { new_value }
|
|
172
164
|
|
|
173
165
|
# new_value = {}
|
|
@@ -175,10 +167,10 @@ module Alexandria
|
|
|
175
167
|
type = get_gconf_type(new_value)
|
|
176
168
|
value_str = new_value
|
|
177
169
|
if new_value.is_a? String
|
|
178
|
-
new_value = new_value.gsub(
|
|
170
|
+
new_value = new_value.gsub('"', '\\"')
|
|
179
171
|
value_str = "\"#{new_value}\""
|
|
180
172
|
end
|
|
181
|
-
log.debug { value_str } if
|
|
173
|
+
log.debug { value_str } if var_path.include?("cols_width")
|
|
182
174
|
`gconftool-2 --type #{type} --set #{var_path} #{value_str}`
|
|
183
175
|
end
|
|
184
176
|
|
|
@@ -245,12 +237,8 @@ module Alexandria
|
|
|
245
237
|
when /^\[(.*)\]$/ # list (assume of type String)
|
|
246
238
|
Regexp.last_match[1].split(",")
|
|
247
239
|
when /^\((.*)\)$/ # pair (assume of type int)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
[discriminate(pair.first), discriminate(pair.last)]
|
|
251
|
-
rescue StandardError
|
|
252
|
-
[0, 0]
|
|
253
|
-
end
|
|
240
|
+
pair = Regexp.last_match[1].split(",")
|
|
241
|
+
[discriminate(pair.first), discriminate(pair.last)]
|
|
254
242
|
else
|
|
255
243
|
value # string
|
|
256
244
|
end
|
|
@@ -43,7 +43,7 @@ module Alexandria
|
|
|
43
43
|
|
|
44
44
|
def self.get_fields(data, type, stripping, mappings = USMARC_MAPPINGS)
|
|
45
45
|
field = ""
|
|
46
|
-
mappings[type][1..mappings[type].length - 1].each do |part|
|
|
46
|
+
mappings[type][1..(mappings[type].length - 1)].each do |part|
|
|
47
47
|
if data.first[part]
|
|
48
48
|
part_data = data.first[part].strip
|
|
49
49
|
if part_data =~ stripping
|
|
@@ -124,7 +124,7 @@ module Alexandria
|
|
|
124
124
|
|
|
125
125
|
def self.marc_text_to_details(marc)
|
|
126
126
|
details = {}
|
|
127
|
-
marc
|
|
127
|
+
marc&.each_line do |line|
|
|
128
128
|
if line =~ /(\d+)\s*(.+)/
|
|
129
129
|
code = Regexp.last_match[1]
|
|
130
130
|
data = Regexp.last_match[2]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Copyright (C) 2005-2006 Christopher Cyll
|
|
4
|
-
# Copyright (C) 2011 Matijs van Zuijlen
|
|
4
|
+
# Copyright (C) 2011, 2014-2022 Matijs van Zuijlen
|
|
5
5
|
#
|
|
6
6
|
# Alexandria is free software; you can redistribute it and/or
|
|
7
7
|
# modify it under the terms of the GNU General Public License as
|
|
@@ -60,14 +60,10 @@ module Alexandria
|
|
|
60
60
|
code = code[0, 13]
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
type = "IBN"
|
|
68
|
-
end
|
|
69
|
-
rescue StandardError
|
|
70
|
-
log.debug { "Cannot translate UPC (#{type}) code #{code} to ISBN" }
|
|
63
|
+
if Library.valid_upc? code
|
|
64
|
+
isbn13 = Library.canonicalise_ean(code)
|
|
65
|
+
code = isbn13
|
|
66
|
+
type = "IBN"
|
|
71
67
|
end
|
|
72
68
|
|
|
73
69
|
return code if type == "IBN"
|
|
@@ -36,13 +36,13 @@ module Alexandria
|
|
|
36
36
|
|
|
37
37
|
# Checks if data looks like a completed scan
|
|
38
38
|
def match?(data)
|
|
39
|
-
data.gsub
|
|
40
|
-
(
|
|
39
|
+
data = data.gsub(/\s/, "")
|
|
40
|
+
data.match?(/^[0-9]{13,18}$/) || data.match?(/^[0-9]{9}[0-9Xx]$/)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# Gets the essential 13-digits from an ISBN barcode (EAN-13)
|
|
44
44
|
def decode(data)
|
|
45
|
-
data.gsub
|
|
45
|
+
data = data.gsub(/\s/, "")
|
|
46
46
|
if data.length == 10
|
|
47
47
|
data
|
|
48
48
|
elsif data.length >= 13
|
data/lib/alexandria/scanners.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Alexandria
|
|
|
12
12
|
include Logging
|
|
13
13
|
include GetText
|
|
14
14
|
extend GetText
|
|
15
|
+
|
|
15
16
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
|
16
17
|
|
|
17
18
|
ALL_RULES = 1
|
|
@@ -148,7 +149,7 @@ module Alexandria
|
|
|
148
149
|
if book
|
|
149
150
|
@cache[book].save(book)
|
|
150
151
|
else
|
|
151
|
-
FileUtils.mkdir_p(base_dir)
|
|
152
|
+
FileUtils.mkdir_p(base_dir)
|
|
152
153
|
File.open(yaml, "w") { |io| io.puts to_hash.to_yaml }
|
|
153
154
|
end
|
|
154
155
|
end
|
|
@@ -162,7 +163,7 @@ module Alexandria
|
|
|
162
163
|
end
|
|
163
164
|
|
|
164
165
|
def copy_covers(somewhere)
|
|
165
|
-
FileUtils.rm_rf(somewhere)
|
|
166
|
+
FileUtils.rm_rf(somewhere)
|
|
166
167
|
FileUtils.mkdir(somewhere)
|
|
167
168
|
each do |book|
|
|
168
169
|
library = @cache[book]
|
|
@@ -233,6 +234,7 @@ module Alexandria
|
|
|
233
234
|
class Rule
|
|
234
235
|
include GetText
|
|
235
236
|
extend GetText
|
|
237
|
+
|
|
236
238
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
|
237
239
|
|
|
238
240
|
attr_accessor :operand, :operation, :value
|
|
@@ -273,8 +275,8 @@ module Alexandria
|
|
|
273
275
|
class LeftOperand < Operand
|
|
274
276
|
attr_accessor :book_selector
|
|
275
277
|
|
|
276
|
-
def initialize(book_selector, *
|
|
277
|
-
super(*
|
|
278
|
+
def initialize(book_selector, *)
|
|
279
|
+
super(*)
|
|
278
280
|
@book_selector = book_selector
|
|
279
281
|
end
|
|
280
282
|
end
|
|
@@ -289,6 +291,7 @@ module Alexandria
|
|
|
289
291
|
module Operands
|
|
290
292
|
include GetText
|
|
291
293
|
extend GetText
|
|
294
|
+
|
|
292
295
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
|
293
296
|
|
|
294
297
|
LEFT = [
|
|
@@ -321,6 +324,7 @@ module Alexandria
|
|
|
321
324
|
include Logging
|
|
322
325
|
include GetText
|
|
323
326
|
extend GetText
|
|
327
|
+
|
|
324
328
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
|
325
329
|
|
|
326
330
|
IS_TRUE = Operator.new(:is_true,
|
|
@@ -328,7 +332,7 @@ module Alexandria
|
|
|
328
332
|
proc { |x| x })
|
|
329
333
|
IS_NOT_TRUE = Operator.new(:is_not_true,
|
|
330
334
|
_("is not set"),
|
|
331
|
-
proc
|
|
335
|
+
proc(&:!))
|
|
332
336
|
IS = Operator.new(:is,
|
|
333
337
|
_("is"),
|
|
334
338
|
proc { |x, y| x == y })
|