Merge branch 'master' into return-service_sms_sender_value

This commit is contained in:
Rebecca Law
2017-10-04 13:29:05 +01:00
14 changed files with 122 additions and 42 deletions

View File

@@ -283,6 +283,9 @@ cf-deploy: ## Deploys the app to Cloud Foundry
.PHONY: cf-deploy-api-db-migration
cf-deploy-api-db-migration:
$(if ${CF_SPACE},,$(error Must specify CF_SPACE))
cf unbind-service notify-api-db-migration notify-db
cf unbind-service notify-api-db-migration notify-config
cf unbind-service notify-api-db-migration notify-aws
cf push notify-api-db-migration -f manifest-api-${CF_SPACE}.yml
cf run-task notify-api-db-migration "python db.py db upgrade" --name api_db_migration

View File

@@ -370,7 +370,7 @@ def create_dvla_file_contents_for_notifications(notifications):
notification.template.__dict__,
notification.personalisation,
notification_reference=notification.reference,
contact_block=notification.service.letter_contact_block,
contact_block=notification.service.get_default_letter_contact(),
org_id=notification.service.dvla_organisation.id,
))
for notification in notifications

View File

@@ -285,3 +285,23 @@ class PopulateServiceSmsSender(Command):
print("Populated sms sender {} services from services".format(second_result.rowcount))
print("{} services in table".format(services_count_query))
print("{} service_sms_senders".format(service_sms_sender_count_query))
class PopulateServiceLetterContact(Command):
def run(self):
services_to_update = """
INSERT INTO service_letter_contacts(id, service_id, contact_block, is_default, created_at)
SELECT uuid_in(md5(random()::text || now()::text)::cstring), id, letter_contact_block, true, '{}'
FROM services
WHERE letter_contact_block IS NOT NULL
AND id NOT IN(
SELECT service_id
FROM service_letter_contacts
)
""".format(datetime.utcnow())
result = db.session.execute(services_to_update)
db.session.commit()
print("Populated letter contacts for {} services".format(result.rowcount))

View File

