BingAds / BingAds-Python-SDK

Other
118 stars 164 forks source link

Reporting API scopes #187

Closed FAKERBNZ closed 3 years ago

FAKERBNZ commented 3 years ago

Hi Team, we are trying to get ad spend reports , we are able to get campaings ids names .... with soap requests but we get invalid client data when we are trying to sent requests to reporting api we try to relay on pyhton sdk for getting reports using this tutorial we get

'invalid_grant', 'AADSTS70000: The request was denied because one or more scopes requested are unauthorized or expired

the user grant msads.mange scope in the authentication consent screen thank you

qitia commented 3 years ago

this seems to me your request failed during oauth. did you try get refreshtoken/accesstoken with msads.manage scope?

FAKERBNZ commented 3 years ago

this seems to me your request failed during oauth. did you try get refreshtoken/accesstoken with msads.manage scope?

yes i can refresh token with soap requests and i even can get campaigns with soap requests but when i try to get ad spend with reporting api i fail wih python sdk and with soap requests with 'invalid client data error'

qitia commented 3 years ago

Can you please share the trackingid/requestid of the failed call?

FAKERBNZ commented 3 years ago
_: 'Invalid client data. Check the SOAP fault details for more information. TrackingId: da934449-3f4f-4481-b29a-71e08e4ce65d.', thank you 
qitia commented 3 years ago

@FakerBZD thanks, will ask the corresponding team for double check.

qitia commented 3 years ago

@FakerBZD , per the trackingid: your access token is expired. could you please share your code on how to calling the reporting api?

qitia commented 3 years ago

https://docs.microsoft.com/en-us/advertising/guides/faq?view=bingads-13#q-when-do-the-access-and-refresh-tokens-expire

FAKERBNZ commented 3 years ago

i can get campains id with the same token and we are refreshing it successfully every 5 min
SOAP request

<s:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header xmlns="https://bingads.microsoft.com/Reporting/v13">
    <Action mustUnderstand="1">SubmitGenerateReport</Action>
    <AuthenticationToken i:nil="false">{accessToken}</AuthenticationToken>
    <CustomerAccountId i:nil="false">AdvertiserAccount.Id</CustomerAccountId>
    <CustomerId i:nil="false">AdvertiserAccount.ParentCustomerId</CustomerId>
    <DeveloperToken i:nil="false">{devToken}</DeveloperToken>
  </s:Header>
  <s:Body>
    <SubmitGenerateReportRequest xmlns="https://bingads.microsoft.com/Reporting/v13">
      <ReportRequest i:nil="false" i:type="AccountPerformanceReportRequest">
        <ExcludeColumnHeaders i:nil="false">false</ExcludeColumnHeaders>
        <ExcludeReportFooter i:nil="false">true</ExcludeReportFooter>
        <ExcludeReportHeader i:nil="false">false</ExcludeReportHeader>
        <Format i:nil="false">Csv</Format>
        <FormatVersion i:nil="false"> 1.0</FormatVersion> 
        <ReportName i:nil="false">AdSpend report</ReportName>
        <ReturnOnlyCompleteData i:nil="false">false</ReturnOnlyCompleteData>
        <Aggregation>Daily</Aggregation>
        <Columns i:nil="false">
          <CampaignPerformanceReportColumn>Spend</CampaignPerformanceReportColumn>
        </Columns>
      <  <Filter i:nil="true" />
        <Scope i:nil="false">
          <AccountIds i:nil="false" xmlns:a1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
            <a1:long>AdvertiserAccount.Id</a1:long>
          </AccountIds>
          <Campaigns i:nil="false">
            <CampaignReportScope>
              <AccountId>AdvertiserAccount.Id</AccountId>
              <CampaignId>CampaignId</CampaignId>
            </CampaignReportScope>
          </Campaigns>
        </Scope>
        <Time i:nil="false">
          <CustomDateRangeEnd i:nil="false">
            <Day>20</Day>
            <Month>6</Month>
            <Year>2021</Year>
          </CustomDateRangeEnd>
          <CustomDateRangeStart i:nil="false">
            <Day>1</Day>
            <Month>1</Month>
            <Year>2018</Year>
          </CustomDateRangeStart>
          <PredefinedTime i:nil="true" />
          <ReportTimeZone i:nil="true" />
        </Time>
      </ReportRequest>
    </SubmitGenerateReportRequest>
  </s:Body>
</s:Envelope>

the soap request failed

the python report request

 #!/usr/bin/python3
import sys
import io
import pandas as pd
from urllib import parse
from datetime import datetime, timedelta
from bingads.service_client import ServiceClient
from bingads.v13 import *
from bingads.v13.reporting import *
from suds import WebFault
from suds.client import Client

