Såvidt begynt på nytt kommandolinjegrensesnitt.

* Kopiert inn noe dokumentasjon (filformat og kommandolinjeargumenter)
  fra forrige forsøk (som var i python-katalogen).

* Kopiert implementajonen av filformatet fra forrige forsøk.

* Kopiert nyttige ting fra mdboh, samt noe fra forrige inkarnasjon av
  kommandolinjegrensesnittet her, til util.py

* Skrevet litt testkode i worblehat.py.
This commit is contained in:
Øystein Ingmar Skartsæterhagen 2011-10-08 09:06:15 +00:00
parent e490174661
commit e0aa1d53d2
5 changed files with 394 additions and 0 deletions

67
cli/arguments.txt Normal file
View File

@ -0,0 +1,67 @@
# Forslag til kommandolinjeargumenter og deres betydning.
# søk etter bøker som inneholder «computer programming» og «knuth» (i
# tittel, undertittel eller forfatternavn):
worlbehat search 'computer programming' knuth
# søk i beskrivelse også:
worblehat --search-description search tex
# list opp alle bøkene:
worblehat list book
# vis boken med en gitt ISBN:
worblehat show 5467237485472
# vis bok, med kart:
worblehat --with-map show 5467237485472
# list opp alle personer:
worblehat list person
# søk etter person:
worblehat search-person donald knuth
# vis en person:
worblehat show dek
# skriv ut informasjon i samme format som commit forventer:
worblehat --commit-format show 5467237485472
worblehat --commit-format show dek
# samme, til en ny fil i /tmp:
worblehat --commit-format --tmp-file show 5467237485472
worblehat --commit-format --tmp-file show dek
# kan vise flere ting samtidig:
worblehat --commit-format --tmp-file show 5467237485472 432175437253 dek rms mlh
# lagre informasjon fra en fil i databasen (filen kan inneholde både
# nye og endrede personer og bøker):
worblehat commit # leser fra stdin
worblehat commit fjas.txt
# endre en bok/person
# (kombinerer `wh --commit-format --tmp-file show ...`,
# `$EDITOR /tmp/...`
# og `wh commit /tmp/...`)
worblehat edit 5467237485472
worblehat edit 5467237485472 432175437253 dek rms mlh
# foreslå informasjon om nye bøker (leser ISBN-numre fra stdin, søker
# i Google Books eller lignende, skriver info i commit-format til
# stdout (eller tmp-fil)):
worblehat suggest-book-data
worblehat --tmp-file suggest-book-data
# registrer nye bøker
# (kombinerer `wh --tmp-file suggest-book-data`,
# `$EDITOR /tmp/...`
# og `wh commit /tmp/...`)
worblehat register-books
# list opp alle kategoriene (med id, navn, muligens noe mer):
worblehat list category
# vis en kategori (vis informasjon om kategorien, samt liste over bøkene):
worblehat show matematikk
# vis en kategori, med plassering på kart:
worblehat --with-map show matematikk
# vis kart over bokhyllene:
worblehat map
# vis kart over hyllen som heter 'A':
worblehat map A
# vis kart over hyllen som heter 'A', og uthev hyllene med matematikk i:
worblehat map A matematikk

View File

