Address PR #19 review feedback round 2:

- Bounty.created_by_user_id is now non-optional (always required)
- Removed is_group from RoomData (negative room_id is self-documenting)
- Added room_id to TrackedBounty to track which room bounty was tracked from
- Added clarifying docstrings explaining TrackingData vs TrackedBounty
- Updated tests to match new model structure
This commit is contained in:
shokollm
2026-04-02 22:24:12 +00:00
parent f1ef33451c
commit 330203e6ef
2 changed files with 38 additions and 42 deletions

View File

@@ -1,53 +1,63 @@
"""Domain dataclasses for JIGAIDO bounty tracker.""" """Domain dataclasses for JIGAIDO bounty tracker."""
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional
@dataclass @dataclass
class Bounty: class Bounty:
"""Bounty - used for both group and personal bounties. """A bounty created by a user.
Use created_by_user_id to distinguish: if set, it's a group bounty The created_by_user_id field always refers to the user who created the bounty.
created by that user. If None, it's a personal/DM bounty. It does NOT indicate whether the bounty is a group or personal bounty.
""" """
id: int id: int
text: Optional[str] text: str | None
link: Optional[str] link: str | None
due_date_ts: Optional[int] due_date_ts: int | None
created_at: int created_at: int
created_by_user_id: Optional[int] = None created_by_user_id: int
@dataclass @dataclass
class TrackedBounty: class TrackedBounty:
"""A bounty that a user is tracking.""" """A bounty that a user is tracking.
Use TrackedBounty when you need to record that a user is tracking a specific
bounty from a specific room. The room_id indicates which room the bounty
was tracked from (useful when users can track bounties across multiple rooms).
For simple tracking lists, just use bounty_id and created_at.
"""
bounty_id: int bounty_id: int
created_at: int created_at: int
room_id: int
@dataclass @dataclass
class RoomData: class RoomData:
"""All data for a room (group or DM). """All data for a room (group or DM).
For groups: is_group=True, room_id is negative (Telegram group ID) The room_id can be negative for Telegram groups or positive for DMs.
For DMs: is_group=False, room_id is the user_id (positive) The next_id field is used to generate unique bounty IDs within this room.
next_id is used to generate unique bounty IDs within this room.
""" """
room_id: int room_id: int
is_group: bool
bounties: list[Bounty] bounties: list[Bounty]
next_id: int next_id: int
@dataclass @dataclass
class TrackingData: class TrackingData:
"""User tracking data within a group. """User tracking state within a group.
group_id is the Telegram group ID (always negative). TrackingData vs TrackedBounty:
- Use TrackingData to store ALL tracked bounties for a user in a specific group.
It contains the group_id, user_id, and a list of TrackedBounty entries.
- Use TrackedBounty to represent a single tracked bounty entry within that list.
TrackingData is the container, TrackedBounty is the item.
""" """
group_id: int group_id: int

View File

