HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: //home/arjun/projects/buyercall/buyercall/blueprints/api2/doc/endpoints/phonenumber.py
import logging
import traceback

from flask import (
    Blueprint,
    make_response,
    request,
    current_app as app,
    jsonify)
from flask_restx import Resource, reqparse
from flask import Blueprint, jsonify, make_response
from buyercall.lib.util_rest import rest_partnership_account, rest_partnership, requires_auth, rest_is_partnership_account, rest_is_partnership
from buyercall.blueprints.partnership.models import Partnership, PartnershipAccount
from buyercall.blueprints.api2.doc import serializers
from buyercall.blueprints.api2.doc.utils import api_validation
from buyercall.blueprints.api2.restx import api
from buyercall.blueprints.api2.doc.twilio_error_codes import errors as twilio_errors
from buyercall.blueprints.agents.models import Agent
from buyercall.blueprints.phonenumbers.models import Phone
from buyercall.blueprints.phonenumbers.views import purchase_bw, purchase_twilio, bandwidth_search, twilio_search, ClientError
from buyercall.lib.bandwidth import BandwidthException
from twilio.base.exceptions import TwilioRestException
from buyercall.lib.util_twilio import (
    bw_client,
    account_client,
    subaccount_client,
)
from buyercall.extensions import db
from sqlalchemy import func, or_, and_, extract

log = logging.getLogger(__name__)
# ns = api.namespace('Operational Phone Numbers', description='Operations related to operational phone numbers.', path='/accounts')
validate = api_validation()

parser = reqparse.RequestParser()
parser.add_argument('type', type=str, location='args', help='The type of phone number')
parser.add_argument('city', type=str, location='args', help='The city')
parser.add_argument('state', type=str, location='args', help='The state')
parser.add_argument('code', type=str, location='args', help='The area code')


# @ns.route('/<int:paid>/phonenumbers/availablenumbers')
@api.doc(responses={200: 'OK',
                    400: 'Error performing operation.',
                    401: 'Unauthorized request.'},
            params={'paid': 'The partner account Id'})
class ApiSearchOperationalPhoneNumbers(Resource):
    @api.expect(parser)
    @requires_auth
    def get(self, paid):
        """
        Retrieves the phone numbers of a partnership account.

        <p>
        The Operational Available Numbers API GET endpoint should be used to find all available phone numbers based on the
        type, which is either 'priority' or 'tracking', city and state. The type will always be required and it's recommended
        that 'priority' be used as type if you are not sure which one to specify.
        </p>
        <br />
        <p>
        Please note we can not guarantee that a specific phone number is available. If no results are returned for a
        specific city and state revert to only search per State, which will return a broader search result.
        </p>
        <br />
        <p>
        You will require a partner authentication token, a partner account id as well as a type, city and state
        search parameters to make a successful request. A response will
        be returned, similar to the example below, based
        on a successful request:
        <br />
        <br />
        </p>
         <pre class="code-background" style="color: white">
        {
          "available_numbers": [
            "+18155270745",
            "+16305244210",
            "+17089720144",
            "+17083152763",
            "+12247231062",
            "+17084983608",
            "+18472436331",
            "+13092132751",
            "+18722137423",
            "+13312254096",
            "+18475586861",
            "+12173963458",
            "+16305175757",
            "+13126355076",
            "+17087940908",
            "+16185375044",
            "+17082942628",
            "+12243343707",
            "+12246548160",
            "+13122168626",
            "+17086956274",
            "+17737895292",
            "+16185051251",
            "+16182056022",
            "+16302803524",
            "+18477448004",
            "+13094080767",
            "+18477448197",
            "+16307967753",
            "+18472436156"
          ]
        }
        </pre>
        """
        if rest_is_partnership and rest_partnership is not None:
            numbers_list = []
            error_message = 'An error occurred performing the search. '

            partnership_account = PartnershipAccount\
                .query\
                .filter(and_(PartnershipAccount.id == paid, PartnershipAccount.partnership_id == rest_partnership.id))\
                .first()

            if partnership_account is not None:
                tollfree = False
                phrase = ''
                prefix = ''
                args = parser.parse_args()
                type = args['type']
                city = args['city']
                state = args['state']
                area_code = args['code']

                #tollfree = (request.args.get('tollfree') == 'true')
                #type = request.args['type']
                #code = request.args['code']
                #phrase = request.args.get('phrase')
                #phrase = '*{}*'.format(phrase) if phrase else None

                if type is None:
                    type = ''

                if city is None:
                    city = ''

                if state is None:
                    state = ''

                if area_code is None:
                    area_code = ''

                # What is the provider we're searching?
                provider = 'bandwidth' if type in ['tracking', 'mobile'] else 'twilio'

                try:
                    if provider == 'bandwidth':
                        numbers_list = bandwidth_search(tollfree, area_code, phrase, city, state,
                                                        partnership_account.id, tn_type=type)
                    else:
                        numbers_list = twilio_search(tollfree, area_code, phrase, city, state, partnership_account.id)

                    return numbers_list

                except BandwidthException as e:
                    log.error(traceback.format_exc())
                    error_array = e.message.split('DETAIL: ')

                    if len(error_array) >= 2:
                        error_message = error_message + error_array[1]

                    return api.abort(400, error_message)

                except ClientError as e:
                    log.error(traceback.format_exc())
                    error_message = error_message + e.message

                    return api.abort(400, error_message)

                except TwilioRestException as e:
                    log.error(traceback.format_exc())

                    if str(e.code) in twilio_errors:
                        error_message = error_message + twilio_errors[str(e.code)]
                    else:
                        error_message = error_message + e.msg

                    return api.abort(400, error_message)

                except Exception as e:
                    log.error(traceback.format_exc())
                    error_details = e.message.split('DETAIL: ')

                    if error_details is not None and len(error_details) >= 2:
                        error_message = error_details[1]
                    else:
                        error_message = 'An unexpected error has occurred.'
                    return api.abort(400, error_message)
            else:
                return api.abort(code=400, message="Partnership account not found.")
        else:
            api.abort(401)


