temporarily include pycon module containing advanced sponsorship app
This commit is contained in:
		
							parent
							
								
									7596922f4d
								
							
						
					
					
						commit
						6d9c1c2e5f
					
				
					 18 changed files with 939 additions and 0 deletions
				
			
		
							
								
								
									
										0
									
								
								pycon/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pycon/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										8
									
								
								pycon/admin.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pycon/admin.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
							
								
								
									
										84
									
								
								pycon/forms.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								pycon/forms.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,84 @@
 | 
				
			||||||
 | 
					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(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								pycon/models.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								pycon/models.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
							
								
								
									
										5
									
								
								pycon/sponsorship/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								pycon/sponsorship/__init__.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					SPONSOR_COORDINATORS = "sponsor-coordinators"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AUTH_GROUPS = [
 | 
				
			||||||
 | 
					    SPONSOR_COORDINATORS
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										69
									
								
								pycon/sponsorship/admin.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								pycon/sponsorship/admin.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
							
								
								
									
										72
									
								
								pycon/sponsorship/forms.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								pycon/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 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"]
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										0
									
								
								pycon/sponsorship/management/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pycon/sponsorship/management/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								pycon/sponsorship/management/commands/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pycon/sponsorship/management/commands/__init__.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,80 @@
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					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()
 | 
				
			||||||
							
								
								
									
										38
									
								
								pycon/sponsorship/managers.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								pycon/sponsorship/managers.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
							
								
								
									
										266
									
								
								pycon/sponsorship/models.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										266
									
								
								pycon/sponsorship/models.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,266 @@
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
							
								
								
									
										0
									
								
								pycon/sponsorship/templatetags/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pycon/sponsorship/templatetags/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										74
									
								
								pycon/sponsorship/templatetags/sponsorship_tags.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								pycon/sponsorship/templatetags/sponsorship_tags.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,74 @@
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
							
								
								
									
										10
									
								
								pycon/sponsorship/urls.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								pycon/sponsorship/urls.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					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"),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										115
									
								
								pycon/sponsorship/views.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								pycon/sponsorship/views.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,115 @@
 | 
				
			||||||
 | 
					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…
	
	Add table
		
		Reference in a new issue