intervaltree overlap queries for room booking conflicts: working versions (tested 2026-10-18)
A room booking schedule on intervaltree: point and range overlap queries, merge_overlaps for busy blocks, and chop/slice to carve out blocked and split slots.
passed6 passed · 0 failed2026-10-18 03:56 UTC- Status
- passed
- Last passed
- 2026-10-18 03:56 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions3
intervaltree@3.2.1sortedcontainers@2.4.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 39095370682
The code
Exactly the files that passed. Your agent can pull them with get_recipe("intervaltree-overlap-queries") (install), or see them on GitHub.
requirements.txt
intervaltree==3.2.1 sortedcontainers==2.4.0 pytest==9.1.1
bookings.py
from intervaltree import IntervalTree
class RoomSchedule:
def __init__(self):
self.tree = IntervalTree()
def reserve(self, start, end, who):
if end <= start:
raise ValueError("end must be after start")
clash = self.tree.overlap(start, end)
if clash:
names = ", ".join(sorted(iv.data for iv in clash))
raise ValueError(f"double booked with {names}")
self.tree.addi(start, end, who)
def who_at(self, moment):
return sorted(iv.data for iv in self.tree.at(moment))
def clashes(self, start, end):
return sorted((iv.begin, iv.end, iv.data) for iv in self.tree.overlap(start, end))
def busy_blocks(self):
blocks = self.tree.copy()
blocks.merge_overlaps(strict=False)
return sorted((iv.begin, iv.end) for iv in blocks)
def block_out(self, start, end):
self.tree.chop(start, end)
def split_at(self, moment):
self.tree.slice(moment)
tests/test_bookings.py
import pytest
from intervaltree import Interval, IntervalTree
from bookings import RoomSchedule
def test_point_query_vs_empty_range():
s = RoomSchedule()
s.reserve(9, 12, "ana")
assert s.who_at(10) == ["ana"]
assert s.clashes(10, 10) == []
assert s.clashes(10, 11) == [(9, 12, "ana")]
def test_touching_reservations_are_allowed():
s = RoomSchedule()
s.reserve(9, 10, "ana")
s.reserve(10, 11, "bo")
assert s.clashes(9, 11) == [(9, 10, "ana"), (10, 11, "bo")]
def test_double_booking_raises():
s = RoomSchedule()
s.reserve(9, 11, "ana")
with pytest.raises(ValueError, match="double booked with ana"):
s.reserve(10, 12, "bo")
def test_merge_overlaps_default_is_strict():
adjacent = IntervalTree([Interval(9, 10, "a"), Interval(10, 11, "b")])
adjacent.merge_overlaps()
assert sorted(adjacent) == [Interval(9, 10, "a"), Interval(10, 11, "b")]
adjacent.merge_overlaps(strict=False)
assert sorted(adjacent) == [Interval(9, 11)]
def test_busy_blocks_join_back_to_back():
s = RoomSchedule()
s.reserve(9, 10, "ana")
s.reserve(10, 11, "bo")
s.reserve(12, 13, "cy")
assert s.busy_blocks() == [(9, 11), (12, 13)]
def test_chop_then_slice():
s = RoomSchedule()
s.reserve(9, 17, "ana")
s.block_out(12, 13)
assert s.clashes(9, 17) == [(9, 12, "ana"), (13, 17, "ana")]
s.split_at(15)
assert s.clashes(9, 17) == [(9, 12, "ana"), (13, 15, "ana"), (15, 17, "ana")]
pytest.ini
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.