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_forms/buyercall/buyercall/blueprints/api2/doc/endpoints/widgets.py
import logging

from flask import (
    Blueprint,
    make_response,
    request,
    jsonify)
from uuid import uuid4
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 flask_restx import Resource
from buyercall.blueprints.api2.doc import serializers
from buyercall.blueprints.partnership.models import Partnership, PartnershipAccount
from buyercall.blueprints.api2.restplus import api
from buyercall.blueprints.billing.models.subscription import Subscription
from buyercall.blueprints.widgets.rest_api import call as widget_call
from buyercall.blueprints.widgets.rest_api import save_lead as widget_save_lead
from buyercall.blueprints.widgets.models import Widget, AgentAssignment
from buyercall.blueprints.widgets.rest_api import fix_name
from buyercall.lib.util_twilio import subaccount_client
from buyercall.lib.util_bandwidth import bw_client
from buyercall.blueprints.widgets.routing import BandwidthRouting, Routing, NoAgentsException, add_widget_lead
from buyercall.blueprints.agents.models import Agent
from buyercall.blueprints.phonenumbers.models import Phone
from buyercall.extensions import db
from sqlalchemy import func, or_, and_, extract, text
from sqlalchemy.orm import load_only

log = logging.getLogger(__name__)
# ns = api.namespace('Widgets', description='Operations related to widgets.', path='/accounts')


# @ns.route('/<int:paid>/widgets/<string:guid>/calls')
@api.doc(responses={200: 'OK',
                    400: 'Error performing operation.',
                    401: 'Unauthorized request.',
                    404: 'Widget not found.'},
         params={'guid': 'The widget GUID',
                 'paid': 'The partner account Id'})