@ -0,0 +1,70 @@
Filformatet som forventes av 'commit'-kommandoen, og som skrives ut av
kommandoen 'suggest-book-data', samt av andre kommandoer når man gir
opsjonen --commit-format
En linje som starter med '#' er en kommentar.
Filen består av ett eller flere avsnitt. En blank linje indikerer
nytt avsnitt. Hvert avsnitt beskriver én ting som skal gjøres. De
mulige tingene å gjøre er: legge inn ny bok eller forfatter, endre en
eksisterende bok eller forfatter, slette en bok eller forfatter.
Hvert avsnitt har en samling felter med tilhørende verdier. Et felt
skrives med feltnavn, kolon, verdi, newline. Hvis verdien skal bestå
av flere linjer, brukes whitespace (minst ett mellomrom eller en tab)
på begynnelsen av hver ekstra linje. Whitespace (inkludert newline)
mellom kolonet og verdien ignoreres. Whitespace på slutten av linjer
ignoreres. Whitespace på begynnelsen av fortsettelseslinjer fjernes i
verdien som lagres, men newline-ene beholdes.
Eksempler på felter:
title: Foo Bar
description:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
In ut est ac ante aliquam dictum. Nulla facilisi. Cras vel
lectus mauris. In nec convallis mauris.
Hvert avsnitt må inneholde feltet 'action', som beskriver hva som skal
gjøres. De mulige verdiene for action-feltet er:
new-book
edit-book
delete-book
new-person
edit-person
delete-person
new-category
edit-category
delete-category
Eksempler:
action: new-book
isbn: 4325463287546
title: Foo Bar
subtitle: Baaaz
category: matematikk
persons:
author rjh
author oo
illustrator ko
publisher: Foo Publishing
published_year: 2010
edition: 1
num_pages: 420
series:
description:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
In ut est ac ante aliquam dictum. Nulla facilisi. Cras vel
lectus mauris. In nec convallis mauris.
picture:
thumbnail:
references:
url http://example.org/
wikipedia http://en.wikipedia.org/wiki/FooBar
action: edit-category
id: matematikk
name: Matematikk
placement: T10 T11 T12

180
cli/fileformat.py Normal file
View File

@ -0,0 +1,180 @@
import types
# The possible fields for each type of object.
#
# Each field is a tuple (fieldname, fieldtype). Fieldtype is either
# 's' (string), 'd' (dictionary, where the values are lists of
# strings), 'l' (list of strings).
fields = {
'book':
[('isbn', 's'), ('title', 's'), ('category', 's'),
('subtitle', 's'), ('persons', 'd'), ('publisher', 's'),
('published_year', 'i'), ('edition', 'i'), ('num_pages', 'i'),
('series', 's'), ('description', 's'), # TODO picture, thumbnail
('references', 'd')],
'person':
[('id', 's'), ('first_name', 's'), ('last_name', 's')],
'category':
[('id', 's'), ('name', 's'), ('placement', 'l')] }
# Fields associated with each action.
#
# The 'type' is a key in the fields dictionary, indicating the set of
# fields which can be used with the action. The 'required' list is a
# list of fields which must be present for the action to be accepted.
action_fields = {
'new-book':
{ 'type': 'book',
'required': ['isbn', 'title', 'category'] },
'edit-book':
{ 'type': 'book',
'required': ['isbn'] },
'delete-book':
{ 'type': 'book',
'required': ['isbn'] },
'new-person':
{ 'type': 'person',
'required': ['id', 'first_name', 'last_name'] },
'edit-person':
{ 'type': 'person',
'required': ['id'] },
'delete-person':
{ 'type': 'person',
'required': ['id'] },
'new-category':
{ 'type': 'category',
'required': ['id', 'name'] },
'edit-category':
{ 'type': 'category',
'required': ['id'] },
'delete-category':
{ 'type': 'category',
'required': ['id'] } }
class CommitFormatSyntaxError(Exception):
pass
def read_field_value_str(val):
return '\n'.join(map(lambda x: x.strip(), val.split('\n'))).strip()
def read_field_value_int(val):
if val.strip() == '':
return None
return int(val.strip())
def read_field_value_dict(val):
d = {}
if val.strip() == '':
return d
for line in val.strip().split('\n'):
key, value = line.strip().split(' ', 1)
if key in d:
d[key].append(value)
else:
d[key] = [value]
return d
def read_field_value_list(val):
return val.strip().split(' ')
def read_action(text):
'''
Parse text as an action, returning a dictionary.
'''
lines = text.split('\n')
print 'reading action'
print 'lines:'
print lines
d = {}
lastfield = None
for line in lines:
if len(line) == 0:
raise CommitFormatSyntaxError('Empty line in action')
if line[0] in [' ', '\t']: # continuation line
if not lastfield:
raise CommitFormatSyntaxError('First line is continuation line: ' + line)
d[lastfield] = d[lastfield] + '\n' + line.strip()
else:
field, value = line.split(':', 1)
d[field] = value.strip()
lastfield = field
if 'action' not in d:
raise CommitFormatSyntaxError('Missing \'action\' field')
action = d['action']
print 'dict:'
print d
for field in action_fields[action]['required']:
if field not in d:
raise CommitFormatSyntaxError('Missing required field \'%s\' in \'%s\' action' % (field, action))
data_type = action_fields[action]['type']
result = { 'action': action }
for field, ftype in fields[data_type]:
if field in d:
reader = { 's': read_field_value_str,
'i': read_field_value_int,
'd': read_field_value_dict,
'l': read_field_value_list }[ftype]
result[field] = reader(d[field])
return result
def read_actionlist(text):
'''
Parse text as a list of actions.
The result is a list of dictionaries.
'''
return map(lambda x: read_action(x.strip()),
text.split('\n\n'))
def write_field_value_str(val):
lines = ''
if not val:
val = ''
val = unicode(val)
value_lines = val.split('\n')
for l in value_lines:
lines += ' ' + l + '\n'
return lines
def write_field_value_dict(val):
lines = '\n'
for (key,values) in val.items():
for single_value in values:
lines += ' ' + key + ' ' + unicode(single_value) + '\n'
return lines
def write_field_value_list(val):
lines = ''
for single_value in val:
lines += ' ' + unicode(single_value)
return lines
def make_comment(s):
return '\n'.join(map(lambda x: '# ' + x,
s.split('\n'))) + '\n'
def write_action(d):
if type(d) in types.StringTypes:
return make_comment(d)
lines = ''
if 'comment' in d:
lines += make_comment(d['comment'])
action = d['action']
lines += 'action: ' + action + '\n'
data_type = action_fields[action]['type']
for field, ftype in fields[data_type]:
if field in d:
value_writer = {'s': write_field_value_str,
'i': write_field_value_str,
'd': write_field_value_dict,
'l': write_field_value_list}[ftype]
lines += field + ':' + value_writer(d[field])
return lines
def write_actionlist(actions):
return '\n'.join(map(write_action, actions))
# test: print write_actionlist([{'comment':'Foo!\nBar!','action':'new-book','isbn':'434545'},{'action':'edit-book','isbn':'654654745','persons':{'author':['ab','foo'],'illustrator':['moo']}},'This\nis\na\ncomment.',{'action':'edit-category','id':'matematikk','name':'Matematikk','placement':['T10','T11']}])

