From: Chris Jaekl Date: Wed, 31 May 2017 14:04:56 +0000 (+0900) Subject: Refactor page generations, and add hard-coded series naming. X-Git-Url: https://jaekl.net/gitweb/?a=commitdiff_plain;h=b31122c5ed23e2e77b527c0979b6355e9e3dda1f;p=quanlib.git Refactor page generations, and add hard-coded series naming. --- diff --git a/book.rb b/book.rb index 03a5f25..f1481f0 100644 --- a/book.rb +++ b/book.rb @@ -4,16 +4,18 @@ require 'zip' require 'author' require 'cover' +require 'store' class Book @@DC_NS_URL = 'http://purl.org/dc/elements/1.1/' - def initialize + def initialize(store) @author = nil @cover = nil @description = nil @path = nil - @series = nil + @series_id = nil + @store = store @title = nil @volume = nil end @@ -42,14 +44,26 @@ class Book return @author end + def author=(value) + @author = value + end + def cover return @cover end + def cover=(value) + @cover = value + end + def description @description end + def description=(value) + @description = value + end + def heading result = [] @@ -63,8 +77,9 @@ class Book end seriesInfo = [] - if nil != @series - seriesInfo.push(@series.to_s) + series = @store.load_series(@series_id) + if nil != series + seriesInfo.push(series.to_s) end if nil != @volume seriesInfo.push(@volume.to_s) @@ -81,8 +96,8 @@ class Book if nil != @author data.push('author="' + @author.inspect + '"') end - if nil != @series - data.push('series="' + @series + '"') + if nil != @series_id + data.push('series_id="' + @series_id.to_s() + '"') end if nil != @volume data.push('volume="' + @volume + '"') @@ -103,8 +118,16 @@ class Book @path end - def series - @series + def path=(value) + @path = value + end + + def series_id + @series_id + end + + def series_id=(value) + @series_id = value end def to_s @@ -115,10 +138,18 @@ class Book @title end + def title=(value) + @title = value + end + def volume @volume end + def volume=(value) + @volume = value + end + protected def isUpper?(c) return /[[:upper:]]/.match(c) @@ -175,12 +206,13 @@ class Book protected def parseFileName!(fileName) parts = fileName.split('/') - (@series, @volume, @title) = processTitle(parts[-1]) + (series_code, @volume, @title) = processTitle(parts[-1]) if parts.length > 1 grouping = parts[-2] reading_order = massage_author(grouping) sort_order = nil @author = Author.new(grouping, reading_order, sort_order) + @series_id = @store.get_series(grouping, series_code) end if fileName.downcase.end_with?(".epub") @@ -274,7 +306,8 @@ class Book content = m['content'] if 'calibre:series' == name - @series = content + # TODO: Dynamically create a new series? + # @series_id = content elsif 'calibre:series-index' == name @volume = content elsif 'cover' == name @@ -286,11 +319,11 @@ class Book #--------------- # Load the cover - @cover = loadCover(zipfile, opfPath, opfDoc, coverId) + @cover = load_cover(zipfile, opfPath, opfDoc, coverId) end protected - def loadCover(zipfile, opfPath, opfDoc, coverId) + def load_cover(zipfile, opfPath, opfDoc, coverId) coverFile = nil if nil == coverId coverId = "cover-image" diff --git a/cover.rb b/cover.rb index b00d002..5cc80a0 100644 --- a/cover.rb +++ b/cover.rb @@ -32,8 +32,7 @@ class Cover return inspect end - def write_image(outputDir, baseName) - filename = baseName + getExt() + def write_image(outputDir, filename) open(outputDir + '/' + filename, 'wb') do |fd| fd.write(@data) end diff --git a/main.rb b/main.rb index 596e11f..d193c22 100644 --- a/main.rb +++ b/main.rb @@ -1,9 +1,10 @@ +require 'page' require 'store' require 'walkdir' outputDir = 'output' -books = [] +book_ids = [] imageCount = 0 def handleArg(arg) @@ -28,65 +29,41 @@ for arg in ARGV if ! arg.start_with?("--") puts 'Scanning directory "' + arg + '"...' w = WalkDir.new(@store, arg) - books += (w.books) + book_ids += (w.books) end end puts 'Creating output...' -if ! Dir.exist?(outputDir) - Dir.mkdir(outputDir) -end - -open(outputDir + '/index.html', 'w') do |fd| - fd.puts '' - fd.puts ' ' - fd.puts ' ' - fd.puts ' Books' - fd.puts ' ' - fd.puts ' ' - fd.puts ' ' - - for book in books - image = nil - if nil != book.cover - imageCount += 1 - (path, mimeType) = book.cover.write_image(outputDir, 'image' + imageCount.to_s) - image = '' - else - image = '(No cover image)' - end +counts = {} - fd.puts '
' - fd.puts ' ' +('A'..'Z').each do |letter| + book_ids = @store.query_books_by_author(letter + '%') + puts 'Authors starting with "' + letter + '": ' + book_ids.length.to_s() + ' books.' + counts[letter] = book_ids.length - heading = book.heading() - description = book.description() - if nil != description - fd.puts ' ' - else - fd.puts ' ' - end - - fd.puts '
' + image + '' + heading + '

