diff --git a/app/config.py b/app/config.py index d27e4fdf3..b1cdd93c2 100644 --- a/app/config.py +++ b/app/config.py @@ -149,7 +149,6 @@ class Config(object): CHANGE_EMAIL_CONFIRMATION_TEMPLATE_ID = 'eb4d9930-87ab-4aef-9bce-786762687884' SERVICE_NOW_LIVE_TEMPLATE_ID = '618185c6-3636-49cd-b7d2-6f6f5eb3bdde' ORGANISATION_INVITATION_EMAIL_TEMPLATE_ID = '203566f0-d835-47c5-aa06-932439c86573' - PRECOMPILED_TEMPLATE_NAME = 'Pre-compiled PDF' BROKER_URL = 'sqs://' BROKER_TRANSPORT_OPTIONS = { diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index 155616801..7c25f8d21 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -83,6 +83,7 @@ def dao_get_template_usage(service_id, limit_days=None): Template.id.label('template_id'), Template.name, Template.template_type, + Template.is_precompiled_letter, notifications_aggregate_query.c.count ).join( notifications_aggregate_query, diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 57d5aa866..838a6336e 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -522,6 +522,7 @@ def dao_fetch_monthly_historical_usage_by_template_for_service(service_id, year) stat.month = result.month stat.year = result.year stat.count = result.count + stat.is_precompiled_letter = result.is_precompiled_letter stats.append(stat) month = get_london_month_from_utc_column(Notification.created_at) @@ -533,6 +534,7 @@ def dao_fetch_monthly_historical_usage_by_template_for_service(service_id, year) if fy_start < datetime.now() < fy_end: today_results = db.session.query( Notification.template_id, + Template.is_precompiled_letter, Template.name, Template.template_type, extract('month', month).label('month'), @@ -547,6 +549,7 @@ def dao_fetch_monthly_historical_usage_by_template_for_service(service_id, year) Notification.key_type != KEY_TYPE_TEST ).group_by( Notification.template_id, + Template.hidden, Template.name, Template.template_type, month, @@ -571,6 +574,7 @@ def dao_fetch_monthly_historical_usage_by_template_for_service(service_id, year) new_stat.month = int(today_result.month) new_stat.year = int(today_result.year) new_stat.count = today_result.count + new_stat.is_precompiled_letter = today_result.is_precompiled_letter stats.append(new_stat) return stats diff --git a/app/dao/stats_template_usage_by_month_dao.py b/app/dao/stats_template_usage_by_month_dao.py index 29aba1cdc..541ab7193 100644 --- a/app/dao/stats_template_usage_by_month_dao.py +++ b/app/dao/stats_template_usage_by_month_dao.py @@ -37,6 +37,7 @@ def dao_get_template_usage_stats_by_service(service_id, year): StatsTemplateUsageByMonth.template_id, Template.name, Template.template_type, + Template.is_precompiled_letter, StatsTemplateUsageByMonth.month, StatsTemplateUsageByMonth.year, StatsTemplateUsageByMonth.count diff --git a/app/dao/templates_dao.py b/app/dao/templates_dao.py index fb7dc7602..9dd397ef0 100644 --- a/app/dao/templates_dao.py +++ b/app/dao/templates_dao.py @@ -5,7 +5,11 @@ from sqlalchemy import asc, desc from sqlalchemy.sql.expression import bindparam from app import db -from app.models import (Template, TemplateHistory, TemplateRedacted) +from app.models import ( + Template, + TemplateHistory, + TemplateRedacted +) from app.dao.dao_utils import ( transactional, version_class @@ -135,6 +139,7 @@ def dao_get_templates_for_cache(cache): query = db.session.query(Template.id.label('template_id'), Template.template_type, Template.name, + Template.is_precompiled_letter, cache_subq.c.count.label('count') ).join(cache_subq, Template.id == cache_subq.c.template_id diff --git a/app/letters/utils.py b/app/letters/utils.py index 5e7730882..a7f05c00f 100644 --- a/app/letters/utils.py +++ b/app/letters/utils.py @@ -76,7 +76,3 @@ def get_letter_pdf(notification): file_content = obj.get()["Body"].read() return file_content - - -def is_precompiled_letter(template): - return template.hidden and template.name == current_app.config['PRECOMPILED_TEMPLATE_NAME'] diff --git a/app/models.py b/app/models.py index 07f4eccc9..8c59ea7e9 100644 --- a/app/models.py +++ b/app/models.py @@ -6,6 +6,7 @@ from flask import url_for, current_app from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.dialects.postgresql import ( UUID, JSON @@ -641,6 +642,9 @@ class TemplateProcessTypes(db.Model): name = db.Column(db.String(255), primary_key=True) +PRECOMPILED_TEMPLATE_NAME = 'Pre-compiled PDF' + + class TemplateBase(db.Model): __abstract__ = True @@ -718,6 +722,14 @@ class TemplateBase(db.Model): else: return None + @hybrid_property + def is_precompiled_letter(self): + return self.hidden and self.name == PRECOMPILED_TEMPLATE_NAME and self.template_type == LETTER_TYPE + + @is_precompiled_letter.setter + def is_precompiled_letter(self, value): + pass + def _as_utils_template(self): if self.template_type == EMAIL_TYPE: return PlainTextEmailTemplate( diff --git a/app/schemas.py b/app/schemas.py index 5592026b1..2131bb9b7 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -454,7 +454,16 @@ class NotificationWithTemplateSchema(BaseSchema): template = fields.Nested( TemplateSchema, - only=['id', 'version', 'name', 'template_type', 'content', 'subject', 'redact_personalisation'], + only=[ + 'id', + 'version', + 'name', + 'template_type', + 'content', + 'subject', + 'redact_personalisation', + 'is_precompiled_letter' + ], dump_only=True ) job = fields.Nested(JobSchema, only=["id", "original_file_name"], dump_only=True) diff --git a/app/service/rest.py b/app/service/rest.py index 99f03cee9..f6c7b8650 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -536,7 +536,8 @@ def get_monthly_template_usage(service_id): 'type': i.template_type, 'month': i.month, 'year': i.year, - 'count': i.count + 'count': i.count, + 'is_precompiled_letter': i.is_precompiled_letter } ) diff --git a/app/template/rest.py b/app/template/rest.py index a4bc1b4a9..c03a2c23e 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -20,7 +20,7 @@ from app.dao.templates_dao import ( dao_get_template_by_id) from notifications_utils.template import SMSMessageTemplate from app.dao.services_dao import dao_fetch_service_by_id -from app.letters.utils import get_letter_pdf, is_precompiled_letter +from app.letters.utils import get_letter_pdf from app.models import SMS_TYPE from app.notifications.validators import service_has_permission, check_reply_to from app.schemas import (template_schema, template_history_schema) @@ -196,7 +196,7 @@ def preview_letter_template_by_notification_id(service_id, notification_id, file template = dao_get_template_by_id(notification.template_id) - if is_precompiled_letter(template): + if template.is_precompiled_letter: try: diff --git a/app/template_statistics/rest.py b/app/template_statistics/rest.py index 86548dc00..b7198409e 100644 --- a/app/template_statistics/rest.py +++ b/app/template_statistics/rest.py @@ -47,7 +47,8 @@ def get_template_statistics_for_service_by_day(service_id): 'count': data.count, 'template_id': str(data.template_id), 'template_name': data.name, - 'template_type': data.template_type + 'template_type': data.template_type, + 'is_precompiled_letter': data.is_precompiled_letter } return jsonify(data=[serialize(row) for row in stats]) diff --git a/tests/app/dao/test_stats_template_usage_by_month_dao.py b/tests/app/dao/test_stats_template_usage_by_month_dao.py index 7d21696b5..676e00952 100644 --- a/tests/app/dao/test_stats_template_usage_by_month_dao.py +++ b/tests/app/dao/test_stats_template_usage_by_month_dao.py @@ -3,7 +3,7 @@ from app.dao.stats_template_usage_by_month_dao import ( insert_or_update_stats_for_template, dao_get_template_usage_stats_by_service ) -from app.models import StatsTemplateUsageByMonth +from app.models import StatsTemplateUsageByMonth, LETTER_TYPE, PRECOMPILED_TEMPLATE_NAME from tests.app.db import create_service, create_template @@ -74,6 +74,36 @@ def test_dao_get_template_usage_stats_by_service(sample_service): assert len(result) == 1 +def test_dao_get_template_usage_stats_by_service_for_precompiled_letters(sample_service): + + letter_template = create_template(service=sample_service, template_type=LETTER_TYPE) + + precompiled_letter_template = create_template( + service=sample_service, template_name=PRECOMPILED_TEMPLATE_NAME, hidden=True, template_type=LETTER_TYPE) + + db.session.add(StatsTemplateUsageByMonth( + template_id=letter_template.id, + month=5, + year=2017, + count=10 + )) + + db.session.add(StatsTemplateUsageByMonth( + template_id=precompiled_letter_template.id, + month=4, + year=2017, + count=20 + )) + + result = dao_get_template_usage_stats_by_service(sample_service.id, 2017) + + assert len(result) == 2 + assert [ + (letter_template.id, 'letter Template Name', 'letter', False, 5, 2017, 10), + (precompiled_letter_template.id, PRECOMPILED_TEMPLATE_NAME, 'letter', True, 4, 2017, 20) + ] == result + + def test_dao_get_template_usage_stats_by_service_specific_year(sample_service): email_template = create_template(service=sample_service, template_type="email") diff --git a/tests/app/dao/test_templates_dao.py b/tests/app/dao/test_templates_dao.py index 8891d2e26..d4768f598 100644 --- a/tests/app/dao/test_templates_dao.py +++ b/tests/app/dao/test_templates_dao.py @@ -13,7 +13,12 @@ from app.dao.templates_dao import ( dao_get_templates_for_cache, dao_redact_template, dao_update_template_reply_to ) -from app.models import Template, TemplateHistory, TemplateRedacted +from app.models import ( + Template, + TemplateHistory, + TemplateRedacted, + PRECOMPILED_TEMPLATE_NAME +) from tests.app.conftest import sample_template as create_sample_template from tests.app.db import create_template, create_letter_contact @@ -503,8 +508,33 @@ def test_get_templates_by_ids_successful(notify_db, notify_db_session): cache = [[k, v] for k, v in sample_cache_dict.items()] templates = dao_get_templates_for_cache(cache) assert len(templates) == 2 - assert [(template_1.id, template_1.template_type, template_1.name, 2), - (template_2.id, template_2.template_type, template_2.name, 3)] == templates + assert [(template_1.id, template_1.template_type, template_1.name, False, 2), + (template_2.id, template_2.template_type, template_2.name, False, 3)] == templates + + +def test_get_letter_templates_by_ids_successful(notify_db, notify_db_session): + template_1 = create_sample_template( + notify_db, + notify_db_session, + template_name=PRECOMPILED_TEMPLATE_NAME, + template_type="letter", + content="Template content", + hidden=True + ) + template_2 = create_sample_template( + notify_db, + notify_db_session, + template_name='Sample Template 2', + template_type="letter", + content="Template content" + ) + sample_cache_dict = {str.encode(str(template_1.id)): str.encode('2'), + str.encode(str(template_2.id)): str.encode('3')} + cache = [[k, v] for k, v in sample_cache_dict.items()] + templates = dao_get_templates_for_cache(cache) + assert len(templates) == 2 + assert [(template_1.id, template_1.template_type, template_1.name, True, 2), + (template_2.id, template_2.template_type, template_2.name, False, 3)] == templates def test_get_templates_by_ids_successful_for_one_cache_item(notify_db, notify_db_session): @@ -519,7 +549,7 @@ def test_get_templates_by_ids_successful_for_one_cache_item(notify_db, notify_db cache = [[k, v] for k, v in sample_cache_dict.items()] templates = dao_get_templates_for_cache(cache) assert len(templates) == 1 - assert [(template_1.id, template_1.template_type, template_1.name, 2)] == templates + assert [(template_1.id, template_1.template_type, template_1.name, False, 2)] == templates def test_get_templates_by_ids_returns_empty_list(): diff --git a/tests/app/db.py b/tests/app/db.py index b159976da..87c344d3e 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -126,13 +126,13 @@ def create_service_with_defined_sms_sender( def create_template( - service, - template_type=SMS_TYPE, - template_name=None, - subject='Template subject', - content='Dear Sir/Madam, Hello. Yours Truly, The Government.', - reply_to=None, - hidden=False + service, + template_type=SMS_TYPE, + template_name=None, + subject='Template subject', + content='Dear Sir/Madam, Hello. Yours Truly, The Government.', + reply_to=None, + hidden=False ): data = { 'name': template_name or '{} Template Name'.format(template_type), diff --git a/tests/app/letters/test_letter_utils.py b/tests/app/letters/test_letter_utils.py index efa55bc0b..244c21f6a 100644 --- a/tests/app/letters/test_letter_utils.py +++ b/tests/app/letters/test_letter_utils.py @@ -1,7 +1,6 @@ import pytest -from flask import current_app -from app.letters.utils import get_bucket_prefix_for_notification, is_precompiled_letter +from app.letters.utils import get_bucket_prefix_for_notification def test_get_bucket_prefix_for_notification_valid_notification(sample_notification): @@ -17,23 +16,3 @@ def test_get_bucket_prefix_for_notification_valid_notification(sample_notificati def test_get_bucket_prefix_for_notification_invalid_notification(): with pytest.raises(AttributeError): get_bucket_prefix_for_notification(None) - - -def test_is_precompiled_letter_false(sample_letter_template): - assert not is_precompiled_letter(sample_letter_template) - - -def test_is_precompiled_letter_true(sample_letter_template): - sample_letter_template.hidden = True - sample_letter_template.name = current_app.config['PRECOMPILED_TEMPLATE_NAME'] - assert is_precompiled_letter(sample_letter_template) - - -def test_is_precompiled_letter_hidden_true_not_name(sample_letter_template): - sample_letter_template.hidden = True - assert not is_precompiled_letter(sample_letter_template) - - -def test_is_precompiled_letter_name_correct_not_hidden(sample_letter_template): - sample_letter_template.name = current_app.config['PRECOMPILED_TEMPLATE_NAME'] - assert not is_precompiled_letter(sample_letter_template) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 7998b6b91..7fb44db12 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -25,7 +25,8 @@ from app.models import ( User, DVLA_ORG_LAND_REGISTRY, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - EMAIL_TYPE, SMS_TYPE, LETTER_TYPE, INTERNATIONAL_SMS_TYPE, INBOUND_SMS_TYPE + EMAIL_TYPE, SMS_TYPE, LETTER_TYPE, INTERNATIONAL_SMS_TYPE, INBOUND_SMS_TYPE, + PRECOMPILED_TEMPLATE_NAME ) from tests import create_authorization_header from tests.app.conftest import ( @@ -1831,7 +1832,12 @@ def test_get_template_usage_by_month_returns_two_templates( sample_service ): - template_one = create_template(sample_service) + template_one = create_template( + sample_service, + template_type=LETTER_TYPE, + template_name=PRECOMPILED_TEMPLATE_NAME, + hidden=True + ) # add a historical notification for template not1 = create_notification_history( @@ -1889,6 +1895,7 @@ def test_get_template_usage_by_month_returns_two_templates( assert resp_json[0]["month"] == 4 assert resp_json[0]["year"] == 2017 assert resp_json[0]["count"] == 1 + assert resp_json[0]["is_precompiled_letter"] is True assert resp_json[1]["template_id"] == str(sample_template.id) assert resp_json[1]["name"] == sample_template.name @@ -1896,6 +1903,7 @@ def test_get_template_usage_by_month_returns_two_templates( assert resp_json[1]["month"] == 4 assert resp_json[1]["year"] == 2017 assert resp_json[1]["count"] == 3 + assert resp_json[1]["is_precompiled_letter"] is False assert resp_json[2]["template_id"] == str(sample_template.id) assert resp_json[2]["name"] == sample_template.name @@ -1903,6 +1911,7 @@ def test_get_template_usage_by_month_returns_two_templates( assert resp_json[2]["month"] == 11 assert resp_json[2]["year"] == 2017 assert resp_json[2]["count"] == 1 + assert resp_json[2]["is_precompiled_letter"] is False def test_search_for_notification_by_to_field(client, notify_db, notify_db_session): @@ -2139,6 +2148,17 @@ def test_get_notification_for_service_includes_template_redacted(admin_request, assert resp['template']['redact_personalisation'] is False +def test_get_notification_for_service_includes_precompiled_letter(admin_request, sample_notification): + resp = admin_request.get( + 'service.get_notification_for_service', + service_id=sample_notification.service_id, + notification_id=sample_notification.id + ) + + assert resp['id'] == str(sample_notification.id) + assert resp['template']['is_precompiled_letter'] is False + + def test_get_all_notifications_for_service_includes_template_redacted(admin_request, sample_service): normal_template = create_template(sample_service) @@ -2162,6 +2182,33 @@ def test_get_all_notifications_for_service_includes_template_redacted(admin_requ assert resp['notifications'][1]['template']['redact_personalisation'] is True +def test_get_all_notifications_for_service_includes_template_hidden(admin_request, sample_service): + letter_template = create_template(sample_service, template_type=LETTER_TYPE) + precompiled_template = create_template( + sample_service, + template_type=LETTER_TYPE, + template_name='Pre-compiled PDF', + subject='Pre-compiled PDF', + hidden=True + ) + + with freeze_time('2000-01-01'): + letter_noti = create_notification(letter_template) + with freeze_time('2000-01-02'): + precompiled_noti = create_notification(precompiled_template) + + resp = admin_request.get( + 'service.get_all_notifications_for_service', + service_id=sample_service.id + ) + + assert resp['notifications'][0]['id'] == str(precompiled_noti.id) + assert resp['notifications'][0]['template']['is_precompiled_letter'] is True + + assert resp['notifications'][1]['id'] == str(letter_noti.id) + assert resp['notifications'][1]['template']['is_precompiled_letter'] is False + + def test_search_for_notification_by_to_field_returns_personlisation( client, notify_db, diff --git a/tests/app/test_model.py b/tests/app/test_model.py index a0b8d4804..c7e253470 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -18,7 +18,8 @@ from app.models import ( NOTIFICATION_STATUS_LETTER_ACCEPTED, NOTIFICATION_STATUS_LETTER_RECEIVED, NOTIFICATION_STATUS_TYPES_FAILED, - NOTIFICATION_TECHNICAL_FAILURE + NOTIFICATION_TECHNICAL_FAILURE, + PRECOMPILED_TEMPLATE_NAME ) from tests.app.conftest import ( sample_template as create_sample_template, @@ -319,3 +320,23 @@ def test_letter_notification_postcode_can_be_null_for_precompiled_letters(client assert json['line_1'] == 'test' assert json['line_2'] == 'London' assert json['postcode'] is None + + +def test_is_precompiled_letter_false(sample_letter_template): + assert not sample_letter_template.is_precompiled_letter + + +def test_is_precompiled_letter_true(sample_letter_template): + sample_letter_template.hidden = True + sample_letter_template.name = PRECOMPILED_TEMPLATE_NAME + assert sample_letter_template.is_precompiled_letter + + +def test_is_precompiled_letter_hidden_true_not_name(sample_letter_template): + sample_letter_template.hidden = True + assert not sample_letter_template.is_precompiled_letter + + +def test_is_precompiled_letter_name_correct_not_hidden(sample_letter_template): + sample_letter_template.name = PRECOMPILED_TEMPLATE_NAME + assert not sample_letter_template.is_precompiled_letter