Skip to content

Commit b3f51e9

Browse files
Merge pull request #1192 from uktrade/KLS-1067-market-guides-update-notification
KLS 1067 market guides update notification
2 parents 59e7ccf + 2410675 commit b3f51e9

File tree

5 files changed

+153
-42
lines changed

5 files changed

+153
-42
lines changed

conf/settings.py

+8
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@
207207
'VERSION': os.environ.get('GIT_TAG', 'dev'),
208208
}
209209

210+
APP_ENVIRONMENT = env.str('APP_ENVIRONMENT', 'dev')
211+
SENTRY_ENVIRONMENT = env.str('SENTRY_ENVIRONMENT', APP_ENVIRONMENT)
210212
# Sentry
211213
if env.str('SENTRY_DSN', ''):
212214
sentry_sdk.init(
@@ -288,6 +290,8 @@
288290
'COLLABORATOR_INVITE_SUBJECT', 'Confirm you’ve been added to {company_name}’s Find a buyer profile'
289291
)
290292
GREAT_MARKETGUIDES_TEAMS_CHANNEL_EMAIL = env.str('GREAT_MARKETGUIDES_TEAMS_CHANNEL_EMAIL')
293+
GREAT_MARKETGUIDES_REVIEW_PERIOD_DAYS = env.int('GREAT_MARKETGUIDES_REVIEW_PERIOD_DAYS', 10)
294+
291295

292296
# Public storage for company profile logo
293297
STORAGE_CLASSES = {
@@ -396,6 +400,10 @@
396400
'GOVNOTIFY_ACCOUNT_OWNERSHIP_TRANSFER_TEMPLATE_ID', 'eecd214c-8072-4d16-87ea-4283d5925f16'
397401
)
398402

403+
GOVNOTIFY_GREAT_MARKETGUIDES_REVIEW_REQUEST_TEMPLATE_ID = env.str(
404+
'GOVNOTIFY_GREAT_MARKETGUIDES_REVIEW_REQUEST_TEMPLATE_ID', '2c49305b-6632-44fa-8d7d-830086363258'
405+
)
406+
399407
# Duplicate companies notification
400408

401409
GOVNOTIFY_DUPLICATE_COMPANIES = env.str('GOVNOTIFY_DUPLICATE_COMPANIES', '9d93b6c9-ff75-4797-b841-2f7a6c78a277')

dataservices/management/commands/helpers.py

+40-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import io
2-
from datetime import datetime
2+
from datetime import datetime, timedelta
3+
from sys import stdout
34
from zipfile import ZipFile
45

56
import pandas as pd
@@ -45,6 +46,35 @@ def send_ingest_error_notify_email(view_name, error_details):
4546
)
4647

4748

49+
def send_review_request_message(view_name):
50+
instance, _created = Metadata.objects.get_or_create(view_name=view_name)
51+
last_release = datetime.strptime(instance.data['source']['last_release'], '%Y-%m-%dT%H:%M:%S')
52+
53+
try:
54+
last_notification_sent = datetime.strptime(
55+
instance.data['review_process']['notification_sent'], '%Y-%m-%dT%H:%M:%S'
56+
)
57+
except KeyError:
58+
instance.data['review_process'] = {'notification_sent': None}
59+
last_notification_sent = None
60+
61+
if last_notification_sent is None or (((last_notification_sent.timestamp() - last_release.timestamp())) < 0):
62+
notifications_client().send_email_notification(
63+
email_address=settings.GREAT_MARKETGUIDES_TEAMS_CHANNEL_EMAIL,
64+
template_id=settings.GOVNOTIFY_GREAT_MARKETGUIDES_REVIEW_REQUEST_TEMPLATE_ID,
65+
personalisation={
66+
'view_name': view_name,
67+
'review_url': 'https://great.staging.uktrade.digital/markets/',
68+
'release_date': (
69+
last_release + timedelta(days=settings.GREAT_MARKETGUIDES_REVIEW_PERIOD_DAYS)
70+
).strftime('%d/%m/%Y'),
71+
},
72+
)
73+
stdout.write(f"Sent review request notification for {view_name}")
74+
instance.data['review_process']['notification_sent'] = datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
75+
instance.save()
76+
77+
4878
class MarketGuidesDataIngestionCommand(BaseCommand):
4979
engine = sa.create_engine(settings.DATA_WORKSPACE_DATASETS_URL, execution_options={'stream_results': True})
5080

