Examples¶
The following examples should help getting insight into timetables.
Cutting a timetable¶
The cut_timetable()
cuts a timetable at specific cut
points and returns a sub-timetable for each cut interval. The following example
shows how a timetable with an entry spanning one month is cut in weekly
intervals:
>>> from datetime import datetime, date, timedelta
>>> from timetable import cut_timetable, datetime_range
>>>
>>> timetable = [
... (datetime(2015, 1, 1), datetime(2015, 2, 1), {'name': 'spam'}),
... ]
>>> cuts = datetime_range(datetime(2015, 1, 1), datetime(2015, 2, 1),
... timedelta(days=7))
>>> for sub_timetable in cut_timetable(timetable, cuts):
... for start, end, entry in sub_timetable:
... print('%s %s %s' % (start.isoformat(), end.isoformat(), entry))
2015-01-01T00:00:00 2015-01-08T00:00:00 {'name': 'spam'}
2015-01-08T00:00:00 2015-01-15T00:00:00 {'name': 'spam'}
2015-01-15T00:00:00 2015-01-22T00:00:00 {'name': 'spam'}
2015-01-22T00:00:00 2015-01-29T00:00:00 {'name': 'spam'}
Annotating a timetable¶
A timetable consists of (start, end, entry)
tuples, where start and end
are datetime
objects and entry is a dictionary. The
entry dictionary contains arbitrary keys and values. You can add additional
keys to this dictionary using the
annotate_timetable()
function. The following example
shows how to compute the hour duration for each entry and add the result under
the key hours to the entry.
>>> from datetime import datetime, date, timedelta
>>> from timetable import cut_timetable, annotate_timetable, datetime_range
>>>
>>> timetable = [
... (datetime(2015, 1, 1), datetime(2015, 2, 1), {'name': 'spam'}),
... ]
>>> cuts = datetime_range(datetime(2015, 1, 1), datetime(2015, 2, 1),
... timedelta(days=7))
>>>
>>> def calc_hours(start, end, entry):
... entry['hours'] = (end - start).total_seconds() / 3600
>>>
>>> for sub_timetable in cut_timetable(timetable, cuts):
... for start, end, entry in annotate_timetable(sub_timetable, calc_hours):
... print('%s %s %s' % (start.isoformat(), end.isoformat(),
... entry['hours']))
2015-01-01T00:00:00 2015-01-08T00:00:00 168.0
2015-01-08T00:00:00 2015-01-15T00:00:00 168.0
2015-01-15T00:00:00 2015-01-22T00:00:00 168.0
2015-01-22T00:00:00 2015-01-29T00:00:00 168.0
Merging timetables¶
If you need to work with multiple timetables (for example from multiple
calendars), you can use the merge_timetables()
function to merge them into a single timetable.
>>> from datetime import datetime
>>> from timetable import merge_timetables
>>>
>>> timetable_spam = [
... (datetime(2015, 1, 1), datetime(2015, 1, 2), {'name': 'spam'}),
... (datetime(2015, 1, 3), datetime(2015, 1, 5), {'name': 'spam'}),
... ]
>>> timetable_eggs = [
... (datetime(2015, 1, 2), datetime(2015, 1, 3), {'name': 'eggs'}),
... (datetime(2015, 1, 4), datetime(2015, 1, 5), {'name': 'eggs'}),
... ]
>>>
>>> for start, end, entry in merge_timetables([timetable_spam, timetable_eggs]):
... print('%s %s %s' % (start.isoformat(), end.isoformat(), entry))
2015-01-01T00:00:00 2015-01-02T00:00:00 {'name': 'spam'}
2015-01-02T00:00:00 2015-01-03T00:00:00 {'name': 'eggs'}
2015-01-03T00:00:00 2015-01-05T00:00:00 {'name': 'spam'}
2015-01-04T00:00:00 2015-01-05T00:00:00 {'name': 'eggs'}
Merge intersections¶
Timetable entries might overlap with each other. The function
merge_intersections()
merges overlapping entries,
thereby generating a non-overlapping timetables. The entries of the
non-overlapping timetable contain a single key entries
, whose value is the
list of merged entries.
>>> from datetime import datetime
>>> from timetable import merge_intersections
>>>
>>> timetable = [
... (datetime(2015, 1, 1), datetime(2015, 1, 3), {'name': 'spam'}),
... (datetime(2015, 1, 2), datetime(2015, 1, 5), {'name': 'eggs'}),
... (datetime(2015, 1, 4), datetime(2015, 1, 6), {'name': 'spam'}),
... ]
>>>
>>> for start, end, entry in merge_intersections(timetable):
... print('%s %s %s' % (start.isoformat(), end.isoformat(), entry))
2015-01-01T00:00:00 2015-01-02T00:00:00 {'entries': [{'name': 'spam'}]}
2015-01-02T00:00:00 2015-01-03T00:00:00 {'entries': [{'name': 'spam'}, {'name': 'eggs'}]}
2015-01-03T00:00:00 2015-01-04T00:00:00 {'entries': [{'name': 'eggs'}]}
2015-01-04T00:00:00 2015-01-05T00:00:00 {'entries': [{'name': 'eggs'}, {'name': 'spam'}]}
2015-01-05T00:00:00 2015-01-06T00:00:00 {'entries': [{'name': 'spam'}]}
Summing it up (literally)¶
The following example puts all the above pieces together to compute the
duration of all entries with a given key. The duration is computed using
another annotation function compute_duration()
. This
function distributes the duration equally to each key in case of overlaps.
>>> from datetime import datetime
>>> from timetable import (merge_intersections, annotate_timetable,
... collect_keys, compute_duration)
>>>
>>> timetable = [
... (datetime(2015, 1, 1), datetime(2015, 1, 3), {'name': 'spam'}),
... (datetime(2015, 1, 2), datetime(2015, 1, 5), {'name': 'eggs'}),
... (datetime(2015, 1, 4), datetime(2015, 1, 6), {'name': 'spam'}),
... ]
>>>
>>> timetable = merge_intersections(timetable)
>>> timetable = annotate_timetable(timetable, collect_keys(key='name'))
>>> timetable = annotate_timetable(timetable, compute_duration(key='name'))
>>>
>>> # Sum durations for each key.
>>> durations = {}
>>> for start, end, entry in timetable:
... for name in entry['name']:
... if not name in durations:
... durations[name] = 0
... durations[name] += entry['duration']
>>>
>>> for name in sorted(durations):
... print('%s %s' % (name, durations[name] / 3600))
eggs 48.0
spam 72.0
There’s also a convenience function sum_timetable()
available, that does all these steps at once. In addition, this function also
cuts the timetable, thereby generating a series of durations.
>>> from datetime import datetime
>>> from timetable import sum_timetable, datetime_range
>>>
>>> timetable = [
... (datetime(2015, 1, 1), datetime(2015, 1, 3), {'name': 'spam'}),
... (datetime(2015, 1, 2), datetime(2015, 1, 5), {'name': 'eggs'}),
... (datetime(2015, 1, 4), datetime(2015, 1, 6), {'name': 'spam'}),
... ]
>>>
>>> # Compute total durations.
>>> durations = sum_timetable(timetable,
... cuts=[datetime(2015, 1, 1), datetime(2015, 1, 6)], key='name')
>>> for name in sorted(durations):
... print('%s %s' % (name, [d / 3600. for d in durations[name]]))
eggs [0.0, 48.0]
spam [0.0, 72.0]
>>>
>>> # Compute daily durations.
>>> cuts = datetime_range(datetime(2015, 1, 1), datetime(2015, 1, 6),
... timedelta(days=1))
>>> durations = sum_timetable(timetable, cuts=cuts, key='name')
>>> for name in sorted(durations):
... print('%s %s' % (name, [d / 3600. for d in durations[name]]))
eggs [0.0, 0.0, 12.0, 24.0, 12.0]
spam [0.0, 24.0, 12.0, 0.0, 12.0]