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
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)[:]
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)
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
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
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
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)
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)
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)
def members_total(self): from solariat_bottle.db.user import User return User.objects(groups__in=[self.id]).count()
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))
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 ])
def _make_agent(self, n, account): u = User(agent_id=n + 100, email="*****@*****.**" % (n + 100)) u.save() account.add_user(u) return u
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')