- Removed PersonalStorage (redundant - RoomStorage handles both via room_id) - Added ensure_room() and ensure_tracking() methods for explicit creation - Added @runtime_checkable to Protocols for isinstance checks - Added tests/test_ports.py with 11 unit tests for storage protocols
75 lines
2.4 KiB
Python
75 lines
2.4 KiB
Python
"""Abstract storage interfaces (Ports) for JIGAIDO storage adapters."""
|
|
|
|
from typing import Protocol, runtime_checkable
|
|
|
|
from core.models import Bounty, RoomData, TrackingData, TrackedBounty
|
|
|
|
|
|
@runtime_checkable
|
|
class RoomStorage(Protocol):
|
|
"""Storage port for room bounties.
|
|
|
|
A room is identified by room_id:
|
|
- Negative room_id: Telegram group (e.g., -1001)
|
|
- Positive room_id: DM/personal context (user's Telegram ID)
|
|
|
|
This single port handles both group and personal bounties.
|
|
"""
|
|
|
|
def load(self, room_id: int) -> RoomData | None:
|
|
"""Load all data for a room. Returns None if room doesn't exist."""
|
|
...
|
|
|
|
def save(self, room_data: RoomData) -> None:
|
|
"""Save all data for a room."""
|
|
...
|
|
|
|
def ensure_room(self, room_id: int) -> RoomData:
|
|
"""Ensure a room exists, creating it if necessary. Returns RoomData."""
|
|
...
|
|
|
|
def add_bounty(self, room_id: int, bounty: Bounty) -> None:
|
|
"""Add a new bounty to a room. Creates room if it doesn't exist."""
|
|
...
|
|
|
|
def update_bounty(self, room_id: int, bounty: Bounty) -> None:
|
|
"""Update an existing bounty in a room."""
|
|
...
|
|
|
|
def delete_bounty(self, room_id: int, bounty_id: int) -> None:
|
|
"""Delete a bounty from a room."""
|
|
...
|
|
|
|
def get_bounty(self, room_id: int, bounty_id: int) -> Bounty | None:
|
|
"""Get a specific bounty from a room by ID."""
|
|
...
|
|
|
|
|
|
@runtime_checkable
|
|
class TrackingStorage(Protocol):
|
|
"""Storage port for tracking data.
|
|
|
|
Tracks which bounties a user is tracking in a specific room.
|
|
Use ensure_tracking() to create a new tracking entry before tracking bounties.
|
|
"""
|
|
|
|
def load(self, room_id: int, user_id: int) -> TrackingData | None:
|
|
"""Load tracking data for a user in a room. Returns None if not tracking anything."""
|
|
...
|
|
|
|
def save(self, tracking_data: TrackingData) -> None:
|
|
"""Save tracking data."""
|
|
...
|
|
|
|
def ensure_tracking(self, room_id: int, user_id: int) -> TrackingData:
|
|
"""Ensure tracking exists for user in room, creating if necessary. Returns TrackingData."""
|
|
...
|
|
|
|
def track_bounty(self, room_id: int, user_id: int, tracked: TrackedBounty) -> None:
|
|
"""Add a bounty to a user's tracking list. Creates tracking if needed."""
|
|
...
|
|
|
|
def untrack_bounty(self, room_id: int, user_id: int, bounty_id: int) -> None:
|
|
"""Remove a bounty from a user's tracking list."""
|
|
...
|