From 8bc696a2dcc057f0be7f4026e29539aacdb5b114 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Sat, 11 Jul 2015 11:27:12 +0900 Subject: [PATCH 1/3] sponsorship benefit relation administration Signed-off-by: Hiroshi Miura --- symposion/sponsorship/admin.py | 50 +++++++++++++++++++++++++++++---- symposion/sponsorship/models.py | 39 ++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/symposion/sponsorship/admin.py b/symposion/sponsorship/admin.py index b64c1a13..c38228fb 100644 --- a/symposion/sponsorship/admin.py +++ b/symposion/sponsorship/admin.py @@ -1,7 +1,8 @@ from django.contrib import admin +from django.utils.translation import ugettext_lazy as _ -from symposion.sponsorship.models import SponsorLevel, Sponsor, Benefit, BenefitLevel, \ - SponsorBenefit +from symposion.sponsorship.models import SponsorLevel, Sponsor, Benefit, \ + BenefitLevel, SponsorBenefit, BENEFITS class BenefitLevelInline(admin.TabularInline): @@ -44,29 +45,66 @@ class SponsorAdmin(admin.ModelAdmin): ] inlines = [SponsorBenefitInline] list_display = ["name", "external_url", "level", "active"] + list_filter = ["level", "active"] def get_form(self, *args, **kwargs): # @@@ 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/models.py b/symposion/sponsorship/models.py index e29ac3d1..d1e8551e 100644 --- a/symposion/sponsorship/models.py +++ b/symposion/sponsorship/models.py @@ -13,6 +13,35 @@ 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"), + } +] + + class SponsorLevel(models.Model): conference = models.ForeignKey(Conference, verbose_name=_("conference")) @@ -27,7 +56,7 @@ class SponsorLevel(models.Model): verbose_name_plural = _("sponsor levels") def __unicode__(self): - return self.name + return u"%s %s" % (self.conference, self.name) def sponsors(self): return self.sponsor_set.filter(active=True).order_by("added") @@ -51,6 +80,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 __unicode__(self): @@ -59,6 +95,7 @@ class Sponsor(models.Model): class Meta: verbose_name = _("sponsor") verbose_name_plural = _("sponsors") + ordering = ['name'] def get_absolute_url(self): if self.active: From 0d7df4db004c11117f37270b323f3a29ee79e9f6 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Sat, 11 Jul 2015 11:27:30 +0900 Subject: [PATCH 2/3] sponsor benfit is complete? Signed-off-by: Hiroshi Miura --- symposion/sponsorship/models.py | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/symposion/sponsorship/models.py b/symposion/sponsorship/models.py index d1e8551e..3378a621 100644 --- a/symposion/sponsorship/models.py +++ b/symposion/sponsorship/models.py @@ -97,6 +97,14 @@ class Sponsor(models.Model): 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: return reverse("sponsor_detail", kwargs={"pk": self.pk}) @@ -166,6 +174,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: @@ -229,12 +250,23 @@ 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 __unicode__(self): return u"%s - %s" % (self.sponsor, self.benefit) + 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()) if self.max_words and num_words > self.max_words: @@ -252,3 +284,17 @@ class SponsorBenefit(models.Model): elif self.benefit.type == "text": 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) From d1740081497421340e29b5993aebba38f64b90e8 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Sat, 11 Jul 2015 11:22:15 +0900 Subject: [PATCH 3/3] sponsor benefit type richitext, simple and option Signed-off-by: Hiroshi Miura --- symposion/sponsorship/forms.py | 3 +++ symposion/sponsorship/models.py | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/symposion/sponsorship/forms.py b/symposion/sponsorship/forms.py index 33b52f42..164d62fd 100644 --- a/symposion/sponsorship/forms.py +++ b/symposion/sponsorship/forms.py @@ -47,6 +47,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 3378a621..343800b6 100644 --- a/symposion/sponsorship/models.py +++ b/symposion/sponsorship/models.py @@ -258,7 +258,8 @@ class SponsorBenefit(models.Model): ordering = ["-active"] def __unicode__(self): - return u"%s - %s" % (self.sponsor, self.benefit) + return u"%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 @@ -281,7 +282,7 @@ 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 []