@@ -82,7 +112,15 @@ def should_ingestion_run(self, view_name, table_name):
82112
if great_metadata is not None:
83113
great_metadata_date = datetime.strptime(great_metadata, '%Y-%m-%dT%H:%M:%S').date()
84114
if swapped_date > great_metadata_date:
85-
return True
115+
if settings.APP_ENVIRONMENT != 'production' or (
116+
settings.APP_ENVIRONMENT == 'production'
117+
and datetime.now().date()
118+
> (swapped_date + timedelta(days=settings.GREAT_MARKETGUIDES_REVIEW_PERIOD_DAYS))
119+
):
120+
self.stdout.write(
121+
self.style.SUCCESS(f'Importing {view_name} data into {settings.APP_ENVIRONMENT} env.')
122+
)
123+
return True
86124

87125
return False
88126

dataservices/management/commands/import_market_guides_data.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
from django.conf import settings
12
from django.core.management import BaseCommand, call_command
23

3-
from dataservices.management.commands.helpers import MarketGuidesDataIngestionCommand, send_ingest_error_notify_email
4+
from dataservices.management.commands.helpers import (
5+
MarketGuidesDataIngestionCommand,
6+
send_ingest_error_notify_email,
7+
send_review_request_message,
8+
)
49

510

611
class Command(BaseCommand):
@@ -37,6 +42,10 @@ def handle(self, *args, **options):
3742
try:
3843
call_command(command_name, **options)
3944
call_command('import_metadata_source_data', table=table_view_names['table_name'])
45+
46+
if settings.APP_ENVIRONMENT == 'staging':
47+
send_review_request_message(table_view_names['view_name'])
48+
4049
self.stdout.write(self.style.SUCCESS(f'Finished import for {table_view_names["view_name"]}'))
4150
except Exception as e:
4251
self.stderr.write(self.style.ERROR(f'Failed import for {table_view_names["view_name"]}'))

dataservices/management/commands/tests/test_import_data.py

+57-39
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import datetime
21
import json
32
import re
3+
from datetime import date, datetime
44
from itertools import cycle, islice
55
from unittest import mock
66

@@ -9,6 +9,7 @@
99
import sqlalchemy
1010
from django.core import management
1111
from django.test import override_settings
12+
from freezegun import freeze_time
1213
from import_export import results
1314
from sqlalchemy import TIMESTAMP, Column, MetaData, String, Table
1415

@@ -301,10 +302,10 @@ def metadata_last_release_raw_data():
301302
return {
302303
'table_name': 'trade__uk_goods_nsa trade__uk_services_nsa trade__uk_totals_sa xxx'.split(),
303304
'last_release': [
304-
datetime.date(2018, 12, 30),
305-
datetime.date(2019, 12, 30),
306-
datetime.date(2020, 12, 30),
307-
datetime.date(2021, 12, 30),
305+
date(2018, 12, 30),
306+
date(2019, 12, 30),
307+
date(2020, 12, 30),
308+
date(2021, 12, 30),
308309
],
309310
}
310311

@@ -423,25 +424,34 @@ def test_import_metadata_source_data_filter_tables():
423424

424425

