Create new CampPropertyListFilter based on admin.SimpleListFilter to use for admin filtering of CampRelatedModels where self.camp is a property instead of a real field. Change the Team models camp field to a property, and make the Team model use the new CampPropertyListFilter in the admin. Change the BaseTicket model to being a CampRelatedModel and add a camp property, also, while here move ticket_created signal to signals.py, connect it in apps.py and rename it to ticket_updated. Change Sponsor model to being a CampRelatedModel and add a camp property.

This commit is contained in:
Thomas Steen Rasmussen 2018-03-04 12:04:07 +01:00
parent 6fe1790e89
commit 60c4bb49fb
9 changed files with 155 additions and 76 deletions

View file

@ -1,5 +1,6 @@
from camps.models import Camp from camps.models import Camp
from django.utils import timezone from django.utils import timezone
from django.contrib import admin
def get_current_camp(): def get_current_camp():
try: try:
@ -7,3 +8,41 @@ def get_current_camp():
except Camp.DoesNotExist: except Camp.DoesNotExist:
return False return False
class CampPropertyListFilter(admin.SimpleListFilter):
"""
SimpleListFilter to filter models by camp when camp is
a property and not a real model field.
"""
title = 'Camp'
parameter_name = 'camp'
def lookups(self, request, model_admin):
# get the current queryset
qs = model_admin.get_queryset(request)
# get a list of the unique camps in the current queryset
unique_camps = set([item.camp for item in qs])
# loop over camps and yield each as a tuple
for camp in unique_camps:
yield (camp.slug, camp.title)
def queryset(self, request, queryset):
# if self.value() is None return everything
if not self.value():
return queryset
# ok, get the Camp
try:
camp = Camp.objects.get(slug=self.value())
except Camp.DoesNotExist:
# camp not found, return nothing
return queryset.model.objects.none()
# filter out items related to other camps
for item in queryset:
if item.camp != camp:
queryset = queryset.exclude(pk=item.pk)
return queryset

View file

