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:
parent
020bdeb058
commit
2ccc230ce7
22
bin/weekly
22
bin/weekly
|
@ -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
|
|
||||||
|
|
|
@ -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, } )
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue