Example #1
0
def main():

    parsed_args = singer.utils.parse_args(REQUIRED_CONFIG_KEYS)

    config = parsed_args.config

    # Twitter Ads SDK Reference: https://github.com/twitterdev/twitter-python-ads-sdk
    client = Client(consumer_key=config.get('consumer_key'),
                    consumer_secret=config.get('consumer_secret'),
                    access_token=config.get('access_token'),
                    access_token_secret=config.get('access_token_secret'),
                    options={
                        'handle_rate_limit': True,
                        'retry_max': 3,
                        'retry_delay': 5000,
                        'retry_on_status': [404, 500, 503],
                        'retry_on_timeouts': True,
                        'timeout': (1.0, 3.0)
                    })

    state = {}
    if parsed_args.state:
        state = parsed_args.state

    catalog = parsed_args.catalog

    reports = config.get('reports', {})

    if parsed_args.discover:
        do_discover(reports)
    elif parsed_args.catalog:
        sync(client=client, config=config, catalog=catalog, state=state)
def json_handler(request):
    """
    Returns json_data {"campaigns": [campaign_list} for given request
    """
    client = Client(settings.SOCIAL_AUTH_TWITTER_KEY,
                    settings.SOCIAL_AUTH_TWITTER_SECRET,
                    settings.TWITTER_ACCESS_TOKEN,
                    settings.TWITTER_ACCESS_TOKEN_SECRET)
    account_id = request.GET.get("account_id", "")
    campaign_id = request.GET.get("campaign_id", "")
    account = client.accounts(account_id)
    # TODO: Link to Ads API Docs for LineItem.rst
    line_items = account.line_items(None, campaign_ids=campaign_id)
    line_item_list = []
    for line_item in line_items:
        name = line_item.name
        identifier = line_item.id
        objective = line_item.objective
        bid_amount = line_item.bid_amount_local_micro
        # Sometimes Bid Amount is None
        if bid_amount is not None:
            bid_amount = bid_amount / 10000
        line_item_list.append({
            "name": name,
            "id": identifier,
            "objective": objective,
            "bid_amount": bid_amount
        })
    return HttpResponse(json.dumps({
        "account_id": account_id,
        "campaign_id": campaign_id,
        "line_items": line_item_list
    }),
                        content_type="application/json")
Example #3
0
def test_active_entities():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION +
                                '/stats/accounts/2iqph/active_entities'),
                  body=with_fixture('active_entities'),
                  content_type='application/json')

    client = Client(characters(40), characters(40), characters(40),
                    characters(40))

    account = Account.load(client, '2iqph')

    end_time = datetime.utcnow().date()
    start_time = end_time - timedelta(days=1)

    active_entities = Campaign.active_entities(account,
                                               start_time,
                                               end_time,
                                               campaign_ids=['foo', 'bar'])

    assert len(responses.calls) == 2
    assert 'campaign_ids=foo%2Cbar' in responses.calls[1].request.url
    assert active_entities is not None
    assert isinstance(active_entities, list)
    assert len(active_entities) == 4
    assert active_entities[0]['entity_id'] == '2mvb28'
def change(request):
    """
    Returns a change to TA to upload a bucket location.
    """
    client = Client(settings.SOCIAL_AUTH_TWITTER_KEY,
                    settings.SOCIAL_AUTH_TWITTER_SECRET,
                    settings.TWITTER_ACCESS_TOKEN,
                    settings.TWITTER_ACCESS_TOKEN_SECRET)
    account_id = request.GET.get("account_id", "")
    identifier = request.GET.get("id", "")
    input_file_path = request.GET.get("input_file_path", "")
    # Update With location
    resource = '/0/accounts/' + account_id + '/tailored_audience_changes'
    params = {
        'tailored_audience_id': identifier,
        'input_file_path': base64.b64decode(input_file_path),
        'operation': "ADD"
    }
    json_data = {}
    try:
        request = Request(client, 'post', resource, params=params).perform()
        json_data["account_id"] = account_id
        json_data["data"] = request.body["data"]

    except Error as e:
        json_data["error"] = e.details
    return HttpResponse(json.dumps(json_data), content_type="application/json")
def new_targeting(request):
    """
    Creates a new
    """
    line_item_id = request.GET.get("line_item_id", "")
    account_id = request.GET.get("account_id", "")
    targeting_value = request.GET.get("targeting_value")
    targeting_type = "BEHAVIOR_EXPANDED"
    json_data = {}
    try:
        client = Client(settings.SOCIAL_AUTH_TWITTER_KEY,
                        settings.SOCIAL_AUTH_TWITTER_SECRET,
                        settings.TWITTER_ACCESS_TOKEN,
                        settings.TWITTER_ACCESS_TOKEN_SECRET)
        account = client.accounts(account_id)
        targeting_criteria = TargetingCriteria(account)
        targeting_criteria.line_item_id = line_item_id
        targeting_criteria.targeting_type = targeting_type
        targeting_criteria.targeting_value = targeting_value
        if targeting_value == "TAILORED_AUDIENCE":
            targeting_criteria.tailored_audience_type = "FLEXIBLE"
        targeting_criteria.save()
        json_data = {
            "valid": True,
            "account_id": account_id,
            "line_item_id": line_item_id,
            "targeting_value": targeting_value
        }
    except Error as e:
        json_data["response"] = e.details
        json_data["valid"] = False
        # passing to push the json_data to the browser
        pass
    return HttpResponse(json.dumps(json_data), content_type="application/json")
def test_tweet_previews_load():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION +
                                '/accounts/2iqph/tweet_previews'),
                  body=with_fixture('tweet_previews'),
                  content_type='application/json')

    client = Client(characters(40), characters(40), characters(40),
                    characters(40))

    account = Account.load(client, '2iqph')

    tweets = TweetPreview.load(
        account,
        tweet_ids=['1130942781109596160', '1101254234031370240'],
        tweet_type=TWEET_TYPE.PUBLISHED)

    assert tweets is not None
    assert isinstance(tweets, Cursor)
    assert tweets.count == 2

    tweet = tweets.next()
    assert tweet.tweet_id == '1130942781109596160'
    assert '<iframe' in tweet.preview
def new(request):
    """
    Returns a new TA path to hold the bucket location.
    """
    client = Client(settings.SOCIAL_AUTH_TWITTER_KEY,
                    settings.SOCIAL_AUTH_TWITTER_SECRET,
                    settings.TWITTER_ACCESS_TOKEN,
                    settings.TWITTER_ACCESS_TOKEN_SECRET)
    account_id = request.GET.get("account_id", "")
    name = request.GET.get("name", "")
    resource = '/0/accounts/' + account_id + '/tailored_audiences'
    params = {'name': name, 'list_type': 'HANDLE'}
    json_data = {}
    try:
        request = Request(client, 'post', resource, params=params).perform()
        # return audience.id to use
        ta_id = request.body['data']['id']
        json_data = {
            "valid": True,
            "account_id": account_id,
            "name": name,
            "id": str(ta_id)
        }
    except Error as e:
        json_data["response"] = e.details
        json_data["valid"] = False
    return HttpResponse(json.dumps(json_data), content_type="application/json")
Example #8
0
def main():

    parsed_args = singer.utils.parse_args(REQUIRED_CONFIG_KEYS)

    config = parsed_args.config

    # Twitter Ads SDK Reference: https://github.com/twitterdev/twitter-python-ads-sdk
    # Client reference: https://github.com/twitterdev/twitter-python-ads-sdk#rate-limit-handling-and-request-options
    client = Client(
        consumer_key=config.get('consumer_key'),
        consumer_secret=config.get('consumer_secret'),
        access_token=config.get('access_token'),
        access_token_secret=config.get('access_token_secret'),
        options={
            'handle_rate_limit': True,  # Handles 429 errors
            'retry_max': 10,
            'retry_delay': 60000,  # milliseconds, wait 1 minute for each retry
            # Error codes: https://developer.twitter.com/en/docs/basics/response-codes
            'retry_on_status': [400, 420, 500, 502, 503, 504],
            'retry_on_timeouts': True,
            'timeout': (5.0, 10.0)
        })  # Tuple: (connect, read) timeout in seconds

    state = {}
    if parsed_args.state:
        state = parsed_args.state

    catalog = parsed_args.catalog

    reports = config.get('reports', {})

    if parsed_args.discover:
        do_discover(reports)
    elif parsed_args.catalog:
        sync(client=client, config=config, catalog=catalog, state=state)
    def delete(self, request, *args, **kwargs):
        instance = self.get_object()
        try:
            tw_account = instance.tw_account_id
            account_id = tw_account.pk
            oauth_token = tw_account.tw_twitter_user_id.oauth_token \
             or settings.TW_ACCESS_TOKEN
            oauth_token_secret = tw_account.tw_twitter_user_id.oauth_secret \
             or settings.TW_ACCESS_SECRET
            _key = 'twitter_tailored_audiences_%s' % account_id
            client = Client(settings.TW_CONSUMER_KEY,
                            settings.TW_CONSUMER_SECRET, oauth_token,
                            oauth_token_secret)
            tw_account_id = int_to_base36(int(tw_account.pk))
            audience_id = int_to_base36(instance.pk)
        except Exception as e:
            return Response(data=dict(msg='invalid tw_account_id',
                                      detail=str(e)),
                            status=status.HTTP_400_BAD_REQUEST)

        try:
            resource = "/{api_version}/accounts/{account_id}/" \
             "tailored_audiences/{audience_id}".format(
              api_version=settings.TW_API_VERSION,
              account_id=tw_account_id,
              audience_id=audience_id)
            response = Request(client, 'delete', resource).perform()
        except Error as e:
            return Response(data=dict(msg=str(e)),
                            status=status.HTTP_400_BAD_REQUEST)

        instance.delete()
        redis_cache.delete(_key)

        return Response(data=dict(status='ok'))
