Pretty sure I fixed the check-in bug.
[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
7 import ceo.library as lib
8
9
10
11 def library(data):
12     menu = make_menu([
13         ("Checkout Book", checkout_book, None),
14         ("Return Book", return_book, None),
15         ("Search Books", search_books, None),
16         ("Add Book", add_book, None),
17         #("Remove Book", remove_book, None),
18         ("Back", raise_back, None),
19     ])
20     push_window(menu, "Library")
21
22 def checkout_book(data):
23     "should only search signed in books"
24     view_books(lib.search(signedout=False))
25
26 def return_book(data):
27     "should bring up a searchbox of all the guys first"
28     view_books(lib.search(signedout=True))
29
30 def search_books(data):
31     push_window(urwid.Filler(SearchPage(), valign='top'), "Search Books")
32
33 def view_book(book):
34     "this should develop into a full fledged useful panel for doing stuff with books. for now it's not."
35     push_window(urwid.Filler(BookPage(book), valign='top'), "Book detail")
36
37 def view_books(books):
38     #XXX should not use a hardcoded 20 in there, should grab the value from the width of the widget
39     #TODO: this should take the search arguments, and stash them away, and everytime you come back to this page it should refresh itself
40     widgets = []
41     for b in books:
42         widgets.append(urwid.AttrWrap(ButtonText(view_book, b, str(b)), None, 'selected'))
43         widgets.append(urwid.Divider())
44     push_window(urwid.ListBox(widgets))
45
46 def add_book(data):
47     push_wizard("Add Book", [AddBookPage])
48
49 #def remove_book(data):
50 #    pass
51
52
53 def parse_commaranges(s):
54     """parse a string into a list of numbers"""
55     """Fixme: this should be in a different module"""
56     def numbers(section):
57         if "-" in section:
58             range_ = section.split("-")
59             assert len(range_) == 2
60             start = int(range_[0])
61             end = int(range_[1])
62             return range(start, end+1) #+1 to be inclusive of end
63         else:
64             return [int(section)]
65     
66     l = []
67     for y in s.split(","):
68         l.extend(numbers(y))
69     return l
70
71
72 class AddBookPage(WizardPanel):
73     def init_widgets(self):
74         self.author = SingleEdit("Author: ")
75         self.title = SingleEdit("Title: ")
76         self.year = SingleIntEdit("Year(s): ")
77         self.widgets = [
78             urwid.Text("Add Book"),
79             urwid.Divider(),
80             self.author,
81             self.title,
82             self.year,
83         ]
84     
85     def check(self):
86         author = self.author.get_edit_text()
87         if author == "":
88             author = None #null it so that searching ignores
89         title = self.title.get_edit_text()
90         if title == "":
91             title = None
92         try:
93             year = self.year.get_edit_text()
94             if year == "":
95                 year = None
96             else:
97                 year = int(year)
98         except:
99             self.focus_widget(self.year)
100             set_status("Invalid year")
101             return True
102         lib.add(author, title, year)
103         raise_back()
104
105
106    
107 class SearchPage(urwid.WidgetWrap):
108     """
109     TODO: need to be able to jump to "search" button quickly; perhaps trap a certain keypress?
110     """
111     def __init__(self):
112         self.author = SingleEdit("Author: ")
113         self.title = SingleEdit("Title: ")
114         self.year = SingleEdit("Year(s): ")
115         self.ISBN = SingleEdit("ISBN: ")
116         self.description = urwid.Edit("Description: ", multiline=True)
117         self.signedout = urwid.CheckBox(": Checked Out")
118         self.ok = urwid.Button("Search", self.search)
119         self.back = urwid.Button("Back", raise_back)
120         widgets = [
121             #urwid.Text("Search Library"),
122             #urwid.Divider(),
123             self.author,
124             self.title,
125             self.year,
126             self.ISBN,
127             self.description,
128             self.signedout,
129             urwid.Divider(),
130             urwid.Text("String fields are regexes.\nYear is a comma-separated list or a hyphen-separated range")
131         ]
132         buttons = urwid.GridFlow([self.ok, self.back], 10, 3, 1, align='right')
133         urwid.WidgetWrap.__init__(self, urwid.Pile([urwid.Pile(widgets), buttons]))        
134         
135     def search(self, *sender):
136         author = self.author.get_edit_text()
137         if author == "":
138             author = None #null it so that searching ignores
139         title = self.title.get_edit_text()
140         if title == "":
141             title = None
142         try:
143             years = self.year.get_edit_text()
144             if years == "":
145                 years = None
146             else:
147                 #try to parse the year field
148                 years = parse_commaranges( years )
149         except:
150             raise
151             self.focus_widget(self.year)
152             set_status("Invalid year")
153             return True
154         ISBN = self.ISBN.get_edit_text()
155         if ISBN == "": ISBN = None
156         description = self.description.get_edit_text()
157         if description == "": description = None
158         signedout = self.signedout.get_state()
159         view_books(lib.search(author, title, years, ISBN, description, signedout)) 
160
161
162
163 #DONTUSE
164 class CheckoutPage(urwid.WidgetWrap):
165     def __init__(self, book):
166         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
167         self.user = SingleEdit("Checkoutee: ")
168         self.widgets = [
169             urwid.Text("Checkout A Book"),
170             urwid.Divider(),
171             self.book,
172             self.user,
173         ]
174         urwid.WidgetWrap.__init__(self, urwid.Pile(self.widgets))
175
176 #DONTUSE
177 class ConfirmDialog(urwid.WidgetWrap):
178     def __init__(self, msg):
179         raise NotImplementedError
180
181 #DONTUSE
182 def Confirm(msg):
183     #this should be in widgets.py
184     push_window(ConfirmDialog(msg))
185
186 #DONTUSE
187 class InputDialog(urwid.WidgetWrap):
188     def __init__(self, msg=None):
189         msg = urwid.Text(msg)
190         self.input = SingleEdit("")
191         ok = urwid.Button("OK", self.ok)
192         cancel = urwid.Button("Cancel", self.cancel)
193         buttons = urwid.Columns([ok, cancel])
194         display = urwid.Pile([msg, self.input, buttons])
195         urwid.WidgetWrap.__init__(self, display)
196     def ok():
197         self.result = self.input.get_edit_text()
198         raise Abort() #break out of the inner event loop
199     def cancel():
200         self.result = None
201         raise Abort()
202
203 #DONTUSE
204 def urwid_input(msg):
205     #this should be in widgets.py
206     dialog = InputDialog(msg)
207     push_window(dialog)
208     event_loop(urwid.main.ui) #HACK
209     return dialog.result
210
211
212 def do_delete(book):
213     if Confirm("Do you wish to delete %r?" % book):
214         lib.delete(book)
215
216 class BookPageBase(urwid.WidgetWrap):
217     def __init__(self):
218         self.author = SingleEdit("Author: ")
219         self.title = SingleEdit("Title: ")
220         self.year = SingleIntEdit("Year: ")
221         self.ISBN = urwid.Text("ISBN: ")
222         self.description = urwid.Edit("Description: ", multiline=True)
223
224         buttons = urwid.GridFlow(self._init_buttons(), 13, 2, 1, 'center') 
225         display = urwid.Pile([self.author, self.title, self.year, self.ISBN, self.description,] +
226                                 self._init_widgets() +
227                                 [urwid.Divider(), buttons])
228         urwid.WidgetWrap.__init__(self, display)
229         self.refresh()
230
231     def _init_widgets(self):
232         return []
233     def _init_buttons(self):
234         return []
235     def refresh(self, *sender):
236         """update the widgets from the data model"""
237         self.author.set_edit_text(self._book.author)
238         self.title.set_edit_text(self._book.title)
239         self.year.set_edit_text(str(self._book.year))
240         self.ISBN.set_text("ISBN: " + self._book.ISBN)
241         self.description.set_edit_text(self._book.description)
242
243
244 class BookPage(BookPageBase):
245     def __init__(self, book):
246         self._book = book
247         BookPageBase.__init__(self)
248     def _init_widgets(self):
249         self.checkout_label = urwid.Text("") 
250         return [self.checkout_label]
251     def _init_buttons(self):
252         save = urwid.Button("Save", self.save)
253         self.checkout_button = urwid.Button("", self.checkout)
254         back = urwid.Button("Back", raise_back)
255         remove = urwid.Button("Delete", self.delete)
256         return [back, self.checkout_button, save, remove]
257         
258     #all these *senders are to allow these to be used as event handlers or just on their own
259     def refresh(self, *sender):
260         BookPageBase.refresh(self, *sender)
261         if self._book.signout is None:
262             self.checkout_label.set_text("Checked In")
263             self.checkout_button.set_label("Check Out")
264         else:
265             self.checkout_label.set_text(self._book.signout._repr_())
266             self.checkout_button.set_label("Check In")
267         
268     def save(self, *sender):
269         self._book.author = self.author.get_edit_text()
270         self._book.title = self.title.get_edit_text()
271         yeartmp = self.year.get_edit_text()
272         if yeartmp is not None: yeartmp = int(yeartmp)
273         self._book.year = yeartmp
274         #self._book.ISBN = .... #no... don't do this...
275         self._book.description = self.description.get_edit_text()
276         lib.save(self._book)
277         self.refresh()
278     
279     def checkout(self, *sender):
280         username = "nguenthe"
281         self._book.sign_out(username)
282         self.save()
283     
284     def checkin(self, *sender):
285         self._book.sign_in()
286         self.save()
287     
288     def delete(self, *sender):
289         lib.delete(self._book)
290         raise_back()