🌐 AI搜索 & 代理 主页
Skip to content

Implement a backend for Sponsorship Applications frontend  #1666

@ewdurbin

Description

@ewdurbin

Carrying on from the work in #1662, this is the basic sketch of the backend flow for the application.

Sponsorship Application Flow

  1. The applicant may or may not be signed into python.org
  2. The applicant navigates to the new_sponsorship_application view and selects a package or customized benefits
  3. 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)
  4. If the User has any associated Sponsor objects, ask if the sponsorship application relates to one of them or if they'd like to create a new Sponsor. Otherwise move to Sponsor creation flow.
  5. Sponsor form(s) should populate SponsorInformation and SponsorContact objects. SponsorInformation objects store basic information like name, description, landing_page_url, web_logo for the sponsor. SponsorContact objects store contact information for people at the Sponsor who may be contacted or may manage the Sponsorship on python.org.
  6. After a Sponsor object and associated SponsorInformation and SponsorContact objects are created, we create a Sponsorship object which will be used to managed an individual Sponsorship agreement. These will have a start_date, end_date, sponsorship_fee, along with other tracking details necessary. This will also relate to SponsorBenefit objects that are "clones" of SponsorshipBenefit objects associated with form selections.
  7. The new Sponsorship and associated SponsorInformation, SponsorContact, and SponsorBenefit objects can be used to construct a Statement of Work after review by PSF Staff. During review the level_name, sponsorship_fee, and associated SponsorBenefits 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/

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions