Skip to content

Commit

Permalink
Merge pull request freelawproject#3639 from ttys0dev/async-tally-stats
Browse files Browse the repository at this point in the history
Make tally_stat async
  • Loading branch information
mlissner authored Jan 20, 2024
2 parents 447de94 + 9c07df5 commit ea4ff97
Show file tree
Hide file tree
Showing 9 changed files with 27 additions and 20 deletions.
3 changes: 2 additions & 1 deletion cl/alerts/management/commands/cl_send_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import warnings

import waffle
from asgiref.sync import async_to_sync
from django.conf import settings
from django.contrib.auth.models import User
from django.core.mail import EmailMultiAlternatives
Expand Down Expand Up @@ -241,7 +242,7 @@ def send_emails_and_webhooks(self, rate):
alerts_sent_count += 1
send_alert(user.profile, hits)

tally_stat(f"alerts.sent.{rate}", inc=alerts_sent_count)
async_to_sync(tally_stat)(f"alerts.sent.{rate}", inc=alerts_sent_count)
logger.info(f"Sent {alerts_sent_count} {rate} email alerts.")

def clean_rt_queue(self):
Expand Down
3 changes: 2 additions & 1 deletion cl/alerts/management/commands/cl_send_scheduled_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import DefaultDict

import waffle
from asgiref.sync import async_to_sync

from cl.alerts.models import (
SCHEDULED_ALERT_HIT_STATUS,
Expand Down Expand Up @@ -116,7 +117,7 @@ def query_and_send_alerts_by_rate(rate: str) -> None:
f"Removed {scheduled_alerts_deleted} Scheduled Alert Hits."
)

tally_stat(f"alerts.sent.{rate}", inc=alerts_sent_count)
async_to_sync(tally_stat)(f"alerts.sent.{rate}", inc=alerts_sent_count)
logger.info(f"Sent {alerts_sent_count} {rate} email alerts.")


Expand Down
7 changes: 5 additions & 2 deletions cl/alerts/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from importlib import import_module
from typing import Dict, List, Tuple, Union, cast

from asgiref.sync import async_to_sync
from celery import Task
from django.conf import settings
from django.contrib.auth.models import User
Expand Down Expand Up @@ -342,7 +343,7 @@ def send_alert_and_webhook(
connection.send_messages(messages)

# Work completed. Tally, log, and clean up
tally_stat("alerts.docket.alerts.sent", inc=len(messages))
async_to_sync(tally_stat)("alerts.docket.alerts.sent", inc=len(messages))
DocketAlert.objects.filter(docket=d).update(date_last_hit=now())

# Send docket entries to webhook
Expand Down Expand Up @@ -594,7 +595,9 @@ def process_percolator_response(response: PercolatorResponseType) -> None:
date_last_hit=now()
)
alerts_sent = len(rt_alerts_to_send)
tally_stat(f"alerts.sent.{Alert.REAL_TIME}", inc=alerts_sent)
async_to_sync(tally_stat)(
f"alerts.sent.{Alert.REAL_TIME}", inc=alerts_sent
)
logger.info(f"Sent {alerts_sent} {Alert.REAL_TIME} email alerts.")


Expand Down
4 changes: 2 additions & 2 deletions cl/search/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ def show_results(request: HttpRequest) -> HttpResponse:
if len(request.GET) == 0:
# No parameters --> Homepage.
if not is_bot(request):
tally_stat("search.homepage_loaded")
async_to_sync(tally_stat)("search.homepage_loaded")

# Ensure we get nothing from the future.
mutable_GET = request.GET.copy() # Makes it mutable
Expand Down Expand Up @@ -503,7 +503,7 @@ def show_results(request: HttpRequest) -> HttpResponse:
else:
# Just a regular search
if not is_bot(request):
tally_stat("search.results")
async_to_sync(tally_stat)("search.results")