@@ -20,14 +20,14 @@ class TestBounty:
link="https://github.com/example/repo/issues/1", link="https://github.com/example/repo/issues/1",
due_date_ts=1735689600, due_date_ts=1735689600,
created_at=1735603200, created_at=1735603200,
created_by_user_id=None, created_by_user_id=123,
) )
assert b.id == 1 assert b.id == 1
assert b.text == "Fix the bug" assert b.text == "Fix the bug"
assert b.link == "https://github.com/example/repo/issues/1" assert b.link == "https://github.com/example/repo/issues/1"
assert b.due_date_ts == 1735689600 assert b.due_date_ts == 1735689600
assert b.created_at == 1735603200 assert b.created_at == 1735603200
assert b.created_by_user_id is None assert b.created_by_user_id == 123
def test_bounty_optional_fields_can_be_none(self): def test_bounty_optional_fields_can_be_none(self):
b = Bounty( b = Bounty(
@@ -36,7 +36,7 @@ class TestBounty:
link=None, link=None,
due_date_ts=None, due_date_ts=None,
created_at=0, created_at=0,
created_by_user_id=None, created_by_user_id=123,
) )
assert b.text is None assert b.text is None
assert b.link is None assert b.link is None
@@ -49,7 +49,7 @@ class TestBounty:
link=None, link=None,
due_date_ts=None, due_date_ts=None,
created_at=0, created_at=0,
created_by_user_id=None, created_by_user_id=123,
) )
b2 = Bounty( b2 = Bounty(
id=1, id=1,
@@ -57,7 +57,7 @@ class TestBounty:
link=None, link=None,
due_date_ts=None, due_date_ts=None,
created_at=0, created_at=0,
created_by_user_id=None, created_by_user_id=123,
) )
assert b1 == b2 assert b1 == b2
@@ -68,7 +68,7 @@ class TestBounty:
link=None, link=None,
due_date_ts=None, due_date_ts=None,
created_at=0, created_at=0,
created_by_user_id=None, created_by_user_id=123,
) )
b2 = Bounty( b2 = Bounty(
id=2, id=2,
@@ -76,31 +76,21 @@ class TestBounty:
link=None, link=None,
due_date_ts=None, due_date_ts=None,
created_at=0, created_at=0,
created_by_user_id=None, created_by_user_id=456,
) )
assert b1 != b2 assert b1 != b2
def test_bounty_with_created_by_user_id(self):
b = Bounty(
id=1,
text="Group task",
link=None,
due_date_ts=None,
created_at=0,
created_by_user_id=123456,
)
assert b.created_by_user_id == 123456
class TestTrackedBounty: class TestTrackedBounty:
def test_create_tracked_bounty(self): def test_create_tracked_bounty(self):
tb = TrackedBounty(bounty_id=5, created_at=1735600000) tb = TrackedBounty(bounty_id=5, created_at=1735600000, room_id=-1001)
assert tb.bounty_id == 5 assert tb.bounty_id == 5
assert tb.created_at == 1735600000 assert tb.created_at == 1735600000
assert tb.room_id == -1001
def test_tracked_bounty_comparison(self): def test_tracked_bounty_comparison(self):
tb1 = TrackedBounty(bounty_id=1, created_at=0) tb1 = TrackedBounty(bounty_id=1, created_at=0, room_id=-1001)
tb2 = TrackedBounty(bounty_id=1, created_at=0) tb2 = TrackedBounty(bounty_id=1, created_at=0, room_id=-1001)
assert tb1 == tb2 assert tb1 == tb2
@@ -108,24 +98,20 @@ class TestRoomData:
def test_create_group_room_data(self): def test_create_group_room_data(self):
rd = RoomData( rd = RoomData(
room_id=-1001, room_id=-1001,
is_group=True,
bounties=[], bounties=[],
next_id=1, next_id=1,
) )
assert rd.room_id == -1001 assert rd.room_id == -1001
assert rd.is_group is True
assert rd.bounties == [] assert rd.bounties == []
assert rd.next_id == 1 assert rd.next_id == 1
def test_create_dm_room_data(self): def test_create_dm_room_data(self):
rd = RoomData( rd = RoomData(
room_id=123456, room_id=123456,
is_group=False,
bounties=[], bounties=[],
next_id=1, next_id=1,
) )
assert rd.room_id == 123456 assert rd.room_id == 123456
assert rd.is_group is False
assert rd.bounties == [] assert rd.bounties == []
assert rd.next_id == 1 assert rd.next_id == 1
@@ -138,7 +124,7 @@ class TestRoomData:
created_at=0, created_at=0,
created_by_user_id=123, created_by_user_id=123,
) )
rd = RoomData(room_id=-1001, is_group=True, bounties=[b], next_id=2) rd = RoomData(room_id=-1001, bounties=[b], next_id=2)
assert len(rd.bounties) == 1 assert len(rd.bounties) == 1
assert rd.bounties[0].text == "Task" assert rd.bounties[0].text == "Task"
assert rd.bounties[0].created_by_user_id == 123 assert rd.bounties[0].created_by_user_id == 123
@@ -152,7 +138,7 @@ class TestTrackingData:
assert td.tracked == [] assert td.tracked == []
def test_tracking_data_with_tracked(self): def test_tracking_data_with_tracked(self):
tb = TrackedBounty(bounty_id=5, created_at=0) tb = TrackedBounty(bounty_id=5, created_at=0, room_id=-1001)
td = TrackingData(group_id=-1001, user_id=123, tracked=[tb]) td = TrackingData(group_id=-1001, user_id=123, tracked=[tb])
assert len(td.tracked) == 1 assert len(td.tracked) == 1
assert td.tracked[0].bounty_id == 5 assert td.tracked[0].bounty_id == 5