From 669db0b4ca62763f3f1fc644ed1c0ff4f2976ffa Mon Sep 17 00:00:00 2001 From: Pea Tyczynska Date: Thu, 25 Apr 2019 18:09:33 +0100 Subject: [PATCH 1/4] New db query that returns data about live services This data includes service and org name, consent to research, contact details and both intended and factual notifications volumes by notification type. This query was created to get data for a csv report for our platform admins. --- app/dao/services_dao.py | 82 +++++++++++++++++++++++++++++- app/service/rest.py | 7 +++ tests/app/dao/test_services_dao.py | 29 +++++++++++ tests/app/service/test_rest.py | 4 ++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 5c1da4d68..e72e359e2 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -2,7 +2,7 @@ import uuid from datetime import date, datetime, timedelta from notifications_utils.statsd_decorators import statsd -from sqlalchemy import asc, func +from sqlalchemy import asc, func, case from sqlalchemy.orm import joinedload from flask import current_app @@ -21,11 +21,13 @@ from app.dao.template_folder_dao import dao_get_valid_template_folders_by_id from app.models import ( AnnualBilling, ApiKey, + FactBilling, InboundNumber, InvitedUser, Job, Notification, NotificationHistory, + Organisation, Permission, Service, ServicePermission, @@ -72,6 +74,84 @@ def dao_count_live_services(): ).count() +def dao_fetch_live_services_data(): + data = db.session.query( + Service.id, + Organisation.name.label("organisation_name"), + Service.name.label("service_name"), + Service.consent_to_research, + Service.go_live_user_id, + User.name.label('user_name'), + User.email_address, + User.mobile_number, + Service.go_live_at.label("live_date"), + Service.volume_sms, + Service.volume_email, + Service.volume_letter, + case([ + (FactBilling.notification_type == 'email', func.sum(FactBilling.notifications_sent)) + ], else_=0).label("email_totals"), + case([ + (FactBilling.notification_type == 'sms', func.sum(FactBilling.notifications_sent)) + ], else_=0).label("sms_totals"), + case([ + (FactBilling.notification_type == 'letter', func.sum(FactBilling.notifications_sent)) + ], else_=0).label("letter_totals"), + ).outerjoin( + Service.organisation + ).outerjoin( + FactBilling, Service.id == FactBilling.service_id + ).outerjoin( + User, Service.go_live_user_id == User.id + ).group_by( + Service.id, + Organisation.name, + Service.name, + Service.consent_to_research, + Service.go_live_user_id, + User.name, + User.email_address, + User.mobile_number, + Service.go_live_at, + Service.volume_sms, + Service.volume_email, + Service.volume_letter, + FactBilling.notification_type + ).all() + results = [] + for row in data: + is_service_in_list = None + i = 0 + while i < len(results): + if results[i]["service_id"] == row.id: + is_service_in_list = i + break + else: + i += 1 + if is_service_in_list is not None: + results[is_service_in_list]["email_totals"] += row.email_totals + results[is_service_in_list]["sms_totals"] += row.sms_totals + results[is_service_in_list]["letter_totals"] += row.letter_totals + else: + results.append({ + "service_id": row.id, + "service_name": row.service_name, + "organisation_name": row.organisation_name, + "consent_to_research": row.consent_to_research, + "contact_name": row.user_name, + "contact_email": row.email_address, + "contact_mobile": row.mobile_number, + "live_date": row.live_date, + "sms_volume_intent": row.volume_sms, + "email_volume_intent": row.volume_email, + "letter_volume_intent": row.volume_letter, + "sms_totals": row.sms_totals, + "email_totals": row.email_totals, + "letter_totals": row.letter_totals, + }) + return results + + def dao_fetch_service_by_id(service_id, only_active=False): query = Service.query.filter_by( id=service_id diff --git a/app/service/rest.py b/app/service/rest.py index 828dcfdaa..8b2be1818 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -49,6 +49,7 @@ from app.dao.services_dao import ( dao_create_service, dao_fetch_all_services, dao_fetch_all_services_by_user, + dao_fetch_live_services_data, dao_fetch_service_by_id, dao_fetch_todays_stats_for_service, dao_fetch_todays_stats_for_all_services, @@ -158,6 +159,12 @@ def get_services(): return jsonify(data=data) +@service_blueprint.route('/live-services-data', methods=['GET']) +def get_live_services_data(): + data = dao_fetch_live_services_data() + return jsonify(data=data) + + @service_blueprint.route('/', methods=['GET']) def get_service_by_id(service_id): if request.args.get('detailed') == 'True': diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index e00b7ebd0..70fd265dc 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -12,12 +12,14 @@ from app.dao.inbound_numbers_dao import ( dao_get_available_inbound_numbers, dao_set_inbound_number_active_flag ) +from app.dao.organisation_dao import dao_add_service_to_organisation from app.dao.service_permissions_dao import dao_add_service_permission, dao_remove_service_permission from app.dao.services_dao import ( dao_create_service, dao_add_user_to_service, dao_remove_user_from_service, dao_fetch_all_services, + dao_fetch_live_services_data, dao_fetch_service_by_id, dao_fetch_all_services_by_user, dao_update_service, @@ -57,7 +59,9 @@ from app.models import ( user_folder_permissions, ) from tests.app.db import ( + create_ft_billing, create_inbound_number, + create_organisation, create_user, create_service, create_service_with_inbound_number, @@ -380,6 +384,31 @@ def test_get_all_user_services_should_return_empty_list_if_no_services_for_user( assert len(dao_fetch_all_services_by_user(user.id)) == 0 +def test_dao_fetch_live_services_data(sample_user, mock): + org = create_organisation() + service = create_service(go_live_user=sample_user) + template = create_template(service=service) + create_service(service_name='second', go_live_user=sample_user) + template2 = create_template(service=service, template_type='email') + dao_add_service_to_organisation(service=service, organisation_id=org.id) + create_ft_billing(bst_date='2019-04-20', notification_type='sms', template=template, service=service) + create_ft_billing(bst_date='2019-04-20', notification_type='email', template=template2, + service=service) + + results = dao_fetch_live_services_data() + assert len(results) == 2 + assert {'service_id': mock.ANY, 'service_name': 'Sample service', 'organisation_name': 'test_org_1', + 'consent_to_research': None, 'contact_name': 'Test User', + 'contact_email': 'notify@digital.cabinet-office.gov.uk', 'contact_mobile': '+447700900986', + 'live_date': None, 'sms_volume_intent': None, 'email_volume_intent': None, + 'letter_volume_intent': None, 'sms_totals': 1, 'email_totals': 1, 'letter_totals': 0} in results + assert {'service_id': mock.ANY, 'service_name': 'second', 'organisation_name': None, 'consent_to_research': None, + 'contact_name': 'Test User', 'contact_email': 'notify@digital.cabinet-office.gov.uk', + 'contact_mobile': '+447700900986', 'live_date': None, 'sms_volume_intent': None, + 'email_volume_intent': None, 'letter_volume_intent': None, + 'sms_totals': 0, 'email_totals': 0, 'letter_totals': 0} in results + + def test_get_service_by_id_returns_none_if_no_service(notify_db): with pytest.raises(NoResultFound) as e: dao_fetch_service_by_id(str(uuid.uuid4())) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index d447ee559..446445ffb 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -136,6 +136,10 @@ def test_get_service_list_should_return_empty_list_if_no_services(admin_request) assert len(json_resp['data']) == 0 +def test_get_live_services_data(): + pass + + def test_get_service_by_id(admin_request, sample_service): json_resp = admin_request.get('service.get_service_by_id', service_id=sample_service.id) assert json_resp['data']['name'] == sample_service.name From c15d7878fc9380c023b8d49117cb7dc349019c71 Mon Sep 17 00:00:00 2001 From: Pea Tyczynska Date: Mon, 29 Apr 2019 15:49:12 +0100 Subject: [PATCH 2/4] Only include ft_billing data for current financial year Test get_live_services_data endpoint Expand dao_fetch_live_services_data test with more ft_billing records --- app/dao/date_util.py | 12 ++++++++++-- app/dao/services_dao.py | 16 +++++++++++----- tests/app/dao/test_services_dao.py | 22 +++++++++++++++++----- tests/app/service/test_rest.py | 25 +++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/app/dao/date_util.py b/app/dao/date_util.py index f9533a826..7a77f9562 100644 --- a/app/dao/date_util.py +++ b/app/dao/date_util.py @@ -7,8 +7,8 @@ import pytz def get_months_for_financial_year(year): return [ convert_bst_to_utc(month) for month in ( - get_months_for_year(4, 13, year) + - get_months_for_year(1, 4, year + 1) + get_months_for_year(4, 13, year) + + get_months_for_year(1, 4, year + 1) ) if convert_bst_to_utc(month) < datetime.now() ] @@ -22,6 +22,14 @@ def get_financial_year(year): return get_april_fools(year), get_april_fools(year + 1) - timedelta(microseconds=1) +def get_current_financial_year(): + now = datetime.utcnow() + current_month = int(now.strftime('%-m')) + current_year = int(now.strftime('%Y')) + year = current_year if current_month > 3 else current_year - 1 + return get_financial_year(year) + + def get_april_fools(year): """ This function converts the start of the financial year April 1, 00:00 as BST (British Standard Time) to UTC, diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index e72e359e2..da71bf595 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -7,6 +7,7 @@ from sqlalchemy.orm import joinedload from flask import current_app from app import db +from app.dao.date_util import get_current_financial_year from app.dao.dao_utils import ( transactional, version_class, @@ -75,6 +76,11 @@ def dao_count_live_services(): def dao_fetch_live_services_data(): + year_start_date, year_end_date = get_current_financial_year() + this_year_ft_billing = FactBilling.query.filter( + FactBilling.bst_date >= year_start_date, + FactBilling.bst_date <= year_end_date, + ).subquery() data = db.session.query( Service.id, Organisation.name.label("organisation_name"), @@ -89,18 +95,18 @@ def dao_fetch_live_services_data(): Service.volume_email, Service.volume_letter, case([ - (FactBilling.notification_type == 'email', func.sum(FactBilling.notifications_sent)) + (this_year_ft_billing.c.notification_type == 'email', func.sum(this_year_ft_billing.c.notifications_sent)) ], else_=0).label("email_totals"), case([ - (FactBilling.notification_type == 'sms', func.sum(FactBilling.notifications_sent)) + (this_year_ft_billing.c.notification_type == 'sms', func.sum(this_year_ft_billing.c.notifications_sent)) ], else_=0).label("sms_totals"), case([ - (FactBilling.notification_type == 'letter', func.sum(FactBilling.notifications_sent)) + (this_year_ft_billing.c.notification_type == 'letter', func.sum(this_year_ft_billing.c.notifications_sent)) ], else_=0).label("letter_totals"), ).outerjoin( Service.organisation ).outerjoin( - FactBilling, Service.id == FactBilling.service_id + this_year_ft_billing, Service.id == this_year_ft_billing.c.service_id ).outerjoin( User, Service.go_live_user_id == User.id ).group_by( @@ -116,7 +122,7 @@ def dao_fetch_live_services_data(): Service.volume_sms, Service.volume_email, Service.volume_letter, - FactBilling.notification_type + this_year_ft_billing.c.notification_type ).all() results = [] for row in data: diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 70fd265dc..4b559a30d 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -384,28 +384,40 @@ def test_get_all_user_services_should_return_empty_list_if_no_services_for_user( assert len(dao_fetch_all_services_by_user(user.id)) == 0 +@freeze_time('2019-04-23T10:00:00') def test_dao_fetch_live_services_data(sample_user, mock): org = create_organisation() service = create_service(go_live_user=sample_user) template = create_template(service=service) - create_service(service_name='second', go_live_user=sample_user) + service_2 = create_service(service_name='second', go_live_user=sample_user) + create_service(service_name='third') template2 = create_template(service=service, template_type='email') + template_letter_1 = create_template(service=service, template_type='letter') + template_letter_2 = create_template(service=service_2, template_type='letter') dao_add_service_to_organisation(service=service, organisation_id=org.id) create_ft_billing(bst_date='2019-04-20', notification_type='sms', template=template, service=service) - create_ft_billing(bst_date='2019-04-20', notification_type='email', template=template2, - service=service) + create_ft_billing(bst_date='2019-04-21', notification_type='sms', template=template, service=service) + create_ft_billing(bst_date='2018-04-20', notification_type='sms', template=template, service=service) + create_ft_billing(bst_date='2019-04-20', notification_type='email', template=template2, service=service) + create_ft_billing(bst_date='2019-04-15', notification_type='letter', template=template_letter_1, service=service) + create_ft_billing(bst_date='2019-04-16', notification_type='letter', template=template_letter_2, service=service_2) results = dao_fetch_live_services_data() - assert len(results) == 2 + assert len(results) == 3 assert {'service_id': mock.ANY, 'service_name': 'Sample service', 'organisation_name': 'test_org_1', 'consent_to_research': None, 'contact_name': 'Test User', 'contact_email': 'notify@digital.cabinet-office.gov.uk', 'contact_mobile': '+447700900986', 'live_date': None, 'sms_volume_intent': None, 'email_volume_intent': None, - 'letter_volume_intent': None, 'sms_totals': 1, 'email_totals': 1, 'letter_totals': 0} in results + 'letter_volume_intent': None, 'sms_totals': 2, 'email_totals': 1, 'letter_totals': 1} in results assert {'service_id': mock.ANY, 'service_name': 'second', 'organisation_name': None, 'consent_to_research': None, 'contact_name': 'Test User', 'contact_email': 'notify@digital.cabinet-office.gov.uk', 'contact_mobile': '+447700900986', 'live_date': None, 'sms_volume_intent': None, 'email_volume_intent': None, 'letter_volume_intent': None, + 'sms_totals': 0, 'email_totals': 0, 'letter_totals': 1} in results + assert {'service_id': mock.ANY, 'service_name': 'third', 'organisation_name': None, 'consent_to_research': None, + 'contact_name': None, 'contact_email': None, + 'contact_mobile': None, 'live_date': None, 'sms_volume_intent': None, + 'email_volume_intent': None, 'letter_volume_intent': None, 'sms_totals': 0, 'email_totals': 0, 'letter_totals': 0} in results diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 446445ffb..4d93585d7 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -35,6 +35,7 @@ from tests.app.conftest import ( sample_notification_with_job ) from tests.app.db import ( + create_ft_billing, create_ft_notification_status, create_service, create_service_with_inbound_number, @@ -136,8 +137,28 @@ def test_get_service_list_should_return_empty_list_if_no_services(admin_request) assert len(json_resp['data']) == 0 -def test_get_live_services_data(): - pass +def test_get_live_services_data(sample_user, admin_request, mock): + org = create_organisation() + service = create_service(go_live_user=sample_user) + template = create_template(service=service) + create_service(service_name='second', go_live_user=sample_user) + template2 = create_template(service=service, template_type='email') + dao_add_service_to_organisation(service=service, organisation_id=org.id) + create_ft_billing(bst_date='2019-04-20', notification_type='sms', template=template, service=service) + create_ft_billing(bst_date='2019-04-20', notification_type='email', template=template2, + service=service) + response = admin_request.get('service.get_live_services_data')["data"] + assert len(response) == 2 + assert {'consent_to_research': None, 'contact_email': 'notify@digital.cabinet-office.gov.uk', + 'contact_mobile': '+447700900986', 'contact_name': 'Test User', 'email_totals': 0, + 'email_volume_intent': None, 'letter_totals': 0, 'letter_volume_intent': None, 'live_date': None, + 'organisation_name': None, 'service_id': mock.ANY, 'service_name': 'second', 'sms_totals': 0, + 'sms_volume_intent': None} in response + assert {'consent_to_research': None, 'contact_email': 'notify@digital.cabinet-office.gov.uk', + 'contact_mobile': '+447700900986', 'contact_name': 'Test User', 'email_totals': 1, + 'email_volume_intent': None, 'letter_totals': 0, 'letter_volume_intent': None, 'live_date': None, + 'organisation_name': 'test_org_1', 'service_id': mock.ANY, 'service_name': 'Sample service', + 'sms_totals': 1, 'sms_volume_intent': None} in response def test_get_service_by_id(admin_request, sample_service): From 382d1de8577cd5f67109b58926f341b6ac2deb0f Mon Sep 17 00:00:00 2001 From: Pea Tyczynska Date: Mon, 29 Apr 2019 16:30:31 +0100 Subject: [PATCH 3/4] Only count_as_live services Add comments to live services dao test for context --- app/dao/services_dao.py | 4 ++++ tests/app/dao/test_services_dao.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index da71bf595..c4f3f5222 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -87,6 +87,7 @@ def dao_fetch_live_services_data(): Service.name.label("service_name"), Service.consent_to_research, Service.go_live_user_id, + Service.count_as_live, User.name.label('user_name'), User.email_address, User.mobile_number, @@ -109,11 +110,14 @@ def dao_fetch_live_services_data(): this_year_ft_billing, Service.id == this_year_ft_billing.c.service_id ).outerjoin( User, Service.go_live_user_id == User.id + ).filter( + Service.count_as_live == True # noqa ).group_by( Service.id, Organisation.name, Service.name, Service.consent_to_research, + Service.count_as_live, Service.go_live_user_id, User.name, User.email_address, diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 4b559a30d..cc22d82c9 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -391,15 +391,21 @@ def test_dao_fetch_live_services_data(sample_user, mock): template = create_template(service=service) service_2 = create_service(service_name='second', go_live_user=sample_user) create_service(service_name='third') + create_service(service_name='not_live', count_as_live=False) template2 = create_template(service=service, template_type='email') template_letter_1 = create_template(service=service, template_type='letter') template_letter_2 = create_template(service=service_2, template_type='letter') dao_add_service_to_organisation(service=service, organisation_id=org.id) + # two sms billing records for 1st service within current financial year: create_ft_billing(bst_date='2019-04-20', notification_type='sms', template=template, service=service) create_ft_billing(bst_date='2019-04-21', notification_type='sms', template=template, service=service) + # one sms billing record for 1st service from previous financial year, should not appear in the result: create_ft_billing(bst_date='2018-04-20', notification_type='sms', template=template, service=service) + # one email billing record for 1st service within current financial year: create_ft_billing(bst_date='2019-04-20', notification_type='email', template=template2, service=service) + # one letter billing record for 1st service within current financial year: create_ft_billing(bst_date='2019-04-15', notification_type='letter', template=template_letter_1, service=service) + # one letter billing record for 2nd service within current financial year: create_ft_billing(bst_date='2019-04-16', notification_type='letter', template=template_letter_2, service=service_2) results = dao_fetch_live_services_data() From 83f39730d0de19dba7c2469d17cda662543e0d42 Mon Sep 17 00:00:00 2001 From: Pea Tyczynska Date: Wed, 1 May 2019 11:04:05 +0100 Subject: [PATCH 4/4] Order results by live date ascending order --- app/dao/services_dao.py | 2 ++ tests/app/dao/test_services_dao.py | 31 ++++++++++++++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index c4f3f5222..4456faaae 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -127,6 +127,8 @@ def dao_fetch_live_services_data(): Service.volume_email, Service.volume_letter, this_year_ft_billing.c.notification_type + ).order_by( + asc(Service.go_live_at) ).all() results = [] for row in data: diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index cc22d82c9..77c93fea8 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -387,10 +387,10 @@ def test_get_all_user_services_should_return_empty_list_if_no_services_for_user( @freeze_time('2019-04-23T10:00:00') def test_dao_fetch_live_services_data(sample_user, mock): org = create_organisation() - service = create_service(go_live_user=sample_user) + service = create_service(go_live_user=sample_user, go_live_at='2014-04-20T10:00:00') template = create_template(service=service) - service_2 = create_service(service_name='second', go_live_user=sample_user) - create_service(service_name='third') + service_2 = create_service(service_name='second', go_live_user=sample_user, go_live_at='2017-04-20T10:00:00') + create_service(service_name='third', go_live_at='2016-04-20T10:00:00') create_service(service_name='not_live', count_as_live=False) template2 = create_template(service=service, template_type='email') template_letter_1 = create_template(service=service, template_type='letter') @@ -410,21 +410,24 @@ def test_dao_fetch_live_services_data(sample_user, mock): results = dao_fetch_live_services_data() assert len(results) == 3 - assert {'service_id': mock.ANY, 'service_name': 'Sample service', 'organisation_name': 'test_org_1', + # checks the results and that they are ordered by date: + assert results == [ + {'service_id': mock.ANY, 'service_name': 'Sample service', 'organisation_name': 'test_org_1', 'consent_to_research': None, 'contact_name': 'Test User', 'contact_email': 'notify@digital.cabinet-office.gov.uk', 'contact_mobile': '+447700900986', - 'live_date': None, 'sms_volume_intent': None, 'email_volume_intent': None, - 'letter_volume_intent': None, 'sms_totals': 2, 'email_totals': 1, 'letter_totals': 1} in results - assert {'service_id': mock.ANY, 'service_name': 'second', 'organisation_name': None, 'consent_to_research': None, - 'contact_name': 'Test User', 'contact_email': 'notify@digital.cabinet-office.gov.uk', - 'contact_mobile': '+447700900986', 'live_date': None, 'sms_volume_intent': None, - 'email_volume_intent': None, 'letter_volume_intent': None, - 'sms_totals': 0, 'email_totals': 0, 'letter_totals': 1} in results - assert {'service_id': mock.ANY, 'service_name': 'third', 'organisation_name': None, 'consent_to_research': None, + 'live_date': datetime(2014, 4, 20, 10, 0), 'sms_volume_intent': None, 'email_volume_intent': None, + 'letter_volume_intent': None, 'sms_totals': 2, 'email_totals': 1, 'letter_totals': 1}, + {'service_id': mock.ANY, 'service_name': 'third', 'organisation_name': None, 'consent_to_research': None, 'contact_name': None, 'contact_email': None, - 'contact_mobile': None, 'live_date': None, 'sms_volume_intent': None, + 'contact_mobile': None, 'live_date': datetime(2016, 4, 20, 10, 0), 'sms_volume_intent': None, 'email_volume_intent': None, 'letter_volume_intent': None, - 'sms_totals': 0, 'email_totals': 0, 'letter_totals': 0} in results + 'sms_totals': 0, 'email_totals': 0, 'letter_totals': 0}, + {'service_id': mock.ANY, 'service_name': 'second', 'organisation_name': None, 'consent_to_research': None, + 'contact_name': 'Test User', 'contact_email': 'notify@digital.cabinet-office.gov.uk', + 'contact_mobile': '+447700900986', 'live_date': datetime(2017, 4, 20, 10, 0), 'sms_volume_intent': None, + 'email_volume_intent': None, 'letter_volume_intent': None, + 'sms_totals': 0, 'email_totals': 0, 'letter_totals': 1} + ] def test_get_service_by_id_returns_none_if_no_service(notify_db):