Пример #1
0
def reactivate_user(logged_user, user):
    email = user.email
    user_db = User.objects.coll.find({User.email.db_field: email}).next()
    reactivated_user = User(user_db)
    reactivated_user.is_archived = False
    logged_user.current_account.add_perm(reactivated_user)
    reactivated_user.save()
    return True
Пример #2
0
def get_agents(user, user_ids):
    # If we filter by account here, then all filters by deleted agents
    # will result in an empty list and thus an invalid filter  (basically
    # all agents will be considered when filtering by a deleted one)
    # Since we already have a channel_id when retrieving data which is
    # binding to an account, I believe we can skip this.
    #if user.account:
    #    agents = user.account.get_users(id__in=user_ids, agent_id__ne=0)
    #else:
    #    agents = User.objects(id__in=user_ids, agent_id__ne=0)
    #return agents[:]
    return User.objects(id__in=user_ids, agent_id__ne=0)[:]
Пример #3
0
def staff_users_view(user):
    if not (user.is_staff or user.is_admin):
        return jsonify(ok=False, error="Insufficient user privileges")

    staff_users = []
    if user.is_staff:
        staff_users = [
            u.email for u in User.objects(
                is_superuser=False, user_roles__in=[STAFF]).sort(email=True)
        ]
    elif user.is_admin:
        account_csm = user.current_account.customer_success_manager
        staff_users = account_csm and [account_csm.email] or []

    return jsonify(ok=True, users=staff_users)
Пример #4
0
    def get_email_parameters(self):
        from solariat_bottle.db.channel.base import Channel
        from solariat_nlp.utils.topics import ALL_TOPICS

        ir = self.export_item.input_filter
        params = {}

        report_name_map = {
            'inbound-volume': 'Inbound Volume',
            'response-time': 'Reponse Time',
            'response-volume': 'Response Volume',
            'missed-posts': 'Missed Posts',
            'sentiment': 'Sentiment',
            'top-topics': 'Trending Topics'
        }
        if 'plot_type' in ir and ir['plot_type'] != 'topics':
            params['report_name'] = report_name_map.get(ir['plot_type'])

        def date_part(s):
            return s.split()[0]

        def date_range(d1, d2):
            d1 = date_part(d1)
            d2 = date_part(d2)
            if d1 == d2:
                return d1
            else:
                return "from: %s  to: %s" % (d1, d2)

        params['date_range'] = date_range(ir['from'], ir['to'])
        ch = Channel.objects.find_one(id=ir['channel_id'])
        if ch and ch.is_smart_tag:
            params['smart_tags'] = ch.title

        for key in {'intentions', 'statuses', 'languages', 'sentiments'}:
            if ir.get(key) and key not in set(ir.get('all_selected', [])):
                params[key] = joined(translate(key, ir[key]))

        if ir['topics']:
            params['keywords'] = joined(
                [x['topic'] for x in ir['topics'] if x['topic'] != ALL_TOPICS])
        if ir.get('agents'):
            from solariat_bottle.db.user import User

            agents = User.objects(id__in=ir['agents'], agent_id__ne=0)
            params['agents'] = joined([u.display_agent for u in agents])
        return params
Пример #5
0
def check_channel_token_valid(channel):
    """
    Make sure the token we have stored for :param channel: is still valid. Otherwise
    flash error messages to users with access to channel and raise error.
    """
    from solariat_bottle.db.user import User
    from solariat_bottle.db.message_queue import TaskMessage
    from solariat_bottle.tasks.exceptions import FacebookConfigurationException

    def __patch_msg(input_error):
        input_error += "Please renew access by a new login to your facebook credentials. "
        input_error += "If you don't have required credentials please contact your administrator. "
        input_error += "The account will be missing new entries for facebook until this is done."
        return input_error

    if channel.facebook_access_token:
        api = facebook_driver.GraphAPI(channel.facebook_access_token,
                                       channel=channel)
        try:
            api.get_object('/me/accounts')
            return True
        except facebook.GraphAPIError, ex:
            error = json.loads(ex.message)
            if error['code'] == 190:
                subcode = error.get('subcode', -1)
                if subcode == 458:
                    error_msg = "Facebook access for channel %s has been revoked. " % channel.title
                    error_msg = __patch_msg(error_msg)
                elif subcode == 460:
                    error_msg = "Facebook token for channel %s no longer valid due to facebook password change. " % channel.title
                    error_msg = __patch_msg(error_msg)
                elif subcode == 463:
                    error_msg = "Facebook token for channel %s has expired. " % channel.title
                    error_msg = __patch_msg(error_msg)
                elif subcode == 467:
                    error_msg = "Facebook token for channel %s is invalid. " % channel.title
                    error_msg = __patch_msg(error_msg)
                else:
                    error_msg = error["message"]
                if subcode > 0:
                    for user in User.objects(account=channel.account):
                        TaskMessage.objects.create_error(user=user,
                                                         content=error_msg)
                raise
            else:
                raise
