Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
33 changes: 28 additions & 5 deletions tests/unit/sponsors/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import pretend

from celery.schedules import crontab
from sqlalchemy import true

from warehouse import sponsors
from warehouse.sponsors.models import Sponsor
from warehouse.sponsors.tasks import update_pypi_sponsors

from ...common.db.sponsors import SponsorFactory
Expand All @@ -24,6 +22,7 @@ def test_includeme():

assert config.add_request_method.calls == [
pretend.call(sponsors._sponsors, name="sponsors", reify=True),
pretend.call(sponsors._footer_sponsors, name="footer_sponsors", reify=True),
]
assert config.add_periodic_task.calls == [
pretend.call(crontab(minute=10), update_pypi_sponsors),
Expand All @@ -42,16 +41,40 @@ def test_do_not_schedule_sponsor_api_integration_if_no_token():

assert config.add_request_method.calls == [
pretend.call(sponsors._sponsors, name="sponsors", reify=True),
pretend.call(sponsors._footer_sponsors, name="footer_sponsors", reify=True),
]
assert not config.add_periodic_task.calls


def test_list_sponsors(db_request):
SponsorFactory.create_batch(5)
expected = SponsorFactory.create_batch(5)
SponsorFactory.create_batch(3, is_active=False)

result = sponsors._sponsors(db_request)
expected = db_request.db.query(Sponsor).filter(Sponsor.is_active == true()).all()

assert result == expected
assert len(result) == 5
assert set(result) == set(expected)
Comment on lines -50 to +56
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice refactor



def test_sponsors_ordered_by_level_then_name_then_infra(db_request):
c = SponsorFactory.create
infra = c(name="AWS", infra_sponsor=True, level_order=0)
vis_b = c(name="Bravo", infra_sponsor=False, level_order=1)
vis_a = c(name="Alpha", infra_sponsor=False, level_order=1)
sus = c(name="Charlie", infra_sponsor=False, level_order=2)

result = sponsors._sponsors(db_request)

assert result == [vis_a, vis_b, sus, infra]


def test_footer_sponsors_ordering(db_request):
c = SponsorFactory.create
infra = c(name="AWS", infra_sponsor=True, footer=False, level_order=0)
vis_b = c(name="Bravo", footer=True, infra_sponsor=False, level_order=1)
vis_a = c(name="Alpha", footer=True, infra_sponsor=False, level_order=1)
sus = c(name="Charlie", footer=True, infra_sponsor=False, level_order=2)
c(name="Nobody", footer=False, infra_sponsor=False, level_order=5)

db_request.sponsors = sponsors._sponsors(db_request)
assert sponsors._footer_sponsors(db_request) == [vis_a, vis_b, sus, infra]
5 changes: 4 additions & 1 deletion tests/unit/sponsors/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

def test_sponsor_color_logo_img_tag(db_request):
sponsor = SponsorFactory.create()
expected = f'<img src="{sponsor.color_logo_url}" alt="" loading="lazy">'
expected = (
'<img class="sponsors__image" '
+ f'src="{sponsor.color_logo_url}" alt="" loading="lazy">'
)
assert sponsor.color_logo_img == expected


Expand Down
52 changes: 52 additions & 0 deletions tests/unit/sponsors/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,58 @@ def test_update_remote_sponsor_with_same_slug_with_new_logo(
assert "Sponsor description" == db_sponsor.service


@pytest.mark.parametrize(
("level_name", "expected_footer"),
[
("Visionary", True),
("Sustainability", True),
("Partner", False),
],
)
def test_footer_set_based_on_level(
monkeypatch,
db_request,
fake_task_request,
sponsor_api_data,
level_name,
expected_footer,
):
sponsor_api_data[0]["level_name"] = level_name
response = pretend.stub(
raise_for_status=lambda: None, json=lambda: sponsor_api_data
)
requests = pretend.stub(
get=pretend.call_recorder(lambda url, headers, timeout: response)
)
monkeypatch.setattr(tasks, "requests", requests)

fake_task_request.db = db_request.db
tasks.update_pypi_sponsors(fake_task_request)

db_sponsor = db_request.db.query(Sponsor).one()
assert db_sponsor.footer is expected_footer


def test_white_logo_synced_when_provided(
monkeypatch, db_request, fake_task_request, sponsor_api_data
):
sponsor_api_data[0]["white_logo"] = "https://logourl.com/white.png"
response = pretend.stub(
raise_for_status=lambda: None, json=lambda: sponsor_api_data
)
monkeypatch.setattr(
tasks,
"requests",
pretend.stub(get=pretend.call_recorder(lambda *a, **kw: response)),
)
fake_task_request.db = db_request.db
tasks.update_pypi_sponsors(fake_task_request)
assert (
db_request.db.query(Sponsor).one().white_logo_url
== "https://logourl.com/white.png"
)


def test_flag_existing_psf_sponsor_to_false_if_not_present_in_api_response(
monkeypatch, db_request, fake_task_request, sponsor_api_data
):
Expand Down
22 changes: 21 additions & 1 deletion warehouse/sponsors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,32 @@


def _sponsors(request):
return request.db.query(Sponsor).filter(Sponsor.is_active == true()).all()
return (
request.db.query(Sponsor)
.filter(Sponsor.is_active == true())
.order_by(Sponsor.infra_sponsor, Sponsor.level_order, Sponsor.name)
.all()
)


def _footer_sponsors(request):
"""Return footer sponsors: PSF by level then name, infra by name."""
all_sponsors = request.sponsors
psf = sorted(
(s for s in all_sponsors if s.footer and not s.infra_sponsor),
key=lambda s: (s.level_order or 0, s.name),
)
infra = sorted(
(s for s in all_sponsors if s.infra_sponsor),
key=lambda s: s.name,
)
return psf + infra


def includeme(config):
# Add a request method which will allow to list sponsors
config.add_request_method(_sponsors, name="sponsors", reify=True)
config.add_request_method(_footer_sponsors, name="footer_sponsors", reify=True)
Comment on lines 34 to +36
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if instead of a new request method, request.sponsors performs the query, and returns a well-structured dictionary that is sorted, ordered, etc and can be used in a template?

Maybe a shape like:

sponsors = {
    "infrastructure": [...],
    "all": [...],
}

Then we remain with a single request method request.sponsors that returns the presorted, filtered objects for the templates to iterate?


# Add a periodic task to update sponsors table
if config.registry.settings.get("pythondotorg.api_token"):
Expand Down
5 changes: 4 additions & 1 deletion warehouse/sponsors/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ class Sponsor(db.Model):

@property
def color_logo_img(self):
return f'<img src="{self.color_logo_url}" alt="" loading="lazy">'
return (
'<img class="sponsors__image" '
+ f'src="{self.color_logo_url}" alt="" loading="lazy">'
)

@property
def white_logo_img(self):
Expand Down
2 changes: 2 additions & 0 deletions warehouse/sponsors/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ def update_pypi_sponsors(request):
sponsor.service = sponsor_info["description"]
sponsor.link_url = sponsor_info["sponsor_url"]
sponsor.color_logo_url = sponsor_info["logo"]
sponsor.white_logo_url = sponsor_info.get("white_logo")
sponsor.level_name = sponsor_info["level_name"]
sponsor.level_order = sponsor_info["level_order"]
sponsor.is_active = True
sponsor.psf_sponsor = True
sponsor.footer = sponsor_info["level_name"] in {"Visionary", "Sustainability"}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition is no longer used in the template

sponsor.origin = "remote"
33 changes: 33 additions & 0 deletions warehouse/static/sass/blocks/_sponsors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@
height: 0;
}

&__group-label {
flex-basis: 100%;
color: color.adjust(colours.$white, $alpha: -0.4);
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.05em;
margin: 4px 0 0;
padding: 0;
}

&__sponsor {
display: inline-grid;
grid-template-rows: fit-content(40%);
Expand Down Expand Up @@ -109,4 +119,27 @@
display: none;
}
}

