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:
2026-02-17 23:06:23 +03:00
parent 7a963eccd1
commit 3f4c7bf66c
3 changed files with 101 additions and 16 deletions

View File

@@ -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: