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 17, 2024
1 parent 0002256 commit cb09a57
Show file tree
Hide file tree
Showing 28 changed files with 884 additions and 565 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_user_about_expiring_actions,
)

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

from django.urls import reverse

from mail_editor.helpers import find_template

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

logger = logging.getLogger(__name__)


def collect_notifications_about_expiring_actions() -> list[dict]:
"""
Gather identifying data about users + their expiring actions for notification
Note:
we store id's of users + expiring actions in a dict for transfer and
reconstruction of the QuerySet when the notification is sent because
the QuerySet cannot be JSON-serialized (needed for celery)
"""
today = date.today()
receivers = User.objects.filter(
is_active=True,
plans_notifications=True,
actions__end_date=today,
).distinct()

notifications = [
dict(
receiver_id=receiver.pk,
object_ids=list(
Action.objects.filter(
is_for=receiver,
end_date=today,
).values_list("id", flat=True)
),
object_type=str(Action),
)
for receiver in receivers
]

return notifications


def notify_user_about_expiring_actions(
receiver_id: int, object_ids: list[int], channel: str
) -> None:
func = _channels[channel]

func(receiver_id=receiver_id, action_ids=object_ids)


def _send_email(receiver_id: int, action_ids: list[int]) -> None:
actions_link = build_absolute_url(reverse("profile:action_list"))
template = find_template("expiring_action")

receiver = User.objects.get(pk=receiver_id)
actions = Action.objects.filter(pk__in=action_ids)

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 = {
"email": _send_email,
}
6 changes: 6 additions & 0 deletions src/open_inwoner/accounts/notifications/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.db import models
from django.utils.translation import gettext as _


class NotificationChannel(models.TextChoices):
email = "email", _("Email")
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_user_about_messages

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

from django.db.models import Count
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

logger = logging.getLogger(__name__)


def collect_notifications_about_messages() -> list[dict]:
"""
Gather identifying data about users + their messages for notification
Note:
we store id's of users + messages on the `Notification` for
reconstruction of the QuerySet when the notification is sent because
the QuerySet cannot be JSON-serialized (needed for celery)
"""
receivers = User.objects.filter(
received_messages__seen=False,
received_messages__sent=False,
is_active=True,
messages_notifications=True,
).distinct()

notifications = [
dict(
receiver_id=receiver.pk,
object_ids=list(
Message.objects.filter(
receiver=receiver,
seen=False,
sent=False,
).values_list("id", flat=True)
),
object_type=str(Message),
)
for receiver in receivers
]

return notifications


def notify_user_about_messages(
receiver_id: int, object_ids: list[int], channel: str
) -> None:
send = _channels[channel]

send(receiver_id=receiver_id, message_ids=object_ids)


def _send_email(receiver_id: int, message_ids: list[int]) -> None:
inbox_url = build_absolute_url(reverse("inbox:index"))
template = find_template("new_messages")

receiver = User.objects.get(pk=receiver_id)
messages = Message.objects.filter(pk__in=message_ids)

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_user_about_expiring_plans,
)

__all__ = [
"collect_notifications_about_expiring_plans",
"notify_user_about_expiring_plans",
]
Loading

0 comments on commit cb09a57

Please sign in to comment.