#Function for date validation
def date_validation(date_text):
    try:
        while date_text != datetime.strptime(date_text, '%Y-%m-%d').strftime('%Y-%m-%d'):
            date_text = input('Please Enter the date in YYYY-MM-DD format\t')
        else:
            return datetime.strptime(date_text,'%Y-%m-%d').date()
    except:
        raise Exception('linkedin_campaign_processing : year does not match format yyyy-mm-dd')

def get_campaign_report(authorization_data,account_id,s_date,e_date,qry_type):
        try:
            startDate = date_validation(s_date)
            dt = startDate+timedelta(1)
            week_number = dt.isocalendar()[1]
            endDate = date_validation(e_date)

            reporting_service = ServiceClient(
                service='ReportingService', 
                version=13,
                authorization_data=authorization_data, 
                environment='production',
                )
            if qry_type in ["day","daily"]:
                aggregation = 'Daily'
            elif qry_type in ["week","weekly"]:
                aggregation = 'Weekly'

            exclude_column_headers=False
            exclude_report_footer=False
            exclude_report_header=False
            time=reporting_service.factory.create('ReportTime')
            # You can either use a custom date range or predefined time.
            #time.PredefinedTime='Yesterday'
            start_date=reporting_service.factory.create('Date')
            start_date.Day=startDate.day
            start_date.Month=startDate.month
            start_date.Year=startDate.year
            time.CustomDateRangeStart=start_date

            end_date=reporting_service.factory.create('Date')
            end_date.Day=endDate.day
            end_date.Month=endDate.month
            end_date.Year=endDate.year
            time.CustomDateRangeEnd=end_date
            time.ReportTimeZone='PacificTimeUSCanadaTijuana'
            return_only_complete_data=False

            report_request=reporting_service.factory.create('CampaignPerformanceReportRequest')
            report_request.Aggregation=aggregation
            report_request.ExcludeColumnHeaders=exclude_column_headers
            report_request.ExcludeReportFooter=exclude_report_footer
            report_request.ExcludeReportHeader=exclude_report_header
            report_request.Format='Csv'
            report_request.ReturnOnlyCompleteData=return_only_complete_data
            report_request.Time=time    
            report_request.ReportName="Campaign Performance Report"
            scope=reporting_service.factory.create('AccountThroughCampaignReportScope')
            scope.AccountIds={'long': [account_id] }
            scope.Campaigns=None
            report_request.Scope=scope     

            report_columns=reporting_service.factory.create('ArrayOfCampaignPerformanceReportColumn')
            report_columns.CampaignPerformanceReportColumn.append(['AccountName','AccountId','TimePeriod','CampaignId',
                'CampaignName','Impressions','Clicks','Conversions' ,'Spend'])
            report_request.Columns=report_columns

            #return campaign_performance_report_request
            return report_request
        except:
                print("\nMS_ADS_CAMPAIGN_REPORT : report processing Failed : ", sys.exc_info())

def download_campaign_report(report_request,authorization_data,s_date,e_date,qry_type):
    try:
        startDate = date_validation(s_date)
        dt = startDate+timedelta(1)
        week_number = dt.isocalendar()[1]
        endDate = date_validation(e_date)

        reporting_download_parameters = ReportingDownloadParameters(
                report_request=report_request,
                result_file_directory = "./data/", 
                result_file_name = "campaign_report.csv", 
                overwrite_result_file = True, # value true if you want to overwrite the same file.
                timeout_in_milliseconds=3600000 # cancel the download after a specified time interval.
            )

        reporting_service_manager=ReportingServiceManager(
            authorization_data=authorization_data, 
            poll_interval_in_milliseconds=5000, 
            environment='production',
        )

        report_container = reporting_service_manager.download_report(reporting_download_parameters)

        if(report_container == None):
            print("There is no report data for the submitted report request parameters.")
            sys.exit(0)

        campaign_analytics_data = pd.DataFrame(columns=["account_id","campaign_name","campaign_id","start_date","end_date",
                                    "cost","impressions","clicks"])

        if "Impressions" in report_container.report_columns and \
            "Clicks" in report_container.report_columns and \
            "Spend" in report_container.report_columns and \
            "CampaignId" in report_container.report_columns:

            report_record_iterable = report_container.report_records

            for record in report_record_iterable:
                tmp_dict = {}
                tmp_dict["impressions"] = record.int_value("Impressions")
                tmp_dict["clicks"] = record.int_value("Clicks")
                tmp_dict["cost"] = float(record.value("Spend"))
                #print(float(record.value("Spend")))
                tmp_dict["conversions"] = record.int_value("Conversions")
                tmp_dict["campaign_name"] = record.value("CampaignName")
                tmp_dict["campaign_id"] = record.int_value("CampaignId")
                tmp_dict["account_name"] = record.value("AccountName")
                tmp_dict["account_id"] = record.int_value("AccountId")

                campaign_analytics_data = campaign_analytics_data.append(tmp_dict,ignore_index = True)
                campaign_analytics_data["start_date"] = startDate
                campaign_analytics_data["end_date"] = endDate

                if qry_type in ["week","weekly"]:
                    campaign_analytics_data["week"] = week_number
                elif qry_type in ["month","monthly"]:
                    campaign_analytics_data["month"] = startDate.month
                elif qry_type in ["day","daily"]:
                    campaign_analytics_data["week"] = week_number

        #Be sure to close the report.
        report_container.close()

        return campaign_analytics_data
    except:
        print("\nDOWNLOAD_CAMPAIGN_REPORT : processing Failed : ", sys.exc_info())

def get_ads_report(authorization_data,account_id,s_date,e_date,qry_type):
        try:
            startDate = date_validation(s_date)
            dt = startDate+timedelta(1)
            week_number = dt.isocalendar()[1]
            endDate = date_validation(e_date)

            reporting_service = ServiceClient(
                service='ReportingService', 
                version=13,
                authorization_data=authorization_data, 
                environment='production',
                )

            if qry_type in ["day","daily"]:
                aggregation = 'Daily'
            elif qry_type in ["week","weekly"]:
                aggregation = 'Weekly'
            exclude_column_headers=False
            exclude_report_footer=False
            exclude_report_header=False
            time=reporting_service.factory.create('ReportTime')
            start_date=reporting_service.factory.create('Date')
            start_date.Day=startDate.day
            start_date.Month=startDate.month
            start_date.Year=startDate.year
            time.CustomDateRangeStart=start_date

            end_date=reporting_service.factory.create('Date')
            end_date.Day=endDate.day
            end_date.Month=endDate.month
            end_date.Year=endDate.year
            time.CustomDateRangeEnd=end_date
            time.ReportTimeZone='PacificTimeUSCanadaTijuana'
            return_only_complete_data=False

            report_request=reporting_service.factory.create('AdPerformanceReportRequest')
            report_request.Aggregation=aggregation
            report_request.ExcludeColumnHeaders=exclude_column_headers
            report_request.ExcludeReportFooter=exclude_report_footer
            report_request.ExcludeReportHeader=exclude_report_header
            report_request.Format='Csv'
            report_request.ReturnOnlyCompleteData=return_only_complete_data
            report_request.Time=time    
            report_request.ReportName="Ads Performance Report"
            scope=reporting_service.factory.create('AccountThroughAdGroupReportScope')
            scope.AccountIds={'long': [account_id] }
            scope.Campaigns=None
            report_request.Scope=scope     

            report_columns=reporting_service.factory.create('ArrayOfAdPerformanceReportColumn')
            report_columns.AdPerformanceReportColumn.append(['AccountId','TimePeriod','CampaignId',
                'CampaignName','AdId','Impressions','Clicks','Conversions','Spend'])
            report_request.Columns=report_columns

            #return campaign_performance_report_request
            return report_request
        except:
                print("\nMS_ADS_REPORT : report processing Failed : ", sys.exc_info())

