Beispiel #1
0
    def setup_prerequisite(self):
        DatabaseManager.reset()

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

        config = {
            'access_token': 'EAAP0okfsZCVkBAI3BCU5s3u8O0iVFh6NAwFHa7X2bKZCGQ'
                            'Lw6VYeTpeTsW5WODeDbekU3ZA0JyVCBSmXq8EqwL1GDuZBO'
                            '7aAlcNEHQ3AZBIx0ZBfFLh95TlJWlLrYetzm9owKNR8Qju8'
                            'HF6qra20ZC6HqNXwGpaP74knlNvQJqUmwZDZD'
        }

        self.platform = Platform(name=u'Test platform',
                                 bot_id=self.bot.id,
                                 type_enum=PlatformTypeEnum.Facebook,
                                 provider_ident='1155924351125985',
                                 config=config).add()
        DatabaseManager.flush()

        self.user_1 = User(platform_id=self.platform.id,
                           platform_user_ident='1153206858057166',
                           last_seen=datetime.datetime(2016, 6, 2, 12, 44, 56,
                                                       tzinfo=pytz.utc),
                           settings={'subscribe': True}).add()
        self.user_2 = User(platform_id=self.platform.id,
                           platform_user_ident='1318395614844436',
                           last_seen=datetime.datetime(2016, 6, 2, 12, 44, 56,
                                                       tzinfo=pytz.utc),
                           settings={'subscribe': True}).add()
        DatabaseManager.commit()
Beispiel #2
0
    def test_broadcast_deletion(self):
        # Test get all broadcasts
        rv = self.app.get('/api/bots/%d/broadcasts' % self.bot_ids[0])
        self.assertEquals(rv.status_code, HTTPStatus.STATUS_OK)
        data = json.loads(rv.data)

        broadcast_id = data['broadcasts'][0]['id']

        # Set broadcast status to SENT
        b = Broadcast.get_by(id=broadcast_id, single=True)
        b.status = BroadcastStatusEnum.SENT
        DatabaseManager.commit()

        # Delete the broadcast (should fail becuase it's sent already)
        rv = self.app.delete('/api/broadcasts/%d' % broadcast_id)
        self.assertEquals(rv.status_code, HTTPStatus.STATUS_CLIENT_ERROR)
        data = json.loads(rv.data)

        # Set broadcast status back to QUEUED
        b2 = Broadcast.get_by(id=broadcast_id, single=True)
        b2.status = BroadcastStatusEnum.QUEUED
        DatabaseManager.commit()

        # Delete the broadcast
        rv = self.app.delete('/api/broadcasts/%d' % broadcast_id)
        self.assertEquals(rv.status_code, HTTPStatus.STATUS_OK)
        data = json.loads(rv.data)

        # Make sure we don't have any broadcasts left
        rv = self.app.get('/api/bots/%d/broadcasts' % self.bot_ids[0])
        self.assertEquals(rv.status_code, HTTPStatus.STATUS_OK)
        data = json.loads(rv.data)
        self.assertEquals(len(data['broadcasts']), 0)
Beispiel #3
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)
Beispiel #4
0
 def process_admin_reply(self, bot, user, unused_user_input=None,
                         unused_input_vars=None):
     try:
         if bot.admin_interaction_timeout > 0:
             user.last_admin_seen = datetime.datetime.now()
     finally:
         DatabaseManager.commit()
Beispiel #5
0
    def setup_prerequisite(self, bot_file):
        InputTransformation.clear()
        self.bot = reset_and_setup_bots([bot_file])[0]
        self.user = User(platform_id=Platform.get_by(id=1, single=True).id,
                         platform_user_ident='blablabla',
                         last_seen=datetime.datetime.now()).add()

        DatabaseManager.commit()
Beispiel #6
0
def delete_broadcast(broadcast_id):
    """Delete a broadcast."""
    broadcast = get_account_broadcast_by_id(broadcast_id)
    if broadcast.status == BroadcastStatusEnum.SENT:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM, 'Broadcast not deletable')
    broadcast.delete()
    DatabaseManager.commit()
    return jsonify(message='ok')
