| from datetime import datetime, timedelta |
|
|
| from calendar_out.freebusy import ( |
| annotate_conflicts, |
| check_conflicts, |
| load_ics_busy, |
| propose_times, |
| ) |
| from calendar_out.ics import events_to_ics |
| from server.schema import ActionPlan, Event |
|
|
|
|
| def _event_at(hour, minutes=60): |
| start = (datetime.now() + timedelta(days=1)).replace( |
| hour=hour, minute=0, second=0, microsecond=0 |
| ) |
| return Event(title="Lunch", start=start.isoformat(), |
| end=(start + timedelta(minutes=minutes)).isoformat()) |
|
|
|
|
| def _busy_overlapping(ev): |
| start = datetime.fromisoformat(ev.start) |
| clash = Event(title="Standup", start=start.isoformat(), |
| end=(start + timedelta(minutes=30)).isoformat()) |
| return load_ics_busy(events_to_ics([clash])) |
|
|
|
|
| def test_overlap_detected_and_alternatives_proposed(): |
| ev = _event_at(13) |
| busy = _busy_overlapping(ev) |
| conflicts = check_conflicts([ev], busy) |
| assert conflicts and conflicts[0].severity == "overlap" |
| alts = propose_times(ev, busy) |
| assert alts |
|
|
|
|
| def test_clear_calendar_has_no_conflicts(): |
| ev = _event_at(13) |
| far = Event(title="Faraway", |
| start=(datetime.fromisoformat(ev.start) + timedelta(days=10)).isoformat()) |
| busy = load_ics_busy(events_to_ics([far])) |
| assert check_conflicts([ev], busy) == [] |
|
|
|
|
| def test_annotate_fills_plan(): |
| ev = _event_at(13) |
| plan = ActionPlan(events=[ev]) |
| plan = annotate_conflicts(plan, _busy_overlapping(ev)) |
| assert plan.conflicts and plan.proposed_times |
|
|