111 lines
4.0 KiB
Python
111 lines
4.0 KiB
Python
import heapq
|
|
from typing import Iterator
|
|
|
|
import arrow
|
|
from arrow.arrow import Arrow
|
|
|
|
from .event import Event
|
|
|
|
|
|
class Timeline(object):
|
|
def __init__(self, calendar) -> None:
|
|
"""Instanciates a new Timeline.
|
|
(You should not have to instanciate a new timeline by yourself)
|
|
|
|
Args:
|
|
calendar : :class:`ics.icalendar.Calendar`
|
|
"""
|
|
self._calendar = calendar
|
|
|
|
def __iter__(self) -> Iterator[Event]:
|
|
"""Iterates on every event from the :class:`ics.icalendar.Calendar` in chronological order
|
|
|
|
Note :
|
|
- chronological order is defined by the comparaison operators in :class:`ics.event.Event`
|
|
- Events with no `begin` will not appear here. (To list all events in a `Calendar` use `Calendar.events`)
|
|
"""
|
|
# Using a heap is faster than sorting if the number of events (n) is
|
|
# much bigger than the number of events we extract from the iterator (k).
|
|
# Complexity: O(n + k log n).
|
|
heap = [x for x in self._calendar.events if x.begin is not None]
|
|
heapq.heapify(heap)
|
|
while heap:
|
|
yield heapq.heappop(heap)
|
|
|
|
def included(self, start: Arrow, stop: Arrow) -> Iterator[Event]:
|
|
"""Iterates (in chronological order) over every event that is included
|
|
in the timespan between `start` and `stop`
|
|
|
|
Args:
|
|
start : (Arrow object)
|
|
stop : (Arrow object)
|
|
"""
|
|
for event in self:
|
|
if (start <= event.begin <= stop # if start is between the bonds
|
|
and start <= event.end <= stop): # and stop is between the bonds
|
|
yield event
|
|
|
|
def overlapping(self, start: Arrow, stop: Arrow) -> Iterator[Event]:
|
|
"""Iterates (in chronological order) over every event that has an intersection
|
|
with the timespan between `start` and `stop`
|
|
|
|
Args:
|
|
start : (Arrow object)
|
|
stop : (Arrow object)
|
|
"""
|
|
for event in self:
|
|
if ((start <= event.begin <= stop # if start is between the bonds
|
|
or start <= event.end <= stop) # or stop is between the bonds
|
|
or event.begin <= start and event.end >= stop): # or event is a superset of [start,stop]
|
|
yield event
|
|
|
|
def start_after(self, instant: Arrow) -> Iterator[Event]:
|
|
"""Iterates (in chronological order) on every event from the :class:`ics.icalendar.Calendar` in chronological order.
|
|
The first event of the iteration has a starting date greater (later) than `instant`
|
|
|
|
Args:
|
|
instant : (Arrow object) starting point of the iteration
|
|
"""
|
|
for event in self:
|
|
if event.begin > instant:
|
|
yield event
|
|
|
|
def at(self, instant: Arrow) -> Iterator[Event]:
|
|
"""Iterates (in chronological order) over all events that are occuring during `instant`.
|
|
|
|
Args:
|
|
instant (Arrow object)
|
|
"""
|
|
|
|
for event in self:
|
|
if event.begin <= instant <= event.end:
|
|
yield event
|
|
|
|
def on(self, day: Arrow, strict: bool = False) -> Iterator[Event]:
|
|
"""Iterates (in chronological order) over all events that occurs on `day`
|
|
|
|
Args:
|
|
day (Arrow object)
|
|
strict (bool): if True events will be returned only if they are\
|
|
strictly *included* in `day`.
|
|
"""
|
|
day_start, day_stop = day.floor('day').span('day')
|
|
if strict:
|
|
return self.included(day_start, day_stop)
|
|
else:
|
|
return self.overlapping(day_start, day_stop)
|
|
|
|
def today(self, strict: bool = False) -> Iterator[Event]:
|
|
"""Iterates (in chronological order) over all events that occurs today
|
|
|
|
Args:
|
|
strict (bool): if True events will be returned only if they are\
|
|
strictly *included* in `day`.
|
|
"""
|
|
return self.on(arrow.now(), strict=strict)
|
|
|
|
def now(self) -> Iterator[Event]:
|
|
"""Iterates (in chronological order) over all events that occurs now
|
|
"""
|
|
return self.at(arrow.now())
|