Projects/worblehat-old
Projects
/
worblehat-old
Archived
12
0
Fork 0

Compare commits

...

10 Commits

Author SHA1 Message Date
rot e1e280b31f Relocate wiki from trac 2023-01-25 13:15:30 +01:00
Péter Henrik Gombos 39361112bc some small bugfixes 2012-11-01 12:12:28 +00:00
Øystein Ingmar Skartsæterhagen 9a733ef73a Improved handling of output to terminal and files.
New module file_io which takes care of this.  All output should go
through file_io.write, which encodes it using the appropriate
character encoding.  For output to a temporary file, use "with
file_io.tmpfile('name')".

Moved WorblehatException to new module exc, so all modules can access
it without importing worblehat.
2011-10-12 19:26:21 +00:00
Øystein Ingmar Skartsæterhagen 1f2c52bb47 Hacks for character encoding in fileformat.py.
I try to consistently translate strings to unicode objects when
reading, and encode all unicode objects when writing.  But it is not
clear when the translation should happen.  This module needs to be
cleaned up a little.
2011-10-11 15:44:17 +00:00
Øystein Ingmar Skartsæterhagen bf188c99ae Hack for character encoding in communication with database.
Added encoding from unicode to str in execute_query, and wrote wrapper
functions with decoding for the cursor methods fetchone and fetchall.

This means that from now on, one should never call these methods
directly on cursor objects, but instead call execute_query, fetchone,
fetchall, fetchone_dict or fetchall_dict in util.py.

I plan to separate out this code to a new module (db.py?), and rewrite
it in a way that makes it harder to write things like c.execute by
mistake.
2011-10-11 15:16:07 +00:00
Péter Henrik Gombos b50fe04b1f utilized book search, work now as list just with where clauses 2011-10-09 18:56:19 +00:00
Péter Henrik Gombos 8c660ecaee added some sql to search 2011-10-09 18:32:24 +00:00
Péter Henrik Gombos cc4eadc885 fixed bug in referenceadding to books 2011-10-09 18:08:01 +00:00
Andreas Lindahl Flåten e14ab5e580 added printing of filename in register-books 2011-10-09 17:57:30 +00:00
Andreas Lindahl Flåten e71965048e fixed commit bugs 2011-10-09 16:15:17 +00:00
13 changed files with 278 additions and 67 deletions

35
README.md Normal file
View File

@ -0,0 +1,35 @@
![](./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

3
cli/exc.py Normal file
View File

@ -0,0 +1,3 @@
class WorblehatException(Exception):
def __init__(self, msg):
Exception.__init__(self, msg)

79
cli/file_io.py Normal file
View File

@ -0,0 +1,79 @@
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)

View File

@ -1,5 +1,6 @@
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.
# #
@ -52,10 +53,13 @@ action_fields = {
{ 'type': 'category', { 'type': 'category',
'required': ['id'] } } 'required': ['id'] } }
class CommitFormatSyntaxError(Exception): class CommitFormatSyntaxError(WorblehatException):
def __init__(self, msg, linenr): def __init__(self, msg, linenr):
super(CommitFormatSyntaxError, self).__init__(self, 'Syntax error on line %d: %s' % WorblehatException.__init__(self, 'Syntax error on line %d: %s' % (linenr, msg))
(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() == '':
@ -65,7 +69,10 @@ 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
return int(val.strip()) try:
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 = {}
@ -171,23 +178,27 @@ 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 + ' ' + unicode(single_value) + '\n' lines += ' ' + key + ' ' + 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 += ' ' + unicode(single_value) lines += ' ' + single_value
return lines return lines
def make_comment(s): def make_comment(s):
@ -197,7 +208,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 = '' lines = u''
if 'comment' in d: if 'comment' in d:
lines += make_comment(d['comment']) lines += make_comment(d['comment'])
action = d['action'] action = d['action']
@ -206,7 +217,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_str, 'i': write_field_value_int,
'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])

View File

@ -1,12 +1,32 @@
import os db_encoding = 'utf8'
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)):
@ -14,14 +34,14 @@ def make_result_dict(cursor, row):
return d return d
def fetchone_dict(cursor): def fetchone_dict(cursor):
row = cursor.fetchone() row = fetchone(cursor)
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),
cursor.fetchall()) fetchall(cursor))
def first(lst): def first(lst):
return lst[0] return lst[0]
@ -56,6 +76,12 @@ 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))
@ -84,16 +110,3 @@ 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

View File