Пример #6
0
def check_user_archived(user):
    logged_user = user
    payload = request.json
    u_payload = payload.get('user')
    email = u_payload.get('email')
    #counting = user.objects(email=email).count()
    counting = User.objects.coll.find({User.email.db_field: email}).count()
    if counting == 0 and not u_payload.get('id'):
        response = jsonify(ok=False, message="No Dialog")
        return response
    else:
        if counting > 0:
            user_db = User.objects.coll.find({
                User.email.db_field: email
            }).next()
            user = User(user_db)
            if user.is_archived:
                if reactivate_user(logged_user, user):
                    response = jsonify(ok=True, message="Dialog")
                    return response
            else:
                response = jsonify(ok=False, message="No Dialog")
                return response
Пример #7
0
    def test_delete_account(self):
        # case #1:
        # user tries to delete account with channels
        self.login(self.su.email)
        account = self.accounts[0]
        Channel.objects.create(account=account, title='testChannel')
        resp = self._call_api('/accounts/json?id=' + str(account.id),
                              assert_false=True,
                              method="DELETE")
        self.assertTrue(resp['error'].startswith(
            "You can not delete an account that contains channels."))

        # case #2:
        # user tries to delete account with no channels but some users
        staff = User.objects.create(account=account,
                                    user_roles=[STAFF],
                                    email='*****@*****.**')
        acc2 = Account.objects.create(name='acc2')
        acc2.add_user(staff)
        staff.account = account
        staff.save()

        staff_users = [u for u in account.get_users() if u.is_staff]
        self.assertTrue(staff_users)

        staff_user_ids = [u.id for u in staff_users]

        Channel.objects.remove(account=account)
        resp = self._call_api('/accounts/json?id=' + str(account.id),
                              method="DELETE")

        # staff users preserved
        self.assertEqual(set(staff_users),
                         set(list(User.objects(id__in=staff_user_ids))))

        staff.reload()
        self.assertEqual(staff.account, acc2)
Пример #8
0
    def test_agents_created_on_demand(self):
        from ..db.channel.twitter import TwitterServiceChannel
        profile1 = UserProfile.objects.upsert(
            'Twitter', dict(user_id='11', screen_name='@profile1'))  # user
        profile2 = UserProfile.objects.upsert(
            'Twitter', dict(user_id='22', screen_name='@profile2'))  # agent1
        profile3 = UserProfile.objects.upsert(
            'Twitter', dict(user_id='33', screen_name='@profile3'))  # agent2
        self.assertEqual(User.objects(agent_id=-1).count(), 0)

        sc = TwitterServiceChannel.objects.create_by_user(self.admin_user,
                                                          account=self.account,
                                                          title='Service')

        post = self._create_db_post(user=self.admin_user,
                                    channels=[sc.inbound_channel],
                                    content='Content',
                                    user_profile=profile1,
                                    twitter={'id': '1111111111'})

        # Agents not created on inbound post
        self.assertEqual(self.account.get_agents().count(), 0)

        # outbound post, but not reply
        outbound_post = self._create_db_post(user=self.admin_user,
                                             channels=[sc.outbound_channel],
                                             content='Content',
                                             user_profile=profile2)

        post, outbound_post  # to disable pyflakes warning

        # Agent created on outbound
        self.assertEqual(self.account.get_agents().count(), 1)

        # Reply to inbound post
        reply = self._create_db_post(user=self.admin_user,
                                     channels=[sc.outbound_channel],
                                     content='Content',
                                     user_profile=profile2,
                                     twitter={
                                         'id': '1231231231',
                                         'in_reply_to_status_id': '1111111111'
                                     })

        self.assertEqual(self.account.get_agents().count(), 1)
        user = list(self.account.get_agents())[0]
        self.assertTrue(user.agent_id > 0)
        self.assertTrue(user.user_profile, profile2)

        # Reply again and verify agents list is the same
        reply = self._create_db_post(user=self.admin_user,
                                     channels=[sc.outbound_channel],
                                     content='Content',
                                     user_profile=profile2,
                                     twitter={
                                         'id': '9879879871',
                                         'in_reply_to_status_id': '1111111111'
                                     })

        self.assertEqual(self.account.get_agents().count(), 1)
        user = list(self.account.get_agents())[0]
        self.assertTrue(user.agent_id > 0)
        self.assertTrue(user.user_profile, profile2)

        # we used to post outbound posts with user_profile=None, but now we don't:

        # reply = self._create_db_post(
        #     user=self.admin_user,
        #     channels=[sc.outbound_channel],
        #     content='Content',
        #     user_profile=None,
        #     twitter={'id':'7657657651', 'in_reply_to_status_id': '1111111111'})
        # self.assertEqual(self.account.get_agents().count(), 2)
        # anon = UserProfile.objects.upsert('Twitter', dict(screen_name='anonymous'))
        # profiles = set([u.user_profile.id for u in self.account.get_agents()])
        # self.assertEqual({anon.id, profile2.id}, profiles)

        # set user_profile to None explicitly and feed it to service channel
        self.assertEqual(User.objects(agent_id=-1).count(), 0)
        reply.user_profile = None
        reply.save()
        agent = User.objects.find_agent_by_post(sc.account, reply)
        self.assertEqual(agent.agent_id, -1)
        # Still 2 agents in account, and created anonymous User with agent_id = -1
        # self.assertEqual(self.account.get_agents().count(), 2)
        self.assertEqual(User.objects(agent_id=-1).count(), 1)

        # Verify agents added to account with 'read' permission and added to service channel
        reply = self._create_db_post(user=self.admin_user,
                                     channels=[sc.outbound_channel],
                                     content='Content',
                                     user_profile=profile3,
                                     twitter={
                                         'id': '8238238238',
                                         'in_reply_to_status_id': '1111111111'
                                     })

        agents = set()
        self.account.reload()
        for agent in self.account.get_agents():
            self.assertTrue(self.account.can_view(agent))
            self.assertFalse(
                self.account.can_edit(agent) and not agent.is_admin)
            agents.add(agent)
        sc.reload()
        self.assertEqual(len(sc.agents), 2)  #profile2, profile3
        self.assertEqual(set(sc.agents), agents)
