#!/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime
from base64 import urlsafe_b64encode
from os.path import basename
from typing import Dict
from typing import List
from zlib import compress

from aws_xray_sdk.core import xray_recorder as xray

from iobot.models import IOBotList
from iobot.views import HomePageView
from iobot.views import ModalView
from iobot.views import SearchView
from slater.models import Account
from slater.models import Accounts
from slater.models import AccountType
from slater.models import AccountTypes
from slater.models import Application
from slater.models import ApplicationContact
from slater.models import ApplicationContacts
from slater.models import Applications
from slater.models import BatchError  # noqa: F401
from slater.models import BatchErrors
from slater.models import BatchImport  # noqa: F401
from slater.models import BatchLoad  # noqa: F401
from slater.models import BatchLoads
from slater.models import BusinessUnit
from slater.models import BusinessUnits
from slater.models import Contact
from slater.models import Contacts
from slater.models import ContactType
from slater.models import ContactTypes
from slater.models import CustomFields
from slater.models import LOB
from slater.models import LOBs
from slater.models import MasterPayer
from slater.models import MasterPayers
from slater.models import SRE
from slater.models import SREs
from slater.models import Tower
from slater.models import Towers
from iobot.utils import pad_if_blank_or_none


@xray.capture()
class SLaterSearchView(SearchView):
    def __init__(self,
                 log,
                 slack_client,
                 callback_id="SLaterSearchView",
                 title="SLater Search",
                 close="Close",
                 blocks=None,
                 metadata=None,
                 action_id="",
                 block_id="",
                 search_info="",
                 search_placeholder="",
                 request: dict = None) -> None:

        super().__init__(log,
                         slack_client,
                         callback_id=callback_id,
                         title=title,
                         close=close,
                         blocks=blocks,
                         metadata=metadata,
                         request=request)

        self.add_blocks(
            IOBotList.to_slack_modal_search(action_id=action_id,
                                            block_id=block_id,
                                            search_info=search_info,
                                            search_placeholder=search_placeholder))


@xray.capture()
class CloudDeliveryServicesAccountDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> account = Account(cloud_account_name='Atari', cloud_account_number='0423456789', next_link='https://localhost/accounts/0423456789', cloud_account_root_email='sonic.hedgehog@root.net')

    >>> contacts = Contacts([Contact(contact_name='speed racer', contact_id='1', next_link='https://localhost/contacts?contact_id=1', contact_email='speed.racer@saturday.org', contact_type='Tech Owner', create_date='2020-07-22',date_last_update='2020-07-23'), Contact(contact_name='racer x', contact_id='2', next_link='https://localhost/contacts?contact_id=2', contact_email='racer.x@saturday.org', contact_type='Account Owner', create_date='2020-07-22',date_last_update='2020-07-23')])

    >>> applications = Applications([Application(application_name='JT Test', application_id='1', t_appid='SVC0123', next_link='https://localhost/applications/1', application_description='testing CDS view', row_num='21', the_secret_to_life='42', slater_update_user='zaphod.beeblebrox')])

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> account_sort_order = ['cloud_account_name', 'cloud_account_number', 'cloud_account_root_email', 'environment_type_name', 'account_type_name', 'master_payer_name', 'primary_funding_applcation_name', 'primary_funding_t_appid', 'budget_alert_monthly_threshold', 'budget_account_string', 'intake_number', 'project_number', 'decommissioned', 'governance_lob_name', 'tower_name', 'slater_update_user', 'date_last_update']

    >>> contacts_sort_order = ['contact_type','contact_name','contact_email']

    >>> applications_sort_order = ['application_name','t_appid','application_description','governance_lob_name','tower_name','business_unit_name','slater_update_user','date_last_updated','the_secret_to_life']

    >>> view = CloudDeliveryServicesAccountDetailView(log, slack_client, account=account, contacts=contacts, applications=applications, account_sort=account_sort_order, contacts_sort=contacts_sort_order, applications_sort=applications_sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_account_action-0423456789'

    >>> len(blocks)
    10

    >>> blocks[2]['text']['text']
    '*Account Name:* Atari\\n*Account Number:* 0423456789\\n*Cloud Account Root Email:* sonic.hedgehog@root.net\\n*Slater Update User:* <@None>'

    >>> blocks[5]['text']['text']
    '*Contact Type:* Tech Owner\\n*Contact Name:* speed racer\\n*Contact Email:* speed.racer@saturday.org'

    >>> blocks[9]['text']['text']
    '*Application Name:* JT Test\\n*t_AppID:* SVC0123\\n*Application Description:* testing CDS view\\n*The Secret To Life:* 42'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    def __init__(self, log, slack_client, account: Account = None, contacts: Contacts = None, applications: Applications = None, account_sort: List[str] = None, contacts_sort: List[str] = None, applications_sort: List[str] = None, callback_id="CloudDeliveryServicesAcctDetailView", title="SLater", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        if not account_sort:
            account_sort = [
                'cloud_account_name',
                'cloud_account_number',
                'cloud_account_root_email',
                'environment_type_name',
                'account_type_name',
                'master_payer_name',
                'primary_funding_applcation_name',
                'primary_funding_t_appid',
                'budget_alert_monthly_threshold',
                'budget_account_string',
                'project_number',
                'governance_lob_name',
                'tower_name',
                'intake_number',
                'decommissioned',
                'slater_update_user',
                'date_last_update'
            ]

        if not contacts_sort:
            contacts_sort = [
                'contact_type',
                'contact_name',
                'contact_email'
            ]

        if not applications_sort:
            applications_sort = [
                'application_name',
                't_appid'
            ]

        field_names_remap = {
            'cloud_account_number': 'account_number',
            'cloud_account_name': 'account_name',
            'account_type_name': 'environment',
            'master_payer_name': 'master_payer',
            'budget_account_string': 'funding_account_string',
            'governance_lob_name': 'governance_LOB',
            'primary_funding_t_appid': 'primary_t_AppID',
            'primary_funding_applcation_name': 'application',
            'tower_name': 'tower',
        }

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"slater_account_action-{account.get('cloud_account_number')}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*Accounts > {account.get('cloud_account_name').title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_account_action-{account.get('cloud_account_number')}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{account.get('cloud_account_number')}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":bust_in_silhouette: Add Contact"
                            },
                            "value": f"add_contact^{account.get('cloud_account_number')}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":label: Custom Fields"
                            },
                            "value": f"get_custom_fields^{account.get('cloud_account_number')}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if account:
            slater_user = f"<@{account.get('slater_update_user')}>"
            account['slater_update_user'] = slater_user
            self.add_block(
                account.to_slack_markdown_section(regex_field_filters=[
                    r'_id$',
                    r'\bnext_link\b',
                    r'\brow_num\b',
                    r'\bcreate_date\b',
                    r'\bmaster_payer_ident\b',
                    r'\bprovisioner\b',
                    r'\bactive\b',
                    r'\bsnow_approval_group\b'
                ],
                    field_sort_order=account_sort,
                    field_names_remap=field_names_remap,
                    drop_empty_values=False)
            )

        valid_contacts: Contacts = Contacts([])
        for contact in contacts:

            if not contact:
                log.warning('null contact...')
                continue

            if not contact.get('contact_type'):
                log.warning(f'broken contact_type on contact: {contact}')
                continue  # broken contact-type
            else:
                valid_contacts.append(contact)

        if valid_contacts:
            if len(valid_contacts) > 0:
                self.add_blocks([
                    {
                        "type": "divider"
                    },
                    {
                        "type": "header",
                        "text": {
                            "type": "plain_text",
                            "text": "Contacts",
                            "emoji": True
                        }
                    }
                ])

            for contact in valid_contacts:
                self.add_block(
                    contact.to_slack_markdown_section(regex_field_filters=[
                        r'_id$',
                        r'\bnext_link\b',
                        r'\brow_num\b',
                        r'\bslater_update_user\b',
                        r'\bcreate_date\b',
                        r'\bdate_last_update\b',
                        r'\bcloud_account_name\b',
                        r'\bcloud_account_number\b',
                        r'\bactive\b',
                        r'\bcloud_account_root_email\b',
                        r'\baccount_type_name\b',
                        r'\bmaster_payer_ident\b',
                        r'\bmaster_payer_name\b',
                        r'\bbudget_alert_monthly_threshold\b',
                        r'\bbudget_account_string\b',
                        r'\bprovisioner\b',
                        r'\bdecommissioned\b',
                        r'\bproject_number\b',
                        r'\bsnow_approval_group\b',
                        r'\bgovernance_lob_name\b'
                    ],
                        field_sort_order=contacts_sort)
                )

        if applications:
            if len(applications) > 0:
                self.add_blocks([
                    {
                        "type": "divider"
                    },
                    {
                        "type": "header",
                        "text": {
                            "type": "plain_text",
                            "text": "Applications",
                            "emoji": True
                        }
                    }
                ])
            for application in applications:
                next_link = application.get('next_link')
                if not next_link:
                    next_link = 'http://localhost'

                encoded_url = urlsafe_b64encode(
                    compress(next_link.encode('utf-8'))).decode('utf-8')
                # 'application_description',
                # 'governance_lob_name',
                # 'tower_name',
                # 'business_unit_name',
                # 'slater_update_user',
                # 'date_last_update'
                self.add_block(
                    application.to_slack_markdown_section(regex_field_filters=[
                        r'_id$',
                        r'\bnext_link\b',
                        r'\brow_num\b',
                        r'\bdate_last_update\b',
                        r'\bslater_update_user\b',
                        r'\baccount_decommissioned\b',
                        r'\bcloud_account_lob\b',
                        r'\bcloud_account_name\b',
                        r'\bcloud_account_number\b',
                        r'\bbudget_alert_monthly_threshold\b',
                        r'\bapplication_active\b',
                        r'\bapplication_lob\b',
                        r'\bmaster_payer_ident\b',
                        r'\bcloud_organization_name\b',
                        r'\benvironment_type_name\b'
                    ],
                        field_sort_order=applications_sort,
                        link={
                        'block_id': f'slater_view_application_detail-{application.get("application_id")}',
                        'action_id': f'slater_view_application_detail-{application.get("application_id")}',
                        'label': 'View',
                        'value': f'view^{application.get("application_id")}^{encoded_url}'
                    }))