def download_ads_report(report_request,authorization_data,s_date,e_date,qry_type):
    try:
        startDate = date_validation(s_date)
        dt = startDate+timedelta(1)
        week_number = dt.isocalendar()[1]
        endDate = date_validation(e_date)

        reporting_download_parameters = ReportingDownloadParameters(
                report_request=report_request,
                result_file_directory = "./data/", 
                result_file_name = "ads_report.csv", 
                overwrite_result_file = True, # Set this value true if you want to overwrite the same file.
                timeout_in_milliseconds=3600000 # You may optionally cancel the download after a specified time interval.
            )

        #global reporting_service_manager
        reporting_service_manager=ReportingServiceManager(
            authorization_data=authorization_data, 
            poll_interval_in_milliseconds=5000, 
            environment='production',
        )

        report_container = reporting_service_manager.download_report(reporting_download_parameters)

        if(report_container == None):
            print("There is no report data for the submitted report request parameters.")
            sys.exit(0)

        ads_analytics_data = pd.DataFrame(columns=["account_id","campaign_name","campaign_id","start_date","end_date",
            "ad_id","cost","impressions","clicks","final_url","currency"])

        if "Impressions" in report_container.report_columns and \
            "Clicks" in report_container.report_columns and \
            "Spend" in report_container.report_columns and \
            "AdId" in report_container.report_columns:

            report_record_iterable = report_container.report_records

            total_impressions = 0
            total_clicks = 0
            distinct_devices = set()
            distinct_networks = set()
            for record in report_record_iterable:
                tmp_dict = {}
                tmp_dict["impressions"] = record.int_value("Impressions")
                tmp_dict["clicks"] = record.int_value("Clicks")
                tmp_dict["cost"] = float(record.value("Spend"))
                tmp_dict["conversions"] = record.int_value("Conversions")
                tmp_dict["campaign_name"] = record.value("CampaignName")
                tmp_dict["campaign_id"] = record.int_value("CampaignId")
                tmp_dict["account_id"] = record.int_value("AccountId")
                tmp_dict["ad_id"] = record.int_value("AdId")
                tmp_dict["currency"] = record.value("CurrencyCode")

                try:
                    utm_campaign = None
                    utm_source = 'bing'
                    o = parse.urlparse(record.value("FinalUrl"))
                    query_url = parse.parse_qs(o.query)
                    url = o._replace(query=None).geturl()
                    #utm_campaign = query_url["utm_campaign"][0]
                    #print(utm_campaign)
                    utm_campaign = "MS "+ record.value("CampaignName")
                except:
                    print("\n***UTM data extraction Failed: ",sys.exc_info())
                    pass

                tmp_dict["final_url"] = url
                tmp_dict["utm_campaign"] = utm_campaign
                tmp_dict["utm_source"] = utm_source

                ads_analytics_data = ads_analytics_data.append(tmp_dict,ignore_index = True)

            ads_analytics_data = ads_analytics_data.append(tmp_dict,ignore_index = True)
            ads_analytics_data["start_date"] = startDate
            ads_analytics_data["end_date"] = endDate

            if qry_type in ["week","weekly"]:
                ads_analytics_data["week"] = week_number
            elif qry_type in ["month","monthly"]:
                ads_analytics_data["month"] = startDate.month
            elif qry_type in ["day","daily"]:
                #ads_analytics_data["day_name"] = startDate.strftime('%A')
                ads_analytics_data["week"] = week_number

        #Be sure to close the report.
        report_container.close()
        return ads_analytics_data
    except:
        print("\nDOWNLOAD_ADS_REPORT : processing Failed : ", sys.exc_info())                
qitia commented 3 years ago

from the log, we can only tell that the access token is expired. we do not know how do you setup your authorization_data. so can not tell what is wrong. I tested the sample and it works with both production and sandbox env. Could you please try that?

FYI OAuthTokens has property access_token_received_datetime and access_token_expires_in_seconds. You may debug into it and see if it expires or not.

FAKERBNZ commented 3 years ago

@qitia thank u alot for your responses, here is my code i setup the authorization_data from ms_auth function here is my code


from bingads.v13.reporting import *
import sys
from bingads.authorization import *
from bingads.service_client import ServiceClient
from urllib import parse
from datetime import datetime, timedelta
from bingads.service_client import ServiceClient
from bingads.v13 import *
from bingads.v13.reporting import *
from suds import WebFault
from suds.client import Client

def ms_auth(refresh_token,client_id,client_secrect,developer_token):
    try:
        authorization_data=AuthorizationData(
        account_id=None,
        customer_id=None,
        developer_token=developer_token,
        authentication=None,
        )
        authentication=OAuthDesktopMobileAuthCodeGrant(
        client_id=client_id,
        env='production'
        )

        authentication.state='bld@bingads_amp'
        authentication.client_secret=client_secrect
        # Assign this authentication instance to the authorization_data. 
        authorization_data.authentication=authentication  

        authorization_data.authentication.request_oauth_tokens_by_refresh_token(refresh_token)

        print("MS_AUTHENTICATION: authentication process finished successfully\n")

        return authorization_data
    except:
        print("\nMS_AUTHENTICATION: authentication process Failed : ",sys.exc_info())

client_id = "**************************"
client_secret = "**************************"
developer_token = "***********************"
access_secret = "********************************"
refresh_token = "*****************************************"

authorization_data = ms_auth(refresh_token,client_id,client_secret,developer_token)

# You must provide credentials in auth_helper.py.

# The report file extension type.
REPORT_FILE_FORMAT='Csv'

# The directory for the report files.
FILE_DIRECTORY='c:/reports/'

# The name of the report download file.
RESULT_FILE_NAME='result.' + REPORT_FILE_FORMAT.lower()

