from django.contrib import admin
from django import forms
from django.forms import ValidationError
from django.forms.utils import ErrorList
from django.db.models import Count
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.contrib.postgres.forms.ranges import RangeWidget
from .models import ConferenceSeries, Conference, ConferenceRegistration
from .models import RegistrationType, Speaker
from .models import ConferenceSession, Track, Room, ConferenceSessionScheduleSlot
from .models import RegistrationClass, RegistrationDay, AttendeeMail
from .models import ShirtSize, ConferenceAdditionalOption
from .models import ConferenceFeedbackQuestion
from .models import PrepaidVoucher, PrepaidBatch, BulkPayment, DiscountCode
from .models import PendingAdditionalOrder
from .models import VolunteerSlot
from .models import AccessToken
from .models import ConferenceNews
from .models import ConferenceRemovedData
from postgresqleu.util.forms import ConcurrentProtectedModelForm
from postgresqleu.accounting.models import Object
from postgresqleu.confsponsor.models import Sponsor
#
# List filters
#
class TrackListFilter(admin.SimpleListFilter):
title = 'Track'
parameter_name = 'track'
def lookups(self, request, model_admin):
cid = int(request.GET.get('conference__id__exact', -1))
if cid >= 0:
return ((t.id, t.trackname) for t in Track.objects.filter(conference__id=cid))
def queryset(self, request, queryset):
if self.value():
return queryset.filter(track_id=self.value())
class RegtypeListFilter(admin.SimpleListFilter):
title = 'Registration type'
parameter_name = 'regtype'
def lookups(self, request, model_admin):
cid = int(request.GET.get('conference__id__exact', -1))
if cid >= 0:
return ((r.id, r.regtype) for r in RegistrationType.objects.filter(conference__id=cid))
def queryset(self, request, queryset):
if self.value():
return queryset.filter(regtype_id=self.value())
class AdditionalOptionListFilter(admin.SimpleListFilter):
title = 'Additional option'
parameter_name = 'addoption'
def lookups(self, request, model_admin):
cid = int(request.GET.get('conference__id__exact', -1))
if cid >= 0:
return ((ao.id, ao.name) for ao in ConferenceAdditionalOption.objects.filter(conference__id=cid))
def queryset(self, request, queryset):
if self.value():
return queryset.filter(additionaloptions__id=self.value())
#
# General admin classes
#
class ConferenceSeriesAdmin(admin.ModelAdmin):
autocomplete_fields = ('administrators', )
class ConferenceAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = Conference
exclude = []
accounting_object = forms.ChoiceField(choices=[], required=False)
def __init__(self, *args, **kwargs):
super(ConferenceAdminForm, self).__init__(*args, **kwargs)
self.fields['volunteers'].queryset = ConferenceRegistration.objects.filter(conference=self.instance, payconfirmedat__isnull=False)
self.fields['checkinprocessors'].queryset = ConferenceRegistration.objects.filter(conference=self.instance, payconfirmedat__isnull=False)
self.fields['accounting_object'].choices = [('', '----'), ] + [(o.name, o.name) for o in Object.objects.filter(active=True)]
def clean(self):
data = super(ConferenceAdminForm, self).clean()
return data
class ConferenceAdmin(admin.ModelAdmin):
form = ConferenceAdminForm
list_display = ('conferencename', 'registrationopen', 'registrationtimerange', 'callforpapersopen', 'callforpaperstimerange',
'callforsponsorsopen', 'callforsponsorstimerange', 'feedbackopen', 'startdate', 'enddate')
ordering = ('-startdate', )
autocomplete_fields = ('administrators', 'testers', 'talkvoters', 'staff', 'volunteers', 'checkinprocessors', )
class ConferenceRegistrationForm(ConcurrentProtectedModelForm):
class Meta:
model = ConferenceRegistration
exclude = []
def __init__(self, *args, **kwargs):
super(ConferenceRegistrationForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['additionaloptions'].queryset = ConferenceAdditionalOption.objects.filter(conference=self.instance.conference)
self.fields['regtype'].queryset = RegistrationType.objects.filter(conference=self.instance.conference)
self.fields['payconfirmedat'].help_text = self.fields['payconfirmedby'].help_text = "Don't edit this field here - instead, go back to the list of registrations and chose to approve from there!"
self.fields['checkedinby'].queryset = self.instance.conference.checkinprocessors
class ConferenceRegistrationAdmin(admin.ModelAdmin):
form = ConferenceRegistrationForm
list_display = ['email', 'conference', 'firstname', 'lastname', 'created_short', 'short_regtype', 'payconfirmedat_short', 'has_invoice']
list_filter = ['conference', RegtypeListFilter, AdditionalOptionListFilter, ]
search_fields = ['email', 'firstname', 'lastname', ]
ordering = ['-payconfirmedat', '-created', 'lastname', 'firstname', ]
filter_horizontal = ('additionaloptions',)
exclude = ('invoice', 'bulkpayment', 'messaging_config', )
readonly_fields = ('invoice_link', 'bulkpayment_link', 'lastmodified', )
autocomplete_fields = ('attendee', 'registrator', )
def payconfirmedat_short(self, inst):
return inst.payconfirmedat
payconfirmedat_short.short_description = "Pay conf"
def created_short(self, inst):
return "%s" % inst.created.strftime("%Y-%m-%d %H:%M")
created_short.allow_tags = True
created_short.short_description = "Created"
def invoice_link(self, inst):
if inst.invoice:
url = reverse('admin:invoices_invoice_change', args=(inst.invoice.id,))
return mark_safe('%s' % (url, inst.invoice))
else:
return ""
invoice_link.short_description = 'Invoice'
def bulkpayment_link(self, inst):
if inst.bulkpayment:
url = reverse('admin:confreg_bulkpayment_change', args=(inst.bulkpayment.id,))
return mark_safe('%s' % (url, inst.bulkpayment))
else:
return ""
bulkpayment_link.short_description = 'Bulk payment'
class ConferenceSessionForm(ConcurrentProtectedModelForm):
class Meta:
model = ConferenceSession
exclude = []
def __init__(self, *args, **kwargs):
super(ConferenceSessionForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs and self.instance.conference_id:
self.fields['track'].queryset = Track.objects.filter(conference=self.instance.conference)
self.fields['room'].queryset = Room.objects.filter(conference=self.instance.conference)
self.fields['tentativeroom'].queryset = Room.objects.filter(conference=self.instance.conference)
self.fields['tentativescheduleslot'].queryset = ConferenceSessionScheduleSlot.objects.filter(conference=self.instance.conference)
def clean_track(self):
if not self.cleaned_data['track']:
return None
if self.cleaned_data['track'].conference != self.cleaned_data['conference']:
raise ValidationError("This track does not belong to this conference!")
return self.cleaned_data['track']
def clean_room(self):
if not self.cleaned_data['room']:
return None
if self.cleaned_data['room'].conference != self.cleaned_data['conference']:
raise ValidationError("This room does not belong to this conference!")
return self.cleaned_data['room']
class ConferenceSessionAdmin(admin.ModelAdmin):
form = ConferenceSessionForm
list_display = ['title', 'conference', 'speaker_list', 'status', 'starttime', 'track', 'initialsubmit', ]
list_filter = ['conference', TrackListFilter, 'status', ]
search_fields = ['title', ]
filter_horizontal = ('speaker',)
autocomplete_fields = ('speaker', )
class ConferenceSessionScheduleSlotAdmin(admin.ModelAdmin):
list_display = ['conference', 'starttime', 'endtime', ]
list_filter = ['conference']
ordering = ['starttime', ]
class RegistrationClassAdmin(admin.ModelAdmin):
list_display = ['regclass', 'conference', ]
list_filter = ['conference', ]
ordering = ['conference', 'regclass', ]
class RegistrationDayAdmin(admin.ModelAdmin):
list_display = ['day', 'conference', ]
list_filter = ['conference', ]
ordering = ['conference', 'day', ]
class RegistrationTypeAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = RegistrationType
exclude = []
def __init__(self, *args, **kwargs):
super(RegistrationTypeAdminForm, self).__init__(*args, **kwargs)
try:
self.fields['regclass'].queryset = RegistrationClass.objects.filter(conference=self.instance.conference)
self.fields['days'].queryset = RegistrationDay.objects.filter(conference=self.instance.conference)
self.fields['requires_option'].queryset = ConferenceAdditionalOption.objects.filter(conference=self.instance.conference)
if self.instance.conference.invoice_autocancel_hours:
self.fields['invoice_autocancel_hours'].help_text = "Automatically cancel invoices after this many hours. Conference settings currently override this to minimum value {0}.".format(self.instance.conference.invoice_autocancel_hours)
except Conference.DoesNotExist:
# If we don't have a conference yet, we can just ignore the fact
# that we couldn't list it.
pass
class RegistrationTypeAdmin(admin.ModelAdmin):
list_display = ['conference', 'regtype', 'cost', 'sortkey', 'active', 'activeuntil', ]
list_filter = ['conference', ]
ordering = ['conference', 'regtype', ]
filter_horizontal = ('requires_option', )
form = RegistrationTypeAdminForm
class ShirtsizeAdmin(admin.ModelAdmin):
list_display = ['shirtsize', 'sortkey', ]
class ConferenceAdditionalOptionAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = ConferenceAdditionalOption
exclude = []
def __init__(self, *args, **kwargs):
super(ConferenceAdditionalOptionAdminForm, self).__init__(*args, **kwargs)
try:
self.fields['requires_regtype'].queryset = RegistrationType.objects.filter(conference=self.instance.conference)
self.fields['mutually_exclusive'].queryset = ConferenceAdditionalOption.objects.filter(conference=self.instance.conference)
self.fields['additionaldays'].queryset = RegistrationDay.objects.filter(conference=self.instance.conference)
except Conference.DoesNotExist:
# If we don't have a conference yet, we can just ignore the fact
# that we couldn't list it.
pass
class ConferenceAdditionalOptionAdmin(admin.ModelAdmin):
list_display = ['conference', 'name', 'maxcount', 'cost', 'used_count', 'confirmed_count', 'unconfirmed_count']
list_filter = ['conference', ]
ordering = ['conference', 'name', ]
search_fields = ['name', ]
filter_horizontal = ('requires_regtype', 'mutually_exclusive', )
form = ConferenceAdditionalOptionAdminForm
def get_queryset(self, request):
return ConferenceAdditionalOption.objects.extra(select={
'confirmed_count': 'SELECT count(*) FROM confreg_conferenceregistration r INNER JOIN confreg_conferenceregistration_additionaloptions cao ON cao.conferenceregistration_id=r.id WHERE cao.conferenceadditionaloption_id=confreg_conferenceadditionaloption.id AND r.payconfirmedat IS NOT NULL',
'unconfirmed_count': 'SELECT count(*) FROM confreg_conferenceregistration r INNER JOIN confreg_conferenceregistration_additionaloptions cao ON cao.conferenceregistration_id=r.id WHERE cao.conferenceadditionaloption_id=confreg_conferenceadditionaloption.id AND r.payconfirmedat IS NULL',
})
return ConferenceAdditionalOption.objects.annotate(reg_count=Count('conferenceregistration'))
def confirmed_count(self, inst):
return inst.confirmed_count
confirmed_count.short_description = 'Confirmed'
def unconfirmed_count(self, inst):
return inst.unconfirmed_count
unconfirmed_count.short_description = 'Unconfirmed'
def used_count(self, inst):
return inst.confirmed_count + inst.unconfirmed_count
used_count.short_description = 'Total used'
class SpeakerAdminForm(ConcurrentProtectedModelForm):
exclude_fields_from_validation = ['photo', 'photo512', ]
class Meta:
model = Speaker
exclude = []
class SpeakerAdmin(admin.ModelAdmin):
list_display = ['user', 'email', 'fullname', 'has_abstract', 'has_photo', 'has_photo512']
search_fields = ['fullname', 'user__email']
autocomplete_fields = ('user', )
ordering = ['fullname']
form = SpeakerAdminForm
class TrackAdmin(admin.ModelAdmin):
list_filter = ['conference', ]
list_display = ['conference', 'trackname', 'sortkey', 'color', 'incfp', ]
class Meta:
model = Track
class RoomAdmin(admin.ModelAdmin):
list_filter = ['conference', ]
class Meta:
model = Room
class ConferenceFeedbackQuestionAdmin(admin.ModelAdmin):
list_display = ['conference', 'sortkey', 'newfieldset', 'question', ]
list_filter = ['conference', ]
class PrepaidVoucherInline(admin.TabularInline):
model = PrepaidVoucher
readonly_fields = ['user', 'usedate', ]
exclude = ['vouchervalue', 'conference', ]
extra = 0
can_delete = False
class PrepaidBatchAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = PrepaidBatch
exclude = []
def __init__(self, *args, **kwargs):
super(PrepaidBatchAdminForm, self).__init__(*args, **kwargs)
try:
self.fields['sponsor'].queryset = Sponsor.objects.filter(conference=self.instance.conference)
self.fields['regtype'].queryset = RegistrationType.objects.filter(conference=self.instance.conference)
except Conference.DoesNotExist:
pass
class PrepaidBatchAdmin(admin.ModelAdmin):
list_display = ['id', 'conference', 'buyer', 'buyername', 'total_num', 'used_num', ]
list_filter = ['conference', ]
autocomplete_fields = ('buyer', )
inlines = [PrepaidVoucherInline, ]
form = PrepaidBatchAdminForm
def get_queryset(self, request):
return PrepaidBatch.objects.extra(select={
'num': 'SELECT count(*) FROM confreg_prepaidvoucher WHERE batch_id=confreg_prepaidbatch.id',
'used': 'SELECT count(*) FROM confreg_prepaidvoucher WHERE batch_id=confreg_prepaidbatch.id AND usedate IS NOT NULL',
})
def total_num(self, inst):
return inst.num
total_num.short_description = 'Total vouchers'
def used_num(self, inst):
return inst.used
used_num.short_description = 'Used vouchers'
class PrepaidVoucherAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = PrepaidVoucher
exclude = []
def __init__(self, *args, **kwargs):
super(PrepaidVoucherAdminForm, self).__init__(*args, **kwargs)
try:
self.fields['batch'].queryset = PrepaidBatch.objects.filter(conference=self.instance.conference)
self.fields['user'].queryset = ConferenceRegistration.objects.filter(conference=self.instance.conference)
except Conference.DoesNotExist:
pass
class PrepaidVoucherAdmin(admin.ModelAdmin):
list_display = ['vouchervalue', 'conference', 'buyername', 'usedby', 'usedate', ]
list_filter = ['conference', ]
form = PrepaidVoucherAdminForm
def buyername(self, obj):
url = reverse('admin:confreg_prepaidbatch_change', args=(obj.batch.pk,))
return mark_safe('%s' % (url, obj.batch.buyername))
buyername.allow_tags = True
def usedby(self, obj):
if obj.user:
return "%s %s" % (obj.user.firstname, obj.user.lastname)
return None
class DiscountCodeAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = DiscountCode
exclude = []
def __init__(self, *args, **kwargs):
super(DiscountCodeAdminForm, self).__init__(*args, **kwargs)
try:
self.fields['registrations'].queryset = ConferenceRegistration.objects.filter(conference=self.instance.conference)
self.fields['requiresoption'].queryset = ConferenceAdditionalOption.objects.filter(conference=self.instance.conference)
self.fields['requiresregtype'].queryset = RegistrationType.objects.filter(conference=self.instance.conference)
self.fields['sponsor'].queryset = Sponsor.objects.filter(conference=self.instance.conference)
except Conference.DoesNotExist:
pass
def clean_discountpercentage(self):
if int(self.cleaned_data['discountpercentage']) < 0:
raise ValidationError('Discount percentage must be a positive number or zero!')
if int(self.cleaned_data['discountpercentage']) > 100:
raise ValidationError('Discount percentage cannot be higher than 100!')
return self.cleaned_data['discountpercentage']
def clean_maxuses(self):
if int(self.cleaned_data['maxuses']) < 0:
raise ValidationError('Max uses must be a positive number or zero!')
return self.cleaned_data['maxuses']
def clean(self):
cleaned_data = super(DiscountCodeAdminForm, self).clean()
if 'discountamount' in cleaned_data and 'discountpercentage' in cleaned_data:
if cleaned_data['discountamount'] > 0 and cleaned_data['discountpercentage'] > 0:
raise ValidationError('Cannot specify both discount amount and discount percentage at the same time!')
if 'discountamount' in cleaned_data and 'regonly' in cleaned_data:
if cleaned_data['discountamount'] > 0 and cleaned_data['regonly']:
raise ValidationError('Regonly field can only be set for percentage discounts!')
if cleaned_data.get('sponsor', None) and not cleaned_data.get('sponsor_rep', None):
self._errors['sponsor_rep'] = ErrorList(["Sponsor rep must be given if sponsor is given!"])
if cleaned_data.get('sponsor_rep', None) and not cleaned_data.get('sponsor', None):
self._errors['sponsor'] = ErrorList(["Sponsor must be given if sponsor rep is given!"])
return cleaned_data
class DiscountCodeAdmin(admin.ModelAdmin):
list_display = ['code', 'conference', 'maxuses', 'count', ]
list_filter = ['conference', ]
autocomplete_fields = ('registrations', 'sponsor_rep', )
form = DiscountCodeAdminForm
class BulkPaymentAdmin(admin.ModelAdmin):
list_display = ['adminstring', 'conference', 'user', 'numregs', 'paidat', 'ispaid', ]
list_filter = ['conference', ]
autocomplete_fields = ['user', ]
class AttendeeMailAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = AttendeeMail
exclude = []
def __init__(self, *args, **kwargs):
super(AttendeeMailAdminForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['regclasses'].queryset = RegistrationClass.objects.filter(conference=self.instance.conference)
self.fields['addopts'].queryset = ConferenceAdditionalOption.objects.filter(conference=self.instance.conference)
class AttendeeMailAdmin(admin.ModelAdmin):
form = AttendeeMailAdminForm
filter_horizontal = ('regclasses', )
autocomplete_fields = ('registrations', 'pending_regs', )
class PendingAdditionalOrderAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = PendingAdditionalOrder
exclude = []
def __init__(self, *args, **kwargs):
super(PendingAdditionalOrderAdminForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['reg'].queryset = ConferenceRegistration.objects.filter(conference=self.instance.reg.conference)
self.fields['options'].queryset = ConferenceAdditionalOption.objects.filter(conference=self.instance.reg.conference)
self.fields['newregtype'].queryset = RegistrationType.objects.filter(conference=self.instance.reg.conference)
class PendingAdditionalOrderAdmin(admin.ModelAdmin):
form = PendingAdditionalOrderAdminForm
list_display = ('reg', 'createtime', 'payconfirmedat')
autocomplete_fields = ('invoice', )
class VolunteerSlotAdminForm(ConcurrentProtectedModelForm):
class Meta:
model = VolunteerSlot
exclude = []
widgets = {
'timerange': RangeWidget(admin.widgets.AdminSplitDateTime()),
}
def clean(self):
data = super(VolunteerSlotAdminForm, self).clean()
if data['max_staff'] < data['min_staff']:
raise ValidationError("Max staff can't be less than min staff!")
return data
class VolunteerSlotAdmin(admin.ModelAdmin):
form = VolunteerSlotAdminForm
list_filter = ['conference', ]
list_display = ('__str__', 'title')
class ConferenceRemovedDataAdmin(admin.ModelAdmin):
list_filter = ['urlname', ]
list_display = ('urlname', 'description')
admin.site.register(ConferenceSeries, ConferenceSeriesAdmin)
admin.site.register(Conference, ConferenceAdmin)
admin.site.register(RegistrationClass, RegistrationClassAdmin)
admin.site.register(RegistrationDay, RegistrationDayAdmin)
admin.site.register(RegistrationType, RegistrationTypeAdmin)
admin.site.register(ShirtSize, ShirtsizeAdmin)
admin.site.register(ConferenceRegistration, ConferenceRegistrationAdmin)
admin.site.register(ConferenceSession, ConferenceSessionAdmin)
admin.site.register(ConferenceSessionScheduleSlot, ConferenceSessionScheduleSlotAdmin)
admin.site.register(Track, TrackAdmin)
admin.site.register(Room, RoomAdmin)
admin.site.register(Speaker, SpeakerAdmin)
admin.site.register(ConferenceAdditionalOption, ConferenceAdditionalOptionAdmin)
admin.site.register(ConferenceFeedbackQuestion, ConferenceFeedbackQuestionAdmin)
admin.site.register(PrepaidBatch, PrepaidBatchAdmin)
admin.site.register(PrepaidVoucher, PrepaidVoucherAdmin)
admin.site.register(DiscountCode, DiscountCodeAdmin)
admin.site.register(BulkPayment, BulkPaymentAdmin)
admin.site.register(AttendeeMail, AttendeeMailAdmin)
admin.site.register(PendingAdditionalOrder, PendingAdditionalOrderAdmin)
admin.site.register(VolunteerSlot, VolunteerSlotAdmin)
admin.site.register(AccessToken)
admin.site.register(ConferenceNews)
admin.site.register(ConferenceRemovedData, ConferenceRemovedDataAdmin)