diff --git a/symposion/sponsorship/admin.py b/symposion/sponsorship/admin.py index c78fc8b3ea5e661051e40ec72bbbd0574d9b10e1..61080171df8a31454aa024c56ab018bf4131a2d3 100644 --- a/symposion/sponsorship/admin.py +++ b/symposion/sponsorship/admin.py @@ -1,12 +1,18 @@ from __future__ import unicode_literals + from django.contrib import admin from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ -from symposion.sponsorship.models import SponsorLevel, Sponsor, Benefit, BenefitLevel, \ - SponsorBenefit - +from symposion.sponsorship.models import ( + Benefit, + BENEFITS, + BenefitLevel, + Sponsor, + SponsorBenefit, + SponsorLevel, +) class BenefitLevelInline(admin.TabularInline): model = BenefitLevel @@ -47,6 +53,7 @@ class SponsorAdmin(admin.ModelAdmin): }) ] inlines = [SponsorBenefitInline] + list_filter = ["level", "active"] list_display = ["name", "external_url", "level", "active", "contact", "applicant_field"] def contact(self, sponsor): @@ -62,24 +69,60 @@ class SponsorAdmin(admin.ModelAdmin): # @@@ kinda ugly but using choices= on NullBooleanField is broken form = super(SponsorAdmin, self).get_form(*args, **kwargs) form.base_fields["active"].widget.choices = [ - (u"1", "unreviewed"), - (u"2", "approved"), - (u"3", "rejected") + (u"1", _("unreviewed")), + (u"2", _("approved")), + (u"3", _("rejected")) ] return form + # Define accessor functions for our benefit fields and add them to + # list_display, so we can sort on them and give them sensible names. + # Add the fields to list_filters while we're at it. + for benefit in BENEFITS: + benefit_name = benefit['name'] + field_name = benefit['field_name'] + + def func_generator(ben): + def column_func(obj): + return getattr(obj, ben['field_name']) + column_func.short_description = ben['column_title'] + column_func.boolean = True + column_func.admin_order_field = ben['field_name'] + return column_func + list_display.append(func_generator(benefit)) + list_filter.append(field_name) + + def save_related(self, request, form, formsets, change): + super(SponsorAdmin, self).save_related(request, form, formsets, change) + obj = form.instance + obj.save() + class BenefitAdmin(admin.ModelAdmin): - list_display = ["name", "type", "description"] + list_display = ["name", "type", "description", "levels"] inlines = [BenefitLevelInline] + def levels(self, benefit): + return u", ".join(l.level.name for l in benefit.benefit_levels.all()) + class SponsorLevelAdmin(admin.ModelAdmin): inlines = [BenefitLevelInline] +class SponsorBenefitAdmin(admin.ModelAdmin): + list_display = ('benefit', 'sponsor', 'active', '_is_complete', 'show_text') + + def show_text(self, sponsor_benefit): + if sponsor_benefit.text: + return sponsor_benefit.text[:100] + else: + return _("None") + + admin.site.register(SponsorLevel, SponsorLevelAdmin) admin.site.register(Sponsor, SponsorAdmin) admin.site.register(Benefit, BenefitAdmin) +admin.site.register(SponsorBenefit, SponsorBenefitAdmin) diff --git a/symposion/sponsorship/forms.py b/symposion/sponsorship/forms.py index 4fd379e62b8111cf2377286ac36aea9f3b1e671c..edde8bd178743f02d7d2f540b25cc8acebf04e04 100644 --- a/symposion/sponsorship/forms.py +++ b/symposion/sponsorship/forms.py @@ -48,6 +48,9 @@ class SponsorDetailsForm(forms.ModelForm): class SponsorBenefitsInlineFormSet(BaseInlineFormSet): + def __init__(self, *args, **kwargs): + kwargs['queryset'] = kwargs.get('queryset', self.model._default_manager).exclude(benefit__type="option") + super(SponsorBenefitsInlineFormSet, self).__init__(*args, **kwargs) def _construct_form(self, i, **kwargs): form = super(SponsorBenefitsInlineFormSet, self)._construct_form(i, **kwargs) diff --git a/symposion/sponsorship/models.py b/symposion/sponsorship/models.py index eb9aa7c099a8e778fd54116fbe810eb5affd4a84..02bacd69513d8361d048e20e49c63bfd8e0c94ab 100644 --- a/symposion/sponsorship/models.py +++ b/symposion/sponsorship/models.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals + import datetime from django.conf import settings @@ -12,10 +13,38 @@ from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User from symposion.conference.models import Conference - from symposion.sponsorship.managers import SponsorManager +# The benefits we track as individual fields on sponsors +# Names are the names in the database as defined by organizers. +# Field names are the benefit names, lowercased, with +# spaces changed to _, and with "_benefit" appended. +# Column titles are arbitrary. + +# "really just care about the ones we have today: print logo, web logo, print description, web description and the ad." + +BENEFITS = [ + { + 'name': 'Web logo', + 'field_name': 'web_logo_benefit', + 'column_title': _(u"Web Logo"), + }, { + 'name': 'Print logo', + 'field_name': 'print_logo_benefit', + 'column_title': _(u"Print Logo"), + }, { + 'name': 'Company Description', + 'field_name': 'company_description_benefit', + 'column_title': _(u"Web Desc"), + }, { + 'name': 'Print Description', + 'field_name': 'print_description_benefit', + 'column_title': _(u"Print Desc"), + } +] + + @python_2_unicode_compatible class SponsorLevel(models.Model): @@ -31,7 +60,7 @@ class SponsorLevel(models.Model): verbose_name_plural = _("sponsor levels") def __str__(self): - return self.name + return "%s %s" % (self.conference, self.name) def sponsors(self): return self.sponsor_set.filter(active=True).order_by("added") @@ -57,6 +86,13 @@ class Sponsor(models.Model): sponsor_logo = models.ForeignKey("SponsorBenefit", related_name="+", null=True, blank=True, editable=False) + # Whether things are complete + # True = complete, False = incomplate, Null = n/a for this sponsor level + web_logo_benefit = models.NullBooleanField(_("Web logo benefit"), help_text=_(u"Web logo benefit is complete")) + print_logo_benefit = models.NullBooleanField(_("Print logo benefit"), help_text=_(u"Print logo benefit is complete")) + print_description_benefit = models.NullBooleanField(_("Print description benefit"), help_text=_(u"Print description benefit is complete")) + company_description_benefit = models.NullBooleanField(_("Company description benefit"), help_text=_(u"Company description benefit is complete")) + objects = SponsorManager() def __str__(self): @@ -65,6 +101,15 @@ class Sponsor(models.Model): class Meta: verbose_name = _("sponsor") verbose_name_plural = _("sponsors") + ordering = ['name'] + + def save(self, *args, **kwargs): + # Set fields related to benefits being complete + for benefit in BENEFITS: + field_name = benefit['field_name'] + benefit_name = benefit['name'] + setattr(self, field_name, self.benefit_is_complete(benefit_name)) + super(Sponsor, self).save(*args, **kwargs) def get_absolute_url(self): if self.active: @@ -141,6 +186,19 @@ class Sponsor(models.Model): def send_coordinator_emails(self): pass # @@@ should this just be done centrally? + def benefit_is_complete(self, name): + """Return True - benefit is complete, False - benefit is not complete, + or None - benefit not applicable for this sponsor's level """ + if BenefitLevel.objects.filter(level=self.level, benefit__name=name).exists(): + try: + benefit = self.sponsor_benefits.get(benefit__name=name) + except SponsorBenefit.DoesNotExist: + return False + else: + return benefit.is_complete + else: + return None # Not an applicable benefit for this sponsor's level + def _store_initial_level(sender, instance, **kwargs): if instance: @@ -217,11 +275,22 @@ class SponsorBenefit(models.Model): text = models.TextField(_("text"), blank=True) upload = models.FileField(_("file"), blank=True, upload_to="sponsor_files") + # Whether any assets required from the sponsor have been provided + # (e.g. a logo file for a Web logo benefit). + is_complete = models.NullBooleanField(_("Complete?"), help_text=_(u"True - benefit complete; False - benefit incomplete; Null - n/a")) + class Meta: ordering = ["-active"] def __str__(self): - return "%s - %s" % (self.sponsor, self.benefit) + return "%s - %s (%s)" % (self.sponsor, self.benefit, self.benefit_type) + + def save(self, *args, **kwargs): + # Validate - save() doesn't clean your model by default, so call + # it explicitly before saving + self.full_clean() + self.is_complete = self._is_complete() + super(SponsorBenefit, self).save(*args, **kwargs) def clean(self): num_words = len(self.text.split()) @@ -237,6 +306,20 @@ class SponsorBenefit(models.Model): """ if self.benefit.type == "file" or self.benefit.type == "weblogo": return ["upload"] - elif self.benefit.type == "text": + elif self.benefit.type in ("text", "richtext", "simple", "option"): return ["text"] return [] + + def _is_complete(self): + return self.active and \ + ((self.benefit.type in ('text', 'richtext', 'simple') and bool(self.text)) + or (self.benefit.type in ('file', 'weblogo') and bool(self.upload))) + + +def _denorm_weblogo(sender, instance, created, **kwargs): + if instance: + if instance.benefit.type == "weblogo" and instance.upload: + sponsor = instance.sponsor + sponsor.sponsor_logo = instance + sponsor.save() +post_save.connect(_denorm_weblogo, sender=SponsorBenefit)