# The maximum amount of time (in milliseconds) that you want to wait for the report download.
TIMEOUT_IN_MILLISECONDS=3600000

def main(authorization_data):
    try:
        # You can submit one of the example reports, or build your own.

        report_request=get_report_request(authorization_data.account_id)

        reporting_download_parameters = ReportingDownloadParameters(
            report_request=report_request,
            result_file_directory = FILE_DIRECTORY, 
            result_file_name = RESULT_FILE_NAME, 
            overwrite_result_file = True, # Set this value true if you want to overwrite the same file.
            timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS # You may optionally cancel the download after a specified time interval.
        )

        #Option A - Background Completion with ReportingServiceManager
        #You can submit a download request and the ReportingServiceManager will automatically 
        #return results. The ReportingServiceManager abstracts the details of checking for result file 
        #completion, and you don't have to write any code for results polling.

        #output_status_message("-----\nAwaiting Background Completion...")
        #background_completion(reporting_download_parameters)

        #Option B - Submit and Download with ReportingServiceManager
        #Submit the download request and then use the ReportingDownloadOperation result to 
        #track status yourself using ReportingServiceManager.get_status().

        #output_status_message("-----\nAwaiting Submit and Download...")
        #submit_and_download(report_request)

        #Option C - Download Results with ReportingServiceManager
        #If for any reason you have to resume from a previous application state, 
        #you can use an existing download request identifier and use it 
        #to download the result file. 

        #For example you might have previously retrieved a request ID using submit_download.
        #reporting_operation=reporting_service_manager.submit_download(report_request)
        #request_id=reporting_operation.request_id

        #Given the request ID above, you can resume the workflow and download the report.
        #The report request identifier is valid for two days. 
        #If you do not download the report within two days, you must request the report again.
        #output_status_message("-----\nAwaiting Download Results...")
        #download_results(request_id, authorization_data)

        #Option D - Download the report in memory with ReportingServiceManager.download_report
        #The download_report helper function downloads the report and summarizes results.
        output_status_message("-----\nAwaiting download_report...")
        download_report(reporting_download_parameters)

    except WebFault as ex:
        output_webfault_errors(ex)
    except Exception as ex:
        output_status_message(ex)

def background_completion(reporting_download_parameters):
    """ You can submit a download request and the ReportingServiceManager will automatically 
    return results. The ReportingServiceManager abstracts the details of checking for result file 
    completion, and you don't have to write any code for results polling. """

    global reporting_service_manager
    result_file_path = reporting_service_manager.download_file(reporting_download_parameters)
    output_status_message("Download result file: {0}".format(result_file_path))

def submit_and_download(report_request):
    """ Submit the download request and then use the ReportingDownloadOperation result to 
    track status until the report is complete e.g. either using
    ReportingDownloadOperation.track() or ReportingDownloadOperation.get_status(). """

    global reporting_service_manager
    reporting_download_operation = reporting_service_manager.submit_download(report_request)

    # You may optionally cancel the track() operation after a specified time interval.
    reporting_operation_status = reporting_download_operation.track(timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS)

    # You can use ReportingDownloadOperation.track() to poll until complete as shown above, 
    # or use custom polling logic with get_status() as shown below.
    #for i in range(10):
    #    time.sleep(reporting_service_manager.poll_interval_in_milliseconds / 1000.0)

    #    download_status = reporting_download_operation.get_status()

    #    if download_status.status == 'Success':
    #        break

    result_file_path = reporting_download_operation.download_result_file(
        result_file_directory = FILE_DIRECTORY, 
        result_file_name = RESULT_FILE_NAME, 
        decompress = True, 
        overwrite = True,  # Set this value true if you want to overwrite the same file.
        timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS # You may optionally cancel the download after a specified time interval.
    )

    output_status_message("Download result file: {0}".format(result_file_path))

def download_results(request_id, authorization_data):
    """ If for any reason you have to resume from a previous application state, 
    you can use an existing download request identifier and use it 
    to download the result file. Use ReportingDownloadOperation.track() to indicate that the application 
    should wait to ensure that the download status is completed. """

    reporting_download_operation = ReportingDownloadOperation(
        request_id = request_id, 
        authorization_data=authorization_data, 
        poll_interval_in_milliseconds=1000, 
        environment='production',
    )

    # Use track() to indicate that the application should wait to ensure that 
    # the download status is completed.
    # You may optionally cancel the track() operation after a specified time interval.
    reporting_operation_status = reporting_download_operation.track(timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS)

    result_file_path = reporting_download_operation.download_result_file(
        result_file_directory = FILE_DIRECTORY, 
        result_file_name = RESULT_FILE_NAME, 
        decompress = True, 
        overwrite = True,  # Set this value true if you want to overwrite the same file.
        timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS # You may optionally cancel the download after a specified time interval.
    ) 

    output_status_message("Download result file: {0}".format(result_file_path))
    output_status_message("Status: {0}".format(reporting_operation_status.status))

