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_new/buyercall/buyercall/blueprints/phonenumbers/models.py
import re
import logging

from flask import (
    current_app as app,
    url_for
)
from sqlalchemy import or_, and_, func, select
from sqlalchemy.dialects import postgresql as pg
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import load_only, joinedload, column_property
from sqlalchemy.sql import text
from sqlalchemy.orm.attributes import flag_modified
from datetime import datetime
from buyercall.lib.util_twilio import (
    bw_client
)
from buyercall.lib.util_sqlalchemy import ResourceMixin
from buyercall.blueprints.user.models import User
from buyercall.blueprints.agents.models import Agent
from buyercall.blueprints.filters import format_phone_number
from buyercall.lib.util_bandwidth import bw_client as bw_dashboard_client
from buyercall.blueprints.partnership.models import PartnershipAccount
from buyercall.blueprints.leads.models import Lead
from buyercall.blueprints.sms.models import Message
from buyercall.blueprints.widgets.models import Widget
from buyercall.extensions import db

log = logging.getLogger(__name__)


class Phone(ResourceMixin, db.Model):

    __tablename__ = 'phonenumbers'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    leads = db.relationship(Lead, backref='inbound')
    messages = db.relationship(Message, backref="inbound")
    partnership_account = db.relationship(PartnershipAccount)
    widgets = db.relationship(Widget, backref="inbound")

    partnership_account_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'partnership_accounts.id',
            onupdate='CASCADE',
            ondelete='CASCADE'),
        index=True, nullable=False
    )
    # Phone numbers.
    phonenumber = db.Column(db.String(20), nullable=False,
                            server_default='')

    # Local phone number.
    local = db.Column('is_local', db.Boolean(), nullable=False,
                      server_default='0')

    # Tollfree phone number.
    tollfree = db.Column('is_tollfree', db.Boolean(), nullable=False,
                         server_default='0')

    # Phone number type (tracking/priority)
    type = db.Column('type', db.String(), nullable=False,
                     server_default='priority')

    # Is an active phone number.
    active = db.Column('is_active', db.Boolean(), nullable=False,
                       server_default='1')

    # Column to determine if callerid is enabled for whisper message.
    caller_id = db.Column(
        'is_callerid', db.Boolean(),
        nullable=False, server_default='0'
    )

    # Phone Number friendly name.
    friendly_name = db.Column(db.String(128), nullable=False,
                              server_default='')

    # Phone Number source name.
    source = db.Column(db.String(256), nullable=False, server_default='')

    # Marketing channel for the phone number
    channel = db.Column(db.String(256), nullable=True)

    # Twilio phone number sid.
    twilio_number_sid = db.Column(db.String(34), nullable=False,
                                  server_default='')

    # Bandwidth application id.
    application_id = db.Column(db.String(256), nullable=False,
                               server_default='')

    # See assets/components/inbound/models.js for JSON fields
    routing_config = db.Column(
        'routings', pg.JSON, nullable=False, server_default='{}')

    notifications = db.Column(
        pg.JSON, nullable=False, server_default='{}')

    provider = db.Column(db.String(10), nullable=False, index=True)
    deactivated_on = db.Column(db.DateTime(), nullable=True)
    is_deactivated = db.Column(db.Boolean(), nullable=False,
                               server_default='0', default=False)

    lead_count = column_property(select([func.count(Lead.id)]).where(Lead.inbound_id == id))

    @classmethod
    def search(cls, query):

        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return text('')

        search_query = '%{0}%'.format(query)

        return or_(Phone.phonenumber.ilike(search_query),
                   Phone.friendly_name.ilike(search_query),
                   Phone.type.ilike(search_query)
                   )

    @classmethod
    def create(cls, params):
        """
        Return whether or not the phonenumber was created successfully.

        :return: bool
        """
        phone_params = params

        phone = Phone(**phone_params)

        db.session.add(phone)
        db.session.commit()

        return True

    @classmethod
    def update(cls, id, partnership_account_id, new_phonenumber, friendly_name,
               twilio_number_sid, source, channel, is_local, is_tollfree):
        """
        Return whether or not the phonenumber was update successfully.

        :return: bool
        """
        phonenumber = db.session.query(Phone).filter(
            and_(Phone.id == id, Phone.partnership_account_id == partnership_account_id)
        ).first()

        if phonenumber is not None:
            phonenumber.friendly_name = friendly_name
            phonenumber.twilio_number_sid = twilio_number_sid
            phonenumber.source = source
            phonenumber.channel = channel
            phonenumber.is_local = is_local
            phonenumber.is_tollfree = is_tollfree
            phonenumber.phonenumber = new_phonenumber
            db.session.commit()
            return True
        else:
            return False

    @classmethod
    def api_update(cls, id, partnership_account_id, friendly_name, source, channel, routing_type,
                   record_calls, config_sms_setup, greeting_message, language, sms_auto_reply, sms_auto_reply_image,
                   sms_auto_reply_image_url, sms_auto_reply_text, voicemail, voicemail_message, voicemail_message_type,
                   whisper_message, whisper_message_type, retry_routing, call_order, dial_digit, digital_prompt, agents,
                   transcribe_answered_call, transcribe_voicemail):
        """
        Return whether or not the phonenumber was updated successfully.

        :return: bool
        """
        result = False

        phone = Phone.query.filter(
            Phone.partnership_account_id == partnership_account_id,
            Phone.id == id,
        ).first()

        default_routing = None
        routing_config = None

        if phone is not None:
            routing_config = phone.routing_config

            if friendly_name is not None:
                phone.friendly_name = friendly_name

            if source is not None:
                phone.source = source

            if channel is not None:
                phone.channel = channel

            if routing_type is not None:
                routing_config['routingType'] = routing_type

            if record_calls is not None:
                routing_config['recordCalls'] = record_calls

            if config_sms_setup is not None:
                routing_config['configSMSSetup'] = config_sms_setup

            if greeting_message is not None:
                routing_config['greetingMessage'] = greeting_message

            if language is not None:
                routing_config['language'] = language

            if sms_auto_reply is not None:
                routing_config['SMSAutoReply'] = sms_auto_reply

            if sms_auto_reply_image is not None:
                routing_config['SMSAutoReplyImage'] = sms_auto_reply_image

            if sms_auto_reply_image_url is not None:
                routing_config['SMSAutoReplyImageUrl'] = sms_auto_reply_image_url

            if sms_auto_reply_text is not None:
                routing_config['SMSAutoReplyText'] = sms_auto_reply_text

            if voicemail is not None:
                routing_config['voicemail'] = voicemail

            if voicemail_message is not None:
                routing_config['voicemailMessage'] = voicemail_message

            if voicemail_message_type is not None:
                routing_config['voicemailMessageType'] = voicemail_message_type

            if whisper_message is not None:
                routing_config['whisperMessage'] = whisper_message

            if whisper_message_type is not None:
                routing_config['whisperMessageType'] = whisper_message_type

            if digital_prompt is not None:
                default_routing = routing_config['defaultRouting']

                if default_routing is not None:
                    default_routing['digitalPrompt'] = digital_prompt

            if dial_digit is not None:
                default_routing = routing_config['defaultRouting']

                if default_routing is not None:
                    default_routing['dialDigit'] = dial_digit

            if retry_routing is not None:
                default_routing = routing_config['defaultRouting']

                if default_routing is not None:
                    default_routing['retryRouting'] = retry_routing

            if call_order is not None:
                default_routing = routing_config['defaultRouting']

                if default_routing is not None:
                    default_routing['callOrder'] = call_order

            if agents is not None and len(agents) > 0:
                default_routing_agents = []
                default_routing = routing_config['defaultRouting']

                for agent in agents:
                    if agent['id'] is not None:
                        if agent['contactUsing'] is None:
                            agent['contactUsing'] = 'phone'

                        if agent['fullName'] is None:
                            agent['fullName'] = ''

                        default_routing_agents.append(agent)

                default_routing['agents'] = default_routing_agents

            if default_routing is not None:
                routing_config['defaultRouting'] = default_routing

            if transcribe_answered_call is not None:
                routing_config['transcribeAnsweredCall'] = transcribe_answered_call

            if transcribe_voicemail is not None:
                routing_config['transcribeVoiceMail'] = transcribe_voicemail

            phone.routing_config = routing_config
            flag_modified(phone, 'routing_config')

            db.session.commit()
            result = True

            # If the sms config is set to True lets set a messaging url variable otherwise leave it empty
            if routing_config["configSMSSetup"] or phone.type == 'mobile':
                sms_enabled = 'on'
            else:
                sms_enabled = 'off'
            # If it's a bandwidth number update the SMS enable status for the TN
            if phone.provider == 'bandwidth':
                partner_account = PartnershipAccount.query\
                    .filter(PartnershipAccount.id == partnership_account_id).first()
                dashboard_client = bw_dashboard_client(partner_account.partnership_id)
                unformatted_phone_number = phone.phonenumber
                bw_pn_formatted = unformatted_phone_number.replace("+1", "")
                if phone.type == 'mobile':
                    dashboard_client.phone_numbers_options.update(partner_account_id=str(partnership_account_id),
                                                                  phonenumber=bw_pn_formatted,
                                                                  sms_option=sms_enabled,
                                                                  sms_campaign_id=app.config.get(
                                                                      'SMS_MOBILE_CAMPAIGN_ID'),
                                                                  sms_campaign_class=app.config.get(
                                                                      'SMS_MOBILE_CAMPAIGN_CLASS')
                                                                  )
                else:
                    dashboard_client.phone_numbers_options.update(partner_account_id=str(partnership_account_id),
                                                                  phonenumber=bw_pn_formatted,
                                                                  sms_option=sms_enabled)

        return result

    @classmethod
    def api_add_agent_update(cls, id, partnership_account_id, agent_id, agent_full_name, agent_contact_using):
        """
        Return whether or not an agent was successfully added to a phone number.

        :return: bool
        """
        result = False

        phone = Phone.query.filter(
            Phone.partnership_account_id == partnership_account_id,
            Phone.id == id,
        ).first()

        default_routing = None
        routing_config = None
        default_routing_agents = None
        updated_routing_agents = []
        found_agent = False
        agent_added = False

        if phone is not None:
            routing_config = phone.routing_config

            if routing_config is not None:

                if 'defaultRouting' in routing_config:
                    default_routing = routing_config['defaultRouting']

                    if 'agents' in default_routing:
                        default_routing_agents = default_routing['agents']

                        for agent in default_routing_agents:
                            if agent['id'] == agent_id:

                                if agent_full_name is not None:
                                    agent['fullName'] = agent_full_name

                                if agent_contact_using is not None:
                                    agent['contactUsing'] = agent_contact_using

                                updated_routing_agents.append(agent)
                                found_agent = True
                            else:
                                updated_routing_agents.append(agent)

                        if found_agent:
                            default_routing['agents'] = updated_routing_agents

                        else:

                            if agent_full_name is None:
                                agent_full_name = ''

                            if agent_contact_using is None:
                                agent_contact_using = 'phone'

                            new_agent = {
                                'id': agent_id,
                                'fullName': agent_full_name,
                                'contactUsing': agent_contact_using
                            }
                            updated_routing_agents.append(new_agent)
                            default_routing['agents'] = updated_routing_agents
                            agent_added = True

                        if agent_added or found_agent:
                            flag_modified(phone, 'routing_config')
                            db.session.commit()
                            result = True

                        return result
                    else:
                        return result
                else:
                    return result
            else:
                return result
        else:
            return result

    @classmethod
    def api_remove_agent_update(cls, id, partnership_account_id, agent_id):
        """
        Return whether or not an agent was successfully removed from a phone number.

        :return: bool
        """
        result = False

        phone = Phone.query.filter(
            Phone.partnership_account_id == partnership_account_id,
            Phone.id == id,
        ).first()

        default_routing = None
        routing_config = None
        default_routing_agents = None
        updated_routing_agents = []
        found_agent = False

        if phone is not None:
            routing_config = phone.routing_config

            if routing_config is not None:

                if 'defaultRouting' in routing_config:
                    default_routing = routing_config['defaultRouting']

                    if 'agents' in default_routing:
                        default_routing_agents = default_routing['agents']

                        for agent in default_routing_agents:
                            if agent['id'] != agent_id:
                                updated_routing_agents.append(agent)
                            else:
                                found_agent = True

                        if found_agent:
                            default_routing['agents'] = updated_routing_agents
                            flag_modified(phone, 'routing_config')
                            db.session.commit()
                            result = True

                            return result
                        else:
                            return result
                    else:
                        return result
                else:
                    return result
            else:
                return result
        else:
            return result

    @classmethod
    def api_mobile_update(cls, id, partnership_account_id, friendly_name, record_calls, voicemail, voicemail_message,
                          voicemail_message_type, greeting_message, whisper_message, whisper_message_type, agent_id,
                          agent_full_name, agent_contact_using, transcribe_answered_call, transcribe_voicemail):
        """
        Return whether or not the phonenumber was updated successfully.

        :return: bool
        """
        result = False
        phone = Phone.query.filter(
            Phone.partnership_account_id == partnership_account_id,
            Phone.id == id,
        ).first()

        default_routing = None
        routing_config = None

        if phone is not None:
            routing_config = phone.routing_config

            if friendly_name is not None:
                phone.friendly_name = friendly_name

            if record_calls is not None:
                routing_config['recordCalls'] = record_calls

            if voicemail is not None:
                routing_config['voicemail'] = voicemail

            if voicemail_message is not None:
                routing_config['voicemailMessage'] = voicemail_message

            if voicemail_message_type is not None:
                routing_config['voicemailMessageType'] = voicemail_message_type

            if greeting_message is not None:
                routing_config['greetingMessage'] = greeting_message

            if whisper_message is not None:
                routing_config['whisperMessage'] = whisper_message

            if whisper_message_type is not None:
                routing_config['whisperMessageType'] = whisper_message_type

            if agent_id is not None:
                default_routing = routing_config['defaultRouting']

                if default_routing is not None:
                    agents = default_routing['agents']
                    first_agent = agents[0]
                    first_agent['id'] = agent_id

            if agent_full_name is not None:
                default_routing = routing_config['defaultRouting']

                if default_routing is not None:
                    agents = default_routing['agents']
                    first_agent = agents[0]
                    first_agent['fullName'] = agent_full_name

            if agent_contact_using is not None:
                default_routing = routing_config['defaultRouting']

                if default_routing is not None:
                    agents = default_routing['agents']
                    first_agent = agents[0]
                    first_agent['contactUsing'] = agent_contact_using

            if default_routing is not None:
                routing_config['defaultRouting'] = default_routing

            if transcribe_answered_call is not None:
                routing_config['transcribeAnsweredCall'] = transcribe_answered_call

            if transcribe_voicemail is not None:
                routing_config['transcribeVoiceMail'] = transcribe_voicemail

            phone.routing_config = routing_config
            flag_modified(phone, 'routing_config')
            db.session.commit()

            result = True
        return result

    @classmethod
    def api_delete(cls, id, partnership_account_id):
        """
        Return whether or not the phonenumber was deleted successfully.

        :return: bool
        """
        try:
            from buyercall.blueprints.phonenumbers.views import delete_bw, delete_twilio

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

            phone = Phone.query.filter(
                Phone.partnership_account_id == partnership_account_id,
                Phone.id == id,
            ).first()

            if not phone:
                return True

            # If debugging, do not delete the numbers
            if not app.config.get('DEBUG'):
                if phone.provider == 'bandwidth':
                    delete_bw(phone)
                elif phone.provider == 'twilio':
                    delete_twilio(phone)

            phone.is_deactivated = True
            phone.deactivated_on = datetime.now()
            db.session.commit()

            if phone.type == 'mobile':
                # Get the endpoint associoated with this number
                from ..mobile.models import Endpoint
                endpoint = Endpoint.query.filter(Endpoint.inbound_id == id).first()
                # Lets get the domain now using the partnership_id
                from ..mobile.models import Domain
                domain = Domain.query.filter(Domain.id == endpoint.domain_id).first()

                # Deactivate the sip_endpoint in BuyerCall
                Endpoint.deactivate(endpoint.id, partnership_account_id)
                # Delete the endpoint from the provider
                if phone.provider == 'bandwidth':
                    client = bw_client(partner.id)
                    client.domains.delete_endpoint(
                        domain_id=domain.domain_id,
                        endpoint_id=endpoint.provider_id
                    )

            return True
        except Exception as e:
            log.error('Error deactivating phone number. Error: {}'.format(e))
            return False

    @classmethod
    def deactivate(cls, id, partnership_account_id):
        """
        Return whether or not the phone number was deactivated successfully.

        :return: bool
        """
        phonenumber = (db.session.query(Phone)\
                       .filter(and_(Phone.id == id, Phone.partnership_account_id == partnership_account_id))).first()

        if phonenumber is not None:
            phonenumber.is_deactivated = True
            phonenumber.active = False
            phonenumber.deactivated_on = datetime.now()
            db.session.commit()
            return True
        else:
            return False

    @classmethod
    def mobile_sip_uri(cls, phone_number):
        """
        Lookup the mobile sip uri for a number
        """
        # Check to see if the agent's number is a BuyerCall provisioned number, mostly used for the
        # mobile app
        bc_phone_number = cls.query.filter(cls.phonenumber == format_phone_number(phone_number)) \
            .order_by(cls.created_on.desc()).first()
        # Check to see if the inbound/phone number has a sip associated with it
        if bc_phone_number:
            from buyercall.blueprints.mobile.models import Endpoint
            sip_endpoint = Endpoint.query \
                .filter(and_(Endpoint.inbound_id == bc_phone_number.id,
                             Endpoint.partnership_account_id == bc_phone_number.partnership_account_id)).first()
            if sip_endpoint:
                return sip_endpoint.sip_uri
        return ''

    @hybrid_property
    def subscription(self):
        """ Return subscription associated with a phone number
        """
        if self.partnership_account.subscription:
            return self.partnership_account.subscription.plan

        if self.partnership.subscription:
            return self.partnership.subscription.plan

        if self.partnership_account.partnership and self.partnership_account.partnership.subscription:
            return self.partnership_account.partnership.subscription.plan

        return None

    @hybrid_property
    def toll_type(self):
        if self.tollfree:
            return 'Toll-Free'
        else:
            return 'Local'

    @hybrid_property
    def partnership_id(self):
        """ Return the partnership id for a phone number
        """
        partner_id = self.partnership_account.partnership_id
        return partner_id

    @toll_type.setter
    def toll_type(self, value):
        if value.lower() == 'toll-free':
            self.tollfree = True
            self.local = False
        else:
            self.tollfree = False
            self.local = True

    @toll_type.expression
    def toll_type(cls):
        return db.case([(cls.tollfree == '1', 'Toll-Free')], else_='Local')

    @property
    def agents_list(self):
        if self.routing_config.get('routingType') == 'default':
            routings = [self.routing_config.get('defaultRouting')]
        elif self.routing_config.get('routingType') == 'digit':
            routings = self.routing_config.get('digitRoutings')
        else:
            return ''

        return ', '.join([
            agent.get('fullName') or '{} {}'.format(agent.get('firstName', ''), agent.get('lastName', ''))
            for r in routings
            for agent in r.get('agents', [])
        ])

    @property
    def mobile_agent_email(self):
        if self.routing_config.get('routingType') == 'default':
            routings = [self.routing_config.get('defaultRouting')]
        elif self.routing_config.get('routingType') == 'digit':
            routings = self.routing_config.get('digitRoutings')
        else:
            routings = []

        agent_id = [
            agent.get('id') for r in routings for agent in r.get('agents', [])
        ]
        agent = Agent.query.options(joinedload('agents')).filter(
            Agent.id.in_(agent_id),
        ).first()
        if agent:
            email = agent.email
        else:
            email = ''
        return email

    @property
    def mobile_agent_id(self):
        if self.routing_config.get('routingType') == 'default':
            routings = [self.routing_config.get('defaultRouting')]
        elif self.routing_config.get('routingType') == 'digit':
            routings = self.routing_config.get('digitRoutings')
        else:
            routings = []

        agent_id = [agent.get('id') for r in routings for agent in r.get('agents', [])]

        agent = Agent.query.options(joinedload('agents')).filter(
            Agent.id.in_(agent_id),
        ).first()
        if agent:
            a_id = agent.id
        else:
            a_id = ''
        return a_id

    @property
    def recording_enabled(self):
        """
        Return whether or not the phonenumber will record the call or voicemail.

        :return: bool
        """
        record_enabled = False
        voicemail_enabled = False

        if 'recordCalls' in self.routing_config:
            record_enabled = self.routing_config.get('recordCalls')

        if 'voicemail' in self.routing_config:
            voicemail_enabled = self.routing_config.get('voicemail')

        if record_enabled or voicemail_enabled:
            return True
        else:
            return False

    @property
    def created_datetime(self):

        """ Return the date/time this phonenumber was created
        """
        return self.created_on.strftime('%Y-%m-%d %H:%M:%S')

    @property
    def updated_datetime(self):

        """ Return the date/time this phonenumber was updated
        """
        return self.updated_on.strftime('%Y-%m-%d %H:%M:%S')

    @property
    def deactivated_on_datetime(self):

        """ Return the date/time this phone was deactivated on.
        """
        if self.deactivated_on is not None and self.deactivated_on is not '':
            return self.deactivated_on.strftime('%Y-%m-%d %H:%M:%S')
        else:
            return ''

    @property
    def notification_recipients(self):
        """ Return a list of email addresses to whom email notifications will
            be sent
        """
        notify_leads = self.notifications.get('notifyLeads', 'none')
        emails = []

        admin = User.query.options(load_only("email")).filter(
            User.partnership_account_id == self.partnership_account_id,
            User.role == 'admin',
            User.is_deactivated.is_(False)
        ).first()

        if self.routing_config.get('routingType') == 'default':
            routings = [self.routing_config.get('defaultRouting')]
        elif self.routing_config.get('routingType') == 'digit':
            routings = self.routing_config.get('digitRoutings')
        else:
            routings = []

        agent_ids = [
            agent.get('id') for r in routings for agent in r.get('agents', [])
        ]
        agents = Agent.query.options(joinedload('agents')).filter(
            Agent.id.in_(agent_ids),
        ).all()
        agent_emails = []
        for a in agents:
            if a.is_group:
                agent_emails.extend(x.email for x in a.agents)
            else:
                agent_emails.append(a.email)

        if notify_leads == 'missed':
            if admin and self.notifications.get('notifyMissedMe'):
                emails.append(admin.email)
            if self.notifications.get('notifyMissedAgents'):
                emails.extend(agent_emails)
            emails.extend(re.split('[;,]', self.notifications.get('notifyMissedCustom')))
        elif notify_leads == 'all':
            if  self.notifications.get('notifyAllMe'):
                emails.append(admin.email)
            if self.notifications.get('notifyAllAgents'):
                emails.extend(agent_emails)
            emails.extend(re.split('[;,]', self.notifications.get('notifyAllCustom')))
        else:
            emails = []

        return sorted(set(x for x in emails if x))

    def as_dict(self):
        return {
            'id': self.id,
            'phoneNumber': self.phonenumber,
            'callerId': self.caller_id,
            'friendlyName': self.friendly_name,
            'typePn': 'tollfree' if self.tollfree else 'local',
            'type': self.type,
            'active': self.active,
            'subscriptionPlan': self.subscription,
            'routingConfig': self.routing_config,
            'notificationConfig': self.notifications
        }

    def connect_audio_files(self):
        """ Add foreign keys for the uploaded audio files
        """
        ids = []
        for field in [
            'whisperMessage', 'digitWhisperMessage', 'noDigitWhisperMessage',
            'voicemailMessage', 'callBackText',
        ]:
            id_ = self.routing_config.get('{}Audio'.format(field))
            if id_:
                ids.append(id_)

        files = Audio.query.filter(
            Audio.id.in_(ids), Audio.inbound_id == None, Audio.widget_guid == None
        ).all()
        for f in files:
            f.inbound_id = self.id

        db.session.commit()


class HoldMusic(ResourceMixin, db.Model):
    __tablename__ = 'holdmusic'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    # Relationships.
    partnership_account_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'partnership_accounts.id',
            onupdate='CASCADE',
            ondelete='CASCADE'
        ),
        index=True, nullable=False
    )

    uuid = db.Column(db.String, nullable=True)

    filename = db.Column(db.String, nullable=True)

    url = db.Column(db.String, nullable=True)


# Audio url table used for whisper message that used audio files instead of text-to-speech
class Audio(ResourceMixin, db.Model):
    __tablename__ = 'audio_urls'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    inbound_id = db.Column(db.Integer,  nullable=True)

    widget_guid = db.Column(db.String(128), nullable=True)

    whisper_message_type = db.Column(db.String(128), nullable=False, server_default='')

    audio_url = db.Column(db.String(256), nullable=False, server_default='')

    enabled = db.Column('is_enabled', db.Boolean(), nullable=False, server_default='0')

    @property
    def created_datetime(self):

        """ Return the date/time this widget was created
        """
        return self.created_on.strftime('%Y-%m-%d %H:%M:%S')

    @property
    def updated_datetime(self):

        """ Return the date/time this widget was updated
        """
        return self.updated_on.strftime('%Y-%m-%d %H:%M:%S')

    @classmethod
    def api_created(cls, params):
        """
        Return the audio url id if it was created successfully.

        :return: bool
        """
        audio_id = -1

        audio_params = params
        audio = Audio(**audio_params)

        db.session.add(audio)
        db.session.flush()

        audio_id = audio.id

        db.session.commit()

        return audio_id

    @classmethod
    def api_update(cls, id, phonenumber_id, widget_guid, whisper_message_type, audio_url, enabled):
        """
        Return whether or not the audio url was updated successfully.

        :return: bool
        """
        result = False

        audio = Audio.query.filter(Audio.id == id).first()

        if audio is not None:
            if phonenumber_id is not None:
                audio.inbound_id = phonenumber_id

            if widget_guid is not None:
                audio.widget_guid = widget_guid

            if whisper_message_type is not None:
                audio.whisper_message_type = whisper_message_type

            if audio_url is not None:
                audio.audio_url = audio_url

            if enabled is not None:
                audio.enabled = enabled

            db.session.commit()
            result = True

        return result

    @classmethod
    def api_delete(cls, id, partnership_account_id):
        """
        Return whether or not the audio url was deleted successfully.

        :return: bool
        """
        result = False
        audio = Audio.query.filter(Audio.id == id).first()

        if audio:
            phone = Phone.query \
                .filter(and_(audio.inbound_id == Phone.id, Phone.partnership_account_id == partnership_account_id)) \
                .first()

            if phone:
                audio.delete()
                db.session.commit()
                result = True

        return result