Things should work.
This commit is contained in:
parent
ffe4056089
commit
e7974c3015
|
@ -9,7 +9,8 @@ CONFIG_FILE = "/etc/csc/library.cf"
|
||||||
cfg = {}
|
cfg = {}
|
||||||
|
|
||||||
def configure():
|
def configure():
|
||||||
""" Load configuration
|
"""
|
||||||
|
Load configuration
|
||||||
"""
|
"""
|
||||||
cfg_fields = [ "library_connect_string" ]
|
cfg_fields = [ "library_connect_string" ]
|
||||||
|
|
||||||
|
@ -20,19 +21,32 @@ def configure():
|
||||||
sqlhub.processConnection = connectionForURI(cfg["library_connect_string"])
|
sqlhub.processConnection = connectionForURI(cfg["library_connect_string"])
|
||||||
|
|
||||||
class Book(SQLObject):
|
class Book(SQLObject):
|
||||||
|
"""
|
||||||
|
A book. This does all the stuff we could
|
||||||
|
ever want to do with a book.
|
||||||
|
"""
|
||||||
isbn = StringCol()
|
isbn = StringCol()
|
||||||
title = StringCol()
|
title = StringCol()
|
||||||
description = StringCol()
|
|
||||||
year = StringCol()
|
year = StringCol()
|
||||||
publisher = StringCol()
|
publisher = StringCol()
|
||||||
authors = SQLRelatedJoin("Author")
|
authors = SQLRelatedJoin("Author")
|
||||||
signouts = SQLMultipleJoin("Signout")
|
signouts = SQLMultipleJoin("Signout")
|
||||||
|
|
||||||
def sign_out(self, u):
|
def sign_out(self, u):
|
||||||
|
"""
|
||||||
|
Call this with a username to sign out
|
||||||
|
a book.
|
||||||
|
"""
|
||||||
s = Signout(username=u, book=self,
|
s = Signout(username=u, book=self,
|
||||||
outdate=datetime.today(), indate=None)
|
outdate=datetime.today(), indate=None)
|
||||||
|
|
||||||
def sign_in(self, u):
|
def sign_in(self, u):
|
||||||
|
"""
|
||||||
|
Call this to check a book back in to
|
||||||
|
the library. Username is used to
|
||||||
|
disambiguate in case more than one
|
||||||
|
copy of this book has been signed out.
|
||||||
|
"""
|
||||||
s = self.signouts.filter(AND(Signout.q.indate==None, Signout.q.username==u))
|
s = self.signouts.filter(AND(Signout.q.indate==None, Signout.q.username==u))
|
||||||
if s.count() > 0:
|
if s.count() > 0:
|
||||||
s.orderBy(Signout.q.outdate).limit(1).getOne(None).sign_in()
|
s.orderBy(Signout.q.outdate).limit(1).getOne(None).sign_in()
|
||||||
|
@ -41,6 +55,10 @@ class Book(SQLObject):
|
||||||
raise Exception("PEBKAC: Book not signed out!")
|
raise Exception("PEBKAC: Book not signed out!")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
Magic drugs to make books display
|
||||||
|
nicely.
|
||||||
|
"""
|
||||||
book = "%s [%s]" % (self.title, self.year)
|
book = "%s [%s]" % (self.title, self.year)
|
||||||
book += "\nBy: "
|
book += "\nBy: "
|
||||||
for a in self.authors:
|
for a in self.authors:
|
||||||
|
@ -64,22 +82,30 @@ class Book(SQLObject):
|
||||||
|
|
||||||
|
|
||||||
class Author(SQLObject):
|
class Author(SQLObject):
|
||||||
|
"""
|
||||||
|
An author can author many books, and a book
|
||||||
|
can have many authors. This lets us map
|
||||||
|
both ways.
|
||||||
|
"""
|
||||||
name = StringCol()
|
name = StringCol()
|
||||||
books = RelatedJoin("Book")
|
books = RelatedJoin("Book")
|
||||||
|
|
||||||
class Signout(SQLObject):
|
class Signout(SQLObject):
|
||||||
|
"""
|
||||||
|
An instance of a signout associates usernames,
|
||||||
|
books, signout dates, and return dates to mark
|
||||||
|
that a book has been signed out by a particular
|
||||||
|
user.
|
||||||
|
"""
|
||||||
username = StringCol()
|
username = StringCol()
|
||||||
book = ForeignKey("Book")
|
book = ForeignKey("Book")
|
||||||
outdate = DateCol()
|
outdate = DateCol()
|
||||||
indate = DateCol()
|
indate = DateCol()
|
||||||
|
|
||||||
# def __init__(self, u, b, o, i):
|
|
||||||
# username = u
|
|
||||||
# book = b
|
|
||||||
# outdate = o
|
|
||||||
# indate = i
|
|
||||||
|
|
||||||
def sign_in(self):
|
def sign_in(self):
|
||||||
|
"""
|
||||||
|
Terminate the signout (return the book).
|
||||||
|
"""
|
||||||
self.indate = datetime.today()
|
self.indate = datetime.today()
|
||||||
|
|
||||||
def _get_due_date(self):
|
def _get_due_date(self):
|
||||||
|
@ -90,8 +116,4 @@ class Signout(SQLObject):
|
||||||
return self.outdate + timedelta(weeks=2)
|
return self.outdate + timedelta(weeks=2)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
configure()
|
|
||||||
Book.createTable()
|
|
||||||
Author.createTable()
|
|
||||||
Signout.createTable()
|
|
||||||
print "This functionality isn't implemented yet."
|
print "This functionality isn't implemented yet."
|
||||||
|
|
|
@ -11,6 +11,9 @@ import ceo.library as lib
|
||||||
|
|
||||||
|
|
||||||
def library(data):
|
def library(data):
|
||||||
|
"""
|
||||||
|
Create the main menu for the library system.
|
||||||
|
"""
|
||||||
menu = make_menu([
|
menu = make_menu([
|
||||||
("Checkout Book", checkout_book, None),
|
("Checkout Book", checkout_book, None),
|
||||||
("Return Book", return_book, None),
|
("Return Book", return_book, None),
|
||||||
|
@ -21,22 +24,40 @@ def library(data):
|
||||||
push_window(menu, "Library")
|
push_window(menu, "Library")
|
||||||
|
|
||||||
def search_books(data):
|
def search_books(data):
|
||||||
|
"""
|
||||||
|
Define menus for searching books.
|
||||||
|
"""
|
||||||
menu = make_menu([
|
menu = make_menu([
|
||||||
("Overdue Books", overdue_books, None),
|
("Overdue Books", overdue_books, None),
|
||||||
])
|
])
|
||||||
push_window(menu, "Book Search")
|
push_window(menu, "Book Search")
|
||||||
|
|
||||||
def overdue_books(data):
|
def overdue_books(data):
|
||||||
|
"""
|
||||||
|
Display a list of all books that are overdue.
|
||||||
|
"""
|
||||||
None
|
None
|
||||||
|
|
||||||
def checkout_book(data):
|
def checkout_book(data):
|
||||||
|
"""
|
||||||
|
Display the book checkout wizard.
|
||||||
|
"""
|
||||||
push_wizard("Checkout", [CheckoutPage, BookSearchPage, ConfirmPage])
|
push_wizard("Checkout", [CheckoutPage, BookSearchPage, ConfirmPage])
|
||||||
|
|
||||||
def return_book(data):
|
def return_book(data):
|
||||||
|
"""
|
||||||
|
Display the book return wizard.
|
||||||
|
"""
|
||||||
push_wizard("Checkout", [CheckinPage, ConfirmPage])
|
push_wizard("Checkout", [CheckinPage, ConfirmPage])
|
||||||
|
|
||||||
class BookSearchPage(WizardPanel):
|
class BookSearchPage(WizardPanel):
|
||||||
|
"""
|
||||||
|
The page used when searching for books.
|
||||||
|
"""
|
||||||
def init_widgets(self):
|
def init_widgets(self):
|
||||||
|
"""
|
||||||
|
Initialize the widgets and state variables.
|
||||||
|
"""
|
||||||
self.search = None
|
self.search = None
|
||||||
self.state["book"] = None
|
self.state["book"] = None
|
||||||
self.isbn = SingleEdit("ISBN: ")
|
self.isbn = SingleEdit("ISBN: ")
|
||||||
|
@ -51,6 +72,9 @@ class BookSearchPage(WizardPanel):
|
||||||
]
|
]
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
|
"""
|
||||||
|
Validate input and update state.
|
||||||
|
"""
|
||||||
if self.state["book"] is None:
|
if self.state["book"] is None:
|
||||||
push_window(SearchPage(self.isbn.get_edit_text(),
|
push_window(SearchPage(self.isbn.get_edit_text(),
|
||||||
self.title.get_edit_text(),
|
self.title.get_edit_text(),
|
||||||
|
@ -62,7 +86,16 @@ class BookSearchPage(WizardPanel):
|
||||||
|
|
||||||
|
|
||||||
class CheckoutPage(WizardPanel):
|
class CheckoutPage(WizardPanel):
|
||||||
|
"""
|
||||||
|
The initial page when checking out a book.
|
||||||
|
"""
|
||||||
def init_widgets(self):
|
def init_widgets(self):
|
||||||
|
"""
|
||||||
|
Initialize widgets and set up state.
|
||||||
|
|
||||||
|
user -> the username to sign the book to
|
||||||
|
task -> used for the confirmation dialog
|
||||||
|
"""
|
||||||
self.state["user"] = "ERROR"
|
self.state["user"] = "ERROR"
|
||||||
self.state["task"] = "sign_out"
|
self.state["task"] = "sign_out"
|
||||||
self.user = SingleEdit("Username: ")
|
self.user = SingleEdit("Username: ")
|
||||||
|
@ -77,7 +110,16 @@ class CheckoutPage(WizardPanel):
|
||||||
self.state['user'] = self.user.get_edit_text()
|
self.state['user'] = self.user.get_edit_text()
|
||||||
|
|
||||||
class ConfirmPage(WizardPanel):
|
class ConfirmPage(WizardPanel):
|
||||||
|
"""
|
||||||
|
The confirmation screen when checking-in and checking-out
|
||||||
|
a book.
|
||||||
|
"""
|
||||||
def init_widgets(self):
|
def init_widgets(self):
|
||||||
|
"""
|
||||||
|
Initialize widgets and state.
|
||||||
|
|
||||||
|
task -> used to deterimine the action
|
||||||
|
"""
|
||||||
self.user = urwid.Text("Username: ")
|
self.user = urwid.Text("Username: ")
|
||||||
self.book = urwid.Text("Book: ")
|
self.book = urwid.Text("Book: ")
|
||||||
|
|
||||||
|
@ -95,11 +137,18 @@ class ConfirmPage(WizardPanel):
|
||||||
]
|
]
|
||||||
|
|
||||||
def activate(self):
|
def activate(self):
|
||||||
|
"""
|
||||||
|
Ensures that correct data is displayed.
|
||||||
|
"""
|
||||||
self.user.set_text("Username: " + self.state["user"])
|
self.user.set_text("Username: " + self.state["user"])
|
||||||
if self.state["book"]:
|
if self.state["book"]:
|
||||||
self.book.set_text("Book: " + self.state["book"].title)
|
self.book.set_text("Book: " + self.state["book"].title)
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
|
"""
|
||||||
|
Generally used for validation, but in this case it does
|
||||||
|
the actual book check-out.
|
||||||
|
"""
|
||||||
#TODO: Validate user at some point (preferrably user entry screen)
|
#TODO: Validate user at some point (preferrably user entry screen)
|
||||||
if self.state["task"] and self.state["task"]=="sign_in":
|
if self.state["task"] and self.state["task"]=="sign_in":
|
||||||
self.state["book"].sign_in(self.state["user"])
|
self.state["book"].sign_in(self.state["user"])
|
||||||
|
@ -109,7 +158,20 @@ class ConfirmPage(WizardPanel):
|
||||||
|
|
||||||
|
|
||||||
class SearchPage(urwid.WidgetWrap):
|
class SearchPage(urwid.WidgetWrap):
|
||||||
|
"""
|
||||||
|
Displays search results. Can search on isbn,
|
||||||
|
title, or username (for books that are currently
|
||||||
|
out).
|
||||||
|
"""
|
||||||
def __init__(self, isbn, title, user, state):
|
def __init__(self, isbn, title, user, state):
|
||||||
|
"""
|
||||||
|
This does the actual search, and sets up the screen
|
||||||
|
when it's done.
|
||||||
|
|
||||||
|
title -> search by (partial) title
|
||||||
|
isbn -> search by (partial) isbn
|
||||||
|
user -> search by username (for checked-out books)
|
||||||
|
"""
|
||||||
self.state = state
|
self.state = state
|
||||||
books = []
|
books = []
|
||||||
widgets = []
|
widgets = []
|
||||||
|
@ -130,11 +192,25 @@ class SearchPage(urwid.WidgetWrap):
|
||||||
urwid.WidgetWrap.__init__(self, urwid.ListBox(widgets))
|
urwid.WidgetWrap.__init__(self, urwid.ListBox(widgets))
|
||||||
|
|
||||||
def select(self, book):
|
def select(self, book):
|
||||||
|
"""
|
||||||
|
Marks a book for check-in or check-out.
|
||||||
|
"""
|
||||||
self.state["book"] = book
|
self.state["book"] = book
|
||||||
pop_window()
|
pop_window()
|
||||||
|
|
||||||
class CheckinPage(WizardPanel):
|
class CheckinPage(WizardPanel):
|
||||||
|
"""
|
||||||
|
The initial page to start the check-in widget.
|
||||||
|
"""
|
||||||
def init_widgets(self):
|
def init_widgets(self):
|
||||||
|
"""
|
||||||
|
Throw some widgets on the screen and set up
|
||||||
|
some state.
|
||||||
|
|
||||||
|
book -> The book to check out.
|
||||||
|
user -> Stupid people like books.
|
||||||
|
task -> What are we doing? (For confirm screen.)
|
||||||
|
"""
|
||||||
self.state["book"] = None
|
self.state["book"] = None
|
||||||
self.state["user"] = "ERROR"
|
self.state["user"] = "ERROR"
|
||||||
self.state["task"] = "sign_in"
|
self.state["task"] = "sign_in"
|
||||||
|
@ -147,6 +223,11 @@ class CheckinPage(WizardPanel):
|
||||||
]
|
]
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
|
"""
|
||||||
|
Pushes the search window.
|
||||||
|
|
||||||
|
Should validate usernames.
|
||||||
|
"""
|
||||||
if self.state["book"] is None:
|
if self.state["book"] is None:
|
||||||
push_window(SearchPage(None,
|
push_window(SearchPage(None,
|
||||||
None,
|
None,
|
||||||
|
|
Loading…
Reference in New Issue