Last active
May 7, 2021 19:22
-
-
Save Zalasyu/92ef7df64d099925d9a26d25387a401b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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