class ConversationDefinitionBase(object):
    """Definition of conversation lifecycle and possible actions.

    NOTE: This is a work in progress. The idea is that we can build a
    completely generic conversation UI framework that uses this definition to
    present all available functionality for any given conversation.
    """

    conversation_type = None
    conversation_display_name = 'Conversation'

    extra_static_endpoints = ()

    actions = ()

    # set to an sub-class of go.api.go_api.action_dispatcher
    # .ConversationActionDispatcher to provide API methods
    api_dispatcher_cls = None

    def __init__(self, conv=None):
        self.conv = conv

    def get_actions(self):
        return [action(self.conv) for action in self.actions]

    def configured_endpoints(self, config):
        return []

    def is_config_valid(self):
        raise NotImplementedError()


class ConversationAction(object):
    """Definition of an action that can be performed on a conversation.

    This includes things like "send a bulk message", "display JS app logs" and
    "send initial survey questions".
    """

    action_name = None
    # title to display for action
    action_display_name = None
    # verb phrase describing the action in second person
    action_display_verb = None
    needs_confirmation = False

    # Some actions are only possible under certain conditions.
    needs_group = False
    needs_running = False

    redirect_to = None

    def __init__(self, conv):
        self._conv = conv

    def perform_action(self, action_data):
        """Perform whatever operations are necessary for this action."""
        pass

    def send_command(self, command_name, **params):
        return self._conv.dispatch_command(
            command_name,
            user_account_key=self._conv.user_account.key,
            conversation_key=self._conv.key,
            **params)

    def is_disabled(self):
        """Returns `None` if the action is enabled, otherwise a reason string.
        """
        if self.needs_group and not self._conv.groups.keys():
            return "This action needs a contact group."

        if self.needs_running and not self._conv.running():
            return "This action needs a running conversation."

        return self.check_disabled()

    def check_disabled(self):
        """Override in subclasses to provide custom disable logic."""
        return None

from go.vumitools.metrics import MessagesSentMetric, MessagesReceivedMetric


class ConversationDefinitionBase(object):
    """Definition of conversation lifecycle and possible actions.

    NOTE: This is a work in progress. The idea is that we can build a
    completely generic conversation UI framework that uses this definition to
    present all available functionality for any given conversation.
    """

    conversation_type = None
    conversation_display_name = 'Conversation'

    extra_static_endpoints = ()

    actions = ()

    metrics = (
        MessagesSentMetric,
        MessagesReceivedMetric)

    # set to an sub-class of go.api.go_api.action_dispatcher
    # .ConversationActionDispatcher to provide API methods
    api_dispatcher_cls = None

    def __init__(self, conv=None):
        self.conv = conv

    def get_actions(self):
        return [action(self.conv) for action in self.actions]

    def get_metrics(self, vumi_api):
        return [metric(self.conv, vumi_api) for metric in self.metrics]

    def configured_endpoints(self, config):
        return []

    def is_config_valid(self):
        raise NotImplementedError()


class ConversationAction(object):
    """Definition of an action that can be performed on a conversation.

    This includes things like "send a bulk message", "display JS app logs" and
    "send initial survey questions".
    """

    action_name = None
    # title to display for action
    action_display_name = None
    # verb phrase describing the action in second person
    action_display_verb = None
    needs_confirmation = False

    # Some actions are only possible under certain conditions.
    needs_group = False
    needs_running = False

    redirect_to = None

    def __init__(self, conv):
        self._conv = conv

    def perform_action(self, action_data):
        """Perform whatever operations are necessary for this action."""
        pass

    def send_command(self, command_name, **params):
        return self._conv.dispatch_command(
            command_name,
            user_account_key=self._conv.user_account.key,
            conversation_key=self._conv.key,
            **params)

    def is_disabled(self):
        """Returns `None` if the action is enabled, otherwise a reason string.
        """
        if self.needs_group and not self._conv.groups.keys():
            return "This action needs a contact group."

        if self.needs_running and not self._conv.running():
            return "This action needs a running conversation."

        return self.check_disabled()

    def check_disabled(self):
        """Override in subclasses to provide custom disable logic."""
        return None

Allow conversation metrics to be specified as part of the conversation definition