Skip to content

Commit

Permalink
notifications: add effective_recipients field.
Browse files Browse the repository at this point in the history
* Adds new `effective_recipients` field into the `Notification`
  resource. This field will stored the recipients if the notification is
  successfully sent.
* Fixes #3288.

Co-Authored-by: Renaud Michotte <renaud.michotte@gmail.com>
  • Loading branch information
zannkukai committed Apr 12, 2023
1 parent 7e86e34 commit edf41de
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 11 deletions.
17 changes: 16 additions & 1 deletion rero_ils/modules/notifications/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,22 @@ def patron_transactions(self):
for result in results:
yield PatronTransaction.get_record(result.meta.id)

# CLASS METHODS ===========================================================
def update_effective_recipients(self, recipients):
"""Update the notification to set effective recipients.
:param recipients: a list of tuple ; first element is the recipient
type, second element is the recipient address.
:return the updated notification.
"""
recipients = recipients or []
for type_, address in recipients:
self.setdefault('effective_recipients', []).append({
'type': type_,
'address': address
})
return self.update(
data=self.dumps(), commit=True, dbcommit=True, reindex=True)

def update_process_date(self, sent=False, status=NotificationStatus.DONE):
"""Update the notification to set process date.
Expand Down
28 changes: 18 additions & 10 deletions rero_ils/modules/notifications/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def get_dispatcher_function(channel):
# The aggregation key we build ensure than aggregated notifications
# are always send to same recipient (patron, lib, vendor, ...) with
# the same communication channel. So we can check any notification
# of the set to get the theses informations.
# of the set to get the these informations.
for aggr_key, aggr_notifications in aggregated.items():
notification = aggr_notifications[0]
comm_channel = notification.get_communication_channel()
Expand All @@ -90,11 +90,13 @@ def get_dispatcher_function(channel):
f'patron: {notification.patron.pid} '
f'documents: {counter}'
)
result = dispatcher_function(aggr_notifications)
result, recipients = dispatcher_function(aggr_notifications)
for notification in aggr_notifications:
notification.update_process_date(sent=result)
if result:
sent += counter
for notification in aggr_notifications:
notification.update_effective_recipients(recipients)
else:
not_sent += counter
return {
Expand Down Expand Up @@ -181,7 +183,10 @@ def send_mail_for_printing(notifications=None):
generator process working with CUPS (using RabbitMQ queue)
:param notifications: the notification set to perform.
:return True if email is send, False if errors are found.
:return a tuple where first element is a boolean value to determine if
email was sent (False if errors are found) ; second element is a
list of used recipients.
:rtype tuple
"""
notifications = notifications or []
if not notifications:
Expand Down Expand Up @@ -219,14 +224,14 @@ def send_mail_for_printing(notifications=None):
current_app.logger.warning(
f'Notification#{notification.pid} for printing is lost :: '
f'({")(".join(error_reasons)})')
return False
return False, None

# 2. Build the context to render the template
notif_class = notification.__class__
context = notif_class.get_notification_context(notifications)
# 3. Force patron communication channel to 'mail'
# In some cases we force the notification to be send by mail despite
# the patron asked to received them by email (cipo reminders
# In some cases we force the notification to be sent by mail despite
# the patron asked to receive them by email (cipo reminders
# notifications with a communication channel to 'mail' value).
# Ensure than the ``include_patron_address`` are set to True.
context['include_patron_address'] = True
Expand All @@ -239,14 +244,17 @@ def send_mail_for_printing(notifications=None):
template=notification.get_template_path()
)
task_send_email.apply_async((msg.__dict__,))
return True
return True, [(RecipientType.TO, recipient)]

@staticmethod
def send_notification_by_email(notifications):
"""Send the notification by email to the patron.
:param notifications: the notification set to perform.
:return True if email is send, False if errors are found.
:return a tuple where first element is a boolean value to determine if
email was sent (False if errors are found) ; second element is a
list of used recipients.
:rtype tuple
"""
notifications = notifications or []
if not notifications:
Expand All @@ -265,7 +273,7 @@ def send_notification_by_email(notifications):
current_app.logger.warning(
f'Notification#{notification.pid} is lost :: '
f'({")(".join(error_reasons)})')
return False
return False, None

# build the context for this notification set
notif_class = notification.__class__
Expand All @@ -279,4 +287,4 @@ def send_notification_by_email(notifications):
)
delay = context.get('delay', 0)
task_send_email.apply_async((msg.__dict__,), countdown=delay)
return True
return True, [(RecipientType.TO, addr) for addr in recipients]
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,38 @@
"acquisition_order"
]
},
"effective_recipients": {
"title": "Effective notification recipients",
"description": "Where the notification were really dispatched",
"type": "array",
"minItems": 1,
"items": {
"title": "Recipient",
"type": "object",
"additionalProperties": false,
"required": [
"type",
"address"
],
"properties": {
"type": {
"title": "Recipient type",
"type": "string",
"enum": [
"to",
"cc",
"bcc",
"reply_to"
]
},
"address": {
"type": "string",
"format": "email",
"pattern": "^.*@.*\\..+$"
}
}
}
},
"context": {
"oneOf": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@
"notification_type": {
"type": "keyword"
},
"effective_recipients": {
"properties": {
"type": {
"type": "keyword"
},
"address": {
"type": "keyword"
}
}
},
"context": {
"properties": {
"loan": {
Expand Down
10 changes: 10 additions & 0 deletions tests/api/notifications/test_notifications_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ def test_delayed_notifications(
assert loan.is_notified(notification_type=NotificationType.AVAILABILITY)
assert loan.is_notified(notification_type=NotificationType.AT_DESK)

# Ensure than `effective_recipients` is filled
# The notification should be sent to library AT_DESK email setting.
notification = Notification.get_record(notification.id)
effective_recipients = [
recipient['address']
for recipient in notification.get('effective_recipients')
]
assert effective_recipients == \
[lib_martigny.get_email(NotificationType.AT_DESK)]

# One notification will be sent : AVAILABILITY (sent to patron).
# Get the last message from mailbox and check it.
availability_msg = mailbox[-1]
Expand Down

0 comments on commit edf41de

Please sign in to comment.