2011-10-08 12:13:32 +02:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import getopt
2011-10-08 11:06:15 +02:00
import pgdb
from fileformat import read_actionlist , write_actionlist
from util import *
2011-10-08 13:38:13 +02:00
# connection = pgdb.connect(database='oysteini_pbb2',
# user='oysteini_pbb',
# password='lio5Aide',
# host='postgres.pvv.ntnu.no');
2011-10-08 11:06:15 +02:00
2011-10-08 13:38:13 +02:00
# c = connection.cursor()
# c.execute('SELECT * from book')
# print fetchall_dict(c)
2011-10-08 12:13:32 +02:00
2011-10-08 13:38:13 +02:00
q_list_books = \
' SELECT isbn, book.id AS id, title, category, person.id AS person_id, ' \
' lastname, firstname ' \
' FROM book ' \
' LEFT JOIN bookperson ON book.isbn=bookperson.book ' \
' LEFT JOIN person ON bookperson.person=person.id '
2011-10-08 15:50:58 +02:00
q_list_persons = \
2011-10-08 15:58:32 +02:00
' SELECT person.id, person.firstname, person.lastname, ' \
' COUNT(bookperson.id) AS num_books ' \
2011-10-08 15:50:58 +02:00
' FROM person LEFT JOIN bookperson ON person.id=bookperson.person ' \
' GROUP BY person.id, person.firstname, person.lastname '
2011-10-08 15:58:32 +02:00
q_list_categories = \
' SELECT category.id, category.name, ' \
' COUNT(book.isbn) AS num_books ' \
' FROM category ' \
' LEFT JOIN book ON category.id=book.category ' \
' GROUP BY category.id, category.name '
2011-10-08 14:22:40 +02:00
q_persons_for_book = \
' SELECT person.id, lastname, firstname, relation ' \
' FROM person ' \
' INNER JOIN bookperson ON person.id=bookperson.person ' \
' WHERE bookperson.book= %(isbn)s '
2011-10-08 15:27:03 +02:00
q_books_for_person = \
' SELECT isbn, title, relation ' \
' FROM bookperson ' \
' INNER JOIN book ON bookperson.book=book.isbn ' \
' WHERE bookperson.person= %(id)s '
2011-10-08 16:35:26 +02:00
q_books_for_category = \
' SELECT isbn, title ' \
' FROM book ' \
' WHERE category= %(id)s '
2011-10-08 13:38:13 +02:00
2011-10-08 18:05:27 +02:00
q_new_person = \
' INSERT INTO person (id, lastname, firstname) ' \
2011-10-08 19:09:31 +02:00
' VALUES ( %(id)s , %(lastname)s , %(firstname)s ) '
2011-10-08 18:05:27 +02:00
q_edit_person = \
' UPDATE person ' \
' SET lastname= %(lastname)s , firstname= %(firstname)s ' \
' WHERE id= %(id)s '
q_new_book = \
' INSERT INTO book ' \
' (isbn, id, title, subtitle, category, publisher, ' \
' published_year, edition, pages, series, description) ' \
' VALUES ( %(isbn)s , %(id)s , %(title)s , %(subtitle)s , %(category)s , %(publisher)s , ' \
' %(published_year)d , %(edition)s , %(pages)d , %(series)s , %(description)s ) '
q_edit_book = \
' UPDATE book ' \
' SET isbn= %(isbn)s , id= %(id)s , title= %(title)s , ' \
' subtitle= %(subtitle)s , category= %(category)s , ' \
' publisher= %(publisher)s , published_year= %(published_year)s , ' \
' edition= %(edition)s , pages= %(pages)s , series= %(series)s , ' \
2011-10-08 19:09:31 +02:00
' description= %(description)s ' \
2011-10-08 18:05:27 +02:00
' WHERE isbn= %(isbn)s '
q_remove_bookpersons = \
' DELETE FROM bookperson WHERE book= %(isbn)s '
q_add_bookperson = \
' INSERT INTO bookperson (book, person, relation) ' \
' VALUES ( %(isbn)s , %(person_id)s , %(relation)s ) '
q_new_category = \
' INSERT INTO category (id, name) ' \
' VALUES ( %(id)s , %(name)s ) '
q_edit_category = \
' UPDATE category ' \
' SET name= %(name)s ' \
' WHERE id= %(id)s '
2011-10-08 13:38:13 +02:00
def connect_to_db ( ) :
connection = pgdb . connect ( database = ' oysteini_pbb2 ' ,
user = ' oysteini_pbb ' ,
password = ' lio5Aide ' ,
host = ' postgres.pvv.ntnu.no ' )
return connection
def get_by_id ( connection , id ) :
c = connection . cursor ( )
q_book = ' SELECT * FROM book WHERE isbn= %(id)s OR id= %(id)s '
q_person = ' SELECT * FROM person WHERE id= %(id)s '
q_cat = ' SELECT * FROM category WHERE id= %(id)s '
for ( typ , q ) in [ ( ' book ' , q_book ) ,
( ' person ' , q_person ) ,
( ' category ' , q_cat ) ] :
c . execute ( q , { ' id ' : id } )
if c . rowcount > 0 :
d = fetchone_dict ( c )
d [ ' type ' ] = typ
return d
return None
2011-10-08 14:22:40 +02:00
def list_of_dicts_to_dict ( lst , key_name , value_name ) :
res = { }
for d in lst :
if d [ key_name ] in res :
res [ d [ key_name ] ] . append ( d [ value_name ] )
else :
res [ d [ key_name ] ] = [ d [ value_name ] ]
return res
def get_persons_for_book ( connection , isbn ) :
c = connection . cursor ( )
c . execute ( q_persons_for_book , { ' isbn ' : isbn } )
return fetchall_dict ( c )
2011-10-08 15:27:03 +02:00
def get_books_for_person ( connection , person_id ) :
c = connection . cursor ( )
c . execute ( q_books_for_person , { ' id ' : person_id } )
return fetchall_dict ( c )
2011-10-08 16:35:26 +02:00
def get_books_for_category ( connection , cat_id ) :
c = connection . cursor ( )
c . execute ( q_books_for_category , { ' id ' : cat_id } )
return fetchall_dict ( c )
2011-10-08 13:38:13 +02:00
def show_book ( book ) :
s = ' '
if book [ ' id ' ] :
s + = ' book %s %s \n ' % ( book [ ' isbn ' ] , book [ ' id ' ] )
else :
s + = ' book %s \n ' % book [ ' isbn ' ]
s + = ' Title: %s \n ' % book [ ' title ' ]
if book [ ' subtitle ' ] :
s + = ' Subtitle: %s \n ' % book [ ' subtitle ' ]
s + = ' ISBN: %s \n ' % book [ ' isbn ' ]
s + = ' Persons: \n '
2011-10-08 14:22:40 +02:00
for bp in book [ ' persons_data ' ] :
s + = ' %s %s %s ( %s ) \n ' % ( bp [ ' id ' ] , bp [ ' firstname ' ] , bp [ ' lastname ' ] ,
bp [ ' relation ' ] )
if len ( book [ ' persons_data ' ] ) == 0 :
s + = ' (no persons associated with this book) \n '
2011-10-08 13:38:13 +02:00
if book [ ' series ' ] :
s + = ' Part of series: %s %s \n ' % ( book [ ' series ' ] , book [ ' series_title ' ] )
s + = ' Category: %s \n ' % book [ ' category ' ]
if book [ ' publisher ' ] :
s + = ' Publisher: %s \n ' % book [ ' publisher ' ]
if book [ ' published_year ' ] :
s + = ' Published year: %s \n ' % book [ ' published_year ' ]
if book [ ' edition ' ] :
s + = ' Edition: %s \n ' % book [ ' edition ' ]
if book [ ' pages ' ] :
s + = ' Number of pages: %s \n ' % book [ ' pages ' ]
if book [ ' description ' ] :
s + = ( ' Description: \n %s \n ' %
' \n ' . join ( map ( lambda line : ' ' + line ,
book [ ' description ' ] . split ( ' \n ' ) ) ) )
return s
def show_person ( person ) :
2011-10-08 15:27:03 +02:00
s = ' person %s \n ' % person [ ' id ' ]
s + = ' Name: %s %s \n ' % ( person [ ' firstname ' ] , person [ ' lastname ' ] )
s + = ' Books: \n '
for book in person [ ' books ' ] :
s + = ' %-13s %s ( %s ) \n ' % ( book [ ' isbn ' ] , book [ ' title ' ] , book [ ' relation ' ] )
if len ( person [ ' books ' ] ) == 0 :
s + = ' (no books by this person) \n '
return s
2011-10-08 13:38:13 +02:00
2011-10-08 16:35:26 +02:00
def show_category ( cat ) :
s = ' category %s \n ' % cat [ ' id ' ]
s + = ' Name: %s \n ' % cat [ ' name ' ]
s + = ' Books: \n '
for book in cat [ ' books ' ] :
s + = ' %-13s %s \n ' % ( book [ ' isbn ' ] , book [ ' title ' ] )
if len ( cat [ ' books ' ] ) == 0 :
s + = ' (no books) \n '
return s
2011-10-08 13:38:13 +02:00
def show ( connection , ids , commit_format = False , tmp_file = False ) :
objects = map ( lambda id : get_by_id ( connection , id ) , ids )
for i in range ( len ( ids ) ) :
2011-10-08 18:05:27 +02:00
if not objects [ i ] :
objects [ i ] = ' No object with id %s . \n ' % ids [ i ]
continue
2011-10-08 14:22:40 +02:00
typ = objects [ i ] [ ' type ' ]
if typ == ' book ' :
persons = get_persons_for_book ( connection , objects [ i ] [ ' isbn ' ] )
objects [ i ] [ ' persons ' ] = list_of_dicts_to_dict ( persons , ' relation ' , ' id ' )
objects [ i ] [ ' persons_data ' ] = persons
2011-10-08 16:35:26 +02:00
elif typ == ' person ' :
2011-10-08 15:27:03 +02:00
books = get_books_for_person ( connection , objects [ i ] [ ' id ' ] )
objects [ i ] [ ' books ' ] = books
2011-10-08 16:35:26 +02:00
elif typ == ' category ' :
books = get_books_for_category ( connection , objects [ i ] [ ' id ' ] )
objects [ i ] [ ' books ' ] = books
2011-10-08 18:05:27 +02:00
if commit_format :
2011-10-08 14:22:40 +02:00
objects [ i ] [ ' action ' ] = ' edit- %s ' % typ
2011-10-08 13:40:43 +02:00
else :
2011-10-08 13:38:13 +02:00
show_funs = { ' book ' : show_book ,
' person ' : show_person ,
' category ' : show_category }
show_fun = show_funs [ objects [ i ] [ ' type ' ] ]
objects [ i ] = show_fun ( objects [ i ] )
2011-10-08 14:22:40 +02:00
2011-10-08 13:38:13 +02:00
if commit_format :
output = write_actionlist ( objects )
else :
output = ' \n ' . join ( objects )
if tmp_file :
2011-10-08 21:53:24 +02:00
filename = write_tmpfile ( ' . ' . join ( ids ) , output )
2011-10-08 13:38:13 +02:00
print filename
return filename
else :
print output . strip ( )
def list_books ( connection ) :
c = connection . cursor ( )
c . execute ( q_list_books )
#print fetchall_dict(c)
s = ' '
last_isbn = None
for i in xrange ( c . rowcount ) :
d = fetchone_dict ( c )
if d [ ' isbn ' ] != last_isbn :
if last_isbn != None :
s + = ' \n '
s + = ' %-13s %-10s %-40s ' % ( d [ ' isbn ' ] , str_or_empty ( d [ ' id ' ] ) , d [ ' title ' ] )
if d [ ' person_id ' ] :
s + = ' ' + d [ ' person_id ' ]
else :
s + = ' , ' + d [ ' person_id ' ]
last_isbn = d [ ' isbn ' ]
print s
def list_persons ( connection ) :
2011-10-08 15:50:58 +02:00
c = connection . cursor ( )
c . execute ( q_list_persons )
for i in xrange ( c . rowcount ) :
person = fetchone_dict ( c )
print ' %-5s %-30s %d books ' % ( person [ ' id ' ] ,
person [ ' firstname ' ] + ' ' + person [ ' lastname ' ] ,
person [ ' num_books ' ] )
2011-10-08 12:13:32 +02:00
2011-10-08 13:38:13 +02:00
def list_categories ( connection ) :
2011-10-08 15:58:32 +02:00
c = connection . cursor ( )
c . execute ( q_list_categories )
for i in xrange ( c . rowcount ) :
cat = fetchone_dict ( c )
print ' %-15s %-30s %d books ' % ( cat [ ' id ' ] , cat [ ' name ' ] , cat [ ' num_books ' ] )
2011-10-08 12:13:32 +02:00
2011-10-08 13:38:13 +02:00
def list_cmd ( connection , what ) :
funs = { ' book ' : list_books ,
' person ' : list_persons ,
' category ' : list_categories }
fun = funs [ what ]
fun ( connection )
def search_book ( connection , search_strings , search_description = False ) :
2011-10-08 17:20:50 +02:00
c = connection . cursor ( )
2011-10-08 21:32:30 +02:00
where_clauses = [ ' book.title ILIKE %s OR book.subtitle ILIKE %s OR book.series ILIKE %s OR person.lastname ILIKE %s OR person.firstname ILIKE %s ' ] * len ( search_strings )
result_list = [ ]
for s in search_strings :
for i in range ( 5 ) :
result_list . append ( s )
c . execute ( ' SELECT * FROM book LEFT JOIN bookperson ON book.isbn=bookperson.book LEFT JOIN person ON person.id=bookperson.person WHERE ' + ' OR ' . join ( where_clauses ) , map ( lambda s : ' % ' + s + ' % ' , result_list ) )
2011-10-08 17:20:50 +02:00
for i in xrange ( c . rowcount ) :
2011-10-08 21:32:30 +02:00
book = fetchone_dict ( c )
print book [ ' isbn ' ] , book [ ' title ' ] , book [ ' person ' ]
2011-10-08 12:13:32 +02:00
2011-10-08 13:38:13 +02:00
def search_person ( connection , search_strings ) :
2011-10-08 21:53:24 +02:00
c = connection . cursor ( )
result_strings = [ ]
for s in search_strings :
for i in range ( 3 ) :
result_strings . append ( s )
c . execute ( ' SELECT * FROM person LEFT JOIN bookperson ON person.id=bookperson.person WHERE person.lastname ILIKE %s or person.firstname ILIKE %s OR person.id ILIKE %s ' , result_strings )
for i in xrange ( c . rowcount ) :
person = fetchone_dict ( c )
print person [ ' lastname ' ] , ' , ' , person [ ' firstname ' ] , ' \t ' , person [ ' book ' ]
2011-10-08 12:13:32 +02:00
2011-10-08 18:05:27 +02:00
def do_action ( connection , action ) :
2011-10-08 19:09:31 +02:00
print ' ACTION %s ' % action
2011-10-08 18:05:27 +02:00
c = connection . cursor ( )
queries = { ' new-person ' : q_new_person ,
' edit-person ' : q_edit_person ,
' new-book ' : q_new_book ,
' edit-book ' : q_edit_book ,
' new-category ' : q_new_category ,
' edit-category ' : q_edit_category }
action_type = action [ ' action ' ]
2011-10-08 21:53:24 +02:00
c . execute ( queries [ action_type ] , action )
2011-10-08 18:05:27 +02:00
if action_type in [ ' new-book ' , ' edit-book ' ] :
2011-10-08 19:09:31 +02:00
print ' FIXING PERSONS: REMOVING '
c . execute ( q_remove_bookpersons , { ' isbn ' : action [ ' isbn ' ] } )
print ' FIXING PERSONS: ADDING '
if action [ ' persons ' ] :
for ( relation , personlist ) in action [ ' persons ' ] . items ( ) :
for person in personlist :
c . execute ( q_add_bookperson ,
{ ' isbn ' : action [ ' isbn ' ] ,
' person_id ' : person ,
' relation ' : relation } )
2011-10-08 18:05:27 +02:00
def commit_actions ( connection , actions ) :
for action in actions :
try :
do_action ( connection , action )
except pgdb . DatabaseError , err :
print >> sys . stderr , ' Error in " %s " action: %s ' % ( action [ ' action ' ] , err )
print >> sys . stderr , ' Giving up. '
return
connection . commit ( )
2011-10-08 13:38:13 +02:00
def commit ( connection , filename = None ) :
2011-10-08 18:05:27 +02:00
if filename :
try :
f = file ( filename , ' r ' )
text = f . read ( )
f . close ( )
except IOError , e :
print ' commit: Error reading file %s : %s ' % ( filename , e )
print ' Exiting. '
sys . exit ( 1 )
else :
text = sys . stdin . read ( )
actions = read_actionlist ( text )
commit_actions ( connection , actions )
2011-10-08 12:13:32 +02:00
2011-10-08 13:38:13 +02:00
def edit ( connection , ids ) :
2011-10-08 21:53:24 +02:00
pass
2011-10-08 12:13:32 +02:00
2011-10-08 13:38:13 +02:00
def map_cmd ( connection , shelfname = None , category = None ) :
2011-10-08 12:13:32 +02:00
pass
commands = { ' show ' :
2011-10-08 13:38:13 +02:00
{ ' args ' : [ ( ' ids ' , ( 1 , None ) ) ] ,
' options ' : [ ' commit_format ' , ' tmp_file ' ] ,
' fun ' : show ,
' use_db ' : True } ,
2011-10-08 12:13:32 +02:00
' list ' :
{ ' args ' : [ ( ' what ' , ( 1 , 1 ) ) ] ,
' options ' : [ ] ,
2011-10-08 13:38:13 +02:00
' fun ' : list_cmd ,
' use_db ' : True } ,
2011-10-08 12:13:32 +02:00
' search ' :
2011-10-08 13:38:13 +02:00
{ ' args ' : [ ( ' search_strings ' , ( 1 , None ) ) ] ,
' options ' : [ ' search_description ' ] ,
' fun ' : search_book ,
' use_db ' : True } ,
2011-10-08 12:13:32 +02:00
' search-person ' :
2011-10-08 13:38:13 +02:00
{ ' args ' : [ ( ' search_strings ' , ( 1 , None ) ) ] ,
' options ' : [ ] ,
' fun ' : search_person ,
' use_db ' : True } ,
2011-10-08 12:13:32 +02:00
' commit ' :
2011-10-08 13:38:13 +02:00
{ ' args ' : [ ( ' filename ' , ( 0 , 1 ) ) ] ,
' options ' : [ ] ,
' fun ' : commit ,
' use_db ' : True } ,
2011-10-08 12:13:32 +02:00
' edit ' :
2011-10-08 13:38:13 +02:00
{ ' args ' : [ ( ' ids ' , ( 1 , None ) ) ] ,
' options ' : [ ] ,
' fun ' : edit ,
' use_db ' : True } ,
2011-10-08 12:13:32 +02:00
' map ' :
2011-10-08 13:38:13 +02:00
{ ' args ' : [ ( ' shelfname ' , ( 0 , 1 ) ) , ( ' category ' , ( 0 , 1 ) ) ] ,
' options ' : [ ] ,
' fun ' : map_cmd ,
' use_db ' : True }
2011-10-08 12:13:32 +02:00
}
flags = { ' commit_format ' :
{ ' help ' : ' output data in the format expected by the commit command ' } ,
' tmp_file ' :
{ ' help ' : ' output data to a new temporary file instead of to stdout ' } ,
' search_description ' :
{ ' help ' : ' include description field when searching ' }
}
general_options = [ ] # options applicable to all commands
class BadCommandLine ( Exception ) :
def __init__ ( self , msg ) :
Exception . __init__ ( self , ' Bad command line: ' + msg )
def check_command_args ( args , command ) :
cmd_decl = commands [ command ]
min_num_args = sum ( map ( lambda a : a [ 1 ] [ 0 ] , cmd_decl [ ' args ' ] ) )
unlimited = any ( map ( lambda a : a [ 1 ] [ 1 ] == None , cmd_decl [ ' args ' ] ) )
if not unlimited :
max_num_args = sum ( map ( lambda a : a [ 1 ] [ 1 ] , cmd_decl [ ' args ' ] ) )
if len ( args ) < min_num_args :
raise BadCommandLine ( ' Too few arguments for command %s (expects at least %d , %d given). '
% ( command , min_num_args , len ( args ) ) )
if ( not unlimited ) and ( len ( args ) > max_num_args ) :
raise BadCommandLine ( ' Too many arguments for command %s (expects at most %d , %d given). '
% ( command , max_num_args , len ( args ) ) )
def check_command_opts ( opts , command ) :
cmd_decl = commands [ command ]
for opt , val in opts :
if ( ( opt not in cmd_decl [ ' options ' ] ) and
( opt not in general_options ) ) :
raise BadCommandLine ( ' Option %s not applicable to command %s . '
% ( opt , command ) )
def assign_command_args ( args , command ) :
cmd_decl = commands [ command ]
d = { }
i = 0
for param in cmd_decl [ ' args ' ] :
pname = param [ 0 ]
pmin = param [ 1 ] [ 0 ]
pmax = param [ 1 ] [ 1 ]
if pmax == 1 :
if i < len ( args ) :
d [ pname ] = args [ i ]
i + = 1
else :
d [ pname ] = None
else :
d [ pname ] = [ ]
j = 0
while i + j < len ( args ) and ( pmax == None or j < pmax ) :
d [ pname ] . append ( args [ i + j ] )
j + = 1
i = i + j
return d
def assign_command_opts ( opts , command ) :
d = { }
for option , value in opts :
if option in flags :
d [ option ] = True
else :
d [ option ] = value
return d
def parse_cmdline ( args ) :
def getopt_option_name ( internal_name ) :
return internal_name . replace ( ' _ ' , ' - ' )
def internal_option_name ( getopt_ret_name ) :
return getopt_ret_name [ 2 : ] . replace ( ' - ' , ' _ ' )
option_names = map ( getopt_option_name , flags )
options , args = getopt . getopt ( args , ' ' , option_names )
if len ( args ) == 0 :
raise BadCommandLine ( ' No command specified. ' )
cmd_name = args [ 0 ]
if cmd_name not in commands :
raise BadCommandLine ( ' Nonexisting command %s . ' % cmd_name )
cmd_decl = commands [ cmd_name ]
cmd_args = args [ 1 : ]
cmd_opts = map ( lambda ( opt , val ) : ( internal_option_name ( opt ) , val ) ,
options )
check_command_args ( cmd_args , cmd_name )
check_command_opts ( cmd_opts , cmd_name )
return { ' command ' : cmd_name ,
' args ' : combine_dicts ( assign_command_args ( cmd_args , cmd_name ) ,
assign_command_opts ( cmd_opts , cmd_name ) ) }
def invoke_command ( command , args ) :
cmd_decl = commands [ command ]
cmd_decl [ ' fun ' ] ( * * args )
2011-10-08 13:38:13 +02:00
def main ( argv ) :
cmdline_parsed = parse_cmdline ( argv [ 1 : ] )
#print 'command line parsed to:', cmdline_parsed
command = cmdline_parsed [ ' command ' ]
args = cmdline_parsed [ ' args ' ]
if commands [ cmdline_parsed [ ' command ' ] ] [ ' use_db ' ] :
connection = connect_to_db ( )
args [ ' connection ' ] = connection
invoke_command ( command , args )
if __name__ == " __main__ " :
main ( sys . argv )