Skip to content

Instantly share code, notes, and snippets.

@Zalasyu
Last active May 7, 2021 19:22
Show Gist options
  • Save Zalasyu/92ef7df64d099925d9a26d25387a401b to your computer and use it in GitHub Desktop.
Save Zalasyu/92ef7df64d099925d9a26d25387a401b to your computer and use it in GitHub Desktop.
# Author: Alec Moldovan
# Date: 04/20/2021
# Description: This file contains all the core logic and data to simulate a primitive Library.
# Custom exceptions starts here.
class InvalidLibraryState(Exception):
"""
This exception is used when an invalid library location is used.
MUST BE: "ON_SHELF", "ON_HOLD_SHELF", "CHECKED_OUT" """
pass
class DNE_ERROR(Exception):
"""Raised when something does not exist"""
pass
class LocationStateError(Exception):
"""This exception represents when a patron tries to check out something that is not availble for check out"""
pass
# LibraryItem class starts here
class LibraryItem():
"""
This class represents a library item.
"""
def __init__(self, library_item_id, title):
"""
Constructor method for LibraryItem class objects
"""
# Exception - Handling
if not (isinstance(library_item_id, str) and isinstance(title,str)):
raise TypeError('Parameter inputs for LibraryItem must be of type str.')
# Data Members
self._library_item_id = library_item_id
self._title = title
self._location = "ON_SHELF"
self._checked_out_by = None # Patron Object
self._date_checked_out = None # Library current_date attribute
self._requested_by = None # Patron Object
# Getter Methods
def get_library_item_id(self):
"""
Returns the id of a LibraryItem object
"""
return self._library_item_id
def get_title(self):
"""
Returns the title of a LibraryItem object
"""
return self._title
def checked_out_by(self):
"""
Returns attribute _checked_out_by from a LibraryItem object
"""
return self._checked_out_by
def requested_by(self):
"""
Returns attribute _requested_by from a LibraryItem object
"""
return self._requested_by
def date_checked_out(self):
"""
Returns current_date from Library object
"""
return self._date_checked_out
def get_location(self):
"""
Returns the location attribute of a LibraryItem object
"""
return self._location
# Setter Methods
def set_library_item_id(self, new_id):
"""
Reassigns the id of a LibraryItem object
"""
self._library_item_id = new_id
return self._library_item_id
def set_title(self, new_title):
"""
Reassigns the title of a LibraryItem object
"""
self._title = new_title
return self._title
def set_checked_out_by(self, new_patron):
"""
Reassigns the attribute _checked_out_by
"""
self._checked_out_by = new_patron
return self._checked_out_by
def set_requested_by(self, new_patron):
"""
Reassigns the attribute _requested_by
"""
self._requested_by = new_patron
return self._requested_by
def set_date_checked_out(self, current_date):
"""
Assigns the current_date from Library object
"""
self._date_checked_out = current_date
return self._date_checked_out
def set_location(self, new_state):
"""
Sets location attribute's state of a LibraryItem object
"""
accepted_states = ['ON_SHELF','ON_HOLD_SHELF','CHECKED_OUT']
# Excption Jandling for Valid LibraryItem state.
if new_state not in accepted_states:
raise InvalidLibraryState('Invalid location for library to be set to.')
self._location = new_state
return self._location
# Book class starts here
class Book(LibraryItem):
"""
This class represents a library book.
"""
# Represents the time limit a book can be checked out for
def __init__(self, library_item_id,
title, author):
"""
This constructor inherits from LibraryItem.
Has an additional field: author
"""
super().__init__(library_item_id, title)
# Exception - Handling
if not isinstance(author, str):
raise TypeError('Parameter author ust be of type str.')
# Data Members
self._author = author
self._check_out_length = 21
# Getter Methods
def get_author(self):
"""
Returns the _author attibute for a Book object
"""
return self._author
# Setter Methods
def set_author(self, new_author):
"""
Reassigns the _author attribute for a Book object
"""
# Exception-Handling for new author name
if not isinstance(new_author, str):
raise TypeError('New author name must be of type str.')
self._author = new_author
return self._author
# Specialized Methods
def get_check_out_length(self):
"""
Returns the check_out_length time limit cap attribute for a Book object
"""
return self._check_out_length
# Album class starts here
class Movie(LibraryItem):
"""
This class represents an Album in a library.
"""
def __init__(self, library_item_id,
title, director):
"""
This constructor inherits from LibraryItem.
Has an additional field: director
"""
super().__init__(library_item_id, title)
# Exception - Handling
if not isinstance(director, str):
raise TypeError('Parameter director ust be of type str.')
# Data Members
self._director = director
self._check_out_length = 7
# Getter Methods
def get_director(self):
"""
Returns the _director attibute for a Book object
"""
return self._director
# Setter Methods
def set_director(self, new_director):
"""
Reassigns the _director attribute for a Book object
"""
# Exception-Handling for new director name
if not isinstance(new_director, str):
raise TypeError('New director name must be of type str.')
self._director = new_director
return self._director
# Specialized Methods
def get_check_out_length(self):
"""
Returns the check_out_length time limit cap attribute for an Album object
"""
return self._check_out_length
# Album class starts here
class Album(LibraryItem):
"""
This class represents a Movie in a library.
"""
# Represents the time limit a book can be checked out for
def __init__(self, library_item_id,
title, artist):
"""
This constructor inherits from LibraryItem.
Has an additional field: artist
"""
super().__init__(library_item_id, title)
# Exception - Handling
if not isinstance(artist, str):
raise TypeError('Parameter artist ust be of type str.')
# Data Members
self._artist = artist
self._check_out_length = 14
# Getter Methods
def get_artist(self):
"""
Returns the _artist attibute for a Book object
"""
return self._artist
# Setter Methods
def set_artist(self, new_artist):
"""
Reassigns the _artist attribute for a Book object
"""
# Exception-Handling for new artist name
if not isinstance(new_artist, str):
raise TypeError('New artist name must be of type str.')
self._artist = new_artist
return self._artist
# Specialized Methods
def get_check_out_length(self):
"""
Returns the check_out_length time limit cap attribute for a Director object
"""
return self._check_out_length
class Banana(LibraryItem):
"This class represents a Banana"
def __init__(self, library_item_id, title, creator):
"""
This initializer inherits from LibraryItem.
Has an additional field: creator
"""
super().__init__(library_item_id, title)
self._creator = creator
self._check_out_length = 3
# Getter Methods
def get_creator(self):
"""
Returns the _creator attibute for a Banana object
"""
return self._creator
# Setter Methods
def set_creator(self, new_creator):
"""
Reassigns the _creator attribute for a Banana object
"""
# Exception-Handling for new artist name
if not isinstance(new_creator, str):
raise TypeError('New artist name must be of type str.')
self._creator = new_creator
return self._creator
# Specialized Methods
def get_check_out_length(self):
"""
Returns the check_out_length time limit cap attribute for a Director object
"""
return self._check_out_length
class Patron():
"""
This class represens a patron of the library.
(i.e. Someone who uses the library services)
"""
def __init__(self, patron_id, name):
"""
Initializes a Patron object with the parameters:
patron_id, and name
"""
# Exception - Handling
if not (isinstance(patron_id, str) or isinstance(name,str)):
raise TypeError('Parameter inputs for Patron object initialization must be of type str.')
# Data Members
self._patron_id = patron_id
self._name = name
self._checked_out_items = dict()
self._fine_amount = 0.00
# Getter Methods
def get_patron_id(self):
"""
Returns _patron_id attribte of a Patron object
"""
return self._patron_id
def get_name(self):
"""
Returns the _name attribute of a Patron object
"""
return self._name
def get_fine_amount(self):
"""
Returns _fine_amount attribute of a Patron object
"""
return self._fine_amount
# Specialized Methods
def add_library_item(self, library_item_obj):
"""
INPUT: library item unique id (str)
OUTPUT: Returns checked_out_items list with appended library item
"""
id = library_item_obj.get_library_item_id()
# Add the target ibrary item to a dict (key : id, value: LibraryItem object)
self._checked_out_items[id] = library_item_obj
return self._checked_out_items
def remove_library_item(self, id):
"""
INPUT: library item unique id (str)
OUTPUT: Pops target item with key id and returns its paired-value
OR returns KeyError if id DNE.
"""
try:
result = self._checked_out_items.pop(id)
except KeyError:
print(f'{id} DNE.')
else:
return result
def amend_fine(self, fine_increment):
"""
INPUT: Float or integer
OUTPUT: New fine amount (neg or pos)"""
self._fine_amount = self._fine_amount + fine_increment
return self._fine_amount
class Library():
"""
This class simulated a Library
"""
def __init__(self):
"""
This method initializes a Library object
"""
self._current_date = 0
self._holdings = dict()
self._members = dict()
# Getter Methods
def get_holdings(self):
"""
Returns the _holdings dictionary data structure"""
return self._holdings
def get_members(self):
"""
Returns the _members dictionary data structure """
return self._members
# Specialized Methods
def add_library_item(self, library_obj):
"""
INPUT: LibraryItem object
OUTPUT: Adds LibraryItem obj to _holdings. Returns _holdings.
"""
if not isinstance(library_obj,LibraryItem):
raise TypeError("Parameter must be of type LibraryItem or a child class of it.")
id = library_obj.get_library_item_id()
self._holdings[id] = library_obj
return self._holdings
def add_patron(self, patron_obj):
"""
INPUT: Takes a Patron object
OUTPUT: Adds Patron object to _members.
(Key : patron_id, Value: patron_obj).
Returns _members.
"""
# Exception-Handling
if not isinstance(patron_obj,Patron):
raise TypeError("Parameter must be of type Patron.")
id = patron_obj.get_patron_id()
self._members[id] = patron_obj
return self._members
def lookup_library_item_from_id(self, id):
"""
INPUT: Takes a string paramter named 'id'
OUTPUT: Returns target LibraryItem obj or None if DNE
"""
# Exception-Handling
if not isinstance(id, str):
raise TypeError("Parameter must be of str type.")
target_item = self._holdings.get(id)
return target_item
def lookup_patron_from_id(self, id):
"""
INPUT: Takes a string paramter named 'id'
OUTPUT: Returns target LibraryItem obj or None if DNE
"""
# Exception-Handling
if not isinstance(id, str):
raise TypeError("Parameter must be of str type.")
target_patron = self._members.get(id)
return target_patron
def check_out_library_item(self, patron_id, library_item_id):
"""
INPUT: patron_id (str), library_item_id (str)
OUTPUT: Reports a DNE_ERROR or LocationStateError. OR
Checks out LibraryItem object and returns a string
"""
target_patron = self.lookup_patron_from_id(patron_id)
target_item = self.lookup_library_item_from_id(library_item_id)
if target_patron == None:
return 'patron not found'
if target_item == None:
return 'item not found'
target_item_state = target_item.get_location()
if target_item_state == "CHECKED_OUT":
return 'item already checked out'
elif (target_item_state == "ON_HOLD_SHELF") and (target_item.requested_by() is not target_patron):
return 'item on hold by other patron'
else:
# Update Library item's checked_out_by attribute
target_item.set_checked_out_by(target_patron)
# Update Library item's date_checked-out attribute
target_item.set_date_checked_out(self._current_date)
# Update target_item's location
target_item.set_location("CHECKED_OUT")
# Update Patron's checked_out_items list
target_patron.add_library_item(target_item)
# If library item was requested by same checking out patron then update requestedo by attribue to None.
if target_item.requested_by() is target_patron:
target_item.set_requested_by(None)
return "check out successful"
def return_library_item(self, library_item_id):
"""
INPUT: library item id (str)
OUTPUT: Returns Error messages if item not found or patron not found OR
Error message if item already in library OR
'return successful'"
"""
target_item = self.lookup_library_item_from_id(library_item_id)
if target_item == None:
return 'item not found'
if target_item.get_location() == ("ON_SHELF" or "ON_HOLD_SHELF"):
return 'item already in library'
else:
target_patron = target_item.checked_out_by()
target_patron.remove_library_item(library_item_id)
if target_item.requested_by() is not None:
target_item.set_location("ON_HOLD_SHELF")
else:
target_item.set_location("ON_SHELF")
target_item.set_checked_out_by(None)
return 'return successful'
def request_library_item(self, patron_id, library_item_id):
"""
INPUT: patron_id (str) and library_item_id (str)
OUTPUT: Returns Error Messages patron not found or item not found OR
Error message item already on hold OR
'request successful'
"""
target_patron = self.lookup_patron_from_id(patron_id)
target_item = self.lookup_library_item_from_id(library_item_id)
if target_patron == None:
return 'patron not found'
if target_item == None:
return 'item not found'
if target_item.get_location() == "ON_HOLD_SHELF":
return 'item already on hold'
elif target_item.get_location() == "CHECKED_OUT":
target_item.set_requested_by(target_patron)
return 'item already checked out'
else:
target_item.set_requested_by(target_patron)
target_item.set_location("ON_HOLD_SHELF")
return 'request successful'
def pay_fine(self, patron_id, dollar_amt):
"""
INPUT: patron_id (str) and dollar_amt (int or float)
OUTPUT: """
target_patron = self.lookup_patron_from_id(patron_id)
if target_patron == None:
return 'patron not found'
print(dollar_amt)
target_patron.amend_fine(-dollar_amt)
def increment_current_date(self):
"""
INPUT: None
OUTPUT: Increment Library object's current date.
Increments fine by 10 cents
after due date of library item each day
"""
self._current_date += 1
for checked_out_item in self._holdings.values():
if checked_out_item.get_location() == "CHECKED_OUT":
days_checked_out = self._current_date - checked_out_item.date_checked_out()
if days_checked_out > checked_out_item.get_check_out_length():
target_patron = checked_out_item.checked_out_by()
target_patron.amend_fine(0.10)
def main():
try:
b1 = Book("345", "Phantom Tollbooth", "Juster")
a1 = Album("456", "...And His Orchestra", "The Fastbacks")
m1 = Movie(56, "Laputa", "Miyazaki")
print(b1.get_author())
print(a1.get_artist())
print(m1.get_director())
p1 = Patron("abc", "Felicity")
p2 = Patron("bcd", "Waldo")
lib = Library()
lib.add_library_item(b1)
lib.add_library_item(a1)
lib.add_patron(p1)
lib.add_patron(p2)
lib.check_out_library_item("bcd", "456")
loc = a1.get_location()
lib.request_library_item("abc", "456")
for i in range(57):\
lib.increment_current_date() # 57 days pass
p2_fine = p2.get_fine_amount()
lib.pay_fine("bcd", p2_fine)
lib.return_library_item("456")
except DNE_ERROR as e:
print(e)
except InvalidLibraryState as e:
print(e)
except LocationStateError as e:
print(e)
except TypeError as e:
print(e)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment