Skip to content

Commit 05c522e

Browse files
authored
Merge branch 'main' into dependabot/pip/django-debug-toolbar-6.2.0
2 parents 30f4799 + fdf3d69 commit 05c522e

File tree

9 files changed

+87
-37
lines changed

9 files changed

+87
-37
lines changed

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ jobs:
2121
with:
2222
python-version: "3.x"
2323

24-
- uses: j178/prek-action@v1
24+
- uses: j178/prek-action@v2

apps/sponsors/admin.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -736,24 +736,35 @@ def get_sponsor_landing_page_url(self, obj):
736736
@admin.display(description="Web Logo")
737737
def get_sponsor_web_logo(self, obj):
738738
"""Render and return the sponsor's web logo as a thumbnail image."""
739-
html = "{% load thumbnail %}{% thumbnail sponsor.web_logo '150x150' format='PNG' quality=100 as im %}<img src='{{ im.url}}'/>{% endthumbnail %}"
739+
img = obj.sponsor.web_logo
740+
if not img:
741+
return "---"
742+
if img.name and img.name.lower().endswith(".svg"):
743+
return format_html(
744+
'<img src="{}" style="max-width:150px;max-height:150px"/>',
745+
img.url,
746+
)
747+
html = "{% load thumbnail %}{% thumbnail img '150x150' format='PNG' quality=100 as im %}<img src='{{ im.url}}'/>{% endthumbnail %}"
740748
template = Template(html)
741-
context = Context({"sponsor": obj.sponsor})
742-
html = template.render(context)
743-
return mark_safe(html) # noqa: S308
749+
context = Context({"img": img})
750+
return mark_safe(template.render(context)) # noqa: S308
744751

