Skip to content

Commit

Permalink
Merge branch 'main' into rel
Browse files Browse the repository at this point in the history
  • Loading branch information
davidfischer committed Oct 23, 2024
2 parents e557ef8 + 6fcc4cc commit f404b43
Show file tree
Hide file tree
Showing 24 changed files with 625 additions and 188 deletions.
24 changes: 24 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ CHANGELOG
.. Then it is formatted and copied into this file.
.. This is included by docs/developer/changelog.rst
Version v5.9.0
--------------

This release added a data aggregation for rotated ads,
it also added a new exportable report for advertisers to get performance
for all of their specific ads over a time period.
This version also updated many dependency versions.
Specifically Celery was having some stability issues with the previous version (5.4)

:Date: October 23, 2024

* @davidfischer: Build an aggregation for ad rotations (#928)
* @davidfischer: Celery upgrade to v5.5rc (#927)
* @dependabot[bot]: Bump starlette from 0.39.2 to 0.40.0 in /requirements (#926)
* @github-actions[bot]: Dependencies: all packages updated via pip-tools (#925)
* @davidfischer: Remove simple history change reason (#924)
* @davidfischer: Per ad per day advertiser table/export (#921)
* @ericholscher: fix pip tools (#920)
* @ericholscher: Migrate JSONField to Django builtin JSONField (#919)
* @dependabot[bot]: Bump bootstrap from 4.6.2 to 5.0.0 (#918)
* @dependabot[bot]: Bump webpack from 5.90.1 to 5.94.0 (#908)


Version v5.8.0
--------------

Expand Down
6 changes: 6 additions & 0 deletions adserver/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from .models import Region
from .models import RegionImpression
from .models import RegionTopicImpression
from .models import RotationImpression
from .models import Topic
from .models import UpliftImpression
from .models import View
Expand Down Expand Up @@ -879,6 +880,11 @@ class UpliftImpressionAdmin(ImpressionsAdmin):
pass


@admin.register(RotationImpression)
class RotationImpressionAdmin(ImpressionsAdmin):
date_hierarchy = "date"


class PlacementImpressionAdmin(ImpressionsAdmin):
readonly_fields = ("div_id", "ad_type_slug") + ImpressionsAdmin.readonly_fields
list_display = ("div_id", "ad_type_slug") + ImpressionsAdmin.list_display
Expand Down
34 changes: 34 additions & 0 deletions adserver/analyzer/migrations/0008_migrate_jsonfield.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 5.0.8 on 2024-09-30 21:04

import adserver.analyzer.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("adserver_analyzer", "0007_add_advertiser_flights"),
]

operations = [
migrations.AlterField(
model_name="analyzedadvertiserurl",
name="keywords",
field=models.JSONField(
blank=True,
null=True,
validators=[adserver.analyzer.validators.KeywordsValidator()],
verbose_name="Keywords for this URL",
),
),
migrations.AlterField(
model_name="analyzedurl",
name="keywords",
field=models.JSONField(
blank=True,
null=True,
validators=[adserver.analyzer.validators.KeywordsValidator()],
verbose_name="Keywords for this URL",
),
),
]
3 changes: 1 addition & 2 deletions adserver/analyzer/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_extensions.db.models import TimeStampedModel
from jsonfield import JSONField

from ..models import Advertiser
from ..models import Flight
Expand All @@ -21,7 +20,7 @@ class BaseAnalyzedUrl(TimeStampedModel):
)

# Fields below are updated by the analyzer
keywords = JSONField(
keywords = models.JSONField(
_("Keywords for this URL"),
blank=True,
null=True,
Expand Down
4 changes: 2 additions & 2 deletions adserver/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from django.utils.text import slugify
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from simple_history.utils import update_change_reason

from .models import Advertisement
from .models import Campaign
Expand Down Expand Up @@ -1358,7 +1357,8 @@ def save(self, commit=True):
user.invite_user()

# Track who added this user
update_change_reason(user, "Invited via authorized users view")
# See: https://github.com/jazzband/django-simple-history/issues/1181
# update_change_reason(user, "Invited via authorized users view")

# You will need to add the user to the publisher/advertiser in the view
return user
Expand Down
110 changes: 110 additions & 0 deletions adserver/migrations/0097_migrate_jsonfield.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Generated by Django 5.0.8 on 2024-09-30 21:04

import adserver.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("adserver", "0096_simple_history_upgrade"),
]

operations = [
migrations.AlterField(
model_name="click",
name="keywords",
field=models.JSONField(
blank=True, null=True, verbose_name="Keyword targeting for this view"
),
),
migrations.AlterField(
model_name="flight",
name="targeting_parameters",
field=models.JSONField(
blank=True,
null=True,
validators=[adserver.validators.TargetingParametersValidator()],
verbose_name="Targeting parameters",
),
),
migrations.AlterField(
model_name="flight",
name="traffic_cap",
field=models.JSONField(
blank=True,
default=None,
null=True,
validators=[adserver.validators.TrafficFillValidator()],
verbose_name="Traffic cap",
),
),
migrations.AlterField(
model_name="flight",
name="traffic_fill",
field=models.JSONField(
blank=True,
default=None,
null=True,
validators=[adserver.validators.TrafficFillValidator()],
verbose_name="Traffic fill",
),
),
migrations.AlterField(
model_name="historicalflight",
name="targeting_parameters",
field=models.JSONField(
blank=True,
null=True,
validators=[adserver.validators.TargetingParametersValidator()],
verbose_name="Targeting parameters",
),
),
migrations.AlterField(
model_name="historicalflight",
name="traffic_cap",
field=models.JSONField(
blank=True,
default=None,
null=True,
validators=[adserver.validators.TrafficFillValidator()],
verbose_name="Traffic cap",
),
),
migrations.AlterField(
model_name="historicalflight",
name="traffic_fill",
field=models.JSONField(
blank=True,
default=None,
null=True,
validators=[adserver.validators.TrafficFillValidator()],
verbose_name="Traffic fill",
),
),
migrations.AlterField(
model_name="offer",
name="keywords",
field=models.JSONField(
blank=True, null=True, verbose_name="Keyword targeting for this view"
),
),
migrations.AlterField(
model_name="region",
name="prices",
field=models.JSONField(
blank=True,
help_text="Topic pricing matrix for this region",
null=True,
validators=[adserver.validators.TopicPricingValidator()],
verbose_name="Topic prices",
),
),
migrations.AlterField(
model_name="view",
name="keywords",
field=models.JSONField(
blank=True, null=True, verbose_name="Keyword targeting for this view"
),
),
]
34 changes: 34 additions & 0 deletions adserver/migrations/0098_rotation_aggregation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 5.0.9 on 2024-10-18 18:03

import django.db.models.deletion
import django_extensions.db.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('adserver', '0097_migrate_jsonfield'),
]

operations = [
migrations.CreateModel(
name='RotationImpression',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('date', models.DateField(db_index=True, verbose_name='Date')),
('decisions', models.PositiveIntegerField(default=0, help_text="The number of times the Ad Decision API was called. The server might not respond with an ad if there isn't inventory.", verbose_name='Decisions')),
('offers', models.PositiveIntegerField(default=0, help_text='The number of times an ad was proposed by the ad server. The client may not load the ad (a view) for a variety of reasons ', verbose_name='Offers')),
('views', models.PositiveIntegerField(default=0, help_text='Number of times the ad was legitimately viewed', verbose_name='Views')),
('clicks', models.PositiveIntegerField(default=0, help_text='Number of times the ad was legitimately clicked', verbose_name='Clicks')),
('advertisement', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='rotated_impressions', to='adserver.advertisement')),
('publisher', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='rotated_impressions', to='adserver.publisher')),
],
options={
'ordering': ('-date',),
'unique_together': {('publisher', 'advertisement', 'date')},
},
),
]
40 changes: 34 additions & 6 deletions adserver/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
from django_countries.fields import CountryField
from django_extensions.db.models import TimeStampedModel
from djstripe.enums import InvoiceStatus
from jsonfield import JSONField
from simple_history.models import HistoricalRecords
from user_agents import parse

Expand Down Expand Up @@ -195,7 +194,7 @@ class Region(TimeStampedModel, models.Model):
help_text=_("Whether advertisers can select this region for new flights"),
)

prices = JSONField(
prices = models.JSONField(
_("Topic prices"),
blank=True,
null=True,
Expand Down Expand Up @@ -831,7 +830,7 @@ class Flight(TimeStampedModel, IndestructibleModel):
Campaign, related_name="flights", on_delete=models.PROTECT
)

targeting_parameters = JSONField(
targeting_parameters = models.JSONField(
_("Targeting parameters"),
blank=True,
null=True,
Expand Down Expand Up @@ -866,7 +865,7 @@ class Flight(TimeStampedModel, IndestructibleModel):
# "countries": {"US": 0.1, "CA": 0.05, "DE": 0.05},
# "regions": {"us-ca": 0.25, "eu": 0.5},
# }
traffic_fill = JSONField(
traffic_fill = models.JSONField(
_("Traffic fill"),
blank=True,
null=True,
Expand All @@ -877,7 +876,7 @@ class Flight(TimeStampedModel, IndestructibleModel):
# If set, any publisher, country, or region whose `traffic_fill` exceeds the cap
# will not be eligible to show on this campaign until they're below the cap.
# Format is the same as `traffic_fill` but this is set manually
traffic_cap = JSONField(
traffic_cap = models.JSONField(
_("Traffic cap"),
blank=True,
null=True,
Expand Down Expand Up @@ -2461,6 +2460,33 @@ def __str__(self):
return "Uplift of %s on %s" % (self.advertisement, self.date)


class RotationImpression(BaseImpression):
"""
Create an index of ads that were rotated.
Indexed one per ad/publisher per day.
This is a subset of AdImpressions. Only rotated ads (rotations > 1) are counted.
"""

publisher = models.ForeignKey(
Publisher, related_name="rotated_impressions", on_delete=models.PROTECT
)
advertisement = models.ForeignKey(
Advertisement,
related_name="rotated_impressions",
on_delete=models.PROTECT,
null=True,
)

class Meta:
ordering = ("-date",)
unique_together = ("publisher", "advertisement", "date")

def __str__(self):
"""Simple override."""
return "Rotations of %s on %s" % (self.advertisement, self.date)


class RegionTopicImpression(BaseImpression):
"""
Create an index combining aggregated keywords & geos.
Expand Down Expand Up @@ -2540,7 +2566,9 @@ class AdBase(TimeStampedModel, IndestructibleModel):
)

# Client data
keywords = JSONField(_("Keyword targeting for this view"), blank=True, null=True)
keywords = models.JSONField(
_("Keyword targeting for this view"), blank=True, null=True
)
div_id = models.CharField(
_("Div id"), blank=True, null=True, max_length=DIV_MAXLENGTH
)
Expand Down
Loading

0 comments on commit f404b43

Please sign in to comment.