' + heading + '

' + description + '

' + heading + '
' + page = Page.new(@store) + if 'A' != letter + page.back = ['../output_' + (letter.ord - 1).chr + '/index.html', 'Prev'] end - - fd.puts " " - fd.puts " " - fd.puts "" + if 'Z' != letter + page.forward = ['../output_' + (letter.ord + 1).chr + '/index.html', 'Next'] + end + page.output_dir = 'output_' + letter + page.title = "Authors starting with '" + letter + "'" + page.up = ['../output/index.html', 'Index'] + + page.write_html(book_ids) end +content = '' +('A'..'Z').each do |letter| + content += ' ' +end +page = Page.new(@store) +page.output_dir = 'output' +page.special = content +page.write_html( [] ) + @store.disconnect() + diff --git a/page.rb b/page.rb new file mode 100644 index 0000000..2e5d735 --- /dev/null +++ b/page.rb @@ -0,0 +1,158 @@ +require 'store' + + +class Page + def initialize(store) + @back = nil + @forward = nil + @output_dir = 'output' + @special = nil + @store = store + @title = 'Books' + @up = nil + end + + def back=(value) + @back = value + end + + def forward=(value) + @forward = value + end + + def navig_link(data) + if (nil == data) + return '' + end + return '' + data[1] + '' + end + + def output_dir=(value) + @output_dir = value + end + + def special=(value) + @special = value + end + + def title=(value) + @title = value + end + + def up=(value) + @up = value + end + + def write_books(fd, book_ids) + for id in book_ids + book = @store.load_book(id) + image = nil + if nil != book.cover + @imageCount += 1 + (path, mimeType) = book.cover.write_image(@output_dir, 'image' + @imageCount.to_s) + image = '' + else + image = '(No cover image)' + end + + fd.puts '
AuthorBooks
Starting with ' + letter + '' + counts[letter].to_s + '
' + fd.puts ' ' + + heading = book.heading() + description = book.description() + if nil != description + fd.puts ' ' + else + fd.puts ' ' + end + + fd.puts '
' + image + '' + heading + '

' + heading + '

' + description + '

' + heading + '
' + end + end + + def write_footer(fd) + fd.puts ' ' + end + + def write_header(fd) + fd.puts '

' + @title + '

