Coverage for polar/models/issue_reward.py: 59%
41 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 12:42 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 12:42 +0000
1from decimal import Decimal
2from typing import TYPE_CHECKING
3from uuid import UUID
5from sqlalchemy import BigInteger, ForeignKey, String, UniqueConstraint, Uuid
6from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship
8from polar.kit.db.models import RecordModel
9from polar.models.organization import Organization
10from polar.models.user import User
12if TYPE_CHECKING: 12 ↛ 13line 12 didn't jump to line 13 because the condition on line 12 was never true
13 from .account import Account
14 from .pledge import Pledge
17class IssueReward(RecordModel):
18 __tablename__ = "issue_rewards"
20 __table_args__ = (
21 UniqueConstraint("issue_reference", "github_username"),
22 UniqueConstraint("issue_reference", "organization_id"),
23 UniqueConstraint("issue_reference", "user_id"),
24 )
26 issue_reference: Mapped[str] = mapped_column(String, nullable=False, index=True)
28 # 10% == 100
29 share_thousands: Mapped[int] = mapped_column(BigInteger, nullable=False)
31 github_username: Mapped[str | None] = mapped_column(
32 String, nullable=True, default=None
33 )
35 organization_id: Mapped[UUID | None] = mapped_column(
36 Uuid, ForeignKey("organizations.id"), nullable=True, default=None, index=True
37 )
39 user_id: Mapped[UUID | None] = mapped_column(
40 Uuid, ForeignKey("users.id"), nullable=True, default=None
41 )
43 @declared_attr
44 def organization(cls) -> Mapped[Organization]:
45 return relationship(
46 Organization,
47 lazy="raise",
48 )
50 @declared_attr
51 def user(cls) -> Mapped[User]:
52 return relationship(User, lazy="raise")
54 @property
55 def pct(self) -> Decimal:
56 # Use decimal to avoid binary floating point issues
57 return Decimal(self.share_thousands) / 1000
59 def get_share_amount(self, pledge: "Pledge") -> int:
60 return round(pledge.amount * self.pct)
62 def get_rewarded(self) -> "Organization | User | None":
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
69 def get_rewarded_account(self) -> "Account | None":
70 rewarded = self.get_rewarded()
71 if rewarded is not None:
72 return rewarded.account
73 return None