feat: send_apikey_usage_emails_task() (#7486)

* feat: send_apikey_usage_emails_task

* chore: update test to use task instead of cmd

* chore: add PeriodicTask

* chore: remove old command + empty management dir

* chore: remove now-empty bin/weekly

* refactor: only consider keys that might have events

---------

Co-authored-by: Robert Sparks <rjsparks@nostrum.com>
This commit is contained in:
Jennifer Richards 2024-05-30 10:31:25 -03:00 committed by GitHub
parent 020bdeb058
commit 2ccc230ce7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 54 additions and 81 deletions

View file

@ -1,22 +0,0 @@
#!/bin/bash
# Weekly datatracker jobs.
#
# This script is expected to be triggered by cron from
# /etc/cron.d/datatracker
export LANG=en_US.UTF-8
export PYTHONIOENCODING=utf-8
DTDIR=/a/www/ietf-datatracker/web
cd $DTDIR/
# Set up the virtual environment
source $DTDIR/env/bin/activate
logger -p user.info -t cron "Running $DTDIR/bin/weekly"
# Send out weekly summaries of apikey usage
$DTDIR/ietf/manage.py send_apikey_usage_emails

View file

@ -1,52 +0,0 @@
# Copyright The IETF Trust 2017-2020, All Rights Reserved
# -*- coding: utf-8 -*-
import datetime
from textwrap import dedent
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone
import debug # pyflakes:ignore
from ietf.person.models import PersonalApiKey, PersonApiKeyEvent
from ietf.utils.mail import send_mail
class Command(BaseCommand):
"""
Send out emails to all persons who have personal API keys about usage.
Usage is show over the given period, where the default period is 7 days.
"""
help = dedent(__doc__).strip()
def add_arguments(self, parser):
parser.add_argument('-d', '--days', dest='days', type=int, default=7,
help='The period over which to show usage.')
def handle(self, *filenames, **options):
"""
"""
self.verbosity = int(options.get('verbosity'))
days = options.get('days')
keys = PersonalApiKey.objects.filter(valid=True)
for key in keys:
earliest = timezone.now() - datetime.timedelta(days=days)
events = PersonApiKeyEvent.objects.filter(key=key, time__gt=earliest)
count = events.count()
events = events[:32]
if count:
key_name = key.hash()[:8]
subject = "API key usage for key '%s' for the last %s days" %(key_name, days)
to = key.person.email_address()
frm = settings.DEFAULT_FROM_EMAIL
send_mail(None, to, frm, subject, 'utils/apikey_usage_report.txt', {'person':key.person,
'days':days, 'key':key, 'key_name':key_name, 'count':count, 'events':events, } )

View file

@ -41,6 +41,7 @@ from ietf.meeting.factories import MeetingFactory
from ietf.nomcom.factories import NomComFactory from ietf.nomcom.factories import NomComFactory
from ietf.person.factories import PersonFactory, EmailFactory, UserFactory, PersonalApiKeyFactory from ietf.person.factories import PersonFactory, EmailFactory, UserFactory, PersonalApiKeyFactory
from ietf.person.models import Person, Email, PersonalApiKey from ietf.person.models import Person, Email, PersonalApiKey
from ietf.person.tasks import send_apikey_usage_emails_task
from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory
from ietf.review.models import ReviewWish, UnavailablePeriod from ietf.review.models import ReviewWish, UnavailablePeriod
from ietf.stats.models import MeetingRegistration from ietf.stats.models import MeetingRegistration
@ -853,9 +854,6 @@ class IetfAuthTests(TestCase):
key2.delete() key2.delete()
def test_send_apikey_report(self): def test_send_apikey_report(self):
from ietf.ietfauth.management.commands.send_apikey_usage_emails import Command
from ietf.utils.mail import outbox, empty_outbox
person = RoleFactory(name_id='secr', group__acronym='secretariat').person person = RoleFactory(name_id='secr', group__acronym='secretariat').person
url = urlreverse('ietf.ietfauth.views.apikey_create') url = urlreverse('ietf.ietfauth.views.apikey_create')
@ -880,9 +878,8 @@ class IetfAuthTests(TestCase):
date = str(date_today()) date = str(date_today())
empty_outbox() empty_outbox()
cmd = Command() send_apikey_usage_emails_task(days=7)
cmd.handle(verbosity=0, days=7)
self.assertEqual(len(outbox), len(endpoints)) self.assertEqual(len(outbox), len(endpoints))
for mail in outbox: for mail in outbox:
body = get_payload_text(mail) body = get_payload_text(mail)

View file

@ -5,12 +5,51 @@
import datetime import datetime
from celery import shared_task from celery import shared_task
from django.conf import settings
from django.utils import timezone from django.utils import timezone
from ietf.utils import log from ietf.utils import log
from .models import PersonApiKeyEvent from ietf.utils.mail import send_mail
from .models import PersonalApiKey, PersonApiKeyEvent
@shared_task
def send_apikey_usage_emails_task(days):
"""Send usage emails to Persons who have API keys"""
earliest = timezone.now() - datetime.timedelta(days=days)
keys = PersonalApiKey.objects.filter(
valid=True,
personapikeyevent__time__gt=earliest,
).distinct()
for key in keys:
events = PersonApiKeyEvent.objects.filter(key=key, time__gt=earliest)
count = events.count()
events = events[:32]
if count:
key_name = key.hash()[:8]
subject = "API key usage for key '%s' for the last %s days" % (
key_name,
days,
)
to = key.person.email_address()
frm = settings.DEFAULT_FROM_EMAIL
send_mail(
None,
to,
frm,
subject,
"utils/apikey_usage_report.txt",
{
"person": key.person,
"days": days,
"key": key,
"key_name": key_name,
"count": count,
"events": events,
},
)
@shared_task @shared_task
def purge_personal_api_key_events_task(keep_days): def purge_personal_api_key_events_task(keep_days):
keep_since = timezone.now() - datetime.timedelta(days=keep_days) keep_since = timezone.now() - datetime.timedelta(days=keep_days)

View file

@ -241,6 +241,17 @@ class Command(BaseCommand):
), ),
) )
PeriodicTask.objects.get_or_create(
name="Send personal API key usage emails",
task="ietf.person.tasks.send_apikey_usage_emails_task",
kwargs=json.dumps(dict(days=7)),
defaults=dict(
enabled=False,
crontab=self.crontabs["weekly"],
description="Send personal API key usage summary emails for the past week",
),
)
PeriodicTask.objects.get_or_create( PeriodicTask.objects.get_or_create(
name="Purge old personal API key events", name="Purge old personal API key events",
task="ietf.person.tasks.purge_personal_api_key_events_task", task="ietf.person.tasks.purge_personal_api_key_events_task",