2017-06-20 17:13:40 +01:00
|
|
|
import json
|
2017-10-11 18:14:56 +01:00
|
|
|
from datetime import datetime
|
2018-02-20 10:57:25 +00:00
|
|
|
from collections import namedtuple, defaultdict
|
2016-11-25 17:32:01 +00:00
|
|
|
|
2016-03-31 15:57:50 +01:00
|
|
|
from flask import current_app
|
2016-04-13 15:31:08 +01:00
|
|
|
from notifications_utils.recipients import (
|
2020-03-12 16:05:25 +00:00
|
|
|
format_postcode_for_printing,
|
2016-09-28 17:00:17 +01:00
|
|
|
RecipientCSV
|
2016-03-31 15:57:50 +01:00
|
|
|
)
|
2018-02-06 09:35:33 +00:00
|
|
|
from notifications_utils.statsd_decorators import statsd
|
2017-10-12 16:21:08 +01:00
|
|
|
from notifications_utils.template import (
|
|
|
|
|
SMSMessageTemplate,
|
|
|
|
|
WithSubjectTemplate,
|
|
|
|
|
)
|
2018-11-26 12:53:39 +00:00
|
|
|
from notifications_utils.timezones import convert_utc_to_bst
|
2017-10-12 16:21:08 +01:00
|
|
|
from requests import (
|
|
|
|
|
HTTPError,
|
2017-11-15 11:43:52 +00:00
|
|
|
request,
|
|
|
|
|
RequestException
|
2017-10-12 16:21:08 +01:00
|
|
|
)
|
2016-06-20 13:33:53 +01:00
|
|
|
from sqlalchemy.exc import SQLAlchemyError
|
2017-11-27 15:11:58 +00:00
|
|
|
|
2016-03-31 15:57:50 +01:00
|
|
|
from app import (
|
|
|
|
|
create_uuid,
|
2017-04-12 17:56:55 +01:00
|
|
|
create_random_identifier,
|
2016-03-31 15:57:50 +01:00
|
|
|
DATETIME_FORMAT,
|
2018-03-02 11:02:54 +00:00
|
|
|
encryption,
|
2016-03-31 15:57:50 +01:00
|
|
|
notify_celery,
|
|
|
|
|
)
|
|
|
|
|
from app.aws import s3
|
2018-01-26 14:05:09 +00:00
|
|
|
from app.celery import provider_tasks, letters_pdf_tasks, research_mode_tasks
|
2017-07-19 13:50:29 +01:00
|
|
|
from app.config import QueueNames
|
2018-02-20 10:57:25 +00:00
|
|
|
from app.dao.daily_sorted_letter_dao import dao_create_or_update_daily_sorted_letter
|
2017-06-20 17:13:40 +01:00
|
|
|
from app.dao.inbound_sms_dao import dao_get_inbound_sms_by_id
|
2016-06-20 13:33:53 +01:00
|
|
|
from app.dao.jobs_dao import (
|
|
|
|
|
dao_update_job,
|
2017-03-15 15:26:58 +00:00
|
|
|
dao_get_job_by_id,
|
2017-10-27 13:53:55 +01:00
|
|
|
)
|
2017-09-20 11:12:37 +01:00
|
|
|
from app.dao.notifications_dao import (
|
|
|
|
|
get_notification_by_id,
|
2017-10-17 11:07:36 +01:00
|
|
|
dao_update_notifications_by_reference,
|
2017-12-01 21:59:46 +00:00
|
|
|
dao_get_last_notification_added_for_job_id,
|
2018-01-26 14:05:09 +00:00
|
|
|
update_notification_status_by_reference,
|
2018-03-01 15:39:51 +00:00
|
|
|
dao_get_notification_history_by_reference,
|
2017-10-27 13:53:55 +01:00
|
|
|
)
|
2019-11-11 15:31:53 +00:00
|
|
|
from app.dao.provider_details_dao import get_provider_details_by_notification_type
|
2019-12-09 16:23:09 +00:00
|
|
|
from app.dao.returned_letters_dao import insert_or_update_returned_letters
|
2018-11-01 17:00:44 +00:00
|
|
|
from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id
|
2017-06-20 17:13:40 +01:00
|
|
|
from app.dao.service_inbound_api_dao import get_service_inbound_api_for_service
|
2018-11-01 17:00:44 +00:00
|
|
|
from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id
|
2016-10-03 10:57:10 +01:00
|
|
|
from app.dao.services_dao import dao_fetch_service_by_id, fetch_todays_total_message_count
|
2016-06-20 13:33:53 +01:00
|
|
|
from app.dao.templates_dao import dao_get_template_by_id
|
2018-03-16 17:18:44 +00:00
|
|
|
from app.exceptions import DVLAException, NotificationTechnicalFailureException
|
2016-03-09 17:46:01 +00:00
|
|
|
from app.models import (
|
2017-10-25 15:39:54 +01:00
|
|
|
DVLA_RESPONSE_STATUS_SENT,
|
2016-06-29 11:50:54 +01:00
|
|
|
EMAIL_TYPE,
|
2017-04-06 17:16:08 +01:00
|
|
|
JOB_STATUS_CANCELLED,
|
|
|
|
|
JOB_STATUS_FINISHED,
|
2017-10-17 11:07:36 +01:00
|
|
|
JOB_STATUS_IN_PROGRESS,
|
|
|
|
|
JOB_STATUS_PENDING,
|
|
|
|
|
KEY_TYPE_NORMAL,
|
2017-10-16 12:32:44 +01:00
|
|
|
LETTER_TYPE,
|
2018-01-26 14:05:09 +00:00
|
|
|
NOTIFICATION_CREATED,
|
2017-10-23 17:01:53 +01:00
|
|
|
NOTIFICATION_DELIVERED,
|
2017-09-26 09:56:09 +01:00
|
|
|
NOTIFICATION_SENDING,
|
2018-01-15 14:22:09 +00:00
|
|
|
NOTIFICATION_TEMPORARY_FAILURE,
|
2017-10-16 12:32:44 +01:00
|
|
|
NOTIFICATION_TECHNICAL_FAILURE,
|
2018-08-31 16:49:06 +01:00
|
|
|
NOTIFICATION_RETURNED_LETTER,
|
2017-10-16 12:32:44 +01:00
|
|
|
SMS_TYPE,
|
2018-02-20 10:57:25 +00:00
|
|
|
DailySortedLetter,
|
2017-09-26 09:56:09 +01:00
|
|
|
)
|
2016-11-11 10:41:39 +00:00
|
|
|
from app.notifications.process_notifications import persist_notification
|
2016-09-28 17:00:17 +01:00
|
|
|
from app.service.utils import service_allowed_to_send_to
|
2016-03-31 15:57:50 +01:00
|
|
|
|
2016-03-09 14:41:36 +00:00
|
|
|
|
2016-02-24 17:12:30 +00:00
|
|
|
@notify_celery.task(name="process-job")
|
2016-08-05 10:44:43 +01:00
|
|
|
@statsd(namespace="tasks")
|
2018-11-01 17:00:44 +00:00
|
|
|
def process_job(job_id, sender_id=None):
|
2016-02-25 11:23:04 +00:00
|
|
|
start = datetime.utcnow()
|
2016-02-24 17:12:30 +00:00
|
|
|
job = dao_get_job_by_id(job_id)
|
2019-10-01 13:05:05 +01:00
|
|
|
current_app.logger.info("Starting process-job task for job id {} with status: {}".format(job_id, job.job_status))
|
2016-03-09 11:28:52 +00:00
|
|
|
|
2017-02-02 11:34:00 +00:00
|
|
|
if job.job_status != JOB_STATUS_PENDING:
|
2016-10-07 12:54:04 +01:00
|
|
|
return
|
|
|
|
|
|
2016-03-09 11:28:52 +00:00
|
|
|
service = job.service
|
|
|
|
|
|
2017-02-02 11:34:00 +00:00
|
|
|
if not service.active:
|
|
|
|
|
job.job_status = JOB_STATUS_CANCELLED
|
|
|
|
|
dao_update_job(job)
|
2018-09-19 10:49:11 +01:00
|
|
|
current_app.logger.warning(
|
2017-02-02 11:34:00 +00:00
|
|
|
"Job {} has been cancelled, service {} is inactive".format(job_id, service.id))
|
|
|
|
|
return
|
|
|
|
|
|
2016-11-11 10:41:39 +00:00
|
|
|
if __sending_limits_for_job_exceeded(service, job, job_id):
|
2016-03-09 13:57:53 +00:00
|
|
|
return
|
2016-03-09 11:28:52 +00:00
|
|
|
|
2017-02-02 11:34:00 +00:00
|
|
|
job.job_status = JOB_STATUS_IN_PROGRESS
|
2017-10-12 16:21:08 +01:00
|
|
|
job.processing_started = start
|
2016-02-24 17:12:30 +00:00
|
|
|
dao_update_job(job)
|
|
|
|
|
|
2019-11-08 10:30:26 +00:00
|
|
|
recipient_csv, template, sender_id = get_recipient_csv_and_template_and_sender_id(job)
|
2016-03-09 07:27:26 +00:00
|
|
|
|
2019-10-01 13:05:05 +01:00
|
|
|
current_app.logger.info("Starting job {} processing {} notifications".format(job_id, job.notification_count))
|
2017-10-10 15:04:55 +01:00
|
|
|
|
2019-11-05 16:47:00 +00:00
|
|
|
for row in recipient_csv.get_rows():
|
2018-11-05 16:16:48 +00:00
|
|
|
process_row(row, template, job, service, sender_id=sender_id)
|
2016-02-24 17:12:30 +00:00
|
|
|
|
2018-02-13 18:38:32 +00:00
|
|
|
job_complete(job, start=start)
|
2017-10-13 16:46:17 +01:00
|
|
|
|
|
|
|
|
|
2018-02-13 18:38:32 +00:00
|
|
|
def job_complete(job, resumed=False, start=None):
|
|
|
|
|
job.job_status = JOB_STATUS_FINISHED
|
2017-04-05 11:57:56 +01:00
|
|
|
|
2016-02-25 11:23:04 +00:00
|
|
|
finished = datetime.utcnow()
|
|
|
|
|
job.processing_finished = finished
|
2016-02-24 17:12:30 +00:00
|
|
|
dao_update_job(job)
|
2017-10-13 16:46:17 +01:00
|
|
|
|
|
|
|
|
if resumed:
|
|
|
|
|
current_app.logger.info(
|
2017-12-08 17:32:18 +00:00
|
|
|
"Resumed Job {} completed at {}".format(job.id, job.created_at)
|
2017-10-13 16:46:17 +01:00
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
current_app.logger.info(
|
|
|
|
|
"Job {} created at {} started at {} finished at {}".format(job.id, job.created_at, start, finished)
|
|
|
|
|
)
|
2016-02-09 13:31:45 +00:00
|
|
|
|
|
|
|
|
|
2019-11-08 10:30:26 +00:00
|
|
|
def get_recipient_csv_and_template_and_sender_id(job):
|
2019-11-05 16:47:00 +00:00
|
|
|
db_template = dao_get_template_by_id(job.template_id, job.template_version)
|
|
|
|
|
|
|
|
|
|
TemplateClass = get_template_class(db_template.template_type)
|
|
|
|
|
template = TemplateClass(db_template.__dict__)
|
2019-11-13 13:40:42 +00:00
|
|
|
contents, meta_data = s3.get_job_and_metadata_from_s3(service_id=str(job.service_id), job_id=str(job.id))
|
2019-11-08 10:30:26 +00:00
|
|
|
recipient_csv = RecipientCSV(file_data=contents,
|
2019-11-05 16:47:00 +00:00
|
|
|
template_type=template.template_type,
|
|
|
|
|
placeholders=template.placeholders)
|
2019-11-08 10:30:26 +00:00
|
|
|
|
|
|
|
|
return recipient_csv, template, meta_data.get("sender_id")
|
2019-11-05 16:47:00 +00:00
|
|
|
|
|
|
|
|
|
2018-11-05 16:16:48 +00:00
|
|
|
def process_row(row, template, job, service, sender_id=None):
|
2017-01-17 12:00:34 +00:00
|
|
|
template_type = template.template_type
|
|
|
|
|
encrypted = encryption.encrypt({
|
|
|
|
|
'template': str(template.id),
|
|
|
|
|
'template_version': job.template_version,
|
|
|
|
|
'job': str(job.id),
|
2018-03-05 15:55:46 +00:00
|
|
|
'to': row.recipient,
|
|
|
|
|
'row_number': row.index,
|
|
|
|
|
'personalisation': dict(row.personalisation)
|
2017-01-17 12:00:34 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
send_fns = {
|
2017-10-18 17:00:37 +01:00
|
|
|
SMS_TYPE: save_sms,
|
|
|
|
|
EMAIL_TYPE: save_email,
|
|
|
|
|
LETTER_TYPE: save_letter
|
2017-01-17 12:00:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
send_fn = send_fns[template_type]
|
|
|
|
|
|
2018-11-05 16:16:48 +00:00
|
|
|
task_kwargs = {}
|
|
|
|
|
if sender_id:
|
|
|
|
|
task_kwargs['sender_id'] = sender_id
|
|
|
|
|
|
2019-09-26 14:19:09 +01:00
|
|
|
notification_id = create_uuid()
|
2017-01-18 11:29:38 +00:00
|
|
|
send_fn.apply_async(
|
|
|
|
|
(
|
|
|
|
|
str(service.id),
|
2019-09-26 14:19:09 +01:00
|
|
|
notification_id,
|
2017-01-18 11:29:38 +00:00
|
|
|
encrypted,
|
|
|
|
|
),
|
2018-11-05 16:16:48 +00:00
|
|
|
task_kwargs,
|
2017-05-25 10:51:49 +01:00
|
|
|
queue=QueueNames.DATABASE if not service.research_mode else QueueNames.RESEARCH_MODE
|
2017-01-17 12:00:34 +00:00
|
|
|
)
|
2019-09-26 14:19:09 +01:00
|
|
|
return notification_id
|
2017-01-17 12:00:34 +00:00
|
|
|
|
|
|
|
|
|
2016-11-11 10:41:39 +00:00
|
|
|
def __sending_limits_for_job_exceeded(service, job, job_id):
|
|
|
|
|
total_sent = fetch_todays_total_message_count(service.id)
|
|
|
|
|
|
|
|
|
|
if total_sent + job.notification_count > service.message_limit:
|
|
|
|
|
job.job_status = 'sending limits exceeded'
|
|
|
|
|
job.processing_finished = datetime.utcnow()
|
|
|
|
|
dao_update_job(job)
|
|
|
|
|
current_app.logger.info(
|
|
|
|
|
"Job {} size {} error. Sending limits {} exceeded".format(
|
|
|
|
|
job_id, job.notification_count, service.message_limit)
|
|
|
|
|
)
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
2017-10-18 17:00:37 +01:00
|
|
|
@notify_celery.task(bind=True, name="save-sms", max_retries=5, default_retry_delay=300)
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def save_sms(self,
|
|
|
|
|
service_id,
|
|
|
|
|
notification_id,
|
2018-11-01 17:00:44 +00:00
|
|
|
encrypted_notification,
|
|
|
|
|
sender_id=None):
|
2017-10-18 17:00:37 +01:00
|
|
|
notification = encryption.decrypt(encrypted_notification)
|
|
|
|
|
service = dao_fetch_service_by_id(service_id)
|
2017-12-15 17:15:31 +00:00
|
|
|
template = dao_get_template_by_id(notification['template'], version=notification['template_version'])
|
2017-10-18 17:00:37 +01:00
|
|
|
|
2018-11-01 17:00:44 +00:00
|
|
|
if sender_id:
|
|
|
|
|
reply_to_text = dao_get_service_sms_senders_by_id(service_id, sender_id).sms_sender
|
|
|
|
|
else:
|
|
|
|
|
reply_to_text = template.get_reply_to_text()
|
|
|
|
|
|
2018-11-01 16:06:07 +00:00
|
|
|
if not service_allowed_to_send_to(notification['to'], service, KEY_TYPE_NORMAL):
|
As Notify matures we probably need less logging, especially to report happy path events.
This PR is a proposal to reduce the average messages we see for a single notification from about 7 messages to 2.
Messaging would change to something like this:
February 2nd 2018, 15:39:05.885 Full delivery response from Firetext for notification: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
{'status': ['0'], 'reference': ['8eda51d5-cd82-4569-bfc9-d5570cdf2126'], 'time': ['2018-02-02 15:39:01'], 'code': ['000']}
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:57.727 SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to provider firetext at 2018-02-02 15:38:56.716814
February 2nd 2018, 15:38:56.727 Starting sending SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 to provider at 2018-02-02 15:38:56.408181
February 2nd 2018, 15:38:56.727 Firetext request for 8eda51d5-cd82-4569-bfc9-d5570cdf2126 finished in 0.30376038211397827
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to the priority-tasks queue for delivery
To somthing like this:
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
2018-02-02 15:55:25 +00:00
|
|
|
current_app.logger.debug(
|
2017-10-18 17:00:37 +01:00
|
|
|
"SMS {} failed as restricted service".format(notification_id)
|
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
saved_notification = persist_notification(
|
|
|
|
|
template_id=notification['template'],
|
|
|
|
|
template_version=notification['template_version'],
|
|
|
|
|
recipient=notification['to'],
|
|
|
|
|
service=service,
|
|
|
|
|
personalisation=notification.get('personalisation'),
|
|
|
|
|
notification_type=SMS_TYPE,
|
2018-11-01 16:06:07 +00:00
|
|
|
api_key_id=None,
|
|
|
|
|
key_type=KEY_TYPE_NORMAL,
|
2017-10-18 17:00:37 +01:00
|
|
|
created_at=datetime.utcnow(),
|
|
|
|
|
job_id=notification.get('job', None),
|
|
|
|
|
job_row_number=notification.get('row_number', None),
|
2017-11-25 11:31:36 +00:00
|
|
|
notification_id=notification_id,
|
2018-11-01 17:00:44 +00:00
|
|
|
reply_to_text=reply_to_text
|
2017-10-18 17:00:37 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
provider_tasks.deliver_sms.apply_async(
|
|
|
|
|
[str(saved_notification.id)],
|
|
|
|
|
queue=QueueNames.SEND_SMS if not service.research_mode else QueueNames.RESEARCH_MODE
|
|
|
|
|
)
|
|
|
|
|
|
As Notify matures we probably need less logging, especially to report happy path events.
This PR is a proposal to reduce the average messages we see for a single notification from about 7 messages to 2.
Messaging would change to something like this:
February 2nd 2018, 15:39:05.885 Full delivery response from Firetext for notification: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
{'status': ['0'], 'reference': ['8eda51d5-cd82-4569-bfc9-d5570cdf2126'], 'time': ['2018-02-02 15:39:01'], 'code': ['000']}
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:57.727 SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to provider firetext at 2018-02-02 15:38:56.716814
February 2nd 2018, 15:38:56.727 Starting sending SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 to provider at 2018-02-02 15:38:56.408181
February 2nd 2018, 15:38:56.727 Firetext request for 8eda51d5-cd82-4569-bfc9-d5570cdf2126 finished in 0.30376038211397827
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to the priority-tasks queue for delivery
To somthing like this:
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
2018-02-02 15:55:25 +00:00
|
|
|
current_app.logger.debug(
|
2017-10-18 17:00:37 +01:00
|
|
|
"SMS {} created at {} for job {}".format(
|
|
|
|
|
saved_notification.id,
|
|
|
|
|
saved_notification.created_at,
|
|
|
|
|
notification.get('job', None))
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
handle_exception(self, notification, notification_id, e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@notify_celery.task(bind=True, name="save-email", max_retries=5, default_retry_delay=300)
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def save_email(self,
|
|
|
|
|
service_id,
|
|
|
|
|
notification_id,
|
2018-11-01 17:00:44 +00:00
|
|
|
encrypted_notification,
|
|
|
|
|
sender_id=None):
|
2017-10-18 17:00:37 +01:00
|
|
|
notification = encryption.decrypt(encrypted_notification)
|
2017-12-15 17:15:31 +00:00
|
|
|
|
2017-10-18 17:00:37 +01:00
|
|
|
service = dao_fetch_service_by_id(service_id)
|
2017-12-15 17:15:31 +00:00
|
|
|
template = dao_get_template_by_id(notification['template'], version=notification['template_version'])
|
2017-10-18 17:00:37 +01:00
|
|
|
|
2018-11-01 17:00:44 +00:00
|
|
|
if sender_id:
|
|
|
|
|
reply_to_text = dao_get_reply_to_by_id(service_id, sender_id).email_address
|
|
|
|
|
else:
|
|
|
|
|
reply_to_text = template.get_reply_to_text()
|
|
|
|
|
|
2018-11-01 16:06:07 +00:00
|
|
|
if not service_allowed_to_send_to(notification['to'], service, KEY_TYPE_NORMAL):
|
2017-10-18 17:00:37 +01:00
|
|
|
current_app.logger.info("Email {} failed as restricted service".format(notification_id))
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
saved_notification = persist_notification(
|
|
|
|
|
template_id=notification['template'],
|
|
|
|
|
template_version=notification['template_version'],
|
|
|
|
|
recipient=notification['to'],
|
|
|
|
|
service=service,
|
|
|
|
|
personalisation=notification.get('personalisation'),
|
|
|
|
|
notification_type=EMAIL_TYPE,
|
2018-11-01 16:06:07 +00:00
|
|
|
api_key_id=None,
|
|
|
|
|
key_type=KEY_TYPE_NORMAL,
|
2017-10-18 17:00:37 +01:00
|
|
|
created_at=datetime.utcnow(),
|
|
|
|
|
job_id=notification.get('job', None),
|
|
|
|
|
job_row_number=notification.get('row_number', None),
|
2017-11-25 11:31:36 +00:00
|
|
|
notification_id=notification_id,
|
2018-11-01 17:00:44 +00:00
|
|
|
reply_to_text=reply_to_text
|
2017-10-18 17:00:37 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
provider_tasks.deliver_email.apply_async(
|
|
|
|
|
[str(saved_notification.id)],
|
|
|
|
|
queue=QueueNames.SEND_EMAIL if not service.research_mode else QueueNames.RESEARCH_MODE
|
|
|
|
|
)
|
|
|
|
|
|
As Notify matures we probably need less logging, especially to report happy path events.
This PR is a proposal to reduce the average messages we see for a single notification from about 7 messages to 2.
Messaging would change to something like this:
February 2nd 2018, 15:39:05.885 Full delivery response from Firetext for notification: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
{'status': ['0'], 'reference': ['8eda51d5-cd82-4569-bfc9-d5570cdf2126'], 'time': ['2018-02-02 15:39:01'], 'code': ['000']}
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:57.727 SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to provider firetext at 2018-02-02 15:38:56.716814
February 2nd 2018, 15:38:56.727 Starting sending SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 to provider at 2018-02-02 15:38:56.408181
February 2nd 2018, 15:38:56.727 Firetext request for 8eda51d5-cd82-4569-bfc9-d5570cdf2126 finished in 0.30376038211397827
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to the priority-tasks queue for delivery
To somthing like this:
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
2018-02-02 15:55:25 +00:00
|
|
|
current_app.logger.debug("Email {} created at {}".format(saved_notification.id, saved_notification.created_at))
|
2017-10-18 17:00:37 +01:00
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
handle_exception(self, notification, notification_id, e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@notify_celery.task(bind=True, name="save-letter", max_retries=5, default_retry_delay=300)
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def save_letter(
|
2018-10-23 13:59:52 +01:00
|
|
|
self,
|
|
|
|
|
service_id,
|
|
|
|
|
notification_id,
|
|
|
|
|
encrypted_notification,
|
2017-10-18 17:00:37 +01:00
|
|
|
):
|
|
|
|
|
notification = encryption.decrypt(encrypted_notification)
|
|
|
|
|
|
|
|
|
|
# we store the recipient as just the first item of the person's address
|
|
|
|
|
recipient = notification['personalisation']['addressline1']
|
|
|
|
|
|
2020-03-12 16:05:25 +00:00
|
|
|
notification['personalisation']['postcode'] = format_postcode_for_printing(
|
|
|
|
|
notification['personalisation']['postcode']
|
|
|
|
|
)
|
|
|
|
|
|
2017-10-18 17:00:37 +01:00
|
|
|
service = dao_fetch_service_by_id(service_id)
|
2017-12-15 17:15:31 +00:00
|
|
|
template = dao_get_template_by_id(notification['template'], version=notification['template_version'])
|
|
|
|
|
|
2017-10-18 17:00:37 +01:00
|
|
|
try:
|
2018-01-26 14:05:09 +00:00
|
|
|
# if we don't want to actually send the letter, then start it off in SENDING so we don't pick it up
|
|
|
|
|
status = NOTIFICATION_CREATED if not service.research_mode else NOTIFICATION_SENDING
|
|
|
|
|
|
2017-10-18 17:00:37 +01:00
|
|
|
saved_notification = persist_notification(
|
|
|
|
|
template_id=notification['template'],
|
|
|
|
|
template_version=notification['template_version'],
|
2019-01-14 17:45:56 +00:00
|
|
|
template_postage=template.postage,
|
2017-10-18 17:00:37 +01:00
|
|
|
recipient=recipient,
|
|
|
|
|
service=service,
|
|
|
|
|
personalisation=notification['personalisation'],
|
|
|
|
|
notification_type=LETTER_TYPE,
|
|
|
|
|
api_key_id=None,
|
|
|
|
|
key_type=KEY_TYPE_NORMAL,
|
|
|
|
|
created_at=datetime.utcnow(),
|
|
|
|
|
job_id=notification['job'],
|
|
|
|
|
job_row_number=notification['row_number'],
|
|
|
|
|
notification_id=notification_id,
|
2017-11-27 11:33:04 +00:00
|
|
|
reference=create_random_identifier(),
|
2018-01-26 14:05:09 +00:00
|
|
|
reply_to_text=template.get_reply_to_text(),
|
|
|
|
|
status=status
|
2017-10-18 17:00:37 +01:00
|
|
|
)
|
|
|
|
|
|
2018-02-13 18:38:32 +00:00
|
|
|
if not service.research_mode:
|
|
|
|
|
letters_pdf_tasks.create_letters_pdf.apply_async(
|
|
|
|
|
[str(saved_notification.id)],
|
|
|
|
|
queue=QueueNames.CREATE_LETTERS_PDF
|
|
|
|
|
)
|
|
|
|
|
elif current_app.config['NOTIFY_ENVIRONMENT'] in ['preview', 'development']:
|
|
|
|
|
research_mode_tasks.create_fake_letter_response_file.apply_async(
|
|
|
|
|
(saved_notification.reference,),
|
|
|
|
|
queue=QueueNames.RESEARCH_MODE
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
update_notification_status_by_reference(saved_notification.reference, 'delivered')
|
2017-12-06 13:54:02 +00:00
|
|
|
|
As Notify matures we probably need less logging, especially to report happy path events.
This PR is a proposal to reduce the average messages we see for a single notification from about 7 messages to 2.
Messaging would change to something like this:
February 2nd 2018, 15:39:05.885 Full delivery response from Firetext for notification: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
{'status': ['0'], 'reference': ['8eda51d5-cd82-4569-bfc9-d5570cdf2126'], 'time': ['2018-02-02 15:39:01'], 'code': ['000']}
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:57.727 SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to provider firetext at 2018-02-02 15:38:56.716814
February 2nd 2018, 15:38:56.727 Starting sending SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 to provider at 2018-02-02 15:38:56.408181
February 2nd 2018, 15:38:56.727 Firetext request for 8eda51d5-cd82-4569-bfc9-d5570cdf2126 finished in 0.30376038211397827
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to the priority-tasks queue for delivery
To somthing like this:
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
2018-02-02 15:55:25 +00:00
|
|
|
current_app.logger.debug("Letter {} created at {}".format(saved_notification.id, saved_notification.created_at))
|
2017-10-18 17:00:37 +01:00
|
|
|
except SQLAlchemyError as e:
|
|
|
|
|
handle_exception(self, notification, notification_id, e)
|
|
|
|
|
|
|
|
|
|
|
2017-09-20 11:12:37 +01:00
|
|
|
@notify_celery.task(bind=True, name='update-letter-notifications-to-sent')
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def update_letter_notifications_to_sent_to_dvla(self, notification_references):
|
2017-09-15 17:46:08 +01:00
|
|
|
# This task will be called by the FTP app to update notifications as sent to DVLA
|
2019-11-11 15:31:53 +00:00
|
|
|
provider = get_provider_details_by_notification_type(LETTER_TYPE)[0]
|
2017-09-15 17:46:08 +01:00
|
|
|
|
2018-08-30 14:27:57 +01:00
|
|
|
updated_count, _ = dao_update_notifications_by_reference(
|
2017-09-20 11:12:37 +01:00
|
|
|
notification_references,
|
|
|
|
|
{
|
|
|
|
|
'status': NOTIFICATION_SENDING,
|
|
|
|
|
'sent_by': provider.identifier,
|
|
|
|
|
'sent_at': datetime.utcnow(),
|
|
|
|
|
'updated_at': datetime.utcnow()
|
|
|
|
|
}
|
|
|
|
|
)
|
2017-09-13 15:25:05 +01:00
|
|
|
|
2017-09-20 11:12:37 +01:00
|
|
|
current_app.logger.info("Updated {} letter notifications to sending".format(updated_count))
|
2017-09-13 15:25:05 +01:00
|
|
|
|
|
|
|
|
|
2017-09-20 11:12:37 +01:00
|
|
|
@notify_celery.task(bind=True, name='update-letter-notifications-to-error')
|
2017-04-18 11:42:48 +01:00
|
|
|
@statsd(namespace="tasks")
|
2017-09-20 11:12:37 +01:00
|
|
|
def update_letter_notifications_to_error(self, notification_references):
|
|
|
|
|
# This task will be called by the FTP app to update notifications as sent to DVLA
|
|
|
|
|
|
2018-08-30 14:27:57 +01:00
|
|
|
updated_count, _ = dao_update_notifications_by_reference(
|
2017-09-20 11:12:37 +01:00
|
|
|
notification_references,
|
|
|
|
|
{
|
|
|
|
|
'status': NOTIFICATION_TECHNICAL_FAILURE,
|
|
|
|
|
'updated_at': datetime.utcnow()
|
|
|
|
|
}
|
|
|
|
|
)
|
2018-03-16 17:18:44 +00:00
|
|
|
message = "Updated {} letter notifications to technical-failure with references {}".format(
|
|
|
|
|
updated_count, notification_references
|
|
|
|
|
)
|
2019-08-12 16:51:39 +01:00
|
|
|
raise NotificationTechnicalFailureException(message)
|
2017-04-18 11:42:48 +01:00
|
|
|
|
|
|
|
|
|
2017-01-17 16:51:27 +00:00
|
|
|
def handle_exception(task, notification, notification_id, exc):
|
|
|
|
|
if not get_notification_by_id(notification_id):
|
|
|
|
|
retry_msg = '{task} notification for job {job} row number {row} and notification id {noti}'.format(
|
|
|
|
|
task=task.__name__,
|
|
|
|
|
job=notification.get('job', None),
|
|
|
|
|
row=notification.get('row_number', None),
|
|
|
|
|
noti=notification_id
|
|
|
|
|
)
|
|
|
|
|
# Sometimes, SQS plays the same message twice. We should be able to catch an IntegrityError, but it seems
|
|
|
|
|
# SQLAlchemy is throwing a FlushError. So we check if the notification id already exists then do not
|
|
|
|
|
# send to the retry queue.
|
|
|
|
|
current_app.logger.exception('Retry' + retry_msg)
|
|
|
|
|
try:
|
2017-05-25 10:51:49 +01:00
|
|
|
task.retry(queue=QueueNames.RETRY, exc=exc)
|
2017-01-17 16:51:27 +00:00
|
|
|
except task.MaxRetriesExceededError:
|
2018-10-22 11:11:07 +01:00
|
|
|
current_app.logger.error('Max retry failed' + retry_msg)
|
2017-01-17 16:51:27 +00:00
|
|
|
|
|
|
|
|
|
2017-01-24 10:53:41 +00:00
|
|
|
def get_template_class(template_type):
|
|
|
|
|
if template_type == SMS_TYPE:
|
2017-01-17 16:51:27 +00:00
|
|
|
return SMSMessageTemplate
|
2017-01-24 10:53:41 +00:00
|
|
|
elif template_type in (EMAIL_TYPE, LETTER_TYPE):
|
2017-01-17 16:51:27 +00:00
|
|
|
# since we don't need rendering capabilities (we only need to extract placeholders) both email and letter can
|
|
|
|
|
# use the same base template
|
|
|
|
|
return WithSubjectTemplate
|
2017-05-12 17:39:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@notify_celery.task(bind=True, name='update-letter-notifications-statuses')
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def update_letter_notifications_statuses(self, filename):
|
2019-03-25 15:30:48 +00:00
|
|
|
notification_updates = parse_dvla_file(filename)
|
|
|
|
|
|
|
|
|
|
temporary_failures = []
|
|
|
|
|
|
|
|
|
|
for update in notification_updates:
|
|
|
|
|
check_billable_units(update)
|
|
|
|
|
update_letter_notification(filename, temporary_failures, update)
|
|
|
|
|
if temporary_failures:
|
|
|
|
|
# This will alert Notify that DVLA was unable to deliver the letters, we need to investigate
|
|
|
|
|
message = "DVLA response file: {filename} has failed letters with notification.reference {failures}" \
|
|
|
|
|
.format(filename=filename, failures=temporary_failures)
|
|
|
|
|
raise DVLAException(message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@notify_celery.task(bind=True, name="record-daily-sorted-counts")
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def record_daily_sorted_counts(self, filename):
|
|
|
|
|
sorted_letter_counts = defaultdict(int)
|
|
|
|
|
notification_updates = parse_dvla_file(filename)
|
|
|
|
|
for update in notification_updates:
|
|
|
|
|
sorted_letter_counts[update.cost_threshold.lower()] += 1
|
|
|
|
|
|
|
|
|
|
unknown_status = sorted_letter_counts.keys() - {'unsorted', 'sorted'}
|
|
|
|
|
if unknown_status:
|
|
|
|
|
message = 'DVLA response file: {} contains unknown Sorted status {}'.format(
|
|
|
|
|
filename, unknown_status.__repr__()
|
|
|
|
|
)
|
|
|
|
|
raise DVLAException(message)
|
|
|
|
|
|
|
|
|
|
billing_date = get_billing_date_in_bst_from_filename(filename)
|
|
|
|
|
persist_daily_sorted_letter_counts(day=billing_date,
|
|
|
|
|
file_name=filename,
|
|
|
|
|
sorted_letter_counts=sorted_letter_counts)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_dvla_file(filename):
|
2017-05-12 17:39:15 +01:00
|
|
|
bucket_location = '{}-ftp'.format(current_app.config['NOTIFY_EMAIL_DOMAIN'])
|
2017-05-22 10:12:18 +01:00
|
|
|
response_file_content = s3.get_s3_file(bucket_location, filename)
|
2017-05-12 17:39:15 +01:00
|
|
|
|
|
|
|
|
try:
|
2019-03-25 15:30:48 +00:00
|
|
|
return process_updates_from_file(response_file_content)
|
2017-05-12 17:39:15 +01:00
|
|
|
except TypeError:
|
2018-02-22 15:05:37 +00:00
|
|
|
raise DVLAException('DVLA response file: {} has an invalid format'.format(filename))
|
2018-02-20 10:57:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_billing_date_in_bst_from_filename(filename):
|
2018-09-04 16:09:56 +01:00
|
|
|
# exclude seconds from the date since we don't need it. We got a date ending in 60 second - which is not valid.
|
|
|
|
|
datetime_string = filename.split('-')[1][:-2]
|
|
|
|
|
datetime_obj = datetime.strptime(datetime_string, '%Y%m%d%H%M')
|
2018-02-20 10:57:25 +00:00
|
|
|
return convert_utc_to_bst(datetime_obj).date()
|
|
|
|
|
|
|
|
|
|
|
2018-03-14 17:04:58 +00:00
|
|
|
def persist_daily_sorted_letter_counts(day, file_name, sorted_letter_counts):
|
2018-02-20 10:57:25 +00:00
|
|
|
daily_letter_count = DailySortedLetter(
|
|
|
|
|
billing_day=day,
|
2018-03-14 17:04:58 +00:00
|
|
|
file_name=file_name,
|
2019-03-22 12:07:08 +00:00
|
|
|
unsorted_count=sorted_letter_counts['unsorted'],
|
|
|
|
|
sorted_count=sorted_letter_counts['sorted']
|
2018-02-20 10:57:25 +00:00
|
|
|
)
|
|
|
|
|
dao_create_or_update_daily_sorted_letter(daily_letter_count)
|
|
|
|
|
|
2017-05-22 10:12:18 +01:00
|
|
|
|
|
|
|
|
def process_updates_from_file(response_file):
|
|
|
|
|
NotificationUpdate = namedtuple('NotificationUpdate', ['reference', 'status', 'page_count', 'cost_threshold'])
|
|
|
|
|
notification_updates = [NotificationUpdate(*line.split('|')) for line in response_file.splitlines()]
|
|
|
|
|
return notification_updates
|
2017-06-20 17:13:40 +01:00
|
|
|
|
|
|
|
|
|
2018-02-22 15:05:37 +00:00
|
|
|
def update_letter_notification(filename, temporary_failures, update):
|
|
|
|
|
if update.status == DVLA_RESPONSE_STATUS_SENT:
|
|
|
|
|
status = NOTIFICATION_DELIVERED
|
|
|
|
|
else:
|
|
|
|
|
status = NOTIFICATION_TEMPORARY_FAILURE
|
|
|
|
|
temporary_failures.append(update.reference)
|
|
|
|
|
|
2018-08-30 14:27:57 +01:00
|
|
|
updated_count, _ = dao_update_notifications_by_reference(
|
2018-02-22 15:05:37 +00:00
|
|
|
references=[update.reference],
|
|
|
|
|
update_dict={"status": status,
|
|
|
|
|
"updated_at": datetime.utcnow()
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not updated_count:
|
|
|
|
|
msg = "Update letter notification file {filename} failed: notification either not found " \
|
|
|
|
|
"or already updated from delivered. Status {status} for notification reference {reference}".format(
|
|
|
|
|
filename=filename, status=status, reference=update.reference)
|
2019-03-22 14:06:45 +00:00
|
|
|
current_app.logger.info(msg)
|
2018-02-22 15:05:37 +00:00
|
|
|
|
|
|
|
|
|
2018-01-17 09:52:13 +00:00
|
|
|
def check_billable_units(notification_update):
|
2019-05-21 16:08:18 +01:00
|
|
|
notification = dao_get_notification_history_by_reference(notification_update.reference)
|
2018-01-17 09:52:13 +00:00
|
|
|
|
|
|
|
|
if int(notification_update.page_count) != notification.billable_units:
|
2018-09-27 16:13:15 +01:00
|
|
|
msg = 'Notification with id {} has {} billable_units but DVLA says page count is {}'.format(
|
2018-01-17 09:52:13 +00:00
|
|
|
notification.id, notification.billable_units, notification_update.page_count)
|
2018-07-24 16:28:30 +01:00
|
|
|
try:
|
|
|
|
|
raise DVLAException(msg)
|
|
|
|
|
except DVLAException:
|
|
|
|
|
current_app.logger.exception(msg)
|
2018-01-17 09:52:13 +00:00
|
|
|
|
|
|
|
|
|
2017-06-20 17:13:40 +01:00
|
|
|
@notify_celery.task(bind=True, name="send-inbound-sms", max_retries=5, default_retry_delay=300)
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def send_inbound_sms_to_service(self, inbound_sms_id, service_id):
|
|
|
|
|
inbound_api = get_service_inbound_api_for_service(service_id=service_id)
|
|
|
|
|
if not inbound_api:
|
|
|
|
|
# No API data has been set for this service
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
inbound_sms = dao_get_inbound_sms_by_id(service_id=service_id,
|
|
|
|
|
inbound_id=inbound_sms_id)
|
|
|
|
|
data = {
|
|
|
|
|
"id": str(inbound_sms.id),
|
2017-11-23 15:22:18 +00:00
|
|
|
# TODO: should we be validating and formatting the phone number here?
|
2017-06-22 10:15:08 +01:00
|
|
|
"source_number": inbound_sms.user_number,
|
|
|
|
|
"destination_number": inbound_sms.notify_number,
|
|
|
|
|
"message": inbound_sms.content,
|
2017-06-20 17:13:40 +01:00
|
|
|
"date_received": inbound_sms.provider_date.strftime(DATETIME_FORMAT)
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-01 16:15:21 +00:00
|
|
|
try:
|
|
|
|
|
response = request(
|
|
|
|
|
method="POST",
|
|
|
|
|
url=inbound_api.url,
|
|
|
|
|
data=json.dumps(data),
|
|
|
|
|
headers={
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
'Authorization': 'Bearer {}'.format(inbound_api.bearer_token)
|
|
|
|
|
},
|
|
|
|
|
timeout=60
|
|
|
|
|
)
|
As Notify matures we probably need less logging, especially to report happy path events.
This PR is a proposal to reduce the average messages we see for a single notification from about 7 messages to 2.
Messaging would change to something like this:
February 2nd 2018, 15:39:05.885 Full delivery response from Firetext for notification: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
{'status': ['0'], 'reference': ['8eda51d5-cd82-4569-bfc9-d5570cdf2126'], 'time': ['2018-02-02 15:39:01'], 'code': ['000']}
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:57.727 SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to provider firetext at 2018-02-02 15:38:56.716814
February 2nd 2018, 15:38:56.727 Starting sending SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 to provider at 2018-02-02 15:38:56.408181
February 2nd 2018, 15:38:56.727 Firetext request for 8eda51d5-cd82-4569-bfc9-d5570cdf2126 finished in 0.30376038211397827
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to the priority-tasks queue for delivery
To somthing like this:
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
2018-02-02 15:55:25 +00:00
|
|
|
current_app.logger.debug('send_inbound_sms_to_service sending {} to {}, response {}'.format(
|
2017-12-01 16:15:21 +00:00
|
|
|
inbound_sms_id,
|
|
|
|
|
inbound_api.url,
|
|
|
|
|
response.status_code
|
|
|
|
|
))
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
except RequestException as e:
|
|
|
|
|
current_app.logger.warning(
|
2018-10-23 13:59:52 +01:00
|
|
|
"send_inbound_sms_to_service failed for service_id: {} for inbound_sms_id: {} and url: {}. exc: {}".format(
|
2017-12-01 16:15:21 +00:00
|
|
|
service_id,
|
2018-10-24 16:19:17 +01:00
|
|
|
inbound_sms_id,
|
2017-12-01 16:15:21 +00:00
|
|
|
inbound_api.url,
|
|
|
|
|
e
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
if not isinstance(e, HTTPError) or e.response.status_code >= 500:
|
|
|
|
|
try:
|
|
|
|
|
self.retry(queue=QueueNames.RETRY)
|
|
|
|
|
except self.MaxRetriesExceededError:
|
2018-10-22 11:11:07 +01:00
|
|
|
current_app.logger.error(
|
2018-10-24 16:37:38 +01:00
|
|
|
"""Retry: send_inbound_sms_to_service has retried the max number of
|
|
|
|
|
times for service: {} and inbound_sms {}""".format(
|
2018-10-24 16:19:17 +01:00
|
|
|
service_id,
|
2018-10-22 11:11:07 +01:00
|
|
|
inbound_sms_id
|
|
|
|
|
)
|
|
|
|
|
)
|
2017-12-01 14:06:13 +00:00
|
|
|
|
|
|
|
|
|
2017-10-16 12:32:44 +01:00
|
|
|
@notify_celery.task(name='process-incomplete-jobs')
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def process_incomplete_jobs(job_ids):
|
2018-03-09 17:16:48 +00:00
|
|
|
jobs = [dao_get_job_by_id(job_id) for job_id in job_ids]
|
|
|
|
|
|
|
|
|
|
# reset the processing start time so that the check_job_status scheduled task doesn't pick this job up again
|
|
|
|
|
for job in jobs:
|
|
|
|
|
job.job_status = JOB_STATUS_IN_PROGRESS
|
|
|
|
|
job.processing_started = datetime.utcnow()
|
|
|
|
|
dao_update_job(job)
|
|
|
|
|
|
2017-10-16 12:32:44 +01:00
|
|
|
current_app.logger.info("Resuming Job(s) {}".format(job_ids))
|
|
|
|
|
for job_id in job_ids:
|
|
|
|
|
process_incomplete_job(job_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_incomplete_job(job_id):
|
2017-10-17 11:07:36 +01:00
|
|
|
job = dao_get_job_by_id(job_id)
|
2017-10-16 12:32:44 +01:00
|
|
|
|
2017-10-17 11:07:36 +01:00
|
|
|
last_notification_added = dao_get_last_notification_added_for_job_id(job_id)
|
2017-10-16 12:32:44 +01:00
|
|
|
|
|
|
|
|
if last_notification_added:
|
|
|
|
|
resume_from_row = last_notification_added.job_row_number
|
|
|
|
|
else:
|
|
|
|
|
resume_from_row = -1 # The first row in the csv with a number is row 0
|
|
|
|
|
|
|
|
|
|
current_app.logger.info("Resuming job {} from row {}".format(job_id, resume_from_row))
|
|
|
|
|
|
2019-11-08 10:30:26 +00:00
|
|
|
recipient_csv, template, sender_id = get_recipient_csv_and_template_and_sender_id(job)
|
2017-10-16 12:32:44 +01:00
|
|
|
|
2019-11-05 16:47:00 +00:00
|
|
|
for row in recipient_csv.get_rows():
|
2018-03-05 15:55:46 +00:00
|
|
|
if row.index > resume_from_row:
|
2019-11-08 10:30:26 +00:00
|
|
|
process_row(row, template, job, job.service, sender_id=sender_id)
|
2017-10-16 12:32:44 +01:00
|
|
|
|
2018-02-13 18:38:32 +00:00
|
|
|
job_complete(job, resumed=True)
|
2018-08-31 16:49:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@notify_celery.task(name='process-returned-letters-list')
|
|
|
|
|
@statsd(namespace="tasks")
|
|
|
|
|
def process_returned_letters_list(notification_references):
|
|
|
|
|
updated, updated_history = dao_update_notifications_by_reference(
|
|
|
|
|
notification_references,
|
|
|
|
|
{"status": NOTIFICATION_RETURNED_LETTER}
|
|
|
|
|
)
|
|
|
|
|
|
2019-12-09 16:23:09 +00:00
|
|
|
insert_or_update_returned_letters(notification_references)
|
|
|
|
|
|
2018-08-31 16:49:06 +01:00
|
|
|
current_app.logger.info(
|
|
|
|
|
"Updated {} letter notifications ({} history notifications, from {} references) to returned-letter".format(
|
|
|
|
|
updated, updated_history, len(notification_references)
|
|
|
|
|
)
|
|
|
|
|
)
|