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