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/mobile/models.py
import logging

from sqlalchemy.ext.hybrid import hybrid_property

from buyercall.lib.util_sqlalchemy import ResourceMixin
from buyercall.extensions import db
from buyercall.lib.util_bandwidth import bw_client
from datetime import datetime
from sqlalchemy import and_
from sqlalchemy.orm import load_only

log = logging.getLogger(__name__)


class Endpoint(ResourceMixin, db.Model):
    __tablename__ = 'sip_endpoints'

    id = db.Column(db.Integer, primary_key=True)
    # Description of sip
    description = db.Column(db.String(128), nullable=False)
    # Identify the provider for the sip endpoint. Either Twilio or Bandwidth
    provider = db.Column(db.String(32), nullable=False, index=True)
    # The provider id for the sip endpoint
    provider_id = db.Column(db.String(128), nullable=False)
    # The sip endpoint url
    sip_uri = db.Column(db.String(128), nullable=False)
    # The SIP username. This is unique to a domain and cant be changed
    sip_username = db.Column(db.String(64), nullable=False, index=True)
    # The SIP password
    sip_password = db.Column(db.String(256), nullable=False)
    # Is the sip enabled or not
    enabled = db.Column(db.Boolean, server_default='t', default=True)
    # The inbound phonenumber the sips is linked to
    inbound_id = db.Column(db.Integer, db.ForeignKey(
        'phonenumbers.id', name='sip_inbound_fkey', onupdate='CASCADE', ondelete='SET NULL'), index=True,
                           nullable=False)
    # The agent the sips is linked to
    agent_id = db.Column(db.Integer, db.ForeignKey(
        'agents.id', name='sip_agent_fkey', onupdate='CASCADE', ondelete='SET NULL'), index=True,
                        nullable=False)
    # The partnership account id associated with the sip
    partnership_account_id = db.Column(db.Integer, db.ForeignKey(
        'partnership_accounts.id', name='sip_partnership_account_fkey', onupdate='CASCADE', ondelete='CASCADE'),
                                       index=True, nullable=False)
    # The partnership account id associated with the sip
    domain_id = db.Column(db.Integer, db.ForeignKey(
        'sip_domains.id', name='sip_domain_fkey', onupdate='CASCADE', ondelete='CASCADE'),
                                       index=True, nullable=False)
    # When a sip endpoint gets deleted it goes into deactivated state instead of removing it from DB
    deactivated_on = db.Column(db.DateTime(), nullable=True)
    is_deactivated = db.Column(db.Boolean(), nullable=False,
                               server_default='0', default=False)
    # The user's device unique token, selector and app_id to be used to identify the mobile user to send
    # push notifications to
    device_token = db.Column(db.String(256), nullable=False, server_default='')
    selector = db.Column(db.String(128), nullable=False, server_default='')
    app_id = db.Column(db.String(64), nullable=False, server_default='')
    install_id = db.Column(db.String(128), nullable=False, server_default='')
    imei = db.Column(db.String(64), nullable=False, server_default='')
    unique_id = db.Column(db.String(64), nullable=False, server_default='')
    build = db.Column(db.String(64), nullable=False, server_default='')
    platform = db.Column(db.String(32), nullable=False, server_default='')
    platform_version = db.Column(db.String(32), nullable=False, server_default='')
    version = db.Column(db.String(32), nullable=False, server_default='')
    app_name = db.Column(db.String(64), nullable=False, server_default='')
    locale = db.Column(db.String(32), nullable=False, server_default='')
    cpu = db.Column(db.String(32), nullable=False, server_default='')
    device = db.Column(db.String(32), nullable=False, server_default='')
    production_build = db.Column(db.String(32), nullable=False, server_default='')
    last_sms_received_id = db.Column(db.String(64), nullable=False, server_default='')
    last_sms_sent_id = db.Column(db.String(64), nullable=False, server_default='')
    qr_url = db.Column(db.String(256), nullable=False, server_default='')
    sip_hash_1 = db.Column(db.String(64), nullable=False, server_default='')
    sip_hash_1_b = db.Column(db.String(64), nullable=False, server_default='')

    @hybrid_property
    def source(self):
        return (
            self.inbound and self.inbound.friendly_name
        ) or ''

    @source.expression
    def source(cls):
        from ..phonenumbers.models import Phone

        return Phone.friendly_name

    @hybrid_property
    def source_id(self):
        return (
            self.inbound and str(self.inbound_id)
        ) or ''

    @classmethod
    def deactivate(cls, id_, partnership_account_id):
        """
        Deactivate sip endpoint. The is_deactivated flag will be set.

        :return: bool
        """
        endpoint = Endpoint.query.filter(and_(Endpoint.id == id_,
                                              Endpoint.partnership_account_id == partnership_account_id)).first()

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

    @classmethod
    def create(cls, description, provider, provider_id, sip_uri, sip_username, sip_password,
               enabled, inbound_id, agent_id, partnership_account_id, domain_id, is_deactivated,
               hash_1=None, hash_1_b=None):
        """
        Create new sip endpoint.

        :return: bool
        """
        new_endpoint = Endpoint(
            description=description, provider=provider, provider_id=provider_id, sip_uri=sip_uri,
            sip_username=sip_username, sip_password=sip_password, enabled=enabled,
            inbound_id=inbound_id, agent_id=agent_id, partnership_account_id=partnership_account_id,
            domain_id=domain_id, is_deactivated=is_deactivated,
            sip_hash_1=hash_1, sip_hash_1_b=hash_1_b
        )
        db.session.add(new_endpoint)
        db.session.commit()

        return True

    @classmethod
    def update(cls, id_, partnership_account_id, sip_password,
               enabled, agent_id):
        """
        Update the endpoint details.

        :return: bool
        """
        endpoint = Endpoint.query\
            .filter(and_(Endpoint.id == id_, Endpoint.partnership_account_id == partnership_account_id))\
            .first()

        if endpoint is not None:
            endpoint.sip_password = sip_password
            endpoint.enabled = enabled
            endpoint.agent_id = agent_id

            db.session.commit()

            return True
        else:
            return False

    @classmethod
    def api_username_check(cls, sip_username):
        """
        Check to see if the username exists in the domain.

        :return: bool
        """
        endpoint = Endpoint.query\
            .filter(and_(Endpoint.sip_username == sip_username, Endpoint.is_deactivated.is_(False))).first()

        if endpoint is None:
            return False
        else:
            return True

    @classmethod
    def sip_agent_id(cls, sip_uri):
        """
        Return the agent id associated with the sip endpoint.

        :return: bool
        """
        endpoint = cls.query.filter(and_(cls.sip_uri == sip_uri, cls.is_deactivated.is_(False))).first()
        log.info('The sip agent id is:{}'.format(endpoint.agent_id))
        if endpoint is None:
            agent_id = None
            return agent_id
        else:
            return endpoint.agent_id

    @classmethod
    def api_update(cls, id_, partnership_account_id, sip_password, sip_description, enabled, agent_id):
        """
        Update the endpoint details.

        :return: bool
        """

        try:
            if sip_password is None and enabled is None and agent_id is None:
                return True

            endpoint = Endpoint.query\
                .filter(and_(Endpoint.id == id_, Endpoint.partnership_account_id == partnership_account_id))\
                .first()

            domain = Domain.query \
                .filter(Domain.id == endpoint.domain_id).first()

            # import partnership information to get partnership id
            from ..partnership.models import Partnership
            partner = Partnership.query.filter(Partnership.id == domain.partnership_id).first()

            client = bw_client(partner.id, tn_type='mobile')

            if endpoint:

                # Get the old QR code before the new one is assigned
                old_qr_url = endpoint.qr_url

                if sip_password:
                    endpoint.sip_password = sip_password

                    # Get the partnership id from phone numbers to determine the domain to use
                    from ..partnership.models import PartnershipAccount
                    partnership_act = PartnershipAccount.query.filter(
                        PartnershipAccount.id == partnership_account_id).first()

                    from ..mobile.tasks import qr_code, delete_qr_code
                    qr = qr_code(endpoint.sip_username,
                                 sip_password,
                                 partnership_act.id,
                                 partnership_act.name,
                                 domain.cloud_id)

                    if qr:
                        endpoint.qr_url = qr
                        # Delete the old QR code image from the server
                        if old_qr_url:
                            delete_qr_code(old_qr_url)

                if enabled:
                    endpoint.enabled = enabled

                if sip_description:
                    endpoint.description = sip_description

                if agent_id:
                    endpoint.agent_id = agent_id

                db.session.commit()

                if endpoint.provider == 'bandwidth':
                    client.realms.update_sip_credentials(
                        realm_id=domain.domain_id,
                        realm=domain.sip_realm,
                        endpoint_id=endpoint.provider_id,
                        sip_username=endpoint.sip_username,
                        sip_password=sip_password
                    )
                return True
            return False

        except Exception as e:
            log.error('Error updating mobile endpoint. Error: {}'.format(e))
            return False


