Esempio n. 1
0
File: bots.py Progetto: aitjcize/bb8
def create_bot():
    """Create a new bot."""
    data = request.json
    try:
        jsonschema.validate(data, BOT_CREATE_SCHEMA)
    except Exception:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_FORM_VALIDATION,
                       'schema validation fail')

    bot = Bot(**data).add()
    g.account.bots.append(bot)
    DatabaseManager.commit()
    return jsonify(bot.to_json(bot.detail_fields))
Esempio n. 2
0
def broadcast_task(broadcast_id):
    with DatabaseSession():
        broadcast = Broadcast.get_by(id=broadcast_id, single=True)
        # The broadcast entry could be delted by user
        if not broadcast:
            return

        # Either broadcast in progress or already sent
        if broadcast.status != BroadcastStatusEnum.QUEUED:
            return

        # The user may have changed the broadcasting time, ignore it as it
        # should be automatically rescheduled.
        if broadcast.scheduled_time >= datetime.now():
            return

        broadcast.status = BroadcastStatusEnum.SENDING
        try:
            DatabaseManager.commit()
        except (InvalidRequestError, IntegrityError):
            # The broadcast task have changed during our modification, retry.
            DatabaseManager.rollback()
            return broadcast_task(broadcast_id)

        # Do the actually broadcast
        bot = Bot.get_by(id=broadcast.bot_id,
                         account_id=broadcast.account_id,
                         single=True)

        # Bot may have been deleted
        if not bot:
            return

        messages = [Message.FromDict(m) for m in broadcast.messages]
        broadcast_message_async(bot, messages)
Esempio n. 3
0
    def setup_prerequisite(self):
        DatabaseManager.reset()

        self.bot = Bot(name=u'test', description=u'test',
                       interaction_timeout=120, session_timeout=86400).add()
        DatabaseManager.commit()

        config = {
            'access_token': 'EAAP0okfsZCVkBAI3BCU5s3u8O0iVFh6NAwFHa7X2bKZCGQ'
                            'Lw6VYeTpeTsW5WODeDbekU3ZA0JyVCBSmXq8EqwL1GDuZBO'
                            '7aAlcNEHQ3AZBIx0ZBfFLh95TlJWlLrYetzm9owKNR8Qju8'
                            'HF6qra20ZC6HqNXwGpaP74knlNvQJqUmwZDZD'
        }
        platform = Platform(name=u'Test Platform',
                            bot_id=self.bot.id,
                            type_enum=PlatformTypeEnum.Facebook,
                            provider_ident='1155924351125985',
                            config=config).add()
        DatabaseManager.commit()

        self.user = User(platform_id=platform.id,
                         platform_user_ident='1153206858057166',
                         last_seen=datetime.datetime.now()).add()

        DatabaseManager.commit()
Esempio n. 4
0
File: misc.py Progetto: aitjcize/bb8
def redirect_track_url(bot_id, platform_user_ident):
    """Handler for tracking URL

    This handler allow us to track 'web_url' button clicks by embedding the
    tracking info in the url parameters and sending to GA.
    """
    url = request.args.get('url', None)
    if not url:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM,
                       'No URL specified')

    path = request.args.get('path', None)
    if not path:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM,
                       'No path specified')

    try:
        ret = Bot.query(Bot.ga_id).filter_by(id=bot_id).one()
    except Exception:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM,
                       'Internal Error')

    g.ga_id = ret[0]
    track(TrackingInfo.Pageview(platform_user_ident, '/Redirect/%s' % path))

    return redirect(url)