Пример #9
0
                    error_msg = __patch_msg(error_msg)
                elif subcode == 467:
                    error_msg = "Facebook token for channel %s is invalid. " % channel.title
                    error_msg = __patch_msg(error_msg)
                else:
                    error_msg = error["message"]
                if subcode > 0:
                    for user in User.objects(account=channel.account):
                        TaskMessage.objects.create_error(user=user,
                                                         content=error_msg)
                raise
            else:
                raise
    else:
        error_msg = "Facebook channel %s is not logged into facebook." % channel.title
        error_msg = __patch_msg(error_msg)
        for user in User.objects(account=channel.account):
            TaskMessage.objects.create_error(user=user, content=error_msg)
        raise FacebookConfigurationException(error_msg)


def check_account_tokens_valid(account):
    """
    Make sure all the EnterpriseFacebookChannel entities from :param account: that are
    still active have a valid access token attached.
    """
    from solariat_bottle.db.channel.facebook import EnterpriseFacebookChannel
    for channel in EnterpriseFacebookChannel.objects(account=account,
                                                     status="Active"):
        check_channel_token_valid(channel)
Пример #10
0
 def members_total(self):
     from solariat_bottle.db.user import User
     return User.objects(groups__in=[self.id]).count()
Пример #11
0
    def test_assignment_parallel(self):
        import pymongo
        from solariat.utils.timer import TimedMethodProxy

        channel, dispatch_channel = self.setup_channels()
        self._create_db_matchable('Here is the carrot',
                                  intention_topics=['carrot'],
                                  channels=[channel.inbound_channel, channel])

        def setup(n_responses, n_agents):
            # setup responses
            self.create_responses(n_responses, channel.inbound_channel)
            # setup agents
            for i in range(0, n_agents):
                assignee = self._create_db_user(
                    email='*****@*****.**' % i,
                    password='******',
                    account=self.user.account,
                    roles=[AGENT])
                channel.add_perm(assignee)
                channel.inbound_channel.add_perm(assignee)
                dispatch_channel.add_perm(assignee)

        def fetch(params):
            agent_n, fetch_limit = params
            agent_email = '*****@*****.**' % agent_n
            self.login(agent_email, '12345')
            user = User.objects.get(email=agent_email)
            filter_ = {
                'channel_id': str(channel.inbound_channel.id),
                'limit': fetch_limit
            }
            return user.id, self.fetch_responses(filter_)

        def do_test(n_responses, n_agents, responses_fetch_limit):
            setup(n_responses, n_agents)

            pool = ProcessPool(n_agents)
            results = pool.map(fetch, [(i, responses_fetch_limit)
                                       for i in range(0, n_agents)])
            return results

        with TimedMethodProxy(pymongo.database.Database,
                              'command',
                              threshold=0.001,
                              override=True):
            # 3 agents simultaneously fetch 5 response out of 10
            fetch_limit = 5
            fetch_results = do_test(n_responses=10,
                                    n_agents=3,
                                    responses_fetch_limit=fetch_limit)
            # expect: 1. all responses assigned
            # 2. each agent should have not more than fetch_limit responses assigned
            # 3. each agent should have non-intersecting set of responses

            # 1.
            agents = User.objects(email__regex='^assignee+')[:]
            responses = Response.objects(channel=channel.inbound_channel)[:]

            responses_by_agents_db = {}
            for agent in agents:
                responses_by_agents_db[str(agent.id)] = []

            for response in responses:
                if response.assignee is not None:
                    self.assertIn(response.assignee, [a.id for a in agents])
                    responses_by_agents_db[str(response.assignee)].append(
                        response.id)

            responses_by_agents_ui = {}
            for agent in agents:
                responses_by_agents_ui[str(agent.id)] = []

            for (assignee_id, responses) in fetch_results:
                responses_by_agents_ui[str(assignee_id)].extend(
                    [r['id'] for r in responses])

            self.assertDictEqual(responses_by_agents_db,
                                 responses_by_agents_ui)
            # 2. and 3.
            seen_responses = set()
            for agent_id, response_ids in responses_by_agents_ui.items():
                self.assertTrue(len(response_ids) <= fetch_limit)
                if set(response_ids).intersection(seen_responses):
                    assert False, "Responses assigned to agent %s are already" \
                                  " assigned to another agent" % agent_id
                seen_responses = seen_responses.union(set(response_ids))