425426
@pytest.mark.django_db
426-
@mock.patch('dataservices.management.commands.helpers.MarketGuidesDataIngestionCommand.should_ingestion_run')
427+
@pytest.mark.parametrize(
428+
'env, review_requested_x_times',
429+
[('dev', 0), ('staging', 3), ('uat', 0), ('production', 0)],
430+
)
427431
@mock.patch('dataservices.management.commands.import_market_guides_data.call_command')
428-
def test_import_market_guides_data(mock_call_command, mock_should_run):
429-
command_list = [
430-
'import_uk_total_trade_data',
431-
'import_uk_trade_in_goods_data',
432-
'import_uk_trade_in_services_data',
433-
]
434-
mock_should_run.return_value = False
435-
management.call_command('import_market_guides_data', '--write')
436-
assert mock_call_command.call_count == 0
437-
438-
mock_should_run.return_value = True
439-
management.call_command('import_market_guides_data', '--write')
440-
assert mock_call_command.call_count == 6
441-
442-
for command in command_list:
443-
assert command in str(mock_call_command.call_args_list)
444-
assert 'write=True' in str(mock_call_command.call_args_list)
432+
@mock.patch('dataservices.management.commands.helpers.MarketGuidesDataIngestionCommand.should_ingestion_run')
433+
@mock.patch('dataservices.management.commands.import_market_guides_data.send_review_request_message')
434+
def test_import_market_guides_data(
435+
mock_send_review_request, mock_should_run, mock_call_command, env, review_requested_x_times
436+
):
437+
with override_settings(APP_ENVIRONMENT=env):
438+
command_list = [
439+
'import_uk_total_trade_data',
440+
'import_uk_trade_in_goods_data',
441+
'import_uk_trade_in_services_data',
442+
]
443+
mock_should_run.return_value = False
444+
management.call_command('import_market_guides_data', '--write')
445+
assert mock_call_command.call_count == 0
446+
447+
mock_should_run.return_value = True
448+
management.call_command('import_market_guides_data', '--write')
449+
assert mock_call_command.call_count == 6
450+
assert mock_send_review_request.call_count == review_requested_x_times
451+
452+
for command in command_list:
453+
assert command in str(mock_call_command.call_args_list)
454+
assert 'write=True' in str(mock_call_command.call_args_list)
445455

446456

447457
@pytest.mark.django_db
@@ -511,24 +521,32 @@ def workspace_data():
511521
'trade__uk_goods_nsa',
512522
],
513523
'source_data_modified_utc': [
514-
datetime.datetime(2023, 6, 10),
524+
datetime(2023, 9, 3),
515525
],
516-
'dataflow_swapped_tables_utc': [datetime.datetime(2023, 6, 10)],
526+
'dataflow_swapped_tables_utc': [datetime(2023, 9, 3)],
517527
}
518528

519529

520530
@pytest.mark.parametrize(
521-
'view_date, expected',
522-
[('2023-04-27T00:00:00', True), ('2023-06-10T00:00:00', False), ('2023-07-01T00:00:00', False)],
531+
'env, view_date, swap_date, expected',
532+
[
533+
('staging', datetime(2023, 9, 12), datetime(2023, 9, 13), True),
534+
('staging', datetime(2023, 9, 14), datetime(2023, 9, 13), False),
535+
('production', datetime(2023, 9, 6), datetime(2023, 9, 6), False),
536+
('production', datetime(2023, 9, 1), datetime(2023, 9, 2), True),
537+
],
523538
)
524539
@mock.patch('dataservices.management.commands.helpers.MarketGuidesDataIngestionCommand.get_view_metadata')
525540
@mock.patch('dataservices.management.commands.helpers.MarketGuidesDataIngestionCommand.get_dataflow_metadata')
526-
def test_helper_should_ingest_run(dataflow_mock, view_mock, view_date, expected, workspace_data):
527-
m = MarketGuidesDataIngestionCommand()
528-
dataflow_mock.return_value = pd.DataFrame(workspace_data)
529-
view_mock.return_value = view_date
530-
actual = m.should_ingestion_run('UKMarketTrendsView', 'trade__uk_goods_nsa')
531-
assert actual == expected
541+
@freeze_time('2023-09-14T15:21:10')
542+
def test_helper_should_ingest_run(dataflow_mock, view_mock, env, view_date, swap_date, expected, workspace_data):
543+
with override_settings(APP_ENVIRONMENT=env):
544+
m = MarketGuidesDataIngestionCommand()
545+
workspace_data['dataflow_swapped_tables_utc'] = swap_date
546+
dataflow_mock.return_value = pd.DataFrame(workspace_data)
547+
view_mock.return_value = view_date.strftime('%Y-%m-%dT%H:%M:%S')
548+
actual = m.should_ingestion_run('UKMarketTrendsView', 'trade__uk_goods_nsa')
549+
assert actual == expected
532550

