def ensure_agents(self, agents, group_by, channel): """ If we are plotting by agent, we need the actual list of agents instead of empty list since we are using that for the y_axis `channel` can be either a string or a sequense """ if not agents and group_by == 'agent': if isinstance(channel, seq_types): sc = get_service_channel(list(channel)[0]) else: sc = get_service_channel(channel) agents = sc and sc.agents or [] # filter out common users, keep only agents agents = [a for a in agents if a.agent_id != 0] return agents
def test_get_service_channel(self): self.sc.add_username('test_handle') outb = TwitterTestDispatchChannel.objects.create_by_user( self.user, title="TestDispatch") outb.twitter_handle = 'test_handle' outb.save() sc = get_service_channel(outb) self.assertEqual(sc.id, self.sc.id) # case-insensitive lookup self.sc.add_username('@test_HANDLE2') outb.twitter_handle = 'TEST_handle2' outb.save() sc = get_service_channel(outb) self.assertEqual(sc.id, self.sc.id)
def service_channel(self): from solariat_bottle.utils.post import get_service_channel if not self.channel: return None return get_service_channel(self.channel)
def su_post_filter_entries(user, channel_id): from solariat_bottle.utils.post import get_service_channel query = {} if channel_id != 'all': channels = [] try: channel = Channel.objects.get(channel_id) service_channel = get_service_channel(channel) dispatch_channel = service_channel.get_outbound_channel(user) channels.append(channel) if service_channel: channels.extend([service_channel, service_channel.inbound_channel, service_channel.outbound_channel]) if dispatch_channel: channels.append(dispatch_channel) except Exception as exc: return jsonify(ok=False, error=str(exc)) query = {'channels__in': channels} from solariat_bottle.db.tracking import PostFilterEntry limit = int(request.args.get('limit', 20)) offset = int(request.args.get('offset', 0)) result = [] for pfe in PostFilterEntry.objects(**query).limit(limit).skip(offset): result.append(dict( entry=pfe.entry, filter_type=pfe.filter_type_id, lang=pfe.lang, channels=channels_to_json(pfe.channels))) return jsonify(list=result)
def __get_token_for_item(channel, object_id): from solariat_bottle.settings import LOGGER from solariat_bottle.tasks.exceptions import FacebookCommunicationException from solariat_bottle.db.post.facebook import FacebookPost from solariat_bottle.db.channel.facebook import EnterpriseFacebookChannel try: if isinstance(channel, EnterpriseFacebookChannel): from solariat_bottle.utils.post import get_service_channel channel = get_service_channel(channel) fb_post = FacebookPost.objects.get(_native_id=str(object_id)) post_type = fb_post.wrapped_data['source_type'] source_ids = set(map(str, get_page_id_candidates(fb_post))) try: if post_type == 'Event': token = [ event for event in channel.all_fb_events if str(event['id']) in source_ids ][0]['access_token'] else: token = [ page for page in channel.facebook_pages if str(page['id']) in source_ids ][0]['access_token'] except Exception, ex: LOGGER.error( "Failed to get page access token for object_id=%s and channel=%s" % (object_id, channel)) token = channel.facebook_access_token return token
def fb_private_message(channel, pmthread_id, message): import facebook from solariat_bottle.tasks.exceptions import FacebookCommunicationException from solariat_bottle.utils.post import get_service_channel fb_service_channel = get_service_channel(channel) try: graph = get_facebook_api(channel) # We don't know based on a thread id which page we should answer from. No better solution # right now than to just try to reply from all pages and see which one we can reply from first errors = [] try: # First try to reply as individual, this will fail since most PMs are to pages and we won't # have permission as user to reply to them return graph.request("%s/messages" % pmthread_id, post_args={"message": message}) except facebook.GraphAPIError, ex: errors.append(ex) accounts_data = fb_service_channel.all_facebook_pages if not accounts_data: accounts_data = graph.request('/me/accounts')['data'] fb_service_channel.update(all_facebook_pages=accounts_data) for account in accounts_data: # Now try to find page from which to reply since we don't have anything but post id page_graph = facebook_driver.GraphAPI(account.get( 'access_token', ''), channel=channel) try: return page_graph.request("%s/messages" % pmthread_id, post_args={"message": message}) except facebook.GraphAPIError, ex: errors.append(ex)
def su_queue_view(user, channel_id): query = {} if channel_id != 'all': try: from solariat_bottle.utils.post import get_service_channel service_channel = get_service_channel(Channel.objects.get(channel_id)) dispatch_channel = service_channel.get_outbound_channel(user) except Exception as exc: return jsonify(ok=False, channel_id=channel_id, error=str(exc)) else: channel_ids = [] if service_channel: channel_ids.append(str(service_channel.id)) if dispatch_channel: channel_ids.append(str(dispatch_channel.id)) query = dict(channel_id__in=channel_ids) limit = int(request.args.get('limit', 20)) offset = int(request.args.get('offset', 0)) from solariat_bottle.db.queue_message import QueueMessage from solariat_bottle.db.post.base import Post messages = [] for message in QueueMessage.objects(**query).limit(limit).skip(offset): post_data = post_to_data(Post(message.post_data)) post_data['message_id'] = str(message.id) post_data['reserved_until'] = str(message.reserved_until) messages.append(post_data) return jsonify(channel_id=channel_id, limit=limit, offset=offset, result=messages, total=QueueMessage.objects(**query).count())
def fb_put_post(channel, target_id, message, picture_name=None, media=None, link=None, link_description=None): """ Send post to a user feed or a page feed """ from solariat_bottle.tasks.exceptions import FacebookCommunicationException from solariat_bottle.tasks.twitter import get_file_from_media from solariat_bottle.utils.post import get_service_channel api = get_facebook_api(channel) service_channel = get_service_channel(channel) if service_channel: possible_targets = service_channel.all_fb_events + service_channel.facebook_pages matched_targets = [ target for target in possible_targets if target['id'] == target_id ] if matched_targets: token = matched_targets[0]['access_token'] api = get_facebook_api(channel, token=token) if media: filename, f = get_file_from_media(media) kwargs = dict() if message: kwargs['caption'] = message # if target_id: # kwargs['target_id'] = target_id photo = api.put_photo(f, album_path=target_id + "/photos", **kwargs) # attachment_data['link'] = picture_info['link'] if f: try: f.close() os.unlink(f.name) except IOError: pass return photo else: attachment_data = {} if link: # attachment_data['link'] = link attachment_data["picture"] = link if link_description: attachment_data['description'] = link_description try: result = api.put_wall_post(force_bytes(message, 'utf-8', 'replace'), profile_id=target_id, attachment=attachment_data) return result except Exception, ex: er = "Failure on posting to facebook. Channel: %s, Target: %s, Message: %s, Pic_N: %s, Pic_B: %s, Link: %s" logger.error( er % (channel, target_id, message, picture_name, media, link)) raise FacebookCommunicationException(ex.message)
def get_twitter_outbound_channel(user, channel): ''' Get the outbound channel based on user access, channel configuration, and as a last resort, channel configurations ''' from solariat_bottle.utils.post import get_service_channel # If for any reason we try to get the dispatch channel for # a dispatch channel, and user has edit perms, we can just use same channel. if isinstance(channel, EnterpriseTwitterChannel) and channel.can_edit(user): return channel # The configured channel is only necessary, or correct, if this is no service # channel, or if there is a service channel with multiple candidates configured_channel = user.get_outbound_channel(channel.platform) sc = get_service_channel(channel) if sc: if sc.dispatch_channel: return sc.dispatch_channel candidates = EnterpriseTwitterChannel.objects.find_by_user( user, account=channel.account, status='Active')[:] # Case insensitive filter for match with user names usernames_lowercase = [ n.lower() if not n.startswith('@') else n[1:] for n in sc.usernames ] candidates = [ c for c in candidates if c.is_authenticated and (c.twitter_handle.lower( ) in usernames_lowercase or not usernames_lowercase) ] # If there are no candidates for the service channel, then do not return anything. if candidates == []: return None # If exactly 1, return it if len(candidates) == 1: return candidates[0] # If more than one, we bring in the configured channel if we have one to disambiguate if configured_channel and configured_channel in candidates: return configured_channel # Otherwise, return nothing. We have no solid way to disambiguate raise TwitterConfigurationException( "Sorry! There is a configuration error. " "You have 2 or more reply channels configured." "Please set up a default reply channel in Settings on the Default Channels page." ) # We only have the configured channel, no candidates based on sc, it could be None, which is OK return configured_channel
def su_print_channel(user, channel_id): try: channel = Channel.objects.get(channel_id) except Channel.DoesNotExist as exc: return jsonify(ok=False, error=str(exc)) from solariat_bottle.utils.post import get_service_channel service_channel = get_service_channel(channel) dispatch_channel = service_channel.get_outbound_channel(user) channels = filter(None, {channel, service_channel, dispatch_channel}) return jsonify( channel_id=channel_id, list=channels_to_json(channels, with_data=True))
def get_languages(channels=None): UNDEFINED = 'und' if channels is None: return get_supported_languages(FULL_SUPPORT) + [UNDEFINED] else: from solariat_bottle.db.channel.base import Channel from solariat_bottle.utils.post import get_service_channel langs = {UNDEFINED} for ch in Channel.objects.ensure_channels(channels): if hasattr(ch, 'get_allowed_langs'): langs.update(set(ch.get_allowed_langs())) else: sc = get_service_channel(ch) if sc: langs.update(set(sc.get_allowed_langs())) return sorted(langs)
def fb_get_channel_description(channel): from solariat_bottle.tasks.exceptions import FacebookCommunicationException, FacebookConfigurationException from solariat_bottle.utils.post import get_service_channel channel_info = {'user': [], 'pages': [], 'events': []} def pick_user_for_channel(ch): from solariat_bottle.db.user import User user = None for user in User.objects.find(accounts__in=[ch.account.id], is_superuser=False): if user.is_admin: return user return user service_channel = get_service_channel(channel) if not service_channel: raise FacebookConfigurationException( "No service channel was found for channel: " + str(channel)) try: cached_info = service_channel.channel_description if cached_info: return cached_info graph = get_facebook_api(service_channel) user_info = graph.get_object('me') picture = graph.get_object('me/picture') if picture: user_info['pic_url'] = picture.get('url') #friends = graph.get_object('me/friends') #user_info['friend_count'] = str(friends.get('summary', {}).get('total_count', 0)) channel_info['user'] = user_info for page_id in service_channel.facebook_page_ids: try: page_info = graph.get_object(page_id) except (facebook_driver.facebook.GraphAPIError, FacebookCommunicationException) as exc: if 'does not exist' in exc.message: logger.info( "%s facebook page %s does not exist. Removing from channel" % (channel, page_id)) service_channel.remove_facebook_page( { 'id': page_id, 'name': page_id }, pick_user_for_channel(service_channel)) else: raise exc else: picture = graph.get_object(page_id + '/picture') if picture: url = picture.get('url') page_info['pic_url'] = url channel_info['pages'].append(page_info) for event_id in service_channel.tracked_fb_event_ids: try: event_info = graph.get_object(event_id) except (facebook_driver.facebook.GraphAPIError, FacebookCommunicationException) as exc: if 'does not exist' in exc.message: logger.info( "%s facebook event %s does not exist. Removing from channel" % (channel, event_id)) service_channel.untrack_fb_event( { 'id': event_id, 'name': event_id }, pick_user_for_channel(service_channel)) else: raise exc else: picture = graph.get_object(event_id + '/picture') if picture: url = picture.get('url') event_info['pic_url'] = url channel_info['events'].append(event_info) service_channel.channel_description = channel_info return channel_info except FacebookConfigurationException: raise except Exception as exc: log_channel_error(service_channel, logger) logger.error(traceback.format_exc()) raise FacebookCommunicationException(exc.message)
def create_by_user(self, user, name, description, members, roles, channels, smart_tags=None, journey_types=None, journey_tags=None, funnels=None, predictors=None): from solariat_bottle.db.channel.base import Channel from solariat_bottle.db.journeys.journey_type import JourneyType from solariat_bottle.db.journeys.journey_tag import JourneyTag from solariat_bottle.db.funnel import Funnel from solariat_bottle.db.predictors.base_predictor import BasePredictor if not (user.is_staff or user.is_admin): raise RuntimeError("Only admin and staff users are allowed to create groups.") roles = [int(role) for role in roles] if roles is not None else [] if not user.current_account: LOGGER.error( "No account could be found for user {}. Aborting group creation.".format( user.email )) raise AppException("Error accessing database, we could not load your account." + "Please try later. If this keeps reproducing please contact a Staff member.") if smart_tags is None: smart_tags = [] if journey_types is None: journey_types = [] if journey_tags is None: journey_tags = [] if funnels is None: funnels = [] if predictors is None: predictors = [] group = super(GroupManager, self).create(name=name, description=description, members=list(set(members)), channels=channels, account=user.current_account, smart_tags=smart_tags, roles=roles, journey_types=journey_types, journey_tags=journey_tags, funnels=funnels, predictors=predictors) # Update acl for objects which this group was given access to for channel in Channel.objects.find(id__in=[ObjectId(c_id) for c_id in channels]): if channel.is_inbound: channel = get_service_channel(channel) or channel channel.add_perm(user, group=group, to_save=True) for tag in Channel.objects.find(id__in=[ObjectId(c_id) for c_id in smart_tags]): tag.add_perm(user, group=group, to_save=True) for jty in JourneyType.objects.find(id__in=journey_types): jty.add_perm(user, group=group, to_save=True) for jtg in JourneyTag.objects.find(id__in=journey_tags): jtg.add_perm(user, group=group, to_save=True) for fnl in Funnel.objects.find(id__in=funnels): fnl.add_perm(user, group=group, to_save=True) for prd in BasePredictor.objects.find(id__in=predictors): prd.add_perm(user, group=group, to_save=True) # Update members which are part of this group user_ids = [user.objects.get(u_id).id for u_id in members] if roles: # There are roles added to this group, we need to add all users which # have any of those associated roles to the group valid_users = user.objects.find(account=user.current_account, user_roles__in=roles)[:] user_ids.extend([u.id for u in valid_users]) user.objects.coll.update({'_id': {'$in': user_ids}}, {'$addToSet': {user.__class__.groups.db_field: group.id}}, multi=True) return group
def update(self, user, name, description, members, roles, channels, smart_tags=None, journey_types=None, journey_tags=None, funnels=None, predictors=None): # First, handle any changes in roles. If new ones were added, we automatically need # to add extra members. If any were removed, we need to remove batch of members from solariat_bottle.db.user import User from solariat_bottle.db.channel.base import Channel from solariat_bottle.db.journeys.journey_type import JourneyType from solariat_bottle.db.journeys.journey_tag import JourneyTag from solariat_bottle.db.funnel import Funnel from solariat_bottle.db.predictors.base_predictor import BasePredictor o_roles = [int(role) for role in roles] new_roles = [role for role in o_roles if role not in self.roles] removed_roles = [role for role in self.roles if role not in o_roles] # Some users have implicit access due to their role. Check if we need to remove this # based on the role we just set full_user_access = User.objects.find(groups__in=[self.id]) removed_members = [member for member in full_user_access if not self._role_check(member, removed_roles)] # For member that were a part only because of a role on this group, remove them now for member in removed_members: if self.id in member.groups: member.groups.remove(self.id) member.save() # For new members that would have access because of the role of the group, add group for new_member in User.objects.find(account=self.account, user_roles__in=new_roles): if self.id not in new_member.groups: new_member.groups.append(self.id) new_member.save() # Now for actual hard specified members, also add group user_ids = [User.objects.get(u_id).id for u_id in members] User.objects.coll.update({'_id': {'$in': user_ids}}, {'$addToSet': {User.groups.db_field: self.id}}, multi=True) new_channels = [channel for channel in channels if channel not in self.channels] removed_channels = [] for channel in self.channels: if str(channel.id) not in channels: removed_channels.append(channel.id) # Remove acl permissions for removed channels for channel in Channel.objects.find(id__in=[ObjectId(c_id) for c_id in removed_channels]): if channel.is_inbound: channel = get_service_channel(channel) or channel channel.del_perm(user, group=self, to_save=True) # Update acl for objects which this group was given access to for channel in Channel.objects.find(id__in=[ObjectId(c_id) for c_id in new_channels]): if channel.is_inbound: channel = get_service_channel(channel) or channel channel.add_perm(user, group=self, to_save=True) if smart_tags: new_tags = [tag for tag in smart_tags if tag not in self.smart_tags] removed_tags = [] for tag in self.smart_tags: if str(tag.id) not in smart_tags: removed_tags.append(tag.id) # Remove acl permissions for removed smart_tags for tag in Channel.objects.find(id__in=[ObjectId(c_id) for c_id in removed_tags]): tag.del_perm(user, group=self, to_save=True) # Update acl for objects which this group was given access to for tag in Channel.objects.find(id__in=[ObjectId(c_id) for c_id in new_tags]): tag.add_perm(user, group=self, to_save=True) if journey_types: saved_journey_types = set(str(_.id) for _ in self.journey_types) new_journey_types = set(journey_types) - saved_journey_types removed_journey_types = saved_journey_types - set(journey_types) for jty in JourneyType.objects.find(id__in=new_journey_types): jty.add_perm(user, group=self, to_save=True) for jty in JourneyType.objects.find(id__in=removed_journey_types): jty.del_perm(user, group=self, to_save=True) if journey_tags: saved_journey_tags = set(str(_.id) for _ in self.journey_tags) new_journey_tags = set(journey_tags) - saved_journey_tags removed_journey_tags = saved_journey_tags - set(journey_tags) for jtg in JourneyTag.objects.find(id__in=new_journey_tags): jtg.add_perm(user, group=self, to_save=True) for jtg in JourneyTag.objects.find(id__in=removed_journey_tags): jtg.del_perm(user, group=self, to_save=True) if funnels: saved_funnels = set(str(_.id) for _ in self.funnels) new_funnels = set(funnels) - saved_funnels removed_funnels = saved_funnels - set(funnels) for fnl in Funnel.objects.find(id__in=new_funnels): fnl.add_perm(user, group=self, to_save=True) for fnl in Funnel.objects.find(id__in=removed_funnels): fnl.del_perm(user, group=self, to_save=True) if predictors: saved_predictors = set(str(_.id) for _ in self.predictors) new_predictors = set(predictors) - saved_predictors removed_predictors = saved_predictors - set(predictors) for prd in BasePredictor.objects.find(id__in=new_predictors): prd.add_perm(user, group=self, to_save=True) for prd in BasePredictor.objects.find(id__in=removed_predictors): prd.del_perm(user, group=self, to_save=True) # Update members which are part of this group '''user_ids = [user.objects.get(u_id).id for u_id in members] user.objects.coll.update({'_id': {'$in': user_ids}}, {'$addToSet': {user.__class__.groups.db_field: self.id}}, multi=True)''' self.name = name self.description = description self.members = members self.roles = [int(r) for r in roles] self.channels = channels if smart_tags: self.smart_tags = smart_tags if journey_types: self.journey_types = journey_types if journey_tags: self.journey_tags = journey_tags if funnels: self.funnels = funnels if predictors: self.predictors = predictors self.save()
def _format_doc(cls, item, channel=None): ''' Format a post ready to be JSONified''' if isinstance(item, DynamicEvent): return super(PostAPIView, cls)._format_doc(item) from solariat_bottle.api.smarttags import SmartTagAPIView tag_scores = [] channel = channel or item.channel for tag in item.active_smart_tags: if tag not in get_service_channel( channel).inbound_channel.smart_tags: continue tag_uri = SmartTagAPIView._format_doc(tag)['uri'] if not tag.match(item): tag_scores.append({ 'name': tag.title, 'confidence': 0, 'uri': tag_uri, 'id': str(tag.id) }) else: tag_scores.append({ 'name': tag.title, 'confidence': cls.translate_score(tag.channel_filter._predict_fit(item)), 'uri': tag_uri, 'id': str(tag.id) }) utterances = [] for s_a in item.speech_acts: sentiment_sample = extract_sentiment(s_a['content']) utterances.append( dict(content=s_a['content'], topics=s_a['intention_topics'], topic_confidence=cls.translate_score( s_a['intention_topic_conf']), intention_type=SATYPE_ID_TO_NAME_MAP[ s_a['intention_type_id']].upper(), intention_type_confidence=cls.translate_score( s_a['intention_type_conf']), sentiment=sentiment_sample['sentiment'].title, sentiment_score=cls.translate_score( sentiment_sample['score']), sentiment_confidence=cls.translate_score( sentiment_sample['confidence']))) sentiment_sample = extract_sentiment(item.content) # handling matchables return dict(id=item.id, content=item.content, smart_tags=tag_scores, actionability=cls.translate_score( channel.channel_filter._predict_fit(item)), utterances=utterances, sentiment=sentiment_sample['sentiment'].title, sentiment_score=cls.translate_score( sentiment_sample['score']), sentiment_confidence=cls.translate_score( sentiment_sample['confidence']))
def create_by_user(self, user, **kw): safe_create = kw.pop('safe_create', False) if not safe_create: raise AppException("Use db.post.utils.factory_by_user instead") add_to_queue = kw.pop('add_to_queue', False) sync = kw.pop('sync', False) # We might consider dropping this entirely self.doc_class.patch_post_kw(kw) # handling extra_fields chat_data = kw.pop('chat_data', None) kw.setdefault("extra_fields", {}) if chat_data: kw["extra_fields"].update({"chat": chat_data}) kw["extra_fields"].setdefault("chat", {}) session_id = kw.get( "session_id", None) or kw["extra_fields"]["chat"].get("session_id") if not session_id: session_id = self.gen_session_id() kw["session_id"] = session_id chat_created_at = chat_data.get('created_at', None) if chat_data else dict() if chat_created_at: kw['_created'] = utc(parse_datetime(chat_created_at)) assert 'actor_id' in kw, "No 'actor_id' provided with chat message, could not infer it based on " + str( kw) assert 'is_inbound' in kw, "No 'is_inbound' provided with chat message, could not infer it based on " + str( kw) CustomerProfile = user.account.get_customer_profile_class() AgentProfile = user.account.get_agent_profile_class() if 'user_profile' not in kw: # If we have customer id but no specific profile, try to find it in our system if kw['is_inbound']: customer_or_agent = CustomerProfile.objects.get(kw['actor_id']) else: customer_or_agent = AgentProfile.objects.get(kw['actor_id']) profile = customer_or_agent.get_profile_of_type(ChatProfile) if not profile: profile = ChatProfile.anonymous_profile(platform='Chat') kw['user_profile'] = profile if not kw['is_inbound']: # We know it's outbound post, we need to figure out actor id based on parent from chat session try: parent = self.doc_class.objects.find( session_id=session_id, is_inbound=True).sort(_created=-1).limit(1)[:][0] # if we can't figure it out, let's put untracked post as a parent except IndexError: parent = UntrackedChatPost() kw['_id'] = pack_event_id(parent.actor.actor_num, kw['_created']) else: actor_num = self.doc_class.get_actor(True, kw['actor_id']).actor_num kw['_id'] = pack_event_id(actor_num, kw['_created']) # We know that it's inbound post, but may be the first post in conversation was outbound. # If that's the case, then this outbound post was fired by UntrackedProfile # Now we can encode id using current CustomerProfile instead of UntrackedProfile outbount_events = self.doc_class.objects.find( session_id=session_id, is_inbound=False)[:] for e in outbount_events: parent_actor_num, dt = unpack_event_id(e.id) if parent_actor_num == 0: e.delete() e.id = pack_event_id(actor_num, dt) e.save() kw['force_create'] = True lang_data = kw.pop('lang', Language(('en', 1))) # creation post = self.create(**kw) # postprocess_new_post(user, post) - failing for now, something with tag assignments assert post.session_id, "ChatPost should have chat session_id" self._set_post_lang(post, lang_data) postprocess_new_post(user, post, add_to_queue) get_service_channel(post.channel).post_received(post) return post
def get(self, user, *args, **kwargs): """ :param id: OPTIONAL, the id of the subscription you want details to :returns If id is passed in, the json format of the desired subscription, otherwise a list with all the subscription the user has access to. Sample responses: Generic GET: { "items": [ { "status": "created", "channel_id": "541aebcb31eddd1f678c2426", "from_date": 1404388800, "to_date": 1405252800, "datasift_historic_id": null, "datasift_push_id": null, "id": "541aebcb31eddd1f678c242a" }, { "status": "created", "finished": [], "actionable": [], "channel_id": "541aebcb31eddd1f678c242b", "from_date": 1404388800, "to_date": 1405252800, "id": "541aebcb31eddd1f678c242e" } ], "ok": true } Specific GET: { "item": { "status": "created", "channel_id": "541aebcb31eddd1f678c2426", "from_date": 1404388800, "to_date": 1405252800, "datasift_historic_id": null, "datasift_push_id": null, "id": "541aebcb31eddd1f678c242a" }, "ok": true } """ if 'id' in kwargs or '_id' in kwargs: subscription = self.get_subscription(user, kwargs) return dict(ok=True, item=subscription.to_dict()) elif 'channel' in kwargs or 'channel_id' in kwargs: channel = self.get_channel(user, kwargs) sc = get_service_channel(channel) subscriptions = BaseHistoricalSubscription.objects( channel_id=sc.id).sort(id=-1)[:10] return dict(ok=True, items=[sub.to_dict() for sub in subscriptions], has_active=any(s.is_active() for s in subscriptions)) else: # List all subscriptions this user has access to accessible_channels = Channel.objects.find_by_user(user) editable_channels = [ c.id for c in accessible_channels if c.can_edit(user) ] available_subscriptions = BaseHistoricalSubscription.objects.find( channel_id__in=editable_channels)[:] return dict( ok=True, items=[sub.to_dict() for sub in available_subscriptions])
def post(self, user, *args, **kwargs): """ :param channel: REQUIRED, the id of the channel we want to create a historic load for :param from_date: REQUIRED, the start date of the load, format is '%d.%m.%Y-%H:%M:%S' :param to_date: REQUIRED, the end date of the load, format is '%d.%m.%Y-%H:%M:%S' :returns A json response, format: {ok=<True|False>, error="", item=subscription.json} Sample response (Twitter): { "ok": true, "subscription": { "status": "created", "channel_id": "541aec9731eddd1fc4507853", "from_date": 1404388800, "to_date": 1405252800, "datasift_historic_id": null, "datasift_push_id": null, "id": "541aec9731eddd1fc4507857" } } Sample response (Facebook): { "ok": true, "subscription": { "status": "created", "finished": [], "actionable": [], "channel_id": "541aeccd31eddd1fec5b2059", "from_date": 1404388800, "to_date": 1405252800, "id": "541aeccd31eddd1fec5b205c" } } """ if 'channel' not in kwargs: exc_err = "Parameter 'channel' is required in order to create subscription." raise exc.InvalidParameterConfiguration(exc_err) channel = self.get_channel(user, kwargs) if 'from_date' not in kwargs: raise exc.InvalidParameterConfiguration( "Parameter 'from_date' required") if 'to_date' not in kwargs: raise exc.InvalidParameterConfiguration( "Parameter 'to_date' required") from_date = parse_datetime(kwargs['from_date']) to_date = parse_datetime(kwargs['to_date']) if from_date >= to_date: raise exc.InvalidParameterConfiguration( "'From' date must be less than 'To' date") type = kwargs.get('type', '') if type == 'event' and not channel.tracked_fb_event_ids: raise exc.InvalidParameterConfiguration( "No Events set, canceling recovery.") sc = get_service_channel(channel) if not isinstance(sc, FacebookServiceChannel): subscription_cls = HistoricalSubscriptionFactory.resolve(sc) if subscription_cls is None: raise exc.InvalidParameterConfiguration( "Could not infer a service channel for channel %s" % (channel.title + '<' + channel.__class__.__name__ + '>')) error = subscription_cls.validate_recovery_range( from_date, to_date) if error: raise exc.InvalidParameterConfiguration(error) if not kwargs.pop('force', False) and subscription_cls.objects.find_one( channel_id=sc.id, status__in=STATUS_ACTIVE): raise exc.ForbiddenOperation( "The recovery process is already in progress for this channel" ) subscription = subscription_cls.objects.create( created_by=user, channel_id=sc.id, from_date=from_date, to_date=to_date, status=SUBSCRIPTION_CREATED) if not get_var('APP_MODE') == 'test': subscription.process() return dict(ok=True, subscription=subscription.to_dict()) else: if isinstance(kwargs['from_date'], int) and isinstance( kwargs['to_date'], int): from_date_str = int(kwargs['from_date']) / 1000 to_date_str = int(kwargs['to_date']) / 1000 else: from_date_str = from_date.strftime("%s") to_date_str = to_date.strftime("%s") url = "%s?token=%s&channel=%s&since=%s&until=%s&type=%s" % \ (settings.FBOT_URL + '/json/restore', settings.FB_DEFAULT_TOKEN, sc.id, from_date_str, to_date_str, type) from solariat_bottle.tasks import async_requests async_requests.ignore('get', url, verify=False, timeout=None) return dict(ok=True)
def has_history(self, channel): service_channel = get_service_channel( channel) if channel and not channel.is_dispatch_channel else None return (channel and (channel in self.engaged_channels or service_channel in self.engaged_channels))