class ApiWidgetCall(Resource):
    @requires_auth
    @api.response(200, 'Widget call successfully triggered.')
    @api.expect(serializers.widget_call, validate=True)
    def post(self, paid, guid):
        """
        Perform a call with a widget.

        <p>
        The Widgets API POST endpoint should be used to create a widget outbound call between partner account agents
        and a lead or customer that requested a call, through functionality, such as a click-to-call button.
        </p>
        <br />
        <p>
        You will require a partner authentication token, a partner account and a widget guid 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">
        {
          "lead_id": 12289,
          "success": true
        }
        </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:
                guid = guid
                received = request.get_json()
                kwargs = {
                    'phoneNumber': str(received['number'])
                }

                if 'firstname' in received:
                    result_firstname = received['firstname']

                    if result_firstname is not None and result_firstname is not '':
                        kwargs['firstName'] = result_firstname

                if 'lastname' in received:
                    result_lastname = received['lastname']

                    if result_lastname is not None and result_lastname is not '':
                        kwargs['lastName'] = result_lastname

                if 'email' in received:
                    result_email = received['email']

                    if result_email is not None and result_email is not '':
                        kwargs['emailAddress'] = result_email

                if 'question' in received:
                    result_question = received['question']

                    if result_question is not None and result_question is not '':
                        kwargs['question'] = result_question

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

                    if result_source is not None and result_source is not '':
                        kwargs['sourceTag'] = result_source

                widget = Widget.query.outerjoin(
                    (AgentAssignment, Widget.assignments)
                ).outerjoin(
                    (Agent, AgentAssignment.agent)
                ).join(Widget.partnership_account).join(PartnershipAccount.partnership).filter(
                    Widget.guid == guid).first()

                if widget is not None:
                    subscription = widget.partnership_account.subscription
                    if not subscription:
                        subscription = widget.partnership_account.partnership.subscription

                    if subscription.usage_over_limit:
                        log.warning('partnership_account {} has exceeded their quota.'.format(
                            widget.partnership_account_id
                        ))
                        return api.abort(400, message='Error performing call. Subscription quota exceeded.')

                    try:
                        lead = add_widget_lead(widget, **kwargs)

                        # import partnership information to get partnership id
                        from buyercall.blueprints.partnership.models import PartnershipAccount, Partnership
                        partner_account = PartnershipAccount.query \
                            .filter(PartnershipAccount.id == partnership_account.id).first()
                        partner = Partnership.query.filter(Partnership.id == partner_account.partnership_id).first()

                        # Is it a Bandwidth or a Twilio number?
                        client = bw_client(partner.id, 'voice')
                        if widget.inbound.type == 'tracking':
                            log.info('Calling Bandwidth number...')
                            BandwidthRouting(client).call_lead(widget, lead)
                        else:
                            log.info('Calling Twilio number...')
                            subaccount_sid = subscription.twilio_subaccount_sid
                            client = subaccount_client(subaccount_sid, partner.id)
                            Routing(client).call_lead(widget, lead)
                    except NoAgentsException:
                        return jsonify(success=False, code='ERR_NO_AGENTS', lead_id=lead.id)

                    return jsonify(success=True, lead_id=lead.id)
                else:
                    return api.abort(404, message='Widget not found.')
            else:
                return api.abort(404, message='Partnership account not found.')


# @ns.route('/<int:paid>/widgets/<string:guid>')
@api.doc(responses={200: 'OK',
                    400: 'Error performing operation.',
                    401: 'Unauthorized request.',
                    404: 'Widget not found.'},
         params={'guid': 'The widget GUID',
                 'paid': 'The partner account Id'})
class ApiWidget(Resource):
    @requires_auth
    @api.response(204, 'Widget successfully deleted.')
    def delete(self, paid, guid):
        """
        Deletes widget details.

        <p>
        The Widgets DELETE endpoint should be used to delete a Widget for a partner account.
        </p>
        <br />
        <p>
        You will require a partner authentication token, a partner account id as well as a widget guid to make a
        successful request.
        </p>

        """
        final_result = False

        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:
                final_result = Widget.delete(None, guid, partnership_account.id)

            if final_result:
                return final_result, 204
            else:
                api.abort(code=400, message="Error deleting widget.")
        else:
            return api.abort(401)

    @api.response(204, 'Widget successfully updated.')
    @api.expect(serializers.widget_edit, validate=True)
    @requires_auth
    def put(self, paid, guid):
        """
        Update a widget for a partnership account.

        This API endpoint can be used to update widgets for an account associated with a partnership.

        """
        if rest_is_partnership and rest_partnership is not None:
            from buyercall.blueprints.user.models import User

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

            if partnership_account is not None:
                received = request.get_json()
                result_name = None
                result_type = None
                result_enabled = None
                result_phonenumber = None
                result_options = None

                result_add_hidden_information = None
                result_language = None
                result_hidden_information = None
                result_record_calls = None
                result_voicemail = None
                result_voicemail_message = None
                result_route_randomly = None
                result_route_in_sequence = None
                result_route_simultaneously = None
                result_retry_routing = None


                result_agent_id = None
                result_agent_full_name = None
                result_agent_contact_using = None

                if received is not None:

                    if 'name' in received:
                        result_name = fix_name(received['name'])

                    if 'type' in received:
                        result_type = received['type']

                    if 'enabled' in received:
                        result_enabled = received['enabled']

                    if 'phonenumber_id' in received:
                        result_phonenumber = received['phonenumber_id']

                    if '_options' in received:
                        result_options = received['_options']

                        if 'routeRandomly' in result_options:
                            result_route_randomly = result_options['routeRandomly']

                        if 'routeInSequence' in result_options:
                            result_route_in_sequence = result_options['routeInSequence']

                        if 'routeSimultaneously' in result_options:
                            result_route_simultaneously = result_options['routeSimultaneously']

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

                        if 'addHiddenInformation' in result_options:
                            result_add_hidden_information = result_options['addHiddenInformation']

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

                        if 'hiddenInformation' in result_options:
                            result_hidden_information = result_options['hiddenInformation']

                        if 'recordCalls' in result_options:
                            result_record_calls = result_options['recordCalls']

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

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

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

                            if result_agents is not None:
                                for agent in result_agents:
                                    if 'contactUsing' in agent:
                                        result_agent_contact_using = agent['contactUsing']

                                    if 'id' in agent:
                                        result_agent_id = int(agent['id'])

                                    if 'fullName' in agent:
                                        result_agent_full_name = agent['fullName']

                    widget_updated = Widget.api_update(paid, guid, result_name, result_type, result_phonenumber,
                                                       result_enabled, result_add_hidden_information,
                                                       result_language, result_hidden_information, result_record_calls,
                                                       result_voicemail, result_voicemail_message, result_agent_id,
                                                       result_agent_contact_using, result_agent_full_name,
                                                       result_route_randomly, result_route_in_sequence,
                                                       result_route_simultaneously, result_retry_routing)

                    if widget_updated:
                        return widget_updated, 204
                    else:
                        return api.abort(code=400, message="Error updating widget.")
                else:
                    return api.abort(code=400, message="Error updating widget.")
            else:
                return api.abort(404, message='Partnership account not found.')
        else:
            return api.abort(401)


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

        <p>
        The Widgets GET endpoint should be used to return information on all widgets associated with
        a partner account.
        </p>
        <br />
        <p>
        You require a partner authentication token and a partner account 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">
        {
          "widgets": [
            {
              "created_on": "2019-04-05 16:25:16",
              "enabled": true,
              "guid": "83798bd5-8a13-444e-8837-9028d1168e98",
              "id": 288,
              "name": "click to call widget",
              "options": {
                "addHiddenInformation": false,
                "agents": [
                  {
                    "contactUsing": "phone",
                    "fullName": "Mark Miller",
                    "id": 475
                  }
                ],
                "enableNotifications": false,
                "hiddenInformation": "A click to call lead",
                "language": "en",
                "notifyNone": true,
                "recordCalls": false,
                "retryRouting": [
                  "0",
                  "",
                  ""
                ],
                "routeInSequence": false,
                "routeRandomly": false,
                "routeSimultaneously": true,
                "voicemail": true,
                "voicemailMessage": "Please leave a message after the beep",
                "widgetType": "widget"
              },
              "phonenumber_id": 160,
              "updated_on": "2019-04-05 16:25:16"
            },
            {
              "created_on": "2019-04-04 23:18:27",
              "enabled": true,
              "guid": "ae79bd20-ff98-490d-9093-ad6bd7dade9d",
              "id": 287,
              "name": "click to call widget",
              "options": {
                "addHiddenInformation": false,
                "agents": [
                  {
                    "contactUsing": "phone",
                    "fullName": "Mark Miller",
                    "id": 475
                  }
                ],
                "enableNotifications": false,
                "hiddenInformation": "A click to call lead",
                "language": "en",
                "notifyNone": true,
                "recordCalls": false,
                "retryRouting": [
                  "0",
                  "",
                  ""
                ],
                "routeInSequence": false,
                "routeRandomly": false,
                "routeSimultaneously": true,
                "voicemail": true,
                "voicemailMessage": "Please leave a message after the beep",
                "widgetType": "widget"
              },
              "phonenumber_id": 172,
              "updated_on": "2019-04-04 23:18:27"
            }
          ]
        }
        </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:
                widget_list = []

                widgets = Widget.query \
                    .filter(Widget.partnership_account_id == paid) \
                    .all()

                if widgets is not None:
                    for widget in widgets:
                        add_widget = {
                            'id': widget.id,
                            'created_on': widget.created_datetime,
                            'updated_on': widget.updated_datetime,
                            'guid': widget.guid,
                            'name': widget.name,
                            'phonenumber_id': widget.inbound_id,
                            'enabled': widget.enabled,
                            'options': widget.options
                        }

                        widget_list.append(add_widget)
                return jsonify(
                    widgets=widget_list)
            else:
                return api.abort(404, message='Partnership account not found.')
        else:
            return api.abort(401)

    @api.response(204, 'Widget successfully created.')
    @api.expect(serializers.widget_add, validate=True)
    @requires_auth
    def post(self, paid):
        """
        Create a widget for a partnership account.

        <p>
        The Widgets API POST endpoint should be used to create a widget for a specific partner account. Widgets can be
        used to generate outbound calls and connect partner account agent(s) and leads or customers. Widgets allow
        additional configuration such as greeting messages and the routing to multiple agents. Widgets are best used for
        features such as click-to-call buttons on websites. For example, when a lead or customer clicks on a
        click-to-call button the Widgets endpoint can be called to initiate a outbound call between an agent and the
        lead or customer.
        </p>
        <br />
        <p>
        You will require a partner authentication token and a partner account 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,
          "widget_guid": "83798bd5-8a13-444e-8837-9028d1168e98"
        }
        </pre>

        """
        if rest_is_partnership and rest_partnership is not None:
            from buyercall.blueprints.user.models import User

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

            if partnership_account is not None:
                received = request.get_json()

                if received is not None:
                    guid = str(uuid4())
                    result_name = fix_name(received['name'])
                    result_type = received['type']
                    result_enabled = received['enabled']
                    result_guid = guid
                    result_phonenumber = received['phonenumber_id']
                    result_options = received['_options']
                    result_options['widgetType'] = result_type
                    result_options['enableNotifications'] = False

                    if partnership_account.name is not None:
                        companybeforeformat = partnership_account.name.replace(" ", "")
                        companyafterformat = ''.join(e for e in companybeforeformat if e.isalnum())

                        widgetcount = Widget.query.filter(
                            Widget.partnership_account_id == partnership_account.id
                        ).count() + 1

                        if widgetcount > 0:
                            widgetcountstring = str(widgetcount)
                        else:
                            widgetcountstring = "0{}".format(widgetcount)

                        params = {
                            'enabled': result_enabled,
                            'email': "{}{}@inbound.buyercall.com".format(companyafterformat, widgetcountstring),
                            'partnership_account_id': partnership_account.id,
                            'guid': result_guid,
                            'name': result_name,
                            'options': result_options
                        }
                        widget_id = Widget.api_create(params, result_phonenumber)

                        if widget_id > 0:
                            return jsonify(widget_guid=result_guid, partnership_account_id=partnership_account.id)
                        else:
                            return api.abort(code=400, message="Error creating widget.")
                    else:
                        log.error('Partnership account: {} has a name of "{}".'.
                                  format(partnership_account.id, partnership_account.name))
                        return api.abort(
                            code=400,
                            message="Error creating widget. Company name unknown."
                                    " Please ensure the partnership account name is set.")
                else:
                    return api.abort(code=400, message="Error creating widget.")
            else:
                return api.abort(404, message='Partnership account not found.')
        else:
            return api.abort(401)