Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Django 4.2 upgrade #781

Merged
merged 11 commits into from
Oct 3, 2023
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
exclude: '.*/dnt-policy.txt$'
- repo: https://github.com/adamchainz/django-upgrade
rev: "1.14.1"
hooks:
- id: django-upgrade
args: [--target-version, "4.2"]
- repo: https://github.com/ambv/black
rev: "22.3.0"
hooks:
Expand Down
44 changes: 20 additions & 24 deletions adserver/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ class TopicAdmin(admin.ModelAdmin):
list_display = (
"name",
"slug",
"selectable",
)
list_filter = ("selectable",)
list_per_page = 1000
ordering = ("slug",)
search_fields = ("name", "slug")
Expand Down Expand Up @@ -137,12 +139,15 @@ class RegionAdmin(admin.ModelAdmin):
"name",
"slug",
"order",
"selectable",
)
list_filter = ("selectable",)
list_per_page = 1000
ordering = ("order", "slug")
search_fields = ("name", "slug")


@admin.register(Publisher)
class PublisherAdmin(RemoveDeleteMixin, SimpleHistoryAdmin):

"""Django admin configuration for publishers."""
Expand Down Expand Up @@ -212,6 +217,7 @@ def has_add_permission(self, request, obj=None):
return False


@admin.register(Advertiser)
class AdvertiserAdmin(RemoveDeleteMixin, SimpleHistoryAdmin):

"""Django admin configuration for advertisers."""
Expand All @@ -225,6 +231,7 @@ class AdvertiserAdmin(RemoveDeleteMixin, SimpleHistoryAdmin):
readonly_fields = ("modified", "created")
search_fields = ("name", "slug", "djstripe_customer__id")

@admin.action(description=_("Create a draft invoice for this customer"))
def action_create_draft_invoice(self, request, queryset):
"""Create a draft invoice for this customer with metadata attached."""
if not settings.STRIPE_ENABLED:
Expand Down Expand Up @@ -285,10 +292,6 @@ def action_create_draft_invoice(self, request, queryset):
_("No Stripe customer ID for {}".format(advertiser)),
)

action_create_draft_invoice.short_description = _(
"Create a draft invoice for this customer"
)

def report(self, instance):
if not instance.pk:
return "" # pragma: no cover
Expand All @@ -309,6 +312,7 @@ def stripe_customer(self, obj):
return None


@admin.register(AdType)
class AdTypeAdmin(SimpleHistoryAdmin):

"""Django admin configuration for ad types."""
Expand Down Expand Up @@ -359,6 +363,7 @@ def get_queryset(self, request):
return queryset


@admin.register(Advertisement)
class AdvertisementAdmin(RemoveDeleteMixin, AdvertisementMixin, SimpleHistoryAdmin):

"""Django admin configuration for advertisements."""
Expand Down Expand Up @@ -528,6 +533,7 @@ def ecpm(self, obj):
return "${:.2f}".format(calculate_ecpm(cost, views))


@admin.register(Flight)
class FlightAdmin(RemoveDeleteMixin, FlightMixin, SimpleHistoryAdmin):

"""Django admin admin configuration for ad Flights."""
Expand Down Expand Up @@ -584,6 +590,7 @@ class FlightAdmin(RemoveDeleteMixin, FlightMixin, SimpleHistoryAdmin):
prepopulated_fields = {"slug": ("name",)}
search_fields = ("name", "slug", "campaign__name", "campaign__slug")