' + + fd.puts ' ' + end + + def write_html(book_ids) + @imageCount = 0 + + if ! Dir.exist?(@output_dir) + Dir.mkdir(@output_dir) + end + + open(@output_dir + '/index.html', 'w') do |fd| + fd.puts '' + fd.puts ' ' + fd.puts ' ' + fd.puts ' ' + @title + '' + + write_style_sheet(fd) + + fd.puts ' ' + fd.puts ' ' + + write_header(fd) + + write_special(fd) + write_books(fd, book_ids) + + write_footer(fd) + + fd.puts " " + fd.puts "" + end + end + + def write_special(fd) + if (nil != @special) + fd.puts(@special) + end + end + + def write_style_sheet(fd) + style = +< + div { + display: inline-block; + width: 400px; + margin: 10px; + border 3px solid #73ad21; + } + h1.header { + background: #4040a0; + color: #ffffff; + text-align: center; + } + img.cover-thumb { max-height: 200px; max-width: 200px; } + p.navigator { } + span.popup { } + span.popup:hover { text-decoration: none; background: #cfffff; z-index: 6; } + span.popup span.pop-inner { + border-color:black; + border-style:solid; + border-width:1px; + display: none; + margin: 4px 0 0 0px; + padding: 3px 3px 3px 3px; + position: absolute; + } + span.popup:hover span.pop-inner { + background: #ffffaf; + display: block; + margin: 20px 0 0 0px; + z-index:6; + } + +EOS + fd.puts style + end +end + diff --git a/store.rb b/store.rb index 645224a..946c43c 100644 --- a/store.rb +++ b/store.rb @@ -1,10 +1,11 @@ +require 'csv' require 'fileutils' require 'pg' class Store def initialize - @basepath = '/home/chris/prog/quanlib/efs' # TODO: FIXME: configure this in a sane way + @basepath = '/arc/quanlib' # TODO: FIXME: configure this in a sane way @conn = nil #@dburl = 'dbi:Pg:quanlib:localhost' @@ -36,7 +37,7 @@ class Store create_authors = < 0 + return rs[0]['id'] + end + end + return nil + end + def init_db sql = "SELECT 1 FROM pg_tables WHERE tableowner='quanlib' AND tablename='books'" found = false @@ -102,18 +136,8 @@ EOS end end - def find_author(author) - sqlSelect = "SELECT id FROM Authors WHERE grouping=$1 AND reading=$2 AND sort=$3;" - args = [author.grouping, author.reading_order, author.sort_order] - @conn.exec_params(sqlSelect, args) do |rs| - if rs.ntuples > 0 - return rs[0]['id'] - end - end - return nil - end - def load_author(id) + #puts 'DEBUG: load_author(' + id + ')' sqlSelect = "SELECT grouping, reading, sort FROM Authors WHERE id=$1" args = [id] @conn.exec_params(sqlSelect, args) do |rs| @@ -121,15 +145,20 @@ EOS raise "Expected 1 row for " + id + " but got " + rs.ntuples + ": " + sqlSelect end row = rs[0] - return Author.new(row['grouping'], row['reading'], row['sort']) + author = Author.new(row['grouping'], row['reading'], row['sort']) + #puts 'DEBUG: author: ' + author.inspect() + return author end + #puts 'DEBUG: NOT FOUND' + return nil end def store_author(author) id = find_author(author) if nil == id - sqlInsert = "INSERT INTO Authors(grouping, reading, sort) VALUES ($1, $2, $3);" - args = [author.grouping, author.reading_order, author.sort_order] + id = next_id('author_id') + sqlInsert = "INSERT INTO Authors(id, grouping, reading, sort) VALUES ($1, $2, $3, $4);" + args = [id, author.grouping, author.reading_order, author.sort_order] begin rs = @conn.exec_params(sqlInsert, args) rescue Exception => e @@ -140,10 +169,11 @@ EOS rs.clear if rs end end - return find_author(author) + return id end def load_book(id) + #puts 'DEBUG: load_book(' + id + ')' sql = "SELECT author, cover, description, path, series, title, volume FROM Books WHERE id=$1;" book = nil @@ -155,30 +185,34 @@ EOS end row = rs[0] - book = Book.new() + book = Book.new(self) book.author = load_author(row['author']) book.cover = load_cover(row['cover']) book.description = row['description'] book.path = row['path'] - book.series = row['series'] + book.series_id = row['series'] book.title = row['title'] book.volume = row['volume'] end rescue Exception => e puts sql + ": " + id puts e.message + puts $@ end + #puts 'DEBUG: loaded book: ' + book.inspect() return book end def store_book(book) - sql = "INSERT INTO Books (author, cover, description, path, series, title, volume) VALUES ($1, $2, $3, $4, $5, $6, $7);" + sql = "INSERT INTO Books (id, author, cover, description, path, series, title, volume) VALUES ($1, $2, $3, $4, $5, $6, $7, $8);" + + book_id = next_id('book_id') author_id = store_author(book.author) (efs_id, mime_type) = store_cover(book) - args = [author_id, efs_id, book.description(), book.path(), book.series(), book.title(), book.volume()] + args = [book_id, author_id, efs_id, book.description(), book.path(), book.series_id(), book.title(), book.volume()] begin rs = @conn.exec_params(sql, args) @@ -189,14 +223,32 @@ EOS ensure rs.clear if rs end + + return book_id end def load_cover(id) + if nil == id + return nil + end + + mime_type = 'application/octet-stream' + + sql = "SELECT mimeType FROM Efs WHERE id=$1" + @conn.exec_params(sql, [id]) do |rs| + if rs.ntuples != 1 + raise "Expected one row but got " + rs.ntuples + ": " + sql + ": " + id + end + mime_type = rs[0]['mimeType'] + end + (efspath, efsname) = construct_efs_path(id) - efspath = @basepath + '/' + efspath - cover = Cover.new() - cover.load_image(efspath + '/' + efsname) - return cover + + File.open(@basepath + '/efs/' + efspath + '/' + efsname, 'rb') do |is| + return Cover.new(is, efsname, mime_type) + end + + return nil end def store_cover(book) @@ -217,7 +269,7 @@ EOS (efspath, efsname) = construct_efs_path(efs_id) - efspath = @basepath + '/' + efspath + efspath = @basepath + '/efs/' + efspath FileUtils.mkdir_p(efspath) @@ -227,6 +279,7 @@ EOS begin rs = @conn.exec_params(sql, [efs_id, mimetype]) rescue Exception => e + puts sql + ": " + efs_id + ", " + mimetype puts e.message puts $@ ensure @@ -235,5 +288,77 @@ EOS return efs_id, mimetype end + + def next_id(seq_name) + id = nil + @conn.exec("SELECT nextval('" + seq_name + "');") do |rs| + id = rs[0]['nextval'] + end + return id + end + + def get_series(grouping, code) + if nil == code + return nil + end + + sql = "SELECT id FROM Series WHERE grouping=$1 AND code=$2;" + args = [grouping, code] + @conn.exec_params(sql, args).each do |row| + return row['id'] + end + + # TODO: Create a new series object here? + puts 'WARNING: series("' + grouping + '", "' + code + '") not found.' + return nil + end + + def load_series(id) + sql = "SELECT descr FROM Series WHERE id=$1;" + args = [id] + @conn.exec_params(sql, args) do |rs| + if rs.ntuples > 0 + return rs[0]['descr'] + end + end + return nil + end + + def populate_series_table + puts "Populating the Series table..." + CSV.foreach(@basepath + '/csv/series.csv') do |row| + id = next_id('series_id') + sqlInsert = "INSERT INTO Series (id, age, genre, grouping, code, descr) VALUES ($1, $2, $3, $4, $5, $6);" + args = [id] + row + begin + # DEBUG: puts 'SQL> ' + sqlInsert + ': ' + args.inspect() + rs = @conn.exec_params(sqlInsert, args) + rescue Exception => e + puts sqlInsert + ": " + args.inspect() + puts e.message + puts $@ + ensure + rs.clear if rs + end + end + end + + def query_books_by_author(pattern) + sql = +<