Skip to content
This repository has been archived by the owner on May 30, 2020. It is now read-only.

Commit

Permalink
More work on get effective periods of a schedule
Browse files Browse the repository at this point in the history
  • Loading branch information
ercpe committed Dec 25, 2015
1 parent 5dfbfba commit e9a3f20
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/maxd/worker.py
Expand Up @@ -73,6 +73,50 @@ def __add__(self, other):
def items(self):
return self.events.items()

def effective(self):
new = {}

for weekday, periods in self.events.items():
periods = sorted(periods)
new_periods = []

while periods:
current = periods.pop(0)
other = periods

# remove current period if another period starts before and ends after
if any((p[0] < current[0] and p[1] > current[1] for p in other)) or \
any((p[0] < current[0] and p[1] > current[1] for p in new_periods)):
logger.debug("Removing period %s because it's contained in a larger period" % (current, ))
continue

new_periods.append(current)

# extend periods (change start or end)
periods = sorted(new_periods)
new_periods = []
while periods:
current = periods.pop(0)
other = periods

candidates = [
(s, e) for s, e in other
if (current[0] <= s <= current[1]) or (current[0] <= e <= current[1])
]
if candidates:
new_start = min(p[0] for p in candidates + [current])
new_end = max(p[1] for p in candidates + [current])
logger.debug("Replacing %s and %s with new: %s to %s" % (current, candidates, new_start, new_end))
new_periods.append((new_start, new_end))
for c in candidates:
del periods[periods.index(c)]
else:
new_periods.append(current)

new[weekday] = new_periods

return new

def __eq__(self, other):
return isinstance(other, Schedule) and self.events == other.events

Expand Down
98 changes: 98 additions & 0 deletions tests/test_worker.py
Expand Up @@ -181,3 +181,101 @@ def test_add(self):

with pytest.raises(ValueError):
Schedule() + 'lalala'

def test_get_effective_contained(self):
def _t(h, m):
return datetime.datetime(2015, 12, 21, h, m, tzinfo=pytz.UTC)

# periods which are contained in a larger period must be removed
assert Schedule({
0: [
(_t(5, 0), _t(10, 0)),
(_t(7, 0), _t(8, 0))
]
}).effective() == {
0: [
(_t(5, 0), _t(10, 0)),
]
}

# same as above, but items revers
assert Schedule({
0: [
(_t(7, 0), _t(8, 0)),
(_t(5, 0), _t(10, 0)),
]
}).effective() == {
0: [
(_t(5, 0), _t(10, 0)),
]
}

def test_get_effective_extend(self):
def _t(h, m):
return datetime.datetime(2015, 12, 21, h, m, tzinfo=pytz.UTC)

# extend periods:
# 0600 0700
# 30 0900
# -> 06:00 - 09:00
assert Schedule({
0: [
(_t(6, 0), _t(7, 0)),
(_t(6, 30), _t(9, 0)),
]
}).effective() == {
0: [
(_t(6, 0), _t(9, 0)),
]
}

# extend periods:
# 0600 0700
# 30 0900
# 1500 1700
# -> 06:00 - 09:00
# -> 15:00 - 17:00
assert Schedule({
0: [
(_t(6, 0), _t(7, 0)),
(_t(15, 0), _t(17, 0)),
(_t(6, 30), _t(9, 0)),
]
}).effective() == {
0: [
(_t(6, 0), _t(9, 0)),
(_t(15, 0), _t(17, 0)),
]
}

def test_get_effective_overlapped_and_contained(self):
def _t(h, m):
return datetime.datetime(2015, 12, 21, h, m, tzinfo=pytz.UTC)

# a larger schedule supersedes the smaller ones which would be merged
assert Schedule({
0: [
(_t(6, 0), _t(7, 0)),
(_t(6, 30), _t(9, 0)),
(_t(4, 00), _t(21, 0))
]
}).effective() == {
0: [
(_t(4, 0), _t(21, 0)),
]
}

# Overlapping and superseding periods together
assert Schedule({
0: [
(_t(6, 0), _t(7, 0)),
(_t(15, 0), _t(17, 0)),
(_t(6, 30), _t(9, 0)),
(_t(13, 0), _t(18, 0)),
]
}).effective() == {
0: [
(_t(6, 0), _t(9, 0)),
(_t(13, 0), _t(18, 0)),
]
}

0 comments on commit e9a3f20

Please sign in to comment.