Beispiel #7
0
def deploy_bot(bot_id):
    bot = get_account_bot_by_id(bot_id)
    try:
        parse_bot(bot.staging, bot.id)
    except Exception as e:
        logger.exception(e)
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM,
                       'Bot definition parsing failed')
    bot_def = BotDef.add_version(bot.id, bot.staging)
    bot.staging = None  # Clear staging area
    DatabaseManager.commit()
    return jsonify(version=bot_def.version)
Beispiel #8
0
def create_platform():
    """Create a new platform."""
    try:
        platform_json = request.json
        platform_json['account_id'] = g.account.id
        platform = parse_platform(platform_json)
    except Exception as e:
        logger.exception(e)
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM,
                       'Platform definition parsing failed')
    DatabaseManager.commit()
    return jsonify(platform.to_json(['config']))
Beispiel #9
0
def create_broadcast():
    """Create a new broadcast."""
    try:
        broadcast_json = request.json
        broadcast_json['account_id'] = g.account.id
        broadcast = parse_broadcast(broadcast_json)
    except Exception as e:
        logger.exception(e)
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM,
                       'Broadcast create request failed')
    DatabaseManager.commit()
    return jsonify(broadcast.to_json())
Beispiel #10
0
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))
Beispiel #11
0
def add_user(platform, sender):
    """Add a new user into the system."""
    profile_info = get_user_profile(platform, sender)
    user = User(platform_id=platform.id,
                platform_user_ident=sender,
                last_seen=datetime.datetime.now(),
                **profile_info).add()
    DatabaseManager.commit()

    statsd.gauge('users', User.count(), tags=[config.ENV_TAG])
    track(
        TrackingInfo.Event(sender, '%s.User' % platform.type_enum.value, 'Add',
                           profile_info['first_name']))
    return user
Beispiel #12
0
    def test_session_mutable_tracking(self):
        bot = reset_and_setup_bots(['test/simple.bot'])[0]
        user = User(platform_id=bot.platforms[0].id,
                    platform_user_ident='',
                    last_seen=datetime.datetime.now(), session=1).add()
        DatabaseManager.commit()

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

        s = User.get_by(id=user.id, single=True)
        s.session.message_sent = True
        DatabaseManager.commit()

        s = User.get_by(id=user.id, single=True)
        self.assertEquals(s.session.message_sent, True)
Beispiel #13
0
def update_bot(bot_id):
    """Modify a bot staging area."""
    bot = get_account_bot_by_id(bot_id)
    try:
        validate_bot_schema(request.json)
    except Exception as e:
        logger.exception(e)
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM,
                       'Bot definition parsing failed')

    bot.name = request.json['bot']['name']
    bot.description = request.json['bot']['description']
    bot.staging = request.json
    DatabaseManager.commit()
    return jsonify(message='ok')
Beispiel #14
0
def update_broadcast(broadcast_id):
    """Update a broadcast."""
    broadcast = get_account_broadcast_by_id(broadcast_id)
    try:
        broadcast_json = request.json
        broadcast_json['account_id'] = g.account.id
        parse_broadcast(broadcast_json, broadcast.id)
    except BroadcastUnmodifiableError:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM, 'Broadcast not modifiable')
    except Exception as e:
        logger.exception(e)
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_WRONG_PARAM,
                       'Broadcast update request failed')
    DatabaseManager.commit()
    return jsonify(message='ok')
Beispiel #15
0
    def test_timestamp_update(self):
        """Make sure the updated_at timestamp automatically updates on
        commit."""
        account = Account(email='*****@*****.**',
                          passwd='test_hashed').add()
        DatabaseManager.commit()

        account.refresh()
        self.assertEquals(account.created_at, account.updated_at)
        last_updated = account.updated_at

        time.sleep(1)
        account.email = '*****@*****.**'
        DatabaseManager.commit()

        account.refresh()
        self.assertNotEquals(last_updated, account.updated_at)
