remove pycon folder, as we've transplanted sponsors
This commit is contained in:
parent
64fdf5147c
commit
53dfff3315
18 changed files with 0 additions and 939 deletions
|
@ -1,8 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
from pycon.models import PyConProposalCategory, PyConTalkProposal, PyConTutorialProposal, PyConPosterProposal
|
|
||||||
|
|
||||||
admin.site.register(PyConProposalCategory)
|
|
||||||
admin.site.register(PyConTalkProposal)
|
|
||||||
admin.site.register(PyConTutorialProposal)
|
|
||||||
admin.site.register(PyConPosterProposal)
|
|
|
@ -1,84 +0,0 @@
|
||||||
from django import forms
|
|
||||||
|
|
||||||
from markitup.widgets import MarkItUpWidget
|
|
||||||
|
|
||||||
from pycon.models import PyConProposalCategory, PyConTalkProposal, PyConTutorialProposal, PyConPosterProposal
|
|
||||||
|
|
||||||
|
|
||||||
class PyConProposalForm(forms.ModelForm):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(PyConProposalForm, self).__init__(*args, **kwargs)
|
|
||||||
self.fields["category"] = forms.ModelChoiceField(
|
|
||||||
queryset = PyConProposalCategory.objects.order_by("name")
|
|
||||||
)
|
|
||||||
|
|
||||||
def clean_description(self):
|
|
||||||
value = self.cleaned_data["description"]
|
|
||||||
if len(value) > 400:
|
|
||||||
raise forms.ValidationError(
|
|
||||||
u"The description must be less than 400 characters"
|
|
||||||
)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class PyConTalkProposalForm(PyConProposalForm):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = PyConTalkProposal
|
|
||||||
fields = [
|
|
||||||
"title",
|
|
||||||
"category",
|
|
||||||
"audience_level",
|
|
||||||
"extreme",
|
|
||||||
"duration",
|
|
||||||
"description",
|
|
||||||
"abstract",
|
|
||||||
"additional_notes",
|
|
||||||
"recording_release",
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
"abstract": MarkItUpWidget(),
|
|
||||||
"additional_notes": MarkItUpWidget(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PyConTutorialProposalForm(PyConProposalForm):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = PyConTutorialProposal
|
|
||||||
fields = [
|
|
||||||
"title",
|
|
||||||
"category",
|
|
||||||
"audience_level",
|
|
||||||
"description",
|
|
||||||
"abstract",
|
|
||||||
"additional_notes",
|
|
||||||
"recording_release",
|
|
||||||
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
"abstract": MarkItUpWidget(),
|
|
||||||
"additional_notes": MarkItUpWidget(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PyConPosterProposalForm(PyConProposalForm):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = PyConPosterProposal
|
|
||||||
fields = [
|
|
||||||
"title",
|
|
||||||
"category",
|
|
||||||
"audience_level",
|
|
||||||
"description",
|
|
||||||
"abstract",
|
|
||||||
"additional_notes",
|
|
||||||
"recording_release",
|
|
||||||
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
"abstract": MarkItUpWidget(),
|
|
||||||
"additional_notes": MarkItUpWidget(),
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
from symposion.proposals.models import ProposalBase
|
|
||||||
|
|
||||||
|
|
||||||
class PyConProposalCategory(models.Model):
|
|
||||||
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
slug = models.SlugField()
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "PyCon proposal category"
|
|
||||||
verbose_name_plural = "PyCon proposal categories"
|
|
||||||
|
|
||||||
|
|
||||||
class PyConProposal(ProposalBase):
|
|
||||||
|
|
||||||
AUDIENCE_LEVEL_NOVICE = 1
|
|
||||||
AUDIENCE_LEVEL_EXPERIENCED = 2
|
|
||||||
AUDIENCE_LEVEL_INTERMEDIATE = 3
|
|
||||||
|
|
||||||
AUDIENCE_LEVELS = [
|
|
||||||
(AUDIENCE_LEVEL_NOVICE, "Novice"),
|
|
||||||
(AUDIENCE_LEVEL_INTERMEDIATE, "Intermediate"),
|
|
||||||
(AUDIENCE_LEVEL_EXPERIENCED, "Experienced"),
|
|
||||||
]
|
|
||||||
|
|
||||||
category = models.ForeignKey(PyConProposalCategory)
|
|
||||||
audience_level = models.IntegerField(choices=AUDIENCE_LEVELS)
|
|
||||||
|
|
||||||
recording_release = models.BooleanField(
|
|
||||||
default=True,
|
|
||||||
help_text="By submitting your talk proposal, you agree to give permission to the Python Software Foundation to record, edit, and release audio and/or video of your presentation. If you do not agree to this, please uncheck this box. See <a href='https://us.pycon.org/2013/speaking/recording/' target='_blank'>PyCon 2013 Recording Release</a> for details."
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class PyConTalkProposal(PyConProposal):
|
|
||||||
|
|
||||||
DURATION_CHOICES = [
|
|
||||||
(0, "No preference"),
|
|
||||||
(1, "I prefer a 30 minute slot"),
|
|
||||||
(2, "I prefer a 45 minute slot"),
|
|
||||||
]
|
|
||||||
|
|
||||||
extreme = models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="'Extreme' talks are advanced talks with little or no introductory material. See <a href='http://us.pycon.org/2013/speaker/extreme/' target='_blank'>Extreme Talks</a> for details."
|
|
||||||
)
|
|
||||||
duration = models.IntegerField(choices=DURATION_CHOICES)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "PyCon talk proposal"
|
|
||||||
|
|
||||||
|
|
||||||
class PyConTutorialProposal(PyConProposal):
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "PyCon tutorial proposal"
|
|
||||||
|
|
||||||
|
|
||||||
class PyConPosterProposal(PyConProposal):
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "PyCon poster proposal"
|
|
|
@ -1,5 +0,0 @@
|
||||||
SPONSOR_COORDINATORS = "sponsor-coordinators"
|
|
||||||
|
|
||||||
AUTH_GROUPS = [
|
|
||||||
SPONSOR_COORDINATORS
|
|
||||||
]
|
|
|
@ -1,69 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
from pycon.sponsorship.models import SponsorLevel, Sponsor, Benefit, BenefitLevel, SponsorBenefit
|
|
||||||
|
|
||||||
|
|
||||||
class BenefitLevelInline(admin.TabularInline):
|
|
||||||
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)
|
|
|
@ -1,72 +0,0 @@
|
||||||
from django import forms
|
|
||||||
from django.forms.models import inlineformset_factory, BaseInlineFormSet
|
|
||||||
|
|
||||||
from django.contrib.admin.widgets import AdminFileWidget
|
|
||||||
|
|
||||||
from pycon.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"]
|
|
||||||
)
|
|
|
@ -1,12 +0,0 @@
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
|
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
|
|
||||||
from pycon.sponsorship import AUTH_GROUPS
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
for group in AUTH_GROUPS:
|
|
||||||
Group.objects.get_or_create(name=group)
|
|
|
@ -1,80 +0,0 @@
|
||||||
import csv
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import zipfile
|
|
||||||
|
|
||||||
from contextlib import closing
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
from django.template.defaultfilters import slugify
|
|
||||||
|
|
||||||
from pycon.sponsorship.models import Sponsor
|
|
||||||
|
|
||||||
|
|
||||||
def zipdir(basedir, archivename):
|
|
||||||
assert os.path.isdir(basedir)
|
|
||||||
with closing(zipfile.ZipFile(archivename, "w", zipfile.ZIP_DEFLATED)) as z:
|
|
||||||
for root, dirs, files in os.walk(basedir):
|
|
||||||
#NOTE: ignore empty directories
|
|
||||||
for fn in files:
|
|
||||||
absfn = os.path.join(root, fn)
|
|
||||||
zfn = absfn[len(basedir)+len(os.sep):] #XXX: relative path
|
|
||||||
z.write(absfn, zfn)
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
try:
|
|
||||||
os.makedirs(os.path.join(os.getcwd(), "build"))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
csv_file = csv.writer(
|
|
||||||
open(os.path.join(os.getcwd(), "build", "sponsors.csv"), "wb")
|
|
||||||
)
|
|
||||||
csv_file.writerow(["Name", "URL", "Level", "Description"])
|
|
||||||
|
|
||||||
for sponsor in Sponsor.objects.all():
|
|
||||||
path = os.path.join(os.getcwd(), "build", slugify(sponsor.name))
|
|
||||||
try:
|
|
||||||
os.makedirs(path)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"name": sponsor.name,
|
|
||||||
"url": sponsor.external_url,
|
|
||||||
"level": sponsor.level.name,
|
|
||||||
"description": "",
|
|
||||||
}
|
|
||||||
for sponsor_benefit in sponsor.sponsor_benefits.all():
|
|
||||||
if sponsor_benefit.benefit_id == 2:
|
|
||||||
data["description"] = sponsor_benefit.text
|
|
||||||
if sponsor_benefit.benefit_id == 1:
|
|
||||||
if sponsor_benefit.upload:
|
|
||||||
data["ad"] = sponsor_benefit.upload.path
|
|
||||||
if sponsor_benefit.benefit_id == 7:
|
|
||||||
if sponsor_benefit.upload:
|
|
||||||
data["logo"] = sponsor_benefit.upload.path
|
|
||||||
|
|
||||||
if "ad" in data:
|
|
||||||
ad_path = data.pop("ad")
|
|
||||||
shutil.copy(ad_path, path)
|
|
||||||
if "logo" in data:
|
|
||||||
logo_path = data.pop("logo")
|
|
||||||
shutil.copy(logo_path, path)
|
|
||||||
|
|
||||||
csv_file.writerow([
|
|
||||||
data["name"].encode("utf-8"),
|
|
||||||
data["url"].encode("utf-8"),
|
|
||||||
data["level"].encode("utf-8"),
|
|
||||||
data["description"].encode("utf-8")
|
|
||||||
])
|
|
||||||
|
|
||||||
zipdir(
|
|
||||||
os.path.join(
|
|
||||||
os.getcwd(), "build"),
|
|
||||||
os.path.join(os.getcwd(), "sponsors.zip"
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,38 +0,0 @@
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
|
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
|
|
||||||
from pycon.sponsorship.models import Sponsor, SponsorBenefit
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
for sponsor in Sponsor.objects.all():
|
|
||||||
level = None
|
|
||||||
try:
|
|
||||||
level = sponsor.level
|
|
||||||
except SponsorLevel.DoesNotExist:
|
|
||||||
pass
|
|
||||||
if level:
|
|
||||||
for benefit_level in level.benefit_levels.all():
|
|
||||||
# Create all needed benefits if they don't exist already
|
|
||||||
sponsor_benefit, created = SponsorBenefit.objects.get_or_create(
|
|
||||||
sponsor=sponsor, benefit=benefit_level.benefit)
|
|
||||||
|
|
||||||
if created:
|
|
||||||
print "created", sponsor_benefit, "for", sponsor
|
|
||||||
|
|
||||||
# and set to default limits for this level.
|
|
||||||
sponsor_benefit.max_words = benefit_level.max_words
|
|
||||||
sponsor_benefit.other_limits = benefit_level.other_limits
|
|
||||||
|
|
||||||
# and set to active
|
|
||||||
sponsor_benefit.active = True
|
|
||||||
|
|
||||||
# @@@ We don't call sponsor_benefit.clean here. This means
|
|
||||||
# that if the sponsorship level for a sponsor is adjusted
|
|
||||||
# downwards, an existing too-long text entry can remain,
|
|
||||||
# and won't raise a validation error until it's next
|
|
||||||
# edited.
|
|
||||||
sponsor_benefit.save()
|
|
|
@ -1,38 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class SponsorManager(models.Manager):
|
|
||||||
|
|
||||||
def active(self):
|
|
||||||
return self.get_query_set().filter(active=True).order_by("level")
|
|
||||||
|
|
||||||
def with_weblogo(self):
|
|
||||||
queryset = self.raw("""
|
|
||||||
SELECT DISTINCT
|
|
||||||
"sponsorship_sponsor"."id",
|
|
||||||
"sponsorship_sponsor"."applicant_id",
|
|
||||||
"sponsorship_sponsor"."name",
|
|
||||||
"sponsorship_sponsor"."external_url",
|
|
||||||
"sponsorship_sponsor"."annotation",
|
|
||||||
"sponsorship_sponsor"."contact_name",
|
|
||||||
"sponsorship_sponsor"."contact_email",
|
|
||||||
"sponsorship_sponsor"."level_id",
|
|
||||||
"sponsorship_sponsor"."added",
|
|
||||||
"sponsorship_sponsor"."active",
|
|
||||||
"sponsorship_sponsorlevel"."order"
|
|
||||||
FROM
|
|
||||||
"sponsorship_sponsor"
|
|
||||||
INNER JOIN
|
|
||||||
"sponsorship_sponsorbenefit" ON ("sponsorship_sponsor"."id" = "sponsorship_sponsorbenefit"."sponsor_id")
|
|
||||||
INNER JOIN
|
|
||||||
"sponsorship_benefit" ON ("sponsorship_sponsorbenefit"."benefit_id" = "sponsorship_benefit"."id")
|
|
||||||
LEFT OUTER JOIN
|
|
||||||
"sponsorship_sponsorlevel" ON ("sponsorship_sponsor"."level_id" = "sponsorship_sponsorlevel"."id")
|
|
||||||
WHERE (
|
|
||||||
"sponsorship_sponsor"."active" = 't' AND
|
|
||||||
"sponsorship_benefit"."type" = 'weblogo' AND
|
|
||||||
"sponsorship_sponsorbenefit"."upload" != ''
|
|
||||||
)
|
|
||||||
ORDER BY "sponsorship_sponsorlevel"."order" ASC, "sponsorship_sponsor"."added" ASC
|
|
||||||
""")
|
|
||||||
return queryset
|
|
|
@ -1,266 +0,0 @@
|
||||||
import datetime
|
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db import models
|
|
||||||
from django.db.models.signals import post_init, post_save
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
from symposion.conference.models import Conference
|
|
||||||
|
|
||||||
from pycon.sponsorship import SPONSOR_COORDINATORS
|
|
||||||
from pycon.sponsorship.managers import SponsorManager
|
|
||||||
# from symposion.utils.mail import send_email
|
|
||||||
|
|
||||||
|
|
||||||
class SponsorLevel(models.Model):
|
|
||||||
|
|
||||||
conference = models.ForeignKey(Conference, verbose_name=_("conference"))
|
|
||||||
name = models.CharField(_("name"), max_length=100)
|
|
||||||
order = models.IntegerField(_("order"), default=0)
|
|
||||||
cost = models.PositiveIntegerField(_("cost"))
|
|
||||||
description = models.TextField(_("description"), blank=True, help_text=_("This is private."))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ["conference", "order"]
|
|
||||||
verbose_name = _("sponsor level")
|
|
||||||
verbose_name_plural = _("sponsor levels")
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return u"%s %s" % (self.conference, self.name)
|
|
||||||
|
|
||||||
def sponsors(self):
|
|
||||||
return self.sponsor_set.filter(active=True).order_by("added")
|
|
||||||
|
|
||||||
|
|
||||||
class Sponsor(models.Model):
|
|
||||||
|
|
||||||
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"))
|
|
||||||
annotation = models.TextField(_("annotation"), blank=True)
|
|
||||||
contact_name = models.CharField(_("Contact Name"), max_length=100)
|
|
||||||
contact_email = models.EmailField(_(u"Contact Email"))
|
|
||||||
level = models.ForeignKey(SponsorLevel, verbose_name=_("level"))
|
|
||||||
added = models.DateTimeField(_("added"), default=datetime.datetime.now)
|
|
||||||
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):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("sponsor")
|
|
||||||
verbose_name_plural = _("sponsors")
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
if self.active:
|
|
||||||
return reverse("sponsor_detail", kwargs={"pk": self.pk})
|
|
||||||
return reverse("sponsor_list")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def website_logo_url(self):
|
|
||||||
if not hasattr(self, "_website_logo_url"):
|
|
||||||
self._website_logo_url = None
|
|
||||||
benefits = self.sponsor_benefits.filter(benefit__type="weblogo", upload__isnull=False)
|
|
||||||
if benefits.exists():
|
|
||||||
# @@@ smarter handling of multiple weblogo benefits?
|
|
||||||
# shouldn't happen
|
|
||||||
if benefits[0].upload:
|
|
||||||
self._website_logo_url = benefits[0].upload.url
|
|
||||||
return self._website_logo_url
|
|
||||||
|
|
||||||
@property
|
|
||||||
def listing_text(self):
|
|
||||||
if not hasattr(self, "_listing_text"):
|
|
||||||
self._listing_text = None
|
|
||||||
benefits = self.sponsor_benefits.filter(benefit__id=7)
|
|
||||||
if benefits.count():
|
|
||||||
self._listing_text = benefits[0].text
|
|
||||||
return self._listing_text
|
|
||||||
|
|
||||||
@property
|
|
||||||
def joblisting_text(self):
|
|
||||||
if not hasattr(self, "_joblisting_text"):
|
|
||||||
self._joblisting_text = None
|
|
||||||
benefits = self.sponsor_benefits.filter(benefit__id=21)
|
|
||||||
if benefits.count():
|
|
||||||
self._joblisting_text = benefits[0].text
|
|
||||||
return self._joblisting_text
|
|
||||||
|
|
||||||
@property
|
|
||||||
def website_logo(self):
|
|
||||||
if self.sponsor_logo is None:
|
|
||||||
benefits = self.sponsor_benefits.filter(benefit__type="weblogo", upload__isnull=False)[:1]
|
|
||||||
if benefits.count():
|
|
||||||
if benefits[0].upload:
|
|
||||||
self.sponsor_logo = benefits[0]
|
|
||||||
self.save()
|
|
||||||
return self.sponsor_logo.upload
|
|
||||||
|
|
||||||
def reset_benefits(self):
|
|
||||||
"""
|
|
||||||
Reset all benefits for this sponsor to the defaults for their
|
|
||||||
sponsorship level.
|
|
||||||
"""
|
|
||||||
level = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
level = self.level
|
|
||||||
except SponsorLevel.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
allowed_benefits = []
|
|
||||||
if level:
|
|
||||||
for benefit_level in level.benefit_levels.all():
|
|
||||||
# Create all needed benefits if they don't exist already
|
|
||||||
sponsor_benefit, created = SponsorBenefit.objects.get_or_create(
|
|
||||||
sponsor=self, benefit=benefit_level.benefit)
|
|
||||||
|
|
||||||
# and set to default limits for this level.
|
|
||||||
sponsor_benefit.max_words = benefit_level.max_words
|
|
||||||
sponsor_benefit.other_limits = benefit_level.other_limits
|
|
||||||
|
|
||||||
# and set to active
|
|
||||||
sponsor_benefit.active = True
|
|
||||||
|
|
||||||
# @@@ We don't call sponsor_benefit.clean here. This means
|
|
||||||
# that if the sponsorship level for a sponsor is adjusted
|
|
||||||
# downwards, an existing too-long text entry can remain,
|
|
||||||
# and won't raise a validation error until it's next
|
|
||||||
# edited.
|
|
||||||
sponsor_benefit.save()
|
|
||||||
|
|
||||||
allowed_benefits.append(sponsor_benefit.pk)
|
|
||||||
|
|
||||||
# Any remaining sponsor benefits that don't normally belong to
|
|
||||||
# this level are set to inactive
|
|
||||||
self.sponsor_benefits.exclude(pk__in=allowed_benefits).update(active=False, max_words=None, other_limits="")
|
|
||||||
|
|
||||||
# @@@ should this just be done centrally?
|
|
||||||
def send_coordinator_emails(self):
|
|
||||||
for user in User.objects.filter(groups__name=SPONSOR_COORDINATORS):
|
|
||||||
send_email(
|
|
||||||
[user.email], "sponsor_signup",
|
|
||||||
context = {"sponsor": self}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _store_initial_level(sender, instance, **kwargs):
|
|
||||||
if instance:
|
|
||||||
instance._initial_level_id = instance.level_id
|
|
||||||
post_init.connect(_store_initial_level, sender=Sponsor)
|
|
||||||
|
|
||||||
|
|
||||||
def _check_level_change(sender, instance, created, **kwargs):
|
|
||||||
if instance and (created or instance.level_id != instance._initial_level_id):
|
|
||||||
instance.reset_benefits()
|
|
||||||
post_save.connect(_check_level_change, sender=Sponsor)
|
|
||||||
|
|
||||||
|
|
||||||
def _send_sponsor_notification_emails(sender, instance, created, **kwargs):
|
|
||||||
if instance and created:
|
|
||||||
instance.send_coordinator_emails()
|
|
||||||
post_save.connect(_send_sponsor_notification_emails, sender=Sponsor)
|
|
||||||
|
|
||||||
|
|
||||||
class Benefit(models.Model):
|
|
||||||
|
|
||||||
name = models.CharField(_("name"), max_length=100)
|
|
||||||
description = models.TextField(_("description"), blank=True)
|
|
||||||
type = models.CharField(
|
|
||||||
_("type"),
|
|
||||||
choices=[
|
|
||||||
("text", "Text"),
|
|
||||||
("file", "File"),
|
|
||||||
("weblogo", "Web Logo"),
|
|
||||||
("simple", "Simple")
|
|
||||||
],
|
|
||||||
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")
|
|
||||||
)
|
|
||||||
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 []
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
|
@ -1,74 +0,0 @@
|
||||||
from django import template
|
|
||||||
|
|
||||||
from symposion.conference.models import current_conference
|
|
||||||
from pycon.sponsorship.models import Sponsor, SponsorLevel
|
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
|
|
||||||
|
|
||||||
class SponsorsNode(template.Node):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def handle_token(cls, parser, token):
|
|
||||||
bits = token.split_contents()
|
|
||||||
if len(bits) == 3 and bits[1] == "as":
|
|
||||||
return cls(bits[2])
|
|
||||||
elif len(bits) == 4 and bits[2] == "as":
|
|
||||||
return cls(bits[3], bits[1])
|
|
||||||
else:
|
|
||||||
raise template.TemplateSyntaxError("%r takes 'as var' or 'level as var'" % bits[0])
|
|
||||||
|
|
||||||
def __init__(self, context_var, level=None):
|
|
||||||
if level:
|
|
||||||
self.level = template.Variable(level)
|
|
||||||
else:
|
|
||||||
self.level = None
|
|
||||||
self.context_var = context_var
|
|
||||||
|
|
||||||
def render(self, context):
|
|
||||||
conference = current_conference()
|
|
||||||
if self.level:
|
|
||||||
level = self.level.resolve(context)
|
|
||||||
queryset = Sponsor.objects.filter(level__conference = conference, level__name__iexact = level, active = True).order_by("added")
|
|
||||||
else:
|
|
||||||
queryset = Sponsor.objects.filter(level__conference = conference, active = True).order_by("level__order", "added")
|
|
||||||
context[self.context_var] = queryset
|
|
||||||
return u""
|
|
||||||
|
|
||||||
|
|
||||||
class SponsorLevelNode(template.Node):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def handle_token(cls, parser, token):
|
|
||||||
bits = token.split_contents()
|
|
||||||
if len(bits) == 3 and bits[1] == "as":
|
|
||||||
return cls(bits[2])
|
|
||||||
else:
|
|
||||||
raise template.TemplateSyntaxError("%r takes 'as var'" % bits[0])
|
|
||||||
|
|
||||||
def __init__(self, context_var):
|
|
||||||
self.context_var = context_var
|
|
||||||
|
|
||||||
def render(self, context):
|
|
||||||
conference = current_conference()
|
|
||||||
context[self.context_var] = SponsorLevel.objects.filter(conference=conference)
|
|
||||||
return u""
|
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
|
||||||
def sponsors(parser, token):
|
|
||||||
"""
|
|
||||||
{% sponsors as all_sponsors %}
|
|
||||||
or
|
|
||||||
{% sponsors "gold" as gold_sponsors %}
|
|
||||||
"""
|
|
||||||
return SponsorsNode.handle_token(parser, token)
|
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
|
||||||
def sponsor_levels(parser, token):
|
|
||||||
"""
|
|
||||||
{% sponsor_levels as levels %}
|
|
||||||
"""
|
|
||||||
return SponsorLevelNode.handle_token(parser, token)
|
|
|
@ -1,10 +0,0 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
|
||||||
from django.views.generic.simple import direct_to_template
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns("pycon.sponsorship.views",
|
|
||||||
url(r"^$", direct_to_template, {"template": "sponsorship/list.html"}, name="sponsor_list"),
|
|
||||||
# url(r"^jobs/$", direct_to_template, {"template": "sponsors/jobs.html"}, name="sponsor_jobs"),
|
|
||||||
url(r"^apply/$", "sponsor_apply", name="sponsor_apply"),
|
|
||||||
url(r"^(?P<pk>\d+)/$", "sponsor_detail", name="sponsor_detail"),
|
|
||||||
)
|
|
|
@ -1,115 +0,0 @@
|
||||||
import itertools
|
|
||||||
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
from django.http import HttpResponse
|
|
||||||
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.admin.views.decorators import staff_member_required
|
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
@staff_member_required
|
|
||||||
def sponsor_export_data(request):
|
|
||||||
sponsors = []
|
|
||||||
data = ""
|
|
||||||
|
|
||||||
for sponsor in Sponsor.objects.order_by("added"):
|
|
||||||
d = {
|
|
||||||
"name": sponsor.name,
|
|
||||||
"url": sponsor.external_url,
|
|
||||||
"level": (sponsor.level.order, sponsor.level.name),
|
|
||||||
"description": "",
|
|
||||||
}
|
|
||||||
for sponsor_benefit in sponsor.sponsor_benefits.all():
|
|
||||||
if sponsor_benefit.benefit_id == 2:
|
|
||||||
d["description"] = sponsor_benefit.text
|
|
||||||
sponsors.append(d)
|
|
||||||
|
|
||||||
def izip_longest(*args):
|
|
||||||
fv = None
|
|
||||||
def sentinel(counter=([fv]*(len(args)-1)).pop):
|
|
||||||
yield counter()
|
|
||||||
iters = [itertools.chain(it, sentinel(), itertools.repeat(fv)) for it in args]
|
|
||||||
try:
|
|
||||||
for tup in itertools.izip(*iters):
|
|
||||||
yield tup
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
def pairwise(iterable):
|
|
||||||
a, b = itertools.tee(iterable)
|
|
||||||
b.next()
|
|
||||||
return izip_longest(a, b)
|
|
||||||
|
|
||||||
def level_key(s):
|
|
||||||
return s["level"]
|
|
||||||
|
|
||||||
for level, level_sponsors in itertools.groupby(sorted(sponsors, key=level_key), level_key):
|
|
||||||
data += "%s\n" % ("-" * (len(level[1])+4))
|
|
||||||
data += "| %s |\n" % level[1]
|
|
||||||
data += "%s\n\n" % ("-" * (len(level[1])+4))
|
|
||||||
for sponsor, next in pairwise(level_sponsors):
|
|
||||||
description = sponsor["description"].strip()
|
|
||||||
description = description if description else "-- NO DESCRIPTION FOR THIS SPONSOR --"
|
|
||||||
data += "%s\n\n%s" % (sponsor["name"], description)
|
|
||||||
if next is not None:
|
|
||||||
data += "\n\n%s\n\n" % ("-"*80)
|
|
||||||
else:
|
|
||||||
data += "\n\n"
|
|
||||||
|
|
||||||
return HttpResponse(data, content_type="text/plain;charset=utf-8")
|
|
Loading…
Reference in a new issue