@@ -358,15 +358,13 @@ def dao_fetch_monthly_historical_stats_for_service(service_id, year):
@statsd(namespace='dao')
def dao_fetch_todays_stats_for_all_services(include_from_test_key=True, trial_mode_services=None):
def dao_fetch_todays_stats_for_all_services(include_from_test_key=True):
query = db.session.query(
Notification.notification_type,
Notification.status,
Notification.service_id,
func.count(Notification.id).label('count')
).join(
Service
).filter(
func.date(Notification.created_at) == date.today(),
).group_by(
@@ -380,9 +378,6 @@ def dao_fetch_todays_stats_for_all_services(include_from_test_key=True, trial_mo
if not include_from_test_key:
query = query.filter(Notification.key_type != KEY_TYPE_TEST)
if trial_mode_services is not None:
query = query.filter(Service.restricted == trial_mode_services)
return query.all()

View File

@@ -208,8 +208,7 @@ class Service(db.Model, Versioned):
created_by = db.relationship('User')
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
_reply_to_email_address = db.Column("reply_to_email_address", db.Text, index=False, unique=False, nullable=True)
letter_contact_block = db.Column(db.Text, index=False, unique=False, nullable=True)
# This column is now deprecated
_letter_contact_block = db.Column('letter_contact_block', db.Text, index=False, unique=False, nullable=True)
sms_sender = db.Column(db.String(11), nullable=False, default=lambda: current_app.config['FROM_NUMBER'])
organisation_id = db.Column(UUID(as_uuid=True), db.ForeignKey('organisation.id'), index=True, nullable=True)
organisation = db.relationship('Organisation')
@@ -274,8 +273,7 @@ class Service(db.Model, Versioned):
if len(default_letter_contact) > 1:
raise Exception("There should only ever be one default")
else:
return default_letter_contact[0].contact_block if default_letter_contact else \
self.letter_contact_block # need to update this to None after dropping the letter_contact_block column
return default_letter_contact[0].contact_block if default_letter_contact else None
class InboundNumber(db.Model):
@@ -549,7 +547,7 @@ class Template(db.Model):
return LetterDVLATemplate(
{'content': self.content, 'subject': self.subject},
notification_reference=1,
contact_block=self.service.letter_contact_block,
contact_block=self.service.get_default_letter_contact(),
)
def serialize(self):
@@ -1450,3 +1448,24 @@ class ServiceLetterContact(db.Model):
'created_at': self.created_at.strftime(DATETIME_FORMAT),
'updated_at': self.updated_at.strftime(DATETIME_FORMAT) if self.updated_at else None
}
class NotificationEmailReplyTo(db.Model):
__tablename__ = "notification_to_email_reply_to"
notification_id = db.Column(
UUID(as_uuid=True),
db.ForeignKey('notifications.id'),
unique=True,
index=True,
nullable=False,
primary_key=True
)
service_email_reply_to_id = db.Column(
UUID(as_uuid=True),
db.ForeignKey('service_email_reply_to.id'),
unique=False,
index=True,
nullable=False,
primary_key=True
)

View File

@@ -184,6 +184,7 @@ class ServiceSchema(BaseSchema):
override_flag = False
reply_to_email_address = fields.Method(method_name="get_reply_to_email_address")
sms_sender = fields.Method(method_name="get_sms_sender")
letter_contact_block = fields.Method(method_name="get_letter_contact")
def get_free_sms_fragment_limit(selfs, service):
return service.free_sms_fragment_limit()
@@ -197,9 +198,12 @@ class ServiceSchema(BaseSchema):
def get_sms_sender(self, service):
return service.get_default_sms_sender()
def get_letter_contact(self, service):
return service.get_default_letter_contact()
class Meta:
model = models.Service
dump_only = ['free_sms_fragment_limit', 'reply_to_email_address']
dump_only = ['free_sms_fragment_limit', 'reply_to_email_address', 'letter_contact_block']
exclude = (
'updated_at',
'created_at',

View File

@@ -90,7 +90,6 @@ def get_services():
detailed = request.args.get('detailed') == 'True'
user_id = request.args.get('user_id', None)
include_from_test_key = request.args.get('include_from_test_key', 'True') != 'False'
trial_mode_services = request.args.get('trial_mode_services')
# If start and end date are not set, we are expecting today's stats.
today = str(datetime.utcnow().date())
@@ -103,8 +102,7 @@ def get_services():
elif detailed:
result = jsonify(data=get_detailed_services(start_date=start_date, end_date=end_date,
only_active=only_active,
include_from_test_key=include_from_test_key,
trial_mode_services=trial_mode_services
include_from_test_key=include_from_test_key
))
return result
else:
@@ -149,7 +147,6 @@ def update_service(service_id):
fetched_service = dao_fetch_service_by_id(service_id)
# Capture the status change here as Marshmallow changes this later
service_going_live = fetched_service.restricted and not req_json.get('restricted', True)
current_data = dict(service_schema.dump(fetched_service).data.items())
current_data.update(request.get_json())
update_dict = service_schema.load(current_data).data
@@ -369,12 +366,10 @@ def get_detailed_service(service_id, today_only=False):
return detailed_service_schema.dump(service).data
def get_detailed_services(start_date, end_date, only_active=False, include_from_test_key=True,
trial_mode_services=None):
def get_detailed_services(start_date, end_date, only_active=False, include_from_test_key=True):
services = {service.id: service for service in dao_fetch_all_services(only_active)}
if start_date == datetime.utcnow().date():
stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=include_from_test_key,
trial_mode_services=trial_mode_services)
stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=include_from_test_key)
else:
stats = fetch_stats_by_date_range_for_all_services(start_date=start_date,

View File

@@ -20,6 +20,7 @@ manager.add_command('populate_monthly_billing', commands.PopulateMonthlyBilling)
manager.add_command('backfill_processing_time', commands.BackfillProcessingTime)
manager.add_command('populate_service_email_reply_to', commands.PopulateServiceEmailReplyTo)
manager.add_command('populate_service_sms_sender', commands.PopulateServiceSmsSender)
manager.add_command('populate_service_letter_contact', commands.PopulateServiceLetterContact)
@manager.command

View File

@@ -0,0 +1,30 @@
"""
Revision ID: 0123_add_noti_to_email_reply
Revises: 0122_add_service_letter_contact
Create Date: 2017-09-27 09:42:39.412731
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = '0123_add_noti_to_email_reply'
down_revision = '0122_add_service_letter_contact'
def upgrade():
op.create_table('notification_to_email_reply_to',
sa.Column('notification_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('service_email_reply_to_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(['notification_id'], ['notifications.id'], ),
sa.ForeignKeyConstraint(['service_email_reply_to_id'], ['service_email_reply_to.id'], ),
sa.PrimaryKeyConstraint('notification_id', 'service_email_reply_to_id')
)
op.create_index(op.f('ix_notification_to_email_reply_to_notification_id'), 'notification_to_email_reply_to', ['notification_id'], unique=True)
op.create_index(op.f('ix_notification_to_email_reply_to_service_email_reply_to_id'), 'notification_to_email_reply_to', ['service_email_reply_to_id'], unique=False)
def downgrade():
op.drop_index(op.f('ix_notification_to_email_reply_to_service_email_reply_to_id'), table_name='notification_to_email_reply_to')
op.drop_index(op.f('ix_notification_to_email_reply_to_notification_id'), table_name='notification_to_email_reply_to')
op.drop_table('notification_to_email_reply_to')

View File

@@ -1,4 +1,5 @@
boto3==1.4.7
cffi==1.11.0 # pyup: != 1.11.1 # 1.11.1 is missing .whl
celery==3.1.25 # pyup: <4
docopt==0.6.2
Flask-Bcrypt==0.7.1

View File

@@ -1 +1 @@
python-3.5.2
python-3.5.4

View File

@@ -2,7 +2,6 @@ import json
import uuid
from datetime import datetime
from unittest.mock import Mock
import pytest
import requests_mock
from flask import current_app
@@ -29,14 +28,17 @@ from app.celery.tasks import (
from app.config import QueueNames
from app.dao import jobs_dao, services_dao
from app.models import (
Notification,
EMAIL_TYPE,
JOB_STATUS_ERROR,
KEY_TYPE_NORMAL,
KEY_TYPE_TEAM,
KEY_TYPE_TEST,
KEY_TYPE_NORMAL,
SMS_TYPE,
EMAIL_TYPE,
LETTER_TYPE,
Job)
SERVICE_PERMISSION_TYPES,
SMS_TYPE,
Job,
Notification
)
from tests.app import load_example_csv
from tests.app.conftest import (
@@ -46,7 +48,16 @@ from tests.app.conftest import (
sample_email_template as create_sample_email_template,
sample_notification as create_sample_notification
)
from tests.app.db import create_user, create_notification, create_job, create_service_inbound_api, create_inbound_sms
from tests.app.db import (
create_inbound_sms,
create_job,
create_letter_contact,
create_notification,
create_service_inbound_api,
create_service,
create_template,
create_user
)
class AnyStringWith(str):
@@ -1076,8 +1087,11 @@ def test_build_dvla_file_retries_if_all_notifications_are_not_created(sample_let
mocked_send_task.assert_not_called()
def test_create_dvla_file_contents_for_job(sample_letter_template, mocker):
job = create_job(template=sample_letter_template, notification_count=2)
def test_create_dvla_file_contents(notify_db_session, mocker):
service = create_service(service_permissions=SERVICE_PERMISSION_TYPES)
create_letter_contact(service=service, contact_block='London,\nNW1A 1AA')
letter_template = create_template(service=service, template_type=LETTER_TYPE)
job = create_job(template=letter_template, notification_count=2)
create_notification(template=job.template, job=job, reference=1)
create_notification(template=job.template, job=job, reference=2)
mocked_letter_template = mocker.patch("app.celery.tasks.LetterDVLATemplate")
@@ -1093,9 +1107,8 @@ def test_create_dvla_file_contents_for_job(sample_letter_template, mocker):
# Personalisation
assert not calls[0][0][1]
assert not calls[1][0][1]
# Named arguments
assert calls[1][1]['contact_block'] == 'London,\nSW1A 1AA'
assert calls[1][1]['contact_block'] == 'London,\nNW1A 1AA'
assert calls[0][1]['notification_reference'] == '1'
assert calls[1][1]['notification_reference'] == '2'
assert calls[1][1]['org_id'] == '001'

View File

@@ -46,7 +46,8 @@ from tests.app.db import (
create_notification,
create_service,
create_api_key,
create_inbound_number
create_inbound_number,
create_letter_contact,
)
@@ -138,7 +139,6 @@ def sample_service(
email_from=None,
permissions=[SMS_TYPE, EMAIL_TYPE],
research_mode=None,
letter_contact_block='London,\nSW1A 1AA',
):
if user is None:
user = create_user()
@@ -150,8 +150,7 @@ def sample_service(
'message_limit': limit,
'restricted': restricted,
'email_from': email_from,
'created_by': user,
'letter_contact_block': letter_contact_block,
'created_by': user
}
service = Service.query.filter_by(name=service_name).first()
if not service:
@@ -184,7 +183,9 @@ def sample_service_full_permissions(notify_db, notify_db_session):
@pytest.fixture(scope='function')
def sample_service_custom_letter_contact_block(notify_db, notify_db_session):
return sample_service(notify_db, notify_db_session, letter_contact_block='((contact block))')
service = sample_service(notify_db, notify_db_session)
create_letter_contact(service, contact_block='((contact block))')
return service
@pytest.fixture(scope='function')

View File

@@ -1548,8 +1548,7 @@ def test_get_services_with_detailed_flag_accepts_date_range(client, mocker):
start_date=date(2001, 1, 1),
end_date=date(2002, 2, 2),
only_active=ANY,
include_from_test_key=ANY,
trial_mode_services=ANY
include_from_test_key=ANY
)
assert resp.status_code == 200
@@ -1566,8 +1565,7 @@ def test_get_services_with_detailed_flag_defaults_to_today(client, mocker):
end_date=date(2002, 2, 2),
include_from_test_key=ANY,
only_active=ANY,
start_date=date(2002, 2, 2),
trial_mode_services=ANY
start_date=date(2002, 2, 2)
)
assert resp.status_code == 200