@ -7,6 +7,8 @@ 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',
@ -57,6 +59,8 @@ 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, ' \
@ -85,7 +89,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))' 'VALUES (%(isbn)s, %(reftype)s, %(value)s)'
def connect_to_db(): def connect_to_db():
connection = pgdb.connect(database='oysteini_pbb2', connection = pgdb.connect(database='oysteini_pbb2',
@ -102,7 +106,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)]:
c.execute(q, {'id': id}) execute_query(c, 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
@ -214,15 +218,16 @@ 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 = write_actionlist(objects) output = encoding_comment() + write_actionlist(objects)
else: else:
output = '\n'.join(objects) output = '\n'.join(objects)
if tmp_file: if tmp_file:
filename = write_tmpfile('.'.join(ids), output) with tmpfile('.'.join(ids)):
print filename write_stderr('%s\n' % tmpfile_name())
return filename write(output)
return tmpfile_name()
else: else:
print output.strip() write(output)
def list_books(connection): def list_books(connection):
c = connection.cursor() c = connection.cursor()
@ -230,7 +235,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)
print('%-13s %-10s %-60s %s' % write('%-13s %-10s %-60s %s\n' %
(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']))
@ -239,16 +244,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)
print '%-5s %-30s %d books' % (person['id'], write('%-5s %-30s %d books\n' % (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)
print '%-15s %-30s %d books' % (cat['id'], cat['name'], cat['num_books']) write('%-15s %-30s %d books\n' % (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,
@ -260,9 +265,11 @@ 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 OR person.lastname ILIKE %s OR person.firstname ILIKE %s OR book.description ILIKE %s']*len(search_strings) 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)
else: else:
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) 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 = [] result_list = []
for s in search_strings: for s in search_strings:
@ -272,10 +279,18 @@ 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 * 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)) c.execute('SELECT isbn,book.id AS id,title,category, \
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)
print book['isbn'], book['title'], book['person'] write('%-13s %-10s %-60s %s' %
(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):
@ -284,13 +299,14 @@ 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 WHERE person.lastname ILIKE %s or person.firstname ILIKE %s OR person.id ILIKE %s', result_strings) 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): for i in xrange(c.rowcount):
person = fetchone_dict(c) person = fetchone_dict(c)
print person['lastname'], ', ', person['firstname'], '\t', person['book'] write(person['lastname'], ', ', person['firstname'], '\t', person['book'])
def do_action(connection, action): def do_action(connection, action):
print 'ACTION %s ' % action debug('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,
@ -301,9 +317,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']:
print 'FIXING PERSONS: REMOVING' debug('FIXING PERSONS: REMOVING')
c.execute(q_remove_bookpersons, {'isbn': action['isbn']}) c.execute(q_remove_bookpersons, {'isbn': action['isbn']})
print 'FIXING PERSONS: ADDING' debug('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:
@ -313,30 +329,23 @@ 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')
refs = c.fetchone() legal_reftypes = [a for list in c.fetchall() for a in list]
for (reftype, reflist) in action['references'].items(): for (reftype, reflist) in action['references'].items():
for ref in reflist: for ref in reflist:
if ref in refs: if reftype in legal_reftypes:
c.execute(q_add_reference, c.execute(q_add_bookreference,
{'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:
print>>sys.stderr, 'Error in "%s" action: %s' % (action['action'], err) raise WorblehatException('commit: 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):
@ -346,9 +355,7 @@ def commit(connection, filename=None):
text = f.read() text = f.read()
f.close() f.close()
except IOError, e: except IOError, e:
print 'commit: Error reading file %s: %s' % (filename, e) raise WorblehatException('commit: Error reading file %s: %s' % (filename, e))
print 'Exiting.'
sys.exit(1)
else: else:
text = sys.stdin.read() text = sys.stdin.read()
@ -357,9 +364,17 @@ 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)
print filename done = False
run_editor(filename) while not done:
commit(connection, filename) run_editor(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
@ -367,17 +382,15 @@ 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():
print "Om nom nom... Thanks!" write("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'],

View File

@ -0,0 +1,53 @@
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.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
wiki/assets/worblehat.dia Normal file

Binary file not shown.

BIN
wiki/assets/worblehat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

4
wiki/faq.md Normal file
View File

@ -0,0 +1,4 @@
# Worblehat FAQ
Q: Hvor kommer navnet fra?
A: Discworld, bibliotekarens tidligere navn. Se https://wiki.lspace.org/Horace_Worblehat

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB