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)