Beispiel #16
0
    def setup_prerequisite(self):
        register_all_modules()

        self.account1 = Account(
            name=u'test', email='*****@*****.**').set_passwd('12345678').add()
        self.account2 = Account(
            name=u'test2',
            email='*****@*****.**').set_passwd('12345678').add()
        DatabaseManager.commit()

        self.login(self.account1)
        self.create_bot()
        self.create_broadcast(self.bot_ids[0])

        self.login(self.account2)
        self.create_bot()
        self.create_broadcast(self.bot_ids[1])

        # Login back as account1
        self.login(self.account1)
Beispiel #17
0
    def setup_prerequisite(self):
        register_all_modules()

        self.account1 = Account(
            name=u'test', email='*****@*****.**').set_passwd('12345678').add()
        self.account2 = Account(
            name=u'test2',
            email='*****@*****.**').set_passwd('12345678').add()
        DatabaseManager.commit()

        self.login(self.account1)
        self.create_bot()
        self.create_platform('dev/bb8.test.platform')

        self.login(self.account2)
        self.create_bot()
        self.create_platform('dev/bb8.test2.platform')

        # Login back as account1
        self.login(self.account1)
Beispiel #18
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
Beispiel #19
0
    def test_CollectedData_API(self):
        for i in range(3):
            CollectedDatum(user_id=self.user_1.id,
                           key='K', value='V%d' % i).add()
            time.sleep(1)
            DatabaseManager.commit()

        g.user = self.user_1
        self.assertEquals(CollectedData.GetLast('K'), 'V2')
        self.assertEquals(CollectedData.Get('K', 3), ['V2', 'V1', 'V0'])
        self.assertEquals(CollectedData.Get('K', 2, 1), ['V1', 'V0'])

        # pylint: disable=W0212
        for i in range(CollectedData._MAX_RETURN_RESULTS + 10):
            CollectedDatum(user_id=self.user_2.id,
                           key='K', value='V%d' % i).add()
        DatabaseManager.commit()

        g.user = self.user_2
        self.assertEquals(len(CollectedData.Get('K', 110)), 100)
        self.assertEquals(CollectedData.Count('K'), 110)
Beispiel #20
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()
Beispiel #21
0
    def test_auth(self):
        DatabaseManager.reset()
        account = Account(name=u'Test Account 3',
                          email='*****@*****.**').add()

        some_passwd = 'abcdefg'
        account.set_passwd(some_passwd)

        DatabaseManager.commit()
        account_ = Account.get_by(id=account.id, single=True)
        self.assertNotEquals(account_.passwd, some_passwd)
        self.assertEquals(account_.verify_passwd(some_passwd), True)
        self.assertEquals(account_.verify_passwd('should be wrong'), False)

        token = account_.auth_token

        account_t = Account.from_auth_token(token)
        self.assertEquals(account_.id, account_t.id)

        fake_token = jwt.encode({
            'iss': 'compose.ai',
            'sub': account_.id,
            'jti': str(uuid.uuid4()),
            'iat': datetime.datetime.utcnow(),
            'exp': datetime.datetime.utcnow() + datetime.timedelta(days=14)
        }, 'im fake secret')

        with self.assertRaises(RuntimeError):
            Account.from_auth_token(fake_token)

        outdated_token = jwt.encode({
            'iss': 'compose.ai',
            'sub': account_.id,
            'jti': str(uuid.uuid4()),
            'iat': datetime.datetime.utcnow() - datetime.timedelta(days=30),
            'exp': datetime.datetime.utcnow() - datetime.timedelta(days=15)
        }, config.JWT_SECRET)

        with self.assertRaises(RuntimeError):
            Account.from_auth_token(outdated_token)
Beispiel #22
0
def email_register():
    data = request.json
    try:
        jsonschema.validate(data, REGISTER_SCHEMA)
        pytz.timezone(data['timezone'])
    except Exception:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_FORM_VALIDATION,
                       'schema validation fail')

    account = Account.get_by(email=data['email'], single=True)
    if not account:
        account = Account(email=data['email']).set_passwd(data['passwd']).add()
        DatabaseManager.commit()
    else:
        raise AppError(HTTPStatus.STATUS_CLIENT_ERROR,
                       CustomError.ERR_USER_EXISTED,
                       'email %s is already taken' % data['email'])

    ret = account.to_json()
    ret[Key.AUTH_TOKEN] = account.auth_token
    return jsonify(ret)