Esempio n. 5
0
    def setup_prerequisite(self):
        DatabaseManager.reset()

        self.bot = Bot(name=u'test',
                       description=u'test',
                       interaction_timeout=120,
                       session_timeout=86400).add()
        DatabaseManager.commit()

        config = {
            'access_token':
            'iHRMgmp3zRLOc6kPCbPNMwEDHyFqLGSy0tyG3uZxnkNlhMKg'
            '8GVFqMGslcOkmgOAFLlBvvYuXmKF9odhXtsCm3tBxRcPryKr'
            'kOvzHBcBvS2zrVGiVmZGh5EBcqazgurYMwVSdgNSrhCm/qp6'
            '2aR7HAdB04t89/1O/w1cDnyilFU=',
            'channel_secret':
            '335c901df3a1969ca28a48bf6ddcc333'
        }
        platform = Platform(bot_id=self.bot.id,
                            name=u'Line',
                            type_enum=PlatformTypeEnum.Line,
                            provider_ident='aitjcize.line',
                            config=config).add()
        DatabaseManager.commit()

        self.user = User(
            platform_id=platform.id,
            platform_user_ident='U7200f33369e7e586c973c3a9df8feee4',
            last_seen=datetime.datetime.now()).add()

        DatabaseManager.commit()
Esempio n. 6
0
File: bots.py Progetto: aitjcize/bb8
def get_account_bot_by_id(bot_id):
    bot = Bot.get_by(id=bot_id, account_id=g.account.id, single=True)
    if bot is None:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_NOT_FOUND,
                       'bot_id: %d not found' % bot_id)
    return bot
Esempio n. 7
0
def parse_platform(platform_json, to_platform_id=None, source='platform_json'):
    """Parse Platform from platform definition.

    If *to_platform_id* is specified, update existing platform specified by
    *to_platform_id* instead of creating a new platform.

    If *to_platform_id* is a callable. The result of the call is used as the
    platform_id.
    """
    validate_platform_schema(platform_json)

    # Validate plaform-specific schema
    provider = get_messaging_provider(
        PlatformTypeEnum(platform_json['type_enum']))
    try:
        jsonschema.validate(platform_json['config'],
                            provider.get_config_schema())
    except jsonschema.exceptions.ValidationError:
        logger.error('Platform config validate failed for `%s\'!', source)
        raise

    if callable(to_platform_id):
        to_platform_id = to_platform_id(platform_json)

    bot_id = platform_json.get('bot_id')
    if bot_id:
        bot = Bot.get_by(id=bot_id,
                         account_id=platform_json['account_id'],
                         single=True)
        if not bot:
            raise RuntimeError('bot does not exist')

    if to_platform_id:
        # Update existing platform.
        logger.info(u'Updating existing Platform(id=%d, name=%s) from %s ...',
                    to_platform_id, platform_json['name'], source)
        platform = Platform.get_by(id=to_platform_id, single=True)
        platform.bot_id = bot_id
        platform.name = platform_json['name']
        platform.deployed = platform_json['deployed']
        platform.type_enum = platform_json['type_enum']
        platform.provider_ident = platform_json['provider_ident']
        platform.config = platform_json['config']
    else:
        # Create a new platform.
        logger.info(u'Creating new Platform(name=%s) from %s ...',
                    platform_json['name'], source)
        platform = Platform(**platform_json).add()

    DatabaseManager.flush()
    return platform
Esempio n. 8
0
    def Broadcast(self, request, unused_context):
        with DatabaseSession():
            bot = Bot.get_by(id=request.bot_id, single=True)
            if not bot:
                raise RuntimeError('Bot<%d> does not exist' % request.bot_id)

            eta = None if request.eta == 0 else request.eta
            messages_dict = cPickle.loads(request.messages_object)
            if request.static:
                msgs = [Message.FromDict(m, {}) for m in messages_dict]
                messaging_tasks.broadcast_message_async(bot, msgs, eta)
            else:
                users = User.get_by(bot_id=request.bot_id)
                messaging_tasks.push_message_from_dict_async(
                    users, messages_dict, eta)

        return app_service_pb2.Empty()