Example #10
0
def test_tweets_get_all():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/tweets'),
                  body=with_fixture('tweets_get'),
                  content_type='application/json')

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')

    tweets = Tweets.all(
        account,
        tweet_ids=['1166476031668015104'],
        tweet_type=TWEET_TYPE.PUBLISHED,
        trim_user=True
    )

    assert tweets is not None
    assert isinstance(tweets, Cursor)
    assert tweets.count == 1
    assert tweets.first['tweet_id'] == '1166476031668015104'
    def get(self, request):
        account_id = request.query_params.get('account_id')
        account = TwitterAccount.objects_raw.get(pk=account_id)
        account_id_base36 = int_to_base36(int(account_id))

        oauth_token = account.tw_twitter_user_id.oauth_token
        oauth_secret = account.tw_twitter_user_id.oauth_secret
        client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET,
                        oauth_token, oauth_secret)
        resource = '/{api_version}/accounts/{account_id}/promotable_users'.format(
            api_version=settings.TW_API_VERSION, account_id=account_id_base36)
        response = Request(client, 'get', resource).perform()
        response.body['data']
        root_promotable_user_id = ''
        user_ids = []
        for u in response.body['data']:
            if u['promotable_user_type'] == 'FULL':
                root_promotable_user_id = int(u['user_id'])
            user_ids.append(u['user_id'])

        api = twitter.Api(consumer_key=settings.TW_CONSUMER_KEY,
                          consumer_secret=settings.TW_CONSUMER_SECRET,
                          access_token_key=settings.TW_ACCESS_TOKEN,
                          access_token_secret=settings.TW_ACCESS_SECRET)
        res = []
        promotable_users = api.UsersLookup(user_id=user_ids)
        for u in promotable_users:
            _id = u.id
            if root_promotable_user_id == u.id:
                _id = ''
            res.append({'id': _id, 'screen_name': '@%s' % u.screen_name})

        return Response({'results': res})
Example #12
0
    def get(self, request):
        account_id = request.query_params.get('account_id')
        keywords = request.query_params.get('keywords')
        number = request.query_params.get('number', 40)
        if account_id and keywords and number:
            account = TwitterAccount.objects_raw.get(pk=account_id)
            oauth_token = account.tw_twitter_user_id.oauth_token or settings.TW_ACCESS_TOKEN
            oauth_secret = account.tw_twitter_user_id.oauth_secret or settings.TW_ACCESS_SECRET
            if not account_id:
                return Response([])
            account_id = long(account_id)
            if isinstance(account_id, (int, long)):
                account_id = int_to_base36(account_id)

            client = Client(settings.TW_CONSUMER_KEY,
                            settings.TW_CONSUMER_SECRET, oauth_token,
                            oauth_secret)
            if settings.TW_SANDBOX:
                client.sandbox = settings.TW_SANDBOX

            api_domain = 'https://ads.twitter.com'
            resource = '/accounts/%s/keyword_recommendations.json?keywords=%s&number=%s' % \
                       (account_id, urllib.quote_plus(keywords), number)
            response = Request(client, 'get', resource,
                               domain=api_domain).perform()

            return Response(response.body)
        else:
            return Response(
                {'errors': 'account_id, keywords or number is missing.'},
                status=status.HTTP_400_BAD_REQUEST)