@xray.capture()
class CloudDeliveryServicesApplicationDetailView(ModalView):
    """ Generate a SLater Application List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> application = Application(application_name='JT Test', application_id='1', t_appid='SVC0123', next_link='https://localhost/applications/1', application_description='testing CDS view', row_num='21', the_secret_to_life='42', slater_update_user='zaphod.beeblebrox')

    >>> contacts = Contacts([Contact(contact_name='speed racer', contact_id='1', next_link='https://localhost/contacts?contact_id=1', contact_email='speed.racer@saturday.org', contact_type='Service VP', create_date='2020-07-22',date_last_update='2020-07-23'), Contact(contact_name='racer x', contact_id='2', next_link='https://localhost/contacts?contact_id=2', contact_email='racer.x@saturday.org', contact_type='Business Contact', create_date='2020-07-22',date_last_update='2020-07-23')])

    >>> accounts = Accounts([Account(cloud_account_name='Atari', cloud_account_number='0423456789', next_link='https://localhost/accounts/0423456789', cloud_account_root_email='sonic.hedgehog@root.net')])

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> application_sort_order = ['application_name','t_appid','application_description','governance_lob_name','tower_name','business_unit_name','slater_update_user','date_last_updated','the_secret_to_life']

    >>> contacts_sort_order = ['contact_type','contact_name','contact_email']

    >>> accounts_sort_order = ['cloud_account_name','cloud_account_number','cloud_account_root_email','environment_type_name']

    >>> view = CloudDeliveryServicesApplicationDetailView(log, slack_client, application=application, contacts=contacts, accounts=accounts, application_sort=application_sort_order, contacts_sort=contacts_sort_order, accounts_sort=accounts_sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_application_action-1'

    >>> len(blocks)
    10

    >>> blocks[2]['text']['text']
    '*Application Name:* JT Test\\n*t_AppID:* SVC0123\\n*Application Description:* testing CDS view\\n*Slater Update User:* <@zaphod.beeblebrox>\\n*The Secret To Life:* 42'

    >>> blocks[5]['text']['text']
    '*Contact Type:* Service VP\\n*Contact Name:* speed racer\\n*Contact Email:* speed.racer@saturday.org'

    >>> blocks[9]['text']['text']
    '*Account Name:* Atari\\n*Account Number:* 0423456789\\n*Cloud Account Root Email:* sonic.hedgehog@root.net'

    Negative test with broken data
    >>> del contacts[0]['contact_type']
    >>> contacts[1] = None
    >>> view = CloudDeliveryServicesApplicationDetailView(log, slack_client, application=application, contacts=contacts, accounts=accounts, application_sort=application_sort_order, contacts_sort=contacts_sort_order, accounts_sort=accounts_sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    6

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self,
                 log,
                 slack_client,
                 application: Application = None,
                 contacts: Contacts = None,
                 accounts: Accounts = None,
                 application_sort: List[str] = None,
                 contacts_sort: List[str] = None,
                 accounts_sort: List[str] = None,
                 callback_id="CloudDeliveryServicesDetailView",
                 title: str = "SLater",
                 close: str = "Close",
                 blocks: dict = None,
                 request: dict = None,
                 metadata: dict = None) -> None:

        super().__init__(log,
                         slack_client,
                         callback_id=callback_id,
                         title=title,
                         close=close,
                         blocks=blocks,
                         metadata=metadata,
                         request=request)

        if not application_sort:
            application_sort = [
                'application_name',
                't_appid',
                'application_description',
                'governance_lob_name',
                'tower_name',
                'business_unit_name',
                'slater_update_user',
                'date_last_update'
            ]

        if not contacts_sort:
            contacts_sort = [
                'contact_type',
                'contact_name',
                'contact_email',
            ]

        if not accounts_sort:
            accounts_sort = [
                'cloud_account_name',
                'cloud_account_number',
                'cloud_account_root_email',
                'environment_type_name'
            ]

        field_names_remap = {
            'governance_lob_name': 'governance_LOB',
            'tower_name': 'tower',
            'cloud_account_number': 'account_number',
            'cloud_account_name': 'account_name',
            'environment_type_name': 'environment',
        }

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"slater_application_action-{application.get('application_id')}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*Applications > {application.get('application_name').title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_application_action-{application.get('application_id')}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{application.get('application_id')}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":bust_in_silhouette: Add Contact"
                            },
                            "value": f"add_contact^{application.get('application_id')}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":label: Custom Fields"
                            },
                            "value": f"get_custom_fields^{application.get('application_id')}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if application:
            slater_user = f"<@{application.get('slater_update_user')}>"
            application['slater_update_user'] = slater_user
            self.add_block(
                application.to_slack_markdown_section(regex_field_filters=[
                    r'_id$',
                    r'\bnext_link\b',
                    r'\brow_num\b',
                    r'\bmaster_payer_ident\b',
                    r'\btower_contact_name\b',
                    r'\btower_contact_email\b',
                    r'\bcreate_date\b',
                    r'\bapplication_budget_account_string\b',
                    r'\bpagerduty_url\b',
                    r'\bactive\b'
                ],
                    field_sort_order=application_sort,
                    field_names_remap=field_names_remap,
                    drop_empty_values=False)
            )

        valid_contacts: Contacts = Contacts([])
        for contact in contacts:

            if not contact:
                log.warning('null contact...')
                continue

            if not contact.get('contact_type'):
                log.warning(f'broken contact_type on contact: {contact}')
                continue  # broken contact-type
            else:
                valid_contacts.append(contact)

        if valid_contacts:
            if len(valid_contacts) > 0:
                self.add_blocks([
                    {
                        "type": "divider"
                    },
                    {
                        "type": "header",
                        "text": {
                            "type": "plain_text",
                            "text": "Contacts",
                            "emoji": True
                        }
                    }
                ])

            for contact in valid_contacts:
                self.add_block(
                    contact.to_slack_markdown_section(regex_field_filters=[
                        r'_id$',
                        r'\bnext_link\b',
                        r'\brow_num\b',
                        r'\btower_contact_name\b',
                        r'\btower_contact_email\b',
                        r'\bslater_update_user\b',
                        r'\bcreate_date\b',
                        r'\bdate_last_update\b',
                        r'\bapplication_name\b',
                        r'\bapplication_description\b',
                        r'\bt_appid\b',
                        r'\bgovernance_lob_name\b',
                        r'\bapplication_budget_account_string\b',
                        r'\bactive\b',
                        r'\bdescription\b'
                    ],
                        field_sort_order=contacts_sort)
                )

        if accounts:
            if len(accounts) > 0:
                self.add_blocks([
                    {
                        "type": "divider"
                    },
                    {
                        "type": "header",
                        "text": {
                            "type": "plain_text",
                            "text": "Accounts",
                            "emoji": True
                        }
                    }
                ])
            for account in accounts:
                next_link = account.get('next_link')
                if not next_link:
                    next_link = 'http://localhost'

                encoded_url = urlsafe_b64encode(
                    compress(next_link.encode('utf-8'))).decode('utf-8')

                self.add_block(
                    account.to_slack_markdown_section(regex_field_filters=[
                        r'_id$',
                        r'\bnext_link\b',
                        r'\brow_num\b',
                        r'\bdate_last_update\b',
                        r'\bslater_update_user\b',
                        r'\baccount_decommissioned\b',
                        r'\bcloud_account_lob\b',
                        r'\bbudget_alert_monthly_threshold\b',
                        r'\bapplication_name\b',
                        r'\bt_appid\b',
                        r'\bapplication_active\b',
                        r'\bapplication_lob\b',
                        r'\bmaster_payer_ident\b',
                        r'\bcloud_organization_name\b'
                    ],
                        field_sort_order=accounts_sort,
                        link={
                        'block_id': f'slater_view_account_detail-{account.get("cloud_account_number")}',
                        'action_id': f'slater_view_account_detail-{account.get("cloud_account_number")}',
                        'label': 'View',
                        'value': f'view^{account.get("cloud_account_number")}^{encoded_url}'
                    },
                        field_names_remap=field_names_remap)
                )


@xray.capture()
class SLaterAccountCreateView(ModalView):
    """ Validate we can create an account modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> account = Account(cloud_account_name='', cloud_account_number='')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterAccountCreateView(log, slack_client, slater_object=account, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'cloud_account_name'

    >>> len(edit.data['blocks'])
    4

    >>> edit = SLaterAccountCreateView(log, slack_client, slater_object=None, request=request)

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, slater_object: Account = None, callback_id="SLaterAccountCreateView", title="SLater - Create", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Save")

        if not slater_object:
            slater_object = Account(**Account().get_blank_form_values())

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = slater_object.to_slack_modal_form(
                field_sort_order=[
                    'cloud_account_name',
                    'cloud_account_number',
                    'cloud_account_root_email',
                    'account_type_id',
                    'master_payer_id',
                    'application_id',
                    # 'primary_funding_applcation_name',
                    # 'primary_funding_t_appid',
                    'budget_alert_monthly_threshold',
                    'budget_account_string',
                    'project_number',
                    'lob_id',
                    'tower_id',
                    'intake_number',
                    'provisioner',
                    'snow_approval_group',
                    'decommissioned',
                ],
                regex_field_filters=[
                    r'cloud_account_id',
                    r'_link$',
                    r'account_type_name',
                    r'master_payer_ident',
                    r'master_payer_name',
                    r'^root_email',
                    r'environment_type_id',
                    r'governance_lob_name',
                    r'create_date',
                    r'primary_funding_application_id',
                    r'primary_funding_t_appid',
                    r'primary_funding_applcation_name',
                    r'tower_name',
                    r'date_last_update',
                    r'slater_update_user',
                    r'row_num'
                ],
                slack_element_options={
                    'account_type_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': None,
                        'action_id': 'slater_selected_account_types',
                        'label': 'Environment'
                    },
                    'decommissioned': {
                        'options': [{'Yes': '0'}, {'No': '1'}],
                        'transform': [{'0': 'Yes'}, {'1': 'No'}]
                    },
                    'lob_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': None,
                        'action_id': 'slater_selected_lobs',
                        'label': 'Governance Lob'
                    },
                    'master_payer_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': None,
                        'action_id': 'slater_selected_master_payers',
                        'label': 'Master Payer'
                    },
                    'tower_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': None,
                        'action_id': 'slater_selected_towers',
                        'label': 'Tower'
                    },
                    'application_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': None,
                        'action_id': 'slater_selected_applications',
                        'label': 't_AppID'
                    }
                })
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterAccountTypeCreateView(ModalView):
    """ Validate we can create an account modal view for edit

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> account_type = AccountType(account_type_name='non-production', account_type_id='1', next_link='https://localhost/accounttypes?account_type_id=1', date_last_update='2020-07-22')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterAccountTypeCreateView(log, slack_client, slater_object=account_type, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'account_type_name'

    >>> len(edit.data['blocks'])
    4

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, slater_object: AccountType = None, callback_id="SLaterAccountTypeCreateView", title="SLater - Create", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Save")
        if not slater_object:
            slater_object = AccountType(
                **AccountType().get_blank_form_values())

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = slater_object.to_slack_modal_form(
                regex_field_filters=[
                    r'account_type_id',
                    r'_link$'
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterApplicationCreateView(ModalView):
    """ Validate we can create an application modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> joust = Application(**{'application_name': 'Joust', 'application_id': '6', 'next_link': 'https://localhost/applications?application_id=6', 't_appid': 'SVCD006', 'application_description': 'Ride a Ostrich', 'lob_id': '1', 'create_date': '1970-01-01', 'update_date': '1970-01-01', 'application_budget_account_string': 'budget$1', 'tower_id':'1'})
    >>> slater = MockSLater()
    >>> joust.initialize_clients({},log,{},slater)
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterApplicationEditView(log, slack_client, application=joust, request=request)

    >>> assert(edit.data['blocks'][2]['element']['action_id'] == "application_name"), f"expected 'application_name', got \'{edit.data['blocks'][0]['action_id']}\'"
    >>> assert(len(edit.data['blocks']) == 7), f"expected seven, got {len(edit.data['blocks'])}"

    >>> json.dumps(edit.data['blocks'])  # noqa E501
    '[{"type": "context", "elements": [{"type": "mrkdwn", "text": ":pearson: _Submitted by @john.torres_ on _2020-07-21_"}]}, {"type": "divider"}, {"type": "input", "block_id": "application_name", "element": {"type": "plain_text_input", "action_id": "application_name", "initial_value": "Joust"}, "label": {"type": "plain_text", "text": "Application Name", "emoji": true}}, {"type": "input", "block_id": "t_appid", "element": {"type": "plain_text_input", "action_id": "t_appid", "initial_value": "SVCD006"}, "label": {"type": "plain_text", "text": "t_AppID", "emoji": true}}, {"type": "input", "block_id": "application_description", "element": {"type": "plain_text_input", "action_id": "application_description", "initial_value": "Ride a Ostrich"}, "label": {"type": "plain_text", "text": "Application Description", "emoji": true}}, {"type": "input", "block_id": "tower_id", "label": {"type": "plain_text", "text": "Tower"}, "element": {"type": "external_select", "action_id": "slater_selected_towers", "placeholder": {"type": "plain_text", "text": "Choose tower_id"}, "min_query_length": 2}}, {"type": "input", "block_id": "application_budget_account_string", "element": {"type": "plain_text_input", "action_id": "application_budget_account_string", "initial_value": "budget$1"}, "label": {"type": "plain_text", "text": "Application Budget Account String", "emoji": true}}]'

    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, slater_object: Application = None, callback_id="SLaterApplicationCreateView", title="SLater - App Create", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Save")
        if not slater_object:
            slater_object = Application(
                **Application().get_blank_form_values())

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = slater_object.to_slack_modal_form(
                regex_field_filters=[
                    r'application_id',
                    r'_link$',
                    r'update_date'
                ],
                slack_element_options={
                    'active': {
                        'options': [{'Yes': '0'}, {'No': '1'}],
                        'transform': [{'0': 'Yes'}, {'1': 'No'}]
                    },
                    'lob_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': None,
                        'action_id': 'slater_selected_lobs',
                        'label': 'Governance Lob'
                    },
                    'tower_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': None,
                        'action_id': 'slater_selected_towers',
                        'label': 'Tower'
                    },
                    'business_unit_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': None,
                        'action_id': 'slater_selected_business_units',
                        'label': 'Business Unit Name'
                    }
                })
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterBusinessUnitCreateView(ModalView):
    """ Validate we can create an account modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> data = BusinessUnit(**{'business_unit_name': 'Assessments', 'business_unit_id': '1', 'next_link': 'https://localhost/businessunits?business_unit_id=1', 'create_date': '1970-01-01', 'update_date': '1970-01-01'}) # noqa E501
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterBusinessUnitCreateView(log, slack_client, slater_object=data, request=request)
    >>> edit.data['blocks'][2]['element']['action_id']
    'business_unit_name'

    >>> len(edit.data['blocks'])
    5

    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, slater_object: BusinessUnit = None, callback_id="SLaterBusinessUnitCreateView", title="SLater - Create", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Save")
        if not slater_object:
            slater_object = BusinessUnit(
                **BusinessUnit().get_blank_form_values())

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = slater_object.to_slack_modal_form(
                regex_field_filters=[
                    r'business_unit_id',
                    r'_link$'
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterContactCreateView(ModalView):
    """ Validate we can create an account modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> data = Contact(contact_name='speed racer', contact_id='1', next_link='https://localhost/contacts?contact_id=1', contact_email='speed.racer@saturday.org', contact_type='Service VP', create_date='2020-07-22',date_last_update='2020-07-23')
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterContactCreateView(log, slack_client, slater_object=data, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'contact_name'

    >>> len(edit.data['blocks'])
    7

    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, slater_object: Contact = None, application: Application = None, account: Account = None, callback_id="SLaterContactCreateView", title="SLater", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title='Save')
        if not slater_object:
            slater_object = Contact(**Contact().get_blank_form_values())

        log.debug(f'contact: {slater_object}')

        if not blocks:
            log.debug('no blocks, populating view from object')
            contact_email = {
                'contact_email': {
                    'options': [],
                    'transform': [],
                    'action_id': 'servicenow_selected_emails'
                }
            }
            log.debug(f"email: {slater_object.get('contact_email')}")
            if slater_object.get('contact_email'):
                contact_email['contact_email']['initial_title'] = slater_object.get(
                    'contact_email')

            log.debug(f'contact_email: {contact_email}')
            blocks = slater_object.to_slack_modal_form(
                regex_field_filters=[
                    r'contact_id',
                    r'_link$',
                    r'update_date'
                ],
                slack_element_options=contact_email)
            self.add_blocks(blocks)

        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterContactTypeCreateView(ModalView):
    """ Validate we can create an account modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> contact_type = ContactType(contact_type='Duckie', contact_type_id='124', next_link='https://localhost/contacttypes?contact_id=124', create_date='2020-07-22', update_date='2020-07-23')
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterContactTypeCreateView(log, slack_client, slater_object=contact_type, request=request)
    >>> edit.data['blocks'][2]['element']['action_id']
    'contact_type'

    >>> len(edit.data['blocks'])
    5
    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, slater_object: ContactType = None, callback_id="SLaterContactTypeCreateView", title="SLater - Create", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Save")
        if not slater_object:
            slater_object = ContactType(
                **ContactType().get_blank_form_values())

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = slater_object.to_slack_modal_form(
                regex_field_filters=[
                    r'contact_type_id',
                    r'_link$'
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterLOBCreateView(ModalView):
    """ Validate we can create an account modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> lob = LOB(governance_lob_name='Assessments', lob_id='1', next_link='https://localhost/lobs?lob_id=1', create_date='2020-07-22',update_date='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterLOBCreateView(log, slack_client, slater_object=lob, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'governance_lob_name'

    >>> len(edit.data['blocks'])
    5

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, slater_object: LOB = None, callback_id="SLaterLOBCreateView", title="SLater - Create", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Save")
        if not slater_object:
            slater_object = LOB(**LOB().get_blank_form_values())

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = slater_object.to_slack_modal_form(
                regex_field_filters=[
                    r'lob_id',
                    r'_link$'
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterMasterPayerCreateView(ModalView):
    """ Validate we can create an account modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> data = MasterPayer(**{'master_payer_ident': '111111111111', 'master_payer_name': 'SLT BU Master Payer 1', 'master_payer_id':'1', 'next_link': 'https://localhost/masterpayers?master_payer_id=1', 'date_last_update': '1970-01-01'}) # noqa E501

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterMasterPayerCreateView(log, slack_client, slater_object=data, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'master_payer_name'

    >>> len(edit.data['blocks'])
    4

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, slater_object: MasterPayer = None, callback_id="SLaterMasterPayerCreateView", title="SLater - Create", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Save")

        if not slater_object:
            slater_object = MasterPayer(
                **MasterPayer().get_blank_form_values())

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = slater_object.to_slack_modal_form(
                regex_field_filters=[
                    r'master_payer_id',
                    r'_link$'
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterTowerCreateView(ModalView):
    """ Validate we can create an account modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> tower = Tower(**{'tower_id': 39, 'tower_name': 'AppEng', 'contact_id': 69, 'date_last_update': '2021-01-22', 'slater_update_user': 'U0EKDSZE2', 'next_link': 'https://api-dev.pearsoncloudmanagement.com/slater/towers?tower_name=AppEng', 'contact_name': 'Saleem Brahmanapalli', 'contact_email': 'syedsaleemjavid.brahmanapalli@pearson.com', 'row_num': 1})
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterTowerCreateView(log, slack_client, slater_object=tower, request=request)
    >>> edit.data['blocks'][2]['element']['action_id']
    'tower_name'

    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, slater_object: Tower = None, callback_id="SLaterTowerCreateView", title="SLater - Create", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Save")

        if not slater_object:
            slater_object = Tower(**Tower().get_blank_form_values())

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = slater_object.to_slack_modal_form(
                regex_field_filters=[
                    r'tower_id',
                    r'_link$'
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterAccountEditView(ModalView):
    """ Validate we can create an account modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> atari = Account(cloud_account_name='Atari', cloud_account_number='0423456789', next_link='https://localhost/accounts/0423456789', description='japan-born')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterAccountEditView(log, slack_client, account=atari, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'cloud_account_name'

    >>> len(edit.data['blocks'])
    4

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, account: Account = None, callback_id="SLaterAccountEditView", title="SLater - Edit", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Update")

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = account.to_slater_edit_form(
                field_sort_order=[
                    'cloud_account_name',
                    'cloud_account_number',
                    'cloud_account_root_email',
                    'account_type_id',
                    'master_payer_id',
                    # 'primary_funding_applcation_name',
                    # 'primary_funding_t_appid',
                    'budget_alert_monthly_threshold',
                    'budget_account_string',
                    'project_number',
                    'lob_id',
                    'tower_id',
                    'intake_number',
                    'decommissioned',
                ],
                regex_field_filters=[
                    r'cloud_account_id',
                    r'_link$',
                    r'update_date',
                ],
                slack_element_options={
                    'account_type_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': account.get('account_type_name'),
                        'action_id': 'slater_selected_account_types',
                        'label': 'Environment'
                    },
                    'decommissioned': {
                        'options': [{'Yes': '0'}, {'No': '1'}],
                        'transform': [{'0': 'Yes'}, {'1': 'No'}]
                    },
                    'lob_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': account.get('governance_lob_name'),
                        'action_id': 'slater_selected_lobs',
                        'label': 'Governance Lob'
                    },
                    'master_payer_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': account.get('master_payer_name'),
                        'action_id': 'slater_selected_master_payers',
                        'label': 'Master Payer'
                    },
                    'tower_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': account.get('tower_name'),
                        'action_id': 'slater_selected_towers',
                        'label': 'Tower'
                    }
                },
                skip_metadata_keys=[
                    'date_last_update',
                    'slater_update_user',
                    'next_link',
                    'governance_lob_name',
                    'business_unit_name',
                    'tower_name',
                    'tower_contact_id',
                    'tower_contact_name',
                    'tower_contact_email',
                    'row_num'
                ],
                slack_element_type_map=account.slack_element_type_map)
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })
        if account.slack_metadata:
            self.add_metadata(account.slack_metadata)


@xray.capture()
class SLaterAccountTypeEditView(ModalView):
    """ Validate we can create an account type modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> account_type = AccountType(account_type_name='non-production', account_type_id='1', next_link='https://localhost/accounttypes?account_type_id=1', date_last_update='2020-07-22')
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterAccountTypeEditView(log, slack_client, account_type=account_type, request=request)
    >>> assert(edit.data['blocks'][2]['element']['action_id'] == "account_type_name"), f"expected 'non-production', got \'{edit.data['blocks'][2]['action_id']}\'"
    >>> assert(len(edit.data['blocks']) == 4), f"expected four, got {len(edit.data['blocks'])}"
    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, account_type: AccountType = None, callback_id="SLaterAccountTypeEditView", title="SLater - Edit", close="Cancel", blocks=None, request: Dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, request=request)

        self.add_submit_button(title="Update")

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = account_type.to_slack_modal_form(
                regex_field_filters=[
                    r'account_type_id',
                    r'_link$',
                    r'update_date',
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterApplicationEditView(ModalView):
    """ Validate we can create an application modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> joust = Application(**{'application_name': 'Joust', 'application_id': '6', 'next_link': 'https://localhost/applications?application_id=6', 't_appid': 'SVCD006', 'application_description': 'Ride a Ostrich', 'lob_id': '1', 'create_date': '1970-01-01', 'update_date': '1970-01-01', 'application_budget_account_string': 'budget$1', 'tower_id':'1'})
    >>> slater = MockSLater()
    >>> joust.initialize_clients({},log,{},slater)
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterApplicationEditView(log, slack_client, application=joust, request=request)

    >>> assert(edit.data['blocks'][2]['element']['action_id'] == "application_name"), f"expected 'application_name', got \'{edit.data['blocks'][0]['action_id']}\'"
    >>> assert(len(edit.data['blocks']) == 7), f"expected seven, got {len(edit.data['blocks'])}"

    >>> json.dumps(edit.data['blocks'])  # noqa E501
    '[{"type": "context", "elements": [{"type": "mrkdwn", "text": ":pearson: _Submitted by @john.torres_ on _2020-07-21_"}]}, {"type": "divider"}, {"type": "input", "block_id": "application_name", "element": {"type": "plain_text_input", "action_id": "application_name", "initial_value": "Joust"}, "label": {"type": "plain_text", "text": "Application Name", "emoji": true}}, {"type": "input", "block_id": "t_appid", "element": {"type": "plain_text_input", "action_id": "t_appid", "initial_value": "SVCD006"}, "label": {"type": "plain_text", "text": "t_AppID", "emoji": true}}, {"type": "input", "block_id": "application_description", "element": {"type": "plain_text_input", "action_id": "application_description", "initial_value": "Ride a Ostrich"}, "label": {"type": "plain_text", "text": "Application Description", "emoji": true}}, {"type": "input", "block_id": "tower_id", "label": {"type": "plain_text", "text": "Tower"}, "element": {"type": "external_select", "action_id": "slater_selected_towers", "placeholder": {"type": "plain_text", "text": "Choose tower_id"}, "min_query_length": 2}}, {"type": "input", "block_id": "application_budget_account_string", "element": {"type": "plain_text_input", "action_id": "application_budget_account_string", "initial_value": "budget$1"}, "label": {"type": "plain_text", "text": "Application Budget Account String", "emoji": true}}]'

    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, application: Application = None, callback_id="SLaterApplicationEditView", title="SLater - App Edit", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_submit_button(title="Update")

        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = application.to_slater_edit_form(
                field_sort_order=[
                    'application_name',
                    't_appid',
                    'application_description',
                    'governance_lob_name',
                    'tower_id',
                    'business_unit_id',
                    'slater_update_user',
                    'application_budget_account_string',
                    'pagerduty_url',
                    'active'
                ],
                regex_field_filters=[
                    r'application_id',
                    r'_link$',
                    r'update_date',
                    r'date_last_update',
                    r'create_date',
                    r'row_num',
                    r'business_unit_name',
                    r'tower_name',
                    r'tower_contact_name',
                    r'tower_contact_email',
                    r'governance_lob_name',
                    r'tower_contact_id',
                    r'slater_update_user'
                ],
                slack_element_options={
                    'active': {
                        'options': [{'Yes': '0'}, {'No': '1'}],
                        'transform': [{'0': 'Yes'}, {'1': 'No'}]
                    },
                    'lob_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': application.get('governance_lob_name'),
                        'action_id': 'slater_selected_lobs',
                        'label': 'Governance Lob'
                    },
                    'tower_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': application.get('tower_name'),
                        'action_id': 'slater_selected_towers',
                        'label': 'Tower'
                    },
                    'business_unit_id': {
                        'options': [],
                        'transform': [],
                        'initial_title': application.get('business_unit_name'),
                        'action_id': 'slater_selected_business_units',
                        'label': 'Business Unit Name'
                    }
                },
                skip_metadata_keys=[
                    'date_last_update',
                    'slater_update_user',
                    'next_link',
                    'governance_lob_name',
                    'business_unit_name',
                    'tower_name',
                    'tower_contact_id',
                    'tower_contact_name',
                    'tower_contact_email',
                    'row_num'
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })
        if application.slack_metadata:
            self.add_metadata(application.slack_metadata)


@xray.capture()
class SLaterApplicationContactEditView(ModalView):
    """ Validate we can create an account type modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> account_type = AccountType(account_type_name='non-production', account_type_id='1', next_link='https://localhost/accounttypes?account_type_id=1', date_last_update='2020-07-22')
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterAccountTypeEditView(log, slack_client, account_type=account_type, request=request)
    >>> assert(edit.data['blocks'][2]['element']['action_id'] == "account_type_name"), f"expected 'non-production', got \'{edit.data['blocks'][2]['action_id']}\'"
    >>> assert(len(edit.data['blocks']) == 4), f"expected four, got {len(edit.data['blocks'])}"
    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: ApplicationContact = None, callback_id="", title="", close="Cancel", blocks=None, request: Dict = None) -> None:
        object_type = data.GetObjectType()

        if not callback_id:
            callback_id = f"SLater{object_type}EditView"

        if not title:
            title = f"SLater {object_type}"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, request=request)

        self.add_submit_button(title="Update")
        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = data.to_slack_modal_form(
                regex_field_filters=[
                    r'application_contact_id',
                    r'_link$',
                    r'update_date',
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterBusinessUnitEditView(ModalView):
    """ Validate we can create an account type modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> data = BusinessUnit(**{'business_unit_name': 'Assessments', 'business_unit_id': '1', 'next_link': 'https://localhost/businessunits?business_unit_id=1', 'create_date': '1970-01-01', 'update_date': '1970-01-01'}) # noqa E501

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterBusinessUnitEditView(log, slack_client, data=data, request=request)

    >>> edit.data['blocks'][2]['element']['action_id'] == "business_unit_name"
    True

    >>> len(edit.data['blocks'])
    4

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: BusinessUnit = None, callback_id="", title="", close="Cancel", blocks=None, request: Dict = None) -> None:
        object_type = data.GetObjectType()

        if not callback_id:
            callback_id = f"SLater{object_type}EditView"

        if not title:
            title = f"SLater {object_type}"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, request=request)

        self.add_submit_button(title="Next")
        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = data.to_slack_modal_form(
                regex_field_filters=[
                    r'business_unit_id',
                    r'_link$',
                    r'update_date',
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterContactEditView(ModalView):
    """ Validate we can create an contact modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved contact
    >>> speed_racer = Contact(contact_name='Speed Racer',contact_id='1',next_link='N/A',contact_email='N/A')

    >>> speed_racer.get('contact_name').title()
    'Speed Racer'

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterContactEditView(log, slack_client, data=speed_racer, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'contact_name'

    >>> len(edit.data['blocks'])
    4

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: Contact = None, callback_id="", title="", close="Cancel", blocks=None, request: Dict = None) -> None:
        object_type = data.GetObjectType()

        if not callback_id:
            callback_id = f"SLater{object_type}EditView"

        if not title:
            title = f"SLater {object_type}"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, request=request)

        self.add_submit_button(title="Update")
        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = data.to_slack_modal_form(
                regex_field_filters=[
                    r'contact_id',
                    r'_link$',
                    r'update_date'
                ],
                slack_element_options={
                    'contact_email': {
                        'options': [],
                        'transform': [],
                        'initial_title': data.get('contact_email'),
                        'action_id': 'servicenow_selected_emails'
                    }
                })
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterContactTypeEditView(ModalView):
    """ Validate we can create an account type modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> contact_type = ContactType(contact_type='Duckie', contact_type_id='124', next_link='https://localhost/contacttypes?contact_id=124', create_date='2020-07-22', update_date='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterContactTypeEditView(log, slack_client, data=contact_type, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'contact_type'

    >>> len(edit.data['blocks'])
    4

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: ContactType = None, callback_id="", title="", close="Cancel", blocks=None, request: Dict = None) -> None:
        object_type = data.GetObjectType()

        if not callback_id:
            callback_id = f"SLater{object_type}EditView"

        if not title:
            title = f"SLater {object_type}"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, request=request)

        self.add_submit_button(title="Update")
        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = data.to_slack_modal_form(
                regex_field_filters=[
                    r'contact_type_id',
                    r'_link$',
                    r'update_date',
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterLOBEditView(ModalView):
    """ Validate we can create an account type modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'
    >>> testing.export_secret_arns(os.environ['STACK_NAME'])
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> data = LOB(**{'governance_lob_name': 'Eiffel Tower', 'lob_id':'1', 'next_link': 'https://localhost/towers?tower_id=1', 'date_last_update': '1970-01-01','contact_id':'1'}) # noqa E501
    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterLOBEditView(log, slack_client, data=data, request=request)
    >>> edit.data['blocks'][2]['element']['action_id'] == "governance_lob_name"
    True

    >>> len(edit.data['blocks'])
    4
    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: LOB = None, callback_id="", title="", close="Cancel", blocks=None, request: Dict = None) -> None:
        object_type = data.GetObjectType()

        if not callback_id:
            callback_id = f"SLater{object_type}EditView"

        if not title:
            title = f"SLater {object_type}"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, request=request)

        self.add_submit_button(title="Update")
        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = data.to_slack_modal_form(
                regex_field_filters=[
                    r'lob_id',
                    r'_link$',
                    r'date_last_update',
                    r'slater_update_user',
                    r'row_num'
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterMasterPayerEditView(ModalView):
    """ Validate we can create an account type modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> data = MasterPayer(**{'master_payer_ident': '111111111111', 'master_payer_name': 'SLT BU Master Payer 1', 'master_payer_id':'1', 'next_link': 'https://localhost/masterpayers?master_payer_id=1', 'date_last_update': '1970-01-01'}) # noqa E501

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterMasterPayerEditView(log, slack_client, data=data, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'master_payer_name'

    >>> len(edit.data['blocks'])
    4

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: MasterPayer = None, callback_id="", title="", close="Cancel", blocks=None, request: Dict = None) -> None:
        object_type = data.GetObjectType()

        if not callback_id:
            callback_id = f"SLater{object_type}EditView"

        if not title:
            title = f"SLater {object_type}"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, request=request)

        self.add_submit_button(title="Update")
        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = data.to_slack_modal_form(
                regex_field_filters=[
                    r'master_payer_id',
                    r'_link$',
                    r'update_date',
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterTowerEditView(ModalView):
    """ Validate we can create an account type modal view for edit
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> tower = Tower(**{'tower_name': 'AppEng', 'contact_id': 69, 'tower_id': 39, 'date_last_update': '2021-01-22', 'slater_update_user': 'U0EKDSZE2', 'next_link': 'https://api-dev.pearsoncloudmanagement.com/slater/towers?tower_name=AppEng', 'contact_name': 'Saleem Brahmanapalli', 'contact_email': 'syedsaleemjavid.brahmanapalli@pearson.com', 'row_num': 1})

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> edit = SLaterTowerEditView(log, slack_client, data=tower, request=request)

    >>> edit.data['blocks'][2]['element']['action_id']
    'tower_name'

    >>> len(edit.data['blocks'])
    9

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: Tower = None, callback_id="", title="", close="Cancel", blocks=None, request: Dict = None) -> None:
        object_type_camel = data.GetObjectType()

        if not callback_id:
            callback_id = f"SLater{object_type_camel}EditView"

        if not title:
            title = f"SLater {object_type_camel}"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, request=request)

        self.add_submit_button(title="Update")
        if not blocks:
            log.debug('no blocks, populating view from object')
            blocks = data.to_slack_modal_form(
                regex_field_filters=[
                    r'tower_id',
                    r'_link$',
                    r'update_date',
                ])
            log.debug(f'blocks populated: {blocks}')
            self.add_blocks(blocks)
        log.debug(f'incoming request: {request}')
        if request:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })
            self.data['blocks'].insert(0, {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                    }
                ]
            })