def download_report(reporting_download_parameters):
    """ You can get a Report object by submitting a new download request via ReportingServiceManager. 
    Although in this case you will not work directly with the file, under the covers a request is 
    submitted to the Reporting service and the report file is downloaded to a local directory.  """

    global reporting_service_manager

    report_container = reporting_service_manager.download_report(reporting_download_parameters)

    #Otherwise if you already have a report file that was downloaded via the API, 
    #you can get a Report object via the ReportFileReader. 

    # report_file_reader = ReportFileReader(
    #     file_path = reporting_download_parameters.result_file_directory + reporting_download_parameters.result_file_name, 
    #     format = reporting_download_parameters.report_request.Format)
    # report_container = report_file_reader.get_report()

    if(report_container == None):
        output_status_message("There is no report data for the submitted report request parameters.")
        sys.exit(0)

    #Once you have a Report object via either workflow above, you can access the metadata and report records. 

    #Output the report metadata

    record_count = report_container.record_count
    output_status_message("ReportName: {0}".format(report_container.report_name))
    output_status_message("ReportTimeStart: {0}".format(report_container.report_time_start))
    output_status_message("ReportTimeEnd: {0}".format(report_container.report_time_end))
    output_status_message("LastCompletedAvailableDate: {0}".format(report_container.last_completed_available_date))
    output_status_message("ReportAggregation: {0}".format(report_container.report_aggregation))
    output_status_message("ReportColumns: {0}".format("; ".join(str(column) for column in report_container.report_columns)))
    output_status_message("ReportRecordCount: {0}".format(record_count))

    #Analyze and output performance statistics

    if "Impressions" in report_container.report_columns and \
        "Clicks" in report_container.report_columns and \
        "DeviceType" in report_container.report_columns and \
        "Network" in report_container.report_columns:

        report_record_iterable = report_container.report_records

        total_impressions = 0
        total_clicks = 0
        distinct_devices = set()
        distinct_networks = set()
        for record in report_record_iterable:
            total_impressions += record.int_value("Impressions")
            total_clicks += record.int_value("Clicks")
            distinct_devices.add(record.value("DeviceType"))
            distinct_networks.add(record.value("Network"))

        output_status_message("Total Impressions: {0}".format(total_impressions))
        output_status_message("Total Clicks: {0}".format(total_clicks))
        output_status_message("Average Impressions: {0}".format(total_impressions * 1.0 / record_count))
        output_status_message("Average Clicks: {0}".format(total_clicks * 1.0 / record_count))
        output_status_message("Distinct Devices: {0}".format("; ".join(str(device) for device in distinct_devices)))
        output_status_message("Distinct Networks: {0}".format("; ".join(str(network) for network in distinct_networks)))

    #Be sure to close the report.

    report_container.close()

def get_report_request(account_id):
    """ 
    Use a sample report request or build your own. 
    """

    aggregation = 'Daily'
    exclude_column_headers=False
    exclude_report_footer=False
    exclude_report_header=False
    time=reporting_service.factory.create('ReportTime')
    # You can either use a custom date range or predefined time.
    time.PredefinedTime='Yesterday'
    time.ReportTimeZone='PacificTimeUSCanadaTijuana'
    return_only_complete_data=False

    #BudgetSummaryReportRequest does not contain a definition for Aggregation.
    budget_summary_report_request=get_budget_summary_report_request(
        account_id=account_id,
        exclude_column_headers=exclude_column_headers,
        exclude_report_footer=exclude_report_footer,
        exclude_report_header=exclude_report_header,
        report_file_format=REPORT_FILE_FORMAT,
        return_only_complete_data=return_only_complete_data,
        time=time)

    campaign_performance_report_request=get_campaign_performance_report_request(
        account_id=account_id,
        aggregation=aggregation,
        exclude_column_headers=exclude_column_headers,
        exclude_report_footer=exclude_report_footer,
        exclude_report_header=exclude_report_header,
        report_file_format=REPORT_FILE_FORMAT,
        return_only_complete_data=return_only_complete_data,
        time=time)

    keyword_performance_report_request=get_keyword_performance_report_request(
        account_id=account_id,
        aggregation=aggregation,
        exclude_column_headers=exclude_column_headers,
        exclude_report_footer=exclude_report_footer,
        exclude_report_header=exclude_report_header,
        report_file_format=REPORT_FILE_FORMAT,
        return_only_complete_data=return_only_complete_data,
        time=time)

    user_location_performance_report_request=get_user_location_performance_report_request(
        account_id=account_id,
        aggregation=aggregation,
        exclude_column_headers=exclude_column_headers,
        exclude_report_footer=exclude_report_footer,
        exclude_report_header=exclude_report_header,
        report_file_format=REPORT_FILE_FORMAT,
        return_only_complete_data=return_only_complete_data,
        time=time)

    return campaign_performance_report_request