@admin.action(description=_("Create a draft invoice for selected flights"))
def action_create_draft_invoice(self, request, queryset):
"""
Create a draft invoice for selected flights with metadata attached.
Expand Down Expand Up @@ -702,10 +709,6 @@ def action_create_draft_invoice(self, request, queryset):
),
)

action_create_draft_invoice.short_description = _(
"Create a draft invoice for selected flights"
)

def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset = queryset.annotate(
Expand Down Expand Up @@ -743,6 +746,7 @@ def has_add_permission(self, request, obj=None):
return False # pragma: no cover


@admin.register(Campaign)
class CampaignAdmin(RemoveDeleteMixin, SimpleHistoryAdmin):

"""Django admin configuration for ad campaigns."""
Expand Down Expand Up @@ -833,6 +837,7 @@ class AdImpressionAdmin(ImpressionsAdmin):
readonly_fields = ("view_time",) + ImpressionsAdmin.readonly_fields


@admin.register(AdvertiserImpression)
class AdvertiserImpressionAdmin(ImpressionsAdmin):
date_hierarchy = "date"
readonly_fields = (
Expand All @@ -854,6 +859,7 @@ class AdvertiserImpressionAdmin(ImpressionsAdmin):
search_fields = ("advertiser__name",)


@admin.register(PublisherImpression, PublisherPaidImpression)
class PublisherImpressionAdmin(ImpressionsAdmin):
date_hierarchy = "date"
readonly_fields = (
Expand Down Expand Up @@ -950,6 +956,7 @@ def has_add_permission(self, request):
return False


@admin.register(Offer)
class OfferAdmin(AdBaseAdmin):

"""Django admin configuration for ad offers."""
Expand All @@ -968,7 +975,7 @@ class OfferAdmin(AdBaseAdmin):
# Without this, the django admin will order by date and PK
# resulting in a very expensive query
# This is due to how the admin determines that the order should be deterministic.
# https://docs.djangoproject.com/en/3.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.ordering
# https://docs.djangoproject.com/en/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.ordering
# Ordering by a UUID isn't very useful.
ordering = ("-pk",)

Expand Down Expand Up @@ -1009,20 +1016,23 @@ def refund_impressions(self, request, queryset):
return None


@admin.register(Click)
class ClickAdmin(AdBaseAdmin):

"""Django admin configuration for ad clicks."""

model = Click


@admin.register(View)
class ViewAdmin(AdBaseAdmin):

"""Django admin configuration for ad views."""

model = View


@admin.register(PublisherPayout)
class PublisherPayoutAdmin(SimpleHistoryAdmin):
list_display = (
"pk",
Expand All @@ -1041,6 +1051,7 @@ class PublisherPayoutAdmin(SimpleHistoryAdmin):
search_fields = ("publisher__name", "pk")


@admin.register(PublisherGroup)
class PublisherGroupAdmin(SimpleHistoryAdmin):
list_display = ("name", "slug", "modified", "created")
list_filter = ("publishers",)
Expand Down Expand Up @@ -1077,21 +1088,6 @@ class RegionTopicAdmin(admin.ModelAdmin):
list_display = readonly_fields


admin.site.register(Publisher, PublisherAdmin)
admin.site.register(PublisherPayout, PublisherPayoutAdmin)
admin.site.register(PublisherGroup, PublisherGroupAdmin)
admin.site.register(Advertiser, AdvertiserAdmin)
admin.site.register(View, ViewAdmin)
admin.site.register(Click, ClickAdmin)
admin.site.register(Offer, OfferAdmin)
admin.site.register(AdType, AdTypeAdmin)
admin.site.register(Advertisement, AdvertisementAdmin)
admin.site.register(Flight, FlightAdmin)
admin.site.register(Campaign, CampaignAdmin)
admin.site.register(AdvertiserImpression, AdvertiserImpressionAdmin)
admin.site.register(PublisherImpression, PublisherImpressionAdmin)
admin.site.register(PublisherPaidImpression, PublisherImpressionAdmin)

# Don't register Impression Admin's outside dev, since they will just 502 from too much data.
if settings.DEBUG:
admin.site.register(AdImpression, AdImpressionAdmin)
Expand Down
6 changes: 6 additions & 0 deletions adserver/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ class AdDecisionSerializer(serializers.Serializer):
# This purposefully doesn't use a URLField so we can disregard invalid values rather than rejecting them
url = serializers.CharField(max_length=256, required=False)

# The placement index (0-indexed)
# 1 or more means there's multiple placements on this page
placement_index = serializers.IntegerField(
required=False, min_value=0, max_value=999
)

# Used to pass the actual ad viewer's data for targeting purposes
user_ip = serializers.IPAddressField(required=False)
user_ua = serializers.CharField(required=False)
Expand Down
3 changes: 2 additions & 1 deletion adserver/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def _prepare_response(self, ad, placement, publisher, keywords, url, forced=Fals
)
cache.set(cache_key, data, duration)
else:
referrer = url or self.request.META.get("HTTP_REFERER")
referrer = url or self.request.headers.get("referer")
log.info(
"Using sticky ad decision. publisher=%s ad_type=%s campaign_type=%s, referrer=%s",
publisher.slug,
Expand Down Expand Up @@ -296,6 +296,7 @@ def decision(self, request, data):
keywords=keywords,
campaign_types=serializer.validated_data.get("campaign_types"),
url=url,
placement_index=serializer.validated_data.get("placement_index"),
# Debugging parameters
ad_slug=serializer.validated_data.get("force_ad"),
campaign_slug=serializer.validated_data.get("force_campaign"),
Expand Down
3 changes: 1 addition & 2 deletions adserver/auth/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class UserAdmin(SimpleHistoryAdmin):
readonly_fields = ("password", "updated_date", "created_date")
search_fields = ("email", "name")

@admin.action(description=_("Invite selected users"))
def invite_user_action(self, request, queryset):
for user in queryset:
if user.invite_user():
Expand All @@ -62,5 +63,3 @@ def invite_user_action(self, request, queryset):
)
% {"user": user},
)

invite_user_action.short_description = _("Invite selected users")
2 changes: 1 addition & 1 deletion adserver/auth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class User(AbstractBaseUser, PermissionsMixin):
"""
The custom extensible user model for the ad server.

https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#specifying-a-custom-user-model
https://docs.djangoproject.com/en/4.2/topics/auth/customizing/#specifying-a-custom-user-model

Inherits from both the AbstractBaseUser and PermissionMixin.
The following attributes are inherited from the superclasses::
Expand Down
12 changes: 12 additions & 0 deletions adserver/decisionengine/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ def __init__(self, request, placements, publisher, **kwargs):
log.debug("Publisher has hit their daily cap. publisher=%s", self.publisher)
self.campaign_types.remove(PAID_CAMPAIGN)

# The placement index (0-based) for this ad request
# 1+ means multiple ads on the same page
self.placement_index = kwargs.get("placement_index") or 0

# When set, only return a specific ad or ads from a campaign
self.ad_slug = kwargs.get("ad_slug")
self.campaign_slug = kwargs.get("campaign_slug")
Expand Down Expand Up @@ -173,6 +177,14 @@ def get_placement(self, advertisement):

def should_display_ads(self):
"""Whether to not display ads based on the user, request, or other settings."""
# Check if the publisher is allowed to have multiple placements
if not self.publisher.allow_multiple_placements and self.placement_index > 0:
log.info(
"Multiple placement request on publisher rejected. publisher=%s",
self.publisher,
)
return False

return True


Expand Down
Loading