Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Victoria Garcia - Ampers - Hotel -updated #28

Open
wants to merge 51 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
35f4197
Initial batch of documents, including lib and spec files for hotel, r…
Lasiorhine Mar 5, 2018
95d867c
Tests for Reservation class written and failing/error-ing in the desi…
Lasiorhine Mar 5, 2018
8ec35b4
within reservation, assign_id method is working, even though it invol…
Lasiorhine Mar 6, 2018
b4b6585
Reseration Class is passing all tests, except for the 'calculate_pric…
Lasiorhine Mar 6, 2018
73776ca
After consideration, price calculation methods added to reservation c…
Lasiorhine Mar 6, 2018
4349a69
total calculation method in Reservation is now passing all tests.
Lasiorhine Mar 6, 2018
bc418d2
A bunch of tests stubbed for the Room class.
Lasiorhine Mar 6, 2018
0b24d31
More tests stubbed for the Room class. It is a stubby kind of day.
Lasiorhine Mar 6, 2018
ac02b87
Some more test-ish stuff for Room, including variables that hold Date…
Lasiorhine Mar 7, 2018
c5d831e
got the first bit of my crazy-ass date system to start throwing a rea…
Lasiorhine Mar 8, 2018
43f6209
My stars and garters but all the freaking tests for the freaking date…
Lasiorhine Mar 8, 2018
77ea867
Beginning of the room-level lookup system now underway.
Lasiorhine Mar 9, 2018
3c5f7ee
A few minor comments in the Reservation class and spec. Got my crazy…
Lasiorhine Mar 9, 2018
52baa85
basic functionality for 'add reservation' working in room, with tests…
Lasiorhine Mar 10, 2018
52cdb0c
tests for conflict-checking mechanism in room are failing in the desi…
Lasiorhine Mar 10, 2018
fa8d67f
a whole bunch of the tests for the conflict-checking machinery are no…
Lasiorhine Mar 10, 2018
b0e3aa3
closer to functional with the conflict-checking mechanism. One more …
Lasiorhine Mar 10, 2018
aaf782f
all tests passing for the conflict-checking system in Room. Also, ad…
Lasiorhine Mar 10, 2018
3a17352
tests for date conflict resolution helper method written and failing …
Lasiorhine Mar 10, 2018
27704ae
all tests for conflict resolution helper method are passing.
Lasiorhine Mar 10, 2018
0658e5e
all the conflict-oriented machinery for Room is now passing all its t…
Lasiorhine Mar 10, 2018
93b0463
tests for reporting method written and failing/erroring appropriately.
Lasiorhine Mar 10, 2018
83c8cb6
last functionality added to Room class. Tests passing.
Lasiorhine Mar 10, 2018
05e25d0
wrote a couple more tests for Room. all functionality for Room, inclu…
Lasiorhine Mar 10, 2018
661cb71
stubbed a whole lot of stuff for FrontDesk.
Lasiorhine Mar 11, 2018
94d4064
some changes to the initialize method for room, which I fought with a…
Lasiorhine Mar 11, 2018
5a6e28d
Lots of stuff stubbed, test-wise, in Front Desk
Lasiorhine Mar 11, 2018
8fc9eed
first few tests passing for FrontDesk
Lasiorhine Mar 11, 2018
47b5909
report_all_rooms is passing its tests.
Lasiorhine Mar 11, 2018
46daa3b
Tests written and failing in the desired way for the availability che…
Lasiorhine Mar 11, 2018
bab282a
the room availability lookup procedure in FrontDesk is passing all it…
Lasiorhine Mar 11, 2018
7b1b62c
tests written and failing in the desired way for the front desk's per…
Lasiorhine Mar 11, 2018
a79308d
initial tests for per-day reservation report in FrontDesk are passing…
Lasiorhine Mar 11, 2018
93595f8
all tests for the FrontDesk's reservation lookup system are passing, …
Lasiorhine Mar 11, 2018
03913cb
the method for generating a list of available rooms for a given date …
Lasiorhine Mar 11, 2018
864dc7f
the available room-reporting and -choosing methods for FrontDesk are …
Lasiorhine Mar 11, 2018
fb5fe9f
tests written for FrontDesk's reservation creation method, and failin…
Lasiorhine Mar 11, 2018
a79376a
helper method for the reservation addint now in place and passing tests.
Lasiorhine Mar 11, 2018
5201052
another small helper method for the reservation creation mechanism in…
Lasiorhine Mar 11, 2018
f1cafbf
fixed up some stuff having to do with reservation price calculation.
Lasiorhine Mar 11, 2018
6c23ed1
tests written for FrontDesk's reservation price calculation method, a…
Lasiorhine Mar 11, 2018
306b345
last of the pre-Wave 3 FrontDesk methods are now passing all tests. t…
Lasiorhine Mar 11, 2018
8ba2485
room identificaiton method for the block stuff is now passing all tests.
Lasiorhine Mar 12, 2018
7e63aa6
a whole bunch of machinery for hte Block Resrvation stuff is now func…
Lasiorhine Mar 12, 2018
cf17877
the block set-aside mechanism in FrontDesk is passing initial tests.
Lasiorhine Mar 12, 2018
620f317
The availability-checking mechanisms for within the blocks is now wor…
Lasiorhine Mar 12, 2018
4c1f2de
This is as far as I'm going. One method shy of finishing Wave 3.
Lasiorhine Mar 12, 2018
554ac8b
All prompts answered.
Lasiorhine Apr 1, 2018
b6dd3c5
Added to the design-activity document, and while doing so, made a few…
Lasiorhine Apr 2, 2018
59dc145
made changes to room availability report-generating mechanisms in Roo…
Lasiorhine Apr 2, 2018
fa4d9e3
Modifications to the room availability report generation mechanism in…
Lasiorhine Apr 2, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
9 changes: 9 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require 'rake/testtask'