# @ns.route('/<int:paid>/phonenumbers')
@api.doc(responses={200: 'OK',
                    400: 'Error performing operation.',
                    401: 'Unauthorized request.'},
            params={'paid': 'The partner account Id'})
class ApiOperationalPhoneNumbers(Resource):
    @api.response(200, 'Phone number successfully provisioned.')
    @api.expect(serializers.phonenumber_add, validate=True)
    @requires_auth
    def post(self, paid):
        """
        Provision a phone number.
        <p>
        The Operational Phone Number API POST endpoint should be used to provision either a priority or
        tracking phone number. It's recommended that 'priority' be used as type if all phone number
        features will be utilized. Priority operational phone numbers can be extensively configured to perform
        various routing scenarios as well the forwarding of calls to multiple agents. Tracking operational phone numbers
        are very basic forwarding phone numbers. By default all phone numbers provisioned through this endpoint will be 'priority' phone numbers unless the type
        is explicitly changed in the request body payload.
        </p>
        <br />
        <p>
        It's important to note that you need to perform an available phone number lookup using the Operational Available
        Phone Number GET request endpoint before provisioning a number. Once you've found an available phone number, you
        will be required to passed the available phone number through the phonenumber field during this POST request to
        actually provision the phone number.
        </p>
        <br />
        <p>
        You require a partner authentication token, a partner account id and an available phone number to
        make a successful request. A response will
        be returned, similar to the example below, based
        on a successful request:
        <br />
        <br />
        </p>
         <pre class="code-background" style="color: white">
        {
          "partnership_account_id": 195,
          "phonenumber": "+13367394103",
          "phonenumber_id": 175
        }
        </pre>

        """
        if rest_is_partnership and rest_partnership is not None:
            error_message = 'An error occurred provisioning number. '
            partnership_account = PartnershipAccount\
                .query\
                .filter(and_(PartnershipAccount.id == paid, PartnershipAccount.partnership_id == rest_partnership.id))\
                .first()

            if partnership_account is not None:
                received = request.json
                routing = Phone(
                    partnership_account_id=partnership_account.id
                )

                if validate.phone_number(received['phonenumber']):
                    routing.phonenumber = received['phonenumber']
                    routing.type = received['type']
                    routing.local = received['local']
                    routing.tollfree = False
                    routing.friendly_name = received['friendly_name']
                    routing.routing_config = received['routing_config']
                    routing.routing_config['routingType'] = 'default'
                    routing.source = ''
                    routing.channel = None

                    if 'defaultRouting' in routing.routing_config:
                        default_routine = routing.routing_config['defaultRouting']
                        default_routine['dialDigit'] = False
                        default_routine['digitalPrompt'] = False

                    if 'source' in received:
                        routing.source = received['source']

                    if 'channel' in received:
                        routing.channel = received['channel']

                    provider = 'bandwidth' if received['type'] in ['tracking', 'mobile'] else 'twilio'
                    routing.provider = provider

                    log.error(str(routing.routing_config))

                    try:
                        log.info('Purchasing number from {}'.format(provider))
                        if provider == 'bandwidth':
                            purchase_bw(routing, True, partnership_account)
                        elif provider == 'twilio':
                            purchase_twilio(routing, True, partnership_account)

                        routing.connect_audio_files()

                        return jsonify(partnership_account_id=partnership_account.id,
                                   phonenumber_id=routing.id,
                                   phonenumber=routing.phonenumber)

                    except BandwidthException as e:
                        log.error(traceback.format_exc())
                        error_array = e.message.split('DETAIL: ')

                        if len(error_array) >= 2:
                            error_message = error_message + error_array[1]

                        return api.abort(400, error_message)

                    except TwilioRestException as e:
                        log.error(traceback.format_exc())

                        if str(e.code) in twilio_errors:
                            error_message = error_message + twilio_errors[str(e.code)]
                        else:
                            error_message = error_message + e.msg

                        return api.abort(400, error_message)

                    except Exception as e:
                        log.error(traceback.format_exc())
                        db.session.rollback()
                        error_details = e.message.split('DETAIL: ')

                        if error_details is not None and len(error_details) >= 2:
                            error_message = error_details[1]
                        else:
                            error_message = 'Error provisioning phone number.'
                        return api.abort(code=400, message=error_message)
                else:
                    return api.abort(code=400, message="Invalid mobile number provided. Number: " + str(received['phonenumber']))
            else:
                return api.abort(code=400, message="Partnership account not found.")
        else:
            api.abort(401)


# @ns.route('/<int:paid>/phonenumbers/<int:pnid>')
@api.doc(responses={200: 'OK',
                    400: 'Error performing operation.',
                    401: 'Unauthorized request.',
                    404: 'Phone number not found.',
                    },
            params={'paid': 'The partner account Id', 'pnid': 'The phone number Id'})
class ApiOperationalPhoneNumbers(Resource):
    @requires_auth
    def get(self, paid, pnid):
        """
        Get a phone number.

        <p>
        The Operational Phone Number GET endpoint should be used to return all information on a specific
        phone number for a partner account.
        </p>
        <br />
        <p>
        You require a partner authentication token and a partner account id as well as a phone number id to make a
        successful request. A response will
        be returned, similar to the example below, based
        on a successful request:
        <br />
        <br />
        </p>
         <pre class="code-background" style="color: white">
        {
          "channel": "Organic Search",
          "deactivated_on": "",
          "friendly_name": "API Testing Number",
          "id": 60,
          "is_deactivated": false,
          "local": true,
          "phonenumber": "+13367394103",
          "routing_config": {
            "SMSAutoReply": false,
            "SMSAutoReplyImage": false,
            "SMSAutoReplyImageUrl": "",
            "SMSAutoReplyText": "Thanks for the message. We'll be in touch.",
            "configSMSSetup": false,
            "defaultRouting": {
              "agents": [
                {
                  "contactUsing": "phone",
                  "fullName": "Sue Matthews",
                  "id": 2
                }
              ],
              "callOrder": "sequence",
              "retryRouting": 0
            },
            "greetingMessage": false,
            "language": "en",
            "recordCalls": false,
            "voicemail": true,
            "voicemailMessage": "Sorry, we are not available at the moment. Please leave a voice message and we'll get back to you as soon as possible.",
            "voicemailMessageType": "text",
            "whisperMessage": "",
            "whisperMessageType": "",
            "transcribeAnsweredCall": false,
            "transcribeVoiceMail": false
          },
          "source": ""
        }
        </pre>
        """
        if rest_is_partnership and rest_partnership is not None:
            partnership_account = PartnershipAccount\
                .query\
                .filter(and_(PartnershipAccount.id == paid, PartnershipAccount.partnership_id == rest_partnership.id))\
                .first()

            if partnership_account is not None:
                phone = Phone.query \
                    .filter(and_(Phone.id == pnid, Phone.partnership_account_id == paid)) \
                    .filter(or_(Phone.type == 'tracking', Phone.type == 'priority')) \
                    .first()

                if phone is not None:
                    try:
                        agent_list = []
                        defaultRouting = phone.routing_config['defaultRouting']
                        result_call_order = None
                        result_retry_routing = None
                        agents = defaultRouting['agents']
                        result_deactivated_on = ''

                        if phone.is_deactivated:
                            if phone.deactivated_on is not None:
                                result_deactivated_on = phone.deactivated_on.strftime('%Y-%m-%d %H:%M:%S')

                        if 'callOrder' in defaultRouting:
                            result_call_order = defaultRouting['callOrder']

                        if 'retryRouting' in defaultRouting:
                            result_retry_routing = defaultRouting['retryRouting']

                        for agent in agents:
                            contactAgentUsing = 'none'

                            if 'contactUsing' in agent:
                                contactAgentUsing = agent['contactUsing']

                            add_agent = {
                                'fullName': agent['fullName'],
                                'id': agent['id'],
                                'contactUsing': contactAgentUsing
                            }
                            agent_list.append(add_agent)

                        result_default_routing = {
                            'agents': agent_list,
                            'callOrder': result_call_order,
                            'retryRouting': result_retry_routing
                        }

                        if 'SMSAutoReply' in phone.routing_config:
                            result_sms_auto_reply = phone.routing_config['SMSAutoReply']
                        else:
                            result_sms_auto_reply = ''

                        if 'SMSAutoReplyImage' in phone.routing_config:
                            result_sms_auto_image = phone.routing_config['SMSAutoReplyImage']
                        else:
                            result_sms_auto_image = ''

                        if 'SMSAutoReplyImageUrl' in phone.routing_config:
                            result_sms_auto_image_url = phone.routing_config['SMSAutoReplyImageUrl']
                        else:
                            result_sms_auto_image_url = ''

                        if 'SMSAutoReplyText' in phone.routing_config:
                            result_sms_auto_reply_text = phone.routing_config['SMSAutoReplyText'] + ' Reply STOP to unsubscribe.'
                        else:
                            result_sms_auto_reply_text = ''

                        if 'configSMSSetup' in phone.routing_config:
                            result_config_sms_setup = phone.routing_config['configSMSSetup']
                        else:
                            result_config_sms_setup = ''

                        if 'greetingMessage' in phone.routing_config:
                            result_greeting_message = phone.routing_config['greetingMessage']
                        else:
                            result_greeting_message = ''

                        if 'whisperMessage' in phone.routing_config:
                            result_whisper_message = phone.routing_config['whisperMessage']
                        else:
                            result_whisper_message = ''

                        if 'whisperMessageType' in phone.routing_config:
                            result_whisper_message_type = phone.routing_config['whisperMessageType']
                        else:
                            result_whisper_message_type = ''

                        if 'voicemail' in phone.routing_config:
                            result_voicemail = phone.routing_config['voicemail']
                        else:
                            result_voicemail = ''

                        if 'voicemailMessage' in phone.routing_config:
                            result_voicemail_message = phone.routing_config['voicemailMessage']
                        else:
                            result_voicemail_message = ''

                        if 'voicemailMessageType' in phone.routing_config:
                            voicemail_message_ype = phone.routing_config['voicemailMessageType']
                        else:
                            result_whisper_message_type = ''
                        # Set the transcribe answered call boolean if it's not present in JSON routing. This is to cover
                        # old numbers with old settings
                        if 'transcribeAnsweredCall' in phone.routing_config and \
                                phone.routing_config['transcribeAnsweredCall'] is not None:
                            result_transcribe_answered_call = phone.routing_config['transcribeAnsweredCall']
                        else:
                            result_transcribe_answered_call = False
                        # Set the transcribe voicemail boolean if it's not present in JSON routing. This is to cover
                        # old numbers with old settings
                        if 'transcribeVoiceMail' in phone.routing_config and \
                                phone.routing_config['transcribeVoiceMail'] is not None:
                            result_transcribe_voicemail = phone.routing_config['transcribeVoiceMail']
                        else:
                            result_transcribe_voicemail = False

                        result_routing_config = {
                            'defaultRouting': result_default_routing,
                            'SMSAutoReply': result_sms_auto_reply,
                            'SMSAutoReplyImage': result_sms_auto_image,
                            'SMSAutoReplyImageUrl': result_sms_auto_image_url,
                            'SMSAutoReplyText': result_sms_auto_reply_text,
                            'configSMSSetup': result_config_sms_setup,
                            'greetingMessage': result_greeting_message,
                            'whisperMessageType': result_whisper_message_type,
                            'whisperMessage': result_whisper_message,
                            'language': phone.routing_config['language'],
                            'recordCalls': phone.routing_config['recordCalls'],
                            #'routingType': phone.routing_config['routingType'],
                            'voicemail': phone.routing_config['voicemail'],
                            'voicemailMessage': phone.routing_config['voicemailMessage'],
                            'voicemailMessageType': phone.routing_config['voicemailMessageType'],
                            'transcribeAnsweredCall': result_transcribe_answered_call,
                            'transcribeVoiceMail': result_transcribe_voicemail
                        }

                        return jsonify(
                            id=phone.id,
                            phonenumber=phone.phonenumber,
                            local=phone.local,
                            #tollfree=phone.tollfree,
                            friendly_name=phone.friendly_name,
                            source=phone.source,
                            channel=phone.channel,
                            routing_config=result_routing_config,
                            is_deactivated=phone.is_deactivated,
                            deactivated_on=result_deactivated_on
                        )
                    except Exception as e:
                        log.error(traceback.format_exc())

                        return api.abort(code=500, message="Error retrieving phone number.")
                else:
                    return api.abort(code=404, message="Phone number not found. Are your sure this is a priority/tacking number?")
            else:
                return api.abort(code=400, message="Partnership account not found.")
        else:
            api.abort(401)

    @api.response(204, 'Phone number successfully deleted.')
    @requires_auth
    def delete(self, paid, pnid):
        """
        Delete a phone number.

        <p>
        The Operational Phone Numbers DELETE endpoint should be used to deactivate an Operational Phone Number for a
        partner account. When a phone number is deactivated the actual phone number has been released to be provisioned
        by someone else. However, we do not delete the phone number record for legacy purposes and the deactivated
        phone number will still appear in the Operational Phone Number GET request results.
        </p>
        <br />
        <p>
        You will require a partner authentication token, a partner account id as well as a phone number id to make a
        successful request.
        </p>
        """
        if rest_is_partnership and rest_partnership is not None:
            partnership_account = PartnershipAccount \
                .query \
                .filter(and_(PartnershipAccount.id == paid, PartnershipAccount.partnership_id == rest_partnership.id)) \
                .first()

            if partnership_account is not None:
                try:
                    result = Phone.api_delete(pnid, paid)

                    if result:
                        return True, 204
                    else:
                        return api.abort(code=400, message="Error deleting phone number.")
                except Exception as e:
                    log.error('Error deleting phone number. Error: ' + str(e))
                    return api.abort(code=400, message="Error deleting phone number.")
            else:
                return api.abort(code=400, message="Partnership account not found.")
        else:
            return api.abort(401)

    @api.response(204, 'Phone number successfully updated.')
    @api.expect(serializers.phonenumber_edit, validate=True)
    @requires_auth
    def put(self, paid, pnid):
        """
        Update a phone number.

        <p>
        The Operational Phone Numbers PUT endpoint should be used to update an Operational Phone Number for a
        partner account. Please note you can't change the type or the actual phone number when performing an PUT
        request. You can only update the configuration and settings for a phone number.
        </p>
        <br />
        <p>
        You will require a partner authentication token, a partner account id as well as a phone number id to make a
        successful request.
        </p>
        """
        if rest_is_partnership and rest_partnership is not None:
            agent_error = False

            partnership_account = PartnershipAccount \
                .query \
                .filter(and_(PartnershipAccount.id == paid, PartnershipAccount.partnership_id == rest_partnership.id)) \
                .first()

            if partnership_account is not None:
                try:
                    received = request.json
                    result_routing_type = 'default'
                    result_recordCalls = None
                    result_voicemail = None
                    result_voicemail_message = None
                    result_voicemail_message_type = None
                    result_whisper_message = None
                    result_whisper_message_type = None
                    result_configSMSSetup = None
                    result_greetingMessage = None
                    result_language = None
                    result_SMSAutoReply = None
                    result_SMSAutoReplyImage = None
                    result_SMSAutoReplyImageUrl = None
                    result_SMSAutoReplyText = None
                    result_dial_digit = None
                    result_transcribe_answered_call = None
                    result_transcribe_voicemail = None

                    result_channel = None
                    result_source = None
                    result_friendly_name = None
                    result_routing_config = None

                    result_default_config = None
                    result_agents = []
                    result_agents_final = []
                    result_call_order = None
                    result_retry_routing = None
                    result_digital_prompt = None

                    if 'friendly_name' in received:
                        result_friendly_name = received['friendly_name']

                    if 'source' in received:
                        result_source = received['source']

                    if 'channel' in received:
                        result_channel = received['channel']

                    if 'routing_config' in received:
                        result_routing_config = received['routing_config']
                        result_routing_config['routingType'] = 'default'

                        if 'recordCalls' in result_routing_config:
                            result_recordCalls = result_routing_config['recordCalls']

                        if 'configSMSSetup' in result_routing_config:
                            result_configSMSSetup = result_routing_config['configSMSSetup']

                        if 'greetingMessage' in result_routing_config:
                            result_greetingMessage = result_routing_config['greetingMessage']

                        if 'language' in result_routing_config:
                            result_language = result_routing_config['language']

                        if 'SMSAutoReply' in result_routing_config:
                            result_SMSAutoReply = result_routing_config['SMSAutoReply']

                        if 'SMSAutoReplyImage' in result_routing_config:
                            result_SMSAutoReplyImage = result_routing_config['SMSAutoReplyImage']

                        if 'SMSAutoReplyImageUrl' in result_routing_config:
                            result_SMSAutoReplyImageUrl = result_routing_config['SMSAutoReplyImageUrl']

                        if 'SMSAutoReplyText' in result_routing_config:
                            result_SMSAutoReplyText = result_routing_config['SMSAutoReplyText'] + ' Reply STOP to unsubscribe.'

                        if 'voicemail' in result_routing_config:
                            result_voicemail = result_routing_config['voicemail']

                        if 'voicemailMessage' in result_routing_config:
                            result_voicemail_message = result_routing_config['voicemailMessage']

                        if 'voicemailMessageType' in result_routing_config:
                            result_voicemail_message_type = result_routing_config['voicemailMessageType']

                        if 'whisperMessage' in result_routing_config:
                            result_whisper_message = result_routing_config['whisperMessage']

                        if 'whisperMessageType' in result_routing_config:
                            result_whisper_message_type = result_routing_config['whisperMessageType']

                        if 'transcribeAnsweredCall' in result_routing_config:
                            result_transcribe_answered_call = result_routing_config['transcribeAnsweredCall']

                        if 'transcribeVoiceMail' in result_routing_config:
                            result_transcribe_voicemail = result_routing_config['transcribeVoiceMail']

                        if 'defaultRouting' in result_routing_config:
                            result_default_config = result_routing_config['defaultRouting']

                            #if 'dialDigit' in result_default_config:
                            #    result_dial_digit = result_default_config['dialDigit']

                            #if 'digitalPrompt' in result_default_config:
                            #    result_digital_prompt = result_default_config['digitalPrompt']

                            if 'retryRouting' in result_default_config:
                                result_retry_routing = result_default_config['retryRouting']

                            if 'callOrder' in result_default_config:
                                result_call_order = result_default_config['callOrder']

                            if 'agents' in result_default_config:
                                result_agents = result_default_config['agents']

                                if result_agents is not None:
                                    for agent in result_agents:

                                        if 'id' in agent:
                                            agent_add = {
                                                'id': int(agent['id'])
                                            }

                                            if 'contactUsing' in agent:
                                                agent_add['contactUsing'] = agent['contactUsing']
                                            else:
                                                agent_add['contactUsing'] = None

                                            if 'fullName' in agent:
                                                agent_add['fullName'] = agent['fullName']
                                            else:
                                                agent_add['fullName'] = None

                                            agent_check = Agent\
                                                .query\
                                                .filter(and_(Agent.id == int(agent['id']), Agent.partnership_account_id == paid))\
                                                .first()

                                            if agent_check is not None:
                                                result_agents_final.append(agent_add)
                                            else:
                                                agent_error = True

                    if agent_error is False:
                        phone_edit_result = Phone.api_update(pnid, paid, result_friendly_name, result_source,
                                                         result_channel, result_routing_type,
                                                         result_recordCalls, result_configSMSSetup,
                                                         result_greetingMessage, result_language,
                                                         result_SMSAutoReply, result_SMSAutoReplyImage,
                                                         result_SMSAutoReplyImageUrl, result_SMSAutoReplyText,
                                                         result_voicemail, result_voicemail_message,
                                                         result_voicemail_message_type, result_whisper_message,
                                                         result_whisper_message_type, result_retry_routing,
                                                         result_call_order, result_dial_digit, result_digital_prompt,
                                                             result_agents_final, result_transcribe_answered_call,
                                                             result_transcribe_voicemail)

                        if phone_edit_result:
                            return True, 204
                        else:
                            return api.abort(code=400, message="Error updating phone number.")
                    else:
                        log.error('Error updating phone number. Agent does not belong to partnership account.')
                        return api.abort(code=400, message="Error updating phone number. Agent does not belong to partnership account.")
                except Exception as e:
                    log.error('Error updating phone number. Error: ' + str(e))
                    return api.abort(code=400, message="Error updating phone number.")
            else:
                return api.abort(code=400, message="Partnership account not found.")
        else:
            return api.abort(401)


# @ns.route('/<int:paid>/phonenumbers/<int:pnid>/agent/<int:aid>')
@api.doc(responses={200: 'OK',
                    400: 'Error performing operation.',
                    401: 'Unauthorized request.',
                    404: 'Phone number not found.',
                    },
            params={'paid': 'The partner account Id', 'pnid': 'The phone number Id', 'aid': 'The agent Id'})
class ApiOperationalPhoneNumbersAgentDelete(Resource):

    @api.response(204, 'Phone number successfully deleted.')
    @requires_auth
    def delete(self, paid, pnid, aid):
        """
        Delete an agent from a phone number.

        <p>
        The Operational Phone Numbers Agent DELETE endpoint should be used to remove an agent from an Operational
        Phone Number for a partner account.
        </p>
        <br />
        <p>
        You will require a partner authentication token, a partner account id, a phone number id as well as an agent id
        to make a successful request.
        </p>
        """
        if rest_is_partnership and rest_partnership is not None:
            partnership_account = PartnershipAccount \
                .query \
                .filter(and_(PartnershipAccount.id == paid, PartnershipAccount.partnership_id == rest_partnership.id)) \
                .first()

            if partnership_account is not None:
                try:
                    result = Phone.api_remove_agent_update(pnid, paid, aid)

                    if result:
                        return True, 204
                    else:
                        return api.abort(code=400, message="Error deleting agent from phone number.")
                except Exception as e:
                    log.error('Error deleting phone number. Error: ' + str(e))
                    return api.abort(code=400, message="Error deleting agent from phone number.")
            else:
                return api.abort(code=400, message="Partnership account not found.")
        else:
            return api.abort(401)


# @ns.route('/<int:paid>/phonenumbers/<int:pnid>/agent')
@api.doc(responses={200: 'OK',
                    400: 'Error performing operation.',
                    401: 'Unauthorized request.',
                    404: 'Phone number not found.',
                    },
            params={'paid': 'The partner account Id', 'pnid': 'The phone number Id'})
class ApiOperationalPhoneNumbersAgentAdd(Resource):

    @api.response(204, 'Agent(s) successfully added to phone number.')
    @api.expect(serializers.phonenumber_agent_add, validate=True)
    @requires_auth
    def post(self, paid, pnid):
        """
        Add an agent to a phone number.

        <p>
        The Operational Phone Numbers Agent POST endpoint should be used to add agents to an Operational
        Phone Number for a partner account.
        </p>
        <br />
        <p>
        You will require a partner authentication token, a partner account id, a phone number id as well and agent ids
        to make a successful request.
        </p>
        """
        if rest_is_partnership and rest_partnership is not None:

            partnership_account = PartnershipAccount \
                .query \
                .filter(and_(PartnershipAccount.id == paid, PartnershipAccount.partnership_id == rest_partnership.id)) \
                .first()

            if partnership_account is not None:
                try:
                    received = request.json
                    result_id = None
                    result_full_name = None
                    result_contact_using = None

                    if 'id' in received:
                        result_id = int(received['id'])

                    if 'contactUsing' in received:
                        result_contact_using = received['contactUsing']

                    if 'fullName' in received:
                        result_full_name = received['fullName']

                    agent_check = Agent \
                        .query \
                        .filter(and_(Agent.id == result_id, Agent.partnership_account_id == paid)) \
                        .first()

                    if agent_check is not None:
                        result = Phone.api_add_agent_update(pnid, paid, result_id, result_full_name, result_contact_using)

                        if result:
                            return True, 204
                        else:
                            return api.abort(code=400, message="Error adding agent to phone number.")
                    else:
                        return api.abort(code=400, message="Error adding agent to phone number, agent does not exist.")

                except Exception as e:
                    log.error('Error adding agent to phone number. Error: ' + str(e))
                    return api.abort(code=400, message="Error adding agent to phone number.")
            else:
                return api.abort(code=400, message="Partnership account not found.")
        else:
            return api.abort(401)



