forked from public/pyceo
Checkout and check-in works!
This commit is contained in:
parent
e334437d6d
commit
e3035e1b9a
258
ceo/library.py
258
ceo/library.py
|
@ -1,183 +1,97 @@
|
|||
""" The backend for the library-tracking system
|
||||
|
||||
This uses shelve which is pretty simplistic, but which should be sufficient for our (extremely minimal) use of the library.
|
||||
|
||||
There is a booklist (book=(Author, Title, Year, Signout)) where signout is None for "Checked In" or a (userid, date) to give who and when that book was signed out.
|
||||
|
||||
We key books by their ISBN number (this is currently only hoboily implemented; we don't use real ISBNs yet)
|
||||
|
||||
Future plans: use barcode scanners, index by ISBN, cross reference to library of congress
|
||||
Future plans: keep a whole stack of people who have checked it out (the last few at least)
|
||||
"""
|
||||
|
||||
import shelve
|
||||
import time
|
||||
import re
|
||||
from sqlobject import *
|
||||
from sqlobject.sqlbuilder import *
|
||||
from ceo import conf
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
### Configuration ###
|
||||
|
||||
CONFIG_FILE = '/etc/csc/library.cf'
|
||||
CONFIG_FILE = "etc/library.cf"
|
||||
|
||||
cfg = {}
|
||||
|
||||
def configure():
|
||||
"""Load Members Configuration"""
|
||||
|
||||
string_fields = [ 'library_db_path' ]
|
||||
numeric_fields = [ ]
|
||||
|
||||
# read configuration file
|
||||
cfg_tmp = conf.read(CONFIG_FILE)
|
||||
|
||||
# verify configuration
|
||||
conf.check_string_fields(CONFIG_FILE, string_fields, cfg_tmp)
|
||||
conf.check_integer_fields(CONFIG_FILE, numeric_fields, cfg_tmp)
|
||||
|
||||
# update the current configuration with the loaded values
|
||||
cfg.update(cfg_tmp)
|
||||
|
||||
|
||||
|
||||
def format_maybe(v):
|
||||
"""little hack to make printing things that may come out as None nicer"""
|
||||
if v is None:
|
||||
return "unknown"
|
||||
else:
|
||||
return str(v)
|
||||
|
||||
class Book:
|
||||
def __init__(self, author, title, year, ISBN=None, description=""):
|
||||
"""Any of these may be None to indicate 'unknown'."""
|
||||
self.author = author
|
||||
self.title = title
|
||||
self.year = year
|
||||
self.ISBN = ISBN
|
||||
self.description = description
|
||||
self.signout = None
|
||||
def sign_out(self, username):
|
||||
if self.signout is None:
|
||||
self.signout = Signout(username)
|
||||
else:
|
||||
raise Exception("Book already signed out to %s" % self.signout.name, self)
|
||||
def sign_in(self):
|
||||
if self.signout is not None:
|
||||
self.signout = None
|
||||
else:
|
||||
raise Exception("Book was not signed out, no need to sign it in")
|
||||
|
||||
def __str__(self):
|
||||
author = self.author
|
||||
book = "%s [%s]\nBy: %s" % (format_maybe(self.title), format_maybe(self.year), format_maybe(self.author))
|
||||
if self.signout:
|
||||
book += "\n Signed out by %s on %s" % (self.signout.name, time.ctime(self.signout.date))
|
||||
return book
|
||||
|
||||
def __repr__(self):
|
||||
return "Book(author=%r, title=%r, year=%r, signout=%r)" % (self.author, self.title, self.year, self.signout)
|
||||
|
||||
class Signout:
|
||||
"""Represents a sign-out of a book to someone. Automatically records when the signout occured"""
|
||||
def __init__(self, name):
|
||||
#in theory we could check that the name given to us is in LDAP
|
||||
self.name = str(name)
|
||||
self.date = time.time()
|
||||
def __repr__(self):
|
||||
return "Signout(%r, %s)" % (self.name, time.ctime(self.date))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def reset():
|
||||
"""make a fresh database"""
|
||||
shelve.open(cfg['library_db_path'],'n').close()
|
||||
|
||||
|
||||
def add(author, title, year):
|
||||
db = shelve.open(cfg['library_db_path'],'c') #use w here (not c) to ensure a crash if the DB file got erased (is this a good idea?)
|
||||
isbn = str(len(db)) #not true, but works for now
|
||||
db[isbn] = Book(author, title, year, isbn)
|
||||
db.close()
|
||||
|
||||
def search(author=None, title=None, year=None, ISBN=None, description=None, signedout=None):
|
||||
"""search for a title
|
||||
author and title are regular expressions
|
||||
year is a single number or a list of numbers (so use range() to search the DB)
|
||||
whichever ones passed in that aren't None are the restrictions used in the search
|
||||
possibly-useful side effect of this design is that search() just gets the list of everything
|
||||
this is extraordinarily inefficient, but whatever (I don't think that without having an indexer run inthe background we can improve this any?)
|
||||
returns: a sequence of Book objects
|
||||
""" Load configuration
|
||||
"""
|
||||
db = shelve.open(cfg['library_db_path'], 'c', writeback=True) #open it for writing so that changes to books get saved
|
||||
if type(year) == int:
|
||||
year = [year]
|
||||
def filter(book):
|
||||
"""filter by the given params, but only apply those that are non-None"""
|
||||
#this code is SOOO bad, someone who has a clear head please fix this
|
||||
#we need to apply:
|
||||
b_auth = b_title = b_year = b_ISBN = b_description = b_signedout = True #default to true (in case of None i.e. this doesn't apply)
|
||||
if author is not None:
|
||||
if re.search(author, book.author):
|
||||
b_auth = True
|
||||
else:
|
||||
b_auth = False
|
||||
if title is not None:
|
||||
if re.search(title, book.title): #should factor this out
|
||||
b_title = True
|
||||
else:
|
||||
b_title = False
|
||||
if year is not None: #assume year is a list
|
||||
if book.year in year:
|
||||
b_year = True
|
||||
else:
|
||||
b_year = False
|
||||
if ISBN is not None:
|
||||
if re.search(ISBN, book.ISBN):
|
||||
b_ISBN = True
|
||||
else:
|
||||
b_ISBN = False
|
||||
if description is not None:
|
||||
if re.search(description, book.description):
|
||||
b_description = True
|
||||
else:
|
||||
b_description = False
|
||||
if signedout is not None:
|
||||
b_signedout = signedout == (book.signout is not None)
|
||||
return b_auth and b_title and b_year and b_ISBN and b_description and b_signedout
|
||||
cfg_fields = [ "library_connect_string" ]
|
||||
|
||||
for i in db:
|
||||
book = db[i]
|
||||
if(filter(book)):
|
||||
yield book
|
||||
db.close()
|
||||
|
||||
|
||||
def save(book):
|
||||
db = shelve.open(cfg['library_db_path'], "w")
|
||||
assert book.ISBN is not None, "We should really handle this case better, like making an ISBN or something"
|
||||
db[book.ISBN] = book
|
||||
db.close()
|
||||
|
||||
def delete(book):
|
||||
db = shelve.open(cfg['library_db_path'], "w")
|
||||
del db[book.ISBN]
|
||||
temp_cfg = conf.read(CONFIG_FILE)
|
||||
conf.check_string_fields(CONFIG_FILE, cfg_fields, temp_cfg)
|
||||
cfg.update(temp_cfg)
|
||||
|
||||
sqlhub.processConnection = connectionForURI(cfg["library_connect_string"])
|
||||
|
||||
#def delete(....):
|
||||
# """must think about how to do this one; it'll have to be tied to the DB ID somehow"""
|
||||
# pass
|
||||
class Book(SQLObject):
|
||||
isbn = StringCol()
|
||||
title = StringCol()
|
||||
description = StringCol()
|
||||
year = StringCol()
|
||||
publisher = StringCol()
|
||||
authors = SQLRelatedJoin("Author")
|
||||
signouts = SQLMultipleJoin("Signout")
|
||||
|
||||
if __name__ == '__main__':
|
||||
#print "Making database"
|
||||
#reset()
|
||||
#print
|
||||
#print "Filling database"
|
||||
#add("Bob McBob", "My Life Of Crime", None)
|
||||
print
|
||||
print "Listing database"
|
||||
for b in search():
|
||||
#b.sign_out("nguenthe")
|
||||
print b
|
||||
def sign_out(self, u):
|
||||
s = Signout(username=u, book=self,
|
||||
outdate=datetime.today(), indate=None)
|
||||
|
||||
def sign_in(self, u):
|
||||
s = self.signouts.filter(AND(Signout.q.indate==None, Signout.q.username==u))
|
||||
if s.count() > 0:
|
||||
s.orderBy(Signout.q.outdate).limit(1).getOne(None).sign_in()
|
||||
return True
|
||||
else:
|
||||
raise Exception("PEBKAC: Book not signed out!")
|
||||
|
||||
def __str__(self):
|
||||
book = "%s [%s]" % (self.title, self.year)
|
||||
book += "\nBy: "
|
||||
for a in self.authors:
|
||||
book += a.name
|
||||
book += ", "
|
||||
|
||||
if self.authors.count() < 1:
|
||||
book += "(unknown)"
|
||||
|
||||
book = book.strip(", ")
|
||||
|
||||
signouts = self.signouts.filter(Signout.q.indate==None)
|
||||
if signouts.count() > 0:
|
||||
book += "\nSigned Out: "
|
||||
for s in signouts:
|
||||
book += s.username + ", "
|
||||
|
||||
book = book.strip(", ")
|
||||
|
||||
return book
|
||||
|
||||
|
||||
class Author(SQLObject):
|
||||
name = StringCol()
|
||||
books = RelatedJoin("Book")
|
||||
|
||||
class Signout(SQLObject):
|
||||
username = StringCol()
|
||||
book = ForeignKey("Book")
|
||||
outdate = DateCol()
|
||||
indate = DateCol()
|
||||
|
||||
# def __init__(self, u, b, o, i):
|
||||
# username = u
|
||||
# book = b
|
||||
# outdate = o
|
||||
# indate = i
|
||||
|
||||
def sign_in(self):
|
||||
self.indate = datetime.today()
|
||||
|
||||
def _get_due_date(self):
|
||||
"""
|
||||
Compute the due date of the book based on the sign-out
|
||||
date.
|
||||
"""
|
||||
return self.outdate + timedelta(weeks=2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
configure()
|
||||
Book.createTable()
|
||||
Author.createTable()
|
||||
Signout.createTable()
|
||||
print "This functionality isn't implemented yet."
|
||||
|
|
|
@ -3,6 +3,8 @@ from ceo import members
|
|||
from ceo.urwid import search
|
||||
from ceo.urwid.widgets import *
|
||||
from ceo.urwid.window import *
|
||||
from sqlobject.sqlbuilder import *
|
||||
from datetime import datetime
|
||||
|
||||
import ceo.library as lib
|
||||
|
||||
|
@ -12,279 +14,145 @@ def library(data):
|
|||
menu = make_menu([
|
||||
("Checkout Book", checkout_book, None),
|
||||
("Return Book", return_book, None),
|
||||
("Search Books", search_books, None),
|
||||
("Add Book", add_book, None),
|
||||
#("Remove Book", remove_book, None),
|
||||
# ("Search Books", search_books, None),
|
||||
# ("Add Book", add_book, None),
|
||||
("Back", raise_back, None),
|
||||
])
|
||||
push_window(menu, "Library")
|
||||
|
||||
def search_books(data):
|
||||
menu = make_menu([
|
||||
("Overdue Books", overdue_books, None),
|
||||
])
|
||||
push_window(menu, "Book Search")
|
||||
|
||||
def overdue_books(data):
|
||||
None
|
||||
|
||||
def checkout_book(data):
|
||||
"should only search signed in books"
|
||||
view_books(lib.search(signedout=False))
|
||||
push_wizard("Checkout", [CheckoutPage, BookSearchPage, ConfirmPage])
|
||||
|
||||
def return_book(data):
|
||||
"should bring up a searchbox of all the guys first"
|
||||
view_books(lib.search(signedout=True))
|
||||
push_wizard("Checkout", [CheckinPage, ConfirmPage])
|
||||
|
||||
def search_books(data):
|
||||
push_window(urwid.Filler(SearchPage(), valign='top'), "Search Books")
|
||||
|
||||
def view_book(book):
|
||||
"this should develop into a full fledged useful panel for doing stuff with books. for now it's not."
|
||||
push_window(urwid.Filler(BookPage(book), valign='top'), "Book detail")
|
||||
|
||||
def view_books(books):
|
||||
#XXX should not use a hardcoded 20 in there, should grab the value from the width of the widget
|
||||
#TODO: this should take the search arguments, and stash them away, and everytime you come back to this page it should refresh itself
|
||||
widgets = []
|
||||
for b in books:
|
||||
widgets.append(urwid.AttrWrap(ButtonText(view_book, b, str(b)), None, 'selected'))
|
||||
widgets.append(urwid.Divider())
|
||||
push_window(urwid.ListBox(widgets))
|
||||
|
||||
def add_book(data):
|
||||
push_wizard("Add Book", [AddBookPage])
|
||||
|
||||
#def remove_book(data):
|
||||
# pass
|
||||
|
||||
|
||||
def parse_commaranges(s):
|
||||
"""parse a string into a list of numbers"""
|
||||
"""Fixme: this should be in a different module"""
|
||||
def numbers(section):
|
||||
if "-" in section:
|
||||
range_ = section.split("-")
|
||||
assert len(range_) == 2
|
||||
start = int(range_[0])
|
||||
end = int(range_[1])
|
||||
return range(start, end+1) #+1 to be inclusive of end
|
||||
else:
|
||||
return [int(section)]
|
||||
|
||||
l = []
|
||||
for y in s.split(","):
|
||||
l.extend(numbers(y))
|
||||
return l
|
||||
|
||||
|
||||
class AddBookPage(WizardPanel):
|
||||
class BookSearchPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.author = SingleEdit("Author: ")
|
||||
self.search = None
|
||||
self.state["book"] = None
|
||||
self.isbn = SingleEdit("ISBN: ")
|
||||
self.title = SingleEdit("Title: ")
|
||||
self.year = SingleIntEdit("Year(s): ")
|
||||
|
||||
self.widgets = [
|
||||
urwid.Text("Add Book"),
|
||||
urwid.Text("Book Search"),
|
||||
urwid.Text("(Only one field required.)"),
|
||||
urwid.Divider(),
|
||||
self.author,
|
||||
self.title,
|
||||
self.year,
|
||||
self.isbn,
|
||||
self.title
|
||||
]
|
||||
|
||||
|
||||
def check(self):
|
||||
author = self.author.get_edit_text()
|
||||
if author == "":
|
||||
author = None #null it so that searching ignores
|
||||
title = self.title.get_edit_text()
|
||||
if title == "":
|
||||
title = None
|
||||
try:
|
||||
year = self.year.get_edit_text()
|
||||
if year == "":
|
||||
year = None
|
||||
else:
|
||||
year = int(year)
|
||||
except:
|
||||
self.focus_widget(self.year)
|
||||
set_status("Invalid year")
|
||||
if self.state["book"] is None:
|
||||
push_window(SearchPage(self.isbn.get_edit_text(),
|
||||
self.title.get_edit_text(),
|
||||
None,
|
||||
self.state))
|
||||
return True
|
||||
lib.add(author, title, year)
|
||||
raise_back()
|
||||
|
||||
|
||||
|
||||
class SearchPage(urwid.WidgetWrap):
|
||||
"""
|
||||
TODO: need to be able to jump to "search" button quickly; perhaps trap a certain keypress?
|
||||
"""
|
||||
def __init__(self):
|
||||
self.author = SingleEdit("Author: ")
|
||||
self.title = SingleEdit("Title: ")
|
||||
self.year = SingleEdit("Year(s): ")
|
||||
self.ISBN = SingleEdit("ISBN: ")
|
||||
self.description = urwid.Edit("Description: ", multiline=True)
|
||||
self.signedout = urwid.CheckBox(": Checked Out")
|
||||
self.ok = urwid.Button("Search", self.search)
|
||||
self.back = urwid.Button("Back", raise_back)
|
||||
widgets = [
|
||||
#urwid.Text("Search Library"),
|
||||
#urwid.Divider(),
|
||||
self.author,
|
||||
self.title,
|
||||
self.year,
|
||||
self.ISBN,
|
||||
self.description,
|
||||
self.signedout,
|
||||
urwid.Divider(),
|
||||
urwid.Text("String fields are regexes.\nYear is a comma-separated list or a hyphen-separated range")
|
||||
]
|
||||
buttons = urwid.GridFlow([self.ok, self.back], 10, 3, 1, align='right')
|
||||
urwid.WidgetWrap.__init__(self, urwid.Pile([urwid.Pile(widgets), buttons]))
|
||||
else:
|
||||
return False
|
||||
|
||||
def search(self, *sender):
|
||||
author = self.author.get_edit_text()
|
||||
if author == "":
|
||||
author = None #null it so that searching ignores
|
||||
title = self.title.get_edit_text()
|
||||
if title == "":
|
||||
title = None
|
||||
try:
|
||||
years = self.year.get_edit_text()
|
||||
if years == "":
|
||||
years = None
|
||||
else:
|
||||
#try to parse the year field
|
||||
years = parse_commaranges( years )
|
||||
except:
|
||||
raise
|
||||
self.focus_widget(self.year)
|
||||
set_status("Invalid year")
|
||||
return True
|
||||
ISBN = self.ISBN.get_edit_text()
|
||||
if ISBN == "": ISBN = None
|
||||
description = self.description.get_edit_text()
|
||||
if description == "": description = None
|
||||
signedout = self.signedout.get_state()
|
||||
view_books(lib.search(author, title, years, ISBN, description, signedout))
|
||||
|
||||
|
||||
|
||||
#DONTUSE
|
||||
class CheckoutPage(urwid.WidgetWrap):
|
||||
def __init__(self, book):
|
||||
self.book = SingleEdit("Book: ") #this needs to be a widget that when you click on it, it takes you to the search_books pane, lets you pick a book, and then drops you back here
|
||||
self.user = SingleEdit("Checkoutee: ")
|
||||
class CheckoutPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.state["user"] = "ERROR"
|
||||
self.state["task"] = "sign_out"
|
||||
self.user = SingleEdit("Username: ")
|
||||
|
||||
self.widgets = [
|
||||
urwid.Text("Checkout A Book"),
|
||||
urwid.Text("Book Checkout"),
|
||||
urwid.Divider(),
|
||||
self.book,
|
||||
self.user,
|
||||
]
|
||||
urwid.WidgetWrap.__init__(self, urwid.Pile(self.widgets))
|
||||
|
||||
#DONTUSE
|
||||
class ConfirmDialog(urwid.WidgetWrap):
|
||||
def __init__(self, msg):
|
||||
raise NotImplementedError
|
||||
def check(self):
|
||||
self.state['user'] = self.user.get_edit_text()
|
||||
|
||||
#DONTUSE
|
||||
def Confirm(msg):
|
||||
#this should be in widgets.py
|
||||
push_window(ConfirmDialog(msg))
|
||||
class ConfirmPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.user = urwid.Text("Username: ")
|
||||
self.book = urwid.Text("Book: ")
|
||||
|
||||
#DONTUSE
|
||||
class InputDialog(urwid.WidgetWrap):
|
||||
def __init__(self, msg=None):
|
||||
msg = urwid.Text(msg)
|
||||
self.input = SingleEdit("")
|
||||
ok = urwid.Button("OK", self.ok)
|
||||
cancel = urwid.Button("Cancel", self.cancel)
|
||||
buttons = urwid.Columns([ok, cancel])
|
||||
display = urwid.Pile([msg, self.input, buttons])
|
||||
urwid.WidgetWrap.__init__(self, display)
|
||||
def ok():
|
||||
self.result = self.input.get_edit_text()
|
||||
raise Abort() #break out of the inner event loop
|
||||
def cancel():
|
||||
self.result = None
|
||||
raise Abort()
|
||||
|
||||
#DONTUSE
|
||||
def urwid_input(msg):
|
||||
#this should be in widgets.py
|
||||
dialog = InputDialog(msg)
|
||||
push_window(dialog)
|
||||
event_loop(urwid.main.ui) #HACK
|
||||
return dialog.result
|
||||
|
||||
|
||||
def do_delete(book):
|
||||
if Confirm("Do you wish to delete %r?" % book):
|
||||
lib.delete(book)
|
||||
|
||||
class BookPageBase(urwid.WidgetWrap):
|
||||
def __init__(self):
|
||||
self.author = SingleEdit("Author: ")
|
||||
self.title = SingleEdit("Title: ")
|
||||
self.year = SingleIntEdit("Year: ")
|
||||
self.ISBN = urwid.Text("ISBN: ")
|
||||
self.description = urwid.Edit("Description: ", multiline=True)
|
||||
|
||||
buttons = urwid.GridFlow(self._init_buttons(), 13, 2, 1, 'center')
|
||||
display = urwid.Pile([self.author, self.title, self.year, self.ISBN, self.description,] +
|
||||
self._init_widgets() +
|
||||
[urwid.Divider(), buttons])
|
||||
urwid.WidgetWrap.__init__(self, display)
|
||||
self.refresh()
|
||||
|
||||
def _init_widgets(self):
|
||||
return []
|
||||
def _init_buttons(self):
|
||||
return []
|
||||
def refresh(self, *sender):
|
||||
"""update the widgets from the data model"""
|
||||
self.author.set_edit_text(self._book.author)
|
||||
self.title.set_edit_text(self._book.title)
|
||||
self.year.set_edit_text(str(self._book.year))
|
||||
self.ISBN.set_text("ISBN: " + self._book.ISBN)
|
||||
self.description.set_edit_text(self._book.description)
|
||||
|
||||
|
||||
class BookPage(BookPageBase):
|
||||
def __init__(self, book):
|
||||
self._book = book
|
||||
BookPageBase.__init__(self)
|
||||
def _init_widgets(self):
|
||||
self.checkout_label = urwid.Text("")
|
||||
return [self.checkout_label]
|
||||
def _init_buttons(self):
|
||||
save = urwid.Button("Save", self.save)
|
||||
self.checkout_button = urwid.Button("", self.checkout)
|
||||
back = urwid.Button("Back", raise_back)
|
||||
remove = urwid.Button("Delete", self.delete)
|
||||
return [back, self.checkout_button, save, remove]
|
||||
|
||||
#all these *senders are to allow these to be used as event handlers or just on their own
|
||||
def refresh(self, *sender):
|
||||
BookPageBase.refresh(self, *sender)
|
||||
if self._book.signout is None:
|
||||
self.checkout_label.set_text("Checked In")
|
||||
self.checkout_button.set_label("Check Out")
|
||||
title = ""
|
||||
if self.state["task"] and self.state["task"]=="sign_in":
|
||||
title = "Checkin"
|
||||
else:
|
||||
self.checkout_label.set_text(self._book.signout._repr_())
|
||||
self.checkout_button.set_label("Check In")
|
||||
title = "Checkout"
|
||||
|
||||
self.widgets = [
|
||||
urwid.Text("Confirm " + title),
|
||||
urwid.Divider(),
|
||||
self.user,
|
||||
self.book
|
||||
]
|
||||
|
||||
def activate(self):
|
||||
self.user.set_text("Username: " + self.state["user"])
|
||||
if self.state["book"]:
|
||||
self.book.set_text("Book: " + self.state["book"].title)
|
||||
|
||||
def check(self):
|
||||
#TODO: Validate user at some point (preferrably user entry screen)
|
||||
if self.state["task"] and self.state["task"]=="sign_in":
|
||||
self.state["book"].sign_in(self.state["user"])
|
||||
else:
|
||||
self.state["book"].sign_out(self.state["user"])
|
||||
pop_window()
|
||||
|
||||
|
||||
def save(self, *sender):
|
||||
self._book.author = self.author.get_edit_text()
|
||||
self._book.title = self.title.get_edit_text()
|
||||
yeartmp = self.year.get_edit_text()
|
||||
if yeartmp is not None: yeartmp = int(yeartmp)
|
||||
self._book.year = yeartmp
|
||||
#self._book.ISBN = .... #no... don't do this...
|
||||
self._book.description = self.description.get_edit_text()
|
||||
lib.save(self._book)
|
||||
self.refresh()
|
||||
|
||||
def checkout(self, *sender):
|
||||
username = "nguenthe"
|
||||
self._book.sign_out(username)
|
||||
self.save()
|
||||
|
||||
def checkin(self, *sender):
|
||||
self._book.sign_in()
|
||||
self.save()
|
||||
|
||||
def delete(self, *sender):
|
||||
lib.delete(self._book)
|
||||
raise_back()
|
||||
class SearchPage(urwid.WidgetWrap):
|
||||
def __init__(self, isbn, title, user, state):
|
||||
self.state = state
|
||||
books = []
|
||||
widgets = []
|
||||
if not title is None and not title=="":
|
||||
books = lib.Book.select(LIKE(lib.Book.q.title, "%" + title + "%"))
|
||||
elif not isbn is None and not isbn=="":
|
||||
books = lib.Book.select(lib.Book.q.isbn==isbn)
|
||||
elif not user is None and not user=="":
|
||||
st = lib.Signout.select(AND(lib.Signout.q.username==user, lib.Signout.q.indate==None))
|
||||
for s in st:
|
||||
books.append(s.book)
|
||||
|
||||
for b in books:
|
||||
widgets.append(urwid.AttrWrap(ButtonText(self.select, b, str(b)),
|
||||
None, 'selected'))
|
||||
widgets.append(urwid.Divider())
|
||||
|
||||
urwid.WidgetWrap.__init__(self, urwid.ListBox(widgets))
|
||||
|
||||
def select(self, book):
|
||||
self.state["book"] = book
|
||||
pop_window()
|
||||
|
||||
class CheckinPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.state["book"] = None
|
||||
self.state["user"] = "ERROR"
|
||||
self.state["task"] = "sign_in"
|
||||
self.user = SingleEdit("Username: ")
|
||||
|
||||
self.widgets = [
|
||||
urwid.Text("Book Checkin"),
|
||||
urwid.Divider(),
|
||||
self.user,
|
||||
]
|
||||
|
||||
def check(self):
|
||||
if self.state["book"] is None:
|
||||
push_window(SearchPage(None,
|
||||
None,
|
||||
self.user.get_edit_text(),
|
||||
self.state))
|
||||
return True
|
||||
else:
|
||||
self.state["user"] = self.user.get_edit_text()
|
||||
return False
|
||||
|
|
Loading…
Reference in New Issue