|
1 | 1 | import typing |
| 2 | +from collections import defaultdict |
2 | 3 | from datetime import date, datetime, time, timedelta |
3 | 4 | from logging import getLogger |
4 | 5 | from typing import Literal |
@@ -114,62 +115,45 @@ def get_bookings_new( |
114 | 115 | ) |
115 | 116 | LOGGER.debug("Found %d bookings between %s and %s", len(bookings_resp), start_date, end_date) |
116 | 117 |
|
117 | | - # filter out bookings with ids that start with "no-booking-id" |
118 | | - # no idea what these are, but I am praying for the poor sap stuck with maintaining OTF's data model |
119 | 118 | results: list[models.BookingV2] = [] |
120 | | - |
121 | 119 | for b in bookings_resp: |
122 | | - if not b.get("id", "").startswith("no-booking-id"): |
123 | | - try: |
124 | | - results.append(models.BookingV2.create(**b, api=self.otf)) |
125 | | - except ValueError as e: |
126 | | - LOGGER.error("Failed to create BookingV2 from response: %s. Booking data:\n%s", e, b) |
127 | | - continue |
| 120 | + try: |
| 121 | + results.append(models.BookingV2.create(**b, api=self.otf)) |
| 122 | + except Exception as e: |
| 123 | + LOGGER.error( |
| 124 | + "Failed to create BookingV2 from response: %s - %s. Booking data:\n%s", type(e).__name__, e, b |
| 125 | + ) |
| 126 | + continue |
128 | 127 |
|
129 | 128 | if not remove_duplicates: |
130 | 129 | return results |
131 | 130 |
|
132 | | - results = self._deduplicate_bookings(results, exclude_cancelled=exclude_cancelled) |
| 131 | + results = self._deduplicate_bookings(results) |
133 | 132 |
|
134 | 133 | return results |
135 | 134 |
|
136 | | - def _deduplicate_bookings( |
137 | | - self, results: list[models.BookingV2], exclude_cancelled: bool = True |
138 | | - ) -> list[models.BookingV2]: |
| 135 | + def _deduplicate_bookings(self, results: list[models.BookingV2]) -> list[models.BookingV2]: |
139 | 136 | """Deduplicate bookings by class_id, keeping the most recent booking. |
140 | 137 |
|
141 | 138 | Args: |
142 | 139 | results (list[BookingV2]): The list of bookings to deduplicate. |
143 | | - exclude_cancelled (bool): If True, will not include cancelled bookings in the results. |
144 | 140 |
|
145 | 141 | Returns: |
146 | 142 | list[BookingV2]: The deduplicated list of bookings. |
147 | 143 | """ |
148 | | - # remove duplicates by class_id, keeping the one with the most recent updated_at timestamp |
149 | | - |
150 | 144 | orig_count = len(results) |
151 | 145 |
|
152 | | - seen_classes: dict[str, models.BookingV2] = {} |
| 146 | + classes_by_id: defaultdict[str, list[models.BookingV2]] = defaultdict(list) |
| 147 | + keep_classes: list[models.BookingV2] = [] |
153 | 148 |
|
154 | 149 | for booking in results: |
155 | | - class_id = booking.otf_class.class_id |
156 | | - if class_id not in seen_classes: |
157 | | - seen_classes[class_id] = booking |
158 | | - continue |
| 150 | + classes_by_id[booking.otf_class.class_id].append(booking) |
159 | 151 |
|
160 | | - existing_booking = seen_classes[class_id] |
161 | | - if exclude_cancelled: |
162 | | - LOGGER.warning( |
163 | | - f"Duplicate class_id {class_id} found when `exclude_cancelled` is True, " |
164 | | - "this is unexpected behavior." |
165 | | - ) |
166 | | - if booking.updated_at > existing_booking.updated_at: |
167 | | - LOGGER.debug( |
168 | | - "Replacing existing booking for class_id %s with more recent booking %s", class_id, booking |
169 | | - ) |
170 | | - seen_classes[class_id] = booking |
| 152 | + for bookings in classes_by_id.values(): |
| 153 | + top_booking = min(bookings, key=lambda b: b.get_sort_key()) |
| 154 | + keep_classes.append(top_booking) |
171 | 155 |
|
172 | | - results = list(seen_classes.values()) |
| 156 | + results = list(keep_classes) |
173 | 157 | results = sorted(results, key=lambda x: x.starts_at) |
174 | 158 |
|
175 | 159 | new_count = len(results) |
|
0 commit comments