Add filtering methods to JsonFileRoomStorage for Phase 2 soft delete support: - list_bounties(room_id): returns only non-deleted bounties for normal queries - list_all_bounties(room_id, include_deleted=True): returns all bounties for /recover Update RoomStorage protocol to include the new methods. Update mock classes in tests to pass isinstance checks. Fixes #42
85 lines
2.6 KiB
Python
85 lines
2.6 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 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."""
|
|
...
|
|
|
|
def list_bounties(self, room_id: int) -> list[Bounty]:
|
|
"""List all non-deleted bounties in a room.
|
|
|
|
Soft-deleted bounties (where deleted_at is not None) are excluded.
|
|
"""
|
|
...
|
|
|
|
def list_all_bounties(
|
|
self, room_id: int, include_deleted: bool = True
|
|
) -> list[Bounty]:
|
|
"""List all bounties including or excluding soft-deleted.
|
|
|
|
Args:
|
|
room_id: The room ID
|
|
include_deleted: If True, return all bounties including soft-deleted.
|
|
If False, return only non-deleted bounties.
|
|
"""
|
|
...
|
|
|
|
|
|
@runtime_checkable
|
|
class TrackingStorage(Protocol):
|
|
"""Storage port for tracking data.
|
|
|
|
Tracks which bounties a user is tracking in a specific room.
|
|
"""
|
|
|
|
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 track_bounty(self, room_id: int, user_id: int, tracked: TrackedBounty) -> None:
|
|
"""Add a bounty to a user's tracking list. Creates tracking entry 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."""
|
|
...
|