def test_analytics_sync_stats():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/stats/accounts/2iqph'),
                  body=with_fixture('analytics_sync_stats'),
                  content_type='application/json')

    client = Client(characters(40), characters(40), characters(40),
                    characters(40))

    account = Account.load(client, '2iqph')

    ids = ['aaaa', 'bbbb']
    metric_groups = [METRIC_GROUP.ENGAGEMENT]
    stats = Campaign.all_stats(account,
                               ids,
                               metric_groups,
                               granularity=GRANULARITY.TOTAL)

    assert len(responses.calls) == 2
    assert 'granularity=TOTAL' in responses.calls[1].request.url
    assert stats is not None
    assert isinstance(stats, list)
    assert len(stats) == 2
    assert stats[0]['id'] == 'aaaa'
def test_rate_limit_cursor_class_access():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION +
                                '/accounts/2iqph/campaigns'),
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '9999',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    client = Client(characters(40), characters(40), characters(40),
                    characters(40))

    account = Account.load(client, '2iqph')

    cursor = Campaign.all(account)
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.account_rate_limit_limit == '10000'
    assert cursor.account_rate_limit_remaining == '9999'
    assert cursor.account_rate_limit_reset == '1546300800'
Example #15
0
def test_targeted_audiences():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'))

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/custom_audiences/2906h'),
                  body=with_fixture('custom_audiences_load'))

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/custom_audiences/abc2/targeted?with_active=True'),
                  body=with_fixture('targeted_audiences'))

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')

    audience = CustomAudience.load(account, '2906h')
    targeted_audiences = audience.targeted(
        with_active=True
    )

    assert isinstance(targeted_audiences, Cursor)
    assert isinstance(targeted_audiences.first.line_items, list)
    assert targeted_audiences.first.campaign_id  == '59hod'
    assert targeted_audiences.first.line_items[0]['id'] == '5gzog'
    assert targeted_audiences.first.line_items[0]['name'] == 'test-line-item'
    assert targeted_audiences.first.line_items[0]['servable'] == True
    assert len(responses.calls) == 3
Example #16
0
def new(request):
    """
    Returns a new campaign
    """
    client = Client(settings.SOCIAL_AUTH_TWITTER_KEY,
                    settings.SOCIAL_AUTH_TWITTER_SECRET,
                    settings.TWITTER_ACCESS_TOKEN,
                    settings.TWITTER_ACCESS_TOKEN_SECRET)
    account_id = request.GET.get("account_id", "")
    campaign_name = request.GET.get("campaign", "")
    daily_budget = request.GET.get("daily_budget", "")
    account = client.accounts(account_id)
    # create your campaign
    json_data = {}
    try:
        campaign = Campaign(account)
        campaign.funding_instrument_id = account.funding_instruments().next(
        ).id
        campaign.daily_budget_amount_local_micro = int(daily_budget) * 1000
        campaign.name = campaign_name
        campaign.paused = True
        campaign.start_time = datetime.datetime.utcnow()
        campaign.save()
        json_data = {
            "valid": True,
            "account_id": account_id,
            "campaign_name": campaign_name,
            "campaign_id": campaign.id
        }
    except Error as e:
        json_data["response"] = e.details
        json_data["valid"] = False
        # passing as we send the json_data
        pass
    return HttpResponse(json.dumps(json_data), content_type="application/json")
Example #17
0
def test_promoted_tweets_all():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/promoted_tweets'),
                  body=with_fixture('promoted_tweets_all'),
                  content_type='application/json')

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')

    cursor = PromotedTweet.all(account)
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.count == 20

    promoted_tweet = cursor.next()
    assert promoted_tweet.id == '6thl4'
    assert promoted_tweet.entity_status == 'ACTIVE'
Example #18
0
def test_promoted_tweets_attach():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.POST,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/promoted_tweets'),
                  body=with_fixture('promoted_tweets_attach'),
                  content_type='application/json')

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')
    response = PromotedTweet.attach(
        account,
        line_item_id='2b7xw',
        tweet_ids=['585127452231467008']
    )

    assert isinstance(response, Cursor)
    assert response.count == 1
    assert response.first.id == '6thl4'
