Compare commits
No commits in common. "e1e280b31fddd9ac5248f36d4af5bd46e9007232" and "09e2bc414d11c26cb75cc27aae87b9f5f8f2e3fc" have entirely different histories.
e1e280b31f
...
09e2bc414d
35
README.md
35
README.md
|
@ -1,35 +0,0 @@
|
||||||
![](./wiki/graphics/project_icon.png)
|
|
||||||
|
|
||||||
# Worblehat
|
|
||||||
|
|
||||||
Worblehat er PVVs biblioteksystem.
|
|
||||||
|
|
||||||
- [FAQ](./wiki/faq.md)
|
|
||||||
|
|
||||||
## Foreløpige planer
|
|
||||||
|
|
||||||
### Database
|
|
||||||
|
|
||||||
Vi skal ha en PostgreSQL-database, med designet gitt i [`db.txt`](./db.txt). Er designet feil, vennligst rett.
|
|
||||||
|
|
||||||
### Informasjonsinnhenting
|
|
||||||
|
|
||||||
Tenkte å bruke protokollen Z39.50 mot BIBSYS. Vi skal ha et kommandolinjegrensesnitt for å legge inn bøker. Vi skal kjøpe en strekkodeleser for å blippe inn ISBN-nummeret, og systemet skal slå opp dette i BIBSYS. Resultatene skal returneres, og vi skal velge riktig bok, som så skal bli lagt inn i databasen. Dette bør det være mulig å gjøre både brukervennlig for registrering av en bok, og raskt ved registrering av flere hundre bøker.
|
|
||||||
|
|
||||||
### Utspørring
|
|
||||||
|
|
||||||
Kommandolinjegrensesnitt, IRC-bot og/eller web-grensesnitt for å spørre databasen. IRC-boten skal ha et logikklag mellom seg selv og databasen, for å slippe så mye logikk internt. Dette laget kan også de andre grensesnittene bruke.
|
|
||||||
|
|
||||||
### Ressurser
|
|
||||||
|
|
||||||
#### Informasjon om bøkene
|
|
||||||
|
|
||||||
**Z39.50**
|
|
||||||
|
|
||||||
- https://www.bibsys.no/wps/wcm/connect/BIBSYS+Eng/Main+Menu/Search/Z39.50+Service+Description
|
|
||||||
- https://www.norzig.no/
|
|
||||||
|
|
||||||
**Klassifikasjon**
|
|
||||||
|
|
||||||
- https://en.wikipedia.org/wiki/Universal_Decimal_Classification
|
|
||||||
- https://en.wikipedia.org/wiki/Dewey_Decimal_Classification
|
|
|
@ -1,3 +0,0 @@
|
||||||
class WorblehatException(Exception):
|
|
||||||
def __init__(self, msg):
|
|
||||||
Exception.__init__(self, msg)
|
|
|
@ -1,79 +0,0 @@
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
import locale
|
|
||||||
import sys
|
|
||||||
from exc import WorblehatException
|
|
||||||
|
|
||||||
stdout_encoding = locale.getpreferredencoding()
|
|
||||||
file_encoding = 'utf-8'
|
|
||||||
|
|
||||||
output = sys.stdout
|
|
||||||
|
|
||||||
# def write_tmpfile(pfix, content, encoding='utf8'):
|
|
||||||
# file = tempfile.NamedTemporaryFile(prefix=pfix+'-', dir='/tmp', delete=False)
|
|
||||||
# file.write(content.encode(encoding))
|
|
||||||
# name = file.name
|
|
||||||
# file.close()
|
|
||||||
# return name
|
|
||||||
|
|
||||||
def open_tmpfile(prefix):
|
|
||||||
global output
|
|
||||||
if output != sys.stdout:
|
|
||||||
raise WorblehatException('open_tmpfile: Already writing to a file')
|
|
||||||
tmpfile = tempfile.NamedTemporaryFile(prefix='worblehat-%s-' % prefix,
|
|
||||||
dir='/tmp',
|
|
||||||
delete=False)
|
|
||||||
output = tmpfile
|
|
||||||
return tmpfile.name
|
|
||||||
|
|
||||||
def close_tmpfile():
|
|
||||||
global output
|
|
||||||
if output == sys.stdout:
|
|
||||||
raise WorblehatException('close_tmpfile: No file open')
|
|
||||||
output.close()
|
|
||||||
output = sys.stdout
|
|
||||||
|
|
||||||
def output_encoding():
|
|
||||||
if output == sys.stdout:
|
|
||||||
return stdout_encoding
|
|
||||||
else:
|
|
||||||
return file_encoding
|
|
||||||
|
|
||||||
def encoding_comment():
|
|
||||||
return '# -*- coding: %s -*-\n' % file_encoding
|
|
||||||
|
|
||||||
def write(data):
|
|
||||||
if type(data) == unicode:
|
|
||||||
data = data.encode(output_encoding())
|
|
||||||
output.write(data)
|
|
||||||
|
|
||||||
def write_stderr(data):
|
|
||||||
if type(data) == unicode:
|
|
||||||
data = data.encode(output_encoding())
|
|
||||||
sys.stderr.write(data)
|
|
||||||
|
|
||||||
debugging = True
|
|
||||||
def debug(msg):
|
|
||||||
if debugging:
|
|
||||||
write_stderr('DEBUG: %s\n' % msg)
|
|
||||||
|
|
||||||
def tmpfile_name():
|
|
||||||
if output == sys.stdout:
|
|
||||||
raise WorblehatException('tmpfile_name: No file open')
|
|
||||||
return output.name
|
|
||||||
|
|
||||||
class tmpfile:
|
|
||||||
def __init__(self, prefix):
|
|
||||||
self.prefix = prefix
|
|
||||||
def __enter__(self):
|
|
||||||
open_tmpfile(self.prefix)
|
|
||||||
def __exit__(self, exc_type, exc, traceback):
|
|
||||||
close_tmpfile()
|
|
||||||
|
|
||||||
def run_editor(filename):
|
|
||||||
if os.path.exists(filename):
|
|
||||||
os.system("%s %s || /usr/bin/env vi %s" %
|
|
||||||
(os.getenv("EDITOR"), filename, filename))
|
|
||||||
else:
|
|
||||||
exit("Error: %s: File does not exist!" % filename)
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import re
|
import re
|
||||||
import types
|
import types
|
||||||
from exc import WorblehatException
|
|
||||||
|
|
||||||
# The possible fields for each type of object.
|
# The possible fields for each type of object.
|
||||||
#
|
#
|
||||||
|
@ -53,13 +52,10 @@ action_fields = {
|
||||||
{ 'type': 'category',
|
{ 'type': 'category',
|
||||||
'required': ['id'] } }
|
'required': ['id'] } }
|
||||||
|
|
||||||
class CommitFormatSyntaxError(WorblehatException):
|
class CommitFormatSyntaxError(Exception):
|
||||||
def __init__(self, msg, linenr):
|
def __init__(self, msg, linenr):
|
||||||
WorblehatException.__init__(self, 'Syntax error on line %d: %s' % (linenr, msg))
|
super(CommitFormatSyntaxError, self).__init__(self, 'Syntax error on line %d: %s' %
|
||||||
|
(linenr, msg))
|
||||||
class CommitFormatError(WorblehatException):
|
|
||||||
def __init__(self, msg):
|
|
||||||
WorblehatException.__init__(self, msg)
|
|
||||||
|
|
||||||
def read_field_value_str(val):
|
def read_field_value_str(val):
|
||||||
if val.strip() == '':
|
if val.strip() == '':
|
||||||
|
@ -69,10 +65,7 @@ def read_field_value_str(val):
|
||||||
def read_field_value_int(val):
|
def read_field_value_int(val):
|
||||||
if val.strip() == '':
|
if val.strip() == '':
|
||||||
return None
|
return None
|
||||||
try:
|
return int(val.strip())
|
||||||
return int(val.strip())
|
|
||||||
except ValueError, TypeError:
|
|
||||||
raise WorblehatException('%s is not an integer' % val)
|
|
||||||
|
|
||||||
def read_field_value_dict(val):
|
def read_field_value_dict(val):
|
||||||
d = {}
|
d = {}
|
||||||
|
@ -178,27 +171,23 @@ def write_field_value_str(val):
|
||||||
lines = ''
|
lines = ''
|
||||||
if not val:
|
if not val:
|
||||||
val = ''
|
val = ''
|
||||||
|
val = unicode(val)
|
||||||
value_lines = val.split('\n')
|
value_lines = val.split('\n')
|
||||||
for l in value_lines:
|
for l in value_lines:
|
||||||
lines += ' ' + l + '\n'
|
lines += ' ' + l + '\n'
|
||||||
if len(value_lines) > 1:
|
|
||||||
lines = '\n' + lines
|
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def write_field_value_int(val):
|
|
||||||
return ' %s\n' % str(val)
|
|
||||||
|
|
||||||
def write_field_value_dict(val):
|
def write_field_value_dict(val):
|
||||||
lines = '\n'
|
lines = '\n'
|
||||||
for (key,values) in val.items():
|
for (key,values) in val.items():
|
||||||
for single_value in values:
|
for single_value in values:
|
||||||
lines += ' ' + key + ' ' + single_value + '\n'
|
lines += ' ' + key + ' ' + unicode(single_value) + '\n'
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def write_field_value_list(val):
|
def write_field_value_list(val):
|
||||||
lines = ''
|
lines = ''
|
||||||
for single_value in val:
|
for single_value in val:
|
||||||
lines += ' ' + single_value
|
lines += ' ' + unicode(single_value)
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def make_comment(s):
|
def make_comment(s):
|
||||||
|
@ -208,7 +197,7 @@ def make_comment(s):
|
||||||
def write_action(d):
|
def write_action(d):
|
||||||
if type(d) in types.StringTypes:
|
if type(d) in types.StringTypes:
|
||||||
return make_comment(d)
|
return make_comment(d)
|
||||||
lines = u''
|
lines = ''
|
||||||
if 'comment' in d:
|
if 'comment' in d:
|
||||||
lines += make_comment(d['comment'])
|
lines += make_comment(d['comment'])
|
||||||
action = d['action']
|
action = d['action']
|
||||||
|
@ -217,7 +206,7 @@ def write_action(d):
|
||||||
for field, ftype in fields[data_type]:
|
for field, ftype in fields[data_type]:
|
||||||
if field in d:
|
if field in d:
|
||||||
value_writer = {'s': write_field_value_str,
|
value_writer = {'s': write_field_value_str,
|
||||||
'i': write_field_value_int,
|
'i': write_field_value_str,
|
||||||
'd': write_field_value_dict,
|
'd': write_field_value_dict,
|
||||||
'l': write_field_value_list}[ftype]
|
'l': write_field_value_list}[ftype]
|
||||||
lines += field + ':' + value_writer(d[field])
|
lines += field + ':' + value_writer(d[field])
|
||||||
|
|
47
cli/util.py
47
cli/util.py
|
@ -1,32 +1,12 @@
|
||||||
db_encoding = 'utf8'
|
import os
|
||||||
|
import tempfile
|
||||||
def value_to_db(value):
|
|
||||||
if type(value) == unicode:
|
|
||||||
return value.encode(db_encoding)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def value_from_db(value):
|
|
||||||
if type(value) == str:
|
|
||||||
return unicode(value, db_encoding)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def execute_query(cursor, query, bindings):
|
def execute_query(cursor, query, bindings):
|
||||||
for (key, val) in bindings.items():
|
for (key, val) in bindings.items():
|
||||||
if val == None:
|
if val == None:
|
||||||
query = query.replace('%(' + key + ')d', 'NULL')
|
query = query.replace('%(' + key + ')d', 'NULL')
|
||||||
bindings = map_dict(value_to_db, bindings)
|
|
||||||
cursor.execute(query, bindings)
|
cursor.execute(query, bindings)
|
||||||
|
|
||||||
def fetchone(cursor):
|
|
||||||
a = cursor.fetchone()
|
|
||||||
if a != None:
|
|
||||||
return map(value_from_db, a)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def fetchall(cursor):
|
|
||||||
return map(lambda row: map(value_from_db, row),
|
|
||||||
cursor.fetchall())
|
|
||||||
|
|
||||||
def make_result_dict(cursor, row):
|
def make_result_dict(cursor, row):
|
||||||
d = {}
|
d = {}
|
||||||
for i in xrange(len(row)):
|
for i in xrange(len(row)):
|
||||||
|
@ -34,14 +14,14 @@ def make_result_dict(cursor, row):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def fetchone_dict(cursor):
|
def fetchone_dict(cursor):
|
||||||
row = fetchone(cursor)
|
row = cursor.fetchone()
|
||||||
if row != None:
|
if row != None:
|
||||||
return make_result_dict(cursor, row)
|
return make_result_dict(cursor, row)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def fetchall_dict(cursor):
|
def fetchall_dict(cursor):
|
||||||
return map(lambda r: make_result_dict(cursor, r),
|
return map(lambda r: make_result_dict(cursor, r),
|
||||||
fetchall(cursor))
|
cursor.fetchall())
|
||||||
|
|
||||||
def first(lst):
|
def first(lst):
|
||||||
return lst[0]
|
return lst[0]
|
||||||
|
@ -76,12 +56,6 @@ def mapcond(fun, predicate, lst):
|
||||||
return x
|
return x
|
||||||
return map(mapfun, lst)
|
return map(mapfun, lst)
|
||||||
|
|
||||||
def map_dict(fun, d):
|
|
||||||
res = {}
|
|
||||||
for key in d:
|
|
||||||
res[key] = fun(d[key])
|
|
||||||
return res
|
|
||||||
|
|
||||||
def maptup(fun, lst):
|
def maptup(fun, lst):
|
||||||
return tuple(map(fun, lst))
|
return tuple(map(fun, lst))
|
||||||
|
|
||||||
|
@ -110,3 +84,16 @@ def combine_dicts(*dicts):
|
||||||
res.update(d)
|
res.update(d)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def run_editor(filename):
|
||||||
|
if os.path.exists(filename):
|
||||||
|
os.system("%s %s || /usr/bin/env vi %s" %
|
||||||
|
(os.getenv("EDITOR"), filename, filename))
|
||||||
|
else:
|
||||||
|
exit("Error: %s: File does not exist!" % filename)
|
||||||
|
|
||||||
|
def write_tmpfile(pfix, content, encoding='utf8'):
|
||||||
|
file = tempfile.NamedTemporaryFile(prefix=pfix+'-', dir='/tmp', delete=False)
|
||||||
|
file.write(content.encode(encoding))
|
||||||
|
name = file.name
|
||||||
|
file.close()
|
||||||
|
return name
|
||||||
|
|
|
@ -7,8 +7,6 @@ import pgdb
|
||||||
from fileformat import read_actionlist, write_actionlist
|
from fileformat import read_actionlist, write_actionlist
|
||||||
from google_interface import google_suggest_book_data
|
from google_interface import google_suggest_book_data
|
||||||
from util import *
|
from util import *
|
||||||
from exc import WorblehatException
|
|
||||||
from file_io import tmpfile, tmpfile_name, write, write_stderr, debug, run_editor, encoding_comment
|
|
||||||
|
|
||||||
# connection = pgdb.connect(database='oysteini_pbb2',
|
# connection = pgdb.connect(database='oysteini_pbb2',
|
||||||
# user='oysteini_pbb',
|
# user='oysteini_pbb',
|
||||||
|
@ -59,8 +57,6 @@ q_edit_person = \
|
||||||
'UPDATE person ' \
|
'UPDATE person ' \
|
||||||
'SET lastname=%(lastname)s, firstname=%(firstname)s ' \
|
'SET lastname=%(lastname)s, firstname=%(firstname)s ' \
|
||||||
'WHERE id=%(id)s'
|
'WHERE id=%(id)s'
|
||||||
q_delete_person = \
|
|
||||||
'DELETE FROM person WHERE id=%(id)s'
|
|
||||||
q_new_book = \
|
q_new_book = \
|
||||||
'INSERT INTO book ' \
|
'INSERT INTO book ' \
|
||||||
' (isbn, id, title, subtitle, category, publisher, ' \
|
' (isbn, id, title, subtitle, category, publisher, ' \
|
||||||
|
@ -89,7 +85,7 @@ q_edit_category = \
|
||||||
'WHERE id=%(id)s'
|
'WHERE id=%(id)s'
|
||||||
q_add_bookreference = \
|
q_add_bookreference = \
|
||||||
'INSERT INTO bookreference (book, reftype, value) ' \
|
'INSERT INTO bookreference (book, reftype, value) ' \
|
||||||
'VALUES (%(isbn)s, %(reftype)s, %(value)s)'
|
'VALUES (%(isbn)s, %(reftype)s, %(value))'
|
||||||
|
|
||||||
def connect_to_db():
|
def connect_to_db():
|
||||||
connection = pgdb.connect(database='oysteini_pbb2',
|
connection = pgdb.connect(database='oysteini_pbb2',
|
||||||
|
@ -106,7 +102,7 @@ def get_by_id(connection, id):
|
||||||
for (typ,q) in [('book', q_book),
|
for (typ,q) in [('book', q_book),
|
||||||
('person', q_person),
|
('person', q_person),
|
||||||
('category', q_cat)]:
|
('category', q_cat)]:
|
||||||
execute_query(c, q, {'id': id})
|
c.execute(q, {'id': id})
|
||||||
if c.rowcount > 0:
|
if c.rowcount > 0:
|
||||||
d = fetchone_dict(c)
|
d = fetchone_dict(c)
|
||||||
d['type'] = typ
|
d['type'] = typ
|
||||||
|
@ -218,16 +214,15 @@ def show(connection, ids, commit_format=False, tmp_file=False):
|
||||||
objects[i] = show_fun(objects[i])
|
objects[i] = show_fun(objects[i])
|
||||||
|
|
||||||
if commit_format:
|
if commit_format:
|
||||||
output = encoding_comment() + write_actionlist(objects)
|
output = write_actionlist(objects)
|
||||||
else:
|
else:
|
||||||
output = '\n'.join(objects)
|
output = '\n'.join(objects)
|
||||||
if tmp_file:
|
if tmp_file:
|
||||||
with tmpfile('.'.join(ids)):
|
filename = write_tmpfile('.'.join(ids), output)
|
||||||
write_stderr('%s\n' % tmpfile_name())
|
print filename
|
||||||
write(output)
|
return filename
|
||||||
return tmpfile_name()
|
|
||||||
else:
|
else:
|
||||||
write(output)
|
print output.strip()
|
||||||
|
|
||||||
def list_books(connection):
|
def list_books(connection):
|
||||||
c = connection.cursor()
|
c = connection.cursor()
|
||||||
|
@ -235,7 +230,7 @@ def list_books(connection):
|
||||||
#print fetchall_dict(c)
|
#print fetchall_dict(c)
|
||||||
for i in xrange(c.rowcount):
|
for i in xrange(c.rowcount):
|
||||||
book = fetchone_dict(c)
|
book = fetchone_dict(c)
|
||||||
write('%-13s %-10s %-60s %s\n' %
|
print('%-13s %-10s %-60s %s' %
|
||||||
(book['isbn'], str_or_empty(book['id']),
|
(book['isbn'], str_or_empty(book['id']),
|
||||||
cut_str(book['title'], 60), book['persons']))
|
cut_str(book['title'], 60), book['persons']))
|
||||||
|
|
||||||
|
@ -244,16 +239,16 @@ def list_persons(connection):
|
||||||
c.execute(q_list_persons)
|
c.execute(q_list_persons)
|
||||||
for i in xrange(c.rowcount):
|
for i in xrange(c.rowcount):
|
||||||
person = fetchone_dict(c)
|
person = fetchone_dict(c)
|
||||||
write('%-5s %-30s %d books\n' % (person['id'],
|
print '%-5s %-30s %d books' % (person['id'],
|
||||||
person['firstname']+' '+person['lastname'],
|
person['firstname']+' '+person['lastname'],
|
||||||
person['num_books']))
|
person['num_books'])
|
||||||
|
|
||||||
def list_categories(connection):
|
def list_categories(connection):
|
||||||
c = connection.cursor()
|
c = connection.cursor()
|
||||||
c.execute(q_list_categories)
|
c.execute(q_list_categories)
|
||||||
for i in xrange(c.rowcount):
|
for i in xrange(c.rowcount):
|
||||||
cat = fetchone_dict(c)
|
cat = fetchone_dict(c)
|
||||||
write('%-15s %-30s %d books\n' % (cat['id'], cat['name'], cat['num_books']))
|
print '%-15s %-30s %d books' % (cat['id'], cat['name'], cat['num_books'])
|
||||||
|
|
||||||
def list_cmd(connection, what):
|
def list_cmd(connection, what):
|
||||||
funs = { 'book': list_books,
|
funs = { 'book': list_books,
|
||||||
|
@ -265,11 +260,9 @@ def list_cmd(connection, what):
|
||||||
def search_book(connection, search_strings, search_description=False):
|
def search_book(connection, search_strings, search_description=False):
|
||||||
c = connection.cursor()
|
c = connection.cursor()
|
||||||
if search_description:
|
if search_description:
|
||||||
where_clauses = ['book.title ILIKE %s OR book.subtitle ILIKE %s OR book.series ILIKE %s \
|
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 OR book.description ILIKE %s']*len(search_strings)
|
||||||
OR person.lastname ILIKE %s OR person.firstname ILIKE %s OR book.description ILIKE %s']*len(search_strings)
|
|
||||||
else:
|
else:
|
||||||
where_clauses = ['book.title ILIKE %s OR book.subtitle ILIKE %s OR book.series ILIKE %s \
|
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)
|
||||||
OR person.lastname ILIKE %s OR person.firstname ILIKE %s']*len(search_strings)
|
|
||||||
|
|
||||||
result_list = []
|
result_list = []
|
||||||
for s in search_strings:
|
for s in search_strings:
|
||||||
|
@ -279,18 +272,10 @@ def search_book(connection, search_strings, search_description=False):
|
||||||
else:
|
else:
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
result_list.append(s)
|
result_list.append(s)
|
||||||
c.execute('SELECT isbn,book.id AS id,title,category, \
|
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))
|
||||||
array_to_string(array_agg(person.lastname || \' (\' || person.id || \')\'), \', \') AS persons \
|
|
||||||
FROM book LEFT JOIN bookperson ON book.isbn=bookperson.book \
|
|
||||||
LEFT JOIN person ON person.id=bookperson.person \
|
|
||||||
WHERE ' + ' OR '.join(where_clauses) + '\
|
|
||||||
GROUP BY isbn, book.id, title, category \
|
|
||||||
', map(lambda s:'%' + s + '%',result_list))
|
|
||||||
for i in xrange(c.rowcount):
|
for i in xrange(c.rowcount):
|
||||||
book = fetchone_dict(c)
|
book = fetchone_dict(c)
|
||||||
write('%-13s %-10s %-60s %s' %
|
print book['isbn'], book['title'], book['person']
|
||||||
(book['isbn'], str_or_empty(book['id']),
|
|
||||||
cut_str(book['title'], 60), book['persons']))
|
|
||||||
|
|
||||||
|
|
||||||
def search_person(connection, search_strings):
|
def search_person(connection, search_strings):
|
||||||
|
@ -299,14 +284,13 @@ def search_person(connection, search_strings):
|
||||||
for s in search_strings:
|
for s in search_strings:
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
result_strings.append(s)
|
result_strings.append(s)
|
||||||
c.execute('SELECT * FROM person LEFT JOIN bookperson ON person.id=bookperson.person \
|
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)
|
||||||
WHERE person.lastname ILIKE %s or person.firstname ILIKE %s OR person.id ILIKE %s', result_strings)
|
|
||||||
for i in xrange(c.rowcount):
|
for i in xrange(c.rowcount):
|
||||||
person = fetchone_dict(c)
|
person = fetchone_dict(c)
|
||||||
write(person['lastname'], ', ', person['firstname'], '\t', person['book'])
|
print person['lastname'], ', ', person['firstname'], '\t', person['book']
|
||||||
|
|
||||||
def do_action(connection, action):
|
def do_action(connection, action):
|
||||||
debug('ACTION %s ' % action)
|
print 'ACTION %s ' % action
|
||||||
c = connection.cursor()
|
c = connection.cursor()
|
||||||
queries = {'new-person': q_new_person,
|
queries = {'new-person': q_new_person,
|
||||||
'edit-person': q_edit_person,
|
'edit-person': q_edit_person,
|
||||||
|
@ -317,9 +301,9 @@ def do_action(connection, action):
|
||||||
action_type = action['action']
|
action_type = action['action']
|
||||||
execute_query(c, queries[action_type], action)
|
execute_query(c, queries[action_type], action)
|
||||||
if action_type in ['new-book', 'edit-book']:
|
if action_type in ['new-book', 'edit-book']:
|
||||||
debug('FIXING PERSONS: REMOVING')
|
print 'FIXING PERSONS: REMOVING'
|
||||||
c.execute(q_remove_bookpersons, {'isbn': action['isbn']})
|
c.execute(q_remove_bookpersons, {'isbn': action['isbn']})
|
||||||
debug('FIXING PERSONS: ADDING')
|
print 'FIXING PERSONS: ADDING'
|
||||||
if action['persons']:
|
if action['persons']:
|
||||||
for (relation, personlist) in action['persons'].items():
|
for (relation, personlist) in action['persons'].items():
|
||||||
for person in personlist:
|
for person in personlist:
|
||||||
|
@ -329,23 +313,30 @@ def do_action(connection, action):
|
||||||
'relation': relation})
|
'relation': relation})
|
||||||
if action['references']:
|
if action['references']:
|
||||||
c.execute('SELECT referencetype.id FROM referencetype')
|
c.execute('SELECT referencetype.id FROM referencetype')
|
||||||
legal_reftypes = [a for list in c.fetchall() for a in list]
|
refs = c.fetchone()
|
||||||
for (reftype, reflist) in action['references'].items():
|
for (reftype, reflist) in action['references'].items():
|
||||||
for ref in reflist:
|
for ref in reflist:
|
||||||
if reftype in legal_reftypes:
|
if ref in refs:
|
||||||
c.execute(q_add_bookreference,
|
c.execute(q_add_reference,
|
||||||
{'isbn': action['isbn'],
|
{'isbn': action['isbn'],
|
||||||
'reftype': reftype,
|
'reftype': reftype,
|
||||||
'value': ref})
|
'value': ref})
|
||||||
else:
|
else:
|
||||||
raise WorblehatException('%s is not in the defined references, please use a more general one' % reftype)
|
raise WorblehatException('%s is not in the defined references, please use a more general one' % reftype)
|
||||||
|
|
||||||
|
class WorblehatException(Exception):
|
||||||
|
def __init__(self, msg):
|
||||||
|
Exception.__init__(self, msg)
|
||||||
|
|
||||||
|
|
||||||
def commit_actions(connection, actions):
|
def commit_actions(connection, actions):
|
||||||
for action in actions:
|
for action in actions:
|
||||||
try:
|
try:
|
||||||
do_action(connection, action)
|
do_action(connection, action)
|
||||||
except pgdb.DatabaseError, err:
|
except pgdb.DatabaseError, err:
|
||||||
raise WorblehatException('commit: Error in "%s" action: %s' % (action['action'], err))
|
print>>sys.stderr, 'Error in "%s" action: %s' % (action['action'], err)
|
||||||
|
print>>sys.stderr, 'Giving up.'
|
||||||
|
return
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
def commit(connection, filename=None):
|
def commit(connection, filename=None):
|
||||||
|
@ -355,7 +346,9 @@ def commit(connection, filename=None):
|
||||||
text = f.read()
|
text = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
raise WorblehatException('commit: Error reading file %s: %s' % (filename, e))
|
print 'commit: Error reading file %s: %s' % (filename, e)
|
||||||
|
print 'Exiting.'
|
||||||
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
text = sys.stdin.read()
|
text = sys.stdin.read()
|
||||||
|
|
||||||
|
@ -364,17 +357,9 @@ def commit(connection, filename=None):
|
||||||
|
|
||||||
def edit(connection, ids):
|
def edit(connection, ids):
|
||||||
filename = show(connection, ids, commit_format=True, tmp_file=True)
|
filename = show(connection, ids, commit_format=True, tmp_file=True)
|
||||||
done = False
|
print filename
|
||||||
while not done:
|
run_editor(filename)
|
||||||
run_editor(filename)
|
commit(connection, filename)
|
||||||
try:
|
|
||||||
commit(connection, filename)
|
|
||||||
done = True
|
|
||||||
except WorblehatException, exc:
|
|
||||||
write_stderr('%s\n' % exc)
|
|
||||||
answer = raw_input('Retry? [Y/n] ')
|
|
||||||
if answer == 'n':
|
|
||||||
done = True
|
|
||||||
|
|
||||||
def map_cmd(connection, shelfname=None, category=None):
|
def map_cmd(connection, shelfname=None, category=None):
|
||||||
pass
|
pass
|
||||||
|
@ -382,15 +367,17 @@ def map_cmd(connection, shelfname=None, category=None):
|
||||||
def suggest_book_data(connection, tmp_file=False):
|
def suggest_book_data(connection, tmp_file=False):
|
||||||
return google_suggest_book_data(connection, tmp_file)
|
return google_suggest_book_data(connection, tmp_file)
|
||||||
|
|
||||||
|
<<<<<<< .mine
|
||||||
def register_books(connection):
|
def register_books(connection):
|
||||||
filename = suggest_book_data(connection, tmp_file=True)
|
filename = suggest_book_data(connection, tmp_file=True)
|
||||||
#print("Tempfile filename: " + filename)
|
|
||||||
run_editor(filename)
|
run_editor(filename)
|
||||||
commit(connection, filename)
|
commit(connection, filename)
|
||||||
|
|
||||||
|
=======
|
||||||
def give_bananas():
|
def give_bananas():
|
||||||
write("Om nom nom... Thanks!")
|
print "Om nom nom... Thanks!"
|
||||||
|
|
||||||
|
>>>>>>> .r164
|
||||||
commands = { 'show':
|
commands = { 'show':
|
||||||
{ 'args': [('ids', (1,None))],
|
{ 'args': [('ids', (1,None))],
|
||||||
'options': ['commit_format', 'tmp_file'],
|
'options': ['commit_format', 'tmp_file'],
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
Hei,
|
|
||||||
|
|
||||||
i går startet vi opp med planleggingen av det nye biblioteksystemet. Det
|
|
||||||
har fått navnet Worblehat, og mailinglista worblehat@pvv.ntnu.no er
|
|
||||||
opprettet. Siden det fremdeles ikke er så mange som har meldt seg på
|
|
||||||
der, spammer jeg denne også til aktive.
|
|
||||||
|
|
||||||
Det er dessuten sendt mail til drift, om å legge oss ut på dev.
|
|
||||||
|
|
||||||
Vi har laget et foreløpig design av både systemet og databasen. Disse er
|
|
||||||
foreløpig å finne på http://www.pvv.ntnu.no/~tirilane/worblehat.
|
|
||||||
Rettelser og forbedringer mottas med takk. Det hadde vært spesielt fint
|
|
||||||
om noen med peiling på databaser kunne gå gjennom worblehat-db.dia og
|
|
||||||
fikse. Og det hadde vært enda bedre om noen med peiling på databaser
|
|
||||||
kunne implementere databasen, og legge inn alle de magiske triksene for
|
|
||||||
å få til til å gå raskt.
|
|
||||||
|
|
||||||
Ellers er designet ganske enkelt. Vi trenger en database, og noen
|
|
||||||
klienter. Vi trenger blant annet en for registrering av nye bøker. Den
|
|
||||||
skal ha to modus:
|
|
||||||
1) Registrering av en bok
|
|
||||||
2) Registrering av mange bøker
|
|
||||||
Vi tenkte at 1) kunne være sånn fin og brukervennlig, mens 2) skal gå
|
|
||||||
raskt for mange bøker. Gjerne slik:
|
|
||||||
Blippe bok, klienten plukker opp ISBN-nummeret, spør ISBNdb.com, og
|
|
||||||
registrerer boka i databasen om den ble funnet. Ble den ikke funnet, må
|
|
||||||
den på en eller annen måte si fra, slik at vi kan ta boka manuelt. Dette
|
|
||||||
kan løses ved å samle opp ISBN-nummeret, men siden det å manuelt lete
|
|
||||||
etter bøker med et gitt ISBN-nummer ikke akkurat er lett, hadde det nok
|
|
||||||
vært bedre om den stoppet opp eller noe.
|
|
||||||
|
|
||||||
Dessuten trenger vi noe for å spørre databasen. Her tenkte vi CHI,
|
|
||||||
IRC-bot og noe web-basert. Strengt tatt trenger vi bare en, og da bør
|
|
||||||
kanskje CLI ha hovedprioritet.
|
|
||||||
|
|
||||||
For at en eventuell IRC-bot ikke skal gjøre for mye arbeid, tenkte vi å
|
|
||||||
ha et lag mellom boten og databasen. Denne skal implementere
|
|
||||||
søke-funksjonene, som boten bare trenger å kalle. De andre (CLI og web)
|
|
||||||
står også fritt til å benytte seg av dette. Det blir opp til de som
|
|
||||||
implementerer. Fordelen er mindre duplisering av kode. Ulempen er at da
|
|
||||||
må alle skrives i samme språk. Men opp til implementørene.
|
|
||||||
|
|
||||||
En ting vi ikke har bestemt, er om plasseringen i hylla skal baseres på
|
|
||||||
Deweys system eller UDC. Fordelen med Dewey er at nummeret kan hentes
|
|
||||||
ned fra ISBNdb.com sammen med resten av informasjonene. Fordelen med UDC
|
|
||||||
er at det er mye mer spesialisert, hvilket kan være en stor fordel når
|
|
||||||
vi har så mange bøker om et såpass smalt felt. Dessuten bruker Teknisk
|
|
||||||
hovedbibliotek UDC. Hva synes man?
|
|
||||||
http://en.wikipedia.org/wiki/Universal_Decimal_Classification
|
|
||||||
http://en.wikipedia.org/wiki/Dewey_Decimal_Classification
|
|
||||||
|
|
||||||
Og hvem har lyst til å fikse hva? Noen frivillige?
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 19 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 8.4 KiB |
|
@ -1,4 +0,0 @@
|
||||||
# Worblehat FAQ
|
|
||||||
|
|
||||||
Q: Hvor kommer navnet fra?
|
|
||||||
A: Discworld, bibliotekarens tidligere navn. Se https://wiki.lspace.org/Horace_Worblehat
|
|
Binary file not shown.
Before Width: | Height: | Size: 105 KiB |
Reference in New Issue