Skip to content

Commit

Permalink
Refactor logic for graying out timeslots (#345)
Browse files Browse the repository at this point in the history
* Round down to closest half hour

When graying out timeslots based on the current
time index, the current time should be rounded
down to the closest half hour.

* Refactor current time idx calculation

* Update endingOperatingHour to 6 pm in frontend

* Fix the error in rounding up when OH start is exactly 10 am

* Add test for round_idx_calculation

* Gray out timeslots when user has an XL drop-in reservation

* Write test for newly added helper function

* Chose to not display past slots instead of graying out

The graying out timeslots was a design choice we made earlier when our table
was static in size. But with the addition of a dynamic table,
this feature was relatively easy to implement.

* Uncomment important code

Some code was left commented out while playing around to implement
the new features. This code just deleted SN156 from the table
to prevent users
from making reservations to the XL.

* Add comments

There is certain part of the code which is no longer needed to gray out
past timeslots. However, I have just commented it out rather than deleting it
if we need to revert back to graying out timeslots logic for any reason.
  • Loading branch information
yuvrajjain2003 authored Mar 19, 2024
1 parent 1b9b0f6 commit 77ef254
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 21 deletions.
66 changes: 48 additions & 18 deletions backend/services/coworking/reservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime, timedelta
from random import random
from typing import Sequence
from sqlalchemy import or_
from sqlalchemy.orm import Session, joinedload
from backend.entities.room_entity import RoomEntity

Expand Down Expand Up @@ -271,8 +272,7 @@ def get_map_reserved_times_by_date(
"""
reserved_date_map: dict[str, list[int]] = {}

# Query DB to get reservable rooms. You can change coworking policy to change
# which rooms are reservable. SN156 should not go in coworking policy.
# Query DB to get reservable rooms.
rooms = self._get_reservable_rooms()

# Generate a 1 day time range to get operating hours on date.
Expand All @@ -298,8 +298,13 @@ def get_map_reserved_times_by_date(
)

# Extract the start time and end time for operating hours rounded to the closest half hour
operating_hours_start = self._round_to_closest_half_hour(
operating_hours_on_date.start, round_up=True
operating_hours_start = max(
self._round_to_closest_half_hour(
operating_hours_on_date.start, round_up=True
),
self._round_to_closest_half_hour(
datetime.now(), round_up=False
)
)
operating_hours_end = self._round_to_closest_half_hour(
operating_hours_on_date.end, round_up=False
Expand All @@ -313,22 +318,24 @@ def get_map_reserved_times_by_date(

# Need current time to gray out slots in the past on that day.
current_time = datetime.now()
current_time_idx = (
self._idx_calculation(current_time, operating_hours_start) + 1
)
current_time_idx = self._idx_calculation(current_time, operating_hours_start)

for room in rooms:
time_slots_for_room = [0] * operating_hours_duration

# Making slots up till current time gray
if date.date() == current_time.date():
for i in range(0, current_time_idx):
time_slots_for_room[i] = RoomState.UNAVAILABLE.value
# # Making slots up till current time gray
# This code no longer required, but may be required in the future.
# Please keep this here for now.
# if date.date() == current_time.date():
# for i in range(0, current_time_idx):
# time_slots_for_room[i] = RoomState.UNAVAILABLE.value

room_id = room.id if room else "SN156"
reservations = self._query_confirmed_reservations_by_date_and_room(
date, room_id
)
if room.id == "SN156":
reservations = self._query_xl_reservations_by_date_for_user(date, subject)
else:
reservations = self._query_confirmed_reservations_by_date_and_room(
date, room.id
)
for reservation in reservations:
start_idx = self._idx_calculation(
reservation.start, operating_hours_start
Expand Down Expand Up @@ -384,7 +391,9 @@ def _round_to_closest_half_hour(
minutes = dt.minute

if round_up:
if minutes < 30:
if minutes == 0:
to_add = timedelta(minutes=0)
elif minutes < 30:
to_add = timedelta(minutes=(30 - minutes))
else:
to_add = timedelta(minutes=(60 - minutes))
Expand Down Expand Up @@ -507,6 +516,28 @@ def _query_confirmed_reservations_by_date_and_room(

return [reservation.to_model() for reservation in reservations]

def _query_xl_reservations_by_date_for_user(
self, date: datetime, subject: User
) -> Sequence[Reservation]:
start = date.replace(hour=0, minute=0, second=0, microsecond=0)
reservations = (
self._session.query(ReservationEntity)
.join(ReservationEntity.users)
.filter(
ReservationEntity.start < start + timedelta(hours=24),
ReservationEntity.end > start,
ReservationEntity.state.not_in(
[ReservationState.CANCELLED, ReservationState.CHECKED_OUT]
),
ReservationEntity.room == None,
UserEntity.id == subject.id
)
.order_by(ReservationEntity.start)
.all()
)

return [reservation.to_model() for reservation in reservations]

def _get_reservable_rooms(self) -> Sequence[RoomDetails]:
"""
Retrieves a list of all reservable rooms.
Expand All @@ -519,10 +550,9 @@ def _get_reservable_rooms(self) -> Sequence[RoomDetails]:
Returns:
Sequence[RoomDetails]: A sequence of RoomDetails models representing all the reservable rooms, excluding room 'SN156'.
"""

rooms = (
self._session.query(RoomEntity)
.where(RoomEntity.reservable == True)
.where(or_(RoomEntity.reservable == True, RoomEntity.id == 'SN156'))
.order_by(RoomEntity.id)
.all()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,45 @@ def test_idx_calculation(reservation_svc: ReservationService):
assert reservation_svc._idx_calculation(time_3, oh_start) == 7


def test_round_idx_calculation(reservation_svc: ReservationService):
time = datetime.now().replace(hour=10, minute=0)
time2 = datetime.now().replace(hour=18, minute=0)
time3 = datetime.now().replace(hour=10, minute=1)
time4 = datetime.now().replace(hour=18, minute=14)
rounded_time = reservation_svc._round_to_closest_half_hour(time, True)
rounded_time2 = reservation_svc._round_to_closest_half_hour(time2, False)
rounded_time3 = reservation_svc._round_to_closest_half_hour(time3, True)
rounded_time4 = reservation_svc._round_to_closest_half_hour(time4, False)

assert rounded_time.hour == 10 and rounded_time.minute == 0
assert rounded_time2.hour == 18 and rounded_time2.minute == 0
assert rounded_time3.hour == 10 and rounded_time3.minute == 30
assert rounded_time4.hour == 18 and rounded_time4.minute == 0

def test_query_confirmed_reservations_by_date_and_room(
reservation_svc: ReservationService, time: dict[str, datetime]
):
"""Test getting all reservations for a particular date."""
reservations = reservation_svc._query_confirmed_reservations_by_date_and_room(time[TOMORROW], 'SN135')
reservations = reservation_svc._query_confirmed_reservations_by_date_and_room(time[NOW], None)
assert True
#TODO: Add in better assert statements here.
#TODO: Add in better assert statements here.

def test_get_reservable_rooms(reservation_svc: ReservationService):
# Hardcoded for now, and this might change depending on which rooms are labeled as reservable.
rooms = reservation_svc._get_reservable_rooms()
assert rooms[0].id == 'SN135' and rooms[0].reservable is True
assert rooms[1].id == 'SN137' and rooms[1].reservable is True
assert rooms[2].id == 'SN139' and rooms[2].reservable is True
assert rooms[3].id == 'SN141' and rooms[3].reservable is True


def test_query_xl_reservations_by_date_for_user(reservation_svc: ReservationService, time: dict[str, datetime]):
reservations = reservation_svc._query_xl_reservations_by_date_for_user(time[NOW], user_data.user)
assert len(reservations) == 1
assert reservations[0].room is None
assert reservations[0].users[0].first_name == 'Sally'


def test_get_map_reserved_times_by_date(
reservation_svc: ReservationService, time: dict[str, datetime]
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class ReservationTableService {
private profile: Profile | undefined;
private profileSubscription!: Subscription;

private EndingOperationalHour: number = 17;
private EndingOperationalHour: number = 18;

static readonly MAX_RESERVATION_CELL_LENGTH: number = 4; // rule for how long a reservation can be consecutively

Expand Down

0 comments on commit 77ef254

Please sign in to comment.