Пример #12
0
def alert_user_candidates(user):
    return jsonify(ok=True,
                   list=[
                       u.to_dict() for u in User.objects(account=user.account)
                       if not u.is_system
                   ])
Пример #13
0
 def _make_agent(self, n, account):
     u = User(agent_id=n + 100, email="*****@*****.**" % (n + 100))
     u.save()
     account.add_user(u)
     return u
Пример #14
0
    def test_status(self):
        from solariat_bottle.db.user import User
        agent = User(email='*****@*****.**')
        agent.signature = '^AG'
        agent.save()
        self.sc.account.add_user(agent)

        # Create inbound post
        self.inbound_post_created_at = parse_date("05/07/2013")
        parent_id = "123456789"
        twitter_data = {
            'twitter': {
                'id': parent_id,
                'created_at':
                datasift_date_format(self.inbound_post_created_at)
            }
        }
        self.inbound_post = self._create_db_post(
            channels=[self.inbound],
            content="@brand I need a foo. Does anyone have a foo? ",
            **twitter_data)
        self.inbound_post.reload()
        self.assertTrue(self.inbound.is_assigned(self.inbound_post))

        agents = None
        inbound_post = self.get_inbound_post()
        self.assertEqual(len(inbound_post.channel_assignments), 1)
        self.assertEqual(
            inbound_post.channel_assignments[str(self.inbound.id)],
            'highlighted')

        stats = self.get_inbound_stats(agents)
        self.assertEqual(stats[SpeechActMap.POTENTIAL], 0)
        self.assertEqual(stats[SpeechActMap.ACTUAL], 0)
        self.assertEqual(stats[SpeechActMap.ACTIONABLE], 2)
        self.assertEqual(stats[SpeechActMap.REJECTED], 0)

        # Reply to inbound post
        twitter_data['twitter']['id'] = '987654231'
        twitter_data['twitter']['in_reply_to_status_id'] = parent_id
        twitter_data['twitter']['created_at'] = datasift_date_format(now())
        self.outbound_post = self._create_db_post(
            user_profile={'user_name': 'agent_test_name'},
            channels=[self.outbound],
            content="Response content. ^AG",
            **twitter_data)

        self.inbound_post.reload()
        self.outbound_post.reload()

        for agents in (None, [agent]):
            stats = self.get_inbound_stats(agents)
            self.assertEqual(stats[SpeechActMap.POTENTIAL], 0)
            self.assertEqual(stats[SpeechActMap.ACTUAL], 2)
            self.assertEqual(stats[SpeechActMap.ACTIONABLE], 0)
            self.assertEqual(stats[SpeechActMap.REJECTED], 0)

            stats = self.get_outbound_stats(agents)
            self.assertEqual(stats[SpeechActMap.POTENTIAL], 0)
            self.assertEqual(stats[SpeechActMap.ACTUAL], 0)
            self.assertEqual(stats[SpeechActMap.ACTIONABLE], 1)
            self.assertEqual(stats[SpeechActMap.REJECTED], 0)

        self.assertEqual(
            self.inbound_post.channel_assignments[str(self.inbound.id)],
            'replied')
        self.assertEqual(len(self.inbound_post.channel_assignments), 1)

        inbound_post = self.get_inbound_post()
        self.assertEqual(len(inbound_post.channel_assignments), 1)
        self.assertEqual(
            inbound_post.channel_assignments[str(self.inbound.id)], 'replied')