Example #19
0
def main():

    parsed_args = singer.utils.parse_args(REQUIRED_CONFIG_KEYS)

    config = parsed_args.config

    # Twitter Ads SDK Reference: https://github.com/twitterdev/twitter-python-ads-sdk
    client = Client(
        consumer_key=config.get("consumer_key"),
        consumer_secret=config.get("consumer_secret"),
        access_token=config.get("access_token"),
        access_token_secret=config.get("access_token_secret"),
        options={
            "handle_rate_limit": True,
            "retry_max": 3,
            "retry_delay": 5000,
            "retry_on_status": [404, 500, 503],
            "retry_on_timeouts": True,
            "timeout": (1.0, 3.0),
        },
    )

    state = {}
    if parsed_args.state:
        state = parsed_args.state

    sync(client=client, config=config, state=state)
def test_campaigns_all():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION +
                                '/accounts/2iqph/campaigns'),
                  body=with_fixture('campaigns_all'),
                  content_type='application/json')

    client = Client(characters(40), characters(40), characters(40),
                    characters(40))

    account = Account.load(client, '2iqph')

    cursor = account.campaigns()
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.count == 10

    campaign = cursor.next()
    assert campaign.id == '2wap7'
    assert campaign.entity_status == 'ACTIVE'
    def get_targeting(self, instance):
        targetings = TwitterTargeting.objects_raw.filter(
            tw_line_item_id=instance.tw_line_item_id).all().order_by(
                'tw_targeting_type')

        user_ids = [
            t.targeting_value for t in TwitterTargeting.objects_raw.filter(
                tw_line_item_id=instance.tw_line_item_id,
                tw_targeting_type=9).all()
        ]
        event_ids = [
            int_to_base36(int(t.tw_targeting_id))
            for t in TwitterTargeting.objects_raw.filter(
                tw_line_item_id=instance.tw_line_item_id,
                tw_targeting_type=23).all()
        ]

        i = 0
        api_domain = 'https://api.twitter.com'
        client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET,
                        settings.TW_ACCESS_TOKEN, settings.TW_ACCESS_SECRET)
        targeting_json = TwitterTargetingDetailSerializer(targetings,
                                                          many=True).data
        try:
            while i < len(user_ids):
                temp = user_ids[i:i + 100]
                i += 100
                resource = '/1.1/users/lookup.json?user_id=%s' % ','.join(temp)
                result = Request(client, 'get', resource,
                                 domain=api_domain).perform()
                for r in result.body:
                    extra = dict(followers_count_str=human_format(
                        r['followers_count']),
                                 targeting_value=r['id_str'],
                                 name=r['name'],
                                 profile_image_url=r['profile_image_url'],
                                 screen_name=r['screen_name'])
                    for k, target in enumerate(targeting_json):
                        if str(target['targeting_value']) == str(r['id']):
                            targeting_json[k]['extra'] = extra
        except Exception as e:
            print str(e)
            print 'fetching users failed'
        try:
            if len(event_ids) > 0:
                resource = '/%s/targeting_criteria/events?ids=%s' % (
                    settings.TW_API_VERSION, ','.join(event_ids))
                result = Request(client, 'get', resource).perform()
                for r in result.body['data']:
                    _id = str(base36_to_int(r['id']))
                    event_type = r['event_type']
                    event_type = event_type.replace('MUSIC_AND_', '')
                    event_type = event_type.replace('_', ' ').capitalize()
                    extra = dict(id=_id, name=r['name'], category=event_type)
                    for k, target in enumerate(targeting_json):
                        if str(target['tw_targeting_id']) == _id:
                            targeting_json[k]['extra'] = extra
        except:
            print 'fetching events failed'
        return targeting_json
Example #22
0
def test_line_items_all():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/line_items'),
                  body=with_fixture('line_items_all'),
                  content_type='application/json')

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')

    cursor = account.line_items()
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.count == 10

    lineitem = cursor.next()
    assert lineitem.id == 'bw2'
    assert lineitem.entity_status == 'ACTIVE'