@xray.capture()
class SLaterAccountDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> account = Account(cloud_account_name='Atari', cloud_account_number='0423456789', next_link='https://localhost/accounts/0423456789', cloud_account_root_email='sonic.hedgehog@root.net')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['cloud_account_name', 'cloud_account_number', 'cloud_account_root_email', 'environment_type_name', 'account_type_name', 'master_payer_name', 'primary_funding_applcation_name', 'primary_funding_t_appid', 'budget_alert_monthly_threshold', 'budget_account_string', 'intake_number', 'project_number', 'decommissioned', 'governance_lob_name', 'tower_name', 'slater_update_user', 'date_last_update']

    >>> view = SLaterAccountDetailView(log, slack_client, data=account, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_account_action-0423456789'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
    '*Account Name:* Atari\\n*Account Number:* 0423456789\\n*Cloud Account Root Email:* sonic.hedgehog@root.net\\n*Slater Update User:* <@None>'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: Account = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type = data.GetObjectType(underscore=False)
        primary_key: str = f"cloud_{object_type.lower()}_number"
        primary_title = f"cloud_{object_type.lower()}_name"
        base_block_id = f"slater_{object_type.lower()}_action"

        if not callback_id:
            callback_id = f"SLater{object_type}DetailView"

        if not title:
            title = f"SLater {object_type}"

        if not sort_order:
            sort_order = [
                'cloud_account_name',
                'cloud_account_number',
                'cloud_account_root_email',
                'environment_type_name',
                'account_type_name',
                'master_payer_name',
                'master_payer_ident',
                'primary_funding_applcation_name',
                'primary_funding_t_appid',
                'budget_alert_monthly_threshold',
                'budget_account_string',
                'project_number',
                'governance_lob_name',
                'tower_name',
                'intake_number',
                'decommissioned',
                'slater_update_user',
                'date_last_update'
            ]

        field_names_remap = {
            'cloud_account_number': 'account_number',
            'cloud_account_name': 'account_name',
            'account_type_name': 'environment',
            'master_payer_name': 'master_payer',
            'master_payer_ident': 'master_payer_number',
            'budget_account_string': 'funding_account_string',
            'governance_lob_name': 'governance_LOB',
            'primary_funding_t_appid': 'primary_t_AppID',
            'primary_funding_applcation_name': 'application',
            'tower_name': 'tower',
        }

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with account: {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{data.GetObjectType()} > {data.get(primary_title).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":bust_in_silhouette: Add Contact"
                            },
                            "value": f"add_contact^{data.get(primary_key)}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":label: Custom Fields"
                            },
                            "value": f"get_custom_fields^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b',
                    r'_id$',
                    r'row_num',
                    r'provisioner',

                ],
                    field_names_remap=field_names_remap,
                    field_sort_order=sort_order,
                    drop_empty_values=False)
            )


