stubborn-otter/otter-boards
Otter Boards, a team task board you can run from the web or from Claude and ChatGPT. FastAPI + DataK3 + appid + Models + MCP.
A remix template · 1 remixes · Remix stubborn-otter/otter-boards
tests/fakes.py
"""In-memory stand-ins, so the rules run in a second with no Dodil account.
MemoryBoards hands out copies, like a database does: a use case that changes an object and forgets
to save it fails here too.
"""
from copy import deepcopy
from boards.application.ports import AssistantUnavailable
class MemoryBoards:
def __init__(self):
self.boards, self.lists, self.cards, self.events, self.vectors = {}, {}, {}, [], {}
self.saves = 0
def get(self, board_id):
return deepcopy(self.boards.get(board_id))
def boards_for(self, email):
return [deepcopy(b) for b in self.boards.values()
if b.owner_email == email or email in b.members]
def save_board(self, board):
self.boards[board.id] = deepcopy(board)
self.saves += 1
def remove_member(self, board_id, email):
self.boards[board_id].members.pop(email, None)
self.saves += 1
def lists_on(self, board_id):
found = [x for x in self.lists.values() if x.board_id == board_id and not x.archived]
return deepcopy(sorted(found, key=lambda x: x.position))
def save_list(self, board_list):
self.lists[board_list.id] = deepcopy(board_list)
self.saves += 1
def get_card(self, card_id):
return deepcopy(self.cards.get(card_id))
def cards_on(self, board_id):
found = [c for c in self.cards.values() if c.board_id == board_id and not c.archived]
return deepcopy(sorted(found, key=lambda c: (c.position, c.number)))
def last_card_number(self, board_id):
return max((c.number for c in self.cards.values() if c.board_id == board_id), default=0)
def insert_card(self, card):
if card.id in self.cards:
return False
self.cards[card.id] = deepcopy(card)
self.saves += 1
return True
def save_card(self, card):
self.cards[card.id] = deepcopy(card)
self.saves += 1
def log(self, event):
self.events.append(deepcopy(event))
self.saves += 1
def history(self, card_id):
return deepcopy(sorted((e for e in self.events if e.card_id == card_id),
key=lambda e: e.id))
def activity(self, board_id, limit):
found = sorted((e for e in self.events if e.board_id == board_id),
key=lambda e: e.id, reverse=True)
return deepcopy(found[:limit])
def embeddings(self, board_id):
return {card_id: (digest, list(vector))
for (owner, card_id), (digest, vector) in self.vectors.items() if owner == board_id}
def save_embedding(self, board_id, card_id, text_hash, vector):
self.vectors[(board_id, card_id)] = (text_hash, list(vector))
class FakeAI:
"""Embeds by topic, so 'meaning' is visibly different from shared words."""
TOPICS = (("money", ("invoice", "bill", "payment", "receipt")),
("party", ("launch", "party", "cake")),
("site", ("website", "homepage", "css")))
def __init__(self):
self.down = False
self.embedded = []
def _guard(self):
if self.down:
raise AssistantUnavailable("Dodil Models didn't answer")
def checklist(self, title, description=""):
self._guard()
return ["step one", "step two"]
def embed(self, texts):
self._guard()
self.embedded.extend(texts)
return [[float(any(w in t.lower() for w in words)) for _, words in self.TOPICS]
for t in texts]
def summary(self, facts):
self._guard()
return "AI summary: " + facts.splitlines()[0]