Coverage for polar/models/issue_reward.py: 59%

41 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-12-05 16:17 +0000

1from decimal import Decimal 1ab

2from typing import TYPE_CHECKING 1ab

3from uuid import UUID 1ab

4 

5from sqlalchemy import BigInteger, ForeignKey, String, UniqueConstraint, Uuid 1ab

6from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship 1ab

7 

8from polar.kit.db.models import RecordModel 1ab

9from polar.models.organization import Organization 1ab

10from polar.models.user import User 1ab

11 

12if TYPE_CHECKING: 12 ↛ 13line 12 didn't jump to line 13 because the condition on line 12 was never true1ab

13 from .account import Account 

14 from .pledge import Pledge 

15 

16 

17class IssueReward(RecordModel): 1ab

18 __tablename__ = "issue_rewards" 1ab

19 

20 __table_args__ = ( 1ab

21 UniqueConstraint("issue_reference", "github_username"), 

22 UniqueConstraint("issue_reference", "organization_id"), 

23 UniqueConstraint("issue_reference", "user_id"), 

24 ) 

25 

26 issue_reference: Mapped[str] = mapped_column(String, nullable=False, index=True) 1ab

27 

28 # 10% == 100 

29 share_thousands: Mapped[int] = mapped_column(BigInteger, nullable=False) 1ab

30 

31 github_username: Mapped[str | None] = mapped_column( 1ab

32 String, nullable=True, default=None 

33 ) 

34 

35 organization_id: Mapped[UUID | None] = mapped_column( 1ab

36 Uuid, ForeignKey("organizations.id"), nullable=True, default=None, index=True 

37 ) 

38 

39 user_id: Mapped[UUID | None] = mapped_column( 1ab

40 Uuid, ForeignKey("users.id"), nullable=True, default=None 

41 ) 

42 

43 @declared_attr 1ab

44 def organization(cls) -> Mapped[Organization]: 1ab

45 return relationship( 1ab

46 Organization, 

47 lazy="raise", 

48 ) 

49 

50 @declared_attr 1ab

51 def user(cls) -> Mapped[User]: 1ab

52 return relationship(User, lazy="raise") 1ab

53 

54 @property 1ab

55 def pct(self) -> Decimal: 1ab

56 # Use decimal to avoid binary floating point issues 

57 return Decimal(self.share_thousands) / 1000 

58 

59 def get_share_amount(self, pledge: "Pledge") -> int: 1ab

60 return round(pledge.amount * self.pct) 

61 

62 def get_rewarded(self) -> "Organization | User | None": 1ab

63 if self.organization is not None: 

64 return self.organization 

65 elif self.user is not None: 

66 return self.user 

67 return None 

68 

69 def get_rewarded_account(self) -> "Account | None": 1ab

70 rewarded = self.get_rewarded() 

71 if rewarded is not None: 

72 return rewarded.account 

73 return None