Skip to content

Commit

Permalink
Merge pull request #740 from readthedocs/davidfischer/disable-inactiv…
Browse files Browse the repository at this point in the history
…e-publishers

Disable inactive publishers
  • Loading branch information
davidfischer committed May 11, 2023
2 parents db193af + 0548759 commit 5551c22
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 0 deletions.
66 changes: 66 additions & 0 deletions adserver/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,72 @@ def notify_of_publisher_changes(difference_threshold=0.25, min_views=10_000):
)


@app.task()
def disable_inactive_publishers(days=60, draft_only=False, dry_run=False):
"""Disable publishers who haven't had a paid impression in the specified `days`."""
if days < 30:
# Prevent the misstep where days is too short and many publishers are marked inactive
log.warning("Disabling publishers over too short a timeframe. Task stopped.")
return

threshold = get_ad_day() - datetime.timedelta(days=days)
site = get_current_site(request=None)

for publisher in Publisher.objects.filter(
allow_paid_campaigns=True, created__lt=threshold
):
if not PublisherPaidImpression.objects.filter(
publisher=publisher, date__gte=threshold
).exists():
log.info(
"Disabling paid ad approval on %s who has not shown a paid ad in at least %s days...",
publisher,
days,
)
if dry_run:
log.info("- Not actually disabling due to dry run")
continue

publisher.allow_paid_campaigns = False
publisher.save()

slack_message(
"adserver/slack/generic-message.slack",
{
"text": f"Disabled paid ad approval on {publisher} who has not shown a paid ad in at least {days} days."
},
)

to_addresses = [u.email for u in publisher.user_set.all()]
context = {
"publisher": publisher,
"days": days,
"site": site,
}

if settings.FRONT_ENABLED and to_addresses:
with mail.get_connection(
settings.FRONT_BACKEND,
sender_name=f"{site.name} Admins",
) as connection:
message = mail.EmailMessage(
_("Publisher account deactivated - %(name)s")
% {"name": site.name},
render_to_string(
"adserver/email/publisher-inactive.html", context
),
from_email=settings.DEFAULT_FROM_EMAIL, # Front doesn't use this
to=to_addresses,
connection=connection,
)

if draft_only:
# Make this a draft instead of just sending it directly if specified
message.draft = True

message.send()


@app.task()
def run_publisher_importers():
"""
Expand Down
23 changes: 23 additions & 0 deletions adserver/templates/adserver/email/publisher-inactive.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% extends 'adserver/email/base.html' %}
{% load i18n %}


{% block body %}


<p>{% blocktrans with publisher_name=publisher.name %}Hello {{ publisher_name }} team,{% endblocktrans %}</p>


<p>
<strong>{% blocktrans %}Your publisher account has been marked inactive.{% endblocktrans %} </strong>
<span>{% blocktrans %}Your site has not shown a paid in a while and as a result, we're marking your account inactive. If you believe this was done by accident or you want to reactivate your account, please respond to this message.{% endblocktrans %}</span>
</p>

<p>
<span>{% blocktrans %}We are sorry to lose you as a publisher on our network and we're interested to hear your feedback on why you chose to go in another direction. If you're willing to share, please respond and let us know. This helps us tremendously in improving our product and network.{% endblocktrans %}</span>
</p>


<p>{% blocktrans with site_name=site.name %}Cheers,<br>{{ site_name }} Team{% endblocktrans %}</p>

{% endblock body %}
44 changes: 44 additions & 0 deletions adserver/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.contrib.auth.models import AnonymousUser
from django.core import mail
from django.test import override_settings
from django.utils import timezone
from django_dynamic_fixture import get
from django_slack.utils import get_backend

Expand All @@ -28,6 +29,7 @@
from ..tasks import daily_update_publishers
from ..tasks import daily_update_regiontopic
from ..tasks import daily_update_uplift
from ..tasks import disable_inactive_publishers
from ..tasks import notify_of_completed_flights
from ..tasks import notify_of_publisher_changes
from ..tasks import remove_old_client_ids
Expand Down Expand Up @@ -244,6 +246,48 @@ def test_notify_of_publisher_changes(self):
messages = backend.retrieve_messages()
self.assertEqual(len(messages), 0)

@override_settings(
# Use the memory email backend instead of front for testing
FRONT_BACKEND="django.core.mail.backends.locmem.EmailBackend",
FRONT_ENABLED=True,
)
def test_disable_inactive_publishers(self):
# Ensure there's a recipient for the email
self.staff_user.publishers.add(self.publisher)

backend = get_backend()
backend.reset_messages()

disable_inactive_publishers()
messages = backend.retrieve_messages()

# The publisher has not hit the threshold
self.assertEqual(len(messages), 0)
self.assertEqual(len(mail.outbox), 0)

# Set this publisher up to be inactive
self.publisher.allow_paid_campaigns = True
self.publisher.created = timezone.now() - datetime.timedelta(days=100)
self.publisher.save()

disable_inactive_publishers(dry_run=True)
self.publisher.refresh_from_db()
self.assertTrue(self.publisher.allow_paid_campaigns)

# Still nothing due to dry-run
messages = backend.retrieve_messages()
self.assertEqual(len(messages), 0)
self.assertEqual(len(mail.outbox), 0)

# Actually disable the publishers
disable_inactive_publishers()
self.publisher.refresh_from_db()
self.assertFalse(self.publisher.allow_paid_campaigns)

messages = backend.retrieve_messages()
self.assertEqual(len(messages), 1)
self.assertEqual(len(mail.outbox), 1)


class AggregationTaskTests(BaseAdModelsTestCase):
def setUp(self):
Expand Down
5 changes: 5 additions & 0 deletions config/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@
# Runs on Wednesday
"schedule": crontab(day_of_week=3, hour="5", minute="0"),
},
"every-week-disable-inactive-publishers": {
"task": "adserver.tasks.disable_inactive_publishers",
# Runs on Tuesday
"schedule": crontab(day_of_week=2, hour="6", minute="0"),
},
# Very fast indexes that can be run more frequently
"halfhourly-advertiser-index": {
"task": "adserver.tasks.daily_update_advertisers",
Expand Down

0 comments on commit 5551c22

Please sign in to comment.