Exemplo n.º 1
0
 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
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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)
Exemplo n.º 7
0
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())
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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
Exemplo n.º 10
0
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))
Exemplo n.º 11
0
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)
Exemplo n.º 12
0
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)
Exemplo n.º 13
0
    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
Exemplo n.º 14
0
    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()
Exemplo n.º 15
0
    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']))
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
    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])
Exemplo n.º 18
0
    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)
Exemplo n.º 19
0
 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))