@ -13,7 +13,7 @@ def get_sponsor_upload_path(instance, filename):
) )
class Sponsor(CreatedUpdatedModel): class Sponsor(CampRelatedModel):
name = models.CharField( name = models.CharField(
max_length=150, max_length=150,
help_text='Name of the sponsor' help_text='Name of the sponsor'
@ -39,6 +39,9 @@ class Sponsor(CreatedUpdatedModel):
def __str__(self): def __str__(self):
return '{} ({})'.format(self.name, self.tier.camp) return '{} ({})'.format(self.name, self.tier.camp)
@property
def camp(self):
return self.tier.camp
class SponsorTier(CampRelatedModel): class SponsorTier(CampRelatedModel):
name = models.CharField( name = models.CharField(

View file

@ -1,6 +1,7 @@
from django.contrib import admin from django.contrib import admin
from .models import Team, TeamArea, TeamMember, TeamTask from .models import Team, TeamArea, TeamMember, TeamTask
from .email import add_added_membership_email, add_removed_membership_email from .email import add_added_membership_email, add_removed_membership_email
from camps.utils import CampPropertyListFilter
@admin.register(TeamTask) @admin.register(TeamTask)
@ -27,11 +28,10 @@ class TeamAdmin(admin.ModelAdmin):
] ]
list_filter = [ list_filter = [
'camp', CampPropertyListFilter,
'needs_members', 'needs_members',
] ]
@admin.register(TeamMember) @admin.register(TeamMember)
class TeamMemberAdmin(admin.ModelAdmin): class TeamMemberAdmin(admin.ModelAdmin):
list_filter = [ list_filter = [

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2018-03-04 09:19
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('teams', '0018_auto_20171122_2204'),
]
operations = [
migrations.AlterUniqueTogether(
name='team',
unique_together=set([]),
),
migrations.RemoveField(
model_name='team',
name='camp',
),
]

View file

@ -29,13 +29,8 @@ class TeamArea(CampRelatedModel):
class Team(CampRelatedModel): class Team(CampRelatedModel):
class Meta:
ordering = ['name']
unique_together = (('name', 'camp'), ('slug', 'camp'))
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, blank=True) slug = models.SlugField(max_length=255, blank=True)
camp = models.ForeignKey('camps.Camp', related_name="teams")
area = models.ForeignKey( area = models.ForeignKey(
'teams.TeamArea', 'teams.TeamArea',
related_name='teams', related_name='teams',
@ -50,9 +45,28 @@ class Team(CampRelatedModel):
) )
mailing_list = models.EmailField(blank=True) mailing_list = models.EmailField(blank=True)
class Meta:
ordering = ['name']
def __str__(self): def __str__(self):
return '{} ({})'.format(self.name, self.camp) return '{} ({})'.format(self.name, self.camp)
def validate_unique(self, exclude):
"""
We cannot use unique_together with the camp field because it is a property,
so check uniqueness of team name and slug here instead
"""
# check if this team name is in use under this camp
if self.camp.teams.filter(name=self.name).exists():
raise ValidationError("This Team name already exists for this Camp")
if self.camp.teams.filter(slug=self.slug).exists():
raise ValidationError("This Team slug already exists for this Camp")
return True
@property
def camp(self):
return self.area.camp
def save(self, **kwargs): def save(self, **kwargs):
if ( if (
not self.pk or not self.pk or
@ -63,10 +77,6 @@ class Team(CampRelatedModel):
super().save(**kwargs) super().save(**kwargs)
def clean(self):
if self.camp != self.area.camp:
raise ValidationError({'camp': 'camp is different from area.camp'})
def memberstatus(self, member): def memberstatus(self, member):
if member not in self.members.all(): if member not in self.members.all():
return "Not member" return "Not member"

View file

@ -1,7 +1,14 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.db.models.signals import post_save
from .signals import ticket_changed
import logging import logging
logger = logging.getLogger("bornhack.%s" % __name__) logger = logging.getLogger("bornhack.%s" % __name__)
class TicketsConfig(AppConfig): class TicketsConfig(AppConfig):
name = 'tickets' name = 'tickets'
def ready(self):
# connect the post_save signal, including a dispatch_uid to prevent it being called multiple times in corner cases
post_save.connect(ticket_changed, sender='models.ShopTicket', dispatch_uid='shopticket_save_signal')

View file

@ -36,15 +36,18 @@ class TicketType(CampRelatedModel, UUIDModel):
return '{} ({})'.format(self.name, self.camp.title) return '{} ({})'.format(self.name, self.camp.title)
class BaseTicket(CreatedUpdatedModel, UUIDModel): class BaseTicket(CampRelatedModel, UUIDModel):
ticket_type = models.ForeignKey('TicketType') ticket_type = models.ForeignKey('TicketType')
checked_in = models.BooleanField(default=False) checked_in = models.BooleanField(default=False)
badge_handed_out = models.BooleanField(default=False) badge_handed_out = models.BooleanField(default=False)
class Meta: class Meta:
abstract = True abstract = True
@property
def camp(self):
return self.ticket_type.camp
def _get_token(self): def _get_token(self):
return hashlib.sha256( return hashlib.sha256(
'{_id}{secret_key}'.format( '{_id}{secret_key}'.format(
@ -146,58 +149,4 @@ class ShopTicket(BaseTicket):
return "shop" return "shop"
@receiver(post_save, sender=ShopTicket)
def ticket_created(sender, instance, created, **kwargs):
# only send a message when a ticket is created
if not created:
return
# queue an IRC message to the orga channel if defined,
# otherwise for the default channel
target = settings.IRCBOT_CHANNELS['orga'] if 'orga' in settings.IRCBOT_CHANNELS else settings.IRCBOT_CHANNELS['default']
# get ticket stats
ticket_prefix = "BornHack {}".format(datetime.now().year)
stats = ", ".join(
[
"{}: {}".format(
tickettype['product__name'].replace(
"{} ".format(ticket_prefix),
""
),
tickettype['total']
) for tickettype in ShopTicket.objects.filter(
product__name__startswith=ticket_prefix
).exclude(
product__name__startswith="{} One Day".format(ticket_prefix)
).values(
'product__name'
).annotate(
total=Count('product__name')
).order_by('-total')
]
)
onedaystats = ShopTicket.objects.filter(
product__name__startswith="{} One Day Ticket".format(ticket_prefix)
).count()
onedaychildstats = ShopTicket.objects.filter(
product__name__startswith="{} One Day Children".format(ticket_prefix)
).count()
# queue the messages
OutgoingIrcMessage.objects.create(
target=target,
message="%s sold!" % instance.product.name,
timeout=timezone.now()+timedelta(minutes=10)
)
OutgoingIrcMessage.objects.create(
target=target,
message="Totals: {}, 1day: {}, 1day child: {}".format(
stats,
onedaystats,
onedaychildstats
)[:200],
timeout=timezone.now()+timedelta(minutes=10)
)

56
src/tickets/signals.py Normal file
View file

@ -0,0 +1,56 @@
from django.conf import settings
from .models import ShopTicket
from ircbot.models import OutgoingIrcMessage
def ticket_changed(sender, instance, created, **kwargs):
"""
This signal is called every time a ShopTicket is saved
"""
# only queue an IRC message when a new ticket is created
if not created:
return
# queue an IRC message to the orga channel if defined,
# otherwise for the default channel
target = settings.IRCBOT_CHANNELS['orga'] if 'orga' in settings.IRCBOT_CHANNELS else settings.IRCBOT_CHANNELS['default']
# get ticket stats
ticket_prefix = "BornHack {}".format(datetime.now().year)
stats = ", ".join(
[
"{}: {}".format(
tickettype['product__name'].replace(
"{} ".format(ticket_prefix),
""
),
tickettype['total']
) for tickettype in ShopTicket.objects.filter(
product__name__startswith=ticket_prefix
).exclude(
product__name__startswith="{} One Day".format(ticket_prefix)
).values(
'product__name'
).annotate(
total=Count('product__name')
).order_by('-total')
]
)
onedaystats = ShopTicket.objects.filter(
product__name__startswith="{} One Day Ticket".format(ticket_prefix)
).count()
onedaychildstats = ShopTicket.objects.filter(
product__name__startswith="{} One Day Children".format(ticket_prefix)
).count()
# queue the messages
OutgoingIrcMessage.objects.create(
target=target,
message="%s sold!" % instance.product.name,
timeout=timezone.now()+timedelta(minutes=10)
)
OutgoingIrcMessage.objects.create(
target=target,
message="Totals: {}, 1day: {}, 1day child: {}".format(
stats,

View file

@ -1,8 +0,0 @@
from django.contrib import admin
from .models import OutgoingEmail
@admin.register(OutgoingEmail)
class OutgoingEmailAdmin(admin.ModelAdmin):
pass