symposion_app/pinaxcon/raffle/signals.py

81 lines
2.8 KiB
Python
Raw Normal View History

2019-01-02 01:44:57 +00:00
from itertools import chain
from random import sample
from django.db import IntegrityError
from django.db.models.signals import post_save, pre_save, pre_delete, post_init
from django.dispatch import receiver
from pinaxcon.raffle.models import DrawnTicket, Raffle, Draw, Prize
# Much of the following could be handled by directly overriding the
# relevant model methods. However, since `.objects.delete()` bypasses
# a model's delete() method but not its pre_ and post_delete signals,
# using signals gives us slightly better coverage of edge cases.
#
# In order to avoid mixing the two approaches we make extensive use of
# signals.
@receiver(post_save, sender=Draw)
def draw_raffle_tickets(sender, instance, created, **kwargs):
"""
Draws tickets once a :model:`pinaxcon_raffle.Draw` instance
has been created and prizes are still available.
"""
if not created:
return
raffle = instance.raffle
prizes = raffle.prizes.filter(winning_ticket__isnull=True)
tickets = list(chain(*(ticket[1] for ticket in raffle.get_tickets())))
if not tickets:
return
drawn_tickets = sample(tickets, len(prizes))
for prize, ticket in zip(prizes, drawn_tickets):
item_id = int(ticket.split('-')[0])
drawn_ticket = DrawnTicket.objects.create(
draw=instance,
prize=prize,
ticket=ticket,
lineitem_id=item_id,
)
prize.winning_ticket = drawn_ticket
prize.save(update_fields=('winning_ticket',))
@receiver(post_init, sender=Prize)
def set_prize_lock(sender, instance, **kwargs):
"""Locks :model:`pinaxcon_raffle.Prize` if a winner exists."""
instance._locked = instance.winning_ticket is not None
@receiver(pre_save, sender=Prize)
def enforce_prize_lock(sender, instance, **kwargs):
"""Denies updates to :model:`pinaxcon_raffle.Prize` if lock is in place."""
if instance.locked:
raise IntegrityError("Updating a locked prize is not allowed.")
@receiver(pre_delete, sender=Prize)
def prevent_locked_prize_deletion(sender, instance, **kwargs):
"""Denies deletion of :model:`pinaxcon_raffle.Prize` if lock is in place."""
if instance.locked:
raise IntegrityError("Deleting a locked prize is not allowed.")
@receiver(pre_delete, sender=DrawnTicket)
def prevent_drawn_ticket_deletion(sender, instance, **kwargs):
2019-01-21 02:39:35 +00:00
"""Protects :model:`pinaxcon_raffle.DrawnTicket` from deletion if lock is in place."""
if instance.locked:
raise IntegrityError("Deleting a drawn ticket is not allowed.")
2019-01-02 01:44:57 +00:00
@receiver(pre_save, sender=DrawnTicket)
def prevent_drawn_ticket_update(sender, instance, **kwargs):
"""Protects :model:`pinaxcon_raffle.DrawnTicket` from updates."""
if getattr(instance, 'pk', None):
raise IntegrityError("Updating a drawn ticket is not allowed.")