745752
@admin.display(description="Print Logo")
746753
def get_sponsor_print_logo(self, obj):
747754
"""Render and return the sponsor's print logo as a thumbnail image."""
748755
img = obj.sponsor.print_logo
749-
html = "---"
750-
if img:
751-
template = Template(
752-
"{% load thumbnail %}{% thumbnail img '150x150' format='PNG' quality=100 as im %}<img src='{{ im.url}}'/>{% endthumbnail %}"
756+
if not img:
757+
return "---"
758+
if img.name and img.name.lower().endswith(".svg"):
759+
return format_html(
760+
'<img src="{}" style="max-width:150px;max-height:150px"/>',
761+
img.url,
753762
)
754-
context = Context({"img": img})
755-
html = mark_safe(template.render(context)) # noqa: S308
756-
return html
763+
template = Template(
764+
"{% load thumbnail %}{% thumbnail img '150x150' format='PNG' quality=100 as im %}<img src='{{ im.url}}'/>{% endthumbnail %}"
765+
)
766+
context = Context({"img": img})
767+
return mark_safe(template.render(context)) # noqa: S308
757768

758769
@admin.display(description="Primary Phone")
759770
def get_sponsor_primary_phone(self, obj):

apps/sponsors/models/benefits.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Benefit feature and configuration models for the sponsors app."""
22

33
from django import forms
4-
from django.db import models
4+
from django.db import IntegrityError, models, transaction
55
from django.db.models import UniqueConstraint
66
from django.urls import reverse
77
from polymorphic.models import PolymorphicModel
@@ -155,11 +155,16 @@ def create_benefit_feature(self, sponsor_benefit, **kwargs):
155155

156156
asset_qs = content_object.assets.filter(internal_name=self.internal_name)
157157
if not asset_qs.exists():
158-
asset = self.ASSET_CLASS(
159-
content_object=content_object,
160-
internal_name=self.internal_name,
161-
)
162-
asset.save()
158+
try:
159+
with transaction.atomic():
160+
asset = self.ASSET_CLASS(
161+
content_object=content_object,
162+
internal_name=self.internal_name,
163+
)
164+
asset.save()
165+
except IntegrityError:
166+
if not content_object.assets.filter(internal_name=self.internal_name).exists():
167+
raise
163168

164169
return benefit_feature
165170

apps/sponsors/templates/sponsors/partials/sponsors-list.html

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ <h2 class="widget-title" style="text-align: center;">Sponsors</h2>
1010
<p style="text-align: center;">Visionary sponsors help to host Python downloads.</p>
1111
<div style="display: grid; grid-gap: 2em; grid-template-columns: repeat(auto-fit, minmax(150px, 0fr)); align-items: center; justify-content: center; margin-top: 1.5em;">
1212
{% for sponsorship in sponsorships %}
13-
{% thumbnail sponsorship.sponsor.web_logo "x150" format="PNG" quality=100 as im %}
1413
<div style="text-align: center;">
1514
<a href="{{ sponsorship.sponsor.landing_page_url }}" rel="sponsored noopener" target="_blank" style="border-bottom: 0;">
15+
{% if sponsorship.sponsor.web_logo.name|lower|slice:"-4:" == ".svg" %}
16+
<img src="{{ sponsorship.sponsor.web_logo.url }}" alt="{{ sponsorship.sponsor.name }} logo" style="max-height:150px;max-width:150px;height:auto;width:auto;">
17+
{% else %}
18+
{% thumbnail sponsorship.sponsor.web_logo "x150" format="PNG" quality=100 as im %}
1619
<img src="{{ im.url }}" alt="{{ sponsorship.sponsor.name }} logo" style="max-height:150px;max-width:150px;height:auto;width:auto;">
20+
{% endthumbnail %}
21+
{% endif %}
1722
</a>
1823
<p style="margin-top: 0.5em; margin-bottom: 0; font-size: 0.875em; color: #4d4d4d;">{{ sponsorship.sponsor.name }}</p>
1924
</div>
20-
{% endthumbnail %}
2125
{% endfor %}
2226
</div>
2327
{% endcache CACHED_DOWNLOAD_SPONSORS_LIST %}
@@ -28,11 +32,15 @@ <h2 class="widget-title" style="text-align: center;">Sponsors</h2>
2832
<h3 class="widget-title">Job Board Sponsors</h3>
2933
<div style="display: grid; grid-gap: 1em; grid-template-columns: repeat(auto-fit, minmax(100px, 0fr)); grid-template-rows: repeat(1, minmax(50px, 0fr)); align-items: center; justify-content: center; margin-top: 1em;">
3034
{% for sponsorship in sponsorships %}
31-
{% thumbnail sponsorship.sponsor.web_logo "x100" format="PNG" quality=100 as im %}
3235
<div>
36+
{% if sponsorship.sponsor.web_logo.name|lower|slice:"-4:" == ".svg" %}
37+
<img src="{{ sponsorship.sponsor.web_logo.url }}" alt="{{ sponsorship.sponsor.name }} logo" style="max-height:100px;max-width:100px;height:auto;width:auto;">
38+
{% else %}
39+
{% thumbnail sponsorship.sponsor.web_logo "x100" format="PNG" quality=100 as im %}
3340
<img src="{{ im.url }}" alt="{{ sponsorship.sponsor.name }} logo" style="max-height:100px;max-width:100px;height:auto;width:auto;">
41+
{% endthumbnail %}
42+
{% endif %}
3443
</div>
35-
{% endthumbnail %}
3644
{% endfor %}
3745
</div>
3846
{% endcache CACHED_JOBS_SPONSORS_LIST %}

apps/sponsors/tests/test_models.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,30 @@ def test_cant_create_same_asset_twice(self):
11271127
self.config.create_benefit_feature(self.sponsor_benefit)
11281128
self.assertEqual(1, TextAsset.objects.count())
11291129

1130+
def test_create_benefit_feature_with_preexisting_asset_no_crash(self):
1131+
"""Regression: submitting a new sponsorship when the sponsor already
1132+
has an asset with the same internal_name (from a prior application)
1133+
should not raise an IntegrityError."""
1134+
sponsor = self.sponsor_benefit.sponsorship.sponsor
1135+
TextAsset.objects.create(
1136+
content_object=sponsor,
1137+
internal_name=self.config.internal_name,
1138+
)
1139+
# should not raise IntegrityError
1140+
self.config.create_benefit_feature(self.sponsor_benefit)
1141+
self.assertEqual(1, TextAsset.objects.count())
1142+
1143+
def test_integrity_error_reraised_when_asset_missing(self):
1144+
"""An IntegrityError that is NOT a duplicate-asset collision must
1145+
propagate so it doesn't get silently swallowed."""
1146+
from unittest.mock import patch
1147+
1148+
with (
1149+
patch.object(TextAsset, "save", side_effect=IntegrityError("unrelated")),
1150+
self.assertRaises(IntegrityError),
1151+
):
1152+
self.config.create_benefit_feature(self.sponsor_benefit)
1153+
11301154
def test_clone_configuration_for_new_sponsorship_benefit_with_new_due_date(self):
11311155
sp_benefit = baker.make(SponsorshipBenefit, year=2023)
11321156

bin/start-nginx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ fi
5454
# We expect nginx to run in foreground.
5555
# We also expect a socket to be at /tmp/nginx.socket.
5656
echo 'buildpack=nginx at=nginx-start'
57+
rm -f /var/run/cabotage/cabotage.sock
5758
cd /tmp
5859
/usr/bin/nginx -p . -c /code/config/nginx.conf
5960
echo 'nginx' >$psmgr

gunicorn.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
bind = 'unix:/var/run/cabotage/nginx.sock'
22
backlog = 1024
3+
workers = 2
34
preload_app = True
45
max_requests = 2048
56
max_requests_jitter = 128

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ dependencies = [
4141
"requests>=2.26.0",
4242
"django-honeypot==1.3.0",
4343
"django-markupfield==2.0.1",
44-
"django-allauth==65.13.0",
44+
"django-allauth==65.14.1",
4545
"django-waffle==2.2.1",
4646
"djangorestframework==3.15.2",
4747
"django-filter==25.1",

uv.lock

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)