Beispiel #23
0
    def test_query_expression_rendering(self):
        """Test that query expresssion can be query and rendered correctly."""
        CollectedDatum(user_id=self.user_1.id, key='data',
                       value='value1').add()
        DatabaseManager.commit()
        time.sleep(1)
        CollectedDatum(user_id=self.user_1.id, key='data',
                       value='value2').add()
        DatabaseManager.commit()
        time.sleep(1)
        CollectedDatum(user_id=self.user_1.id, key='data',
                       value='value3').add()
        CollectedDatum(user_id=self.user_1.id, key='aaa',
                       value='aaa').add()
        CollectedDatum(user_id=self.user_2.id, key='data',
                       value='value4').add()
        DatabaseManager.commit()

        g.user = self.user_1
        m = Message("{{data('data').first|upper}}")
        self.assertEquals(m.as_dict()['text'], 'VALUE1')

        m = Message("{{data('data').get(1)}}")
        self.assertEquals(m.as_dict()['text'], 'value2')

        m = Message("{{data('data').last}}")
        self.assertEquals(m.as_dict()['text'], 'value3')

        m = Message("{{data('data').lru(0)}}")
        self.assertEquals(m.as_dict()['text'], 'value3')

        m = Message("{{data('data').lru(1)}}")
        self.assertEquals(m.as_dict()['text'], 'value2')

        m = Message("{{data('data').fallback('valuef').get(5)}}")
        self.assertEquals(m.as_dict()['text'], 'valuef')

        m = Message("{{data('data').order_by('-created_at').first}}")
        self.assertEquals(m.as_dict()['text'], 'value3')

        m = Message("{{data('data').count}}")
        self.assertEquals(m.as_dict()['text'], '3')

        # Test error
        with self.assertRaises(Exception):
            Message("{{data('data')|some_filter}}")

        wrong_tmpl = "{{data('some_key').first}}"
        m = Message(wrong_tmpl)
        self.assertEquals(m.as_dict()['text'], wrong_tmpl)
Beispiel #24
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)
Beispiel #25
0
 def setup_prerequisite(self):
     Account(name=u'test',
             email='*****@*****.**').set_passwd('12345678').add()
     DatabaseManager.commit()
Beispiel #26
0
def delete_platform(platform_id):
    """Delete a platform."""
    platform = get_account_platform_by_id(platform_id)
    platform.delete()
    DatabaseManager.commit()
    return jsonify(message='ok')
Beispiel #27
0
def delete_bot(bot_id):
    bot = get_account_bot_by_id(bot_id)
    bot.delete()
    DatabaseManager.commit()
    return jsonify(message='ok')
Beispiel #28
0
 def test_schema(self):
     """Test database schema and make sure all the tables can be created
     without problems."""
     DatabaseManager.reset()
     DatabaseManager.commit()