&__sponsor--infra {
opacity: 0.75;

.sponsors__image {
max-width: 80px;
opacity: 0.7;
}

.sponsors__name,
.sponsors__service {
max-width: 80px;
font-size: 0.68rem;
}

&:hover {
opacity: 0.95;

.sponsors__image {
opacity: 0.9;
}
}
}
}
41 changes: 19 additions & 22 deletions warehouse/templates/includes/sponsors-footer.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,25 @@
<div class="sponsors">
<p class="sponsors__title">Supported by</p>
<div class="sponsors__divider"></div>
{% for sponsor in request.sponsors | sort(attribute='name') %}
{# Short-circuit if we don't have an image for them #}
{% if sponsor.white_logo_url %}
{# Check if they belong in the footer #}
{% if sponsor.infra_sponsor or sponsor.footer %}
<a class="sponsors__sponsor"
target="_blank"
rel="noopener"
href="{{ sponsor.link_url }}">
{{ sponsor.white_logo_img|camoify|safe }}
<span class="sponsors__name">{{ sponsor.name }}</span>
<span class="sponsors__service">
{# If they're an infra sponsor, we should have a service for them,
otherwise they're a PSF sponsor #}
{% if sponsor.infra_sponsor %}
{{ sponsor.service }}
{% elif sponsor.footer %}
PSF Sponsor
{% endif %}
</span>
</a>
{% endif %}
{% for sponsor in request.footer_sponsors %}
{% if sponsor.infra_sponsor and loop.previtem is defined and not loop.previtem.infra_sponsor %}
<div class="sponsors__divider"></div>
<p class="sponsors__group-label">Infrastructure partners</p>
<div class="sponsors__divider"></div>
{% endif %}
<a class="sponsors__sponsor {{ 'sponsors__sponsor--infra' if sponsor.infra_sponsor }}"
target="_blank"
rel="noopener"
href="{{ sponsor.link_url }}">
{% if sponsor.white_logo_url %}{{ sponsor.white_logo_img|camoify|safe }}{% endif %}
<span class="sponsors__name">{{ sponsor.name }}</span>
<span class="sponsors__service">
{% if sponsor.infra_sponsor %}
{{ sponsor.service }}
{% else %}
PSF Sponsor
{% endif %}
</span>
</a>
{% endfor %}
</div>
2 changes: 1 addition & 1 deletion warehouse/templates/pages/sponsors.html
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ <h2 class="centered-heading__title">Infrastructure sponsors</h2>
<hr class="centered-heading__rule">
</div>
<div class="sponsor-grid sponsor-grid--bottom-margin">
{% for sponsor in request.sponsors | sort(attribute="name") %}
{% for sponsor in request.sponsors %}
{% if sponsor.infra_sponsor %}
<div class="sponsor-grid__sponsor">
<div class="sponsor-grid__sponsor-img">{{ sponsor.color_logo_img|camoify|safe }}</div>
Expand Down
Loading