Example #23
0
def test_analytics_async():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.POST,
                  with_resource('/' + API_VERSION + '/stats/jobs/accounts/2iqph'),
                  body=with_fixture('analytics_async_post'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/stats/jobs/accounts/2iqph'),
                  body=with_fixture('analytics_async_get'),
                  content_type='application/json')

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')

    ids = ['aaaa']
    metric_groups = [METRIC_GROUP.ENGAGEMENT]
    stats = Campaign.queue_async_stats_job(
        account,
        ids,
        metric_groups,
        granularity=GRANULARITY.TOTAL
    )

    # test POST request response - queue_async_stats_job()
    assert 'granularity=TOTAL' in responses.calls[1].request.url
    assert stats is not None
    assert isinstance(stats, dict)
    assert stats['entity_ids'] == ids

    # call async_stats_job_result() through Campaign class (inheritance)
    job_id = stats['id_str']
    job_result = Campaign.async_stats_job_result(
        account,
        [job_id]).first

    assert job_result is not None
    assert isinstance(job_result, dict)
    assert job_result['url'] == 'https://ton.twimg.com/advertiser-api-async-analytics/stats.json.gz'

    # call async_stats_job_result() from Analytics class directly
    job_result = Analytics.async_stats_job_result(
        account,
        [job_id]).first

    assert job_result is not None
    assert isinstance(job_result, dict)
    assert job_result['url'] == 'https://ton.twimg.com/advertiser-api-async-analytics/stats.json.gz'
Example #24
0
    def fetch_behaviors(self, data, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET):
        res = {}
        res['data'] = []
        res['success'] = False
        account_id = data.get('account_id')
        country_code = data.get('country_code', 'US')

        if isinstance(account_id,(int,long)):
            account_id = int_to_base36(account_id)

        if account_id is None:
            res = {
                'success': False,
                'message': "Missing Twitter Account ID"
            }
            return res

        client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token,
                        oauth_token_secret)
        if settings.TW_SANDBOX:
            client.sandbox = settings.TW_SANDBOX
        try:
            resource = '/{api_version}/targeting_criteria/behaviors?count=1000&country_code={country_code}'.format(api_version=settings.TW_API_VERSION, country_code=country_code)
            response = Request(client, 'get', resource).perform()

            if response.headers['x-rate-limit-remaining'] == "0" and settings.TW_RATE_LIMIT_ALERT:
                send_twitter_alert_email({"account_id": account_id, "endpoint": resource})

            res['data'] = response.body['data']
            next_cursor = response.body.get('next_cursor', False)
            while next_cursor:
                resource = '/{api_version}/targeting_criteria/behaviors?count=1000&cursor={next_cursor}&country_code={country_code}'.format(next_cursor=next_cursor, country_code=country_code, api_version=settings.TW_API_VERSION)
                response = Request(client, 'get', resource).perform()
                next_cursor = response.body.get('next_cursor', False)
                res['data'] += response.body['data']

            res['success'] = True

        except Error as e:
            code = None
            if e.code:
                code = e.code
            elif e.details[0]['code']:
                code = e.details[0]['code']
            res = {
                'data': {},
                'success': False,
                'message': e.details[0]['message'] if e.details and e.details[0] and e.details[0]['message'] else '',
                'errors': { str(code): True } if code else {}
            }
        except Exception as e:
            res = {
                'success': False,
                'message': str(e)
            }
        return res
    def get(self, request):
        account_id = request.query_params.get('account_id')
        next_cursor = request.query_params.get('next_cursor')
        objective = request.query_params.get('objective', 'APP_INSTALLS')
        promotable_user_id = request.query_params.get('promotable_user_id')

        if objective == 'APP_INSTALLS':
            objective = 1
        elif objective == 'WEBSITE_CLICKS':
            objective = 5
        else:
            objective = 0

        if not account_id:
            return Response([])

        account = TwitterAccount.objects_raw.get(pk=account_id)
        account_id_base36 = int_to_base36(int(account_id))

        oauth_token = account.tw_twitter_user_id.oauth_token
        oauth_secret = account.tw_twitter_user_id.oauth_secret
        client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET,
                        oauth_token, oauth_secret)
        api_domain = 'https://ads.twitter.com'

        if next_cursor:
            next_cursor = '&cursor=%s' % next_cursor
        else:
            next_cursor = ''
        resource = '/accounts/{account_id}/tweets/dashboard/tweet_rows?' \
            'manage_campaigns=true&objective={objective}&account={account_id}' \
            '&lang=en&promotable_user={promotable_user}{next_cursor}' \
            .format(
                account_id=account_id_base36,
                next_cursor=next_cursor,
                objective=objective,
                promotable_user=promotable_user_id
            )
        response = Request(client, 'get', resource,
                           domain=api_domain).perform()

        results = []
        html = response.body['tweetRows']
        soup = BeautifulSoup(html, 'html.parser')
        checkboxes = soup.find_all("input", {"class": "tweet-checkbox"})
        tweets = soup.find_all("div", {"class": "Tweet--timeline"})
        for i in range(len(tweets)):
            result = {
                'preview': tweets[i].prettify(),
                'tweet_id': str(checkboxes[i]['value'])
            }
            results.append(result)

        return Response(
            dict(results=results, next_cursor=response.body.get('cursor')))