Rake::TestTask.new do |t|
t.libs = ["lib"]
t.warning = true
t.test_files = FileList['specs/*_spec.rb']
end

task default: :test
94 changes: 94 additions & 0 deletions design-activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@

PART I: SHOPPING CART, ETC:

1. Which classes does each implementation include? Are the lists the same?

CartEntry -- Impl. A & Impl. B
ShoppingCart -- Impl. A & Impl. B
Order -- Impl. A & Impl. B

(So yes, the lists are the same.)

2. Write down a sentence to describe each class.

CartEntry keeps track of unit price and quantity, and in Implementation B, has a method for multiplying those values to obtain a price. (It does not, however, have any way of retaining the value calculated.)

ShoppingCart holds an array, called @entries, and in Implementation B, it has a method for calculating an overall sum based on the contents of @entries that makes use of CartEntry's price calculation method.

Order contains an instance of ShoppingCart, stores SALES_TAX as a variable, and includes a method for calculating the total price of the ShoppingCart instance's contents, with the B version leveraging ShoppingCart's inborn price-calculation method, while the A version has its own, entirely _de novo_ price calculation method.

3. How do the classes relate to each other?

A CartEntry appears to correspond to the type of item that a person might buy-- so a blue, spade-tip Sharpie, or a paperback copy of POODR, or a banana. (We don't get the name of the itme here, just its per-item price and the quantity the customer is buying. ShoppingCart appears to be meant to hold instances of CartEntry. And Order holds an instance of ShoppingCart (which contains CartEntry instances) so that the price of its contents can be tallied, and have an appropriate sales tax rate can be applied.

Basically, they're like Matryoshka dolls, with the smallest doll being CartEntry, and the largest being Order.

4. What DATA does each class store? How (if at all) does this differ between the two implementations?

CartEntry stores a unit price and a quantity in both implementations, and nothing else. (While Implementation B has a means of calculating a price based on those two values, it does not have a way of storing it.)

ShoppingCart stores instances of CartEntry (each, presumably, with a quantity and a unit price), and nothing else. (Much like CartEntry, Implementation B has a means of generating an overall price for the contents of its array of CartEntry instances, but no way of storing that price.)

Order stores the sales tax rate, and an instance of ShoppingCart, which (presumably) contains one or more instances of CartEntry. Both implementations A and B have a method for calculating the overall price of the contents, with sales tax, but no way of storing that value.

5. What METHODS does each class have? How (if at all) does this differ between the two implementations?

CartEntry:
initialize: unit_price, quantity -- Imp. A & Imp. B
attr_accessor: unit_price, quantity -- Imp. A only
price: -- Imp. B only

ShoppingCart:
intiialize: entries -- Imp. A & Imp. B
attr_accessor: entries -- Imp. A only
price: -- Imp. B only

Order:
initialize: CartEntry -- Imp. A & Imp. B
total_price: -- Imp. A & Imp. B

KEY DIFFERENCES:

Accessors: Implementation A has attr_accessor methods that Implementation B does not have. This, I think, is because B provides access to the values of those variables as the return value of its .price methods, instead of having the Order method's total_price method access them directly.

Price methods: As mentioned above, in Implementation B, the CartEntry and ShoppingCart methods have their own price methods. ShppingCart's price method uses values returned by CartEntry's price method, and Order does something similar with ShoppingCart's price method. In contrast, Implementation A has a single price method that obtains a result by accessing variables within the other classes directly.

6. Consider the Order#total_price method. In each implementation:

* Is logic to compute the price delegated to 'lower level' classes like ShoppingCart and CartEntry, or is it retained in Order?

In implementation B, it is delegated to lower classes. In Implementation A, it is retained by Order.

* Does total_price directly manipulate the instance variables of other classes?

Depends on your definition of 'manipulate'. It does not change the values of those variables in either case. However, in Implementation A, it does directly access the values of variables in other classes, and it performs calculations based on those values.

7. If decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?

I'm guessing that you'd want to have some sort of discount get triggered, probably as a multiplier, if the value of quantity for a given CartEntry instance is above a certain threshhold.

In Implementation B, you could do this easily, by adding a conditional to CartEntry's price method (and either putting the discount logic there, or triggering a helper method.) Downstream of that, ShoppingCart#price and Order#total_price wouldn't have to change.

In Implementation A, you'd have to put the discounting logic in Order#total_price, and you'd have a conditional (and possibly the trigger for a helper method) inside of a .each loop, which would be inefficient and fiddly.

The code for this would be easier to write and read in Implementation B.

8. Which implementation adheres better to the single responsibility principle?

Initially, I thought it was Implementation A, because there, Order has sole responsibility for calculating prices. However, after working through these prompts, I can see that it's actually Implementation B, because: (1) No class accesses the instances variables of instances of another class; and (2) Since instances of each class can generate their own prices (and the price method has a consistent name across classes) they're actually more fungible and easier to modify and use.


9. Which implementation is more loosely coupled?

Implementation B. In Implementation B, the Order class doesn't need to know the names of variables within another class, and the lower classes themselves are more fungible.


PART II: REVISITING HOTEL:

For this project, I chose to work on the program's room availability reporting mechanics. (Note that this is far from the worst problem that my implementation of Hotel has-- I chose this because it felt like a finite task with an achievable goal, not because it was the code's most important issue.)