Esempio n. 9
0
def parse_broadcast(broadcast_json, to_broadcast_id=None):
    """Parse Broadcast from broadcast definition."""
    validate_broadcast_schema(broadcast_json)

    if callable(to_broadcast_id):
        to_broadcast_id = to_broadcast_id(broadcast_json)

    # Validate that the target bot is own by the same account.
    bot = Bot.get_by(id=broadcast_json['bot_id'],
                     account_id=broadcast_json['account_id'],
                     single=True)
    if not bot:
        raise RuntimeError('bot does not exist for broadcast')

    if to_broadcast_id:
        broadcast = Broadcast.get_by(id=to_broadcast_id, single=True)
        if broadcast.status != BroadcastStatusEnum.QUEUED:
            raise BroadcastUnmodifiableError

        # Update existing broadcast.
        logger.info(u'Updating existing Broadcast(id=%d, name=%s) ...',
                    to_broadcast_id, broadcast_json['name'])
        broadcast = Broadcast.get_by(id=to_broadcast_id, single=True)
        broadcast.bot_id = broadcast_json['bot_id']
        broadcast.name = broadcast_json['name']
        broadcast.messages = broadcast_json['messages']
        broadcast.scheduled_time = datetime.utcfromtimestamp(
            broadcast_json['scheduled_time'])
        broadcast.status = broadcast_json.get('status', broadcast.status)
    else:
        # Create a new broadcast.
        logger.info(u'Creating new Broadcast(name=%s) ...',
                    broadcast_json['name'])
        broadcast_json['scheduled_time'] = datetime.utcfromtimestamp(
            broadcast_json['scheduled_time'])
        broadcast = Broadcast(**broadcast_json).add()

    DatabaseManager.commit()
    schedule_broadcast(broadcast)

    return broadcast
Esempio n. 10
0
    def test_Bot_API(self):
        """Test Bot model APIs."""
        DatabaseManager.reset()

        bots = reset_and_setup_bots(['test/simple.bot', 'test/postback.bot'])
        bot1 = bots[0]
        bot2 = bots[1]

        bot2_node_len = len(bot2.nodes)

        bot1.delete_all_nodes()
        DatabaseManager.commit()

        # All nodes and links related to this bot should be gone.
        self.assertEquals(bot1.nodes, [])

        # Make sure delete_all_nodes does not accidentally delete node
        # of other bot
        self.assertEquals(len(bot2.nodes), bot2_node_len)

        # Test bot reconstruction
        parse_bot_from_file(get_bot_filename('test/simple.bot'), bot1.id)
        DatabaseManager.commit()

        self.assertNotEquals(bot1.nodes, [])
        self.assertEquals(bot1.users, [])

        User(platform_id=bot1.platforms[0].id,
             platform_user_ident='blablabla',
             last_seen=datetime.datetime.now()).add()
        User(platform_id=bot1.platforms[1].id,
             platform_user_ident='blablabla2',
             last_seen=datetime.datetime.now()).add()
        DatabaseManager.commit()
        self.assertEquals(len(bot1.users), 2)

        bot1_id = bot1.id
        bot1.delete()
        DatabaseManager.commit()
        self.assertEquals(Bot.get_by(id=bot1_id, single=True), None)