# @ns.route('/<int:paid>/phonenumbers/<int:pnid>/usage')
@api.doc(responses={200: 'OK',
                    400: 'Error performing operation.',
                    401: 'Unauthorized request.',
                    404: 'Phone number not found.',
                    },
            params={'paid': 'The partner account Id', 'pnid': 'The phone number Id'})
class ApiOperationalPhoneNumbersUsage(Resource):
    @requires_auth
    def get(self, paid, pnid):
        """
        Get the call and message usage of a number.

        <p>
        The Operational Phone Number Usage API GET endpoint should be used to retrieve the usage data for a specific
        operational phone number. The usage data will include inbound calls, inbound messages, outbound calls and
        outbound messages.
        </p>
        <br />
        <p>
        You require a partner authentication token, a partner account id and an available phone number to
        make a successful request. A response will
        be returned, similar to the example below, based
        on a successful request:
        <br />
        <br />
        </p>
         <pre class="code-background" style="color: white">
        {
          "inbound_call_count": 0,
          "inbound_message_count": 0,
          "outbound_call_count": 0,
          "outbound_message_count": 0
        }
        </pre>
        """
        if rest_is_partnership and rest_partnership is not None:
            partnership_account = PartnershipAccount\
                .query\
                .filter(and_(PartnershipAccount.id == paid, PartnershipAccount.partnership_id == rest_partnership.id))\
                .first()

            if partnership_account is not None:

                phone = Phone.query \
                    .filter(and_(Phone.id == pnid, Phone.partnership_account_id == paid)) \
                    .filter(or_(Phone.type == 'tracking', Phone.type == 'priority')) \
                    .first()

                if phone is not None:
                    try:
                        return PartnershipAccount.phone_number_usage(paid, pnid)
                    except Exception as e:
                        log.error(traceback.format_exc())

                        return api.abort(code=500, message="Error retrieving phone number.")
                else:
                    return api.abort(code=404,
                                     message="Phone number not found. Are your sure this is a priority/tacking number?")
            else:
                return api.abort(code=400, message="Partnership account not found.")
        else:
            api.abort(401)