-
Notifications
You must be signed in to change notification settings - Fork 656
Closed
Labels
sponsor-applicationsRelates to sponsor applications at python.org/sponsors/application/Relates to sponsor applications at python.org/sponsors/application/
Description
Carrying on from the work in #1662, this is the basic sketch of the backend flow for the application.
Sponsorship Application Flow
- The applicant may or may not be signed into
python.org - The applicant navigates to the
new_sponsorship_applicationview and selects a package or customized benefits - When pressing "Submit", anonymous users must be asked to sign in or create an account. We should serialize the form selections in some way to carry it through after the signup/signin flow (cookie or local storage)
- If the
Userhas any associatedSponsorobjects, ask if the sponsorship application relates to one of them or if they'd like to create a newSponsor. Otherwise move toSponsorcreation flow. Sponsorform(s) should populateSponsorInformationandSponsorContactobjects.SponsorInformationobjects store basic information likename,description,landing_page_url,web_logofor the sponsor.SponsorContactobjects store contact information for people at theSponsorwho may be contacted or may manage theSponsorshiponpython.org.- After a
Sponsorobject and associatedSponsorInformationandSponsorContactobjects are created, we create aSponsorshipobject which will be used to managed an individualSponsorshipagreement. These will have astart_date,end_date,sponsorship_fee, along with other tracking details necessary. This will also relate toSponsorBenefitobjects that are "clones" ofSponsorshipBenefitobjects associated with form selections. - The new
Sponsorshipand associatedSponsorInformation,SponsorContact, andSponsorBenefitobjects can be used to construct a Statement of Work after review by PSF Staff. During review thelevel_name,sponsorship_fee, and associatedSponsorBenefits should be modifiable by the PSF staff member.
Diff with sketch of initial modeling:
diff --git a/sponsors/models.py b/sponsors/models.py
index 5988523..5644f93 100644
--- a/sponsors/models.py
+++ b/sponsors/models.py
@@ -1,4 +1,5 @@
from django.conf import settings
+from django.contrib.auth import get_user_model
from django.db import models
from django.template.defaultfilters import truncatechars
from markupfield.fields import MarkupField
@@ -10,6 +11,7 @@ from companies.models import Company
from .managers import SponsorQuerySet
DEFAULT_MARKUP_TYPE = getattr(settings, "DEFAULT_MARKUP_TYPE", "restructuredtext")
+User = get_user_model()
class SponsorshipPackage(OrderedModel):
@@ -156,6 +158,110 @@ class SponsorshipBenefit(OrderedModel):
pass
+class SponsorInformation(models.Model):
+ sponsor = models.ForeignKey("Sponsor", on_delete=models.CASCADE)
+ name = models.CharField(
+ max_length=100,
+ verbose_name="Sponsor name",
+ help_text="Name of the sponsor, for public display.",
+ )
+ description = models.TextField(
+ verbose_name="Sponsor description",
+ help_text="Brief description of the sponsor for public display.",
+ )
+ landing_page_url = models.URLField(
+ blank=True,
+ null=True,
+ verbose_name="Sponsor landing page",
+ help_text="Sponsor landing page URL. This may be provided by the sponsor, however the linked page may not contain any sales or marketing information.",
+ )
+ web_logo = models.ImageField(
+ upload_to="sponsor_web_logos",
+ verbose_name="Sponsor web logo",
+ help_text="For display on our sponsor webpage. High resolution PNG or JPG, smallest dimension no less than 256px",
+ )
+ print_logo = models.FileField(
+ upload_to="sponsor_print_logos",
+ blank=True,
+ null=True,
+ verbose_name="Sponsor print logo",
+ help_text="For printed materials, signage, and projection. SVG or EPS",
+ )
+
+ primary_phone = models.CharField("Sponsor Primary Phone", max_length=32)
+ mailing_address = models.TextField("Sponsor Mailing/Billing Address")
+
+
+class SponsorContact(models.Model):
+ sponsor = models.ForeignKey("Sponsor", on_delete=models.CASCADE)
+ user = models.ForeignKey(
+ User, null=True, blank=True, on_delete=models.CASCADE
+ ) # Optionally related to a User! (This needs discussion)
+ primary = models.BooleanField(
+ default=False, help_text="If this is the primary contact for the sponsor"
+ )
+ manager = models.BooleanField(
+ default=False,
+ help_text="If this contact can manage sponsorship information on python.org",
+ )
+ name = models.CharField(max_length=100)
+ email = models.EmailField(max_length=256)
+ phone = models.CharField("Contact Phone", max_length=32)
+
+ # Sketch of something we'll need to determine if a user is able to make _changes_ to sponsorship benefits/logos/descriptons/etc.
+ @property
+ def can_manage(self):
+ if self.user is not None and (self.primary or self.manager):
+ return True
+
+
+class Sponsorship(models.Model):
+ sponsor = models.ForeignKey("Sponsor", null=True, on_delete=models.SET_NULL)
+ applied_on = models.DateField()
+ approved_on = models.DateField(null=True, blank=True)
+ start_date = models.DateField(null=True, blank=True)
+ end_date = models.DateField(null=True, blank=True)
+
+ level_name = models.CharField(max_length=64)
+ sponsorship_fee = models.PositiveIntegerField(null=True, blank=True)
+
+
+class SponsorBenefit(models.Model):
+ sponsorship = models.ForeignKey(Sponsorship, on_delete=models.CASCADE)
+ sponsorship_benefit = models.ForeignKey(
+ SponsorshipBenefit,
+ null=True,
+ blank=False,
+ on_delete=models.SET_NULL,
+ help_text="Sponsorship Benefit this Sponsor Benefit came from",
+ )
+ name = models.CharField(
+ max_length=1024,
+ verbose_name="Benefit Name",
+ help_text="For display in the statement of work and sponsor dashboard.",
+ )
+ description = models.TextField(
+ null=True,
+ blank=True,
+ verbose_name="Benefit Description",
+ help_text="For display in the statement of work and sponsor dashboard.",
+ )
+ program = models.ForeignKey(
+ SponsorshipProgram,
+ null=True,
+ blank=False,
+ on_delete=models.SET_NULL,
+ verbose_name="Sponsorship Program",
+ help_text="Which sponsorship program the benefit is associated with.",
+ )
+
+
+################################################################################
+# Honestly not sure if we want to keep this object as is, or consider
+# reimplementing from scratch. For the purposes of this work I'm just going to
+# work around it for the moment and we can consider deletion/replacement at a
+# later review
+################################################################################
class Sponsor(ContentManageable):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
content = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE, blank=True)
```
Metadata
Metadata
Assignees
Labels
sponsor-applicationsRelates to sponsor applications at python.org/sponsors/application/Relates to sponsor applications at python.org/sponsors/application/