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:
parent
e490174661
commit
e0aa1d53d2
67
cli/arguments.txt
Normal file
67
cli/arguments.txt
Normal 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
|
70
cli/commit-file-format.txt
Normal file
70
cli/commit-file-format.txt
Normal 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
180
cli/fileformat.py
Normal 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
65
cli/util.py
Normal 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
12
cli/worblehat.py
Normal 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)
|
Reference in New Issue
Block a user