Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

## Requirements

- Python 3.9 to 3.13
- Python 3.10 to 3.14
- Django 4.2, 5.1, or 5.2

Additionally, if you are planning on developing, and/or building the JS bundles yourself:

- Node (only LTS versions are officially supported, currently 18, 20, and 22)
- Node (only LTS versions are officially supported, currently 20, 22, and 24)
- `yarn` (`npm i -g yarn`)
- `pre-commit` (`pip install pre-commit`)

Expand Down
14 changes: 7 additions & 7 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ jobs:
strategy:
matrix:
Oldest:
PYTHON_VERSION: '3.9'
PYTHON_VERSION: '3.10'
DJANGO_VERSION: '4.2'
Django42:
PYTHON_VERSION: '3.12'
DJANGO_VERSION: '4.2'
Django51:
PYTHON_VERSION: '3.13'
DJANGO_VERSION: '5.1'
Python3A:
PYTHON_VERSION: '3.10'
DJANGO_VERSION: '5.2'
Python3B:
PYTHON_VERSION: '3.11'
DJANGO_VERSION: '5.2'
Python3C:
PYTHON_VERSION: '3.12'
DJANGO_VERSION: '5.2'
Latest:
Python3D:
PYTHON_VERSION: '3.13'
DJANGO_VERSION: '5.2'
Latest:
PYTHON_VERSION: '3.14'
DJANGO_VERSION: '5.2'

steps:
- task: UsePythonVersion@0
Expand Down Expand Up @@ -165,12 +165,12 @@ jobs:
YARN_CACHE_FOLDER: $(Pipeline.Workspace)/.yarn
strategy:
matrix:
Node18:
NODE_VERSION: 18
Node20:
NODE_VERSION: 20
Node22:
NODE_VERSION: 22
Node24:
NODE_VERSION: 24

steps:
- task: NodeTool@0
Expand Down
2 changes: 2 additions & 0 deletions bundles/processing/modules/donations/DonationRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ export default function DonationRow(props: DonationRowProps) {
<HighlightKeywords>{donation.donor_name || UNKNOWN_DONOR_NAME}</HighlightKeywords>
</strong>
{donation.pinned && <Pin className={styles.pinIcon} />}
{' · '}
<Tag>{donation.domain}</Tag>
</Text>
);

Expand Down
9 changes: 9 additions & 0 deletions bundles/public/apiv2/APITypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,17 @@ export interface DonationPost {
bids: DonationPostBid[];
domain?: DonationDomain; // defaults to 'LOCAL'
// only with creation permission
domain_id?: string; // required for 'TWITCH' donations
donor_email?: string;
donor_id?: number;
donor_twitch_id?: number;
}

export interface DonationBidPost {
bid?: number;
amount: number;
parent?: number;
name?: string;
}

export interface APIRun
Expand Down
2 changes: 1 addition & 1 deletion bundles/public/apiv2/Models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export interface Event extends ModelBase {
}

export type DonationTransactionState = 'COMPLETED' | 'PENDING' | 'CANCELLED' | 'FLAGGED';
export type DonationDomain = 'PAYPAL' | 'LOCAL' | 'CHIPIN';
export type DonationDomain = 'PAYPAL' | 'LOCAL' | 'CHIPIN' | 'TWITCH';
export type DonationReadState = 'PENDING' | 'READY' | 'IGNORED' | 'READ' | 'FLAGGED';
export type DonationCommentState = 'ABSENT' | 'PENDING' | 'DENIED' | 'APPROVED' | 'FLAGGED';

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ classifiers = [
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries :: Python Modules',
]
Expand All @@ -44,7 +44,7 @@ dynamic = ['version']
license-files = ['LICENSE', 'tracker/static/gen/**/*.LICENSE.txt']
name = 'django-donation-tracker'
readme = 'README.md'
requires-python = '>= 3.9'
requires-python = '>= 3.10'
[project.optional-dependencies]
development = [
'daphne~=4.0',
Expand Down
82 changes: 82 additions & 0 deletions tests/apiv2/test_donate.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
class TestDonate(APITestCase):
def setUp(self):
super().setUp()
self.event.minimumdonation = 5
self.event.save()
self.opened_challenge = models.Bid.objects.create(
event=self.event, name='Challenge', goal=1000, istarget=True, state='OPENED'
)
Expand Down Expand Up @@ -136,6 +138,14 @@ def test_donate(self):
expected_error_codes={'domain': 'invalid'},
)

