Stores book metadata to PostgreSQL database.
[quanlib.git] / store.rb
1
2 require 'fileutils'
3 require 'pg'
4
5 class Store
6   def initialize
7     @basepath = '/home/chris/prog/quanlib/efs'  # TODO: FIXME: configure this in a sane way
8     @conn = nil
9
10     #@dburl = 'dbi:Pg:quanlib:localhost'
11     @dbhost = "localhost"
12     @dbport = 5432
13     @dbname = 'quanlib'
14     @dbuser = 'quanlib'
15     @dbpass = 'quanlib'
16   end
17
18   def connect
19     # @conn = PGconn.connect('localhost', 5432, '', '', 'quanlib', 'quanlib', 'quanlib')
20     @conn = PG.connect('localhost', 5432, '', '', 'quanlib', 'quanlib', 'quanlib')
21     return @conn
22   end
23
24   def disconnect
25     @conn.close()
26   end
27
28   def construct_efs_path(efs_id)
29     id_str = sprintf('%010d', efs_id)
30     path = sprintf('%s/%s/%s/%s', id_str[0,2], id_str[2,2], id_str[4,2], id_str[6,2])
31     name = id_str + '.dat'
32     return path, name
33   end
34
35   def create_schema
36     create_authors = 
37 <<EOS
38       CREATE TABLE Authors (
39         id          SERIAL PRIMARY KEY,
40         grouping    VARCHAR(64),
41         reading     VARCHAR(128),
42         sort        VARCHAR(128)
43       );
44 EOS
45
46     create_books = 
47 <<EOS
48       CREATE TABLE Books (
49         id          SERIAL PRIMARY KEY,
50         author      INTEGER REFERENCES Authors(id),
51         cover       INTEGER,
52         description TEXT,
53         path        VARCHAR(256),
54         series      VARCHAR(128),
55         title       VARCHAR(196),
56         volume      VARCHAR(16)
57       );
58 EOS
59
60     create_efs = 
61 <<EOS
62       CREATE TABLE EFS (
63         id          INTEGER,
64         mimetype    VARCHAR(64)
65       );
66 EOS
67
68     stmts = [
69       create_authors,
70       create_books,
71       create_efs,
72       'CREATE SEQUENCE efs_id;'
73     ]
74
75     for stmt in stmts
76       @conn.exec(stmt)
77     end
78   end
79
80   def dropSchema
81     stmts = [
82       'DROP TABLE Books;',
83       'DROP TABLE Authors;',
84       'DROP TABLE EFS;',
85       'DROP SEQUENCE efs_id;'
86     ]
87
88     for stmt in stmts do
89       @conn.exec(stmt)
90     end
91   end
92
93   def init_db
94     sql = "SELECT 1 FROM pg_tables WHERE tableowner='quanlib' AND tablename='books'"
95     found = false
96     @conn.exec(sql).each do |row|
97       found = true
98     end
99
100     if ! found
101       create_schema()
102     end
103   end
104
105   def find_author(author)
106     sqlSelect = "SELECT id FROM Authors WHERE grouping=$1 AND reading=$2 AND sort=$3;"
107     args = [author.grouping, author.reading_order, author.sort_order]
108     @conn.exec_params(sqlSelect, args) do |rs|
109       if rs.ntuples > 0
110         return rs[0]['id']
111       end
112     end
113     return nil
114   end
115
116   def store_author(author)
117     id = find_author(author)
118     if nil == id
119       sqlInsert = "INSERT INTO Authors(grouping, reading, sort) VALUES ($1, $2, $3);"
120       args = [author.grouping, author.reading_order, author.sort_order]
121       begin 
122         rs = @conn.exec_params(sqlInsert, args)
123       rescue Exception => e
124         puts sqlInsert + ":  " + args.inspect()
125         puts e.message
126         puts $@
127       ensure
128         rs.clear if rs
129       end
130     end
131     return find_author(author)
132   end
133
134   def store_book(book)
135     sql = "INSERT INTO Books (author, cover, description, path, series, title, volume) VALUES ($1, $2, $3, $4, $5, $6, $7);"
136
137     author_id = store_author(book.author)
138     (efs_id, mime_type) = store_cover(book)
139
140     args = [author_id, efs_id, book.description(), book.path(), book.series(), book.title(), book.volume()]
141
142     begin
143       rs = @conn.exec_params(sql, args)
144     rescue Exception => e
145       puts sql + ": " + args.inspect()
146       puts e.message 
147       puts $@
148     ensure
149       rs.clear if rs
150     end
151   end
152
153   def store_cover(book)
154     efs_id = nil
155     cover = book.cover()
156
157     if nil == cover
158       return nil
159     end
160
161     @conn.exec("SELECT nextval('efs_id')") do |rs|
162       efs_id = rs[0]['nextval']
163     end
164
165     if nil == efs_id
166       return nil
167     end
168
169     (efspath, efsname) = construct_efs_path(efs_id)
170
171     efspath = @basepath + '/' + efspath
172
173     FileUtils.mkdir_p(efspath)
174
175     (filepath, mimetype) = cover.writeImage(efspath, efsname)
176
177     sql = "INSERT INTO efs VALUES ($1, $2)"
178     begin
179       rs = @conn.exec_params(sql, [efs_id, mimetype])
180     rescue Exception => e
181       puts e.message
182       puts $@
183     ensure
184       rs.clear if rs
185     end
186     
187     return efs_id, mimetype
188   end
189 end
190