Beispiel #29
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)
Beispiel #30
0
    def step(self, bot, user, user_input=None, input_vars=None):
        """Main function for executing a node."""

        try:  # pylint: disable=R0101
            now = datetime.datetime.now()

            if user.session is None:
                if user_input:
                    user_input.disable_jump()
                user.goto(Bot.START_STABLE_ID)

            g.user = user
            if user_input:
                if config.STORE_CONVERSATION:
                    Conversation(bot_id=user.bot_id, user_id=user.id,
                                 sender_enum=SenderEnum.Human,
                                 msg=user_input).add()

                # Parse audio as text if there are audio payload
                user_input.ParseAudioAsText(user)
                user_input = user_input.RunInputTransformation()

            # If there was admin interaction, and admin_interaction_timeout
            # haven't reached yet, do not run engine.
            if (bot.admin_interaction_timeout > 0 and
                    ((now - user.last_admin_seen).total_seconds() <
                     bot.admin_interaction_timeout)):
                return

            # Check has been idle for too long, reset it's state if yes.
            if (bot.session_timeout > 0 and
                    ((now - user.last_seen).total_seconds() >
                     bot.session_timeout)):
                user.last_seen = datetime.datetime.now()
                user.goto(Bot.ROOT_STABLE_ID)

            if user_input and user_input.jump():
                node = Node.get_by(stable_id=user_input.jump_node_id,
                                   bot_id=bot.id, single=True)
                # Check if the node belongs to current bot
                if node is None:
                    logger.critical('Invalid jump node_id %s' %
                                    user_input.jump_node_id)
                # If payload button is pressed, we need to jump to the
                # corresponding node if payload's node_id != current_node_id
                elif user_input.jump_node_id != user.session.node_id:
                    user.goto(user_input.jump_node_id)
                    user.session.message_sent = True

            node = Node.get_by(stable_id=user.session.node_id, bot_id=bot.id,
                               eager=['content_module', 'parser_module'],
                               single=True)
            g.node = node

            if node is None:
                logger.critical('Invalid node_id %s' % user.session.node_id)
                user.goto(Bot.ROOT_STABLE_ID)
                user.session.message_sent = True
                return self.step(bot, user, user_input)

            track(TrackingInfo.Pageview(user.platform_user_ident,
                                        '/%s' % node.stable_id))

            # Shared global variables
            global_variables = {
                'user': user.to_json(),
                'bot_id': bot.id
            }

            if not user.session.message_sent:
                env = {
                    'platform_type': SupportedPlatform(
                        user.platform.type_enum.value)
                }
                # Prepare input variables
                input_vars = input_vars or {}
                input_vars.update(global_variables)

                # TODO(aitjcize): figure out how to deal with cm exceptions
                cm = node.content_module.get_module()

                # Send message
                messages = cm.run(node.content_config, env, input_vars)
                messaging.send_message(user, messages)
                user.session.message_sent = True

                # Store InputTransformation in session
                user.session.input_transformation = InputTransformation.get()

                if not node.expect_input:
                    # There are no parser module, which means we are at end of
                    # subgraph.
                    if node.parser_module is None:
                        user.goto(Bot.ROOT_STABLE_ID)
                        user.session.message_sent = True
                        return
                    elif node.parser_module.id == PASSTHROUGH_MODULE_ID:
                        return self.step(bot, user)
                    else:
                        raise RuntimeError('Node `%s\' with parser module '
                                           'not expecting input' % node)
            else:
                # We are already at root node and there is no user input.
                # Display root node again.
                if not user_input and node.stable_id == Bot.ROOT_STABLE_ID:
                    user.session.message_sent = False
                    return self.step(bot, user, user_input)

                # No parser module associate with this node, go back to root
                # node.
                if node.parser_module is None:
                    user.goto(Bot.ROOT_STABLE_ID)
                    user.session.message_sent = True
                    # Run at root instead, so disable jump
                    user_input.disable_jump()
                    return self.step(bot, user, user_input)

                if (not user_input and
                        node.parser_module.id != PASSTHROUGH_MODULE_ID):
                    raise RuntimeError('no user input when running parser')

                result, variables = self.run_parser_module(
                    node, user, user_input, global_variables, False)

                # Node parser failed, try root parser:
                if result.errored and node.stable_id != Bot.ROOT_STABLE_ID:
                    root_result, root_variables = self.run_parser_module(
                        bot.root_node, user, user_input, global_variables,
                        True)

                    # If root paser matched, use root_parser result as result.
                    if not root_result.errored:
                        result = root_result
                        variables = root_variables

                if result.ack_message:
                    self.send_ack_message(user, result.ack_message, variables)

                # end_node_id may be None, either there is a bug or parser
                # module decide not to move at all.
                if result.end_node_id:
                    user.goto(result.end_node_id)

                    # If we are going back the same node, assume there is an
                    # error and we want to retry. don't send message in this
                    # case.
                    if (result.end_node_id == node.stable_id and
                            node.stable_id != Bot.ROOT_STABLE_ID and
                            result.skip_content_module):
                        user.session.message_sent = True
                        return
                else:
                    # There is no link, replay current node.
                    user.session.message_sent = False

                # Run next content module
                return self.step(bot, user, None, variables)
        except Exception as e:
            logger.exception(e)
            # Rollback when error happens, so user won't get stuck in some
            # weird state.
            DatabaseManager.rollback()
        finally:
            user.last_seen = datetime.datetime.now()
            DatabaseManager.commit()