Esempio n. 11
0
def parse_bot(bot_json, to_bot_id=None, source='bot_json'):
    """Parse Bot from bot definition.

    If *to_bot_id* is specified, update existing bot specified by *to_bot_id*
    instead of creating a new bot.

    If *to_bot_id* is a callable. The result of the call is used as the bot_id.
    """
    validate_bot_schema(bot_json, source=source)

    bot_desc = bot_json['bot']

    if callable(to_bot_id):
        to_bot_id = to_bot_id(bot_desc)

    if to_bot_id:
        # Update existing bot.
        logger.info(u'Updating existing Bot(id=%d, name=%s) from %s ...',
                    to_bot_id, bot_desc['name'], source)

        bot = Bot.get_by(id=to_bot_id, single=True)
        bot.delete_all_nodes()
        bot.name = bot_desc['name']
        bot.description = bot_desc['description']
        bot.interaction_timeout = bot_desc['interaction_timeout']
        bot.admin_interaction_timeout = bot_desc['admin_interaction_timeout']
        bot.session_timeout = bot_desc['session_timeout']
        bot.ga_id = bot_desc.get('ga_id', None)
        bot.settings = bot_desc['settings']
        DatabaseManager.flush()
    else:
        # Create a new bot
        logger.info(u'Creating new Bot(name=%s) from %s ...', bot_desc['name'],
                    source)
        bot = Bot(
            name=bot_desc['name'],
            description=bot_desc['description'],
            interaction_timeout=bot_desc['interaction_timeout'],
            admin_interaction_timeout=bot_desc['admin_interaction_timeout'],
            session_timeout=bot_desc['session_timeout'],
            ga_id=bot_desc.get('ga_id', None),
            settings=bot_desc['settings']).add()
        DatabaseManager.flush()

    # Bind Platform with Bot
    platforms = bot_json.get('platforms', {})
    for unused_name, provider_ident in platforms.iteritems():
        platform = Platform.get_by(provider_ident=provider_ident, single=True)
        if platform is None:
            raise RuntimeError('associated platform `%s\' does not exist',
                               provider_ident)

        # Bind
        platform.bot_id = bot.id

        provider = get_messaging_provider(platform.type_enum)
        if not platform.deployed or (config.DEPLOY and platform.deployed):
            provider.apply_settings(platform.config, bot.settings)

        DatabaseManager.flush()

    nodes = bot_desc['nodes']
    id_map = {}  # Mapping of stable_id to id

    # Build node
    for stable_id, node in nodes.iteritems():
        try:
            cm = ContentModule.get_by(id=node['content_module']['id'],
                                      single=True)
            if cm is None:
                raise RuntimeError('Content_module `%d\' does not exist',
                                   node['content_module']['id'])
            jsonschema.validate(node['content_module']['config'],
                                cm.get_module().schema())
        except jsonschema.exceptions.ValidationError:
            logger.error(
                'Node `%s\' content module config validation '
                'failed', stable_id)
            raise

        n = Node(stable_id=stable_id,
                 bot_id=bot.id,
                 name=unicode(node['name']),
                 description=unicode(node['description']),
                 expect_input=node['expect_input'],
                 content_module_id=node['content_module']['id'],
                 content_config=node['content_module']['config']).add()

        if 'parser_module' in node:
            n.parser_module_id = node['parser_module']['id']

        DatabaseManager.flush()
        id_map[stable_id] = n.id

    # Validate that parser module linkages are present in this bot file.
    for stable_id, node in nodes.iteritems():
        n = Node.get_by(id=id_map[stable_id], single=True)
        if n.parser_module is not None:
            nodes = bot_json['bot']['nodes']
            n.parser_config = node['parser_module']['config']
            pm = n.parser_module.get_module()
            try:
                jsonschema.validate(n.parser_config, pm.schema())
            except jsonschema.exceptions.ValidationError:
                logger.error(
                    'Node `%s\' parser module config validation '
                    'failed', stable_id)
                raise

            for end_node_id in pm.get_linkages(n.parser_config):
                if end_node_id is not None:
                    if re.search(HAS_VARIABLE_RE, end_node_id):
                        logger.info('Rendered end_node_id `%s\', check '
                                    'skipped ...' % end_node_id)
                        continue
                    if end_node_id not in id_map.keys():
                        raise RuntimeError('end_node_id `%s\' is invalid' %
                                           end_node_id)

    DatabaseManager.flush()
    return bot
Esempio n. 12
0
 def find_bot_by_name(bot_desc):
     bot = Bot.get_by(name=bot_desc['name'], single=True)
     if bot:
         return bot.id
     else:
         return None
