class ItemsHiring(App): """ Main application class """ status_text = StringProperty() def on_start(self): """ Load items from the CSV file on start """ source_items = load_items() self.item_list = ItemList(source_items) self.create_item_buttons() self.set_mode(Mode.listing) def on_stop(self): """ Save items to the CSV file on exit """ save_items(self.item_list.export_items()) def build(self): """ Build Kivy app from the kv file """ self.title = "Items Hiring" self.root = Builder.load_file('app.kv') return self.root def create_item_button(self, item, item_index): """ Create an item button for a certain item """ button = ItemButton(text=item.name, hired=item.hired, item_index=item_index) button.bind(on_release=self.press_item) self.root.ids.itemsGrid.add_widget(button) def create_item_buttons(self): """ Create the item buttons and add them to the GUI """ for i, item in enumerate(self.item_list.items): self.create_item_button(item, i) def clear_selection(self): """ Clear any buttons that have been selected """ for instance in self.root.ids.itemsGrid.children: instance.set_selected(False) def update_item_buttons(self): """ Update colours of item buttons """ for instance in self.root.ids.itemsGrid.children: item = self.item_list.items[instance.item_index] instance.hired = item.hired def set_mode(self, mode): """ Switch between 'listing', 'hiring' and 'returning' modes """ ids = self.root.ids for button in [ids.buttonList, ids.buttonHire, ids.buttonReturn]: button.state = 'normal' if mode == Mode.listing: self.status_text = "Choose action from the left menu, then select items on the right" ids.buttonList.state = 'down' elif mode == Mode.hiring: if self.item_list.count(hired=False) > 0: self.status_text = "Select available items to hire" else: self.status_text = "No items available for hire" ids.buttonHire.state = 'down' elif mode == Mode.returning: if self.item_list.count(hired=True) > 0: self.status_text = "Select available items to return" else: self.status_text = "No items are currently on hire" ids.buttonReturn.state = 'down' self.clear_selection() self.mode = mode def show_details(self, item_index): """ Display item details in the status bar """ item = self.item_list.items[item_index] status = "out" if item.hired else "in" self.status_text = "{}({}), ${:.2f} is {}".format(item.name, item.description, item.price, status) def selecting_allowed(self, hired): """ Check if item is selectable depending on hired status """ if self.mode == Mode.hiring and not hired: return True elif self.mode == Mode.returning and hired: return True return False def show_selection_status(self): """ Display selected items in the status bar """ names = [] total_price = 0 for i, button in enumerate(self.root.ids.itemsGrid.children): item = self.item_list.items[button.item_index] if button.selected: names.append(item.name) total_price += item.price if len(names) == 0: names = "no items" else: names = ", ".join(names) if self.mode == Mode.hiring: self.status_text = "Hiring: {} for ${:.2f}".format(names, total_price) else: self.status_text = "Returning: {}".format(names) def press_item(self, instance): """ Handler for pressing an item button """ if self.mode == Mode.listing: self.show_details(instance.item_index) return if self.selecting_allowed(instance.hired): instance.set_selected(not instance.selected) self.show_selection_status() def press_list(self): """ Handler for pressing the 'List Items' button """ self.set_mode(Mode.listing) def press_hire(self): """ Handler for pressing the 'Hire Items' button """ self.set_mode(Mode.hiring) def press_return(self): """ Handler for pressing the 'Return Items' button """ self.set_mode(Mode.returning) def press_confirm(self): """ Handler for pressing the 'Conrirm' button """ for button in self.root.ids.itemsGrid.children: if not button.selected: continue item = self.item_list.items[button.item_index] if self.mode == Mode.hiring: self.item_list.hire_item(button.item_index) elif self.mode == Mode.returning: self.item_list.return_item(button.item_index) self.update_item_buttons() self.set_mode(Mode.listing) def press_add(self): """ Handler for pressing the 'Add New Item' button """ self.status_text = "Enter details for new item" self.root.ids.popup.open() def press_save(self, name, description, price): """ Handler for pressing the save button in the popup """ if not name or not description or not price: self.status_text = "All fields must be completed" return try: price = float(price) except: self.status_text = "Price must be a valid number" return if price <= 0: self.status_text = "Price must not be negative or zero" return self.item_list.add_item(name, description, price, hired=False) last_index = len(self.item_list.items) - 1 item = self.item_list.items[last_index] self.create_item_button(item, last_index) self.close_popup() def close_popup(self): """ Close the popup and clear input fields """ self.root.ids.popup.dismiss() self.clear_fields() def clear_fields(self): """ Clear input fields in the popup """ self.root.ids.itemName.text = "" self.root.ids.itemDescription.text = "" self.root.ids.itemPrice.text = ""
class LibraryController(object): """Responsible for all access to objects in library Including all items (books, DVDs and journals), and users.""" MAX_LOANS = 5 MAX_FINE = 50 def _log(self, message): """Poor man's 'logging'!""" if self._verbose: print(message) def __init__(self, verbose=False): """Initialize library, optionally setting verbose mode.""" self._verbose = verbose self._log('Initializing {}'.format(self)) # Initialize internal lists. self._item_list = ItemList() self._user_list = UserList() def add_item(self, item): """Add an item to the library.""" self._log('Adding item: {}'.format(item)) self._item_list.add_item(item) def add_user(self, user): """Add an user to the library.""" #self._log('Adding user: {}'.format(user)) self._user_list.add_user(user) def is_on_loan(self, item_title): """Find out if an item is on load.""" return self._item_list.is_on_loan(item_title) def pay_fine(self, user_id, amount): """Pay a find for a user.""" self._log('Paying fine: {} - {}'.format(user_id, amount)) self._user_list.pay_fine(user_id, amount) def user_checkout(self, user_id, item_title, date=dt.now()): """Checkout an item for a user.""" self._log('User checkout: {} - {}'.format(user_id, item_title)) if self._user_list.able_to_borrow(user_id, self.MAX_LOANS, self.MAX_FINE): self._log('User {} able to borrow.'.format(user_id)) item = self._item_list.checkout_item(item_title, date) self._user_list.checkout_item(user_id, item, date) return True else: self._log('User {} not able to borrow'.format(user_id)) return False def get_user_fine(self, user_id): """Get fines that apply to a user.""" return self._user_list.get_fine_total(user_id) def user_return(self, user_id, item_title): """Return an item for a user.""" self._log('User returning: {} - {}'.format(user_id, item_title)) self._user_list.return_item(user_id, item_title) self._item_list.return_item(item_title)