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:
parent
6fe1790e89
commit
60c4bb49fb
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 = [
|
||||||
|
|
23
src/teams/migrations/0019_auto_20180304_1019.py
Normal file
23
src/teams/migrations/0019_auto_20180304_1019.py
Normal 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',
|
||||||
|
),
|
||||||
|
]
|
|
@ -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"
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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
56
src/tickets/signals.py
Normal 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,
|
|
@ -1,8 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
from .models import OutgoingEmail
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(OutgoingEmail)
|
|
||||||
class OutgoingEmailAdmin(admin.ModelAdmin):
|
|
||||||
pass
|
|
Loading…
Reference in a new issue