Esempio n. 13
0
def run(parser_config, user_input, unused_as_root):
    """
    {
      "links": [
        {
          "action_ident": "continue",
          "end_node_id": null,
          "ack_message": ""
        },
        {
          "action_ident": "done",
          "end_node_id": "[[root]]",
          "ack_message": ""
        }
      ]
    }
    """
    if user_input.text:
        if u'重設' in user_input.text or 'reset' in user_input.text.lower():
            Memory.Clear()
            return ParseResult(ack_message=u'讓我們重新開始吧!')

    if user_input.event:
        event = user_input.event
        if event.key == 'CONTROL_FLOW':
            if event.value == 'reset':
                Memory.Clear()
                return ParseResult(parser_config['done'], ack_message=u'放棄操作')
        elif event.key == 'SELECT_BOT':
            Memory.Set('bot', event.value)
        elif event.key == 'SELECT_OP':
            Memory.Set('operation', event.value)
        elif event.key == 'MESSAGE_INPUT':
            if event.value == 'done':
                Memory.Set('status', 'preview_message')
            elif event.value == 'restart':
                Memory.Set('broadcast_message', None)
                Memory.Set('status', None)
        elif event.key == 'CONFIRM_MESSAGE':
            if event.value:
                bot = Bot.get_by(id=Memory.Get('bot'), single=True)
                if not bot:
                    return ParseResult(ack_message=u'發生錯誤,找不到對應的' u'機器人')
                msgs = [
                    Message.FromDict(raw_msg)
                    for raw_msg in Memory.Get('broadcast_message')
                ]
                BroadcastMessage(bot, msgs)
                Memory.Clear()
                return ParseResult(parser_config['done'],
                                   ack_message=u'您的訊息已送出!')
            else:
                Memory.Set('broadcast_message', None)

    status = Memory.Get('status')
    if status == 'input_broadcast_message':
        broadcast_message = Memory.Get('broadcast_message') or []
        broadcast_message.append(user_input.raw_message)
        Memory.Set('broadcast_message', broadcast_message)

    return ParseResult(skip_content_module=False)
Esempio n. 14
0
def run(content_config, unused_env, variables):
    """
    content_config schema:
    {
        "bot_admins": {
            "platform_user_ident_1": ["bot_id_1", "bot_id_2" ..],
            ...
        }
    }
    """
    user = g.user

    bots = content_config['bot_admins'].get(user.platform_user_ident)
    if not bots:
        return []

    bot = Memory.Get('bot')
    if not bot:
        msgs = [
            Message(u'嗨 {{user.first_name}},你想要操作哪隻機器人呢?', variables=variables)
        ]
        page = 1
        m = Message()
        msgs.append(m)
        bubble = Message.Bubble('第 %d 頁' % page)

        for bot_id in bots:
            bot = Bot.get_by(id=bot_id, single=True)
            if not bot:
                continue

            bubble.add_button(
                Message.Button(Message.ButtonType.POSTBACK,
                               title=bot.name,
                               payload=EventPayload('SELECT_BOT', bot_id)))

            if len(bubble.buttons) == 3:
                m.add_bubble(bubble)
                page += 1
                bubble = Message.Bubble(u'第 %d 頁' % page)

        if len(bubble.buttons):
            m.add_bubble(bubble)

        msgs[-1].add_quick_reply(
            Message.QuickReply(Message.QuickReplyType.TEXT,
                               u'放棄',
                               payload=EventPayload('CONTROL_FLOW', 'reset'),
                               acceptable_inputs=['(?i)giveup', '(?i)reset']))
        return msgs

    operation = Memory.Get('operation')
    if not operation:
        m = Message(buttons_text=u'你想要執行什麼動作呢?')
        m.add_button(
            Message.Button(Message.ButtonType.POSTBACK,
                           title=u'廣播訊息',
                           payload=EventPayload('SELECT_OP', 'broadcast')))
        m.add_button(
            Message.Button(Message.ButtonType.POSTBACK,
                           title=u'放棄',
                           payload=EventPayload('CONTROL_FLOW', 'reset')))
        return [m]

    if operation == 'broadcast':
        broadcast_message = Memory.Get('broadcast_message')
        status = Memory.Get('status')
        if status == 'input_broadcast_message':
            m = Message(u'你可以繼續輸入下一則訊息:')
            m.add_quick_reply(
                Message.QuickReply(
                    Message.QuickReplyType.TEXT,
                    u'完成',
                    payload=EventPayload('MESSAGE_INPUT', 'done'),
                    acceptable_inputs=[u'好了', u'(?i)done', '(?i)y']))
            m.add_quick_reply(
                Message.QuickReply(
                    Message.QuickReplyType.TEXT,
                    u'重來',
                    payload=EventPayload('MESSAGE_INPUT', 'restart'),
                    acceptable_inputs=[u'好了', u'(?i)restart', '(?i)cancel']))
            m.add_quick_reply(
                Message.QuickReply(
                    Message.QuickReplyType.TEXT,
                    u'放棄',
                    payload=EventPayload('CONTROL_FLOW', 'reset'),
                    acceptable_inputs=['(?i)giveup', '(?i)reset']))
            return [m]
        elif not broadcast_message:
            Memory.Set('status', 'input_broadcast_message')
            return [Message(u'請輸入你要廣播的訊息:')]
        elif status == 'preview_message':
            msgs = [Message(u'請確認你要廣播的訊息:')]
            for raw_msg in broadcast_message:
                msgs.append(Message.FromDict(raw_msg))

            m = msgs[-1]
            m.add_quick_reply(
                Message.QuickReply(Message.QuickReplyType.TEXT,
                                   u'確認',
                                   payload=EventPayload(
                                       'CONFIRM_MESSAGE', True),
                                   acceptable_inputs=[u'是', '(?i)y',
                                                      '(?i)ok']))
            m.add_quick_reply(
                Message.QuickReply(
                    Message.QuickReplyType.TEXT,
                    u'取消',
                    payload=EventPayload('CONFIRM_MESSAGE', False),
                    acceptable_inputs=[u'是', '(?i)no', '(?i)cancel']))

            return msgs

    return [Message(u'錯誤的操作')]
