From 5fbad5bd69cdd453900a97efc1a30c44d261fcf5 Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Sat, 25 May 2024 20:59:08 -0600 Subject: [PATCH] Added endpoints for #1006 and #1007 --- app/dao/date_util.py | 5 +++ app/dao/services_dao.py | 54 +++++++++++++++++++++++- app/service/rest.py | 86 +++++++++++++++++++++++++++++++++++---- app/service/statistics.py | 3 +- 4 files changed, 137 insertions(+), 11 deletions(-) diff --git a/app/dao/date_util.py b/app/dao/date_util.py index 7aafd711f..66aadc9df 100644 --- a/app/dao/date_util.py +++ b/app/dao/date_util.py @@ -1,3 +1,4 @@ +import calendar from datetime import date, datetime, time, timedelta @@ -64,3 +65,7 @@ def get_calendar_year_for_datetime(start_date): return year - 1 else: return year + + +def get_number_of_days_for_month(year, month): + return calendar.monthrange(year, month)[1] diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index bc2a5ddf3..f6724f247 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -431,7 +431,6 @@ def dao_fetch_stats_for_service_from_day(service_id, day): # day_date = datetime.strptime(day, '%Y-%m-%d').date() start_date = get_midnight_in_utc(day) end_date = get_midnight_in_utc(day + timedelta(days=1)) - print(start_date) return ( db.session.query( NotificationHistory.notification_type, @@ -452,6 +451,33 @@ def dao_fetch_stats_for_service_from_day(service_id, day): ) +def dao_fetch_stats_for_service_from_day_for_user(service_id, day, user_id): + # today = datetime.now(timezone.utc).date() + # 2024-05-20 + # day_date = datetime.strptime(day, '%Y-%m-%d').date() + start_date = get_midnight_in_utc(day) + end_date = get_midnight_in_utc(day + timedelta(days=1)) + return ( + db.session.query( + NotificationHistory.notification_type, + NotificationHistory.status, + func.count(NotificationHistory.id).label("count"), + ) + .filter( + NotificationHistory.service_id == service_id, + NotificationHistory.key_type != KeyType.TEST, + NotificationHistory.created_at >= start_date, + NotificationHistory.created_at <= end_date, + NotificationHistory.created_by_id == user_id, + ) + .group_by( + NotificationHistory.notification_type, + NotificationHistory.status, + ) + .all() + ) + + def dao_fetch_todays_stats_for_all_services( include_from_test_key=True, only_active=True ): @@ -633,3 +659,29 @@ def get_live_services_with_organization(): ) return query.all() + + +def fetch_notification_stats_for_service_by_month_by_user( + start_date, end_date, service_id, user_id +): + return ( + db.session.query( + func.date_trunc("month", NotificationHistory.created_at).label("month"), + NotificationHistory.notification_type, + (NotificationHistory.status).label("notification_status"), + func.count(NotificationHistory.id).label("count"), + ) + .filter( + NotificationHistory.service_id == service_id, + NotificationHistory.created_at >= start_date, + NotificationHistory.created_at < end_date, + NotificationHistory.key_type != KeyType.TEST, + NotificationHistory.created_by_id == user_id, + ) + .group_by( + func.date_trunc("month", NotificationHistory.created_at).label("month"), + NotificationHistory.notification_type, + NotificationHistory.status, + ) + .all() + ) diff --git a/app/service/rest.py b/app/service/rest.py index 9c0295304..7d8d53c8e 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -17,7 +17,11 @@ from app.dao.api_key_dao import ( save_model_api_key, ) from app.dao.dao_utils import dao_rollback, transaction -from app.dao.date_util import get_calendar_year +from app.dao.date_util import ( + get_calendar_year, + get_month_start_and_end_date_in_utc, + get_number_of_days_for_month, +) from app.dao.fact_notification_status_dao import ( fetch_monthly_template_usage_for_service, fetch_notification_status_for_service_by_month, @@ -64,12 +68,14 @@ from app.dao.services_dao import ( dao_fetch_live_services_data, dao_fetch_service_by_id, dao_fetch_stats_for_service_from_day, + dao_fetch_stats_for_service_from_day_for_user, dao_fetch_todays_stats_for_all_services, dao_fetch_todays_stats_for_service, dao_remove_user_from_service, dao_resume_service, dao_suspend_service, dao_update_service, + fetch_notification_stats_for_service_by_month_by_user, get_services_by_partial_name, ) from app.dao.templates_dao import dao_get_template_by_id @@ -223,20 +229,17 @@ def get_service_statistics_for_specific_days(service_id, start, days=1): if days == 1: stats = {} - stats[start] = { - "value": statistics.format_statistics( - dao_fetch_stats_for_service_from_day(service_id, start_date) - ) - } + stats[start] = statistics.format_statistics( + dao_fetch_stats_for_service_from_day(service_id, start_date) + ) else: stats = {} for d in range(days): new_date = start_date + timedelta(days=d) key = new_date.strftime("%Y-%m-%d") - value = statistics.format_statistics( + stats[key] = statistics.format_statistics( dao_fetch_stats_for_service_from_day(service_id, new_date) ) - stats[key] = {"value": value} return stats @@ -612,6 +615,7 @@ def get_monthly_notification_stats(service_id): stats = fetch_notification_status_for_service_by_month( start_date, end_date, service_id ) + statistics.add_monthly_notification_status_stats(data, stats) now = datetime.utcnow() @@ -624,6 +628,72 @@ def get_monthly_notification_stats(service_id): return jsonify(data=data) +@service_blueprint.route( + "//notifications//monthly", methods=["GET"] +) +def get_monthly_notification_stats_by_user(service_id, user_id): + # check service_id validity + dao_fetch_service_by_id(service_id) + # user = get_user_by_id(user_id=user_id) + + try: + year = int(request.args.get("year", "NaN")) + except ValueError: + raise InvalidRequest("Year must be a number", status_code=400) + + start_date, end_date = get_calendar_year(year) + + data = statistics.create_empty_monthly_notification_status_stats_dict(year) + + stats = fetch_notification_stats_for_service_by_month_by_user( + start_date, end_date, service_id, user_id + ) + + statistics.add_monthly_notification_status_stats(data, stats) + + now = datetime.utcnow() + if end_date > now: + todays_deltas = fetch_notification_status_for_service_for_day( + now, service_id=service_id + ) + statistics.add_monthly_notification_status_stats(data, todays_deltas) + + return jsonify(data=data) + + +@service_blueprint.route( + "//notifications//month", methods=["GET"] +) +def get_single_month_notification_stats_by_user(service_id, user_id): + # check service_id validity + dao_fetch_service_by_id(service_id) + + try: + month = int(request.args.get("month", "NaN")) + year = int(request.args.get("year", "NaN")) + except ValueError: + raise InvalidRequest( + "Both a month and year are required as numbers", status_code=400 + ) + + month_year = datetime(year, month, 10, 00, 00, 00) + days = get_number_of_days_for_month(year, month) + start_date, end_date = get_month_start_and_end_date_in_utc(month_year) + + stats = {} + for d in range(days): + new_date = start_date + timedelta(days=d) + if new_date <= end_date: + key = new_date.strftime("%Y-%m-%d") + stats[key] = statistics.format_statistics( + dao_fetch_stats_for_service_from_day_for_user( + service_id, new_date, user_id + ) + ) + + return jsonify(stats) + + def get_detailed_service(service_id, today_only=False): service = dao_fetch_service_by_id(service_id) diff --git a/app/service/statistics.py b/app/service/statistics.py index 90b933960..a6b58e067 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -113,7 +113,6 @@ def create_empty_monthly_notification_status_stats_dict(year): def add_monthly_notification_status_stats(data, stats): for row in stats: month = row.month.strftime("%Y-%m") - data[month][row.notification_type][row.notification_status] += row.count - + data[month][row.notification_type][StatisticsType.REQUESTED] += row.count return data