@xray.capture()
class SLaterAccountTypeDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> account_type = AccountType(account_type_name='non-production', account_type_id='1', next_link='https://localhost/accounttypes?account_type_id=1', date_last_update='2020-07-22')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['account_type_name', 'slater_update_user', 'date_last_update']

    >>> view = SLaterAccountTypeDetailView(log, slack_client, data=account_type, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_account_type_action-1'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
    '*Account Type Name:* non-production\\n*Slater Update User:* <@None>\\n*Date Last Update:* 2020-07-22'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: AccountType = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_lower = data.GetObjectType(underscore=True)
        object_type_camel = data.GetObjectType(underscore=False)
        primary_key = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}_name"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DetailView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{data.GetObjectType()} > {data.get(primary_title).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\baccount_type_id\b',
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )


@xray.capture()
class SLaterApplicationDetailView(ModalView):
    @xray.capture()
    def __init__(self, log, slack_client, application: Application = None, contacts: Contacts = None, accounts: Accounts = None, application_sort: List[str] = None, contacts_sort: List[str] = None, accounts_sort: List[str] = None, callback_id="SLaterApplicationDetailView", title="SLater", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        if not application_sort:
            application_sort = [
                'application_name',
                't_appid',
                'application_description',
                'governance_lob_name',
                'tower_name',
                'business_unit_name',
                'slater_update_user',
                'tower_contact_name',
                'tower_contact_email',
                'application_budget_account_string',
                'pagerduty_url',
                'date_last_update'
            ]

        if not contacts_sort:
            contacts_sort = [
                'contact_type',
                'contact_name',
                'contact_email',
            ]

        if not accounts_sort:
            accounts_sort = [
                'cloud_account_name',
                'cloud_account_number',
                'cloud_account_root_email',
                'environment_type_name'
            ]

        field_names_remap = {
            'governance_lob_name': 'governance_LOB',
            'tower_name': 'tower',
        }

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"slater_application_action-{application.get('application_id')}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*Applications > {application.get('application_name').title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_application_action-{application.get('application_id')}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{application.get('application_id')}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":bust_in_silhouette: Add Contact"
                            },
                            "value": f"add_contact^{application.get('application_id')}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":label: Custom Fields"
                            },
                            "value": f"get_custom_fields^{application.get('application_id')}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if application:
            slater_user = f"<@{application.get('slater_update_user')}>"
            application['slater_update_user'] = slater_user
            self.add_block(
                application.to_slack_markdown_section(
                    field_sort_order=application_sort,
                    field_names_remap=field_names_remap,
                    drop_empty_values=False
                )
            )

        if contacts:
            if len(contacts) > 0:
                self.add_blocks([
                    {
                        "type": "divider"
                    },
                    {
                        "type": "header",
                        "text": {
                            "type": "plain_text",
                            "text": "Contacts",
                            "emoji": True
                        }
                    }
                ])
            for contact in contacts:
                # print(f'contact_type: {contact.get("contact_type")}')
                if ('Service VP' in contact.get('contact_type') or
                        'Business Contact' in contact.get('contact_type')):
                    self.add_block(
                        contact.to_slack_markdown_section(regex_field_filters=[
                            r'_id$',
                            r'\bnext_link\b',
                            r'\brow_num\b',
                            r'\btower_contact_name\b',
                            r'\btower_contact_email\b',
                            r'\bslater_update_user\b',
                            r'\bcreate_date\b',
                            r'\bdate_last_update\b',
                            r'\bapplication_name\b',
                            r'\bapplication_description\b',
                            r'\bt_appid\b',
                            r'\bgovernance_lob_name\b',
                            r'\bapplication_budget_account_string\b',
                            r'\bactive\b',
                            r'\bdescription\b'
                        ],
                            field_sort_order=contacts_sort)
                    )

        if accounts:
            if len(accounts) > 0:
                self.add_blocks([
                    {
                        "type": "divider"
                    },
                    {
                        "type": "header",
                        "text": {
                            "type": "plain_text",
                            "text": "Accounts",
                            "emoji": True
                        }
                    }
                ])
            for account in accounts:
                next_link = account.get('next_link')
                if not next_link:
                    next_link = 'http://localhost'

                encoded_url = urlsafe_b64encode(
                    compress(next_link.encode('utf-8'))).decode('utf-8')

                self.add_block(
                    account.to_slack_markdown_section(regex_field_filters=[
                        r'_id$',
                        r'\bnext_link\b',
                        r'\brow_num\b',
                        r'\bdate_last_update\b',
                        r'\bslater_update_user\b',
                        r'\baccount_decommissioned\b',
                        r'\bcloud_account_lob\b',
                        r'\bbudget_alert_monthly_threshold\b',
                        r'\bapplication_name\b',
                        r'\bt_appid\b',
                        r'\bapplication_active\b',
                        r'\bapplication_lob\b',
                        r'\bmaster_payer_ident\b',
                        r'\bcloud_organization_name\b'
                    ],
                        field_sort_order=accounts_sort,
                        link={
                        'block_id': f'slater_view_account_detail-{account.get("cloud_account_number")}',
                        'action_id': f'slater_view_account_detail-{account.get("cloud_account_number")}',
                        'label': 'View',
                        'value': f'view^{account.get("cloud_account_number")}^{encoded_url}'
                    })
                )


@xray.capture()
class SLaterApplicationContactDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> contact = Contact(contact_name='speed racer', contact_id='1', next_link='https://localhost/contacts?contact_id=1', contact_email='speed.racer@saturday.org', contact_type='Tech Owner', create_date='2020-07-22',date_last_update='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['contact_type','contact_name','contact_email']

    >>> view = SLaterApplicationContactDetailView(log, slack_client, data=contact, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_contact_action-1'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
    '*Contact Type:* Tech Owner\\n*Contact Name:* speed racer\\n*Contact Email:* speed.racer@saturday.org'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: ApplicationContact = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_lower = data.GetObjectType(underscore=True)
        object_type_camel = data.GetObjectType(underscore=False)
        primary_key = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}_id"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DetailView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {str(data.get(primary_title)).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )


@xray.capture()
class SLaterBusinessUnitDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = BusinessUnit(**{'business_unit_name': 'Assessments', 'business_unit_id': '1', 'next_link': 'https://localhost/businessunits?business_unit_id=1', 'create_date': '1970-01-01', 'update_date': '1970-01-01'}) # noqa E501

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['business_unit_name', 'slater_update_user', 'create_date', 'update_date']

    >>> view = SLaterBusinessUnitDetailView(log, slack_client, data=data, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_business_unit_action-1'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
    '*Business Unit Name:* Assessments\\n*Slater Update User:* <@None>\\n*Create Date:* 1970-01-01\\n*Update Date:* 1970-01-01'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: BusinessUnit = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_lower = data.GetObjectType(underscore=True)
        object_type_camel = data.GetObjectType(underscore=False)
        primary_key = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}_name"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DetailView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {data.get(primary_title).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bbusiness_unit_id\b',
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )


@xray.capture()
class SLaterContactDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> contact = Contact(contact_name='speed racer', contact_id='1', next_link='https://localhost/contacts?contact_id=1', contact_email='speed.racer@saturday.org', contact_type='Tech Owner', create_date='2020-07-22',date_last_update='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['contact_type','contact_name','contact_email','create_date','slater_update_user','date_last_update']

    >>> view = SLaterContactDetailView(log, slack_client, data=contact, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_contact_action-1'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
    '*Contact Type:* Tech Owner\\n*Contact Name:* speed racer\\n*Contact Email:* speed.racer@saturday.org\\n*Create Date:* 2020-07-22\\n*Slater Update User:* <@None>\\n*Date Last Update:* 2020-07-23'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: Contact = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_camel = data.GetObjectType()
        object_type_lower = object_type_camel.lower()

        primary_key = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}_name"

        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DetailView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {data.get(primary_title).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bcontact_id\b',
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )


@xray.capture()
class SLaterContactTypeDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> contact_type = ContactType(contact_type='Duckie', contact_type_id='124', next_link='https://localhost/contacttypes?contact_id=124', create_date='2020-07-22', update_date='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['contact_type', 'contact_typd_id', 'create_date', 'update_date', 'contact_type','slater_update_user', 'date_last_update']

    >>> view = SLaterContactTypeDetailView(log, slack_client, data=contact_type,sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_contact_type_action-124'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
    '*Contact Type:* Duckie\\n*Create Date:* 2020-07-22\\n*Update Date:* 2020-07-23\\n*Slater Update User:* <@None>'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: ContactType = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_lower = data.GetObjectType(underscore=True)
        object_type_camel = data.GetObjectType(underscore=False)

        primary_key = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DetailView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {data.get(primary_title).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bcontact_type_id\b',
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )


@xray.capture()
class SLaterLOBDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> lob = LOB(governance_lob_name='Assessments', lob_id='1', next_link='https://localhost/lobs?lob_id=1', create_date='2020-07-22',update_date='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['governance_lob_name','create_date','slater_update_user','update_date']

    >>> view = SLaterLOBDetailView(log, slack_client, data=lob, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_lob_action-1'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
    '*Governance Lob Name:* Assessments\\n*Create Date:* 2020-07-22\\n*Slater Update User:* <@None>\\n*Update Date:* 2020-07-23'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: LOB = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_camel = data.GetObjectType()
        object_type_lower = object_type_camel.lower()
        primary_key = f"{object_type_lower}_id"
        primary_title = f"governance_{object_type_lower}_name"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DetailView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {data.get(primary_title).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\blob_id\b',
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )


@xray.capture()
class SLaterMasterPayerDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = MasterPayer(**{'master_payer_ident': '111111111111', 'master_payer_name': 'SLT BU Master Payer 1', 'master_payer_id':'1', 'next_link': 'https://localhost/masterpayers?master_payer_id=1', 'date_last_update': '1970-01-01'}) # noqa E501

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['master_payer_name','master_payer_ident','slater_update_user','date_last_update']

    >>> view = SLaterMasterPayerDetailView(log, slack_client, data=data, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_master_payer_action-1'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
    '*Master Payer Name:* SLT BU Master Payer 1\\n*Master Payer Ident:* 111111111111\\n*Slater Update User:* <@None>\\n*Date Last Update:* 1970-01-01'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: MasterPayer = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_lower = data.GetObjectType(underscore=True)
        object_type_camel = data.GetObjectType(underscore=False)
        primary_key = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}_name"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DetailView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_lower} > {data.get(primary_title).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bmaster_payer_id\b',
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )


@xray.capture()
class SLaterTowerDetailView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> tower = Tower(**{'contact_id': 69, 'tower_name': 'AppEng', 'tower_id': 39, 'date_last_update': '2021-01-22', 'slater_update_user': 'U0EKDSZE2', 'next_link': 'https://api-dev.pearsoncloudmanagement.com/slater/towers?tower_name=AppEng', 'contact_name': 'Saleem Brahmanapalli', 'contact_email': 'syedsaleemjavid.brahmanapalli@pearson.com', 'row_num': 1})

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['tower_name', 'contact_name', 'contact_email', 'date_last_update', 'account_type_name', 'slater_update_user']

    >>> view = SLaterTowerDetailView(log, slack_client, data=tower, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> blocks[0]['accessory']['action_id']
    'slater_tower_action-39'

    >>> len(blocks)
    3

    >>> blocks[2]['text']['text']
     '*Tower Name:* AppEng\\n*Contact Name:* Saleem Brahmanapalli\\n*Contact Email:* syedsaleemjavid.brahmanapalli@pearson.com\\n*Date Last Update:* 2021-01-22\\n*Slater Update User:* <@U0EKDSZE2>'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: Tower = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_camel: str = data.GetObjectType()
        object_type_lower: str = object_type_camel.lower()
        primary_key: str = f"{object_type_lower}_id"
        primary_title: str = f"{object_type_lower}_name"
        base_block_id: str = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DetailView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{data.GetObjectType()} > {data.get(primary_title).title()} > Details*"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"{base_block_id}-{data.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{data.get(primary_key)}"
                        }
                    ]
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\btower_id\b',
                    r'\bcontact_id\b',
                    r'\bnext_link\b',
                    r'\brow_num\b'
                ],
                    field_sort_order=sort_order)
            )


@xray.capture()
class SLaterAccountDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> account = Account(cloud_account_name='Atari', cloud_account_number='0423456789', next_link='https://localhost/accounts/0423456789', cloud_account_root_email='sonic.hedgehog@root.net')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['cloud_account_name', 'cloud_account_number', 'cloud_account_root_email', 'environment_type_name', 'account_type_name', 'master_payer_name', 'primary_funding_applcation_name', 'primary_funding_t_appid', 'budget_alert_monthly_threshold', 'budget_account_string', 'intake_number', 'project_number', 'decommissioned', 'governance_lob_name', 'tower_name', 'slater_update_user', 'date_last_update']

    >>> view = SLaterAccountDeleteView(log, slack_client, data=account, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Cloud Account Name:* Atari\\n*Cloud Account Number:* 0423456789\\n*Cloud Account Root Email:* sonic.hedgehog@root.net\\n*Slater Update User:* <@None>'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: Account = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type = data.GetObjectType(underscore=False)
        primary_key: str = f"cloud_{object_type.lower()}_number"
        primary_title = f"cloud_{object_type.lower()}_name"
        base_block_id = f"slater_{object_type.lower()}_action"

        if not callback_id:
            callback_id = f"SLater{object_type}DeleteView"

        if not title:
            title = f"SLater {object_type}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with account: {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{data.GetObjectType()} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterAccountTypeDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> account_type = AccountType(account_type_name='non-production', account_type_id='1', next_link='https://localhost/accounttypes?account_type_id=1', date_last_update='2020-07-22')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['account_type_name', 'account_type_id', 'slater_update_user', 'date_last_update']

    >>> view = SLaterAccountTypeDeleteView(log, slack_client, data=account_type, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Account Type Name:* non-production\\n*Account Type Id:* 1\\n*Slater Update User:* <@None>\\n*Date Last Update:* 2020-07-22'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: Account = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_camel = data.GetObjectType(underscore=False)
        object_type_lower = data.GetObjectType(underscore=True)
        primary_key: str = f"{object_type_lower}_number"
        primary_title = f"{object_type_lower}_name"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DeleteView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with account: {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{data.GetObjectType()} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterApplicationDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> application = Application(application_name='JT Test', application_id='1', t_appid='SVC0123', next_link='https://localhost/applications/1', application_description='testing CDS view', row_num='21', the_secret_to_life='42', slater_update_user='zaphod.beeblebrox')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['application_name','t_appid','application_description', 'application_id', 'governance_lob_name','tower_name','business_unit_name','slater_update_user','date_last_updated','the_secret_to_life']

    >>> view = SLaterApplicationDeleteView(log, slack_client, data=application, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Application Name:* JT Test\\n*t_AppID:* SVC0123\\n*Application Description:* testing CDS view\\n*Application Id:* 1\\n*Slater Update User:* <@zaphod.beeblebrox>\\n*The Secret To Life:* 42'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: Application = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type = data.GetObjectType(underscore=False)
        primary_key: str = f"{object_type.lower()}_id"
        primary_title = f"{object_type.lower()}_name"
        base_block_id = f"slater_{object_type.lower()}_action"

        if not callback_id:
            callback_id = f"SLater{object_type}DeleteView"

        if not title:
            title = f"SLater {object_type}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with account: {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{data.GetObjectType()} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b',
                    r'\brow_num\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterApplicationContactDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> skip('talk with jonathan')

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> application = Application(application_name='JT Test', application_id='1', t_appid='SVC0123', next_link='https://localhost/applications/1', application_description='testing CDS view', row_num='21', the_secret_to_life='42', slater_update_user='zaphod.beeblebrox')

    >>> contacts = Contacts([Contact(contact_name='speed racer', contact_id='1', next_link='https://localhost/contacts?contact_id=1', contact_email='speed.racer@saturday.org', contact_type='Tech Owner', create_date='2020-07-22',date_last_update='2020-07-23')])

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['contact_type','contact_name','contact_email']
    >>> application_sort_order = ['application_name','t_appid','application_description','governance_lob_name','tower_name','business_unit_name','slater_update_user','date_last_updated','the_secret_to_life']

    >>> view = SLaterApplicationContactDeleteView(log, slack_client, data=contacts, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Cloud Account Name:* Atari\\n*Cloud Account Number:* 0423456789\\n*Cloud Account Root Email:* sonic.hedgehog@root.net\\n*Slater Update User:* <@None>'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: ApplicationContact = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_lower = data.GetObjectType(underscore=True)
        object_type_camel = data.GetObjectType(underscore=False)
        primary_key: str = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}_name"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DeleteView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with : {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterBusinessUnitDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = BusinessUnit(**{'business_unit_name': 'Assessments', 'business_unit_id': '1', 'next_link': 'https://localhost/businessunits?business_unit_id=1', 'create_date': '1970-01-01', 'update_date': '1970-01-01'}) # noqa E501

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['business_unit_name', 'business_unit_id', 'slater_update_user', 'create_date', 'update_date']

    >>> view = SLaterBusinessUnitDeleteView(log, slack_client, data=data, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Business Unit Name:* Assessments\\n*Business Unit Id:* 1\\n*Slater Update User:* <@None>\\n*Create Date:* 1970-01-01\\n*Update Date:* 1970-01-01'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: BusinessUnit = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type = data.GetObjectType(underscore=True)
        primary_key: str = f"{object_type.lower()}_id"
        primary_title = f"{object_type.lower()}_name"
        base_block_id = f"slater_{object_type.lower()}_action"

        if not callback_id:
            callback_id = f"SLater{object_type}DeleteView"

        if not title:
            title = f"SLater {object_type}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with account: {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{data.GetObjectType()} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterContactDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = Contact(contact_name='speed racer', contact_id='1', next_link='https://localhost/contacts?contact_id=1', contact_email='speed.racer@saturday.org', contact_type='Tech Owner', create_date='2020-07-22',date_last_update='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['contact_type','contact_name','contact_email','contact_id','create_date','date_last_update','slater_update_user']

    >>> view = SLaterContactDeleteView(log, slack_client, data=data, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Contact Type:* Tech Owner\\n*Contact Name:* speed racer\\n*Contact Email:* speed.racer@saturday.org\\n*Contact Id:* 1\\n*Create Date:* 2020-07-22\\n*Date Last Update:* 2020-07-23\\n*Slater Update User:* <@None>'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: Contact = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type = data.GetObjectType(underscore=False)
        primary_key: str = f"{object_type.lower()}_id"
        primary_title = f"{object_type.lower()}_name"
        base_block_id = f"slater_{object_type.lower()}_action"

        if not callback_id:
            callback_id = f"SLater{object_type}DeleteView"

        if not title:
            title = f"SLater {object_type}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with account: {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{data.GetObjectType()} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterContactTypeDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = ContactType(contact_type='Duckie', contact_type_id='124', next_link='https://localhost/contacttypes?contact_id=124', create_date='2020-07-22', update_date='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['contact_type', 'contact_type_id', 'create_date', 'update_date', 'contact_type','slater_update_user', 'date_last_update']

    >>> view = SLaterContactTypeDeleteView(log, slack_client, data=data, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Contact Type:* Duckie\\n*Contact Type Id:* 124\\n*Create Date:* 2020-07-22\\n*Update Date:* 2020-07-23\\n*Slater Update User:* <@None>'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: ContactType = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_lower = data.GetObjectType(underscore=True)
        object_type_camel = data.GetObjectType(underscore=False)
        primary_key: str = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DeleteView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with : {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterLOBDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = LOB(governance_lob_name='Assessments', lob_id='1', next_link='https://localhost/lobs?lob_id=1', create_date='2020-07-22',update_date='2020-07-23')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['governance_lob_name','create_date','slater_update_user','update_date']

    >>> view = SLaterLOBDeleteView(log, slack_client, data=data, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Governance Lob Name:* Assessments\\n*Create Date:* 2020-07-22\\n*Slater Update User:* <@None>\\n*Update Date:* 2020-07-23'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: LOB = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type = data.GetObjectType(underscore=False)
        primary_key: str = f"{object_type.lower()}_id"
        primary_title = f"governance_{object_type.lower()}_name"
        base_block_id = f"slater_{object_type.lower()}_action"

        if not callback_id:
            callback_id = f"SLater{object_type}DeleteView"

        if not title:
            title = f"SLater {object_type}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with : {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterMasterPayerDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = MasterPayer(**{'master_payer_ident': '111111111111', 'master_payer_name': 'SLT BU Master Payer 1', 'master_payer_id':'1', 'next_link': 'https://localhost/masterpayers?master_payer_id=1', 'date_last_update': '1970-01-01'}) # noqa E501

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['master_payer_name','master_payer_ident','slater_update_user','date_last_update']

    >>> view = SLaterMasterPayerDeleteView(log, slack_client, data=data, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Master Payer Name:* SLT BU Master Payer 1\\n*Master Payer Ident:* 111111111111\\n*Slater Update User:* <@None>\\n*Date Last Update:* 1970-01-01'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, data: MasterPayer = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_camel = data.GetObjectType(underscore=False)
        object_type_lower = data.GetObjectType(underscore=True)
        primary_key: str = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}_name"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DeleteView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with account: {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


class SLaterSREDeleteView(ModalView):
    pass


@xray.capture()
class SLaterTowerDeleteView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = Tower(**{'contact_id': 69, 'tower_name': 'AppEng', 'tower_id': 39, 'date_last_update': '2021-01-22', 'slater_update_user': 'U0EKDSZE2', 'next_link': 'https://api-dev.pearsoncloudmanagement.com/slater/towers?tower_name=AppEng', 'contact_name': 'Saleem Brahmanapalli', 'contact_email': 'syedsaleemjavid.brahmanapalli@pearson.com', 'row_num': 1})

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> sort_order = ['tower_name', 'contact_name', 'contact_email', 'date_last_update', 'account_type_name', 'slater_update_user']

    >>> view = SLaterTowerDeleteView(log, slack_client, data=data, sort_order=sort_order)

    >>> blocks = view.to_slack_message().Blocks()

    >>> len(blocks)
    4

    >>> blocks[2]['text']['text']
    '*Tower Name:* AppEng\\n*Contact Name:* Saleem Brahmanapalli\\n*Contact Email:* syedsaleemjavid.brahmanapalli@pearson.com\\n*Date Last Update:* 2021-01-22\\n*Slater Update User:* <@U0EKDSZE2>'

    >>> blocks[3]['element']['options'][0]['text']['text']
    '*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._'

    >>> blocks[3]['element']['action_id']
    'confirm'

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: Tower = None, sort_order: List[str] = None, callback_id="", title="", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        object_type_camel = data.GetObjectType(underscore=False)
        object_type_lower = data.GetObjectType(underscore=True)
        primary_key: str = f"{object_type_lower}_id"
        primary_title = f"{object_type_lower}_name"
        base_block_id = f"slater_{object_type_lower}_action"

        if not callback_id:
            callback_id = f"SLater{object_type_camel}DeleteView"

        if not title:
            title = f"SLater {object_type_camel}"

        if not sort_order:
            sort_order = []

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        log.debug(f'working with account: {data}')
        log.debug(f'primary title: {primary_title}')
        self.add_submit_button(title="Delete")
        self.add_blocks([
            {
                "type": "section",
                "block_id": f"{base_block_id}-{data.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{object_type_camel} > {data.get(primary_title).title()} > Delete*"
                }
            },
            {
                "type": "divider"
            }
        ])

        if data:
            slater_user = f"<@{data.get('slater_update_user')}>"
            data['slater_update_user'] = slater_user
            self.add_block(
                data.to_slack_markdown_section(regex_field_filters=[
                    r'\bnext_link\b'
                ],
                    field_sort_order=sort_order)
            )

            self.add_block({
                "type": "input",
                "block_id": f"{base_block_id}_confirm-{data.get(primary_key)}",
                "element": {
                    "type": "checkboxes",
                    "options": [
                        {
                            "text": {
                                "type": "mrkdwn",
                                "text": "*Yes*, _I understand that this will *delete* the record and this action *cannot* be undone._"
                            },
                            "value": "yes"
                        }
                    ],
                    "action_id": "confirm"
                },
                "label": {
                    "type": "plain_text",
                    "text": f"Are you sure you want to delete {data.get(primary_title).title()} ({data.get(primary_key)})?",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterAccountsListView(ModalView):
    """ Generate a SLater Accounts List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> from slater import SLater

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    # >>> accounts = Accounts([Account(cloud_account_name='Atari', cloud_account_number='0423456789', next_link='https://localhost/accounts/0423456789',description='japan-born')])

    >>> slater = SLater(log,boto_session,http_session,config_file='environments/unit.ini')

    >>> accounts = slater.GetAccounts(limit=2, next_token=12)

    >>> accounts.next_token
    14

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> metadata = {'pages': [-1,3,6,9,12]}

    >>> view = SLaterAccountsListView(log, slack_client, accounts=accounts, metadata=metadata)

    # >>> view.data['blocks'][4]['accessory']['action_id']
    # 'slater_account_action-0123456789'

    # >>> len(view.data['blocks'])
    # 8

    # >>> print(view.toJSON())

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, accounts: Accounts = [], callback_id="SLaterAccountsListView", title="SLater Accounts List", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": "*Accounts*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": "help^accounts"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"GetAccounts^next_token={inclusive_bound},limit={accounts.limit}^GetSLaterAccountsListView"
            })

        if len(accounts) >= accounts.limit and accounts.next_token >= accounts.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"GetAccounts^next_token={accounts.next_token},limit={accounts.limit}^GetSLaterAccountsListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for account in accounts:
            log.debug(f"account: {account.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(account.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })
            self.add_block({
                "type": "section",
                "block_id": f"slater_account_action-{account.get('cloud_account_number')}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{account.get('cloud_account_name')}* ({account.get('cloud_account_number')})\n*Root Email:* {account.get('cloud_account_root_email')}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_account_action-{account.get('cloud_account_number')}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{account.get('cloud_account_number')}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":bust_in_silhouette: Add Contact"
                            },
                            "value": f"add_contact^{account.get('cloud_account_number')}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":label: Custom Fields"
                            },
                            "value": f"get_custom_fields^{account.get('cloud_account_number')}"
                        }
                    ]
                }
            })
            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_account_detail-{account.get('cloud_account_number')}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_account_detail-{account.get('cloud_account_number')}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{account.get('cloud_account_number')}^{encoded_url}"
                    }
                ]
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterAccountTypesListView(ModalView):
    def to_slack_message(self):
        return super().to_slack_message()

    def __init__(self, log, slack_client, data: AccountTypes = [], callback_id="", primary_key='account_type_id', primary_title='account_type_name', title="", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        object_type_lower: str = data.GetObjectType(underscore=True)
        object_type_camel: str = data.GetObjectType(underscore=False)

        if not primary_key:
            primary_key = 'id'

        if not primary_title:
            primary_title = 'name'

        if not callback_id:
            callback_id = f"SLater{object_type_camel}ListView"

        if not title:
            title = f"SLater {object_type_camel} List"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": f"*{object_type_camel}*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": f"help^{object_type_lower}"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"Get{object_type_camel}^next_token={inclusive_bound},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(data) >= data.limit and data.next_token >= data.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"Get{object_type_camel}^next_token={data.next_token},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for item in data:
            item_object_type: str = item.GetObjectType(underscore=True)
            log.debug(f"data: {data.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(item.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{item.get(primary_title)}*: {item.get(primary_key)}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{item.get(primary_key)}"
                        }
                    ]
                }
            })

            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{item.get(primary_key)}^{encoded_url}"
                    }
                ]
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterApplicationsListView(ModalView):
    """SLaterApplicationsListView represents a modal view listing of slater applications with pagination controls

    Parameters
    ==========
    log : psi_logger.Logger()
        Traditional python logger
    slack_client : slack_sdk.WebClient()
        Slack WebClient
    applications : slater.models.Applications()
        SLater Applications List
    callback_id : str
        A value that can be referenced in Slack's eventing model
    title: str
        Title for the Modal window
    close: str
        Title for the close button
    blocks: list
        List of Slack BlockKit elements

    Testing
    =======

    An example of asserting modal view elements:

    ** Setup testing environment **
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    ** Create a slack client object **
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> applications = Applications([Application(application_name='Joust', application_id='6', next_link='https://localhost/applications?application_id=6', t_appid='SVCD006', application_description='Ride a Ostrich', lob='foo', created='1970-01-01', updated='1970-01-01', budget='budget$1')])

    >>> view = SLaterApplicationsListView(log, slack_client, applications = applications, callback_id = 'SLaterApplicationsListViewTest', title = 'SLater Edit - Joust', close = 'Cancel')
    >>> view.get('title')
    {'type': 'plain_text', 'text': 'SLater Edit - Joust'}

    >>> view.Blocks()[4]
    {'type': 'section', 'block_id': 'slater_application_action-6', 'text': {'type': 'mrkdwn', 'text': '*Joust* (SVCD006)\\nDescription: Ride a Ostrich'}, 'accessory': {'type': 'overflow', 'action_id': 'slater_application_action-6', 'options': [{'text': {'type': 'plain_text', 'emoji': True, 'text': ':pencil2: Edit'}, 'value': 'edit^6'}, {'text': {'type': 'plain_text', 'emoji': True, 'text': ':bust_in_silhouette: Add Contact'}, 'value': 'add_contact^6'}, {'text': {'type': 'plain_text', 'emoji': True, 'text': ':label: Custom Fields'}, 'value': 'get_custom_fields^6'}]}}

    Tear down testing environment
    >>> testing.unexport_secret_arns()
    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def __init__(self, log, slack_client, applications: Applications = [], callback_id: str = "SLaterApplicationsListView", title: str = "SLater", close: str = "Close", blocks: list = None, metadata=None, pages: list = [], request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": "*Applications*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": "help^contacts"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"GetApplications^next_token={inclusive_bound},limit={applications.limit}^GetSLaterApplicationsListView"
            })

        if len(applications) >= applications.limit and applications.next_token >= applications.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"GetApplications^next_token={applications.next_token},limit={applications.limit}^GetSLaterApplicationsListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for application in applications:
            log.debug(f"application: {application.to_json()}")
            app_name = application.get('application_name')
            app_num = application.get('application_id')
            app_desc = application.get('application_description')
            app_url = application.get('next_link')
            encoded_url = urlsafe_b64encode(
                compress(app_url.encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })
            self.add_block({
                "type": "section",
                "block_id": f"slater_application_action-{app_num}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{app_name}* ({application.get('t_appid')})\nDescription: {app_desc}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_application_action-{app_num}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{app_num}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":bust_in_silhouette: Add Contact"
                            },
                            "value": f"add_contact^{app_num}"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":label: Custom Fields"
                            },
                            "value": f"get_custom_fields^{app_num}"
                        }
                    ]
                }
            })
            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_application_detail-{app_num}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_application_detail-{app_num}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{app_num}^{encoded_url}"
                    }
                ]
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "divider"
            })
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterApplicationContactsListView(ModalView):
    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    def __init__(self, log, slack_client, data: ApplicationContacts = [], callback_id="", primary_key='application_contact_id', primary_title='application_contact_id', title="", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        object_type: str = data.GetObjectType()

        if not primary_key:
            primary_key = 'id'

        if not primary_title:
            primary_title = 'name'

        if not callback_id:
            callback_id = f"SLater{object_type}ListView"

        if not title:
            title = f"SLater {object_type} List"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": f"*{object_type}*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": f"help^{object_type.lower()}"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"Get{object_type}^next_token={inclusive_bound},limit={data.limit}^GetSLater{object_type}ListView"
            })

        if len(data) >= data.limit and data.next_token >= data.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"Get{object_type}^next_token={data.next_token},limit={data.limit}^GetSLater{object_type}ListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for item in data:
            item_object_type: str = item.GetObjectType(underscore=True)
            log.debug(f"data: {data.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(item.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{item.get(primary_title)}*: {item.get(primary_key)}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{item.get(primary_key)}"
                        }
                    ]
                }
            })

            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{item.get(primary_key)}^{encoded_url}"
                    }
                ]
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterBatchLoadsListView(ModalView):
    """ Validate we can create a batchloads list view

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> batch_loads = BatchLoads([BatchLoad(batch_load_id = 4, cannot_process = 1, s3_key = 'foo/applications.csv', errors = '*t_appid not found in applications table *master_payer_name not found in master_payers table', row_num= 1, entity_type= 'account_error', cloud_account_number= '0123456789', cloud_account_name= 'sega', cloud_account_root_email= 'sonic@root.net', master_payer_name= 'SLT BU Master Payer 4', intake_number= 'APIIntake54321', account_type_name= 'SLT BU Prod', governance_lob_name= 'lob a', create_date= '2020-04-30', budget_account_string= 'budget$3', budget_alert_monthly_threshold= 500, project_number= 'apiProjNum', snow_approval_group= 'API Test SNOW approval group', t_appid= 'SVCD002', tower_name= 'SLT Tower 2')])

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> view = SLaterBatchLoadsListView(log, slack_client, batch_loads=batch_loads)

    >>> view.data['blocks'][3]['block_id']
    'slater_bulk_import_control-4^'

    >>> len(view.data['blocks'])
    5

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    # TODO: finish changes from imports to errors
    def __init__(self, log, slack_client, batch_loads: BatchLoads = [], callback_id="SLaterBatchLoadListView", title="SLater Batch Loads", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": "*Batch Errors*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": "help^BatchLoads"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        for batch_load in batch_loads:
            log.debug(f"batch: {batch_load}")

            self.add_block({
                "type": "divider"
            })

            block = {
                'type': 'section',
                'block_id': f'slater_bulk_import_control-{batch_load.get("batch_load_id")}^',
                'text': {
                    'type': 'mrkdwn',
                    'text': f'*Batch ID:* {batch_load.get("batch_load_id")} - {basename(batch_load.get("s3_key"))}\n*Status:* {batch_load.get("status")} - *Errors:* {batch_load.get("num_errors")}'
                },
                'accessory': {
                    'type': 'button',
                    'text': {
                        'type': 'plain_text',
                        'text': 'Details',
                        'emoji': True
                    },
                    'value': f'00000000-0000-0000-0000-000000000000^{batch_load.get("next_link")}^{batch_load.get("batch_load_id")}',
                    'action_id': f'slater_bulk_import_errors-{batch_load.get("batch_load_id")}^'
                }
            }

            if batch_load.get("num_errors") == 0:
                del block['accessory']

            self.add_block(block)

        self.add_block({
            "type": "divider"
        })


@xray.capture()
class SLaterBatchErrorsListView(ModalView):
    """ Validate we can create an account modal view for edit

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    Validate retrieved account
    >>> batches = BatchErrors([BatchError(batch_load_id = 4, cannot_process = 1, s3_key = 'foo/applications.csv', errors = '*t_appid not found in applications table *master_payer_name not found in master_payers table', row_num= 1, entity_type= 'account_error', cloud_account_number= '0123456789', cloud_account_name= 'sega', cloud_account_root_email= 'sonic@root.net', master_payer_name= 'SLT BU Master Payer 4', intake_number= 'APIIntake54321', account_type_name= 'SLT BU Prod', governance_lob_name= 'lob a', create_date= '2020-04-30', budget_account_string= 'budget$3', budget_alert_monthly_threshold= 500, project_number= 'apiProjNum', snow_approval_group= 'API Test SNOW approval group', t_appid= 'SVCD002', tower_name= 'SLT Tower 2')])

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> view = SLaterBatchErrorsListView(log, slack_client, batch_errors=batches)

    >>> view.data['blocks'][4]['block_id']
    'slater_batch_error_action-4-0'

    >>> len(view.data['blocks'])
    7

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    # TODO: finish changes from imports to errors
    def __init__(self, log, slack_client, batch_errors: BatchErrors = [], callback_id="SLaterBatchErrorsListView", title="SLater Batch Errors", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)
        control_center = {
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": "*Batch Errors*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": "help^BatchErrors"
                    },
                ]
            }
        }

        if batch_errors:
            control_center['accessory']['options'].append({
                "text": {
                    "type": "plain_text",
                    "emoji": True,
                    "text": ":outbox_tray: Export Errors (CSV)"
                },
                "value": f"export^GetBatchImportErrors-{batch_errors.batch_load_id}^csv"
            })

        self.add_block(control_center)

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"GetBatchImportErrors^batch_load_id={batch_errors.batch_load_id},next_token={inclusive_bound},limit={batch_errors.limit}^GetSLaterBatchErrorsListView"
            })

        if len(batch_errors) >= batch_errors.limit and batch_errors.next_token >= batch_errors.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"GetBatchImportErrors^batch_load_id={batch_errors.batch_load_id},next_token={batch_errors.next_token},limit={batch_errors.limit}^GetSLaterBatchErrorsListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for position, batch in enumerate(batch_errors):
            log.debug(f"batch: {batch}")

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_batch_error_action-{batch.get('batch_load_id')}-{position}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{batch.get('entity_type')}*: {batch.get('batch_load_id')}\n*Cannot Process*: {batch.get('cannot_process')}\n*Message:* {batch.get('errors')}\n*Row Number:* {batch.get('row_num')}\n*Entity: {batch.get('entity')}"
                }
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterBusinessUnitsListView(ModalView):
    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    def __init__(self, log, slack_client, data: BusinessUnits = [], callback_id="", primary_key='business_unit_id', primary_title='business_unit_name', title="SLater BUs List", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        """
        >>> os.environ['STACK_NAME'] = 'iobots-stack'

        >>> testing.export_secret_arns(os.environ['STACK_NAME'])

        ** Create a slack client object **
        >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

        >>> data = BusinessUnits([BusinessUnit(**{'business_unit_name': 'Assessments', 'business_unit_id': '1', 'next_link': 'https://localhost/businessunits?business_unit_id=1', 'create_date': '1970-01-01', 'update_date': '1970-01-01'}),BusinessUnit(**{'business_unit_name': 'HED', 'business_unit_id': '2', 'next_link': 'https://localhost/businessunits?business_unit_id=2', 'create_date': '1970-01-01', 'update_date': '1970-01-01'})]) # noqa E501

        >>> view = SLaterBusinessUnitsListView(log, slack_client, data = data, callback_id = 'SLaterBusinessUnitsListViewTest', title = 'SLater BU List', close = 'Cancel')

        >>> view.get('title')
        {'type': 'plain_text', 'text': 'SLater BU List'}

        >>> view.Blocks()[4]
        {'type': 'section', 'block_id': 'slater_business_unit_action-1', 'text': {'type': 'mrkdwn', 'text': '*Assessments*: 1'}, 'accessory': {'type': 'overflow', 'action_id': 'slater_business_unit_action-1', 'options': [{'text': {'type': 'plain_text', 'emoji': True, 'text': ':pencil2: Edit'}, 'value': 'edit^1'}]}}

        Tear down testing environment
        >>> testing.unexport_secret_arns()

        >>> del os.environ['STACK_NAME']
        """
        object_type_lower: str = data.GetObjectType(underscore=True)
        object_type_camel: str = data.GetObjectType(underscore=False)

        if not primary_key:
            primary_key = 'id'

        if not primary_title:
            primary_title = 'name'

        if not callback_id:
            callback_id = f"SLater{object_type_camel}ListView"

        if not title:
            title = f"SLater {object_type_camel} List"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": f"*{object_type_camel}*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": f"help^{object_type_lower}"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"Get{object_type_camel}^next_token={inclusive_bound},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(data) >= data.limit and data.next_token >= data.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"Get{object_type_camel}^next_token={data.next_token},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for item in data:
            item_object_type: str = item.GetObjectType(underscore=True)
            log.debug(f"data: {data.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(item.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{item.get(primary_title)}*: {item.get(primary_key)}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{item.get(primary_key)}"
                        }
                    ]
                }
            })

            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{item.get(primary_key)}^{encoded_url}"
                    }
                ]
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterContactsListView(ModalView):
    """
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    ** Create a slack client object **
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = Contacts([Contact(**{'contact_name': 'speed racer', 'contact_id': '1', 'next_link': 'https://localhost/contacts?contact_id=1', 'contact_email': 'speed.racer@saturday.org', 'create_date': '1970-01-01', 'update_date': '1970-01-01'})])

    >>> view = SLaterContactsListView(log, slack_client, contacts = data, callback_id = 'SLaterBusinessUnitsListViewTest', title = 'SLater Contacts List', close = 'Cancel')

    >>> view.get('title')
    {'type': 'plain_text', 'text': 'SLater Contacts List'}

    >>> view.Blocks()[4]
    {'type': 'section', 'block_id': 'slater_contact_action-1', 'text': {'type': 'mrkdwn', 'text': '*speed racer*: 1\\n*Email:* speed.racer@saturday.org'}, 'accessory': {'type': 'overflow', 'action_id': 'slater_contact_action-1', 'options': [{'text': {'type': 'plain_text', 'emoji': True, 'text': ':pencil2: Edit'}, 'value': 'edit^1'}]}}

    Tear down testing environment
    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    def __init__(self, log, slack_client, contacts: Contacts = [], callback_id="SLaterContactsListView", title="SLater Contacts List", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": "*Contacts*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": "help^contacts"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"GetContacts^next_token={inclusive_bound},limit={contacts.limit}^GetSLaterContactsListView"
            })

        if len(contacts) >= contacts.limit and contacts.next_token >= contacts.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"GetContacts^next_token={contacts.next_token},limit={contacts.limit}^GetSLaterContactsListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for contact in contacts:
            log.debug(f"contact: {contact.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(contact.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_contact_action-{contact.get('contact_id')}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{contact.get('contact_name')}*: {contact.get('contact_id')}\n*Email:* {contact.get('contact_email')}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_contact_action-{contact.get('contact_id')}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{contact.get('contact_id')}"
                        }
                    ]
                }
            })
            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_contact_detail-{contact.get('contact_id')}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_contact_detail-{contact.get('contact_id')}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{contact.get('contact_id')}^{encoded_url}"
                    }
                ]
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterContactTypesListView(ModalView):
    """
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    ** Create a slack client object **
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = ContactTypes([ContactType(contact_type='Duckie', contact_type_id='124', next_link='https://localhost/contacttypes?contact_type_id=124', create_date='2020-07-22', update_date='2020-07-23')])

    >>> view = SLaterContactTypesListView(log, slack_client, data = data, callback_id = 'SLaterContactTypesListViewTest', title = 'SLater ContactTypes List', close = 'Cancel')

    >>> view.get('title')
    {'type': 'plain_text', 'text': 'SLater ContactTypes List'}

    >>> view.Blocks()[4]
    {'type': 'section', 'block_id': 'slater_contact_type_action-124', 'text': {'type': 'mrkdwn', 'text': '*Duckie*: 124'}, 'accessory': {'type': 'overflow', 'action_id': 'slater_contact_type_action-124', 'options': [{'text': {'type': 'plain_text', 'emoji': True, 'text': ':pencil2: Edit'}, 'value': 'edit^124'}]}}

    Tear down testing environment
    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    def __init__(self, log, slack_client, data: ContactTypes = [], callback_id="", primary_key='contact_type_id', primary_title='contact_type', title="", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        object_type_lower: str = data.GetObjectType(underscore=True)
        object_type_camel: str = data.GetObjectType(underscore=False)

        if not primary_key:
            primary_key = 'id'

        if not primary_title:
            primary_title = 'name'

        if not callback_id:
            callback_id = f"SLater{object_type_camel}ListView"

        if not title:
            title = f"SLater {object_type_camel} List"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": f"*{object_type_camel}*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": f"help^{object_type_lower}"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"Get{object_type_camel}^next_token={inclusive_bound},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(data) >= data.limit and data.next_token >= data.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"Get{object_type_camel}^next_token={data.next_token},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for item in data:
            item_object_type: str = item.GetObjectType(underscore=True)
            log.debug(f"data: {data.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(item.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{item.get(primary_title)}*: {item.get(primary_key)}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{item.get(primary_key)}"
                        }
                    ]
                }
            })

            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{item.get(primary_key)}^{encoded_url}"
                    }
                ]
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterCustomFieldsListView(ModalView):
    """ Generate a SLater CustomFields List View

    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    >>> from slater import SLater

    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> slater = SLater(log,boto_session,http_session,config_file='environments/unit.ini')

    >>> custom_fields = slater.GetCustomFieldsByCloudAccountID(cloud_account_id=4, limit=2)

    >>> account = slater.GetAccountByName('atari')

    >>> request={'submitter':{'name':'john.torres'},'submitted':1595314865}

    >>> view = SLaterCustomFieldsListView(log, slack_client, custom_fields=custom_fields, account=account)

    >>> len(view.data['blocks'])
    7

    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, custom_fields: CustomFields = [], account: Account = None, application: Application = None, callback_id="SLaterCustomFieldsListView", title="SLater Custom Fields ", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        if account:
            obj_type, search_param = 'Account', 'cloud_account_id'
            search_value = account.get(search_param)
        elif application:
            obj_type, search_param = 'Application', 'application_id'
            search_value = application.get(search_param)

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"GetCustomFieldsBy{obj_type}ID^{search_param}={search_value},next_token={inclusive_bound},limit={custom_fields.limit}^SLaterCustomFieldsListView"
            })

        if len(custom_fields) >= custom_fields.limit and custom_fields.next_token >= custom_fields.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"GetCustomFieldsBy{obj_type}ID^{search_param}={search_value},next_token={custom_fields.next_token},limit={custom_fields.limit}^SLaterCustomFieldsListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for custom_field in custom_fields:
            log.debug(f"account: {custom_field.to_json()}")

            self.add_block({
                "type": "divider"
            })
            self.add_block(custom_field.to_slack_markdown_section(regex_field_filters=[
                'cloud_account_number',
                'cloud_account_name',
                't_appid',
                'application_name'
            ],
            field_sort_order=[
                'custom_field_name',
                'custom_field_value',
                'create_date',
                'date_last_update',
                'slater_update_user'
            ]
            ))
            
        if len(nav_blocks) > 0:
            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })

@xray.capture()
class SLaterLOBsListView(ModalView):
    """
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    ** Create a slack client object **
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> data = LOBs([LOB(governance_lob_name='speed racer', lob_id='1', next_link='https://localhost/lobs?lob_id=1', create_date='2020-07-22', update_date='2020-07-23')])

    >>> view = SLaterLOBsListView(log, slack_client, data = data, callback_id = 'SLaterLOBsListViewTest', title = 'SLater LOBs List', close = 'Cancel')

    >>> view.get('title')
    {'type': 'plain_text', 'text': 'SLater LOBs List'}

    >>> view.Blocks()[4]
    {'type': 'section', 'block_id': 'slater_lob_action-1', 'text': {'type': 'mrkdwn', 'text': '*speed racer*: 1'}, 'accessory': {'type': 'overflow', 'action_id': 'slater_lob_action-1', 'options': [{'text': {'type': 'plain_text', 'emoji': True, 'text': ':pencil2: Edit'}, 'value': 'edit^1'}]}}

    Tear down testing environment
    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """

    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    def __init__(self, log, slack_client, data: LOBs = [], callback_id="", primary_key='lob_id', primary_title='governance_lob_name', title="", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        object_type_camel: str = data.GetObjectType()
        object_type_lower: str = object_type_camel.lower()

        if not primary_key:
            primary_key = 'id'

        if not primary_title:
            primary_title = 'name'

        if not callback_id:
            callback_id = f"SLater{object_type_camel}ListView"

        if not title:
            title = f"SLater {object_type_camel} List"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": f"*{object_type_camel}*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": f"help^{object_type_lower}"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"Get{object_type_camel}^next_token={inclusive_bound},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(data) >= data.limit and data.next_token >= data.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"Get{object_type_camel}^next_token={data.next_token},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for item in data:
            item_object_type: str = item.GetObjectType().lower()
            log.debug(f"data: {data.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(item.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{item.get(primary_title)}*: {item.get(primary_key)}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{item.get(primary_key)}"
                        }
                    ]
                }
            })

            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{item.get(primary_key)}^{encoded_url}"
                    }
                ]
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterMasterPayersListView(ModalView):
    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    def __init__(self, log, slack_client, data: MasterPayers = [], callback_id="", primary_key='master_payer_id', primary_title='master_payer_name', title: str = None, close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        object_type_lower: str = data.GetObjectType(underscore=True)
        object_type_camel: str = data.GetObjectType(underscore=False)

        if not primary_key:
            primary_key = 'id'

        if not primary_title:
            primary_title = 'name'

        if not callback_id:
            callback_id = f"SLater{object_type_camel}ListView"

        if not title:
            title = f"SLater {object_type_camel} List"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": f"*{object_type_camel}*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": f"help^{object_type_lower}"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"Get{object_type_camel}^next_token={inclusive_bound},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(data) >= data.limit and data.next_token >= data.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"Get{object_type_camel}^next_token={data.next_token},limit={data.limit}^GetSLater{object_type_camel}ListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for item in data:
            item_object_type: str = item.GetObjectType()
            log.debug(f"data: {data.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(item.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{item.get(primary_title)}*: {item.get(primary_key)}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{item.get(primary_key)}"
                        }
                    ]
                }
            })

            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{item.get(primary_key)}^{encoded_url}"
                    }
                ]
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterTowersListView(ModalView):
    @xray.capture()
    def to_slack_message(self):
        return super().to_slack_message()

    @xray.capture()
    def __init__(self, log, slack_client, data: Towers = [], callback_id="", primary_key='tower_id', primary_title='tower_name', title="", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:

        object_type: str = data.GetObjectType()
        if not primary_key:
            primary_key = 'id'

        if not primary_title:
            primary_title = 'name'

        if not callback_id:
            callback_id = f"SLater{object_type}ListView"

        if not title:
            title = f"SLater {object_type} List"

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_block({
            "type": "section",
            "block_id": "slater_control_center",
            "text": {
                "type": "mrkdwn",
                "text": f"*{object_type}*"
            },
            "accessory": {
                "type": "overflow",
                "action_id": "slater_help_button",
                "options": [
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":question: Help"
                        },
                        "value": f"help^{object_type.lower()}"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (CSV)"
                        },
                        "value": "export^GetExportsAll^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export All Data (JSON)"
                        },
                        "value": "export^GetExportsAll^json"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Accounts Data (CSV)"
                        },
                        "value": "export^GetExportsAccounts^csv"
                    },
                    {
                        "text": {
                            "type": "plain_text",
                            "emoji": True,
                            "text": ":outbox_tray: Export Applications Data (CSV)"
                        },
                        "value": "export^GetExportsApplications^csv"
                    }
                ]
            }
        })

        self.add_block({
            "type": "divider"
        })

        nav_blocks: list = []
        if pages and len(pages) > 1:
            inclusive_bound = pages[-1]
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_previous^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Previous",
                    "emoji": True
                },
                "style": "danger",
                "value": f"Get{object_type}^next_token={inclusive_bound},limit={data.limit}^GetSLater{object_type}ListView"
            })

        if len(data) >= data.limit and data.next_token >= data.limit:
            nav_blocks.append({
                "type": "button",
                "action_id": f"slater_pagination_next^{','.join(pages)}",
                "text": {
                    "type": "plain_text",
                    "text": "Next",
                    "emoji": True
                },
                "style": "primary",
                "value": f"Get{object_type}^next_token={data.next_token},limit={data.limit}^GetSLater{object_type}ListView"
            })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-header",
                "elements": nav_blocks
            })

        for item in data:
            item_object_type: str = item.GetObjectType()
            log.debug(f"data: {data.to_json()}")
            encoded_url = urlsafe_b64encode(
                compress(item.get('next_link').encode('utf-8'))).decode('utf-8')

            self.add_block({
                "type": "divider"
            })

            self.add_block({
                "type": "section",
                "block_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{item.get(primary_title)}*: {item.get(primary_key)}"
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": f"slater_{item_object_type}_action-{item.get(primary_key)}",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":pencil2: Edit"
                            },
                            "value": f"edit^{item.get(primary_key)}"
                        }
                    ]
                }
            })

            self.add_block({
                "type": "actions",
                "block_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                "elements": [
                    {
                        "type": "button",
                        "action_id": f"slater_view_{item_object_type}_detail-{item.get(primary_key)}",
                        "text": {
                            "type": "plain_text",
                            "text": "View Details",
                            "emoji": True
                        },
                        "value": f"view^{item.get(primary_key)}^{encoded_url}"
                    }
                ]
            })

        self.add_block({
            "type": "divider"
        })

        if len(nav_blocks) > 0:
            self.add_block({
                "type": "actions",
                "block_id": "slater_pagination_control-footer",
                "elements": nav_blocks
            })


@xray.capture()
class SLaterAccountsSearchView(ModalView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterAccountsSearchView", title="SLater Accounts", close="Close", blocks=None, metadata=None, request=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": "slater_home_accounts_lookup",
                "text": {
                    "type": "mrkdwn",
                    "text": "Search by *Account _Name_* or *Account _ID_*"
                },
                "accessory": {
                    "action_id": "slater_selected_accounts",
                    "type": "external_select",
                    "placeholder": {
                        "type": "plain_text",
                        "text": "Search Accounts"
                    },
                    "min_query_length": 2
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "context",
                "elements": [{
                    "type": "mrkdwn",
                    "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                }]
            }
        ])


@xray.capture()
class SLaterAccountTypesSearchView(SLaterSearchView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterAccountTypesSearchView", title="SLater AccountTypes", close="Close", blocks=None, metadata=None, request: dict = None,
                 action_id="slater_selected_account_types", block_id="", search_info="Search by AccountType Name or AccountType ID", search_placeholder="Search AccountTypes") -> None:

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks,
                         metadata=metadata, action_id=action_id,
                         block_id=block_id, search_info=search_info,
                         search_placeholder=search_placeholder, request=request)


@xray.capture()
class SLaterApplicationsSearchView(ModalView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterApplicationsSearchView", title="SLater Apps", close="Close", blocks=None, metadata=None, request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": "slater_home_applications_lookup",
                "text": {
                    "type": "mrkdwn",
                    "text": "Search by *Application _Name_*, _ID_* or *_tAppID_*"
                },
                "accessory": {
                    "action_id": "slater_selected_applications",
                    "type": "external_select",
                    "placeholder": {
                        "type": "plain_text",
                        "text": "Search Applications"
                    },
                    "min_query_length": 2
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "context",
                "elements": [{
                    "type": "mrkdwn",
                    "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                }]
            }
        ])


@xray.capture()
class SLaterApplicationContactsSearchView(SLaterSearchView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterApplicationContactsSearchView", title="SLater App Contact", close="Close", blocks=None, metadata=None,
                 action_id="slater_selected_application_contacts", block_id="", search_info="Search by ApplicationContact Name or ApplicationContact ID", search_placeholder="Search ApplicationContacts", request: dict = None) -> None:

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks,
                         metadata=metadata, action_id=action_id,
                         block_id=block_id, search_info=search_info,
                         search_placeholder=search_placeholder, request=request)


@xray.capture()
class SLaterBusinessUnitsSearchView(SLaterSearchView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterBusinessUnitsSearchView", title="SLater BUs", close="Close", blocks=None, metadata=None, request: dict = None,
                 action_id="slater_selected_business_units", block_id="", search_info="Search by BusinessUnit Name or BusinessUnit ID", search_placeholder="Search BusinessUnits") -> None:

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks,
                         metadata=metadata, action_id=action_id,
                         block_id=block_id, search_info=search_info,
                         search_placeholder=search_placeholder, request=request)


@xray.capture()
class SLaterContactsSearchView(ModalView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterContactsSearchView", title="SLater Contacts", close="Close", blocks=None, metadata=None, request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": "slater_home_contacts_lookup",
                "text": {
                    "type": "mrkdwn",
                    "text": "Search by *Contact _Name_* or *Contact _Email_*"
                },
                "accessory": {
                    "action_id": "slater_selected_contacts",
                    "type": "external_select",
                    "placeholder": {
                        "type": "plain_text",
                        "text": "Search Contacts"
                    },
                    "min_query_length": 2
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "context",
                "elements": [{
                    "type": "mrkdwn",
                    "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                }]
            }
        ])


@xray.capture()
class SLaterContactTypesSearchView(SLaterSearchView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterContactTypesSearchView", title="SLater ContactTypes", close="Close", blocks=None, metadata=None, request: dict = None,
                 action_id="slater_selected_contact_types", block_id="", search_info="Search by ContactType Name or ContactType ID", search_placeholder="Search ContactTypes") -> None:

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks,
                         metadata=metadata, action_id=action_id,
                         block_id=block_id, search_info=search_info,
                         search_placeholder=search_placeholder, request=request)


@xray.capture()
class SLaterLOBsSearchView(SLaterSearchView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterLOBsSearchView", title="SLater LOBs", close="Close", blocks=None, metadata=None, request: dict = None,
                 action_id="slater_selected_lobs", block_id="", search_info="Search by LOB Name or LOB ID", search_placeholder="Search LOBs") -> None:

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks,
                         metadata=metadata, action_id=action_id,
                         block_id=block_id, search_info=search_info,
                         search_placeholder=search_placeholder, request=request)


@xray.capture()
class SLaterMasterPayersSearchView(SLaterSearchView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterMasterPayersSearchView", title="SLater Payers", close="Close", blocks=None, metadata=None, request: dict = None,
                 action_id="slater_selected_master_payers", block_id="", search_info="Search by MasterPayer Name or MasterPayer ID", search_placeholder="Search Master Payers") -> None:

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks,
                         metadata=metadata, action_id=action_id,
                         block_id=block_id, search_info=search_info,
                         search_placeholder=search_placeholder, request=request)


@xray.capture()
class SLaterRootContactsSearchView(ModalView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterRootContactsSearchView", title="SLater Root Contacts", close="Close", blocks=None, metadata=None, request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata)

        self.add_blocks([
            {
                "type": "section",
                "block_id": "slater_home_rootcontacts_lookup",
                "text": {
                    "type": "mrkdwn",
                    "text": "Search by *Root Account _Email_*"
                },
                "accessory": {
                    "action_id": "slater_selected_rootcontacts",
                    "type": "external_select",
                    "placeholder": {
                        "type": "plain_text",
                        "text": "Search Root Contacts"
                    },
                    "min_query_length": 2
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "context",
                "elements": [{
                    "type": "mrkdwn",
                    "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                }]
            }
        ])


@xray.capture()
class SLaterTowersSearchView(SLaterSearchView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="SLaterTowersSearchView", title="SLater Towers", close="Close", blocks=None, metadata=None, request: dict = None,
                 action_id="slater_selected_towers", block_id="", search_info="Search by Tower Name or Tower ID", search_placeholder="Search Towers") -> None:

        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks,
                         metadata=metadata, action_id=action_id,
                         block_id=block_id, search_info=search_info,
                         search_placeholder=search_placeholder, request=request)


@xray.capture()
class SLaterSlashCommandHelpView(ModalView):
    """
    >>> os.environ['STACK_NAME'] = 'iobots-stack'

    >>> testing.export_secret_arns(os.environ['STACK_NAME'])

    ** Create a slack client object **
    >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

    >>> view = SLaterSlashCommandHelpView(log, slack_client,  callback_id = 'SLaterLOBsListViewTest', title = 'SLater Help', close = 'Close')

    >>> view.get('title')
    {'type': 'plain_text', 'text': 'SLater Help'}

    >>> view.Blocks()[4]
    {'type': 'header', 'text': {'type': 'plain_text', 'text': 'Applications', 'emoji': True}}

    >>> view = SLaterSlashCommandHelpView(log, slack_client,  callback_id = 'SLaterLOBsListViewTest', title = 'SLater Help', close = 'Close', error_message='Invalid command "foobar"')

    >>> view.Blocks()[1]
    {'type': 'section', 'text': {'type': 'mrkdwn', 'text': 'Invalid command "foobar"'}}

    Tear down testing environment
    >>> testing.unexport_secret_arns()

    >>> del os.environ['STACK_NAME']
    """
    @xray.capture()
    def __init__(self, log, slack_client, data: Dict[str, str] = None, callback_id="SLaterSlashCommandHelpView", title="SLater Help", close="Close", blocks=None, metadata=None, error_message="", request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        if not data:
            data = {}

        self.add_blocks([
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "Accounts",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater accounts\n  -h, --help\n\tshow this help message and exit\n\n  -n aiprimary, --account-name aiprimary\n\tname of cloud account\n\n  -i 0123456789012, --account-id 0123456789012\n\tid of cloud account\n\n  -p 0123456789012, --payerid 0123456789012\n\tfilter accounts by payer id\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "AccountTypes",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater account-types\n  -h, --help\n\tshow this help message and exit\n\n  -n production, --account-type-name production\n\tname of account type\n\n  -i 123, --account-type-id 123\n\tid of account type\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "Applications",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater applications\n  -h, --help\n\tshow this help message and exit\n\n  -n slater, --application-name slater\n\tname of application\n\n  -i 123, --application-id 123\n\tid of application\n\n  -t SVC00284, --application-tapp-id SVC00284\n\tt_appid of application\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "ApplicationContacts",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater application-contacts\n  -h, --help\n\tshow this help message and exit\n\n  -n VP, --application-contact VP\n\tname of application contact\n\n  -i 123, --application-contact-id 123\n\tid of application contact type\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "BusinessUnits",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater business-units\n  -h, --help\n\tshow this help message and exit\n\n  -n BU 1, --business-unit-name BU 1\n\tname of business unit\n\n  -i 123, --business-unit-id 123\n\tid of business unit\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "Contacts",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater contacts\n  -h, --help\n\tshow this help message and exit\n\n  -n AC Slater, --contact-name AC Slater\n\tname of contact\n\n  -e a.b@email.com, --contact-name a.b@email.com\n\temail of contact\n\n  -i 123, --contact-id 123\n\tid of contact\n\n  -t owner, --contact-type owner\n\tcontact type name\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "ContactTypes",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater contact-types\n  -h, --help\n\tshow this help message and exit\n\n  -n VP, --contact-type VP\n\tname of contact type\n\n  -i 123, --contact-type-id 123\n\tid of contact type\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "LOBs",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater lobs\n  -h, --help\n\tshow this help message and exit\n\n  -n lob 1, --governance-lob-name lob 1\n\tname of line of business\n\n  -i 123, --lob-id 123\n\tid of lob\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "MasterPayers",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater master-payers\n  -h, --help\n\tshow this help message and exit\n\n  -n CIO, --master-payer-name CIO\n\tname of payer account\n\n  -i 123, --payer-id 123\n\tid of payer\n\n  -I 123456789012, --master-payer-ident 123456789012\n\tcloud account id of payer\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "Towers",
                    "emoji": True
                }
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "```usage:\n\n/slater towers\n  -h, --help\n\tshow this help message and exit\n\n  -n T2, --tower-name T2\n\tname of tower\n\n  -i 123, --tower-id 123\n\tid of tower\n\n  -c, --create\n\tcreate new entry\n\n  -l, --list\n\tlist all values\n\n  -u, --update\n\tupdate the specified entry, used with -i or -n\n\n  -f, --fuzzy\n\tfuzzy search the specified entry, used with -i or -n\n\n  -v, --message-view\n\treturn message view instead of pop-up modal view\n\n```"
                }
            },
            {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                    }
                ]
            }
        ])

        if error_message:
            self.data['blocks'].insert(0, {
                "type": "divider"
            })

            self.data['blocks'].insert(0, {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": pad_if_blank_or_none(error_message)
                }
            })
            self.data['blocks'].insert(0, 		{
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "Error Message:",
                    "emoji": True
                }
            })


@xray.capture()
class SLaterSlashCommandNoResultsView(ModalView):
    @xray.capture()
    def __init__(self, log, slack_client, data: str = "No results for your query, please try again...", callback_id="SLaterSlashCommandNoResultsView", title="SLater No Results...", close="Close", blocks=None, metadata=None, request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "section",
                "block_id": "slater_help",
                "text": {
                    "type": "mrkdwn",
                    "text": data
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "context",
                "elements": [{
                    "type": "mrkdwn",
                    "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                }]
            }
        ])


@xray.capture()
class SLaterSlashCommandErrorView(ModalView):
    @xray.capture()
    def __init__(self, log, slack_client, data: str = "We've encountered an error....", callback_id="SLaterSlashCommandErrorView", title="SLater Error", close="Close", blocks=None, metadata=None, request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)

        self.add_blocks([
            {
                "type": "image",
                "title": {
                    "type": "plain_text",
                    "text": "Uh-oh! Something went awry.",
                    "emoji": True
                },
                "image_url": "https://downloads.pearsoncloudmanagement.com/slater/dev/static/images/sad_panda_error_small.png",
                "alt_text": "Uh-oh! Something went awry."
            },
            {
                "type": "section",
                "block_id": "slater_error",
                "text": {
                    "type": "mrkdwn",
                    "text": data
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "context",
                "elements": [{
                    "type": "mrkdwn",
                    "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                }]
            }
        ])


@xray.capture()
class SLaterHome(HomePageView):
    @xray.capture()
    def __init__(self, log, slack_client, callback_id="", title="", blocks=None):
        """
        >>> os.environ['STACK_NAME'] = 'iobots-stack'

        >>> testing.export_secret_arns(os.environ['STACK_NAME'])

        ** Create a slack client object **
        >>> slack_client = slack_sdk.WebClient(token=testing.slack_bearer_token())

        >>> data = BusinessUnits([BusinessUnit(**{'business_unit_name': 'Assessments', 'business_unit_id': '1', 'next_link': 'https://localhost/businessunits?business_unit_id=1', 'create_date': '1970-01-01', 'update_date': '1970-01-01'}),BusinessUnit(**{'business_unit_name': 'HED', 'business_unit_id': '2', 'next_link': 'https://localhost/businessunits?business_unit_id=2', 'create_date': '1970-01-01', 'update_date': '1970-01-01'})]) # noqa E501

        >>> view = SLaterHome(log, slack_client)

        >>> len(view.data['blocks'])
        8

        Tear down testing environment
        >>> testing.unexport_secret_arns()

        >>> del os.environ['STACK_NAME']
        """
        super().__init__(log, slack_client, callback_id=callback_id, title=title, blocks=[
            {
                "type": "section",
                "block_id": "slater_control_center",
                "text": {
                    "type": "mrkdwn",
                    "text": " "
                },
                "accessory": {
                    "type": "overflow",
                    "action_id": "slater_help_button",
                    "options": [
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":question: Help"
                            },
                            "value": "help^accounts"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":outbox_tray: Export All Data (CSV)"
                            },
                            "value": "export^GetExportsAll^csv"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":outbox_tray: Export All Data (JSON)"
                            },
                            "value": "export^GetExportsAll^json"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":outbox_tray: Export Accounts Data (CSV)"
                            },
                            "value": "export^GetExportsAccounts^csv"
                        },
                        {
                            "text": {
                                "type": "plain_text",
                                "emoji": True,
                                "text": ":outbox_tray: Export Applications Data (CSV)"
                            },
                            "value": "export^GetExportsApplications^csv"
                        }
                    ]
                }
            },
            {
                "type": "image",
                "title": {
                    "type": "plain_text",
                    "text": "SLater - Service Registry",
                    "emoji": True
                },
                "image_url": "https://downloads.pearsoncloudmanagement.com/slater/dev/static/images/friendly_slater_128.png",
                "alt_text": "SLater"
            },
            {
                "type": "divider"
            },
            {
                "type": "section",
                "block_id": "slater_home_accounts_lookup",
                "text": {
                    "type": "mrkdwn",
                    "text": "Search by *Account _Name_* or *Account _ID_*"
                },
                "accessory": {
                    "action_id": "slater_selected_accounts",
                    "type": "external_select",
                    "placeholder": {
                        "type": "plain_text",
                        "text": "Search Accounts"
                    },
                    "min_query_length": 2
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "section",
                "block_id": "slater_home_applications_lookup",
                "text": {
                    "type": "mrkdwn",
                    "text": "Search by *Application _Name_*, _ID_* or *_tAppID_*"
                },
                "accessory": {
                    "action_id": "slater_selected_applications",
                    "type": "external_select",
                    "placeholder": {
                        "type": "plain_text",
                        "text": "Search Applications"
                    },
                    "min_query_length": 2
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "context",
                "elements": [{
                    "type": "mrkdwn",
                    "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                }]
            }
        ])

    @xray.capture()
    def Blocks(self) -> list:
        return self.data['blocks']


class SLaterSREsListView(ModalView):
    def __init__(self, log, slack_client, sres: SREs = [], callback_id="SLaterSREsListView", title="SLater SREs List", close="Close", blocks=None, metadata=None, pages: list = [], request: dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)


class SLaterSREEditView(ModalView):
    def __init__(self, log, slack_client, sre: SRE = None, callback_id="SLaterSREEditView", title="SLater - Edit", close="Cancel", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)


class SLaterSREDetailView(ModalView):
    def __init__(self, log, slack_client, sre: SRE = None, callback_id="SLaterSREDetailView", title="SLater", close="Close", blocks=None, request: Dict = None, metadata=None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks, metadata=metadata, request=request)


@xray.capture()
class SLaterAddContactView(ModalView):
    @xray.capture()
    def __init__(self, log, slack_client, application: Application = None, account: Account = None, contacts: Contacts = None, callback_id="SLaterAddContactView", title="SLater", close="Close", blocks=None, request: Dict = None) -> None:
        super().__init__(log, slack_client, callback_id=callback_id,
                         title=title, close=close, blocks=blocks)

        self.add_submit_button(title='Add Contact')
        if not blocks:
            if application:
                self.add_blocks([
                    {
                        "type": "header",
                        "block_id": f"slater_selected_application^{application.get('application_id')}",
                        "text": {
                            "type": "plain_text",
                            "text": f"{application.get('application_name').title()} ({application.get('t_appid')})",
                            "emoji": True
                        }
                    },
                    {
                        "type": "input",
                        "block_id": "slater_selected_contacts",
                        "label": {
                            "type": "plain_text",
                            "text": "Contact Name"
                        },
                        "element": {
                            "action_id": "slater_selected_contacts",
                            "type": "external_select",
                            "placeholder": {
                                "type": "plain_text",
                                "text": "Add a contact by name"
                            },
                            "min_query_length": 2,
                        },
                    },
                    {
                        "type": "input",
                        "block_id": "slater_selected_contact_types",
                        "label": {
                            "type": "plain_text",
                            "text": "Contact Type"
                        },
                        "element": {
                            "action_id": "slater_selected_contact_types",
                            "type": "external_select",
                            "placeholder": {
                                "type": "plain_text",
                                "text": "Select a contact type (e.x \"Primary Business Contact\")"
                            },
                            "min_query_length": 2,
                        }
                    },
                    {
                        "type": "divider"
                    }
                ])
            elif account:
                self.add_blocks([
                    {
                        "type": "header",
                        "block_id": f"slater_selected_account^{account.get('cloud_account_id')}",
                        "text": {
                            "type": "plain_text",
                            "text": f"{account.get('cloud_account_name').title()} ({account.get('cloud_account_number')})",
                            "emoji": True
                        }
                    },
                    {
                        "type": "input",
                        "block_id": "slater_selected_contacts",
                        "label": {
                            "type": "plain_text",
                            "text": "Contact Name"
                        },
                        "element": {
                            "action_id": "slater_selected_contacts",
                            "type": "external_select",
                            "placeholder": {
                                "type": "plain_text",
                                "text": "Add a contact by name"
                            },
                            "min_query_length": 2,
                        },
                    },
                    {
                        "type": "input",
                        "block_id": "slater_selected_contact_types",
                        "label": {
                            "type": "plain_text",
                            "text": "Contact Type"
                        },
                        "element": {
                            "action_id": "slater_selected_contact_types",
                            "type": "external_select",
                            "placeholder": {
                                "type": "plain_text",
                                "text": "Select a contact type (e.x \"Primary Business Contact\")"
                            },
                            "min_query_length": 2,
                        }
                    },
                    {
                        "type": "divider"
                    }
                ])
            else:
                self.add_blocks([
                    {
                        "type": "divider"
                    },
                    {
                        "type": "input",
                        "block_id": "contact_name",
                        "element": {
                            "type": "plain_text_input",
                            "action_id": "contact_name"
                        },
                        "label": {
                            "type": "plain_text",
                            "text": "Name",
                            "emoji": True
                        }
                    },
                    {
                        "type": "input",
                        "block_id": "servicenow_selected_emails",
                        "element": {
                            "action_id": "servicenow_selected_emails",
                            "type": "multi_external_select",
                            "placeholder": {
                                "type": "plain_text",
                                "text": "Import Contact"
                            },
                            "min_query_length": 2,
                            "max_selected_items": 1
                        },
                        "label": {
                            "type": "plain_text",
                            "text": "Search Pearson Email",
                            "emoji": True
                        }
                    },
                    {
                        "type": "divider"
                    },
                    {
                        "type": "context",
                        "elements": [{
                            "type": "mrkdwn",
                            "text": "*SLater* all rights reserved <https://pearson.com|:pearson: Pearson>"
                        }]
                    }
                ])

            valid_contacts: Contacts = Contacts([])
            for contact in contacts:

                if not contact:
                    log.warning('null contact...')
                    continue

                if not contact.get('contact_type'):
                    log.warning(f'broken contact_type on contact: {contact}')
                    continue  # broken contact-type

                valid_contacts.append(contact)

                if valid_contacts:
                    if len(valid_contacts) > 0:
                        self.add_block(
                            {
                                "type": "section",
                                "text": {
                                    "type": "mrkdwn",
                                    "text": f"*Contact:* {contact.get('contact_type', '').title()}\n*Contact Name:* {contact.get('contact_name', '').title()}\n*Contact ID:* {contact.get('contact_id')}"
                                }
                            }
                        )

            if request:
                self.data['blocks'].insert(0, {
                    "type": "context",
                    "elements": [
                        {
                            "type": "mrkdwn",
                            "text": f":pearson: _Submitted by @{request['submitter']['name']}_ on _{datetime.datetime.utcfromtimestamp(request['submitted']).date()}_"
                        }
                    ]
                })