Esempio n. 15
0
    def test_schema_sanity(self):
        """Populate data into all tables and make sure there are no error."""
        DatabaseManager.reset()

        account = Account(name=u'Test Account',
                          email='*****@*****.**', passwd='test_hashed').add()

        bot = Bot(name=u'test', description=u'test', interaction_timeout=120,
                  session_timeout=86400).add()
        account.bots.append(bot)

        content = ContentModule(id='test', name='Content1', description='desc',
                                module_name='', ui_module_name='').add()
        parser = ParserModule(id='test', name='Parser1', description='desc',
                              module_name='passthrough', ui_module_name='',
                              variables={}).add()

        # Test for oauth schema
        oauth1 = OAuthInfo(provider=OAuthProviderEnum.Facebook,
                           provider_ident='mock-facebook-id').add()

        oauth2 = OAuthInfo(provider=OAuthProviderEnum.Github,
                           provider_ident='mock-github-id').add()

        account.oauth_infos.append(oauth1)
        account.oauth_infos.append(oauth2)
        DatabaseManager.commit()

        account_ = Account.get_by(id=account.id, single=True)
        self.assertNotEquals(account_, None)
        self.assertEquals(len(account_.oauth_infos), 2)

        oauth_ = OAuthInfo.get_by(provider_ident='mock-facebook-id',
                                  single=True)
        self.assertNotEquals(oauth_, None)
        self.assertNotEquals(oauth_.account, None)
        self.assertEquals(oauth_.account.id, account.id)

        # Test for bot
        account.bots.append(bot)

        DatabaseManager.commit()

        self.assertNotEquals(Account.get_by(id=account.id, single=True), None)
        self.assertNotEquals(Bot.get_by(id=bot.id, single=True), None)
        self.assertNotEquals(ContentModule.get_by(id=content.id, single=True),
                             None)
        self.assertNotEquals(ParserModule.get_by(id=parser.id, single=True),
                             None)

        # Check acccount_bot association table
        self.assertEquals(len(account.bots), 1)
        self.assertEquals(account.bots[0].id, bot.id)

        platform = Platform(name=u'Test platform',
                            bot_id=bot.id,
                            type_enum=PlatformTypeEnum.Facebook,
                            provider_ident='facebook_page_id',
                            config={}).add()
        DatabaseManager.commit()

        self.assertNotEquals(Platform.get_by(id=platform.id, single=True),
                             None)
        self.assertEquals(len(bot.platforms), 1)
        self.assertEquals(bot.platforms[0].id, platform.id)

        node1 = Node(stable_id='node1', name=u'1', bot_id=bot.id,
                     expect_input=True, content_module_id=content.id,
                     content_config={}, parser_module_id=parser.id,
                     parser_config={}).add()

        node2 = Node(stable_id='node2', name=u'2', bot_id=bot.id,
                     expect_input=True, content_module_id=content.id,
                     content_config={}, parser_module_id=parser.id,
                     parser_config={}).add()

        node3 = Node(stable_id='node3', name=u'3', bot_id=bot.id,
                     expect_input=True, content_module_id=content.id,
                     content_config={}, parser_module_id=parser.id,
                     parser_config={}).add()

        bot.orphan_nodes.append(node3)

        DatabaseManager.commit()

        self.assertNotEquals(Node.get_by(id=node1.id, single=True), None)
        self.assertNotEquals(Node.get_by(id=node2.id, single=True), None)
        self.assertNotEquals(Node.get_by(id=node3.id, single=True), None)

        # Test bot_node association table
        self.assertEquals(bot.orphan_nodes[0].id, node3.id)

        user = User(platform_id=platform.id,
                    platform_user_ident='',
                    last_seen=datetime.datetime.now()).add()
        DatabaseManager.commit()

        self.assertNotEquals(User.get_by(id=user.id, single=True), None)

        event = Event(bot_id=bot.id, user_id=user.id, event_name='event',
                      event_value={}).add()
        DatabaseManager.commit()

        self.assertNotEquals(Event.get_by(id=event.id, single=True), None)

        collected_datum = CollectedDatum(user_id=user.id,
                                         key='key', value={}).add()
        DatabaseManager.commit()

        self.assertNotEquals(CollectedDatum.get_by(id=collected_datum.id,
                                                   single=True), None)
        self.assertEquals(len(user.colleted_data), 1)
        self.assertEquals(user.colleted_data[0].id, collected_datum.id)

        conversation = Conversation(bot_id=bot.id, user_id=user.id,
                                    sender_enum=SenderEnum.Bot, msg={}).add()
        DatabaseManager.commit()
        self.assertNotEquals(Conversation.get_by(id=conversation.id,
                                                 single=True), None)

        # Broadcast
        bc = Broadcast(account_id=account.id, bot_id=bot.id,
                       name=u'New broadcast', messages=[],
                       scheduled_time=datetime.datetime.utcnow()).add()

        DatabaseManager.commit()
        self.assertNotEquals(Broadcast.get_by(id=bc.id, single=True), None)

        # PublicFeed, Feed
        account = Account(name=u'Test Account - 1',
                          email='*****@*****.**', passwd='test_hashed').add()
        feed1 = Feed(url='example.com/rss', type=FeedEnum.RSS,
                     title=u'foo.com', image_url='foo.com/logo').add()
        feed2 = Feed(url='example.com/rss', type=FeedEnum.RSS,
                     title=u'bar.com', image_url='bar.com/logo').add()
        feed3 = Feed(url='example.com/rss', type=FeedEnum.RSS,
                     title=u'baz.com', image_url='baz.com/logo').add()

        account.feeds.append(feed1)
        account.feeds.append(feed2)
        account.feeds.append(feed3)

        DatabaseManager.commit()
        self.assertNotEquals(Feed.get_by(id=feed1.id, single=True), None)

        feeds = Feed.search_title('ba')
        self.assertEquals(len(list(feeds)), 2)

        pfeed = PublicFeed(url='example.com/rss', type=FeedEnum.RSS,
                           title=u'example.com',
                           image_url='example.com/logo').add()

        DatabaseManager.commit()
        self.assertNotEquals(PublicFeed.get_by(id=pfeed.id, single=True), None)