65
cli/util.py Normal file
View File

@ -0,0 +1,65 @@
def make_result_dict(cursor, row):
d = {}
for i in xrange(len(row)):
d[cursor.description[i][0]] = row[i]
return d
def fetchone_dict(cursor):
row = cursor.fetchone()
if row != None:
return make_result_dict(cursor, row)
return None
def fetchall_dict(cursor):
return map(lambda r: make_result_dict(cursor, r),
cursor.fetchall())
def first(lst):
return lst[0]
def second(lst):
return lst[1]
def count(predicate, lst):
c = 0
for elem in lst:
if predicate(elem):
c = c+1
return c
def find(predicate, lst):
for elem in lst:
if predicate(elem):
return elem
return None
def unique(lst):
newlst = []
for elem in lst:
if elem not in newlst:
newlst.append(elem)
return newlst
def mapcond(fun, predicate, lst):
def mapfun(x):
if predicate(x):
return fun(x)
return x
return map(mapfun, lst)
def maptup(fun, lst):
return tuple(map(fun, lst))
def translate(value, translations):
return translations.get(value, value)
def p(s):
encoded = s
if isinstance(s, unicode):
encoded = s.encode('utf8')
print encoded
def cut_str(string, length, ellipsis='...'):
if len(string) < length:
return string
return string[0:length-len(ellipsis)]+ellipsis

12
cli/worblehat.py Normal file
View File

@ -0,0 +1,12 @@
import pgdb
from fileformat import read_actionlist, write_actionlist
from util import *
connection = pgdb.connect(database='oysteini_pbb2',
user='oysteini_pbb',
password='lio5Aide',
host='postgres.pvv.ntnu.no');
c = connection.cursor()
c.execute('SELECT * from bok')
print fetchall_dict(c)