# Create bare-bones alert form.
alert_form = CreateAlertForm(
Expand Down
11 changes: 6 additions & 5 deletions cl/stats/tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from asgiref.sync import async_to_sync

from cl.stats.models import Stat
from cl.stats.utils import get_milestone_range, tally_stat
Expand All @@ -21,17 +22,17 @@ def tearDown(self) -> None:
Stat.objects.all().delete()

def test_tally_a_stat(self) -> None:
count = tally_stat("test")
count = async_to_sync(tally_stat)("test")
self.assertEqual(count, 1)

def test_increment_a_stat(self) -> None:
count = tally_stat("test2")
count = async_to_sync(tally_stat)("test2")
self.assertEqual(count, 1)
count = tally_stat("test2")
count = async_to_sync(tally_stat)("test2")
self.assertEqual(count, 2)

def test_increment_by_two(self) -> None:
count = tally_stat("test3", inc=2)
count = async_to_sync(tally_stat)("test3", inc=2)
self.assertEqual(count, 2)
count = tally_stat("test3", inc=2)
count = async_to_sync(tally_stat)("test3", inc=2)
self.assertEqual(count, 4)
6 changes: 3 additions & 3 deletions cl/stats/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_milestone_range(start, end):
return out


def tally_stat(name, inc=1, date_logged=None):
async def tally_stat(name, inc=1, date_logged=None):
"""Tally an event's occurrence to the database.
Will assume the following overridable values:
Expand All @@ -56,15 +56,15 @@ def tally_stat(name, inc=1, date_logged=None):
"""
if date_logged is None:
date_logged = now()
stat, created = Stat.objects.get_or_create(
stat, created = await Stat.objects.aget_or_create(
name=name, date_logged=date_logged, defaults={"count": inc}
)
if created:
return stat.count
else:
count_cache = stat.count
stat.count = F("count") + inc
stat.save()
await stat.asave()
# stat doesn't have the new value when it's updated with a F object, so
# we fake the return value instead of looking it up again for the user.
return count_cache + inc
Expand Down
3 changes: 2 additions & 1 deletion cl/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import timedelta
from email.utils import parseaddr

from asgiref.sync import async_to_sync
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import logout, update_session_auth_hash
Expand Down Expand Up @@ -532,7 +533,7 @@ def register(request: HttpRequest) -> HttpResponse:
email["from_email"],
email["to"],
)
tally_stat("user.created")
async_to_sync(tally_stat)("user.created")
get_str = "?next=%s&email=%s" % (
urlencode(redirect_to),
urlencode(user.email),
Expand Down
4 changes: 2 additions & 2 deletions cl/visualizations/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ async def build_visualization(viz):
g = await viz.build_nx_digraph(**build_kwargs)
except TooManyNodes:
# Still too many hops. Abort.
tally_stat("visualization.too_many_nodes_failure")
await tally_stat("visualization.too_many_nodes_failure")
return "too_many_nodes", viz

if len(g.edges()) == 0:
tally_stat("visualization.too_few_nodes_failure")
await tally_stat("visualization.too_few_nodes_failure")
return "too_few_nodes", viz

t2 = time.time()
Expand Down
6 changes: 3 additions & 3 deletions cl/visualizations/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,9 @@ async def privatize_visualization(request: HttpRequest) -> HttpResponse:
)


def mapper_homepage(request: HttpRequest) -> HttpResponse:
async def mapper_homepage(request: HttpRequest) -> HttpResponse:
if not is_bot(request):
tally_stat("visualization.scotus_homepage_loaded")
await tally_stat("visualization.scotus_homepage_loaded")

visualizations = (
SCOTUSMap.objects.filter(published=True, deleted=False)
Expand All @@ -281,7 +281,7 @@ def mapper_homepage(request: HttpRequest) -> HttpResponse:
.order_by("-date_published", "-date_modified", "-date_created")[:2]
)

return render(
return TemplateResponse(
request,
"visualization_home.html",
{"visualizations": visualizations, "private": False},
Expand Down

0 comments on commit ea4ff97

Please sign in to comment.