From 2313c3c490c62f131c3e5db3a7adb63e80e581f1 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sun, 13 Sep 2026 16:16:14 +0200 Subject: [PATCH] feat: lazily find-or-create the Bestellungen Deck board and status stacks Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01V57jSQPqwkGG8BuAXg59X5 --- Claude outputs/main.py | 33 +++++++++++++++++ Claude outputs/tests/test_orders.py | 56 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/Claude outputs/main.py b/Claude outputs/main.py index dd0b18a..8ca93ed 100644 --- a/Claude outputs/main.py +++ b/Claude outputs/main.py @@ -485,6 +485,39 @@ async def _deck_request(method: str, path: str, json_body: dict = None): _deck_cache: dict = {} +_DECK_BOARD_TITLE = "Bestellungen" +_DECK_STACK_TITLES = ["Offen", "In Arbeit", "Erledigt"] + + +async def _ensure_deck_board() -> dict: + """Finds (or lazily creates) the 'Bestellungen' board and its three + status stacks. Result is cached in _deck_cache after the first call - + board/stack ids don't change at runtime, and finding/creating them + costs several sequential Deck API calls.""" + if "board_id" in _deck_cache: + return _deck_cache + + boards = await _deck_request("GET", "/boards") + board = next((b for b in boards if b["title"] == _DECK_BOARD_TITLE), None) + if board is None: + board = await _deck_request("POST", "/boards", {"title": _DECK_BOARD_TITLE, "color": "0082C9"}) + board_id = board["id"] + + stacks = await _deck_request("GET", f"/boards/{board_id}/stacks") + existing_by_title = {s["title"]: s["id"] for s in stacks} + + stack_ids = {} + for order, title in enumerate(_DECK_STACK_TITLES): + if title in existing_by_title: + stack_ids[title] = existing_by_title[title] + else: + created = await _deck_request("POST", f"/boards/{board_id}/stacks", {"title": title, "order": order}) + stack_ids[title] = created["id"] + + _deck_cache["board_id"] = board_id + _deck_cache["stacks"] = stack_ids + return _deck_cache + def _decode_mime_words(value: str) -> str: parts = decode_header(value) diff --git a/Claude outputs/tests/test_orders.py b/Claude outputs/tests/test_orders.py index 5511445..8a45134 100644 --- a/Claude outputs/tests/test_orders.py +++ b/Claude outputs/tests/test_orders.py @@ -85,3 +85,59 @@ async def test_deck_request_raises_on_error_status(): with patch.object(main.aiohttp, "ClientSession", return_value=fake_session): with pytest.raises(RuntimeError): await main._deck_request("GET", "/boards") + + +@pytest.mark.asyncio +async def test_ensure_deck_board_creates_board_and_stacks_when_missing(): + titles_created = [] + + async def fake_deck_request(method, path, json_body=None): + if (method, path) == ("GET", "/boards"): + return [] + if (method, path) == ("POST", "/boards"): + return {"id": 10, "title": "Bestellungen"} + if (method, path) == ("GET", "/boards/10/stacks"): + return [] + if (method, path) == ("POST", "/boards/10/stacks"): + titles_created.append(json_body["title"]) + return {"id": 100 + len(titles_created), "title": json_body["title"]} + raise AssertionError(f"unexpected call {method} {path}") + + with patch.object(main, "_deck_request", new=AsyncMock(side_effect=fake_deck_request)): + board = await main._ensure_deck_board() + + assert board["board_id"] == 10 + assert titles_created == ["Offen", "In Arbeit", "Erledigt"] + assert set(board["stacks"].keys()) == {"Offen", "In Arbeit", "Erledigt"} + + +@pytest.mark.asyncio +async def test_ensure_deck_board_reuses_existing_board_and_stacks(): + async def fake_deck_request(method, path, json_body=None): + if (method, path) == ("GET", "/boards"): + return [{"id": 10, "title": "Bestellungen"}] + if (method, path) == ("GET", "/boards/10/stacks"): + return [ + {"id": 1, "title": "Offen", "cards": []}, + {"id": 2, "title": "In Arbeit", "cards": []}, + {"id": 3, "title": "Erledigt", "cards": []}, + ] + raise AssertionError(f"unexpected call {method} {path}") + + with patch.object(main, "_deck_request", new=AsyncMock(side_effect=fake_deck_request)) as mock_req: + board = await main._ensure_deck_board() + + assert board == {"board_id": 10, "stacks": {"Offen": 1, "In Arbeit": 2, "Erledigt": 3}} + assert mock_req.call_count == 2 + + +@pytest.mark.asyncio +async def test_ensure_deck_board_caches_after_first_lookup(): + main._deck_cache["board_id"] = 10 + main._deck_cache["stacks"] = {"Offen": 1, "In Arbeit": 2, "Erledigt": 3} + + with patch.object(main, "_deck_request", new=AsyncMock()) as mock_req: + board = await main._ensure_deck_board() + + mock_req.assert_not_called() + assert board["board_id"] == 10