db.py
'''\
Python DataBase Module.
Classes that use the Python Shelf to implement simple data storage.
classes:
MyDB
functions:
db_simple_check()
db_simple_print(fp)
db_simple_update()
sandbox()
Developer@Sonnack.com
July 2012
'''
from sys import stdout, stderr, argv
from os import path
from datetime import datetime, date, timedelta
import shelve
class MyDB (object):
'''\
DataBase Class (based on Python Shelf).
properties:
.name - Database name
.user - Database user
.db - DB object
.mode - Open mode
.db_created - Date DB created
.db_updated - Last update date
.last_login - Last update user
.last_count - Number of rcds (when opened)
methods:
.exists - Test for database file
.open - Open (and possibly create) database
.close - Close database
.userkeys - List of keys, excluding sysparams
.keys - List of keys (includes sysparams)
.values - List of values
.create - Create a new blank database
.put - Write a single record to the database
.get - Read a single record from the database
.write - Write a (key, flds) list of records to the database
.read - Read a list of records from the database given a list of keys
.update - Write a {key, flds} dict to the database
.search - Get a list of records from the database given a query
.print_stats - Print database info and sysparams
.print_recds - Output a list of database records (uses `repr(rcd)`)
._sysparams - Load sysparams from database
._updated - Time stamp $updated sysparam
obj[key] - Get/Set database item 'key'
del obj[key] - Remove database item 'key'
iter(obj) - iterator
if obj - if {database is open}
key in obj - contains key
len(obj) - DB length
str(obj) - string representation
repr(obj) - JSON-like string
database sysparams:
$created - date database created
$updated - date database last updated
$login - username of last write/create access
'''
def __init__ (self, name, user):
''' Create new MyDB instance. '''
self.name = name
self.user = user
self.db = None
self.mode = None
self.db_created = (None, user)
self.db_updated = (None, user)
self.last_login = (None, user)
self.last_count = 0
if self.exists():
self.open()
self.close()
else:
print 'DB does not exist; creating...'
self.create()
def __del__ (self):
''' Close the DB if it's still open when Object is destroyed. '''
if self.db:
self.db.close()
def userkeys (self, key_filter=None):
''' Return list of DataBase Keys excluding system keys (filterable). '''
if not self.db:
raise Exception, E_DB_NOT_OPEN
ks = filter(lambda k: not k.startswith('$'), self.db.keys())
if key_filter:
ks = filter(key_filter, ks)
return ks
def keys (self, key_filter=None):
''' Return list of DataBase Keys (filterable). '''
if not self.db:
raise Exception, E_DB_NOT_OPEN
ks = self.db.keys()
if key_filter:
ks = filter(key_filter, ks)
return ks
def values (self, value_filter=None):
''' Return list of DataBase Values (filterable). '''
if not self.db:
raise Exception, E_DB_NOT_OPEN
vs = self.db.values()
if value_filter:
vs = filter(value_filter, vs)
return vs
def __str__ (self):
''' Return Pretty Print string. '''
db_name = path.basename(self.name)
status = '' if self.db == None else ' (open)'
dt_cre = self.db_created[0].strftime('%m-%d-%Y %H:%M')
t = (db_name, self.user, status, dt_cre)
s = '%s [by %s]%s (%s)'
return s % t
def __repr__ (self):
''' Return JSON-like string. '''
status = 'open' if self.db else 'closed'
t = (self.name, self.last_count, self.user, status, self.last_login, self.db_created)
s = '{MyDB:{name:"%s", rcds:%d, user:"%s", status:"%s", dla:"%s", cre:"%s"}}'
return s % t
def __nonzero__ (self):
return (self.db != None)
def __len__ (self):
''' Return length of DataBase (if open, else zero). '''
if self.db:
return len(self.db)
return 0
def __iter__ (self):
''' Return DataBase iterator. '''
if self.db:
return iter(self.db)
raise Exception, E_DB_NOT_OPEN
def __contains__ (self, key):
''' Test for existence of a Key. '''
if self.db:
return (True if key in self.db else False)
raise Exception, E_DB_NOT_OPEN
def __getitem__ (self, key):
''' Return a DataBase Entry. '''
if self.db:
return (self.db[key] if key in self.db else None)
raise Exception, E_DB_NOT_OPEN
def __setitem__ (self, key, obj):
''' Set a DataBase Entry. '''
if not self.db:
raise Exception, E_DB_NOT_OPEN
self.db[key] = obj
self._updated()
def __delitem__ (self, key):
''' Delete a DataBase Entry. '''
if not self.db:
raise Exception, E_DB_NOT_OPEN
if key not in self.db:
raise Exception, E_DB_NO_ENTRY
del self.db[key]
self._updated()
def exists (self):
''' Test if DataBase exists already. '''
if path.exists(self.name) and path.isfile(self.name):
return True
return False
def open (self, mode='r'):
''' Open the DataBase for access. (Modes: r=read existing, w=r/w existing, c=r/w create, n=r/w new)'''
if self.db:
raise Exception, E_DB_IS_OPEN
self.mode = mode
try:
self.db = shelve.open(self.name, flag=self.mode)
self._sysparams()
if self.mode != 'r':
self.db['$login'] = (datetime.today(), self.user)
except Exception as e:
print >> stderr, e
raise e
def close (self):
''' Close the DataBase. '''
try:
if self.db:
if self.mode != 'r':
self.db['$logout'] = (datetime.today(), self.user)
self.db.close()
self.db = None
except Exception as e:
print >> stderr, e
def _sysparams (self):
self.db_created = self['$created']
self.db_updated = self['$updated']
self.last_login = self['$login']
self.last_count = len(self.db)
def _updated (self):
self.db_updated = (datetime.today(), self.user)
self.db['$updated'] = self.db_updated
def create (self):
''' Create a new blank DataBase.
'''
self.close()
self.db_created = (datetime.today(), self.user)
self.db = shelve.open(self.name, flag='n')
try:
self.db['$login'] = self.db_created
self.db['$created'] = self.db_created
self._updated()
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
def put (self, key, obj):
''' Write an entry given a Key and Object.
'''
self.close()
self.open(mode='w')
try:
self.db[key] = obj
self._updated()
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
def write (self, key_obj_list):
''' Write a list of Objects given a list of Key+Object pairs.
('key_obj_list' is a list of (key, flds) tuples.)
'''
self.close()
self.open(mode='w')
try:
for key_obj in key_obj_list:
self.db[key_obj[0]] = key_obj[1]
self._updated()
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
def update (self, key_list, fields_list):
''' Update a list of Objects given lists of Keys and Fields.
('key_list' is a list of DataBase keys.)
(fields_list' is a dict of fields to update.)
'''
self.close()
self.open(mode='w')
try:
for key,flds in zip(key_list, fields_list):
if key in self.db:
obj = self.db[key]
for fld in flds:
obj[fld] = flds[fld]
self.db[key] = obj
else:
self.db[key] = flds
print >> stderr, 'INSERT: "%s"' % key
self._updated()
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
def get (self, key):
''' Return an Object given its Key.
(Returns None if key not in DataBase.)
'''
self.close()
self.open(mode='r')
try:
if key in self.db:
return self.db[key]
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
return None
def read (self, key_list):
''' Return a list of Objects given a list of Keys.
(Returns list of values.)
((Returns None in list slot if key not in DataBase.))
'''
self.close()
self.open(mode='r')
try:
a = []
for key in key_list:
obj = self.db[key] if key in self.db else None
a.append(obj)
return a
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
return []
def search (self, key_filter=None, value_filter=None):
''' Return all entries.
(optional: 'key_filter' is a filter function on the key set.)
(optional: 'value_filter' is a filter function on the value set.)
(Returns list of (key, flds) tuples.)
'''
self.close()
self.open(mode='r')
try:
a = []
keys = sorted(self.db)
if key_filter:
keys = filter(key_filter, keys)
for key in keys:
obj = self.db[key]
if value_filter and not value_filter(obj):
continue
t = (key, obj)
a.append(t)
return a
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
return []
def print_stats (self, ostream):
''' Return Status string. '''
self.close()
self.open(mode='r')
try:
print >> ostream, 'Name: %s' % path.basename(self.name)
print >> ostream, 'Created: %s [by %s]' % (self.db_created[0], self.db_created[1])
print >> ostream, 'Updated: %s [by %s]' % (self.db_updated[0], self.db_updated[1])
print >> ostream, 'LastUse: %s [by %s]' % (self.last_login[0], self.last_login[1])
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
def print_recds (self, ostream):
''' Output a list of DB contents.
'''
self.close()
self.open(mode='r')
try:
for ix,key in enumerate(sorted(self.db.keys())):
obj = self.db[key]
t = (ix, type(obj), key, repr(obj))
print >> ostream, '[%d] (%s) "%s" "%s"' % t
except Exception as e:
print >> stderr, e
raise e
finally:
self.close()
E_DB_NOT_OPEN = 'DB not open!'
E_DB_IS_OPEN = 'DB already open!'
E_DB_NO_ENTRY = 'Key not in DB!'
ChrisDBName = 'chris.db'
def db_simple_check ():
''' Check DataBase. '''
db = MyDB(ChrisDBName, 'chris')
db.open()
try:
if '$created' in db:
print 'DB-Created: %s by %s' % db['$created']
print 'DB-Updated: %s by %s' % db['$updated']
print 'Last-Login: %s by %s' % db['$login']
print 'Last-Logout: %s by %s' % db['$logout']
else:
print 'No system fields!'
except:
raise
finally:
db.close()
return db
def db_simple_print (fp):
''' Print contents of DataBase. '''
db = MyDB(ChrisDBName, 'chris')
db.open(mode='w')
try:
dict_type = type(dict())
keys = db.keys(key_filter=lambda k: type(db[k]) == dict_type)
for key in sorted(keys):
print >> fp, '%s %s' % (db[key]['fname'], db[key]['lname'])
db['$accessed'] = datetime.today()
except:
raise
finally:
db.close()
return db
def db_simple_update ():
''' Update Datebase. '''
db = MyDB(ChrisDBName, 'chris')
keys = map(lambda x: x[1], simple_data)
vals = map(lambda x: {'fname':x[0], 'lname':x[1], 'place':x[2], 'type':x[3]}, simple_data)
items = zip(keys, vals)
db.write(items)
return db
simple_data = [
('Cindy' , 'Egeness' , 'Wife' , 'WM')
, ('Kathleen' , 'Matrass' , 'Minnesota' , 'GM')
, ('Ellen' , 'Buecker' , 'Minnesota' , 'LM')
, ('Barb' , 'Anderson' , 'Minnesota' , 'LM')
, ('Christine', 'Straining', 'Minnesota' , 'GM')
, ('Cindy' , 'Coe' , 'Las Vegas' , 'l1')
, ('Carol' , 'Hyne' , 'Los Angeles', 'FM')
, ('Ingrid' , 'Pashalak' , 'Los Angeles', 'GF')
, ('Shantih' , 'Hasst' , 'Los Angeles', 'FM')
, ('Hope' , 'Morrow' , 'Los Angeles', 'GM')
, ('Jayme' , 'Povalitus', 'College' , 'FR')
, ('Nancy' , 'Buckley' , 'College' , 'BF')
, ('Anglea' , 'Paris' , 'College' , 'GM')
, ('Debbie' , 'Fasciano' , 'College' , 'FM')
, ('Valerie' , 'Klayman' , 'College' , 'FM')
, ('Jeanne' , 'Schulte' , 'College' , 'BF')
, ('Gloria' , 'Wright' , 'College' , 'GM')
, ('Diana' , 'Yoon' , 'College' , 'F1')
, ('Debbie' , 'Vessels' , 'College' , 'GT')
, ('Jan' , 'Reeder' , 'High School', 'LT')
, ('Susan' , 'Sweeny' , 'High School', 'LT')
, ('Julie' , 'Nelson' , 'Neighbor' , 'FL')
]
def sandbox ():
''' Sandbox: a place to experiment. '''
db = PlayersDB('chris')
db.open(mode='r')
try:
print db['$login']
except:
raise
finally:
db.close()
return db
def dispatch (command, *args):
print command
print
if 'check' in command:
return db_simple_check()
if 'update' in command:
return db_simple_update()
if 'print' in command:
return db_simple_print(stdout)
if 'sandbox' in command:
return sandbox()
if 'main' in command:
return db_simple_print(stdout)
return None
if __name__ == '__main__':
print 'autorun:',argv[0]
command = argv[1] if 1 < len(argv) else 'main'
arguments = argv[2:]
obj = dispatch(command, arguments)
print type(obj)