Implement date range handling for vacation and unavailable events
- Added helper functions to generate ISO 8601 formatted start and end times for calendar days. - Introduced logic to merge consecutive vacation dates into a single record for improved data representation. - Updated the duty schedule import process to utilize the new date handling functions for unavailable and vacation events. - Enhanced integration tests to validate the correct handling of vacation periods and unavailable dates. - Modified the web application to display formatted date ranges for vacation and unavailable events.
This commit is contained in:
@@ -60,6 +60,33 @@ def _duty_to_iso(d: date, hour_utc: int, minute_utc: int) -> str:
|
||||
return dt.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
|
||||
def _day_start_iso(d: date) -> str:
|
||||
"""ISO 8601 start of calendar day UTC: YYYY-MM-DDT00:00:00Z."""
|
||||
return d.isoformat() + "T00:00:00Z"
|
||||
|
||||
|
||||
def _day_end_iso(d: date) -> str:
|
||||
"""ISO 8601 end of calendar day UTC: YYYY-MM-DDT23:59:59Z."""
|
||||
return d.isoformat() + "T23:59:59Z"
|
||||
|
||||
|
||||
def _consecutive_date_ranges(dates: list[date]) -> list[tuple[date, date]]:
|
||||
"""Sort dates and merge consecutive ones into (first, last) ranges. Empty list -> []."""
|
||||
if not dates:
|
||||
return []
|
||||
sorted_dates = sorted(set(dates))
|
||||
ranges: list[tuple[date, date]] = []
|
||||
start_d = end_d = sorted_dates[0]
|
||||
for d in sorted_dates[1:]:
|
||||
if (d - end_d).days == 1:
|
||||
end_d = d
|
||||
else:
|
||||
ranges.append((start_d, end_d))
|
||||
start_d = end_d = d
|
||||
ranges.append((start_d, end_d))
|
||||
return ranges
|
||||
|
||||
|
||||
async def import_duty_schedule_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
if not update.message or not update.effective_user:
|
||||
return
|
||||
@@ -118,18 +145,22 @@ def _run_import(
|
||||
insert_duty(session, user.id, start_at, end_at, event_type="duty")
|
||||
num_duty += 1
|
||||
for d in entry.unavailable_dates:
|
||||
start_at = _duty_to_iso(d, hour_utc, minute_utc)
|
||||
d_next = d + timedelta(days=1)
|
||||
end_at = _duty_to_iso(d_next, hour_utc, minute_utc)
|
||||
insert_duty(
|
||||
session, user.id, start_at, end_at, event_type="unavailable"
|
||||
session,
|
||||
user.id,
|
||||
_day_start_iso(d),
|
||||
_day_end_iso(d),
|
||||
event_type="unavailable",
|
||||
)
|
||||
num_unavailable += 1
|
||||
for d in entry.vacation_dates:
|
||||
start_at = _duty_to_iso(d, hour_utc, minute_utc)
|
||||
d_next = d + timedelta(days=1)
|
||||
end_at = _duty_to_iso(d_next, hour_utc, minute_utc)
|
||||
insert_duty(session, user.id, start_at, end_at, event_type="vacation")
|
||||
for start_d, end_d in _consecutive_date_ranges(entry.vacation_dates):
|
||||
insert_duty(
|
||||
session,
|
||||
user.id,
|
||||
_day_start_iso(start_d),
|
||||
_day_end_iso(end_d),
|
||||
event_type="vacation",
|
||||
)
|
||||
num_vacation += 1
|
||||
return (len(result.entries), num_duty, num_unavailable, num_vacation)
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user