class Domain(ResourceMixin, db.Model):
    __tablename__ = 'sip_domains'

    id = db.Column(db.Integer, primary_key=True)
    # Identifies the id of the domain provided by the provider
    domain_id = db.Column(db.String(32), nullable=False)
    # Identify the name of the domain
    name = db.Column(db.String(16), nullable=False, index=True)
    # The description for the domain
    description = db.Column(db.String(128), nullable=False)
    # Identify the provider for the sip domain. Either Twilio or Bandwidth
    provider = db.Column(db.String(32), nullable=False, index=True)
    # The partnership id associated with the sip domain
    partnership_id = db.Column(db.Integer, db.ForeignKey(
        'partnerships.id', name='sip_domain_partnership_fkey', onupdate='CASCADE', ondelete='CASCADE'),
                                       index=True, nullable=False)
    # When a sip domain gets deleted it goes into deactivated state instead of removing it from DB
    deactivated_on = db.Column(db.DateTime(), nullable=True)
    is_deactivated = db.Column(db.Boolean(), nullable=False,
                               server_default='0', default=False)
    cloud_id = db.Column(db.String(128), nullable=False)
    default_domain = db.Column(db.Boolean(), nullable=False,
                               server_default='0', default=False)
    call_uri = db.Column(db.String(256), nullable=False, server_default='')
    sip_realm = db.Column(db.String(64), nullable=False, server_default='')