In the previous implementation, FrontDesk generated a report of bookings for a given date by going through all the instances of Room, and using one of Room's class methods (report_reservations_for_day) to look at all the instances of Reservation held by a given room, checking each Reservation instance's @days_booked_am_and_pm variable for matching dates. In other words, there was a multi-layered class entanglement here, and it had a really rotten time complexity to boot.

To improve this, I created a different method (report_reservation_status_for_day) within Room that looks, not at any of Reservation's class variables, but at Room's own @dates_unavailable variable. (The new method then produces a hash where the key is the room's number, and the value is its booking status for the query date.)

I also created a new reporting method in FrontDesk (report_overall_booking_status_for_date(date)) that uses Room's new method, report_reservation_status_for_day, to generate its contents, instead of having Room's older report_reservations_for_day(date_julian) read from Reservation's @days_booked_am_and_pm variable.
22 changes: 22 additions & 0 deletions lib/BlockRoom.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@


module Hotel
class BlockRoom < Hotel::Room

attr_reader :room_number, :base_resv_price, :min_res_sec, :rate_with_discount, :block_id, :block_start, :block_end

attr_accessor :discount, :reservations, :dates_unavailable


def initialize(room_number, block_id, discount, block_start, block_end)

super(room_number)

@room_number = room_number.concat("-B")
@block_id = block_id
@block_start = DateTime.parse(block_start)
@block_end = DateTime.parse(block_end)
end

end
end
203 changes: 203 additions & 0 deletions lib/front_desk.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
require 'date'
require 'pry'

require_relative 'reservation'
require_relative 'room'

module Hotel
class FrontDesk

attr_accessor :rooms, :blocks

TOTAL_ROOMS_IN_FACILITY = 20
@@last_block_base = 100

def initialize

@rooms = generate_rooms
@blocks = []

end

def generate_rooms
room_array = []
for i in 1 .. TOTAL_ROOMS_IN_FACILITY
room_number = i.to_s
new_room = Hotel::Room.new(room_number)
room_array << new_room
end
return room_array
end


# def find_all_reservations_for_date(date)
# query_date = DateTime.parse(date).jd.to_s
# overall_report = {}
# @rooms.each do |room|
# per_room_report = nil
# per_room_report = room.report_reservations_for_day(query_date)
# unless per_room_report == nil
# resv_id_array = []
# per_room_report.each {|reservation| resv_id_array << reservation.id }
# per_room_report = {room.room_number => resv_id_array}
# overall_report.merge!(per_room_report)
# end
# end
# return overall_report
# end

def report_overall_booking_status_for_date(date)
query_date = DateTime.parse(date).jd.to_s
overall_report = {}
@rooms.each do |room|
overall_report.merge!(room.report_reservation_status_for_day(query_date))
end
return overall_report
end

def report_all_available_rooms(start_dt, end_dt, room_set)
imaginary_reservation = Hotel::Reservation.new(start_dt, end_dt)
all_available_rooms = []
room_set.each do |room|
available_room = nil
availability = room.can_accept_reservation?(imaginary_reservation)
if availability[:accept] == true
available_room = room
end
unless available_room == nil
available_room = available_room.room_number
all_available_rooms << available_room
end
end
return all_available_rooms
end

def find_available_room(start_d, end_d, room_set)
room_to_assign = nil
rooms_available = report_all_available_rooms(start_d, end_d, room_set)
unless rooms_available.empty?
rooms_by_int = rooms_available.map{ |rm_numb| rm_numb.to_i }
rooms_by_int.sort!
room_to_assign = rooms_by_int[0].to_s
end
return room_to_assign
end

def locate_room_by_id(query_rm_numb)
target_room = @rooms.find {|room| room.room_number == query_rm_numb}
end

def look_up_per_night_price_for_room(query_room_numb)
target_room = locate_room_by_id(query_room_numb)
target_price = target_room.rate_with_discount
end

def create_reservation_basic(start_date, end_date, room_set)
room_numb_for_new_res = find_available_room(start_date, end_date, room_set)
if room_numb_for_new_res.nil?
raise StandardError.new ("Alas, no rooms are available for this reservation.")
else
new_reservation = Hotel::Reservation.new(start_date, end_date)
end

new_reservation.hotel_room_id = room_numb_for_new_res

new_reservation.per_night_price = look_up_per_night_price_for_room(room_numb_for_new_res)

room_instance_for_res = locate_room_by_id(room_numb_for_new_res)

room_instance_for_res.add_reservation(new_reservation)

return new_reservation
end




def find_reservation_price(query_id)

target_reservation = nil
price_of_target = nil
@rooms.each do |room|
unless target_reservation != nil
room.reservations.each do |reservation|
if reservation.id == query_id
target_reservation = reservation
price_of_target = target_reservation.total_reservation_cost
end
end
end
end
return price_of_target
end


def check_block_feasibility(st_dt, end_dt,room_set, block_size)
block_y_or_n = nil
elig_for_block = report_all_available_rooms(st_dt, end_dt, room_set)
if elig_for_block.length >= block_size
block_y_or_n = {:yes => elig_for_block}
else
block_y_or_n = {:no => []}
end
return block_y_or_n
end

def create_placeholder_res(start_date, end_date, room_id, block_id)

reservation_for_block = Hotel::Reservation.new(start_date, end_date)

reservation_for_block.hotel_room_id = room_id
reservation_for_block.block_set_aside = true
reservation_for_block.block_id = block_id

return reservation_for_block
end

def create_room_block(st_date, end_date, block_size, room_set, block_discount)
feasability_result = check_block_feasibility(st_date, end_date, room_set, block_size)
if feasability_result.keys.include?(:no)
raise StandardError.new("There are not enough available rooms to create this block")
else
block_id = @@last_block_base.to_s
@@last_block_base += 1
rooms_available_for_block = feasability_result[:yes]
room_surplus = rooms_available_for_block.length - block_size
as_many_rooms_as_needed = rooms_available_for_block.drop(room_surplus)
rooms_reserved_in_block = []
as_many_rooms_as_needed.each do |room_id|
room_for_block = locate_room_by_id(room_id)
placeholder_res = create_placeholder_res(st_date, end_date, room_id, block_id)
room_for_block.add_reservation(placeholder_res)
block_availability_object = Hotel::BlockRoom.new(room_id, block_id, block_discount, st_date, end_date)
block_availability_object.discount = block_discount
rooms_reserved_in_block << block_availability_object
end
block = {block_id.to_sym => rooms_reserved_in_block}
@blocks << block
end
return block
end

def check_availability_within_block(start_date, end_date, block_id)
block_key = block_id.to_sym
target_block = @blocks.find {|element| element.has_key?(block_key)}
available_block_rooms = report_all_available_rooms(start_date, end_date, target_block.values[0])
return available_block_rooms
end

# RIGHT HERE IS WHERE I RAN OUT OF GAS.

#I ALMOST GOT THROGUH WAVE 3, but at 6:00 am, I just had nothing left.


def create_reservation_within_block(start_date, end_date, block_id)
rooms_available = check_availability_within_block(start_date, end_date, block_id)
room_instances_for_reservation = rooms_available.map {|id| @blocks.find }


new_block_reservation = create_reservation_basic(start_date, end_date, rooms_available)
return new_block_reservation
end
end
end
Loading