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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user