Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add pending stats to daily stats endpoint #1493

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 82 additions & 10 deletions app/dao/services_dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,14 +455,43 @@ def dao_fetch_stats_for_service_from_days(service_id, start_date, end_date):
start_date = get_midnight_in_utc(start_date)
end_date = get_midnight_in_utc(end_date + timedelta(days=1))

total_substmt = (
select(
func.date_trunc("day", NotificationAllTimeView.created_at).label("day"),
Job.notification_count.label("notification_count"),
)
.join(Job, NotificationAllTimeView.job_id == Job.id)
.where(
NotificationAllTimeView.service_id == service_id,
NotificationAllTimeView.key_type != KeyType.TEST,
NotificationAllTimeView.created_at >= start_date,
NotificationAllTimeView.created_at < end_date,
)
.group_by(
Job.id,
Job.notification_count,
func.date_trunc("day", NotificationAllTimeView.created_at),
)
.subquery()
)

total_stmt = select(
total_substmt.c.day,
func.sum(total_substmt.c.notification_count).label("total_notifications"),
).group_by(total_substmt.c.day)

total_notifications = {
row.day: row.total_notifications for row in db.session.execute(total_stmt).all()
}

stmt = (
select(
NotificationAllTimeView.notification_type,
NotificationAllTimeView.status,
func.date_trunc("day", NotificationAllTimeView.created_at).label("day"),
func.count(NotificationAllTimeView.id).label("count"),
)
.filter(
.where(
NotificationAllTimeView.service_id == service_id,
NotificationAllTimeView.key_type != KeyType.TEST,
NotificationAllTimeView.created_at >= start_date,
Expand All @@ -474,7 +503,10 @@ def dao_fetch_stats_for_service_from_days(service_id, start_date, end_date):
func.date_trunc("day", NotificationAllTimeView.created_at),
)
)
return db.session.execute(stmt).all()

data = db.session.execute(stmt).all()

return total_notifications, data


def dao_fetch_stats_for_service_from_days_for_user(
Expand All @@ -483,15 +515,44 @@ def dao_fetch_stats_for_service_from_days_for_user(
start_date = get_midnight_in_utc(start_date)
end_date = get_midnight_in_utc(end_date + timedelta(days=1))

total_substmt = (
select(
func.date_trunc("day", NotificationAllTimeView.created_at).label("day"),
Job.notification_count.label("notification_count"),
)
.join(Job, NotificationAllTimeView.job_id == Job.id)
.where(
NotificationAllTimeView.service_id == service_id,
NotificationAllTimeView.key_type != KeyType.TEST,
NotificationAllTimeView.created_at >= start_date,
NotificationAllTimeView.created_at < end_date,
NotificationAllTimeView.created_by_id == user_id,
)
.group_by(
Job.id,
Job.notification_count,
func.date_trunc("day", NotificationAllTimeView.created_at),
)
.subquery()
)

total_stmt = select(
total_substmt.c.day,
func.sum(total_substmt.c.notification_count).label("total_notifications"),
).group_by(total_substmt.c.day)

total_notifications = {
row.day: row.total_notifications for row in db.session.execute(total_stmt).all()
}

stmt = (
select(
NotificationAllTimeView.notification_type,
NotificationAllTimeView.status,
func.date_trunc("day", NotificationAllTimeView.created_at).label("day"),
func.count(NotificationAllTimeView.id).label("count"),
)
.select_from(NotificationAllTimeView)
.filter(
.where(
NotificationAllTimeView.service_id == service_id,
NotificationAllTimeView.key_type != KeyType.TEST,
NotificationAllTimeView.created_at >= start_date,
Expand All @@ -504,7 +565,10 @@ def dao_fetch_stats_for_service_from_days_for_user(
func.date_trunc("day", NotificationAllTimeView.created_at),
)
)
return db.session.execute(stmt).scalars().all()

data = db.session.execute(stmt).all()

return total_notifications, data


def dao_fetch_todays_stats_for_all_services(
Expand Down Expand Up @@ -720,7 +784,9 @@ def fetch_notification_stats_for_service_by_month_by_user(
return db.session.execute(stmt).all()


def get_specific_days_stats(data, start_date, days=None, end_date=None):
def get_specific_days_stats(
data, start_date, days=None, end_date=None, total_notifications=None
):
if days is not None and end_date is not None:
raise ValueError("Only set days OR set end_date, not both.")
elif days is not None:
Expand All @@ -731,13 +797,19 @@ def get_specific_days_stats(data, start_date, days=None, end_date=None):
raise ValueError("Either days or end_date must be set.")

grouped_data = {date: [] for date in gen_range} | {
day: [row for row in data if row.day.date() == day]
for day in {item.day.date() for item in data}
day: [row for row in data if row.day == day]
for day in {item.day for item in data}
}

stats = {
day.strftime("%Y-%m-%d"): statistics.format_statistics(rows)
day.strftime("%Y-%m-%d"): statistics.format_statistics(
rows,
total_notifications=(
total_notifications.get(day, 0)
if total_notifications is not None
else None
),
)
for day, rows in grouped_data.items()
}

return stats
1 change: 1 addition & 0 deletions app/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,4 @@ class StatisticsType(StrEnum):
REQUESTED = "requested"
DELIVERED = "delivered"
FAILURE = "failure"
PENDING = "pending"
31 changes: 23 additions & 8 deletions app/service/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,18 @@ def get_service_statistics_for_specific_days(service_id, start, days=1):
end_date = datetime.strptime(start, "%Y-%m-%d")
start_date = end_date - timedelta(days=days - 1)

results = dao_fetch_stats_for_service_from_days(service_id, start_date, end_date)
total_notifications, results = dao_fetch_stats_for_service_from_days(
service_id,
start_date,
end_date,
)

stats = get_specific_days_stats(results, start_date, days=days)
stats = get_specific_days_stats(
results,
start_date,
days=days,
total_notifications=total_notifications,
)

return stats

Expand All @@ -259,12 +268,16 @@ def get_service_statistics_for_specific_days_by_user(
end_date = datetime.strptime(start, "%Y-%m-%d")
start_date = end_date - timedelta(days=days - 1)

results = dao_fetch_stats_for_service_from_days_for_user(
total_notifications, results = dao_fetch_stats_for_service_from_days_for_user(
service_id, start_date, end_date, user_id
)

stats = get_specific_days_stats(results, start_date, days=days)

stats = get_specific_days_stats(
results,
start_date,
days=days,
total_notifications=total_notifications,
)
return stats


Expand Down Expand Up @@ -654,11 +667,11 @@ def get_single_month_notification_stats_by_user(service_id, user_id):
month_year = datetime(year, month, 10, 00, 00, 00)
start_date, end_date = get_month_start_and_end_date_in_utc(month_year)

results = dao_fetch_stats_for_service_from_days_for_user(
total_notifications, results = dao_fetch_stats_for_service_from_days_for_user(
service_id, start_date, end_date, user_id
)

stats = get_specific_days_stats(results, start_date, end_date=end_date)
stats = get_specific_days_stats(results, start_date, end_date=end_date, total_notifications=total_notifications,)
return jsonify(stats)


Expand All @@ -678,7 +691,9 @@ def get_single_month_notification_stats_for_service(service_id):
month_year = datetime(year, month, 10, 00, 00, 00)
start_date, end_date = get_month_start_and_end_date_in_utc(month_year)

results = dao_fetch_stats_for_service_from_days(service_id, start_date, end_date)
__, results = dao_fetch_stats_for_service_from_days(
service_id, start_date, end_date
)

stats = get_specific_days_stats(results, start_date, end_date=end_date)
return jsonify(stats)
Expand Down
28 changes: 25 additions & 3 deletions app/service/statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
from datetime import datetime

from app.dao.date_util import get_months_for_financial_year
from app.enums import KeyType, NotificationStatus, StatisticsType, TemplateType
from app.enums import (
KeyType,
NotificationStatus,
NotificationType,
StatisticsType,
TemplateType,
)


def format_statistics(statistics):
def format_statistics(statistics, total_notifications=None):
# 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
Expand All @@ -14,11 +20,27 @@ def format_statistics(statistics):
# 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:
_update_statuses_from_row(counts[row.notification_type], row)
_update_statuses_from_row(
counts[row.notification_type],
row,
)

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]
sms_dict[StatisticsType.PENDING] = calculate_pending_stats(
delivered_count, failed_count, total_notifications
)

return counts


def calculate_pending_stats(delivered_count, failed_count, total_notifications):
pending_count = total_notifications - (delivered_count + failed_count)
return max(0, pending_count)


def format_admin_stats(statistics):
counts = create_stats_dict()

Expand Down
Loading
Loading