Skip to content

Commit

Permalink
Merge branch 'main' into stage
Browse files Browse the repository at this point in the history
  • Loading branch information
MelissaAutumn committed Jan 22, 2024
2 parents d61e736 + beee973 commit 5d21511
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 13 deletions.
2 changes: 1 addition & 1 deletion backend/src/appointment/controller/apis/google_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def create_event(self, calendar_id, body, token):
response = None
with build("calendar", "v3", credentials=token, cache_discovery=False) as service:
try:
response = service.events().insert(calendarId=calendar_id, sendUpdates="all", body=body).execute()
response = service.events().insert(calendarId=calendar_id, body=body).execute()
except HttpError as e:
logging.warning(f"[google_client.create_event] Request Error: {e.status_code}/{e.error_details}")

Expand Down
7 changes: 4 additions & 3 deletions backend/src/appointment/controller/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def available_slots_from_schedule(s: models.Schedule) -> list[schemas.SlotBase]:
# We add a day here because it should be inclusive of the final day.
farthest_booking = now + timedelta(days=1, minutes=s.farthest_booking)

schedule_start = datetime.combine(s.start_date, s.start_time)
schedule_start = max([datetime.combine(s.start_date, s.start_time), earliest_booking])
schedule_end = min([datetime.combine(s.end_date, s.end_time), farthest_booking]) if s.end_date else farthest_booking

start_time = datetime.combine(now.min, s.start_time) - datetime.min
Expand All @@ -319,8 +319,9 @@ def available_slots_from_schedule(s: models.Schedule) -> list[schemas.SlotBase]:
weekdays = [1, 2, 3, 4, 5]

# Between the available booking time
for day in range(earliest_booking.day, schedule_end.day):
current_datetime = datetime(year=schedule_start.year, month=schedule_start.month, day=day)
for ordinal in range(schedule_start.toordinal(), schedule_end.toordinal()):
date = datetime.fromordinal(ordinal)
current_datetime = datetime(year=date.year, month=date.month, day=date.day)
# Check if this weekday is within our schedule
if current_datetime.isoweekday() in weekdays:
# Generate each timeslot based on the selected duration
Expand Down
16 changes: 8 additions & 8 deletions backend/src/appointment/routes/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,26 +101,26 @@ def read_schedule_availabilities(
if not schedule.active:
raise validation.ScheduleNotFoundException()

# calculate theoretically possible slots from schedule config
availableSlots = Tools.available_slots_from_schedule(schedule)

# get all events from all connected calendars in scheduled date range
calendars = repo.get_calendars_by_subscriber(db, subscriber.id, False)

if not calendars or len(calendars) == 0:
raise validation.CalendarNotFoundException()

existingEvents = Tools.existing_events_for_schedule(schedule, calendars, subscriber, google_client, db)
actualSlots = Tools.events_set_difference(availableSlots, existingEvents)
# calculate theoretically possible slots from schedule config
available_slots = Tools.available_slots_from_schedule(schedule)

# get all events from all connected calendars in scheduled date range
existing_slots = Tools.existing_events_for_schedule(schedule, calendars, subscriber, google_client, db)
actual_slots = Tools.events_set_difference(available_slots, existing_slots)

if not actualSlots or len(actualSlots) == 0:
if not actual_slots or len(actual_slots) == 0:
raise validation.SlotNotFoundException()

return schemas.AppointmentOut(
title=schedule.name,
details=schedule.details,
owner_name=subscriber.name,
slots=actualSlots,
slots=actual_slots,
)


Expand Down
2 changes: 1 addition & 1 deletion backend/test/factory/subscriber_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def _make_subscriber(level, name=FAKER_RANDOM_VALUE, username=FAKER_RANDOM_VALUE
with with_db() as db:
subscriber = repo.create_subscriber(db, schemas.SubscriberBase(
name=name if factory_has_value(name) else fake.name(),
username=username if factory_has_value(username) else fake.name(),
username=username if factory_has_value(username) else fake.name().replace(' ', '_'),
email=email if factory_has_value(email) else fake.email(),
level=level,
timezone='America/Vancouver'
Expand Down
83 changes: 83 additions & 0 deletions backend/test/integration/test_schedule.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import os
from datetime import date, time

from freezegun import freeze_time

from appointment.controller.auth import signed_url_by_subscriber
from appointment.controller.calendar import CalDavConnector
from defines import DAY1, DAY5, DAY14, auth_headers, DAY2


Expand Down Expand Up @@ -210,3 +216,80 @@ def test_update_foreign_schedule(self, with_client, make_pro_subscriber, make_ca
headers=auth_headers,
)
assert response.status_code == 403, response.text

def test_public_availability(self, monkeypatch, with_client, make_pro_subscriber, make_caldav_calendar, make_schedule):
class MockCaldavConnector:
@staticmethod
def __init__(self, url, user, password):
"""We don't want to initialize a client"""
pass

@staticmethod
def list_events(self, start, end):
return []

monkeypatch.setattr(CalDavConnector, "__init__", MockCaldavConnector.__init__)
monkeypatch.setattr(CalDavConnector, "list_events", MockCaldavConnector.list_events)

start_date = date(2024, 4, 1)
start_time = time(9)
end_time = time(17)

subscriber = make_pro_subscriber()
generated_calendar = make_caldav_calendar(subscriber.id, connected=True)
make_schedule(
calendar_id=generated_calendar.id,
active=True,
start_date=start_date,
start_time=start_time,
end_time=end_time,
end_date=None,
earliest_booking=1440,
farthest_booking=20160,
slot_duration=30)

signed_url = signed_url_by_subscriber(subscriber)

# Check availability at the start of the schedule
with freeze_time(start_date):
response = with_client.post(
"/schedule/public/availability",
json={"url": signed_url},
headers=auth_headers,
)
assert response.status_code == 200, response.text
data = response.json()
slots = data['slots']

# Based off the earliest_booking our earliest slot is tomorrow at 9:00am
assert slots[0]['start'] == '2024-04-02T09:00:00'
# Based off the farthest_booking our latest slot is 4:30pm
assert slots[-1]['start'] == '2024-04-15T16:30:00'

# Check availability over a year from now
with freeze_time(date(2025, 6, 1)):
response = with_client.post(
"/schedule/public/availability",
json={"url": signed_url},
headers=auth_headers,
)
assert response.status_code == 200, response.text
data = response.json()
slots = data['slots']

assert slots[0]['start'] == '2025-06-02T09:00:00'
assert slots[-1]['start'] == '2025-06-13T16:30:00'

# Check availability with a start date day greater than the farthest_booking day
with freeze_time(date(2025, 6, 27)):
response = with_client.post(
"/schedule/public/availability",
json={"url": signed_url},
headers=auth_headers,
)
assert response.status_code == 200, response.text
data = response.json()
slots = data['slots']

assert slots[0]['start'] == '2025-06-30T09:00:00'
assert slots[-1]['start'] == '2025-07-11T16:30:00'

0 comments on commit 5d21511

Please sign in to comment.