Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions sponsors/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class ProvidedFileAssetConfigurationInline(StackedPolymorphicInline.Child):
ProvidedFileAssetConfigurationInline,
]


@admin.register(SponsorshipBenefit)
class SponsorshipBenefitAdmin(PolymorphicInlineSupportMixin, OrderedModelAdmin):
change_form_template = "sponsors/admin/sponsorshipbenefit_change_form.html"
Expand Down Expand Up @@ -179,12 +180,12 @@ def update_related_sponsorships(self, *args, **kwargs):
@admin.register(SponsorshipPackage)
class SponsorshipPackageAdmin(OrderedModelAdmin):
ordering = ("-year", "order",)
list_display = ["name", "year", "advertise", "allow_a_la_carte", "move_up_down_links"]
list_display = ["name", "year", "advertise", "allow_a_la_carte", "get_benefit_split", "move_up_down_links"]
list_filter = ["advertise", "year", "allow_a_la_carte"]
search_fields = ["name"]

def get_readonly_fields(self, request, obj=None):
readonly = []
readonly = ["get_benefit_split"]
if obj:
readonly.append("slug")
if not request.user.is_superuser:
Expand All @@ -196,6 +197,36 @@ def get_prepopulated_fields(self, request, obj=None):
return {'slug': ['name']}
return {}

def get_benefit_split(self, obj: SponsorshipPackage) -> str:
# colors selected from defined css variables in order to look like they fit in the theme
colors = [
"secondary", # blue
"admin-interface-header-background-color", # dark green
"admin-interface-title-color", # yellow
"admin-interface-header-text-color", # light green
"admin-interface-delete-button-background-hover-color", # red
"admin-interface-save-button-background-hover-color", # darker green
"admin-interface-module-background-selected-color", # light yellow
"admin-interface-generic-link-hover-color", # medium green
]
split = obj.get_default_revenue_split()
# I hope there's never more than 8 things in the split, but just in case...
if len(split) > len(colors):
colors = colors * (1 + (len(split) // len(colors)))
# build some span elements to show the percentages and have the program name in the title (to show on hover)
widths, spans = [], []
for i, (name, pct) in enumerate(split):
pct_str = f"{pct:.0f}%"
widths.append(pct_str)
spans.append(f"<span title='{name}' style='background-color:var(--{colors[i]})'>{pct_str}</span>")
# define a style that will show our span elements like a single horizontal stacked bar chart
style = f'color:#fff;text-align:center;cursor:pointer;display:grid;grid-template-columns:{" ".join(widths)}'
# wrap it all up and put a bow on it
html = f"<div style='{style}'>{''.join(spans)}</div>"
return mark_safe(html)

get_benefit_split.short_description = "Revenue split"


class SponsorContactInline(admin.TabularInline):
model = SponsorContact
Expand Down
12 changes: 12 additions & 0 deletions sponsors/models/sponsorship.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ def clone(self, year: int):
slug=self.slug, year=year, defaults=defaults
)

def get_default_revenue_split(self) -> list[tuple[str, float]]:
"""
Give the admin an indication of how revenue for sponsorships in this package will be divvied up
"""
values, key = {}, "program__description"
for benefit in self.benefits.values(key).annotate(amount=Sum("internal_value", default=0)).order_by("-amount"):
values[benefit[key]] = values.get(benefit[key], 0) + (benefit["amount"] or 0)
total = sum(values.values())
if not total:
return [] # nothing to split!
return [(k, round(v / total * 100, 3)) for k, v in values.items()]


class SponsorshipProgram(OrderedModel):
"""
Expand Down
9 changes: 9 additions & 0 deletions sponsors/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,15 @@ def test_clone_does_not_repeate_already_cloned_package(self):
self.assertFalse(created)
self.assertEqual(pkg_2023.pk, repeated_pkg_2023.pk)

def test_get_default_revenue_split(self):
for i in range(3):
self.package_benefits[i].internal_value = 1000
self.package_benefits[i].save()
split = self.package.get_default_revenue_split()
total = sum((pct for _, pct in split))
self.assertEqual(total, 100)


class SponsorContactModelTests(TestCase):
def test_get_primary_contact_for_sponsor(self):
sponsor = baker.make(Sponsor)
Expand Down