Fixing imports.
[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 from ceo.pymazon import PyMazon
9
10 from ceo import terms
11
12 import ceo.library as lib
13
14 CONFIG_FILE = "/etc/csc/library.cf"
15
16 cfg = {}
17
18 def configure():
19     """
20     Load configuration
21     """
22     cfg_fields = [ "aws_account_key" ]
23     temp_cfg = conf.read(CONFIG_FILE)
24     conf.check_string_fields(CONFIG_FILE, cfg_fields, temp_cfg)
25     cfg.update(temp_cfg)
26
27 def library(data):
28     """
29     Create the main menu for the library system.
30     """
31     menu = make_menu([
32         ("Checkout Book", checkout_book, None),
33         ("Return Book", return_book, None),
34         ("Search Books", search_books, None),
35 #        ("Add Book", add_book, None),
36         ("Back", raise_back, None),
37     ])
38     push_window(menu, "Library")
39
40 def search_books(data):
41     """
42     Define menus for searching books.
43     """
44     menu = make_menu([
45         ("Overdue Books", overdue_books, None),
46         ("Signed Out Books", outbooks_search, None),
47     ])
48     push_window(menu, "Book Search")
49
50 def add_book(data):
51     """
52     Add book to library.  Also stab Sapphyre.
53     """
54     push_wizard("Add Book", [BookAddPage])
55
56 def overdue_books(data):
57     """
58     Display a list of all books that are overdue.
59     """
60     oldest = datetime.today() - timedelta(weeks=2)
61     overdue = lib.Signout.select(lib.Signout.q.outdate<oldest)
62
63     widgets = []
64
65     for s in overdue:
66         widgets.append(urwid.AttrWrap(ButtonText(None, s.book, str(s.book)),
67                                       None, 'selected'))
68         widgets.append(urwid.Divider())
69         
70     push_window(urwid.ListBox(widgets))
71
72     None
73
74 def outbooks_search(data):
75     """
76     Display a list of all books that are signed out.
77     """
78     overdue = lib.Signout.select(lib.Signout.q.indate==None)
79
80     widgets = []
81
82     for s in overdue:
83         widgets.append(urwid.AttrWrap(ButtonText(None, s.book, str(s.book)),
84                                       None, 'selected'))
85         widgets.append(urwid.Divider())
86         
87     push_window(urwid.ListBox(widgets))
88
89     None
90
91
92 def checkout_book(data):
93     """
94     Display the book checkout wizard.
95     """
96     push_wizard("Checkout", [CheckoutPage, BookSearchPage, ConfirmPage])
97
98 def return_book(data):
99     """
100     Display the book return wizard.
101     """
102     push_wizard("Checkout", [CheckinPage, ConfirmPage])
103
104 class BookAddPage(WizardPanel):
105     """
106     Thingy for going on screen to add books.
107     """
108     def init_widgets(self):
109         """
110         Make some widgets.
111         """
112         self.isbn = SingleEdit("ISBN: ")
113         
114         self.widgets = [
115             urwid.Text("Adding New Book"),
116             urwid.Divider(),
117             self.isbn
118         ]
119
120     def check(self):
121         """
122         Do black magic.
123         """
124         isbn = self.isbn.get_edit_text()
125
126         try:
127             pymazon = PyMazon(cfg["aws_account_key"])
128             book = pymazon.lookup(isbn)
129
130             currents = lib.Book.select(lib.Book.q.isbn==isbn)
131             if len(currents) == 0:
132                 lib.Book(isbn=isbn, title=book.title,
133                          year=book.year, publisher=book.publisher)
134             else:
135                 sys.stderr.write("Fuck you.\n")
136                 set_status("Book already exists, fucker.")
137                 
138         except PyMazonError, e:
139             sys.stderr.write("Book not added: " + e.message + "\n")
140             set_status("Amazon thinks this is not a book.  Take it up with them.")
141         
142
143 class BookSearchPage(WizardPanel):
144     """
145     The page used when searching for books.
146     """
147     def init_widgets(self):
148         """
149         Initialize the widgets and state variables.
150         """
151         self.search = None
152         self.state["book"] = None
153         self.isbn = SingleEdit("ISBN: ")
154         self.title = SingleEdit("Title: ")
155
156         self.widgets = [
157             urwid.Text("Book Search"),
158             urwid.Text("(Only one field required.)"),
159             urwid.Divider(),
160             self.isbn,
161             self.title
162         ]
163
164     def check(self):
165         """
166         Validate input and update state.
167         """
168         if self.state["book"] is None:
169             push_window(SearchPage(self.isbn.get_edit_text(),
170                                    self.title.get_edit_text(),
171                                    None,
172                                    self.state))
173             return True
174         else:
175             return False
176         
177
178 class CheckoutPage(WizardPanel):
179     """
180     The initial page when checking out a book.
181     """
182     def init_widgets(self):
183         """
184         Initialize widgets and set up state.
185
186         user -> the username to sign the book to
187         task -> used for the confirmation dialog
188         """
189         self.state["user"] = "ERROR"
190         self.state["task"] = "sign_out"
191         self.user = LdapWordEdit(csclub_uri, csclub_base, 'uid', "Username: ")
192         
193         self.widgets = [
194             urwid.Text("Book Checkout"),
195             urwid.Divider(),
196             self.user,
197         ]
198
199     def check(self):
200         self.state['user'] = self.user.get_edit_text()
201         if not members.registered(self.state['user'], terms.current()):
202             set_status("User not registered for this term!")
203             return True
204         return False
205
206 class ConfirmPage(WizardPanel):
207     """
208     The confirmation screen when checking-in and checking-out
209     a book.
210     """
211     def init_widgets(self):
212         """
213         Initialize widgets and state.
214
215         task -> used to deterimine the action
216         """
217         self.user = urwid.Text("Username: ")
218         self.book = urwid.Text("Book: ")
219
220         title = ""
221         if self.state["task"] and self.state["task"]=="sign_in":
222             title = "Checkin"
223         else:
224             title = "Checkout"
225
226         self.widgets = [
227             urwid.Text("Confirm " + title),
228             urwid.Divider(),
229             self.user,
230             self.book
231         ]
232
233     def activate(self):
234         """
235         Ensures that correct data is displayed.
236         """
237         self.user.set_text("Username: " + self.state["user"])
238         if self.state["book"]:
239             self.book.set_text("Book: " + self.state["book"].title)
240
241     def check(self):
242         """
243         Generally used for validation, but in this case it does
244         the actual book check-out.
245         """
246         #TODO: Validate user at some point (preferrably user entry screen)
247         if self.state["task"] and self.state["task"]=="sign_in":
248             self.state["book"].sign_in(self.state["user"])
249         else:
250             self.state["book"].sign_out(self.state["user"])
251         pop_window()
252
253         
254 class SearchPage(urwid.WidgetWrap):
255     """
256     Displays search results.  Can search on isbn,
257     title, or username (for books that are currently
258     out).
259     """
260     def __init__(self, isbn, title, user, state):
261         """
262         This does the actual search, and sets up the screen
263         when it's done.
264
265         title -> search by (partial) title
266         isbn -> search by (partial) isbn
267         user -> search by username (for checked-out books)
268         """
269         self.state = state
270         books = []
271         widgets = []
272         if not title is None and not title=="":
273             books = lib.Book.select(LIKE(lib.Book.q.title, "%" + title + "%"))
274         elif not isbn is None and not isbn=="":
275             books = lib.Book.select(lib.Book.q.isbn==isbn)
276         elif not user is None and not user=="":
277             st = lib.Signout.select(AND(lib.Signout.q.username==user, lib.Signout.q.indate==None))
278             for s in st:
279                 books.append(s.book)
280
281         for b in books:
282             widgets.append(urwid.AttrWrap(ButtonText(self.select, b, str(b)),
283                                           None, 'selected'))
284             widgets.append(urwid.Divider())
285
286         urwid.WidgetWrap.__init__(self, urwid.ListBox(widgets))
287
288     def select(self, book):
289         """
290         Marks a book for check-in or check-out.
291         """
292         self.state["book"] = book
293         pop_window()
294
295 class CheckinPage(WizardPanel):
296     """
297     The initial page to start the check-in widget.
298     """
299     def init_widgets(self):
300         """
301         Throw some widgets on the screen and set up
302         some state.
303
304         book -> The book to check out.
305         user -> Stupid people like books.
306         task -> What are we doing?  (For confirm screen.)
307         """
308         self.state["book"] = None
309         self.state["user"] = "ERROR"
310         self.state["task"] = "sign_in"
311         self.user = LdapWordEdit(csclub_uri, csclub_base, 'uid', "Username: ")
312         
313         self.widgets = [
314             urwid.Text("Book Checkin"),
315             urwid.Divider(),
316             self.user,
317         ]
318
319     def check(self):
320         """
321         Pushes the search window.
322
323         Should validate usernames.
324         """
325         if self.state["book"] is None:
326             push_window(SearchPage(None,
327                                    None,
328                                    self.user.get_edit_text(),
329                                    self.state))
330             return True
331         else:
332             self.state["user"] = self.user.get_edit_text()
333             return False