533551

534552
@pytest.mark.django_db
@@ -576,22 +594,22 @@ def test_helper_get_dataflow_metadata():
576594
m.engine.execute(
577595
tbl.insert().values(
578596
table_name='trade__uk_goods_nsa',
579-
source_data_modified_utc=datetime.date(2023, 1, 1),
580-
dataflow_swapped_tables_utc=datetime.date(2023, 1, 1),
597+
source_data_modified_utc=date(2023, 1, 1),
598+
dataflow_swapped_tables_utc=date(2023, 1, 1),
581599
)
582600
)
583601
m.engine.execute(
584602
tbl.insert().values(
585603
table_name='trade__uk_goods_nsa',
586-
source_data_modified_utc=datetime.date(2023, 4, 1),
587-
dataflow_swapped_tables_utc=datetime.date(2023, 4, 1),
604+
source_data_modified_utc=date(2023, 4, 1),
605+
dataflow_swapped_tables_utc=date(2023, 4, 1),
588606
)
589607
)
590608
m.engine.execute(
591609
tbl.insert().values(
592610
table_name='trade__uk_services_nsa',
593-
source_data_modified_utc=datetime.date(2023, 6, 1),
594-
dataflow_swapped_tables_utc=datetime.date(2023, 6, 1),
611+
source_data_modified_utc=date(2023, 6, 1),
612+
dataflow_swapped_tables_utc=date(2023, 6, 1),
595613
)
596614
)
597615
result = m.get_dataflow_metadata('trade__uk_goods_nsa')

dataservices/tests/test_helpers.py

+38
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import re
2+
from datetime import datetime, timedelta
23
from unittest import mock
34

45
import pytest
56
from django.conf import settings
7+
from freezegun import freeze_time
68

79
from dataservices import helpers, models
810
from dataservices.management.commands import helpers as dmch
@@ -196,3 +198,39 @@ def test_notify_error_message(mock_notify):
196198
},
197199
)
198200
assert mock_notify.call_count == 1
201+
202+
203+
@freeze_time('2023-09-13T15:21:10')
204+
@pytest.mark.django_db
205+
@mock.patch('dataservices.management.commands.helpers.notifications_client')
206+
@pytest.mark.parametrize(
207+
"last_release, notification_sent, result",
208+
[
209+
(datetime.now().strftime('%Y-%m-%dT%H:%M:%S'), None, 1),
210+
(
211+
datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
212+
(datetime.now() - timedelta(days=10)).strftime('%Y-%m-%dT%H:%M:%S'),
213+
1,
214+
),
215+
(
216+
datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
217+
(datetime.now() + timedelta(minutes=1)).strftime('%Y-%m-%dT%H:%M:%S'),
218+
0,
219+
),
220+
],
221+
)
222+
def test_send_review_request_message(mock_notify, last_release, notification_sent, result):
223+
data = {'source': {'last_release': last_release}}
224+
data['review_process'] = {} if notification_sent is None else {'notification_sent': notification_sent}
225+
factories.MetadataFactory(view_name='TestView', data=data)
226+
dmch.send_review_request_message('TestView')
227+
mock_notify.call_args = mock.call(
228+
email_address='[email protected]',
229+
template_id=settings.GOVNOTIFY_GREAT_MARKETGUIDES_REVIEW_REQUEST_TEMPLATE_ID,
230+
personalisation={
231+
'view_name': 'view_name',
232+
'review_url': 'https://great.staging.uktrade.digital/markets/',
233+
'release_date': 'dd/mm/YYYY',
234+
},
235+
)
236+
assert mock_notify.call_count == result

0 commit comments

Comments
 (0)