def remove_client_by_client_id(self, client_id):
     """
     Function to remove the client from the client repository that has the ID given by the parameter. If no client
     is found identified by the given ID no clients are removed, but an error is raised instead. Otherwise, the
     client is removed from the client repository. Nothing is returned.
     :param client_id: integer, holds the value of the ID of the client to be removed. It should be a natural integer
     """
     client_index, client = self.find_client_by_client_id(client_id)
     if client_index is None:
         raise RepoError("Client ID not found. ")
     self._client_repository.remove_client_by_index(client_index)
     self._action_service.push_remove_action_to_undo_list(client)
 def remove_book_by_book_id(self, book_id):
     """
     Function to remove the book from the book repository that has the ID given by the parameter. If no book is found
     identified by the given ID no books are removed, but an error is raised instead. Otherwise, the book is removed
     from the book repository. Nothing is returned.
     :param book_id: integer, holds the value of the ID of the book to be removed. It should be a natural integer.
     """
     book_index, book = self.find_book_by_book_id(book_id)
     if book_index is None:
         raise RepoError("Book not found. ")
     self.__book_repository.remove_book_by_index(book_index)
     self.__action_service.push_remove_action_to_undo_list(book)
    def return_rental_by_book_id(self, book_id):
        """
        Function to return the rental by the 'book_id'. The function verifies if the 'book_id' is valid. If it is not,
        then no rental is returned but an error is raised instead. Otherwise, the function searches for the book in the
        book repository. If no book is found, no rental is returned but an error is raised instead. Otherwise, the ID of
        rental of the book is looked for. If no rental ID is found active for the book at the moment, no rental is
        returned but an error is raised instead. Otherwise, the rental is marked as returned with the 'returned_date' of
        today.
        :param book_id: integer, holds the ID of the book whose rental will be returned.
        """
        self._rental_validator.validate_book_and_client_ids(book_id, 5)
        book_id = int(book_id)

        is_valid_book = self.is_book_id_in_repository(book_id)
        if not is_valid_book:
            raise RepoError("Book ID not found. ")

        rental_id = self.find_active_rental_id_by_book_id(book_id)
        if rental_id is None:
            raise RepoError("Book is not rented. ")

        self.return_rental_by_id(rental_id)
    def add_rental(self, book_id, client_id):
        """
        Function to add a rental to the rental repository with the book and client IDs given by the parameters. The
        function creates a new 'Rental' entity which then passes through the 'RentalValidator'. In case any of the
        rental's parameters are incorrect, the rental is not added but an error is raised instead. Otherwise, the
        function checks whether the book is available for rent. If it is not, then no rental is added but an error is
        raised instead. Otherwise, the rental is added to the rental repository.
        :param book_id: integer, holds the ID value of the book that is rented.
        :param client_id: integer, holds the ID value of the client that rents the book.
        """
        self._rental_validator.validate_book_and_client_ids(book_id, client_id)

        book_id = int(book_id)
        client_id = int(client_id)

        is_valid_book = self.is_book_id_in_repository(book_id)
        is_valid_client = self.is_client_id_in_repository(client_id)
        is_available_book = self.is_book_available_by_book_id(book_id)

        if is_valid_book:
            if is_valid_client:
                if is_available_book:
                    rental_id = self._rental_repository.get_next_rental_id()
                    rented_date = date.today()
                    returned_date = None
                    rental = Rental(rental_id, book_id, client_id, rented_date,
                                    returned_date)
                    self._rental_validator.validate_rental(rental)
                    self._rental_repository.add_rental(rental)
                    self._action_service.push_add_action_to_undo_list(rental)
                else:
                    raise RepoError("Book is currently rented. ")
            else:
                raise RepoError("Client ID not found. ")
        else:
            raise RepoError("Book ID not found. ")
 def update_client_by_client_id(self, client_id, new_name_as_string):
     """
     Function to update the details of a client entity found in the client repository with the values given by the
     function's parameters. If no client is found in the repository identified by the given client ID, an error is
     raised instead. Otherwise, the client name is replaced by the given value. If the given value does not
     properly create a client entity, an error is raised instead. Nothing is returned.
     :param client_id: integer, holds the value of the client that the function updates. Should be an natural integer
     :param new_name_as_string: string, holds the new name of the client. Should be between 1 and 25 characters
     """
     client_index, client = self.find_client_by_client_id(client_id)
     if client is None:
         raise RepoError("Client ID not found. ")
     new_client = Client(client_id, new_name_as_string)
     self._client_validator.validate_client(new_client)
     self._client_repository.update_client(client_index, new_client)
     self._action_service.push_update_action_to_undo_list(
         client, new_client)
 def update_client_by_name(self, old_name_as_string, new_name_as_string):
     """
     Function to update the details of an already existing client from the client repository. The function tries to
     find the client in the client repository. If no client is found, no client is updated but an error is raised
     instead. Otherwise, the client's new details are passed through a 'ClientValidator'. If the validation fails,
     no client is updated but an error is raised instead. Otherwise, the client is updated with the new details.
     :param old_name_as_string: string, holds the name of the client to be updated in the repository.
     :param new_name_as_string: string, holds the name the client will be updated with.
     """
     client_index, client = self.find_client_by_name(old_name_as_string)
     if client is None:
         raise RepoError("Name not found. ")
     client_id = client.id
     new_client = Client(client_id, new_name_as_string)
     self._client_validator.validate_client(new_client)
     self._client_repository.update_client(client_index, new_client)
     self._action_service.push_update_action_to_undo_list(
         client, new_client)
 def redo(self):
     try:
         action = self.__action_repository.get_redo()
         action_id = action["id"]
     except IndexError:
         raise RepoError("Nothing to redo")
     while action["id"] == action_id:
         entity_type = action["entity_type"]
         if entity_type == "book":
             self.__redo.redo_book(action)
         elif entity_type == "client":
             self.__redo.redo_client(action)
         elif entity_type == "rental":
             self.__redo.redo_rental(action)
         self.__action_repository.push_undo(action)
         self.__action_repository.pop_redo()
         try:
             action = self.__action_repository.get_redo()
         except IndexError:
             break
 def update_book_by_book_id(self, book_id, new_title_as_string,
                            new_author_name_as_string):
     """
     Function to update the details of a book entity found in the book repository with the values given by the
     function's parameters. If no book is found in the repository identified by the given book ID, an error is raised
     instead. Otherwise, the title and author name are replaced by the given values. If the given values do not
     properly create a book entity, an error is raised instead. Nothing is returned.
     :param book_id: integer, holds the value of the book that the function updates. Should be an natural integer.
     :param new_title_as_string: string, holds the new name of the title. Should be between 1 and 25 characters.
     :param new_author_name_as_string: string, holds the new name of the author. Should be between 1 and
         25 characters.
     """
     book_index, book_to_update = self.find_book_by_book_id(book_id)
     if book_to_update is None:
         raise RepoError("Book not found. ")
     updated_book = Book(book_id, new_title_as_string,
                         new_author_name_as_string)
     self.__book_validator.validate_book(updated_book)
     self.__book_repository.update_book(book_index, updated_book)
     self.__action_service.push_update_action_to_undo_list(
         book_to_update, updated_book)
 def return_rental_by_id(self, rental_id):
     """
     Function to return a rental by the 'rental_id'. The function verifies if the 'rental_id' is valid. If it is not,
     then no rental is returned but an error is raised instead. Otherwise, the function searches for the rental in
     the rental repository. If no rental is found, no rental is returned but an error is raised instead. Otherwise,
     the rental is marked as returned with the current date as the 'returned_date'.
     :param rental_id: integer, holds the ID of the rental to be returned.
     """
     try:
         rental_id = int(rental_id)
     except ValueError:
         raise ValidError("Rental ID must have natural number value. ")
     rental_index, rental = self.find_rental_index_by_id(rental_id)
     if rental_index is None:
         raise RepoError("Rental ID not found. ")
     today = date.today()
     new_rental = Rental(rental.id, rental.book_id, rental.client_id,
                         rental.rented_date, date.today())
     self._action_service.push_update_action_to_undo_list(
         rental, new_rental)
     self._rental_repository.update_rental_return_by_index(
         rental_index, today)