forked from GamesDoneQuick/donation-tracker
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathprizeutil.py
121 lines (111 loc) · 4.32 KB
/
prizeutil.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import datetime
import pytz
import random
from django.db import transaction
from . import util
from tracker.models import PrizeKey, PrizeWinner
from functools import reduce
@transaction.atomic()
def draw_prize(prize, seed=None, rand=None):
try:
rand = rand or random.Random(seed)
except TypeError:
return False, {'error': 'Seed parameter was unhashable'}
if prize.key_code:
return False, {'error': 'Key code prizes cannot be drawn with this method.'}
eligible = prize.eligible_donors()
if prize.maxed_winners():
if prize.maxwinners == 1:
return False, {'error': 'Prize: ' + prize.name + ' already has a winner.'}
else:
return (
False,
{
'error': 'Prize: '
+ prize.name
+ ' already has the maximum number of winners allowed.'
},
)
today = datetime.datetime.today()
delta = datetime.timedelta(days=prize.event.prize_accept_deadline_delta)
acceptDeadline = (
today.replace(tzinfo=util.anywhere_on_earth_tz(), hour=23, minute=59, second=59)
+ delta
)
if not eligible:
return False, {'error': 'Prize: ' + prize.name + ' has no eligible donors.'}
# TODO: clean this up and make it real
# elif len(prize.eligible_donors()) <= (prize.maxwinners - len(prize.get_prize_winners())):
# winners = PrizeWinner.objects.bulk_create(
# [PrizeWinner(prize=prize, winner_id=d['donor'], acceptdeadline=acceptDeadline) for d in eligible]
# )
# return True, {'winners': [w.id for w in winners]}
else:
psum = reduce(lambda a, b: a + b['weight'], eligible, 0.0)
result = rand.random() * psum
ret = {'sum': psum, 'result': result}
for d in eligible:
if result < d['weight']:
try:
winRecord, created = PrizeWinner.objects.get_or_create(
prize=prize,
winner_id=d['donor'],
defaults=dict(acceptdeadline=acceptDeadline),
)
if not created:
winRecord.pendingcount += 1
ret['winner'] = winRecord.winner.id
winRecord.save()
except Exception as e:
return (
False,
{'error': 'Error drawing prize: ' + prize.name + ', ' + str(e)},
)
return True, ret
result -= d['weight']
return False, {'error': 'Prize drawing algorithm failed.'}
@transaction.atomic()
def draw_keys(prize, seed=None, rand=None):
try:
rand = rand or random.Random(seed)
except TypeError:
return False, {'error': 'Seed parameter was unhashable'}
eligible = prize.eligible_donors()
if not eligible:
return False, {'error': 'Prize: ' + prize.name + ' has no eligible donors.'}
unclaimed_keys = (
PrizeKey.objects.select_for_update()
.filter(prize=prize, prize_winner_id=None)
.order_by()
)
if unclaimed_keys.count() >= len(eligible):
winners = eligible
else:
winners = rand.sample(eligible, unclaimed_keys.count())
for key, d in zip(unclaimed_keys, winners):
key.prize_winner = PrizeWinner.objects.create(
prize=prize,
winner_id=d['donor'],
pendingcount=0,
acceptcount=1,
emailsent=True,
acceptemailsentcount=1,
shippingstate='SHIPPED',
)
key.save()
return True, {'winners': [w['donor'] for w in winners]}
def get_past_due_prize_winners(event):
now = datetime.datetime.utcnow().astimezone(pytz.utc)
return PrizeWinner.objects.filter(acceptdeadline__lte=now, pendingcount__gte=1)
def close_past_due_prize_winners(event, verbosity=0, dry_run=False):
for prizewinner in get_past_due_prize_winners(event):
if verbosity > 0:
print(
'Closing Prize Winner #{0} with {1} pending'.format(
prizewinner.id, prizewinner.pendingcount
)
)
if not dry_run:
prizewinner.declinecount += prizewinner.pendingcount
prizewinner.pendingcount = 0
prizewinner.save()