diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..e8238f385 Binary files /dev/null and b/.DS_Store differ diff --git a/lib/date_range.rb b/lib/date_range.rb new file mode 100644 index 000000000..f5794a7a5 --- /dev/null +++ b/lib/date_range.rb @@ -0,0 +1,36 @@ +require 'date' + +module Hotel + class DateRange + attr_reader :start_date, :end_date + + def initialize(start_date, end_date) + @start_date = start_date + @end_date = end_date + raise ArgumentError.new("This is not a valid date range!") if @end_date < @start_date + end + + def overlap?(other) + # check to see if the date range itself is overlapping with "other"? + # A reservation is allowed start on the same day that another reservation for the same room ends + if self.start_date.between?(other.start_date, other.end_date - 1) || other.start_date.between?(self.start_date, self.end_date - 1) + return true + else + return false + end + end + + def include?(date) + # see if the date provided is in between the date range + if date.between?(self.start_date, self.end_date - 1) + return true + else + return false + end + end + + def duration + return @end_date - @start_date + end + end +end \ No newline at end of file diff --git a/lib/hotel_block.rb b/lib/hotel_block.rb new file mode 100644 index 000000000..73beb33c3 --- /dev/null +++ b/lib/hotel_block.rb @@ -0,0 +1,18 @@ +require 'date' +require 'time' +require_relative 'reservation' +require_relative 'date_range' + + +module Hotel + class BlockReservation + attr_reader :date_range, :status + attr_accessor :room_num , :no_of_rooms, :block_reservations + + def initialize(date_range, no_of_rooms) + @date_range = date_range + @no_of_rooms = no_of_rooms + @block_reservations = Array.new + end + end +end \ No newline at end of file diff --git a/lib/hotel_controller.rb b/lib/hotel_controller.rb new file mode 100644 index 000000000..2b14994a3 --- /dev/null +++ b/lib/hotel_controller.rb @@ -0,0 +1,162 @@ +require 'date' + +require_relative 'reservation' +require_relative 'date_range' +require_relative 'hotel_block' + +module Hotel + class HotelController + attr_reader :reservation_list + attr_accessor :room_list, :specific_date_reservation, :hotel_block_list + + # Wave 1 + # default number of room in the hotel is 20 + def initialize(number_of_room = 20) + @reservation_list = Hash.new + @room_list = Array.new + @hotel_block_list = Hash.new + + # added flexibilty to the number of room to have in the future + n = 1 + number_of_room.times do + @room_list << "Room #{n}" + n += 1 + end + + end + + # to get the list of rooms in the hotel + def get_room_list + return @room_list + end + + def reserve_room(start_date, end_date, customer_name) + # start_date and end_date should be instances of class Date + # new data range and reservation created + # first available room assigned to the new reservation + raise ArgumentError.new("They are not the valid Date class for start_date and end_date") if (start_date.class != Date || end_date.class != Date) + resevation_duration = DateRange.new(start_date, end_date) + new_reservation = Reservation.new(resevation_duration, customer_name) + new_reservation.cost + reservation_id = @reservation_list.length + 1 + if self.available_rooms(start_date, end_date)[0] == nil + raise ArgumentError.new("Opps! There is no room availiable for this date range!") + else + new_reservation.room_num = self.available_rooms(start_date, end_date)[0] + end + @reservation_list[reservation_id] = new_reservation + end + + def reservations(date) + specific_date_reservation = Array.new + @reservation_list.each_value do |reservation| + if reservation.date_range.include?(date) + specific_date_reservation << reservation + end + end + + if specific_date_reservation.empty? + raise ArgumentError.new("There is no reservation on this day!") + else + return specific_date_reservation + end + end + + + def get_list_of_reservations(specific_room, start_date, end_date) + date_range_reservation_list = Array.new + check_date_range = Hotel::DateRange.new(start_date, end_date) + @reservation_list.each_value do |reservation| + if (reservation.date_range.overlap?(check_date_range) ) && (reservation.room_num == specific_room) + date_range_reservation_list << reservation + end + end + return date_range_reservation_list + end + + + + def get_reservation_cost(reservation_id) + return self.reservation_list[reservation_id].cost + end + + # Wave 2 + def available_rooms(start_date, end_date) + # start_date and end_date should be instances of class Date + available_rooms_list = Array.new + occupied_rooms_list = Array.new + perferred_date_range = Hotel::DateRange.new(start_date, end_date) + @reservation_list.each_value do |reservation| + if reservation.date_range.overlap?(perferred_date_range) + occupied_rooms_list << reservation.room_num + end + end + if occupied_rooms_list.empty? + return @room_list + else + available_rooms_list = @room_list - occupied_rooms_list + if available_rooms_list.empty? + raise ArgumentError.new("There is no more room for you!") + else + return available_rooms_list + end + end + end + + #Wave 3 Hotel Block + def create_hotel_block(start_date, end_date, number_of_rooms) + raise ArgumentError.new("They are not the valid Date class for start_date and end_date") if (start_date.class != Date || end_date.class != Date) + raise ArgumentError.new("There is not enough room!") if number_of_rooms > 5 || number_of_rooms > self.available_rooms(start_date, end_date).length + block_duration = DateRange.new(start_date, end_date) + new_hotel_block = BlockReservation.new(block_duration, number_of_rooms) + number_of_rooms.times do + block_room = self.reserve_room(block_duration.start_date, block_duration.end_date, "Hotel Block Reserve") + block_room.status = :open_hotel_block + new_hotel_block.block_reservations << block_room + end + hotel_block_id = hotel_block_list.length + 1 + hotel_block_list[hotel_block_id] = new_hotel_block + return new_hotel_block + end + + # check if a given block has any rooms available + # if there is available room - will return the list of rooms that are open + # else will raise argument + + def check_hotel_block_list_availability(hotel_block_id) + raise ArgumentError.new("Not a valid Hotel Block ID!") if !(hotel_block_list.keys.include? hotel_block_id) + available_rooms_in_block = Array.new + hotel_block_list[hotel_block_id].block_reservations.each do |reservation| + if reservation.check_status + available_rooms_in_block << reservation.room_num + end + end + + if available_rooms_in_block == [] + raise ArgumentError.new("No room available in this hotel block anymore!") + else + return available_rooms_in_block + end + end + + def reserve_room_hotel_block(hotel_block_id, customer_name, specific_room) + raise ArgumentError.new("Not a valid Hotel Block ID!") if !(hotel_block_list.keys.include? hotel_block_id) + + error_status = true + + hotel_block_list[hotel_block_id].block_reservations.each do |reservation| + # check if the specific room requested in within the hotel block and not booked by another guests + #if hotel_block_list[hotel_block_id].check_valid_room(specific_room) && reservation.check_status + if (reservation.check_valid_room(specific_room)) && (reservation.check_status) + booking_room = reservation + booking_room.customer_name = customer_name + booking_room.status = :reserved_hotel_block + error_status = false + end + end + if error_status + raise ArgumentError.new("This is not a reserved room for the hotel block guest! Or it is already reserved by another guest!") + end + end + end +end \ No newline at end of file diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..3c4560f94 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,41 @@ +require 'date' +require 'time' +require_relative 'reservation' +require_relative 'date_range' +require_relative 'hotel_block' + +module Hotel + class Reservation + attr_reader :date_range + attr_accessor :room_num, :status, :customer_name + + def initialize(date_range, customer_name) + @date_range = date_range + @room_num = nil + @customer_name = customer_name + @status = :comfirmed + @cost = nil + end + + def cost + # offering discount to hotel block customers + if @customer_name == "Hotel Block Reserve" + @cost = ((@date_range.duration)* 150).to_f + elsif + # how many night times 200 per night + @cost = ((@date_range.duration)* 200).to_f + end + return @cost + end + + # for hotel blocks - to check if request room is within the block + def check_valid_room(request_room) + return self.room_num == request_room + end + + # for hotel blocks - to make sure it is still open for hotel block guests + def check_status + return self.status == :open_hotel_block + end + end +end \ No newline at end of file diff --git a/refactors.txt b/refactors.txt new file mode 100644 index 000000000..a6e5a9172 --- /dev/null +++ b/refactors.txt @@ -0,0 +1,7 @@ +To refactor: +- I think some of my methods are too complicated and too much dependency within methods. + I would love to spend some time to clean that up +- try to follow the single responsibility +- Dry it up! +- would like to add more stuff in each class so it would be small methods +- maybe I should have a room class? \ No newline at end of file diff --git a/test/date_range_test.rb b/test/date_range_test.rb new file mode 100644 index 000000000..387a99585 --- /dev/null +++ b/test/date_range_test.rb @@ -0,0 +1,33 @@ +require_relative 'test_helper' + +describe "Date Range class" do + before do + @data_range = Hotel::DateRange.new( + start_date = Date.new(2010,03,02), + end_date = Date.new(2010,03,05) + ) + @date_range_two = Hotel::DateRange.new( + start_date = Date.new(2010,03,8), + end_date = Date.new(2010,03,10) + ) + @date_range_three = Hotel::DateRange.new( + start_date = Date.new(2010,03,8), + end_date = Date.new(2010,03,10) + ) + end + + it "is an instance of Date Range" do + expect(@data_range).must_be_kind_of Hotel::DateRange + end + + describe "overlap?" do + it "will return false if the time is not overlapping" do + expect(@date_range_two.overlap?(@data_range)).must_equal false + end + + it "will return true if the time is overlapping" do + expect(@date_range_two.overlap?(@date_range_three)).must_equal true + end + end +end + \ No newline at end of file diff --git a/test/hotel_controller_test.rb b/test/hotel_controller_test.rb new file mode 100644 index 000000000..59cc20540 --- /dev/null +++ b/test/hotel_controller_test.rb @@ -0,0 +1,95 @@ +require_relative 'test_helper' + +describe "Hotel Controller" do + before do + @front_desk = Hotel::HotelController.new(20) + @front_desk.reserve_room(Date.new(2020,01,05), Date.new(2020,01,10), "Alex") + @front_desk.reserve_room(Date.new(2020,03,05), Date.new(2020,03,10), "Macy") + @front_desk.reserve_room(Date.new(2020,03,12), Date.new(2020,03,15), "Leo") + + end + + describe "Initalizer" do + it "is an instance of Hotel Controller" do + expect(@front_desk).must_be_kind_of Hotel::HotelController + end + + it "will create an array of rooms according to the number of room provided as an argument" do + expect(@front_desk.room_list.length).must_equal 20 + end + end + + describe "reserve_room" do + it "will create a new Reservation class and push into Reservation List" do + expect(@front_desk.reservation_list.length).must_equal 3 + expect(@front_desk.reservation_list[1]).must_be_kind_of Hotel::Reservation + expect(@front_desk.reservation_list[1].date_range).must_be_kind_of Hotel::DateRange + end + end + + describe "reservations" do + it "will return a list of reservation on that specific date" do + check_date = @front_desk.reservations(Date.new(2020,01,05)) + + expect(check_date).must_be_kind_of Array + expect(check_date.length).must_equal 1 + expect{@front_desk.reservations(Date.new(2020,10,05))}.must_raise ArgumentError + end + end + + describe "get_list_of_reservation" do + @front_desk = Hotel::HotelController.new(20) + it "will return a list of reservations for a specific room in that date range" do + room_reservation_list = @front_desk.get_list_of_reservations("Room 1", Date.new(2020,03,01), Date.new(2020,03,31,)) + expect(room_reservation_list.length).must_equal 2 + end + end + + describe "available_rooms" do + it "will return a list of avaliable_rooms" do + available_list = @front_desk.available_rooms(Date.new(2020,03,01), Date.new(2020,03,31,)) + available_list_two = @front_desk.available_rooms(Date.new(2020,10,01), Date.new(2020,10,31,)) + expect(available_list.length).must_equal 19 + expect(available_list_two.length).must_equal 20 + end + end + + describe "create_hotel_block" do + it "will create a hotel_block, create the correct amount of reservation according to the block" do + hotel_block = @front_desk.create_hotel_block(Date.new(2020,06,20), Date.new(2020,06,25), 5) + + + expect(hotel_block).must_be_kind_of Hotel::BlockReservation + expect(@front_desk.reservations(Date.new(2020,06,20)).length).must_equal 5 + expect{@front_desk.create_hotel_block(Date.new(2020,06,20), Date.new(2020,06,25), 10)}.must_raise ArgumentError + end + end + + describe "check_hotel_block_list_availability" do + it "will retrun the rooms from hotel block that is available" do + hotel_block = @front_desk.create_hotel_block(Date.new(2020,06,20), Date.new(2020,06,25), 5) + rooms_from_block = @front_desk.check_hotel_block_list_availability(1) + + expect(rooms_from_block.length).must_equal 5 + expect{@front_desk.check_hotel_block_list_availability(10)}.must_raise ArgumentError + end + end + + describe "reserve_room_hotel_block(hotel_block_id, customer_name, specific_room)" do + it "will reserve room within the hotel block" do + + # Arrange & Act + @front_desk.create_hotel_block(Date.new(2020,06,20), Date.new(2020,06,25), 5) + @front_desk.create_hotel_block(Date.new(2020,06,20), Date.new(2020,06,25), 4) + + @front_desk.reserve_room_hotel_block(1, "Lily", "Room 1") + + # Assert + expect(@front_desk.get_list_of_reservations("Room 1",Date.new(2020,06,20), Date.new(2020,06,25))[0].status).must_equal :reserved_hotel_block + expect(@front_desk.get_list_of_reservations("Room 1",Date.new(2020,06,20), Date.new(2020,06,25))[0].customer_name).must_equal "Lily" + expect{@front_desk.reserve_room_hotel_block(1, "Peter", "Room 1")}.must_raise ArgumentError + expect{@front_desk.reserve_room_hotel_block(5, "Hello Kitty", 1)}.must_raise ArgumentError + end + end +end + diff --git a/test/reservation_test.rb b/test/reservation_test.rb new file mode 100644 index 000000000..ae6c14218 --- /dev/null +++ b/test/reservation_test.rb @@ -0,0 +1,35 @@ +require_relative 'test_helper' + +describe "Reservation class" do + before do + @new_hotel = Hotel::HotelController.new(10) + @new_hotel.reserve_room(Date.new(2010,02,10),Date.new(2010,02,13), "Sharon") + @new_reservation = @new_hotel.reservation_list[1] + end + + describe "Initializer" do + it "is an instance of Reservation" do + expect(@new_reservation).must_be_kind_of Hotel::Reservation + end + end + + describe "cost" do + it "will calculate the total cost of the reservation" do + expect(@new_reservation.cost).must_equal 600 + end + end + + describe "check_valid_room(request_room)" do + it "will return true if the request room matches with the room the reservation itself" do + expect(@new_reservation.check_valid_room("Room 1")).must_equal true + expect(@new_reservation.check_valid_room("Room 10")).must_equal false + end + end + + describe "check_status" do + it "will return false is the status of the reservation is not equal to :open_hotel_block" do + expect(@new_reservation.check_status).must_equal false + end + end +end + \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index c3a7695cf..61fed229c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,8 +1,18 @@ # Add simplecov +require 'simplecov' +SimpleCov.start + require "minitest" require "minitest/autorun" require "minitest/reporters" +require "date" + Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # require_relative your lib files here! + +require_relative '../lib/reservation' +require_relative '../lib/hotel_controller' +require_relative '../lib/date_range' +