Skip to content

Commit

Permalink
[#2598] Refactor notifications for expiring actions/plans, messages, …
Browse files Browse the repository at this point in the history
…cases

    - replace cron jobs with celery tasks
    - explicitly mark internal utility functions for case notifications
  • Loading branch information
pi-sigma committed Jul 15, 2024
1 parent c891aba commit c31b6ea
Show file tree
Hide file tree
Showing 27 changed files with 776 additions and 477 deletions.
48 changes: 0 additions & 48 deletions src/open_inwoner/accounts/management/commands/actions_expire.py

This file was deleted.

This file was deleted.

6 changes: 6 additions & 0 deletions src/open_inwoner/accounts/notifications/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .tasks import collect_notifications, process_notifications

__all__ = [
"collect_notifications",
"process_notifications",
]
9 changes: 9 additions & 0 deletions src/open_inwoner/accounts/notifications/actions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .notify import (
collect_notifications_about_expiring_actions,
notify_about_expiring_actions,
)

__all__ = [
"collect_notifications_about_expiring_actions",
"notify_about_expiring_actions",
]
63 changes: 63 additions & 0 deletions src/open_inwoner/accounts/notifications/actions/notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import logging
from datetime import date

from django.db.models.query import QuerySet
from django.urls import reverse

from mail_editor.helpers import find_template

from open_inwoner.accounts.models import Action, User
from open_inwoner.utils.constants import NotificationChannel
from open_inwoner.utils.url import build_absolute_url

from ..data_structures import Notification

logger = logging.getLogger(__name__)


def collect_notifications_about_expiring_actions():
today = date.today()
users_with_expiring_actions = User.objects.filter(actions__end_date=today)
receivers = User.objects.filter(
is_active=True,
plans_notifications=True,
pk__in=users_with_expiring_actions.values_list("id"),
).distinct()

notifications = []
for receiver in receivers:
notification = Notification(
receiver=receiver,
objects=Action.objects.filter(is_for=receiver, end_date=today),
)
notifications.append(notification)

return notifications


def notify_about_expiring_actions(
receiver: User, objects: QuerySet[Action], channel: str
):
func = _channels[channel]

func(receiver=receiver, actions=objects)


def _send_email(receiver: User, actions: QuerySet[Action]):
actions_link = build_absolute_url(reverse("profile:action_list"))
template = find_template("expiring_action")
context = {
"actions": actions,
"actions_link": actions_link,
}

template.send_email([receiver.email], context)

logger.info(
f"The email was sent to the user {receiver} about {actions.count()} expiring actions"
)


_channels = {
NotificationChannel.email: _send_email,
}
11 changes: 11 additions & 0 deletions src/open_inwoner/accounts/notifications/data_structures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from dataclasses import dataclass

from django.db.models import QuerySet

from open_inwoner.accounts.models import User


@dataclass
class Notification:
receiver: User
objects: QuerySet
6 changes: 6 additions & 0 deletions src/open_inwoner/accounts/notifications/messages/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .notify import collect_notifications_about_messages, notify_about_messages

__all__ = [
"collect_notifications_about_messages",
"notify_about_messages",
]
76 changes: 76 additions & 0 deletions src/open_inwoner/accounts/notifications/messages/notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import logging

from django.db.models import Count
from django.db.models.query import QuerySet
from django.urls import reverse

from mail_editor.helpers import find_template

from open_inwoner.accounts.models import Message, User
from open_inwoner.utils.url import build_absolute_url

from ..data_structures import Notification

logger = logging.getLogger(__name__)


def collect_notifications_about_messages() -> list[Notification]:
"""
Collect and return data for notifications about messages
"""
receivers = User.objects.filter(
received_messages__seen=False,
received_messages__sent=False,
is_active=True,
messages_notifications=True,
).distinct()

notifications = []
for receiver in receivers:
notifications.append(
Notification(
receiver=receiver,
objects=Message.objects.filter(
receiver=receiver,
seen=False,
sent=False,
),
)
)

return notifications


def notify_about_messages(receiver: User, objects: QuerySet[Message], channel: str):
send = _channels[channel]

send(receiver=receiver, messages=objects)


def _send_email(receiver: User, messages: QuerySet[Message]):
inbox_url = build_absolute_url(reverse("inbox:index"))
template = find_template("new_messages")

total_messages = messages.count()
total_senders = messages.aggregate(total_senders=Count("sender", distinct=True))[
"total_senders"
]

context = {
"total_messages": total_messages,
"total_senders": total_senders,
"inbox_link": inbox_url,
}

template.send_email([receiver.email], context)

messages.update(sent=True)

logger.info(
f"The email was sent to the user {receiver} about {total_messages} new messages"
)


_channels = {
"email": _send_email,
}
9 changes: 9 additions & 0 deletions src/open_inwoner/accounts/notifications/plans/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .notify import (
collect_notifications_about_expiring_plans,
notify_about_expiring_plans,
)

__all__ = [
"collect_notifications_about_expiring_plans",
"notify_about_expiring_plans",
]
83 changes: 83 additions & 0 deletions src/open_inwoner/accounts/notifications/plans/notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import logging
from datetime import date

from django.db.models.query import Q, QuerySet
from django.urls import reverse

from mail_editor.helpers import find_template

from open_inwoner.accounts.models import User
from open_inwoner.plans.models import Plan
from open_inwoner.userfeed import hooks as userfeed_hooks
from open_inwoner.utils.url import build_absolute_url

from ..data_structures import Notification

logger = logging.getLogger(__name__)


def collect_notifications_about_expiring_plans() -> list[Notification]:
"""
Collect and return data for notifications about expiring plans
"""
today = date.today()

# receivers are users who:
# created or are otherwise associated with a plan that ends today and
# are active and have notifications enabled
receivers = (
User.objects.filter(
Q(id__in=Plan.objects.filter(end_date=today).values_list("created_by__id"))
| Q(
id__in=Plan.objects.filter(end_date=today).values_list(
"plan_contacts__id"
)
)
)
.filter(
is_active=True,
plans_notifications=True,
)
.distinct()
)

notifications = []
for receiver in receivers:
notification = Notification(
receiver=receiver,
objects=Plan.objects.filter(end_date=today).filter(
Q(created_by=receiver) | Q(plan_contacts=receiver)
),
)
notifications.append(notification)

return notifications


def notify_about_expiring_plans(receiver: User, objects: QuerySet[Plan], channel: str):
for plan in objects:
userfeed_hooks.plan_expiring(receiver, plan)

send = _channels[channel]

send(receiver=receiver, plans=objects)


def _send_email(receiver: User, plans: QuerySet[Plan]):
plan_list_link = build_absolute_url(reverse("collaborate:plan_list"))
template = find_template("expiring_plan")
context = {
"plans": plans,
"plan_list_link": plan_list_link,
}

template.send_email([receiver.email], context)

logger.info(
f"The email was sent to the user {receiver} about {plans.count()} expiring plans"
)


_channels = {
"email": _send_email,
}
Loading

0 comments on commit c31b6ea

Please sign in to comment.