def get_budget_summary_report_request(
        account_id,
        exclude_column_headers,
        exclude_report_footer,
        exclude_report_header,
        report_file_format,
        return_only_complete_data,
        time):

    report_request=reporting_service.factory.create('BudgetSummaryReportRequest')
    report_request.ExcludeColumnHeaders=exclude_column_headers
    report_request.ExcludeReportFooter=exclude_report_footer
    report_request.ExcludeReportHeader=exclude_report_header
    report_request.Format=report_file_format
    report_request.ReturnOnlyCompleteData=return_only_complete_data
    report_request.Time=time    
    report_request.ReportName="My Budget Summary Report"
    scope=reporting_service.factory.create('AccountThroughCampaignReportScope')
    scope.AccountIds={'long': [account_id] }
    scope.Campaigns=None
    report_request.Scope=scope     

    report_columns=reporting_service.factory.create('ArrayOfBudgetSummaryReportColumn')
    report_columns.BudgetSummaryReportColumn.append([
        'AccountName',
        'AccountNumber',
        'AccountId',
        'CampaignName',
        'CampaignId',
        'Date',
        'CurrencyCode',
        'MonthlyBudget',
        'DailySpend',
        'MonthToDateSpend'
    ])
    report_request.Columns=report_columns

    return report_request

def get_campaign_performance_report_request(
        account_id,
        aggregation,
        exclude_column_headers,
        exclude_report_footer,
        exclude_report_header,
        report_file_format,
        return_only_complete_data,
        time):

    report_request=reporting_service.factory.create('CampaignPerformanceReportRequest')
    report_request.Aggregation=aggregation
    report_request.ExcludeColumnHeaders=exclude_column_headers
    report_request.ExcludeReportFooter=exclude_report_footer
    report_request.ExcludeReportHeader=exclude_report_header
    report_request.Format=report_file_format
    report_request.ReturnOnlyCompleteData=return_only_complete_data
    report_request.Time=time    
    report_request.ReportName="My Campaign Performance Report"
    scope=reporting_service.factory.create('AccountThroughCampaignReportScope')
    scope.AccountIds={'long': [account_id] }
    scope.Campaigns=None
    report_request.Scope=scope     

    report_columns=reporting_service.factory.create('ArrayOfCampaignPerformanceReportColumn')
    report_columns.CampaignPerformanceReportColumn.append([
        'TimePeriod',
        'CampaignId',
        'CampaignName',
        'DeviceType',
        'Network',
        'Impressions',
        'Clicks',  
        'Spend'
    ])
    report_request.Columns=report_columns

    return report_request

def get_keyword_performance_report_request(
        account_id,
        aggregation,
        exclude_column_headers,
        exclude_report_footer,
        exclude_report_header,
        report_file_format,
        return_only_complete_data,
        time):

    report_request=reporting_service.factory.create('KeywordPerformanceReportRequest')
    report_request.Aggregation=aggregation
    report_request.ExcludeColumnHeaders=exclude_column_headers
    report_request.ExcludeReportFooter=exclude_report_footer
    report_request.ExcludeReportHeader=exclude_report_header
    report_request.Format=report_file_format
    report_request.ReturnOnlyCompleteData=return_only_complete_data
    report_request.Time=time    
    report_request.ReportName="My Keyword Performance Report"
    scope=reporting_service.factory.create('AccountThroughAdGroupReportScope')
    scope.AccountIds={'long': [account_id] }
    scope.Campaigns=None
    scope.AdGroups=None
    report_request.Scope=scope     

    report_columns=reporting_service.factory.create('ArrayOfKeywordPerformanceReportColumn')
    report_columns.KeywordPerformanceReportColumn.append([
        'TimePeriod',
        'AccountId',
        'CampaignId',
        'Keyword',
        'KeywordId',
        'DeviceType',
        'Network',
        'Impressions',
        'Clicks',  
        'Spend',
        'BidMatchType',              
        'Ctr',
        'AverageCpc',        
        'QualityScore'
    ])
    report_request.Columns=report_columns

    return report_request

