def test_get_notification_from_different_api_key_of_same_type_succeeds(client, sample_notification, key_type): creation_api_key = ApiKey(service=sample_notification.service, name='creation_api_key', created_by=sample_notification.service.created_by, key_type=key_type) save_model_api_key(creation_api_key) querying_api_key = ApiKey(service=sample_notification.service, name='querying_api_key', created_by=sample_notification.service.created_by, key_type=key_type) save_model_api_key(querying_api_key) sample_notification.api_key = creation_api_key sample_notification.key_type = key_type dao_update_notification(sample_notification) response = client.get( path='/notifications/{}'.format(sample_notification.id), headers=_create_auth_header_from_key(querying_api_key)) assert response.status_code == 200 notification = json.loads(response.get_data(as_text=True))['data']['notification'] assert sample_notification.api_key_id != querying_api_key.id assert notification['id'] == str(sample_notification.id)
def send_sms_to_provider(notification): service = dao_fetch_service_by_id(notification.service_id) provider = provider_to_use(SMS_TYPE, notification.id) if notification.status == 'created': template_model = dao_get_template_by_id(notification.template_id, notification.template_version) template = SMSMessageTemplate( template_model.__dict__, values=notification.personalisation, prefix=service.name, sender=service.sms_sender ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: send_sms_response.apply_async( (provider.get_name(), str(notification.id), notification.to), queue='research-mode' ) notification.billable_units = 0 else: provider.send_sms( to=validate_and_format_phone_number(notification.to), content=str(template), reference=str(notification.id), sender=service.sms_sender ) notification.billable_units = template.fragment_count notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name() notification.status = 'sending' dao_update_notification(notification) current_app.logger.info( "SMS {} sent to provider at {}".format(notification.id, notification.sent_at) ) delta_milliseconds = (datetime.utcnow() - notification.created_at).total_seconds() * 1000 statsd_client.timing("sms.total-time", delta_milliseconds)
def malware_failure(notification): notification.status = NOTIFICATION_VIRUS_SCAN_FAILED dao_update_notification(notification) raise NotificationTechnicalFailureException( "Send {} for notification id {} to provider is not allowed. Notification contains malware".format( notification.notification_type, notification.id))
def create_letters_pdf(self, notification_id): try: notification = get_notification_by_id(notification_id, _raise=True) pdf_data, billable_units = get_letters_pdf( notification.template, contact_block=notification.reply_to_text, filename=notification.service.letter_branding and notification.service.letter_branding.filename, values=notification.personalisation) upload_letter_pdf(notification, pdf_data) if notification.key_type != KEY_TYPE_TEST: notification.billable_units = billable_units dao_update_notification(notification) current_app.logger.info( 'Letter notification reference {reference}: billable units set to {billable_units}' .format(reference=str(notification.reference), billable_units=billable_units)) except (RequestException, BotoClientError): try: current_app.logger.exception( "Letters PDF notification creation for id: {} failed".format( notification_id)) self.retry(queue=QueueNames.RETRY) except MaxRetriesExceededError: current_app.logger.error( "RETRY FAILED: task create_letters_pdf failed for notification {}" .format(notification_id), ) update_notification_status_by_id(notification_id, 'technical-failure')
def test_get_notification_from_different_api_key_of_same_type_succeeds(notify_api, sample_notification, key_type): with notify_api.test_request_context(), notify_api.test_client() as client: creation_api_key = ApiKey(service=sample_notification.service, name='creation_api_key', created_by=sample_notification.service.created_by, key_type=key_type) save_model_api_key(creation_api_key) querying_api_key = ApiKey(service=sample_notification.service, name='querying_api_key', created_by=sample_notification.service.created_by, key_type=key_type) save_model_api_key(querying_api_key) sample_notification.api_key = creation_api_key sample_notification.key_type = key_type dao_update_notification(sample_notification) response = client.get( path='/notifications/{}'.format(sample_notification.id), headers=_create_auth_header_from_key(querying_api_key)) assert response.status_code == 200 notification = json.loads(response.get_data(as_text=True))['data']['notification'] assert sample_notification.api_key_id != querying_api_key.id assert notification['id'] == str(sample_notification.id)
def technical_failure(notification): notification.status = NOTIFICATION_TECHNICAL_FAILURE dao_update_notification(notification) raise NotificationTechnicalFailureException( "Send {} for notification id {} to provider is not allowed: service {} is inactive" .format(notification.notification_type, notification.id, notification.service_id))
def process_precompiled_letter_notifications(*, letter_data, api_key, template, reply_to_text): try: status = NOTIFICATION_PENDING_VIRUS_CHECK letter_content = base64.b64decode(letter_data['content']) pages = pdf_page_count(io.BytesIO(letter_content)) except ValueError: raise BadRequestError(message='Cannot decode letter content (invalid base64 encoding)', status_code=400) except PdfReadError: current_app.logger.exception(msg='Invalid PDF received') raise BadRequestError(message='Letter content is not a valid PDF', status_code=400) notification = create_letter_notification(letter_data=letter_data, template=template, api_key=api_key, status=status, reply_to_text=reply_to_text) filename = upload_letter_pdf(notification, letter_content, precompiled=True) pages_per_sheet = 2 notification.billable_units = math.ceil(pages / pages_per_sheet) dao_update_notification(notification) current_app.logger.info('Calling task scan-file for {}'.format(filename)) # call task to add the filename to anti virus queue notify_celery.send_task( name=TaskNames.SCAN_FILE, kwargs={'filename': filename}, queue=QueueNames.ANTIVIRUS, ) return notification
def update_notification_to_sending(notification, provider): notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name() # We currently have no callback method for SNS # notification.status = NOTIFICATION_SENT if notification.international else NOTIFICATION_SENDING notification.status = NOTIFICATION_SENT if notification.notification_type == "sms" else NOTIFICATION_SENDING dao_update_notification(notification)
def update_notification(notification, provider, international=False): notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name() if international: notification.status = NOTIFICATION_SENT else: notification.status = NOTIFICATION_SENDING dao_update_notification(notification)
def fail_pii(notification, pii_type): notification.status = NOTIFICATION_CONTAINS_PII dao_update_notification(notification) raise NotificationTechnicalFailureException( "Send {} for notification id {} to provider is not allowed. Notification contains PII: {}".format( notification.notification_type, notification.id, pii_type))
def update_notification(notification, provider, status=None): notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name() if status is not None: notification.status = status else: notification.status = NOTIFICATION_SENDING dao_update_notification(notification)
def lookup_va_profile_id(self, notification_id): current_app.logger.info( f"Retrieving VA Profile ID from MPI for notification {notification_id}" ) notification = notifications_dao.get_notification_by_id(notification_id) try: va_profile_id = mpi_client.get_va_profile_id(notification) notification.recipient_identifiers.set( RecipientIdentifier(notification_id=notification.id, id_type=IdentifierType.VA_PROFILE_ID.value, id_value=va_profile_id)) notifications_dao.dao_update_notification(notification) current_app.logger.info( f"Successfully updated notification {notification_id} with VA PROFILE ID {va_profile_id}" ) except MpiRetryableException as e: current_app.logger.warning( f"Received {str(e)} for notification {notification_id}.") try: self.retry(queue=QueueNames.RETRY) except self.MaxRetriesExceededError: message = "RETRY FAILED: Max retries reached. " \ f"The task lookup_va_profile_id failed for notification {notification_id}. " \ "Notification has been updated to technical-failure" notifications_dao.update_notification_status_by_id( notification_id, NOTIFICATION_TECHNICAL_FAILURE, status_reason=e.failure_reason) raise NotificationTechnicalFailureException(message) from e except (BeneficiaryDeceasedException, IdentifierNotFound, MultipleActiveVaProfileIdsException) as e: message = f"{e.__class__.__name__} - {str(e)}: " \ f"Can't proceed after querying MPI for VA Profile ID for {notification_id}. " \ "Stopping execution of following tasks. Notification has been updated to permanent-failure." current_app.logger.warning(message) self.request.chain = None notifications_dao.update_notification_status_by_id( notification_id, NOTIFICATION_PERMANENT_FAILURE, status_reason=e.failure_reason) except Exception as e: message = f"Failed to retrieve VA Profile ID from MPI for notification: {notification_id} " \ "Notification has been updated to technical-failure" current_app.logger.exception(message) status_reason = e.failure_reason if hasattr( e, 'failure_reason') else 'Unknown error from MPI' notifications_dao.update_notification_status_by_id( notification_id, NOTIFICATION_TECHNICAL_FAILURE, status_reason=status_reason) raise NotificationTechnicalFailureException(message) from e
def send_sms_to_provider(notification): service = notification.service if not service.active: technical_failure(notification=notification) return if notification.status == "created": provider = provider_to_use( SMS_TYPE, notification.id, notification.international, notification.reply_to_text, ) template_dict = dao_get_template_by_id( notification.template_id, notification.template_version).__dict__ template = SMSMessageTemplate( template_dict, values=notification.personalisation, prefix=service.name, show_prefix=service.prefix_sms, ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: notification.reference = send_sms_response(provider.get_name(), notification.to) update_notification_to_sending(notification, provider) else: try: reference = provider.send_sms( to=validate_and_format_phone_number( notification.to, international=notification.international), content=str(template), reference=str(notification.id), sender=notification.reply_to_text, ) except Exception as e: notification.billable_units = template.fragment_count dao_update_notification(notification) dao_toggle_sms_provider(provider.name) raise e else: notification.reference = reference notification.billable_units = template.fragment_count update_notification_to_sending(notification, provider) # Record StatsD stats to compute SLOs statsd_client.timing_with_dates("sms.total-time", notification.sent_at, notification.created_at) statsd_key = f"sms.process_type-{template_dict['process_type']}" statsd_client.timing_with_dates(statsd_key, notification.sent_at, notification.created_at) statsd_client.incr(statsd_key)
def send_sms_to_provider(notification): service = notification.service if not service.active: technical_failure(notification=notification) return if notification.status == 'created': provider = provider_to_use(SMS_TYPE, notification.international) template_model = dao_get_template_by_id(notification.template_id, notification.template_version) template = SMSMessageTemplate( template_model.__dict__, values=notification.personalisation, prefix=service.name, show_prefix=service.prefix_sms, ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: update_notification_to_sending(notification, provider) send_sms_response(provider.get_name(), str(notification.id), notification.to) else: try: provider.send_sms(to=validate_and_format_phone_number( notification.to, international=notification.international), content=str(template), reference=str(notification.id), sender=notification.reply_to_text) except Exception as e: notification.billable_units = template.fragment_count dao_update_notification(notification) dao_reduce_sms_provider_priority( provider.get_name(), time_threshold=timedelta(minutes=1)) raise e else: notification.billable_units = template.fragment_count update_notification_to_sending(notification, provider) delta_seconds = (datetime.utcnow() - notification.created_at).total_seconds() statsd_client.timing("sms.total-time", delta_seconds) if notification.key_type == KEY_TYPE_TEST: statsd_client.timing("sms.test-key.total-time", delta_seconds) else: statsd_client.timing("sms.live-key.total-time", delta_seconds) if str(service.id) in current_app.config.get( 'HIGH_VOLUME_SERVICE'): statsd_client.timing("sms.live-key.high-volume.total-time", delta_seconds) else: statsd_client.timing("sms.live-key.not-high-volume.total-time", delta_seconds)
def send_sms_to_provider(notification): service = notification.service if not service.active: technical_failure(notification=notification) return if notification.status == 'created': provider = provider_to_use(SMS_TYPE, notification.id, notification.international) current_app.logger.debug( "Starting sending SMS {} to provider at {}".format( notification.id, datetime.utcnow())) template_model = dao_get_template_by_id(notification.template_id, notification.template_version) template = SMSMessageTemplate( template_model.__dict__, values=notification.personalisation, prefix=service.name, show_prefix=service.prefix_sms, ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: notification.billable_units = 0 update_notification(notification, provider) try: send_sms_response(provider.get_name(), str(notification.id), notification.to) except HTTPError: # when we retry, we only do anything if the notification is in created - it's currently in sending, # so set it back so that we actually attempt the callback again notification.sent_at = None notification.sent_by = None notification.status = NOTIFICATION_CREATED dao_update_notification(notification) raise else: try: provider.send_sms(to=validate_and_format_phone_number( notification.to, international=notification.international), content=str(template), reference=str(notification.id), sender=notification.reply_to_text) except Exception as e: dao_toggle_sms_provider(provider.name) raise e else: notification.billable_units = template.fragment_count update_notification(notification, provider, notification.international) current_app.logger.debug("SMS {} sent to provider {} at {}".format( notification.id, provider.get_name(), notification.sent_at)) delta_milliseconds = (datetime.utcnow() - notification.created_at).total_seconds() * 1000 statsd_client.timing("sms.total-time", delta_milliseconds)
def test_updating_notification_updates_notification_history(sample_notification): hist = NotificationHistory.query.one() assert hist.id == sample_notification.id assert hist.status == 'created' sample_notification.status = 'sending' dao_update_notification(sample_notification) hist = NotificationHistory.query.one() assert hist.id == sample_notification.id assert hist.status == 'sending'
def update_billable_units_for_letter(self, notification_id, page_count): notification = get_notification_by_id(notification_id, _raise=True) billable_units = get_billable_units_for_letter_page_count(page_count) if notification.key_type != KEY_TYPE_TEST: notification.billable_units = billable_units dao_update_notification(notification) current_app.logger.info( f"Letter notification id: {notification_id} reference {notification.reference}: " f"billable units set to {billable_units}")
def send_sms_to_provider(notification): service = notification.service if not service.active: technical_failure(notification=notification) return if notification.status == 'created': provider = provider_to_use(SMS_TYPE, notification, notification.international) template_model = dao_get_template_by_id(notification.template_id, notification.template_version) template = SMSMessageTemplate( template_model.__dict__, values=notification.personalisation, prefix=service.name, show_prefix=service.prefix_sms, ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: notification.reference = create_uuid() update_notification_to_sending(notification, provider) send_sms_response(provider.get_name(), str(notification.id), notification.to, notification.reference) else: try: reference = provider.send_sms( to=validate_and_format_phone_number( notification.to, international=notification.international), content=str(template), reference=str(notification.id), sender=notification.reply_to_text) except Exception as e: notification.billable_units = template.fragment_count dao_update_notification(notification) dao_toggle_sms_provider(provider.name) raise e else: notification.billable_units = template.fragment_count notification.reference = reference update_notification_to_sending(notification, provider) current_app.logger.info( f"Saved provider reference: {reference} for notification id: {notification.id}" ) delta_milliseconds = (datetime.utcnow() - notification.created_at).total_seconds() * 1000 statsd_client.timing("sms.total-time", delta_milliseconds)
def lookup_contact_info(self, notification_id): current_app.logger.info( f"Looking up contact information for notification_id:{notification_id}." ) notification = get_notification_by_id(notification_id) va_profile_id = notification.recipient_identifiers[ IdentifierType.VA_PROFILE_ID.value].id_value try: if EMAIL_TYPE == notification.notification_type: recipient = va_profile_client.get_email(va_profile_id) elif SMS_TYPE == notification.notification_type: recipient = va_profile_client.get_telephone(va_profile_id) else: raise NotImplementedError( f"The task lookup_contact_info failed for notification {notification_id}. " f"{notification.notification_type} is not supported") except VAProfileRetryableException as e: current_app.logger.exception(e) try: self.retry(queue=QueueNames.RETRY) except self.MaxRetriesExceededError: message = "RETRY FAILED: Max retries reached. " \ f"The task lookup_contact_info failed for notification {notification_id}. " \ "Notification has been updated to technical-failure" update_notification_status_by_id(notification_id, NOTIFICATION_TECHNICAL_FAILURE) raise NotificationTechnicalFailureException(message) from e except NoContactInfoException as e: message = f"{e.__class__.__name__} - {str(e)}: " \ f"Can't proceed after querying VA Profile for contact information for {notification_id}. " \ "Stopping execution of following tasks. Notification has been updated to permanent-failure." current_app.logger.warning(message) self.request.chain = None update_notification_status_by_id(notification_id, NOTIFICATION_PERMANENT_FAILURE) except VAProfileNonRetryableException as e: current_app.logger.exception(e) message = f"The task lookup_contact_info failed for notification {notification_id}. " \ "Notification has been updated to technical-failure" update_notification_status_by_id(notification_id, NOTIFICATION_TECHNICAL_FAILURE) raise NotificationTechnicalFailureException(message) from e else: notification.to = recipient dao_update_notification(notification)
def test_should_by_able_to_update_status_by_reference(sample_email_template, ses_provider): data = _notification_json(sample_email_template, status='sending') notification = Notification(**data) dao_create_notification(notification) assert Notification.query.get(notification.id).status == "sending" notification.reference = 'reference' dao_update_notification(notification) updated = update_notification_status_by_reference('reference', 'delivered') assert updated.status == 'delivered' assert Notification.query.get(notification.id).status == 'delivered'
def _process_for_status(notification_status, client_name, provider_reference, detailed_status_code=None): # record stats if client_name == 'Twilio': notification = notifications_dao.update_notification_status_by_reference( reference=provider_reference, status=notification_status) else: notification = notifications_dao.update_notification_status_by_id( notification_id=provider_reference, status=notification_status, sent_by=client_name.lower(), detailed_status_code=detailed_status_code) if not notification: return statsd_client.incr('callback.{}.{}'.format(client_name.lower(), notification_status)) if notification.sent_at: statsd_client.timing_with_dates( 'callback.{}.elapsed-time'.format(client_name.lower()), datetime.utcnow(), notification.sent_at) if notification.billable_units == 0: service = notification.service template_model = dao_get_template_by_id(notification.template_id, notification.template_version) template = SMSMessageTemplate( template_model.__dict__, values=notification.personalisation, prefix=service.name, show_prefix=service.prefix_sms, ) notification.billable_units = template.fragment_count notifications_dao.dao_update_notification(notification) if notification_status != NOTIFICATION_PENDING: service_callback_api = get_service_delivery_status_callback_api_for_service( service_id=notification.service_id) # queue callback task only if the service_callback_api exists if service_callback_api: encrypted_notification = create_delivery_status_callback_data( notification, service_callback_api) send_delivery_status_to_service.apply_async( [str(notification.id), encrypted_notification], queue=QueueNames.CALLBACKS)
def test_should_not_update_status_one_notification_status_is_delivered(notify_db, notify_db_session, sample_email_template, ses_provider): notification = sample_notification(notify_db=notify_db, notify_db_session=notify_db_session, template=sample_email_template, status='sending') assert Notification.query.get(notification.id).status == "sending" notification.reference = 'reference' dao_update_notification(notification) update_notification_status_by_reference('reference', 'delivered') assert Notification.query.get(notification.id).status == 'delivered' update_notification_status_by_reference('reference', 'failed') assert Notification.query.get(notification.id).status == 'delivered'
def send_email_to_provider(notification): service = dao_fetch_service_by_id(notification.service_id) provider = provider_to_use(EMAIL_TYPE, notification.id) if notification.status == 'created': template_dict = dao_get_template_by_id(notification.template_id, notification.template_version).__dict__ html_email = HTMLEmailTemplate( template_dict, values=notification.personalisation, **get_html_email_options(service) ) plain_text_email = PlainTextEmailTemplate( template_dict, values=notification.personalisation ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: reference = str(create_uuid()) send_email_response.apply_async( (provider.get_name(), reference, notification.to), queue='research-mode' ) notification.billable_units = 0 else: from_address = '"{}" <{}@{}>'.format(service.name, service.email_from, current_app.config['NOTIFY_EMAIL_DOMAIN']) reference = provider.send_email( from_address, notification.to, plain_text_email.subject, body=str(plain_text_email), html_body=str(html_email), reply_to_address=service.reply_to_email_address, ) notification.reference = reference notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name(), notification.status = 'sending' dao_update_notification(notification) current_app.logger.info( "Email {} sent to provider at {}".format(notification.id, notification.sent_at) ) delta_milliseconds = (datetime.utcnow() - notification.created_at).total_seconds() * 1000 statsd_client.timing("email.total-time", delta_milliseconds)
def _sanitise_precompiled_pdf(self, notification, precompiled_pdf): try: response = requests_post( '{}/precompiled/sanitise'.format( current_app.config['TEMPLATE_PREVIEW_API_HOST']), data=precompiled_pdf, headers={ 'Authorization': 'Token {}'.format( current_app.config['TEMPLATE_PREVIEW_API_KEY']), 'Service-ID': str(notification.service_id), 'Notification-ID': str(notification.id) }) response.raise_for_status() return response.json(), "validation_passed" except RequestException as ex: if ex.response is not None and ex.response.status_code == 400: message = "sanitise_precompiled_pdf validation error for notification: {}. ".format( notification.id) if response.json().get("message"): message += response.json()["message"] if response.json().get("invalid_pages"): message += ( " on pages: " + ", ".join(map(str, response.json()["invalid_pages"]))) current_app.logger.info(message) return response.json(), "validation_failed" try: current_app.logger.exception( "sanitise_precompiled_pdf failed for notification: {}".format( notification.id)) self.retry(queue=QueueNames.RETRY) except MaxRetriesExceededError: current_app.logger.error( "RETRY FAILED: sanitise_precompiled_pdf failed for notification {}" .format(notification.id), ) notification.status = NOTIFICATION_TECHNICAL_FAILURE dao_update_notification(notification) raise
def _sanitise_precompiled_pdf(self, notification, precompiled_pdf): try: response = requests_post( "{}/precompiled/sanitise".format( current_app.config["TEMPLATE_PREVIEW_API_HOST"]), data=precompiled_pdf, headers={ "Authorization": "Token {}".format( current_app.config["TEMPLATE_PREVIEW_API_KEY"]), "Service-ID": str(notification.service_id), "Notification-ID": str(notification.id), }, ) response.raise_for_status() return response except RequestException as ex: if ex.response is not None and ex.response.status_code == 400: message = "sanitise_precompiled_pdf validation error for notification: {}. ".format( notification.id) if "message" in response.json(): message += response.json()["message"] current_app.logger.info(message) return None try: current_app.logger.exception( "sanitise_precompiled_pdf failed for notification: {}".format( notification.id)) self.retry(queue=QueueNames.RETRY) except MaxRetriesExceededError: current_app.logger.error( "RETRY FAILED: sanitise_precompiled_pdf failed for notification {}" .format(notification.id), ) notification.status = NOTIFICATION_TECHNICAL_FAILURE dao_update_notification(notification) raise
def test_update_notification(sample_notification, sample_template): assert sample_notification.status == 'created' sample_notification.status = 'failed' dao_update_notification(sample_notification) notification_from_db = Notification.query.get(sample_notification.id) assert notification_from_db.status == 'failed'
def set_notification_sent_by(notification, client_name): notification.sent_by = client_name dao_update_notification(notification)
def update_notification_to_sending(notification, provider): notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name() notification.status = NOTIFICATION_SENDING dao_update_notification(notification)
def send_sms_to_provider(notification): service = notification.service if not service.active: technical_failure(notification=notification) return if notification.status == 'created': # TODO: issue is that this does not get the provider based on who owns # the inbound number. The notification.reply_to_text below is the phone # number that we should send from, but we need to look at that and see # who the provider is. # TODO: now that we do get the right provider, the issue is that the # reply to text could be different because the service is able to choose # the sender when sending a message. So we need to check if the sender # ID that was chosen is also an inbound number. provider = None preferred_provider = get_preferred_sms_provider(service) if preferred_provider: provider = get_sms_provider_client(preferred_provider, notification.id) else: provider = provider_to_use(SMS_TYPE, notification.id, notification.international) current_app.logger.debug( "Starting sending SMS {} to provider at {}".format( notification.id, datetime.utcnow())) template_model = dao_get_template_by_id(notification.template_id, notification.template_version) template = SMSMessageTemplate( template_model.__dict__, values=notification.personalisation, prefix=service.name, show_prefix=service.prefix_sms, ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: notification.billable_units = 0 update_notification(notification, provider) try: send_sms_response(provider.get_name(), str(notification.id), notification.to) except HTTPError: # when we retry, we only do anything if the notification is in created - it's currently in sending, # so set it back so that we actually attempt the callback again notification.sent_at = None notification.sent_by = None notification.status = NOTIFICATION_CREATED dao_update_notification(notification) raise else: status = None try: reference, status = provider.send_sms( to=notification.normalised_to, content=str(template), reference=str(notification.id), sender=notification.reply_to_text) notification.reference = reference notification.billable_units = template.fragment_count # An international notification (i.e. a text message with an # abroad recipient phone number) instantly get marked as "sent". # It might later get marked as "delivered" when the provider # status callback is triggered. if notification.international: status = NOTIFICATION_SENT except Exception as e: dao_toggle_sms_provider(provider.name) raise e else: update_notification(notification, provider, status=status) current_app.logger.debug("SMS {} sent to provider {} at {}".format( notification.id, provider.get_name(), notification.sent_at)) delta_milliseconds = (datetime.utcnow() - notification.created_at).total_seconds() * 1000 statsd_client.timing("sms.total-time", delta_milliseconds)
def update_notification_to_sending(notification, provider): notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name() if notification.status not in NOTIFICATION_STATUS_TYPES_COMPLETED: notification.status = NOTIFICATION_SENT if notification.international else NOTIFICATION_SENDING dao_update_notification(notification)