first pass at merging pycon's sponsorship app with more basic symposion one
This commit is contained in:
parent
32c8c0ce50
commit
3ffcc4da7c
6 changed files with 305 additions and 8 deletions
|
@ -1,7 +1,69 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from symposion.sponsorship.models import SponsorLevel, Sponsor
|
from symposion.sponsorship.models import SponsorLevel, Sponsor, Benefit, BenefitLevel, SponsorBenefit
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(SponsorLevel)
|
class BenefitLevelInline(admin.TabularInline):
|
||||||
admin.site.register(Sponsor, list_display=("name", "level", "added", "active"), list_filter = ("level", ))
|
model = BenefitLevel
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorBenefitInline(admin.StackedInline):
|
||||||
|
model = SponsorBenefit
|
||||||
|
extra = 0
|
||||||
|
fieldsets = [
|
||||||
|
(None, {
|
||||||
|
"fields": [
|
||||||
|
("benefit", "active"),
|
||||||
|
("max_words", "other_limits"),
|
||||||
|
"text",
|
||||||
|
"upload",
|
||||||
|
]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
|
save_on_top = True
|
||||||
|
fieldsets = [
|
||||||
|
(None, {
|
||||||
|
"fields": [
|
||||||
|
("name", "applicant"),
|
||||||
|
("level", "active"),
|
||||||
|
"external_url",
|
||||||
|
"annotation",
|
||||||
|
("contact_name", "contact_email")
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
("Metadata", {
|
||||||
|
"fields": ["added"],
|
||||||
|
"classes": ["collapse"]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
inlines = [SponsorBenefitInline]
|
||||||
|
|
||||||
|
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")
|
||||||
|
]
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
|
class BenefitAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
|
inlines = [BenefitLevelInline]
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorLevelAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
|
inlines = [BenefitLevelInline]
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(SponsorLevel, SponsorLevelAdmin)
|
||||||
|
admin.site.register(Sponsor, SponsorAdmin)
|
||||||
|
admin.site.register(Benefit, BenefitAdmin)
|
||||||
|
|
72
symposion/sponsorship/forms.py
Normal file
72
symposion/sponsorship/forms.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
from django import forms
|
||||||
|
from django.forms.models import inlineformset_factory, BaseInlineFormSet
|
||||||
|
|
||||||
|
from django.contrib.admin.widgets import AdminFileWidget
|
||||||
|
|
||||||
|
from symposion.sponsorship.models import Sponsor, SponsorBenefit
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorApplicationForm(forms.ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.user = kwargs.pop("user")
|
||||||
|
kwargs.update({
|
||||||
|
"initial": {
|
||||||
|
"contact_name": self.user.get_full_name,
|
||||||
|
"contact_email": self.user.email,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
super(SponsorApplicationForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Sponsor
|
||||||
|
fields = ["name", "contact_name", "contact_email", "level"]
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
obj = super(SponsorApplicationForm, self).save(commit=False)
|
||||||
|
obj.applicant = self.user
|
||||||
|
if commit:
|
||||||
|
obj.save()
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorDetailsForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Sponsor
|
||||||
|
fields = [
|
||||||
|
"name",
|
||||||
|
"external_url",
|
||||||
|
"contact_name",
|
||||||
|
"contact_email"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorBenefitsInlineFormSet(BaseInlineFormSet):
|
||||||
|
|
||||||
|
def _construct_form(self, i, **kwargs):
|
||||||
|
form = super(SponsorBenefitsInlineFormSet, self)._construct_form(i, **kwargs)
|
||||||
|
|
||||||
|
# only include the relevant data fields for this benefit type
|
||||||
|
fields = form.instance.data_fields()
|
||||||
|
form.fields = dict((k, v) for (k, v) in form.fields.items() if k in fields + ["id"])
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
# don't need a label, the form template will label it with the benefit name
|
||||||
|
form.fields[field].label = ""
|
||||||
|
|
||||||
|
# provide word limit as help_text
|
||||||
|
if form.instance.benefit.type == "text" and form.instance.max_words:
|
||||||
|
form.fields[field].help_text = u"maximum %s words" % form.instance.max_words
|
||||||
|
|
||||||
|
# use admin file widget that shows currently uploaded file
|
||||||
|
if field == "upload":
|
||||||
|
form.fields[field].widget = AdminFileWidget()
|
||||||
|
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
|
SponsorBenefitsFormSet = inlineformset_factory(
|
||||||
|
Sponsor, SponsorBenefit,
|
||||||
|
formset=SponsorBenefitsInlineFormSet,
|
||||||
|
can_delete=False, extra=0,
|
||||||
|
fields=["text", "upload"]
|
||||||
|
)
|
7
symposion/sponsorship/managers.py
Normal file
7
symposion/sponsorship/managers.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorManager(models.Manager):
|
||||||
|
|
||||||
|
def active(self):
|
||||||
|
return self.get_query_set().filter(active=True).order_by("level")
|
|
@ -1,17 +1,24 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from symposion.conference.models import Conference
|
from symposion.conference.models import Conference
|
||||||
|
|
||||||
|
from symposion.sponsorship.managers import SponsorManager
|
||||||
|
|
||||||
|
|
||||||
class SponsorLevel(models.Model):
|
class SponsorLevel(models.Model):
|
||||||
|
|
||||||
conference = models.ForeignKey(Conference, verbose_name=_("conference"))
|
conference = models.ForeignKey(Conference, verbose_name=_("conference"))
|
||||||
name = models.CharField(_("name"), max_length=100)
|
name = models.CharField(_("name"), max_length=100)
|
||||||
order = models.IntegerField(_("order"), default=0)
|
order = models.IntegerField(_("order"), default=0)
|
||||||
description = models.TextField(_("description"), blank=True)
|
cost = models.PositiveIntegerField(_("cost"))
|
||||||
|
description = models.TextField(_("description"), blank=True, help_text=_("This is private."))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["conference", "order"]
|
ordering = ["conference", "order"]
|
||||||
|
@ -27,19 +34,101 @@ class SponsorLevel(models.Model):
|
||||||
|
|
||||||
class Sponsor(models.Model):
|
class Sponsor(models.Model):
|
||||||
|
|
||||||
name = models.CharField(_("name"), max_length=100)
|
applicant = models.ForeignKey(User, related_name="sponsorships", verbose_name=_("applicant"), null=True)
|
||||||
|
|
||||||
|
name = models.CharField(_("Sponsor Name"), max_length=100)
|
||||||
external_url = models.URLField(_("external URL"))
|
external_url = models.URLField(_("external URL"))
|
||||||
annotation = models.TextField(_("annotation"), blank=True)
|
annotation = models.TextField(_("annotation"), blank=True)
|
||||||
contact_name = models.CharField(_("contact_name"), max_length=100)
|
contact_name = models.CharField(_("Contact Name"), max_length=100)
|
||||||
contact_email = models.EmailField(_(u"Contact e\u2011mail"))
|
contact_email = models.EmailField(_(u"Contact Email"))
|
||||||
logo = models.ImageField(_("logo"), upload_to="sponsor_logos/")
|
|
||||||
level = models.ForeignKey(SponsorLevel, verbose_name=_("level"))
|
level = models.ForeignKey(SponsorLevel, verbose_name=_("level"))
|
||||||
added = models.DateTimeField(_("added"), default=datetime.datetime.now)
|
added = models.DateTimeField(_("added"), default=datetime.datetime.now)
|
||||||
active = models.BooleanField(_("active"), default=False)
|
active = models.BooleanField(_("active"), default=False)
|
||||||
|
|
||||||
|
# Denormalization (this assumes only one logo)
|
||||||
|
sponsor_logo = models.ForeignKey("SponsorBenefit", related_name="+", null=True, blank=True, editable=False)
|
||||||
|
|
||||||
|
objects = SponsorManager()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("sponsor")
|
verbose_name = _("sponsor")
|
||||||
verbose_name_plural = _("sponsors")
|
verbose_name_plural = _("sponsors")
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
if self.active:
|
||||||
|
return reverse("sponsor_detail", kwargs={"pk": self.pk})
|
||||||
|
return reverse("sponsor_list")
|
||||||
|
|
||||||
|
|
||||||
|
BENEFIT_TYPE_CHOICES = [
|
||||||
|
("text", "Text"),
|
||||||
|
("file", "File"),
|
||||||
|
("weblogo", "Web Logo"),
|
||||||
|
("simple", "Simple")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Benefit(models.Model):
|
||||||
|
|
||||||
|
name = models.CharField(_("name"), max_length=100)
|
||||||
|
description = models.TextField(_("description"), blank=True)
|
||||||
|
type = models.CharField(_("type"), choices=BENEFIT_TYPE_CHOICES, max_length=10, default="simple")
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class BenefitLevel(models.Model):
|
||||||
|
|
||||||
|
benefit = models.ForeignKey(Benefit, related_name="benefit_levels", verbose_name=_("benefit"))
|
||||||
|
level = models.ForeignKey(SponsorLevel, related_name="benefit_levels", verbose_name=_("level"))
|
||||||
|
|
||||||
|
# default limits for this benefit at given level
|
||||||
|
max_words = models.PositiveIntegerField(_("max words"), blank=True, null=True)
|
||||||
|
other_limits = models.CharField(_("other limits"), max_length=200, blank=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["level"]
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s - %s" % (self.level, self.benefit)
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorBenefit(models.Model):
|
||||||
|
|
||||||
|
sponsor = models.ForeignKey(Sponsor, related_name="sponsor_benefits", verbose_name=_("sponsor"))
|
||||||
|
benefit = models.ForeignKey(Benefit, related_name="sponsor_benefits", verbose_name=_("benefit"))
|
||||||
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
# Limits: will initially be set to defaults from corresponding BenefitLevel
|
||||||
|
max_words = models.PositiveIntegerField(_("max words"), blank=True, null=True)
|
||||||
|
other_limits = models.CharField(_("other limits"), max_length=200, blank=True)
|
||||||
|
|
||||||
|
# Data: zero or one of these fields will be used, depending on the
|
||||||
|
# type of the Benefit (text, file, or simple)
|
||||||
|
text = models.TextField(_("text"), blank=True)
|
||||||
|
upload = models.FileField(_("file"), blank=True, upload_to="sponsor_files")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-active']
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s - %s" % (self.sponsor, self.benefit)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if self.max_words and len(self.text.split()) > self.max_words:
|
||||||
|
raise ValidationError("Sponsorship level only allows for %s words." % self.max_words)
|
||||||
|
|
||||||
|
def data_fields(self):
|
||||||
|
"""
|
||||||
|
Return list of data field names which should be editable for
|
||||||
|
this ``SponsorBenefit``, depending on its ``Benefit`` type.
|
||||||
|
"""
|
||||||
|
if self.benefit.type == "file" or self.benefit.type == "weblogo":
|
||||||
|
return ["upload"]
|
||||||
|
elif self.benefit.type == "text":
|
||||||
|
return ["text"]
|
||||||
|
return []
|
||||||
|
|
9
symposion/sponsorship/urls.py
Normal file
9
symposion/sponsorship/urls.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from django.conf.urls.defaults import patterns, url
|
||||||
|
from django.views.generic.simple import direct_to_template
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = patterns("symposion.sponsorship.views",
|
||||||
|
url(r"^$", direct_to_template, {"template": "sponsorship/list.html"}, name="sponsor_list"),
|
||||||
|
url(r"^apply/$", "sponsor_apply", name="sponsor_apply"),
|
||||||
|
url(r"^(?P<pk>\d+)/$", "sponsor_detail", name="sponsor_detail"),
|
||||||
|
)
|
58
symposion/sponsorship/views.py
Normal file
58
symposion/sponsorship/views.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
from django.shortcuts import render_to_response, redirect, get_object_or_404
|
||||||
|
from django.template import RequestContext
|
||||||
|
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
from pycon.sponsorship.forms import SponsorApplicationForm, SponsorDetailsForm, SponsorBenefitsFormSet
|
||||||
|
from pycon.sponsorship.models import Sponsor, SponsorBenefit
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def sponsor_apply(request):
|
||||||
|
if request.method == "POST":
|
||||||
|
form = SponsorApplicationForm(request.POST, user=request.user)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
return redirect("dashboard")
|
||||||
|
else:
|
||||||
|
form = SponsorApplicationForm(user=request.user)
|
||||||
|
|
||||||
|
return render_to_response("sponsorship/apply.html", {
|
||||||
|
"form": form,
|
||||||
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def sponsor_detail(request, pk):
|
||||||
|
sponsor = get_object_or_404(Sponsor, pk=pk)
|
||||||
|
|
||||||
|
if not sponsor.active or sponsor.applicant != request.user:
|
||||||
|
return redirect("sponsor_list")
|
||||||
|
|
||||||
|
formset_kwargs = {
|
||||||
|
"instance": sponsor,
|
||||||
|
"queryset": SponsorBenefit.objects.filter(active=True)
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
|
||||||
|
form = SponsorDetailsForm(request.POST, instance=sponsor)
|
||||||
|
formset = SponsorBenefitsFormSet(request.POST, request.FILES, **formset_kwargs)
|
||||||
|
|
||||||
|
if form.is_valid() and formset.is_valid():
|
||||||
|
form.save()
|
||||||
|
formset.save()
|
||||||
|
|
||||||
|
messages.success(request, "Your sponsorship application has been submitted!")
|
||||||
|
|
||||||
|
return redirect(request.path)
|
||||||
|
else:
|
||||||
|
form = SponsorDetailsForm(instance=sponsor)
|
||||||
|
formset = SponsorBenefitsFormSet(**formset_kwargs)
|
||||||
|
|
||||||
|
return render_to_response("sponsorship/detail.html", {
|
||||||
|
"sponsor": sponsor,
|
||||||
|
"form": form,
|
||||||
|
"formset": formset,
|
||||||
|
}, context_instance=RequestContext(request))
|
Loading…
Reference in a new issue