def get_user_location_performance_report_request(
        account_id,
        aggregation,
        exclude_column_headers,
        exclude_report_footer,
        exclude_report_header,
        report_file_format,
        return_only_complete_data,
        time):

    report_request=reporting_service.factory.create('UserLocationPerformanceReportRequest')
    report_request.Aggregation=aggregation
    report_request.ExcludeColumnHeaders=exclude_column_headers
    report_request.ExcludeReportFooter=exclude_report_footer
    report_request.ExcludeReportHeader=exclude_report_header
    report_request.Format=report_file_format
    report_request.ReturnOnlyCompleteData=return_only_complete_data
    report_request.Time=time    
    report_request.ReportName="My User Location Performance Report"
    scope=reporting_service.factory.create('AccountThroughAdGroupReportScope')
    scope.AccountIds={'long': [account_id] }
    scope.Campaigns=None
    scope.AdGroups=None
    report_request.Scope=scope 

    report_columns=reporting_service.factory.create('ArrayOfUserLocationPerformanceReportColumn')
    report_columns.UserLocationPerformanceReportColumn.append([
        'TimePeriod',
        'AccountId',
        'AccountName',
        'CampaignId',
        'AdGroupId',
        'LocationId',
        'Country',
        'Clicks',
        'Impressions',
        'DeviceType',
        'Network',
        'Ctr',
        'AverageCpc',
        'Spend',
    ])
    report_request.Columns=report_columns

    return report_request

# Main execution
if __name__ == '__main__':

    print("Loading the web service client proxies...")

    authorization_data=AuthorizationData(
        account_id=None,
        customer_id=None,
        developer_token='****************',
        authentication=None,
    )

    reporting_service_manager=ReportingServiceManager(
        authorization_data=authorization_data, 
        poll_interval_in_milliseconds=5000, 
        environment='production',
    )

    # In addition to ReportingServiceManager, you will need a reporting ServiceClient 
    # to build the ReportRequest.

    reporting_service=ServiceClient(
        service='ReportingService', 
        version=13,
        authorization_data=authorization_data, 
        environment='production',
    )

    main(authorization_data)
FAKERBNZ commented 3 years ago

do i pass authorization_data correctly ? "NoneType' object has no attribute 'enrich_headers'" i get this error

qitia commented 3 years ago

hi @FakerBZD from you code I see two issues: 1, in the main method you create the authorization_data again without authentication, you initiate ServiceClient with this auth_data, it is wrong 2, in the ms_auth method, you did not specify accountid and customerid, which are expected.

Could you please try again after fix them?

FAKERBNZ commented 3 years ago

hi, @qitia thank you for help 1- the authentication take object OAuthDesktopMobileAuthCodeGrant ?

        authorization_data=AuthorizationData(
        account_id='*******',
        customer_id='************',
        developer_token='*************',
        authentication=None,
    )
        authentication=OAuthDesktopMobileAuthCodeGrant(
        client_id=client_id,
        env='production'
        )

        authentication.state='bld@bingads_amp'
        authentication.client_secret=client_secrect
        # Assign this authentication instance to the authorization_data. 
        authorization_data.authentication=authentication  

        authorization_data.authentication.request_oauth_tokens_by_refresh_token(refresh_token)

how i can iniate ServiceClient ? sorry for the silly questions i started the project with soap requests so i really still don"t understand the sdk

update : i get this error

MS_AUTHENTICATION: authentication process finished successfully
> Loading the web service client proxies...
> -----
> Awaiting download_report...
> Web service reported a SOAP processing fault using an unexpected HTTP status code 200. Reporting as an internal server error.
> <suds.sax.document.Document object at 0x00000229F8A1BF40>
> ErrorCode: AccountNotAuthorized
> Code: 2003
> Details:
> Message: The specified report request contains at least one account which you have insufficient privileges to access. Please submit a report request for accounts that you are authorized to access.
>  i get this error im getting  account id and customer_id with getuser soap request 
qitia commented 3 years ago

did you fix the first issue? in your code, authorization_data you created in your main method never get authenticate...

if you already fix this issue? are you getting "The specified report request contains at least one account which you have insufficient privileges to access. Please submit a report request for accounts that you are authorized to access." now? if yes please reach out the support team to verify you have the right access to that account.

FAKERBNZ commented 3 years ago

@qitia , yes im getting "The specified report request contains at least one account which you have insufficient privileges to access. Please submit a report request for accounts that you are authorized to access" now thanks alot for the help i will reach the support team

qitia commented 3 years ago

so I close this issue. feel free to reopen in case of further question.