with self.subTest('twitch without uuid'):
self.post_new(
data={**valid, 'domain': 'TWITCH'},
status_code=400,
model_name='donate',
expected_error_codes={'domain_id': 'invalid'},
)

with self.subTest('other domain'):
self.post_new(
data={**valid, 'domain': 'CHIPIN'},
Expand Down Expand Up @@ -371,6 +381,7 @@ def test_donate(self):
)
donation = models.Donation.objects.get(id=response['id'])
self.assertV2ModelPresent(DonationSerializer(donation).data, response)
self.assertEqual(donation.donor, self.donor)

response = self.post_new(
data={
Expand All @@ -386,3 +397,74 @@ def test_donate(self):
)
donation = models.Donation.objects.get(id=response['id'])
self.assertV2ModelPresent(DonationSerializer(donation).data, response)
self.assertEqual(donation.donor, self.donor)

with self.subTest('create twitch'), self.saveSnapshot():
with self.subTest('without payload'):
response = self.post_new(
data={
**valid,
'amount': 1, # twitch donations can be as small as a dollar
# FIXME: what to do about hidden bids?
'bids': [],
'domain': 'TWITCH',
'domain_id': 'some-twitch-uuid',
'requested_alias': 'Kappa',
'donor_twitch_id': 12345678,
},
model_name='donate',
status_code=201,
user=self.add_user,
)
donation = models.Donation.objects.get(id=response['id'])
self.assertV2ModelPresent(DonationSerializer(donation).data, response)
self.assertEqual(donation.donor.twitch_id, 12345678)
self.assertEqual(donation.donor.email, '[email protected]')
self.assertEqual(donation.donor.alias, 'Kappa')
self.assertEqual(donation.donor.visibility, 'ALIAS')
self.assertEqual(donation.domainId, 'some-twitch-uuid'),
self.assertEqual(donation.requestedalias, 'Kappa')
self.assertEqual(donation.requestedvisibility, 'ALIAS')
self.assertEqual(donation.transactionstate, 'COMPLETED')

with self.subTest('with payload'):
twitch_payload = dict(
id='a1b2c3-aabb-4455-d1e2f3',
campaign_id='123-abc-456-def',
broadcaster_user_id='123456',
broadcaster_user_name='SunnySideUp',
broadcaster_user_login='sunnysideup',
user_id='654321',
user_login='generoususer1',
user_name='GenerousUser1',
charity_name='Example name',
charity_description='Example description',
charity_logo='https://abc.cloudfront.net/ppgf/1000/100.png',
charity_website='https://www.example.com',
amount=dict(value=10000, decimal_places=2, currency='USD'),
)
response = self.post_new(
data=dict(
event=valid['event'],
twitch=twitch_payload,
),
model_name='donate',
status_code=201,
user=self.add_user,
)
donation = models.Donation.objects.get(id=response['id'])
self.assertV2ModelPresent(DonationSerializer(donation).data, response)
self.assertEqual(
donation.donor.twitch_id, int(twitch_payload['user_id'])
)
self.assertEqual(
donation.donor.email,
f"{int(twitch_payload['user_id'])}@users.twitch.tv.fake",
)
self.assertEqual(donation.donor.alias, twitch_payload['user_name'])
self.assertEqual(donation.donor.visibility, 'ALIAS')
self.assertEqual(donation.domainId, twitch_payload['id'])
self.assertEqual(donation.requestedalias, twitch_payload['user_name'])
self.assertEqual(donation.requestedvisibility, 'ALIAS')
self.assertEqual(donation.transactionstate, 'COMPLETED')
self.assertEqual(donation.amount, 100)
Loading
Loading