Example #26
0
def test_accounts_with_id():
    responses.add(responses.GET,
                  with_resource('/1/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    client = Client(characters(40), characters(40), characters(40),
                    characters(40))

    account = client.accounts('2iqph')
    assert account is not None
    assert isinstance(account, Account)
    assert account.id == '2iqph'
Example #27
0
def test_accounts_with_no_id():
    responses.add(responses.GET,
                  with_resource('/1/accounts'),
                  body=with_fixture('accounts_all'),
                  content_type='application/json')

    client = Client(characters(40), characters(40), characters(40),
                    characters(40))

    cursor = client.accounts()
    assert cursor is not None
    assert isinstance(cursor, Cursor)
    assert cursor.count == 5
    def get(self, request, oauth_token=settings.TW_ACCESS_TOKEN, oauth_token_secret=settings.TW_ACCESS_SECRET):
        account_id = request.query_params.get('account_id')
        if not account_id:
            return Response([])

        client = Client(settings.TW_CONSUMER_KEY, settings.TW_CONSUMER_SECRET, oauth_token,
                            oauth_token_secret)
        if settings.TW_SANDBOX:
            client.sandbox = settings.TW_SANDBOX
        resource = '/{api_version}/accounts/{account_id}/app_lists'.format(api_version=settings.TW_API_VERSION, account_id=account_id)
        response = Request(client, 'get', resource).perform()
        json = response.body['data']
        return Response(json)
def test_audience_summary():
    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.POST,
                  with_resource('/' + API_VERSION + '/accounts/2iqph/audience_estimate'),
                  body=with_fixture('audience_estimate'),
                  content_type='application/json')

    client = Client(
        characters(40),
        characters(40),
        characters(40),
        characters(40)
    )

    account = Account.load(client, '2iqph')

    params = {
        "targeting_criteria": [
          {
            "targeting_type":"LOCATION",
            "targeting_value":"96683cc9126741d1"
          },
          {
            "targeting_type":"BROAD_KEYWORD",
            "targeting_value":"cats"
          },
          {
            "targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
            "targeting_value": "14230524"
          },
          {
            "targeting_type":"SIMILAR_TO_FOLLOWERS_OF_USER",
            "targeting_value": "90420314"
          }
        ]
      }

    audience_summary = AudienceEstimate.load(
        account=account,
        params=params
    )

    print (audience_summary)
    assert audience_summary is not None
    assert audience_summary.audience_size is not None
    assert audience_summary.audience_size['min'] == 41133600
    assert audience_summary.audience_size['max'] == 50274400
def test_retry_count_error(monkeypatch):
    monkeypatch.setattr(time, 'sleep', lambda s: None)

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION + '/accounts/2iqph'),
                  body=with_fixture('accounts_load'),
                  content_type='application/json')

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION +
                                '/accounts/2iqph/campaigns'),
                  status=404,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '0',
                      'x-account-rate-limit-reset': str(int(time.time()) + 5)
                  })

    responses.add(responses.GET,
                  with_resource('/' + API_VERSION +
                                '/accounts/2iqph/campaigns'),
                  status=404,
                  body=with_fixture('campaigns_all'),
                  content_type='application/json',
                  headers={
                      'x-account-rate-limit-limit': '10000',
                      'x-account-rate-limit-remaining': '9999',
                      'x-account-rate-limit-reset': '1546300800'
                  })

    client = Client(characters(40),
                    characters(40),
                    characters(40),
                    characters(40),
                    options={
                        'retry_max': 1,
                        'retry_delay': 3000,
                        'retry_on_status': [404, 500, 503]
                    })

    account = Account.load(client, '2iqph')

    try:
        cursor = Campaign.all(account)
    except Exception as e:
        error = e
        print(error)
    assert len(responses.calls) == 3
    assert isinstance(error, NotFound)