2018-06-28 15:10:14 +01:00
|
|
|
from collections import defaultdict
|
2017-11-28 10:35:16 +00:00
|
|
|
from datetime import datetime
|
2016-07-28 13:34:24 +01:00
|
|
|
|
2018-06-28 15:10:14 +01:00
|
|
|
from app.dao.date_util import get_months_for_financial_year
|
2025-01-10 16:21:15 -05:00
|
|
|
from app.enums import (
|
|
|
|
|
KeyType,
|
|
|
|
|
NotificationStatus,
|
2025-01-10 18:47:39 -08:00
|
|
|
NotificationType,
|
2025-01-10 16:21:15 -05:00
|
|
|
StatisticsType,
|
|
|
|
|
TemplateType,
|
|
|
|
|
)
|
2016-07-28 13:34:24 +01:00
|
|
|
|
|
|
|
|
|
2025-01-10 18:18:20 -08:00
|
|
|
def format_statistics(statistics, total_notifications=None):
|
2016-07-28 13:34:24 +01:00
|
|
|
# statistics come in a named tuple with uniqueness from 'notification_type', 'status' - however missing
|
|
|
|
|
# statuses/notification types won't be represented and the status types need to be simplified/summed up
|
|
|
|
|
# so we can return emails/sms * created, sent, and failed
|
2016-08-23 17:08:53 +01:00
|
|
|
counts = create_zeroed_stats_dicts()
|
2016-07-28 13:34:24 +01:00
|
|
|
for row in statistics:
|
2019-01-09 11:43:40 +00:00
|
|
|
# any row could be null, if the service either has no notifications in the notifications table,
|
|
|
|
|
# or no historical data in the ft_notification_status table.
|
|
|
|
|
if row.notification_type:
|
2025-01-10 13:55:01 -05:00
|
|
|
_update_statuses_from_row(
|
|
|
|
|
counts[row.notification_type],
|
|
|
|
|
row,
|
|
|
|
|
)
|
2016-07-28 13:34:24 +01:00
|
|
|
|
2025-01-10 18:18:20 -08:00
|
|
|
if NotificationType.SMS in counts and total_notifications is not None:
|
|
|
|
|
sms_dict = counts[NotificationType.SMS]
|
|
|
|
|
delivered_count = sms_dict[StatisticsType.DELIVERED]
|
|
|
|
|
failed_count = sms_dict[StatisticsType.FAILURE]
|
2025-01-13 13:39:34 -08:00
|
|
|
sms_dict[StatisticsType.PENDING] = calculate_pending_stats(
|
|
|
|
|
delivered_count, failed_count, total_notifications
|
|
|
|
|
)
|
2025-01-10 18:18:20 -08:00
|
|
|
|
2025-01-13 13:39:34 -08:00
|
|
|
return counts
|
2016-07-28 13:34:24 +01:00
|
|
|
|
2025-01-10 18:18:20 -08:00
|
|
|
|
2025-01-13 13:39:34 -08:00
|
|
|
def calculate_pending_stats(delivered_count, failed_count, total_notifications):
|
|
|
|
|
pending_count = total_notifications - (delivered_count + failed_count)
|
|
|
|
|
return max(0, pending_count)
|
2016-07-28 13:34:24 +01:00
|
|
|
|
2025-01-10 18:42:43 -08:00
|
|
|
|
2018-06-20 14:04:38 +01:00
|
|
|
def format_admin_stats(statistics):
|
|
|
|
|
counts = create_stats_dict()
|
|
|
|
|
|
|
|
|
|
for row in statistics:
|
2024-02-21 14:14:45 -05:00
|
|
|
if row.key_type == KeyType.TEST:
|
2023-08-29 14:54:30 -07:00
|
|
|
counts[row.notification_type]["test-key"] += row.count
|
2018-06-20 14:04:38 +01:00
|
|
|
else:
|
2023-08-29 14:54:30 -07:00
|
|
|
counts[row.notification_type]["total"] += row.count
|
|
|
|
|
if row.status in (
|
2024-02-21 13:47:04 -05:00
|
|
|
NotificationStatus.TECHNICAL_FAILURE,
|
|
|
|
|
NotificationStatus.PERMANENT_FAILURE,
|
|
|
|
|
NotificationStatus.TEMPORARY_FAILURE,
|
|
|
|
|
NotificationStatus.VIRUS_SCAN_FAILED,
|
2023-08-29 14:54:30 -07:00
|
|
|
):
|
|
|
|
|
counts[row.notification_type]["failures"][row.status] += row.count
|
2018-06-20 14:04:38 +01:00
|
|
|
|
|
|
|
|
return counts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_stats_dict():
|
|
|
|
|
stats_dict = {}
|
2024-02-09 11:42:53 -05:00
|
|
|
for template in (TemplateType.SMS, TemplateType.EMAIL):
|
2018-06-20 14:04:38 +01:00
|
|
|
stats_dict[template] = {}
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
for status in ("total", "test-key"):
|
2018-06-20 14:04:38 +01:00
|
|
|
stats_dict[template][status] = 0
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
stats_dict[template]["failures"] = {
|
2024-02-21 13:47:04 -05:00
|
|
|
NotificationStatus.TECHNICAL_FAILURE: 0,
|
|
|
|
|
NotificationStatus.PERMANENT_FAILURE: 0,
|
|
|
|
|
NotificationStatus.TEMPORARY_FAILURE: 0,
|
|
|
|
|
NotificationStatus.VIRUS_SCAN_FAILED: 0,
|
2018-06-20 14:04:38 +01:00
|
|
|
}
|
|
|
|
|
return stats_dict
|
|
|
|
|
|
|
|
|
|
|
2017-02-14 17:59:18 +00:00
|
|
|
def format_monthly_template_notification_stats(year, rows):
|
2017-02-20 11:29:15 +00:00
|
|
|
stats = {
|
2023-08-29 14:54:30 -07:00
|
|
|
datetime.strftime(date, "%Y-%m"): {}
|
|
|
|
|
for date in [datetime(year, month, 1) for month in range(4, 13)]
|
|
|
|
|
+ [datetime(year + 1, month, 1) for month in range(1, 4)]
|
2017-02-14 17:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for row in rows:
|
2023-08-29 14:54:30 -07:00
|
|
|
formatted_month = row.month.strftime("%Y-%m")
|
2017-02-20 11:29:15 +00:00
|
|
|
if str(row.template_id) not in stats[formatted_month]:
|
|
|
|
|
stats[formatted_month][str(row.template_id)] = {
|
2017-02-14 17:59:18 +00:00
|
|
|
"name": row.name,
|
2017-02-20 11:29:15 +00:00
|
|
|
"type": row.template_type,
|
2024-02-21 12:35:18 -05:00
|
|
|
"counts": dict.fromkeys(list(NotificationStatus), 0),
|
2017-02-14 17:59:18 +00:00
|
|
|
}
|
2017-02-20 11:29:15 +00:00
|
|
|
stats[formatted_month][str(row.template_id)]["counts"][row.status] += row.count
|
2017-02-14 17:59:18 +00:00
|
|
|
|
2017-02-20 11:29:15 +00:00
|
|
|
return stats
|
2017-02-14 17:59:18 +00:00
|
|
|
|
|
|
|
|
|
2016-08-23 17:08:53 +01:00
|
|
|
def create_zeroed_stats_dicts():
|
2016-07-28 13:34:24 +01:00
|
|
|
return {
|
2024-02-21 13:18:33 -05:00
|
|
|
template_type: {status: 0 for status in StatisticsType}
|
2024-02-08 17:15:03 -05:00
|
|
|
for template_type in (TemplateType.SMS, TemplateType.EMAIL)
|
2016-07-28 13:34:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-01-10 16:07:49 -05:00
|
|
|
def _update_statuses_from_row(update_dict, row):
|
2024-02-21 12:35:18 -05:00
|
|
|
if row.status != NotificationStatus.CANCELLED:
|
2024-02-21 13:18:33 -05:00
|
|
|
update_dict[StatisticsType.REQUESTED] += row.count
|
2024-02-21 12:35:18 -05:00
|
|
|
if row.status in (NotificationStatus.DELIVERED, NotificationStatus.SENT):
|
2024-02-21 13:18:33 -05:00
|
|
|
update_dict[StatisticsType.DELIVERED] += row.count
|
2025-01-10 18:41:09 -08:00
|
|
|
elif row.status in (
|
2024-02-21 12:35:18 -05:00
|
|
|
NotificationStatus.FAILED,
|
|
|
|
|
NotificationStatus.TECHNICAL_FAILURE,
|
|
|
|
|
NotificationStatus.TEMPORARY_FAILURE,
|
|
|
|
|
NotificationStatus.PERMANENT_FAILURE,
|
|
|
|
|
NotificationStatus.VALIDATION_FAILED,
|
|
|
|
|
NotificationStatus.VIRUS_SCAN_FAILED,
|
2023-08-29 14:54:30 -07:00
|
|
|
):
|
2024-02-22 09:33:36 -05:00
|
|
|
update_dict[StatisticsType.FAILURE] += row.count
|
2018-06-28 15:10:14 +01:00
|
|
|
|
2025-01-10 13:55:01 -05:00
|
|
|
|
2018-06-28 15:10:14 +01:00
|
|
|
def create_empty_monthly_notification_status_stats_dict(year):
|
|
|
|
|
utc_month_starts = get_months_for_financial_year(year)
|
|
|
|
|
# nested dicts - data[month][template type][status] = count
|
|
|
|
|
return {
|
2023-08-29 14:54:30 -07:00
|
|
|
start.strftime("%Y-%m"): {
|
2024-02-08 17:15:03 -05:00
|
|
|
template_type: defaultdict(int)
|
|
|
|
|
for template_type in (TemplateType.SMS, TemplateType.EMAIL)
|
2018-06-28 15:10:14 +01:00
|
|
|
}
|
|
|
|
|
for start in utc_month_starts
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_monthly_notification_status_stats(data, stats):
|
|
|
|
|
for row in stats:
|
2023-08-29 14:54:30 -07:00
|
|
|
month = row.month.strftime("%Y-%m")
|
2018-07-03 14:36:41 +01:00
|
|
|
data[month][row.notification_type][row.notification_status] += row.count
|
2024-05-25 20:59:08 -06:00
|
|
|
data[month][row.notification_type][StatisticsType.REQUESTED] += row.count
|
2018-06-28 15:10:14 +01:00
|
|
|
return data
|