From 69478e15432baf71d2c454ac48a551a779a3dc21 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Tue, 26 Dec 2023 23:49:55 +0000 Subject: [PATCH] Find gaps that are available between travel Closes: #86 --- agenda/data.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/agenda/data.py b/agenda/data.py index a908231..31ec45e 100644 --- a/agenda/data.py +++ b/agenda/data.py @@ -272,6 +272,58 @@ def find_markets_during_stay( return overlapping_markets +def find_gaps(events: list[Event], min_gap_days: int = 5) -> list[tuple[date, date]]: + """Gaps of at least `min_gap_days` between events in a list of events.""" + # Sort events by start date + + gaps: list[tuple[date, date]] = [] + previous_event_end = None + + for event in events: + # Use start date for current event + start_date = event.as_date + + # If previous event exists, calculate the gap + if previous_event_end: + gap_days = (start_date - previous_event_end).days + if gap_days >= (min_gap_days + 2): + start_end = ( + previous_event_end + timedelta(days=1), + start_date - timedelta(days=1), + ) + gaps.append(start_end) + + # Update previous event end date + end = event.end_as_date if event.end_date else start_date + if not previous_event_end or end > previous_event_end: + previous_event_end = end + + return gaps + + +def busy_event(e: Event) -> bool: + """Busy.""" + if e.name not in { + "event", + "accommodation", + "conference", + "dodainville", + "transport", + "meetup", + }: + return False + + if e.name == "conference" and not e.going: + return False + if not e.title: + return True + if e.title == "LHG Run Club" or "Third Thursday Social" in e.title: + return False + + lc_title = e.title.lower() + return "rebels" not in lc_title and "south west data social" not in lc_title + + async def get_data( now: datetime, config: flask.config.Config ) -> typing.Mapping[str, str | object]: @@ -304,7 +356,7 @@ async def get_data( bristol_waste_collection_events(data_dir, today), ) - reply = { + reply: dict[str, typing.Any] = { "now": now, "gbpusd": gbpusd, "stock_markets": stock_market.open_and_close(), @@ -394,8 +446,25 @@ async def get_data( events.append(e) events += [Event(name="today", date=today)] + + busy_events = [ + e + for e in sorted(events, key=lambda e: e.as_date) + if e.as_date > today and e.as_date < next_year and busy_event(e) + ] + + gaps = find_gaps(busy_events) + + events += [Event(name="gap", date=start, end_date=end) for start, end in gaps] + + # Sort events by their datetime; the "today" event is prioritised + # at the top of the list for today. This is achieved by sorting first by + # the datetime attribute, and then ensuring that events with the name + # "today" are ordered before others on the same date. events.sort(key=lambda e: (e.as_datetime, e.name != "today")) + reply["gaps"] = gaps + observer = sun.bristol() reply["sunrise"] = sun.sunrise(observer) reply["sunset"] = sun.sunset(observer)