Added search for books that are signed out.
[public/pyceo-broken.git] / ceo / urwid / library.py
1 import urwid
2 from ceo import members
3 from ceo.urwid import search
4 from ceo.urwid.widgets import *
5 from ceo.urwid.window import *
6 from sqlobject.sqlbuilder import *
7 from datetime import datetime, timedelta
8
9 from ceo import terms
10
11 import ceo.library as lib
12
13
14
15 def library(data):
16     """
17     Create the main menu for the library system.
18     """
19     menu = make_menu([
20         ("Checkout Book", checkout_book, None),
21         ("Return Book", return_book, None),
22         ("Search Books", search_books, None),
23 #        ("Add Book", add_book, None),
24         ("Back", raise_back, None),
25     ])
26     push_window(menu, "Library")
27
28 def search_books(data):
29     """
30     Define menus for searching books.
31     """
32     menu = make_menu([
33         ("Overdue Books", overdue_books, None),
34         ("Signed Out Books", outbooks_search, None),
35     ])
36     push_window(menu, "Book Search")
37
38 def overdue_books(data):
39     """
40     Display a list of all books that are overdue.
41     """
42     oldest = datetime.today() - timedelta(weeks=2)
43     overdue = lib.Signout.select(lib.Signout.q.outdate<oldest)
44
45     widgets = []
46
47     for s in overdue:
48         widgets.append(urwid.AttrWrap(ButtonText(None, s.book, str(s.book)),
49                                       None, 'selected'))
50         widgets.append(urwid.Divider())
51         
52     push_window(urwid.ListBox(widgets))
53
54     None
55
56 def outbooks_search(data):
57     """
58     Display a list of all books that are signed out.
59     """
60     overdue = lib.Signout.select(lib.Signout.q.indate==None)
61
62     widgets = []
63
64     for s in overdue:
65         widgets.append(urwid.AttrWrap(ButtonText(None, s.book, str(s.book)),
66                                       None, 'selected'))
67         widgets.append(urwid.Divider())
68         
69     push_window(urwid.ListBox(widgets))
70
71     None
72
73
74 def checkout_book(data):
75     """
76     Display the book checkout wizard.
77     """
78     push_wizard("Checkout", [CheckoutPage, BookSearchPage, ConfirmPage])
79
80 def return_book(data):
81     """
82     Display the book return wizard.
83     """
84     push_wizard("Checkout", [CheckinPage, ConfirmPage])
85
86 class BookSearchPage(WizardPanel):
87     """
88     The page used when searching for books.
89     """
90     def init_widgets(self):
91         """
92         Initialize the widgets and state variables.
93         """
94         self.search = None
95         self.state["book"] = None
96         self.isbn = SingleEdit("ISBN: ")
97         self.title = SingleEdit("Title: ")
98
99         self.widgets = [
100             urwid.Text("Book Search"),
101             urwid.Text("(Only one field required.)"),
102             urwid.Divider(),
103             self.isbn,
104             self.title
105         ]
106
107     def check(self):
108         """
109         Validate input and update state.
110         """
111         if self.state["book"] is None:
112             push_window(SearchPage(self.isbn.get_edit_text(),
113                                    self.title.get_edit_text(),
114                                    None,
115                                    self.state))
116             return True
117         else:
118             return False
119         
120
121 class CheckoutPage(WizardPanel):
122     """
123     The initial page when checking out a book.
124     """
125     def init_widgets(self):
126         """
127         Initialize widgets and set up state.
128
129         user -> the username to sign the book to
130         task -> used for the confirmation dialog
131         """
132         self.state["user"] = "ERROR"
133         self.state["task"] = "sign_out"
134         self.user = SingleEdit("Username: ")
135         
136         self.widgets = [
137             urwid.Text("Book Checkout"),
138             urwid.Divider(),
139             self.user,
140         ]
141
142     def check(self):
143         self.state['user'] = self.user.get_edit_text()
144         if not members.registered(self.state['user'], terms.current()):
145             set_status("User not registered for this term!")
146             return True
147         return False
148
149 class ConfirmPage(WizardPanel):
150     """
151     The confirmation screen when checking-in and checking-out
152     a book.
153     """
154     def init_widgets(self):
155         """
156         Initialize widgets and state.
157
158         task -> used to deterimine the action
159         """
160         self.user = urwid.Text("Username: ")
161         self.book = urwid.Text("Book: ")
162
163         title = ""
164         if self.state["task"] and self.state["task"]=="sign_in":
165             title = "Checkin"
166         else:
167             title = "Checkout"
168
169         self.widgets = [
170             urwid.Text("Confirm " + title),
171             urwid.Divider(),
172             self.user,
173             self.book
174         ]
175
176     def activate(self):
177         """
178         Ensures that correct data is displayed.
179         """
180         self.user.set_text("Username: " + self.state["user"])
181         if self.state["book"]:
182             self.book.set_text("Book: " + self.state["book"].title)
183
184     def check(self):
185         """
186         Generally used for validation, but in this case it does
187         the actual book check-out.
188         """
189         #TODO: Validate user at some point (preferrably user entry screen)
190         if self.state["task"] and self.state["task"]=="sign_in":
191             self.state["book"].sign_in(self.state["user"])
192         else:
193             self.state["book"].sign_out(self.state["user"])
194         pop_window()
195
196         
197 class SearchPage(urwid.WidgetWrap):
198     """
199     Displays search results.  Can search on isbn,
200     title, or username (for books that are currently
201     out).
202     """
203     def __init__(self, isbn, title, user, state):
204         """
205         This does the actual search, and sets up the screen
206         when it's done.
207
208         title -> search by (partial) title
209         isbn -> search by (partial) isbn
210         user -> search by username (for checked-out books)
211         """
212         self.state = state
213         books = []
214         widgets = []
215         if not title is None and not title=="":
216             books = lib.Book.select(LIKE(lib.Book.q.title, "%" + title + "%"))
217         elif not isbn is None and not isbn=="":
218             books = lib.Book.select(lib.Book.q.isbn==isbn)
219         elif not user is None and not user=="":
220             st = lib.Signout.select(AND(lib.Signout.q.username==user, lib.Signout.q.indate==None))
221             for s in st:
222                 books.append(s.book)
223
224         for b in books:
225             widgets.append(urwid.AttrWrap(ButtonText(self.select, b, str(b)),
226                                           None, 'selected'))
227             widgets.append(urwid.Divider())
228
229         urwid.WidgetWrap.__init__(self, urwid.ListBox(widgets))
230
231     def select(self, book):
232         """
233         Marks a book for check-in or check-out.
234         """
235         self.state["book"] = book
236         pop_window()
237
238 class CheckinPage(WizardPanel):
239     """
240     The initial page to start the check-in widget.
241     """
242     def init_widgets(self):
243         """
244         Throw some widgets on the screen and set up
245         some state.
246
247         book -> The book to check out.
248         user -> Stupid people like books.
249         task -> What are we doing?  (For confirm screen.)
250         """
251         self.state["book"] = None
252         self.state["user"] = "ERROR"
253         self.state["task"] = "sign_in"
254         self.user = SingleEdit("Username: ")
255         
256         self.widgets = [
257             urwid.Text("Book Checkin"),
258             urwid.Divider(),
259             self.user,
260         ]
261
262     def check(self):
263         """
264         Pushes the search window.
265
266         Should validate usernames.
267         """
268         if self.state["book"] is None:
269             push_window(SearchPage(None,
270                                    None,
271                                    self.user.get_edit_text(),
272                                    self.state))
273             return True
274         else:
275             self.state["user"] = self.user.get_edit_text()
276             return False